From 350babc6eed21af628bd50c68eaa4aa95b50965e Mon Sep 17 00:00:00 2001 From: willbot Date: Thu, 22 Oct 2026 14:58:29 +0200 Subject: [PATCH 001/118] TML-3709: Plan the convenience date and time types project and draft ADR 279 PostgreSQL date and time types get two layers: the explicit, database-named types stay, and DateTime, Date, Time and Duration become convenience types whose value: and column: arguments choose one explicit type, with defaults that need no Temporal. Adds the project spec, plan and design notes, the draft ADR, its index row, and a note in the rename project that it now keeps DateTime. Co-Authored-By: Claude Opus 5.5 Signed-off-by: willbot Signed-off-by: Will Madden --- docs/architecture docs/ADR-INDEX.md | 1 + ...e an explicit type from their arguments.md | 158 ++++++++++++++++++ projects/datetime-convenience-types/README.md | 12 ++ .../design-notes.md | 66 ++++++++ projects/datetime-convenience-types/plan.md | 52 ++++++ projects/datetime-convenience-types/spec.md | 120 +++++++++++++ projects/target-named-scalars-sql/README.md | 2 + 7 files changed, 411 insertions(+) create mode 100644 docs/architecture docs/adrs/ADR 279 - Convenience date and time types choose an explicit type from their arguments.md create mode 100644 projects/datetime-convenience-types/README.md create mode 100644 projects/datetime-convenience-types/design-notes.md create mode 100644 projects/datetime-convenience-types/plan.md create mode 100644 projects/datetime-convenience-types/spec.md diff --git a/docs/architecture docs/ADR-INDEX.md b/docs/architecture docs/ADR-INDEX.md index 825d5e4c55e3..3428a89ded74 100644 --- a/docs/architecture docs/ADR-INDEX.md +++ b/docs/architecture docs/ADR-INDEX.md @@ -47,6 +47,7 @@ This document provides a comprehensive index of all Architectural Decision Recor | 272 | Packages contribute collection methods computed from a model | **Accepted.** A package reads each model of the contract (target, name, fields, relations, storage table) and contributes collection methods under names it chooses; every SQL ORM collection offers them under the reserved member `using`, such as `db.orm.public.Post.using.post_search(q)` and `db.orm.public.Post.using.embedding_nearest(v)`. The type half registers in `CollectionMethodRegistry` by declaration merging through the package's own entry points, never through the emitted `contract.d.ts`; the run-time half goes on a target or extension descriptor. Two contributions that give a model the same name are a compile error and an `ORM.ARGUMENT_INVALID` at `orm()` | [ADR 272 - Packages contribute collection methods computed from a model.md](adrs/ADR%20272%20-%20Packages%20contribute%20collection%20methods%20computed%20from%20a%20model.md) | | 273 | A column of a type no data type claims is a raw column | A column whose database type no data type claims has data type `sql/raw`, whose parameter `sql` is the type exactly as the catalog prints it, and codec `sql/raw@1`, which passes values through typed `unknown`; migrations write the text into DDL and `db verify` compares it exactly; a Prisma 8 field declares one with ``Raw(sql`...`)``, and its default is a `sql` expression; the Prisma 7 reader reads `Unsupported("...")` as a raw column with no field | [ADR 273 - A column of a type no data type claims is a raw column.md](adrs/ADR%20273%20-%20A%20column%20of%20a%20type%20no%20data%20type%20claims%20is%20a%20raw%20column.md) | | 275 | Client generators are field attributes, and a type alias is expanded into each column | **Proposed.** `@onCreate(fn())` and `@onUpdate(fn())` set the client's generators, resolved from the column's codec through a generator function registry the stack assembles, with params from the column's type parameters; `@default` means a database default only; a `types {}` alias may carry `@default`, `@onCreate` and `@onUpdate`, is expanded into each column, and is never stored, so `storage.types` keeps only types a contract space registers; field presets are removed from SQL and Mongo, PSL and the TypeScript builder, and targets contribute aliases such as `temporal.UpdatedAt` | [ADR 275 - Client generators are field attributes, and a type alias is expanded into each column.md](adrs/ADR%20275%20-%20Client%20generators%20are%20field%20attributes%2C%20and%20a%20type%20alias%20is%20expanded%20into%20each%20column.md) | +| 279 | Convenience date and time types choose an explicit type from their arguments | **Proposed.** PostgreSQL date and time types have two layers: the explicit, database-named types (`TimestamptzJsDate`, `TimestamptzTemporal`, `DateString`, …), each fixing one codec, and the convenience types `DateTime`, `Date`, `Time` and `Duration`, named for the application concept, whose `value:` and `column:` arguments choose one explicit type, with defaults that need no `Temporal`; a type constructor may choose its codec from its arguments; the contract stores only the explicit type | [ADR 279 - Convenience date and time types choose an explicit type from their arguments.md](adrs/ADR%20279%20-%20Convenience%20date%20and%20time%20types%20choose%20an%20explicit%20type%20from%20their%20arguments.md) | ## Query System diff --git a/docs/architecture docs/adrs/ADR 279 - Convenience date and time types choose an explicit type from their arguments.md b/docs/architecture docs/adrs/ADR 279 - Convenience date and time types choose an explicit type from their arguments.md new file mode 100644 index 000000000000..b8234dfeefbd --- /dev/null +++ b/docs/architecture docs/adrs/ADR 279 - Convenience date and time types choose an explicit type from their arguments.md @@ -0,0 +1,158 @@ +# ADR 279 — Convenience date and time types choose an explicit type from their arguments + +**Status:** Proposed +**Builds on:** [ADR 241 — Scalar types use the authoring type-constructor channel](ADR%20241%20-%20Scalar%20types%20use%20the%20authoring%20type-constructor%20channel.md) +**Amends:** [ADR 254 — Data types and casts](ADR%20254%20-%20Data%20types%20and%20casts.md), [ADR 257 — Scalar types are named after the target on every surface](ADR%20257%20-%20Scalar%20types%20are%20named%20after%20the%20target%20on%20every%20surface.md), [ADR 275 — Client generators are field attributes, and a type alias is expanded into each column](ADR%20275%20-%20Client%20generators%20are%20field%20attributes%2C%20and%20a%20type%20alias%20is%20expanded%20into%20each%20column.md) +**Brings back:** [ADR 246 — Option arguments and select templates for authoring helpers](ADR%20246%20-%20Option%20arguments%20and%20select%20templates%20for%20authoring%20helpers.md), which ADR 275 supersedes, for type constructors only + +## At a glance + +On PostgreSQL today, each date and time type name fixes one column type and one kind of value that the application reads: `Timestamptz` is a `timestamptz` column read as a `Temporal.Instant`, `TimestamptzString` is the same column read as a string, and so on. This decision calls those the **explicit types**, and keeps them. + +It adds four **convenience types**, named for what a field means in the application: `DateTime` for a moment in time, `Date` for a calendar date, `Time` for a time of day, and `Duration` for a length of time. Each convenience type becomes one explicit type. Two optional arguments say which: `value`, what the application reads, and `column`, the PostgreSQL column type. + +```prisma +model Order { + id Int @id + paidAt DateTime + heldAt DateTime(value: temporal, column: timestamptz, precision: 3) + shipBy Date(value: string) + opensAt Time(value: temporal, precision: 0) +} +``` + +| Field | Becomes the explicit type | Column | The application reads | +| --- | --- | --- | --- | +| `paidAt` | `TimestamptzJsDate(3)` | `timestamptz(3)` | a JavaScript `Date` | +| `heldAt` | `TimestamptzTemporal(3)` | `timestamptz(3)` | `Temporal.Instant` | +| `shipBy` | `DateString` | `date` | a string, such as `"2024-02-29"` | +| `opensAt` | `TimeTemporal(0)` | `time(0)` | `Temporal.PlainTime` | + +The contract stores what the explicit type stores: the codec, its data type and its parameters. `heldAt` is stored exactly as if the user had written `heldAt TimestamptzTemporal(3)`, and nothing in the contract records that a convenience type was written. + +## Decision + +1. **Date and time types have two layers.** Each explicit type uses one codec, so it fixes one column type and one application value, and is named after the database as ADR 257 says. Each convenience type is named for what a field means in the application, and becomes exactly one explicit type. +2. **A convenience type's arguments choose its explicit type.** `value` says what the application reads: a JavaScript `Date`, a `Temporal` object or a string. `column` says the PostgreSQL column type. Each has a default that the convenience type declares. Any other argument, such as `precision`, is passed on to the chosen explicit type. +3. **A convenience type is a type constructor whose arguments choose its codec.** A type constructor is how PSL defines a type name that can take arguments, such as `Timestamptz(3)`. Today each one always uses the same codec. A type constructor may now choose its codec from its arguments. +4. **The contract stores only the explicit type.** The contract format, the codecs, the data types, the storage hash and the execution hash are the same whether a schema writes the convenience type or the explicit type it becomes. +5. **The PostgreSQL target provides four convenience types:** + + | Convenience type | `value` choices | `column` choices | Defaults | + | --- | --- | --- | --- | + | `DateTime` | `jsDate` (a JavaScript `Date`), `temporal` (`Temporal.Instant`), `string` | `timestamptz`, `timestamp` | `jsDate`, `timestamptz`; precision 3 with `jsDate`, PostgreSQL's default otherwise | + | `Date` | `string`, `temporal` (`Temporal.PlainDate`) | `date` | `string` | + | `Time` | `string`, `temporal` (`Temporal.PlainTime`) | `time`; `timetz` is allowed only with `value: string` | `string`, `time` | + | `Duration` | `string` (an ISO 8601 duration such as `P1Y2M`), `parts` (the object `{ months, days, micros }`) | `interval` | `string` | + + The value names repeat the suffixes of the explicit types, so `DateTime(value: jsDate)` and `TimestamptzJsDate` read as the same thing. No default needs a global `Temporal`, so every default works on a runtime without one. A JavaScript `Date` is the default only for a moment in time; a calendar date, a time of day and a length of time default to plain ISO 8601 strings, because JavaScript has no built-in value for them and a `Date` there stores the wrong day. + +6. **Every explicit type for a column that has several codecs carries a suffix naming its kind of value.** The explicit types that read `Temporal` values gain the suffix `Temporal`, next to the existing `String` and `JsDate` suffixes: + + | Column | `Temporal` value | Explicit name today | Explicit name after | + | --- | --- | --- | --- | + | `timestamptz` | `Temporal.Instant` | `Timestamptz`, and `DateTime` as a second name | `TimestamptzTemporal` | + | `timestamp` | `Temporal.PlainDateTime` | `Timestamp` | `TimestampTemporal` | + | `date` | `Temporal.PlainDate` | `Date` | `DateTemporal` | + | `time` | `Temporal.PlainTime` | `Time` | `TimeTemporal` | + + This frees `DateTime`, `Date` and `Time` for the convenience types. Without the suffix, a name such as `Timestamptz` would look like a neutral default when it is one specific kind of value. For the same reason, `Interval`, which reads the object `{ months, days, micros }`, becomes `IntervalParts`, next to the new `IntervalString`. + +7. **Three codecs are added:** a `timestamp` column read as a JavaScript `Date`, and a `timestamp` column read as a `Temporal.Instant`, both reading and writing the stored value as UTC; and an `interval` column read as an ISO 8601 duration string. `DateTime(column: timestamp)` with `value: jsDate` or `value: temporal`, and `Duration` with `value: string`, use them. +8. **The Prisma 7 reader maps Prisma 7's `DateTime` to a `timestamp(3)` column read as a JavaScript `Date`,** the new codec, instead of a string. A Prisma 7 `DateTime` is a `timestamp(3)` column read as a `Date`, so an upgraded application keeps the values its code was written for. +9. **A string value is ISO 8601, the same on every server.** Every string codec of a date or time type returns the form its data type stores (see ADR 254, "Date and time types"), whatever the database session's `TimeZone` and `DateStyle`, and reads that form back unchanged. PostgreSQL's display text, such as `2024-01-01 00:00:00+00` or a ` BC` year, never reaches the application. +10. **`contract infer` and `contract print` write convenience types,** with arguments only where they differ from the defaults. A column no convenience type covers, a `timestamp` read as `Temporal.PlainDateTime`, is written as its explicit type. `prisma orm init` writes `DateTime`. +11. **A convenience name means the same thing on every target.** This decision covers PostgreSQL. Another target adds convenience types under the same names, and renames any explicit type that already uses one of them for something else. + +## Why + +Prisma 8 gives users explicit, predictable controls first, then makes common cases easy by building convenience APIs on top of them. For example, the `postgres({ contract, url })` function builds the runtime, adapter and driver that a user could otherwise create and connect by hand. The date and time types have the explicit controls but nothing built on top of them. + +In Prisma 7, every date or time field is `DateTime`, and the application always reads a JavaScript `Date`. A `@db.*` attribute changes the column type. A user who never learns those attributes still gets a working field. That was simple, but Prisma made the choice for the user, and for some fields it was wrong. A `Date` is a moment in time, not a calendar date: a birthday created at local midnight on a machine east of UTC is stored as the day before. + +Prisma 8's explicit types fix that by making every choice visible. But each name fixes both the column type and the kind of value, so PostgreSQL has eleven date and time type names (`Timestamptz`, `TimestamptzJsDate`, `TimestamptzString`, `Timestamp`, `TimestampString`, `Date`, `DateString`, `Time`, `TimeString`, `Timetz`, and `DateTime` as a second name for `Timestamptz`). Prisma 8 does not say which one a new user should pick. To choose, a user must already know the difference between `timestamp` and `timestamptz`, what each `Temporal` type means, and how each kind of value behaves. A user writing a schema thinks "this field is when the order was paid", not "this is a `timestamptz` column read as a `Temporal.Instant`". + +The convenience types give that user one name per concept, with defaults that work. Every explicit choice stays available, as an argument or as the explicit type itself. + +## How it works + +### A type constructor chooses its codec + +A type constructor's output is an `AuthoringStorageTypeTemplate`: a `codecId`, and a `typeParams` template whose values may refer to the constructor's arguments. Today `codecId` is a plain string: + +```ts +Timestamptz: { + kind: 'typeConstructor', + documentation: 'An instant represented as Temporal.Instant, with optional fractional-second precision.', + args: [{ kind: 'number', name: 'precision', integer: true, optional: true }], + output: { + codecId: 'pg/timestamptz-temporal@1', + typeParams: { precision: { kind: 'arg', index: 0 } }, + }, +}, +``` + +A convenience constructor's `codecId` is chosen by its `value` and `column` arguments. ADR 246 introduced the two pieces this needs, for the `temporal.*` field presets such as `temporal.timestamp(3, onCreate: now)`: + +- an argument that takes one of a fixed set of bare names (`AuthoringOption`, `kind: 'option'`); +- a template value that picks one case by the value of such an argument (`AuthoringSelectRef`, `kind: 'select'`). + +ADR 275 removes both together with the presets. This decision keeps them, for type constructors, and lets a `select` give a type constructor's `codecId`. That is more than a new case: + +- `codecId` is typed `string` in `AuthoringStorageTypeTemplate`, and many places read it as a plain string: the control stack, the PSL column resolution, the attribute specs, the generator attributes, the inferred-constructor map, the Prisma 7 reader and the MongoDB interpreter. Each must resolve the template against the call's arguments first. +- The TypeScript contract builder works out a column's application type at compile time from its `codecId`. Its type-level resolver, `ResolveTemplateValue`, cannot evaluate a `select`; ADR 246 bars `select` from a preset's `codecId` for that reason. The resolver must learn to evaluate it, so that a convenience type written in a TypeScript contract gets an application type as precise as an explicit one. A TypeScript contract reaches the convenience types through the `type.*` helpers that ADR 275 gives every PSL type constructor. + +A combination a convenience type does not list, such as `Date(value: jsDate)`, is an error. The PSL interpreter reports it at the argument when it reads the schema, and the message lists the choices the type accepts. + +### What the contract and the tools see + +The PSL interpreter resolves a convenience type to its explicit type's codec and parameters while it builds the contract, so the contract only ever holds the explicit type. The migration planner, `db verify` and the runtime read the contract and are unchanged. + +`contract infer` and `contract print` write a schema from a database or a contract, where nothing records which layer the user wrote. They write the convenience type: the one whose choices reach the column's codec, with the `value:`, `column:` and `precision:` arguments that differ from its defaults. Each data type marks at most one type constructor `inferred` (ADR 254); a convenience type is the inferred constructor of every data type it reaches, and the explicit type is written only for a codec no convenience type reaches. The printer runs the text it writes back through the interpreter, as it does today, to prove it gives the same codec and parameters. + +### Combinations left out + +- A JavaScript `Date` for a calendar date or a time of day. A `Date` is a moment in time, so a calendar date stored through one depends on the time zone of the machine that made it. That is the Prisma 7 bug this layer must not bring back. +- `Temporal.PlainTime` on a `timetz` column, because it drops the stored UTC offset. +- `Temporal.PlainDateTime`, a date and a time with no time zone. It is rarely what an application means: a field that holds "9am wherever the user is". It stays available as the explicit type `TimestampTemporal`. `DateTime(value: temporal, column: timestamp)` reads a `Temporal.Instant`, treating the stored value as UTC. + +### Created-at and updated-at + +ADR 275 has the PostgreSQL target ship ready-made type aliases under `temporal`, such as `temporal.UpdatedAt`, built on `Timestamptz`, with `String` and `JsDate` variants for the other kinds of value. With this decision they are built on `DateTime` instead, for example `temporal.UpdatedAt = DateTime @onCreate(now()) @onUpdate(now())`, and use its defaults. `now()` picks its generator from the field's final codec, so it writes a `Date`, a `Temporal.Instant` or a string to match. + +The `String` and `JsDate` variants are not needed. A contributed alias takes no arguments (ADR 275), so a user who wants another kind of value writes the attributes on the convenience type, `DateTime(value: string) @onCreate(now()) @onUpdate(now())`, or defines their own alias with that line. + +## Consequences + +- A user can write `DateTime`, `Date` or `Time` and get a working field without knowing PostgreSQL's column types or the `Temporal` API. A user who wants a specific column type or kind of value says so with an argument, or writes the explicit type. +- **Breaking for schemas that write `DateTime`, `Date` or `Time`.** Each name changes meaning from one explicit type to a convenience type, whose defaults may choose a different kind of value. ADR 257's rule for renaming keeps an old name for one release line as a deprecated alias of the new one. That is not possible here, because the old names are reused at once. A schema that wants today's meaning writes the new explicit names (`TimestamptzTemporal`, `DateTemporal`, `TimeTemporal`), and its contract does not change. `Timestamptz` and `Timestamp` follow ADR 257's rule, as deprecated aliases of `TimestamptzTemporal` and `TimestampTemporal` for one release line. +- **Breaking for `contract print`,** which today writes `DateTime` for a `timestamptz` column read as a `Temporal.Instant`. +- **Upgraded Prisma 7 schemas read `Date` values instead of strings.** Their contracts change codec, so each database needs `prisma db sign` after the upgrade. A schema written by an earlier `contract infer` or `prisma orm init` keeps its explicit string types and its contract. +- **String values change form.** An application that reads a string date or time type gets ISO 8601 text instead of PostgreSQL's display text, for example `2024-01-01T00:00:00Z` instead of `2024-01-01 00:00:00+00`. The codec ids and the contract do not change. +- **`Interval` reads the same object as before, under the name `IntervalParts`;** `Duration` reads an ISO string by default. +- ADR 254 says a type constructor names one codec and maps its arguments onto parameters. A type constructor may now also choose the codec. +- ADR 257 says the second codec of a stored type adds a suffix. Where a stored type has several codecs, every one now has a suffix, and the unsuffixed name of the concept belongs to a convenience type. +- ADR 275's removal of the `option` argument kind and the `select` template does not apply to type constructors. Its `String` and `JsDate` alias variants are not built. +- The planned rename of the PostgreSQL and SQLite types, which applies ADR 257 to those targets (for example `Int` to `Int4`), keeps `DateTime` as the convenience type instead of removing it, and adds the renames in item 6. +- The editor's completion offers each convenience type's `value` and `column` choices, and its hover text gives the explicit type each choice becomes. + +## Not decided here + +- SQLite and MongoDB. Each declares its own convenience types over its own explicit types, under item 11, and each already uses one of the names: SQLite's explicit `DateTime` is its `sqlite/datetime@1` type, and MongoDB's explicit `Date` is `mongo/date@1`, a moment in time, with `DateTime` as a deprecated alias of it. +- Whether other scalar types get convenience types, for example an `Int` whose arguments choose between the explicit `Int2`, `Int4` and `Int8`. + +## Alternatives considered + +**Name every type after the application's meaning, with no explicit types.** Every field would be written `DateTime`, `Date` or `Time` and refined by arguments, and the database-named types would go. This reverses ADR 257, and leaves arguments as the only way to state a column type exactly. Two layers keep the database-named types as the precise form and add the short form on top. + +**One codec for `DateTime`, with the column type as a parameter.** A codec converts values of exactly one data type, and a column stores the data type its codec represents (ADR 254). A codec whose data type depends on a parameter breaks both rules. Choosing among existing codecs in the type constructor gives the same schema syntax without changing codecs. + +**Convenience types as contributed type aliases with arguments.** A type alias attaches attributes such as `@onCreate` to one fixed type (ADR 275). Choosing which type is what a type constructor does, and a type constructor already takes arguments. + +**Keep only the explicit types and document which to choose.** This adds nothing to learn, but the schema offers no default, so every user must understand every choice before writing a first field. + +**Keep `Timestamptz` and `Timestamp` unsuffixed and rename only `Date` and `Time`.** It changes fewer names, but `Timestamptz` would remain an unmarked name for one particular kind of value while its neighbours `Date` and `Time` become convenience types, and the explicit types would follow two naming patterns. + +**Name the convenience types after the `Temporal` API: `Instant`, `PlainDate`, `PlainTime`.** The names would say exactly what value the application reads, but they are a library's vocabulary. A user modelling an application thinks "a birthday" or "when the order was paid", and the mapping from `Date` to a calendar date is obvious without it. + +**A Prisma-owned value for calendar dates, times of day and durations, or a branded string type.** A class would be one more date API to learn and convert from, would not survive a trip through JSON, and would be redundant once runtimes ship `Temporal`. A branded string (`string & { __brand }`) makes every write that starts from a plain string, which is almost every write, go through a helper or a cast, to catch a mistake the codec already reports when the value is written. Plain ISO 8601 strings, validated by the codec, have neither cost. diff --git a/projects/datetime-convenience-types/README.md b/projects/datetime-convenience-types/README.md new file mode 100644 index 000000000000..c45f16f74c9f --- /dev/null +++ b/projects/datetime-convenience-types/README.md @@ -0,0 +1,12 @@ +# Convenience date and time types + +Project folder for [Convenience date and time types](https://linear.app/prisma-company/project/convenience-date-and-time-types-6a969ca1c76e). PSL gets `DateTime`, `Date`, `Time` and `Duration`, one type per concept in the application, each choosing one of PostgreSQL's explicit date and time types from its `value:` and `column:` arguments, with defaults that need no Temporal polyfill. + +- [`spec.md`](./spec.md) — purpose, settled decisions, non-goals, cross-cutting requirements, project DoD. +- [`design-notes.md`](./design-notes.md) — the design discussion of 2026-10-21 and 2026-10-22, with the alternatives rejected. +- [`plan.md`](./plan.md) — slice plan. +- `slices//` — per-slice spec, plan and reviews. + +The decision record is [ADR 279](../../docs/architecture%20docs/adrs/ADR%20279%20-%20Convenience%20date%20and%20time%20types%20choose%20an%20explicit%20type%20from%20their%20arguments.md). + +Everything in this folder is transient. At close-out the durable parts move to `docs/` and the ADR, and the folder is deleted. diff --git a/projects/datetime-convenience-types/design-notes.md b/projects/datetime-convenience-types/design-notes.md new file mode 100644 index 000000000000..22f4c25be867 --- /dev/null +++ b/projects/datetime-convenience-types/design-notes.md @@ -0,0 +1,66 @@ +# Design notes — Convenience date and time types + +Record of the design discussion between the operator and the orchestrator on 2026-10-21 and 2026-10-22. It started from a research report comparing how a user declares and reads date and time fields on PostgreSQL in Prisma 7 and Prisma 8. + +## What the research found + +- **Prisma 7** had one type, `DateTime`, whose value was always a JavaScript `Date`. `@db.*` attributes changed the column type. It was easy to learn, but a `Date` is the wrong value for a calendar date or a time of day. A birthday created at local midnight east of UTC was stored as the day before, and a `timetz` offset was lost on read. +- **Prisma 8** has eleven type names for dates and times. Each fixes both the column type and the kind of value. Nothing says which to start with, and each kind of value had a problem: + - The Temporal types fail on Node without a polyfill. The failure comes at the first read, not at startup. A `create` can commit and then throw, and without the type declarations the fields are typed `any`. + - The string types return PostgreSQL's display text, which depends on the server's `TimeZone` and `DateStyle`. Every schema Prisma 8 writes for the user uses them. + - A `Date` is available only for `timestamptz`. + - An upgraded Prisma 7 app goes from `Date` values to strings. + +## Principles + +- **Explicit controls first, convenience on top.** Prisma 8 gives users explicit, predictable controls, then makes the common cases easy by composing convenience APIs over them, as the client facades compose the runtime, adapter and driver. The date and time types had the first half only. +- **The schema describes the application's data in the application's terms;** database details are configuration (architecture overview, "Domain-first surfaces"). +- **A convenience API never hides a choice the explicit layer exposes.** Every convenience form becomes one explicit type, and the contract records only that. + +## Decisions and why + +**Convenience types over explicit types, not a renaming.** An early version of the proposal renamed every type after the application's meaning, which would have reversed ADR 257. The operator reframed it as layering: the database-named types stay as the precise form, and the new types are the short form built on them. Nothing is reversed, and a user who needs a specific column type and value kind keeps writing the explicit type. + +**A type constructor chooses the codec.** One codec for `DateTime` with the database type as a parameter was rejected: a codec represents values of exactly one data type, and a column stores the data type its codec represents (ADR 254). A type alias with arguments was rejected: an alias attaches attributes to one fixed type, while choosing a type from arguments is what a type constructor does. Type constructors already take arguments. + +**Names follow the application's concepts: `DateTime`, `Date`, `Time`, `Duration`.** Naming them after the Temporal types (`Instant`, `PlainDate`, `PlainTime`) was rejected as library vocabulary, not domain vocabulary: a user thinks "a birthday", not "a plain date". The mapping from each name to its value must be obvious. + +**The explicit Temporal types get a suffix.** `Date` and `Time` were explicit types (Temporal-valued), and the convenience layer needed the names. Renaming only those two would leave `Timestamptz` as an unmarked name for one kind of value next to suffixed neighbours. So all four Temporal-valued explicit types carry `Temporal`, as the others carry `String` and `JsDate`. The orchestrator first claimed that today's `Date` and `Time` broke ADR 257's naming rule; that was wrong. ADR 257 suffixes only the second codec of a stored type, so this is a refinement of the rule, recorded as an amendment. + +**Defaults need no setup.** The operator's rule: pick a default, and the other options must be possible. `DateTime` defaults to a JavaScript `Date`: it works everywhere and matches Prisma 7. `Date`, `Time` and `Duration` default to ISO 8601 strings, because JavaScript has no built-in calendar-date or time-of-day value, and a `Date` there causes the wrong-day bug. + +**Plain strings, not a Prisma value class or a branded string.** A class owned by Prisma was rejected: users would learn another date API, it would not survive JSON across a server-client boundary, and Temporal will make it redundant. A branded string (`string & { __brand }`) was rejected: almost every written value starts as a plain string from a form or a request, so every write would need a helper or a cast, to catch a mistake the codec already reports at runtime. + +**`Duration` defaults to an ISO duration string.** Today's `{ months, days, micros: bigint }` breaks `JSON.stringify`, and the contract already stores interval defaults as ISO durations. The object stays available as `value: parts`. + +**`@db.*` does not come back.** Storage options are arguments in the type position, which agrees with ADR 241. + +**`contract infer` and `contract print` write convenience types,** with arguments only where they differ from the defaults, so a schema the tools write reads like one a user would write. Only `Temporal.PlainDateTime`, which no convenience type covers, prints as its explicit type. + +**The Prisma 7 reader gives back `Date`.** Prisma 7's `DateTime` is a `timestamp(3)` column read as a `Date`. Mapping it to `DateTime(column: timestamp)` keeps an upgraded app's code compiling. This reverses the choice made when inferred and Prisma 7 columns moved to string types, which rejected `Date` only because no `timestamp` `Date` codec existed. + +**TypeScript follows the `type.*` helpers.** The field-preset removal project gives every PSL type constructor a `type.*` helper. This project makes the builder's compile-time resolver evaluate a codec chosen by arguments, so `type.DateTime(...)` types precisely once the helpers land, instead of building a separate TypeScript surface now. + +**PostgreSQL only.** A convenience name must mean the same on every target. SQLite's `DateTime` and MongoDB's `Date` already use the names for explicit types, and MongoDB's `Date` is a moment in time. Resolving them is TML-3714. + +## Alternatives rejected + +- Rename every scalar after its application meaning, with no explicit layer (reverses ADR 257; loses the precise form). +- One parameterized codec per concept (breaks the one-data-type-per-codec rule). +- Type aliases with arguments (aliases attach attributes, they don't choose types). +- Bring back `@db.*` for storage options (the decision to select storage only in type position stands). +- Names from the Temporal API (`Instant`, `PlainDate`): library vocabulary, not domain vocabulary. +- A Prisma-owned date and time value class, or a branded string (see above). +- Document the explicit types and add no convenience layer (no default; every user must understand every choice first). +- Keep `Timestamptz` and `Timestamp` unsuffixed and rename only `Date` and `Time` (two naming patterns in one layer). + +## Open questions + +- Codec ids and explicit names of the two new `timestamp` codecs (slice 3). +- Whether slice 1 sets session settings, normalises in the codecs, or both. + +## References + +- ADR 279 (draft) and ADR 241, 246, 254, 257, 275. +- Architecture overview: "Domain-first surfaces", "Explicit over implicit". +- The research report (private artifact, 2026-10-21). diff --git a/projects/datetime-convenience-types/plan.md b/projects/datetime-convenience-types/plan.md new file mode 100644 index 000000000000..ad3419275ba2 --- /dev/null +++ b/projects/datetime-convenience-types/plan.md @@ -0,0 +1,52 @@ +# Plan — Convenience date and time types + +**Spec:** [`spec.md`](./spec.md) · **Linear:** [Convenience date and time types](https://linear.app/prisma-company/project/convenience-date-and-time-types-6a969ca1c76e) + +## Summary + +Five slices. Slices 1, 2 and 3 are independent and can run in parallel. Slice 4 builds on slice 3, and slice 5 on slice 4. + +Five is one over the usual ceiling. Slices 1 and 2 are fixes the report found that any naming would need; they share the project because they serve the same purpose (date and time values that are predictable), not because the other slices depend on them. Folding either into another slice would make that slice two unrelated reviews. + +## Slices + +| # | Slice | Linear | Builds on | Hands to | +| --- | --- | --- | --- | --- | +| 1 | String date and time types read ISO 8601 that does not depend on the server | [TML-3418](https://linear.app/prisma-company/issue/TML-3418) | — | String codecs whose value is the ADR 254 stored form, for `Date`, `Time` and `Duration` to default to | +| 2 | A missing Temporal is reported when the client is created, and Temporal fields are never typed `any` | [TML-3710](https://linear.app/prisma-company/issue/TML-3710) | — | Temporal opt-ins that fail early and type precisely | +| 3 | Explicit types carry a `Temporal` suffix; `timestamp` and `interval` get the missing codecs | [TML-3711](https://linear.app/prisma-company/issue/TML-3711) | — | A complete explicit layer: every cell of the spec's decision 3 table has an explicit type, and `TimestamptzTemporal`, `TimestampTemporal`, `DateTemporal`, `TimeTemporal`, `IntervalParts`, `IntervalString` exist | +| 4 | PSL convenience types `DateTime`, `Date`, `Time` and `Duration` | [TML-3712](https://linear.app/prisma-company/issue/TML-3712) | 3 | Type constructors that choose their codec; the four convenience types; the TypeScript resolver evaluating the choice | +| 5 | `contract infer`, `contract print`, the Prisma 7 reader and `orm init` write the convenience types | [TML-3713](https://linear.app/prisma-company/issue/TML-3713) | 4 | Adopted and new projects read `Date` timestamps with no polyfill | + +### Slice 1 — ISO strings + +`pg/timestamptz-string@1`, `pg/timestamp-string@1`, `pg/date-string@1`, `pg/time-string@1` and `pg/timetz@1` return the ISO 8601 form ADR 254 stores, on any server `TimeZone` and `DateStyle`, BC years and years past 9999 included; `infinity` stays `infinity`. Covers the related case in the ticket: a database `now()` default on a `timestamp` column and the client's `now()` generator store the same clock. Amends ADR 254's "Date and time types" paragraph on text codecs. + +### Slice 2 — Temporal at startup + +Creating a client whose contract uses a Temporal codec, in a process without a global `Temporal`, fails with one error that names the fix. The generated `contract.d.ts` and the TypeScript builder's inferred types reference the Temporal types so that a missing declaration is a type error, not `any`. + +### Slice 3 — The explicit layer + +Renames with deprecated aliases where the name is not reused: `Timestamptz` → `TimestamptzTemporal`, `Timestamp` → `TimestampTemporal`, `Interval` → `IntervalParts`. New explicit names `DateTemporal` and `TimeTemporal` beside today's `Date` and `Time`, which keep their meaning until slice 4. New codecs and explicit types: a `timestamp` column read as a `Date`, and as a `Temporal.Instant` (both UTC), and `IntervalString`. Each new codec passes the codec conformance kit and the three codec probes (dod.md). + +### Slice 4 — The convenience types + +A type constructor's `codecId` may be a `select` over `option` arguments; every reader of a constructor's output resolves it. The four types with the spec's choices and defaults; an unlisted combination is an error at the argument. `DateTime`, `Date` and `Time` take over their names (breaking, with upgrade instructions). The TypeScript builder's type-level resolver evaluates the choice. Hover text and completion list each type's choices. ADR 279 moves to Accepted, with notes in ADR 246, 254, 257 and 275. + +### Slice 5 — The tools write convenience types + +`contract infer` and `contract print` write convenience types, with arguments only where they differ from the defaults, and the explicit type only where no convenience type fits. The Prisma 7 reader maps `DateTime` to `DateTime(column: timestamp)` and `@db.*` date and time columns to the matching convenience form. `prisma orm init` writes `DateTime`. Upgrade instructions: re-emit and `db sign` for adopted projects. + +## Dependencies + +- **Field-preset removal project, slice 2.** Delivers the `type.*` helpers that expose slice 4's types in TypeScript, and the contributed aliases built on `DateTime`. Not blocking; coordinate so its `temporal.CreatedAt` and `temporal.UpdatedAt` land on `DateTime` once slice 4 merges. +- **PostgreSQL and SQLite rename project** (not started). Must not remove `DateTime` and must take slice 3's names. Its spec gets a note in this project's shaping PR. +- **Release.** Users get the convenience types with the first `@prisma/orm-postgres` release after slice 4 merges. + +## Close-out (required) + +- [ ] Verify the project DoD in [`spec.md`](./spec.md). +- [ ] ADR 279 accepted; `docs/reference/scalar-types.md` describes both layers. +- [ ] Strip repo-wide references to `projects/datetime-convenience-types/**`. +- [ ] Delete `projects/datetime-convenience-types/`. diff --git a/projects/datetime-convenience-types/spec.md b/projects/datetime-convenience-types/spec.md new file mode 100644 index 000000000000..5b23ca86cb5e --- /dev/null +++ b/projects/datetime-convenience-types/spec.md @@ -0,0 +1,120 @@ +# Project spec — Convenience date and time types + +**Linear:** [Convenience date and time types](https://linear.app/prisma-company/project/convenience-date-and-time-types-6a969ca1c76e) ([TML-3709](https://linear.app/prisma-company/issue/TML-3709)) · **Branch:** `tml-3709-plan-datetime-convenience-types` · **Design record:** [ADR 279 — Convenience date and time types choose an explicit type from their arguments](../../docs/architecture%20docs/adrs/ADR%20279%20-%20Convenience%20date%20and%20time%20types%20choose%20an%20explicit%20type%20from%20their%20arguments.md) (Proposed) + +## Purpose + +Prisma 8 gives users explicit, predictable controls first, then makes the common cases easy with convenience APIs built on top. PostgreSQL date and time columns have the explicit half only. Each of eleven type names fixes both the column type and the value the application reads, and nothing says which one to start with. The obvious name, `DateTime`, reads a `Temporal.Instant`, which fails on Node without a polyfill. The string types return text whose format depends on the database server's settings. And an app upgraded from Prisma 7 goes from `Date` values to those strings. + +This project gives a user one type per concept in their application (a moment in time, a calendar date, a time of day, a length of time), with defaults that work on stock Node. Every explicit choice stays available. Along the way, the values every date and time type returns become predictable. + +## At a glance + +```prisma +model Order { + id Int @id + paidAt DateTime // timestamptz(3), read as a JavaScript Date + shipBy Date // date, read as "2024-02-29" + opensAt Time(precision: 0) // time(0), read as "09:30:00" + period Duration // interval, read as "P1M" + settledAt DateTime(value: temporal) // timestamptz, read as a Temporal.Instant + legacyAt DateTime(column: timestamp) // timestamp(3), read as a JavaScript Date, stored as UTC + exactAt TimestamptzTemporal(6) // an explicit type, written directly +} +``` + +`DateTime`, `Date`, `Time` and `Duration` are convenience types. Each becomes one explicit type, chosen by its optional `value:` and `column:` arguments. The contract stores only the explicit type, so `settledAt` is stored exactly as `settledAt TimestamptzTemporal` would be. + +## Decisions settled in discussion (2026-10-21 and 2026-10-22) + +The reasoning and the alternatives rejected are in [`design-notes.md`](./design-notes.md) and ADR 279. + +1. **Two layers.** The explicit types stay, named after the database as ADR 257 says, each fixing one codec. The convenience types are named for what a field means in the application, and each becomes exactly one explicit type. Nothing is reversed: the explicit layer is the precise form, the convenience layer is the short form. +2. **A convenience type is a type constructor whose arguments choose its codec.** Not a codec parameterized by database type (a codec represents one data type, ADR 254), and not a type alias (an alias attaches attributes to one fixed type). +3. **The four PostgreSQL convenience types and their choices:** + + | Type | `value:` | `column:` | Default | + | --- | --- | --- | --- | + | `DateTime` | `jsDate`, `temporal` (`Temporal.Instant`), `string` | `timestamptz`, `timestamp` | `jsDate`, `timestamptz`; precision 3 when the value is `jsDate`, PostgreSQL's default otherwise | + | `Date` | `string`, `temporal` (`Temporal.PlainDate`) | `date` | `string` | + | `Time` | `string`, `temporal` (`Temporal.PlainTime`) | `time`, `timetz` (with `string` only) | `string`, `time` | + | `Duration` | `string` (ISO 8601 duration), `parts` (`{ months, days, micros }`) | `interval` | `string` | + + The value names repeat the suffixes of the explicit types (`TimestamptzJsDate`, `TimestamptzTemporal`, `TimestamptzString`). +4. **Defaults need no setup.** No default needs a Temporal polyfill. `DateTime` defaults to a JavaScript `Date`, which is also what Prisma 7 users have. `Date`, `Time` and `Duration` default to plain ISO 8601 strings. A JavaScript `Date` is never offered for a calendar date or a time of day. +5. **Plain strings, no brand and no Prisma value class.** A brand or class adds friction on every write and does not survive JSON. The codec validates the format on write. +6. **Every explicit type of a column with several codecs carries a suffix.** `Timestamptz`, `Timestamp`, `Date` and `Time` become `TimestamptzTemporal`, `TimestampTemporal`, `DateTemporal` and `TimeTemporal`. `Interval` becomes `IntervalParts`, next to the new `IntervalString`. +7. **Four codecs are added:** a `timestamp` column read as a `Date` and as a `Temporal.Instant`, both treating the stored value as UTC; and an `interval` column read as an ISO 8601 duration string. (The `parts` codec already exists.) +8. **String values are ISO 8601, the same on every server.** Every string codec returns the form ADR 254 stores, not PostgreSQL's display text. +9. **A missing Temporal is reported when the client is created,** and Temporal fields are never typed `any`. +10. **`contract infer` and `contract print` write convenience types**, with arguments only where they differ from the defaults; a column no convenience type covers (`Temporal.PlainDateTime`) is written as its explicit type. **The Prisma 7 reader** maps Prisma 7's `DateTime` to `DateTime(column: timestamp)`, so an upgraded app reads `Date` values again. +11. **PostgreSQL only.** A convenience name means the same thing on every target; SQLite and MongoDB follow in [TML-3714](https://linear.app/prisma-company/issue/TML-3714). +12. **TypeScript:** this project ships the PSL types and makes the TypeScript builder's compile-time resolver evaluate a codec chosen by arguments. The `type.DateTime(...)` helper itself arrives with the `type.*` helpers of the field-preset removal project. + +## Non-goals + +- SQLite and MongoDB convenience types ([TML-3714](https://linear.app/prisma-company/issue/TML-3714)). +- Convenience types for other scalars, such as an `Int` choosing between `Int2`, `Int4` and `Int8` ([TML-3715](https://linear.app/prisma-company/issue/TML-3715)). +- The `type.*` helpers in the TypeScript builder (field-preset removal project, its second slice). +- `temporal.CreatedAt` and `temporal.UpdatedAt`. They belong to the field-preset removal project; this project only says they are built on `DateTime` (ADR 279). +- A `Temporal.PlainDateTime` convenience form. It stays reachable as the explicit `TimestampTemporal`. +- Bundling a Temporal polyfill. +- Any change to how values are stored: the contract form of a value (ADR 254) and every existing codec's stored form stay as they are. + +## Place in the larger world + +- **ADR 254, data types and casts.** Codecs, data types and stored forms are unchanged. A type constructor gains the ability to choose its codec, which amends the definition there. The text codecs today keep PostgreSQL's year text (` BC`); decision 8 changes that to the ISO form, which amends the "Date and time types" section. +- **ADR 257, scalar names.** The explicit layer follows it. The `Temporal` suffix applies the suffix rule to every codec of a multi-codec column, and `Date`, `Time` and `DateTime` are reused at once, which its one-release deprecation rule cannot cover. +- **ADR 246 and ADR 275.** The `option` argument kind and the `select` template, which ADR 275 removes with the presets, are kept for type constructors. +- **The PostgreSQL and SQLite rename project** (`projects/target-named-scalars-sql/`, not started) must keep `DateTime` and take this project's renames instead of its own `DateTime` and `Timestamptz` rows. +- **The field-preset removal project** (`projects/remove-field-presets/`, slice 1 merged): its `type.*` helpers carry the convenience types into TypeScript, and its contributed aliases are built on `DateTime`; its `String` and `JsDate` alias variants are not needed. +- **`prisma orm init`** writes `TimestamptzString` and `temporal.updatedAtString()` today. It moves to the convenience types in slice 5. + +## Cross-cutting requirements + +1. **The contract records only explicit types.** A schema written with a convenience type and the same schema written with the explicit type it becomes emit byte-identical `contract.json` and equal storage and execution hashes. +2. **Every argument combination is either a listed choice or an error at the argument** that names the accepted choices. No combination silently picks a codec. +3. **Every default works on Node 24 with no polyfill.** A test runs each convenience type's default in a process with no global `Temporal`. +4. **A string value round-trips.** A string written through a string codec reads back as the same string, on a server whose `TimeZone` is not UTC and whose `DateStyle` is not ISO. +5. **Renamed explicit names follow ADR 257's rename path** where the old name is not reused (`Timestamptz`, `Timestamp`, `Interval`: deprecated aliases, `PSL_DEPRECATED_SCALAR_NAME`). Where it is reused (`DateTime`, `Date`, `Time`), the change is breaking and the upgrade instructions say so. +6. **Every user-visible change ships upgrade instructions** in the same PR (`skills-contrib/record-upgrade-instructions`). +7. **Docs move with the code.** `docs/reference/scalar-types.md`, the error reference, the editor hover text and the codec authoring guide are updated in the slice that changes what they describe. + +## Transitional-shape constraints + +- Until slice 4 lands, `DateTime`, `Date` and `Time` keep today's explicit meaning. Slice 3 adds the `Temporal`-suffixed names beside them, so slice 3 alone breaks nothing; slice 4 takes the old names over. +- Slice 5 changes what `contract infer` and the Prisma 7 reader write, and so the codec of adopted columns. It ships with the upgrade instruction to re-emit and `db sign`. + +## Contract-impact + +No new contract entities or kinds. New codec ids: `pg/timestamp-date@1`, `pg/timestamp-instant@1` (names settled in slice 3) and `pg/interval-string@1`. Contracts that use the existing codecs do not change. Contracts emitted by `contract infer` and `prisma7Schema` after slice 5 use different codecs for date and time columns. + +## Adapter-impact + +PostgreSQL target and adapter only: data-type-bound codecs, type constructors, the runtime connection setup (slice 1), the Prisma 7 reader and `contract infer`'s type map. The framework authoring layer changes (codec choice in a type constructor); the SQL family's PSL column resolution and the TypeScript builder's type-level resolver follow. SQLite and MongoDB are untouched. + +## ADR pointer + +ADR 279 (draft in this project's shaping PR, status Proposed until slice 4 merges). It amends ADR 254, 257 and 275 and brings back ADR 246 for type constructors; each amended ADR gets a note in the slice that implements the change. + +## Project DoD + +Inherits the team floor in [`drive/calibration/dod.md`](../../drive/calibration/dod.md). In addition: + +- [ ] Every row of the decision 3 table works end to end: a PSL schema emits, `migration plan` produces the expected column DDL, and a query on PostgreSQL reads and writes the expected value type, with and without a global `Temporal` as each row requires. +- [ ] A new project from `prisma orm init`, and a Prisma 7 project adopted with `prisma7Schema(...)`, run their first query on Node with no polyfill and read `Date` values for their timestamps. +- [ ] The report's use-case table (see References) re-run against the shipped surface, with each row's Prisma 8 cell updated. +- [ ] ADR 279 accepted; ADR 246, 254, 257 and 275 carry notes; `docs/reference/scalar-types.md` describes both layers. +- [ ] TML-3714 and TML-3715 remain open with their context. + +## Open questions + +- The codec ids and explicit names of the two new `timestamp` codecs (slice 3). +- Whether the runtime sets `TimeZone` and `DateStyle` on its connections, or the string codecs normalise what they read, or both (slice 1). + +## References + +- ADR 279 (draft) and its amended ADRs: 241, 246, 254, 257, 275. +- The research report comparing Prisma 7 and Prisma 8 date and time fields on PostgreSQL (2026-10-21, published as a private artifact; ask the operator). +- [TML-3418](https://linear.app/prisma-company/issue/TML-3418), the server-dependent string bug, now slice 1. +- The pull request that moved inferred and Prisma 7 date columns to string types: prisma/orm#30553. diff --git a/projects/target-named-scalars-sql/README.md b/projects/target-named-scalars-sql/README.md index 255fd4e844f3..e3fa163aec87 100644 --- a/projects/target-named-scalars-sql/README.md +++ b/projects/target-named-scalars-sql/README.md @@ -2,4 +2,6 @@ Shaped 2026-09-25, not started; spec input from the Mongo defaults, codecs and Prisma 6 source project (merged 2026-09-25 to 2026-09-27). See [`spec.md`](./spec.md) for the rename tables, the `Json` preset split, `Json` refusing non-JSON values on PostgreSQL and SQLite, the enum storage-type rule moving into the framework, the upgrade fragments and the documentation changes. The decision it applies is [ADR 257](../../docs/architecture%20docs/adrs/ADR%20257%20-%20Scalar%20types%20are%20named%20after%20the%20target%20on%20every%20surface.md). +> **Changed by the Convenience date and time types project (2026-10-22).** [ADR 279](../../docs/architecture%20docs/adrs/ADR%20279%20-%20Convenience%20date%20and%20time%20types%20choose%20an%20explicit%20type%20from%20their%20arguments.md) (Proposed) keeps `DateTime` as a convenience type and renames the PostgreSQL date and time types itself: `Timestamptz` → `TimestamptzTemporal`, `Timestamp` → `TimestampTemporal`, `Date` → `DateTemporal`, `Time` → `TimeTemporal`, `Interval` → `IntervalParts`. The `DateTime` and `Timestamptz` rows of this spec's Postgres table, and `field.dateTime()` → `field.timestamptz()`, are superseded; see `projects/datetime-convenience-types/`. + > Everything under `projects/` is transient — migrate long-lived architecture and upgrade documentation to `docs/`, remove repo-wide references to this workspace, and delete it at project close-out per [`projects/README.md`](../README.md). From 0d41c692af28faf0d8c35301bc05e26be393f5cf Mon Sep 17 00:00:00 2001 From: willbot Date: Thu, 22 Oct 2026 15:06:12 +0200 Subject: [PATCH 002/118] TML-3709: ADR 279 names the explicit types of the new codecs and records the codec-version question Co-Authored-By: Claude Opus 5.5 Signed-off-by: willbot Signed-off-by: Will Madden --- ... time types choose an explicit type from their arguments.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/docs/architecture docs/adrs/ADR 279 - Convenience date and time types choose an explicit type from their arguments.md b/docs/architecture docs/adrs/ADR 279 - Convenience date and time types choose an explicit type from their arguments.md index b8234dfeefbd..76557946b10e 100644 --- a/docs/architecture docs/adrs/ADR 279 - Convenience date and time types choose an explicit type from their arguments.md +++ b/docs/architecture docs/adrs/ADR 279 - Convenience date and time types choose an explicit type from their arguments.md @@ -58,7 +58,7 @@ The contract stores what the explicit type stores: the codec, its data type and This frees `DateTime`, `Date` and `Time` for the convenience types. Without the suffix, a name such as `Timestamptz` would look like a neutral default when it is one specific kind of value. For the same reason, `Interval`, which reads the object `{ months, days, micros }`, becomes `IntervalParts`, next to the new `IntervalString`. -7. **Three codecs are added:** a `timestamp` column read as a JavaScript `Date`, and a `timestamp` column read as a `Temporal.Instant`, both reading and writing the stored value as UTC; and an `interval` column read as an ISO 8601 duration string. `DateTime(column: timestamp)` with `value: jsDate` or `value: temporal`, and `Duration` with `value: string`, use them. +7. **Three codecs are added:** a `timestamp` column read as a JavaScript `Date`, and a `timestamp` column read as a `Temporal.Instant`, both reading and writing the stored value as UTC; and an `interval` column read as an ISO 8601 duration string. Each gets an explicit type: `TimestampJsDate(p)` for the `Date` codec, `TimestampInstant(p)` for the `Temporal.Instant` codec, and `IntervalString(p)`. `DateTime(column: timestamp)` with `value: jsDate` becomes `TimestampJsDate`, with `value: temporal` becomes `TimestampInstant`, and `Duration` with `value: string` becomes `IntervalString`. `TimestampInstant` names its `Temporal` type rather than taking the `Temporal` suffix, because the `timestamp` column has two `Temporal` codecs and `TimestampTemporal` is the one that reads `Temporal.PlainDateTime`. 8. **The Prisma 7 reader maps Prisma 7's `DateTime` to a `timestamp(3)` column read as a JavaScript `Date`,** the new codec, instead of a string. A Prisma 7 `DateTime` is a `timestamp(3)` column read as a `Date`, so an upgraded application keeps the values its code was written for. 9. **A string value is ISO 8601, the same on every server.** Every string codec of a date or time type returns the form its data type stores (see ADR 254, "Date and time types"), whatever the database session's `TimeZone` and `DateStyle`, and reads that form back unchanged. PostgreSQL's display text, such as `2024-01-01 00:00:00+00` or a ` BC` year, never reaches the application. 10. **`contract infer` and `contract print` write convenience types,** with arguments only where they differ from the defaults. A column no convenience type covers, a `timestamp` read as `Temporal.PlainDateTime`, is written as its explicit type. `prisma orm init` writes `DateTime`. @@ -138,6 +138,7 @@ The `String` and `JsDate` variants are not needed. A contributed alias takes no ## Not decided here +- Whether the string codecs keep their ids when their application value changes from PostgreSQL's display text to ISO 8601 (item 9), or the ISO behaviour ships as new codec versions (`pg/timestamptz-string@2` and so on) with the old versions kept for existing contracts and an upgrade path between them. - SQLite and MongoDB. Each declares its own convenience types over its own explicit types, under item 11, and each already uses one of the names: SQLite's explicit `DateTime` is its `sqlite/datetime@1` type, and MongoDB's explicit `Date` is `mongo/date@1`, a moment in time, with `DateTime` as a deprecated alias of it. - Whether other scalar types get convenience types, for example an `Int` whose arguments choose between the explicit `Int2`, `Int4` and `Int8`. From 9e6ca0f56171b3f1b7dbf5923399118d47b40b41 Mon Sep 17 00:00:00 2001 From: willbot Date: Thu, 22 Oct 2026 15:14:12 +0200 Subject: [PATCH 003/118] TML-3709: ADR 279 names the exact type the Prisma 7 reader writes for DateTime Co-Authored-By: Claude Opus 5.5 Signed-off-by: willbot Signed-off-by: Will Madden --- ...d time types choose an explicit type from their arguments.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/architecture docs/adrs/ADR 279 - Convenience date and time types choose an explicit type from their arguments.md b/docs/architecture docs/adrs/ADR 279 - Convenience date and time types choose an explicit type from their arguments.md index 76557946b10e..2ce79df16557 100644 --- a/docs/architecture docs/adrs/ADR 279 - Convenience date and time types choose an explicit type from their arguments.md +++ b/docs/architecture docs/adrs/ADR 279 - Convenience date and time types choose an explicit type from their arguments.md @@ -59,7 +59,7 @@ The contract stores what the explicit type stores: the codec, its data type and This frees `DateTime`, `Date` and `Time` for the convenience types. Without the suffix, a name such as `Timestamptz` would look like a neutral default when it is one specific kind of value. For the same reason, `Interval`, which reads the object `{ months, days, micros }`, becomes `IntervalParts`, next to the new `IntervalString`. 7. **Three codecs are added:** a `timestamp` column read as a JavaScript `Date`, and a `timestamp` column read as a `Temporal.Instant`, both reading and writing the stored value as UTC; and an `interval` column read as an ISO 8601 duration string. Each gets an explicit type: `TimestampJsDate(p)` for the `Date` codec, `TimestampInstant(p)` for the `Temporal.Instant` codec, and `IntervalString(p)`. `DateTime(column: timestamp)` with `value: jsDate` becomes `TimestampJsDate`, with `value: temporal` becomes `TimestampInstant`, and `Duration` with `value: string` becomes `IntervalString`. `TimestampInstant` names its `Temporal` type rather than taking the `Temporal` suffix, because the `timestamp` column has two `Temporal` codecs and `TimestampTemporal` is the one that reads `Temporal.PlainDateTime`. -8. **The Prisma 7 reader maps Prisma 7's `DateTime` to a `timestamp(3)` column read as a JavaScript `Date`,** the new codec, instead of a string. A Prisma 7 `DateTime` is a `timestamp(3)` column read as a `Date`, so an upgraded application keeps the values its code was written for. +8. **The Prisma 7 reader maps Prisma 7's `DateTime` to `TimestampJsDate(3)`,** which `contract print` writes as `DateTime(column: timestamp)`: a `timestamp(3)` column read as a JavaScript `Date`, instead of a string. The column and its stored values do not change; only the application value goes from a string back to the `Date` that Prisma 7 gave, so an upgraded application keeps the values its code was written for. `prisma orm init`, which creates new columns, writes plain `DateTime`, a `timestamptz(3)` column read as a `Date`. 9. **A string value is ISO 8601, the same on every server.** Every string codec of a date or time type returns the form its data type stores (see ADR 254, "Date and time types"), whatever the database session's `TimeZone` and `DateStyle`, and reads that form back unchanged. PostgreSQL's display text, such as `2024-01-01 00:00:00+00` or a ` BC` year, never reaches the application. 10. **`contract infer` and `contract print` write convenience types,** with arguments only where they differ from the defaults. A column no convenience type covers, a `timestamp` read as `Temporal.PlainDateTime`, is written as its explicit type. `prisma orm init` writes `DateTime`. 11. **A convenience name means the same thing on every target.** This decision covers PostgreSQL. Another target adds convenience types under the same names, and renames any explicit type that already uses one of them for something else. From 0273369ebcece261acbafd3ffd94a61feed0e82c Mon Sep 17 00:00:00 2001 From: willbot Date: Thu, 22 Oct 2026 17:00:00 +0200 Subject: [PATCH 004/118] TML-3709: Record the 2026-10-22 decisions: ISO string codecs keep their ids, DateStyle=ISO is a connection requirement, no deprecated aliases before RC, Temporal types from temporal-spec, and the Prisma 7 now() fix as slice 2 Co-Authored-By: Claude Opus 5.5 Signed-off-by: willbot Signed-off-by: Will Madden --- ...ose an explicit type from their arguments.md | 6 +++--- .../datetime-convenience-types/design-notes.md | 9 +++++++-- projects/datetime-convenience-types/plan.md | 14 +++++++------- projects/datetime-convenience-types/spec.md | 17 ++++++++--------- 4 files changed, 25 insertions(+), 21 deletions(-) diff --git a/docs/architecture docs/adrs/ADR 279 - Convenience date and time types choose an explicit type from their arguments.md b/docs/architecture docs/adrs/ADR 279 - Convenience date and time types choose an explicit type from their arguments.md index 2ce79df16557..3ca98bc58402 100644 --- a/docs/architecture docs/adrs/ADR 279 - Convenience date and time types choose an explicit type from their arguments.md +++ b/docs/architecture docs/adrs/ADR 279 - Convenience date and time types choose an explicit type from their arguments.md @@ -60,7 +60,7 @@ The contract stores what the explicit type stores: the codec, its data type and 7. **Three codecs are added:** a `timestamp` column read as a JavaScript `Date`, and a `timestamp` column read as a `Temporal.Instant`, both reading and writing the stored value as UTC; and an `interval` column read as an ISO 8601 duration string. Each gets an explicit type: `TimestampJsDate(p)` for the `Date` codec, `TimestampInstant(p)` for the `Temporal.Instant` codec, and `IntervalString(p)`. `DateTime(column: timestamp)` with `value: jsDate` becomes `TimestampJsDate`, with `value: temporal` becomes `TimestampInstant`, and `Duration` with `value: string` becomes `IntervalString`. `TimestampInstant` names its `Temporal` type rather than taking the `Temporal` suffix, because the `timestamp` column has two `Temporal` codecs and `TimestampTemporal` is the one that reads `Temporal.PlainDateTime`. 8. **The Prisma 7 reader maps Prisma 7's `DateTime` to `TimestampJsDate(3)`,** which `contract print` writes as `DateTime(column: timestamp)`: a `timestamp(3)` column read as a JavaScript `Date`, instead of a string. The column and its stored values do not change; only the application value goes from a string back to the `Date` that Prisma 7 gave, so an upgraded application keeps the values its code was written for. `prisma orm init`, which creates new columns, writes plain `DateTime`, a `timestamptz(3)` column read as a `Date`. -9. **A string value is ISO 8601, the same on every server.** Every string codec of a date or time type returns the form its data type stores (see ADR 254, "Date and time types"), whatever the database session's `TimeZone` and `DateStyle`, and reads that form back unchanged. PostgreSQL's display text, such as `2024-01-01 00:00:00+00` or a ` BC` year, never reaches the application. +9. **A string value is ISO 8601, the same on every server.** Every string codec of a date or time type returns the form its data type stores (see ADR 254, "Date and time types") and reads that form back unchanged. PostgreSQL's display text, such as `2024-01-01 00:00:00+00` or a ` BC` year, never reaches the application. The codecs convert any time zone offset to UTC themselves. Prisma requires `DateStyle=ISO` on its database connections: it sets it on the connections it opens, and refuses a value it reads in another style with an error naming the setting. The codec ids do not change; the old text was never a format an application could rely on. 10. **`contract infer` and `contract print` write convenience types,** with arguments only where they differ from the defaults. A column no convenience type covers, a `timestamp` read as `Temporal.PlainDateTime`, is written as its explicit type. `prisma orm init` writes `DateTime`. 11. **A convenience name means the same thing on every target.** This decision covers PostgreSQL. Another target adds convenience types under the same names, and renames any explicit type that already uses one of them for something else. @@ -125,10 +125,11 @@ The `String` and `JsDate` variants are not needed. A contributed alias takes no ## Consequences - A user can write `DateTime`, `Date` or `Time` and get a working field without knowing PostgreSQL's column types or the `Temporal` API. A user who wants a specific column type or kind of value says so with an argument, or writes the explicit type. -- **Breaking for schemas that write `DateTime`, `Date` or `Time`.** Each name changes meaning from one explicit type to a convenience type, whose defaults may choose a different kind of value. ADR 257's rule for renaming keeps an old name for one release line as a deprecated alias of the new one. That is not possible here, because the old names are reused at once. A schema that wants today's meaning writes the new explicit names (`TimestamptzTemporal`, `DateTemporal`, `TimeTemporal`), and its contract does not change. `Timestamptz` and `Timestamp` follow ADR 257's rule, as deprecated aliases of `TimestamptzTemporal` and `TimestampTemporal` for one release line. +- **Breaking for schemas that write `DateTime`, `Date`, `Time`, `Timestamptz`, `Timestamp` or `Interval`.** `DateTime`, `Date` and `Time` change meaning from one explicit type to a convenience type, whose defaults may choose a different kind of value. `Timestamptz`, `Timestamp` and `Interval` become errors. ADR 257's rule keeps a renamed name for one release line as a deprecated alias; that rule is not applied before Prisma 8 reaches its release candidate, so every name changes at once and the upgrade instructions give the rewrite. A schema that wants today's meaning writes the explicit names (`TimestamptzTemporal`, `TimestampTemporal`, `DateTemporal`, `TimeTemporal`, `IntervalParts`), and its contract does not change. - **Breaking for `contract print`,** which today writes `DateTime` for a `timestamptz` column read as a `Temporal.Instant`. - **Upgraded Prisma 7 schemas read `Date` values instead of strings.** Their contracts change codec, so each database needs `prisma db sign` after the upgrade. A schema written by an earlier `contract infer` or `prisma orm init` keeps its explicit string types and its contract. - **String values change form.** An application that reads a string date or time type gets ISO 8601 text instead of PostgreSQL's display text, for example `2024-01-01T00:00:00Z` instead of `2024-01-01 00:00:00+00`. The codec ids and the contract do not change. +- **Temporal fields are typed from `temporal-spec`,** a package of TypeScript declarations with no runtime code, instead of an undeclared global `Temporal`. A Temporal field is then never typed `any` in a project without the polyfill's global declarations, and no polyfill code reaches an application that does not opt into Temporal values. A missing global `Temporal` at runtime is still reported when a Temporal codec or generator first runs. - **`Interval` reads the same object as before, under the name `IntervalParts`;** `Duration` reads an ISO string by default. - ADR 254 says a type constructor names one codec and maps its arguments onto parameters. A type constructor may now also choose the codec. - ADR 257 says the second codec of a stored type adds a suffix. Where a stored type has several codecs, every one now has a suffix, and the unsuffixed name of the concept belongs to a convenience type. @@ -138,7 +139,6 @@ The `String` and `JsDate` variants are not needed. A contributed alias takes no ## Not decided here -- Whether the string codecs keep their ids when their application value changes from PostgreSQL's display text to ISO 8601 (item 9), or the ISO behaviour ships as new codec versions (`pg/timestamptz-string@2` and so on) with the old versions kept for existing contracts and an upgrade path between them. - SQLite and MongoDB. Each declares its own convenience types over its own explicit types, under item 11, and each already uses one of the names: SQLite's explicit `DateTime` is its `sqlite/datetime@1` type, and MongoDB's explicit `Date` is `mongo/date@1`, a moment in time, with `DateTime` as a deprecated alias of it. - Whether other scalar types get convenience types, for example an `Int` whose arguments choose between the explicit `Int2`, `Int4` and `Int8`. diff --git a/projects/datetime-convenience-types/design-notes.md b/projects/datetime-convenience-types/design-notes.md index 22f4c25be867..ab5207f372ab 100644 --- a/projects/datetime-convenience-types/design-notes.md +++ b/projects/datetime-convenience-types/design-notes.md @@ -56,8 +56,13 @@ Record of the design discussion between the operator and the orchestrator on 202 ## Open questions -- Codec ids and explicit names of the two new `timestamp` codecs (slice 3). -- Whether slice 1 sets session settings, normalises in the codecs, or both. +None. Settled on 2026-10-22: + +- The string codecs keep their ids when they switch to ISO; it is a bug fix, and Prisma 8 keeps no backward compatibility before RC. +- Prisma requires `DateStyle=ISO` on its connections ("we get to dictate our connection requirements"); the codecs convert any time zone offset themselves. +- Renamed explicit types get no deprecated aliases before RC. +- No startup check for a missing Temporal: Temporal is an opt-in, and the first-use error names the fix. Fields are typed from `temporal-spec`, a types-only package, never from the polyfill, which is too large to include. +- `prisma7Schema` must map Prisma 7's `@default(now())` to `@default(now()) @onCreate(now())`; omitting the client-side default was a bug. ## References diff --git a/projects/datetime-convenience-types/plan.md b/projects/datetime-convenience-types/plan.md index ad3419275ba2..4a403b924a7e 100644 --- a/projects/datetime-convenience-types/plan.md +++ b/projects/datetime-convenience-types/plan.md @@ -4,31 +4,31 @@ ## Summary -Five slices. Slices 1, 2 and 3 are independent and can run in parallel. Slice 4 builds on slice 3, and slice 5 on slice 4. +Five slices. Slices 1, 2 and 3 are independent and can run in parallel. Slice 4 builds on slice 3, and slice 5 on slices 2 and 4. -Five is one over the usual ceiling. Slices 1 and 2 are fixes the report found that any naming would need; they share the project because they serve the same purpose (date and time values that are predictable), not because the other slices depend on them. Folding either into another slice would make that slice two unrelated reviews. +Five is one over the usual ceiling. Slices 1 and 2 are bug fixes the research found that any naming would need; they share the project because they serve the same purpose (date and time values that are predictable), not because the other slices depend on them. A startup check for a missing Temporal was planned as slice 2 and dropped (2026-10-22): Temporal values are now an explicit opt-in, and its type half moved into slice 3. ## Slices | # | Slice | Linear | Builds on | Hands to | | --- | --- | --- | --- | --- | | 1 | String date and time types read ISO 8601 that does not depend on the server | [TML-3418](https://linear.app/prisma-company/issue/TML-3418) | — | String codecs whose value is the ADR 254 stored form, for `Date`, `Time` and `Duration` to default to | -| 2 | A missing Temporal is reported when the client is created, and Temporal fields are never typed `any` | [TML-3710](https://linear.app/prisma-company/issue/TML-3710) | — | Temporal opt-ins that fail early and type precisely | +| 2 | `prisma7Schema` maps `@default(now())` to `@default(now()) @onCreate(now())` | [TML-3719](https://linear.app/prisma-company/issue/TML-3719) | — | Adopted Prisma 7 schemas write the client's UTC time on create, as Prisma 7 did | | 3 | Explicit types carry a `Temporal` suffix; `timestamp` and `interval` get the missing codecs | [TML-3711](https://linear.app/prisma-company/issue/TML-3711) | — | A complete explicit layer: every cell of the spec's decision 3 table has an explicit type, and `TimestamptzTemporal`, `TimestampTemporal`, `DateTemporal`, `TimeTemporal`, `IntervalParts`, `IntervalString` exist | | 4 | PSL convenience types `DateTime`, `Date`, `Time` and `Duration` | [TML-3712](https://linear.app/prisma-company/issue/TML-3712) | 3 | Type constructors that choose their codec; the four convenience types; the TypeScript resolver evaluating the choice | -| 5 | `contract infer`, `contract print`, the Prisma 7 reader and `orm init` write the convenience types | [TML-3713](https://linear.app/prisma-company/issue/TML-3713) | 4 | Adopted and new projects read `Date` timestamps with no polyfill | +| 5 | `contract infer`, `contract print`, the Prisma 7 reader and `orm init` write the convenience types | [TML-3713](https://linear.app/prisma-company/issue/TML-3713) | 2, 4 | Adopted and new projects read `Date` timestamps with no polyfill | ### Slice 1 — ISO strings `pg/timestamptz-string@1`, `pg/timestamp-string@1`, `pg/date-string@1`, `pg/time-string@1` and `pg/timetz@1` return the ISO 8601 form ADR 254 stores, on any server `TimeZone` and `DateStyle`, BC years and years past 9999 included; `infinity` stays `infinity`. Covers the related case in the ticket: a database `now()` default on a `timestamp` column and the client's `now()` generator store the same clock. Amends ADR 254's "Date and time types" paragraph on text codecs. -### Slice 2 — Temporal at startup +### Slice 2 — Prisma 7 `now()` defaults -Creating a client whose contract uses a Temporal codec, in a process without a global `Temporal`, fails with one error that names the fix. The generated `contract.d.ts` and the TypeScript builder's inferred types reference the Temporal types so that a missing declaration is a type error, not `any`. +`prisma7Schema` reads Prisma 7's `@default(now())` as the database default `now()` plus the client generator `@onCreate(now())` resolved from the column's codec. Today it writes the database default only, so on a server whose `TimeZone` is not UTC a create through Prisma 8 stores local time where Prisma 7 stored UTC. A bug fix independent of the naming work. ### Slice 3 — The explicit layer -Renames with deprecated aliases where the name is not reused: `Timestamptz` → `TimestamptzTemporal`, `Timestamp` → `TimestampTemporal`, `Interval` → `IntervalParts`. New explicit names `DateTemporal` and `TimeTemporal` beside today's `Date` and `Time`, which keep their meaning until slice 4. New codecs and explicit types: a `timestamp` column read as a `Date`, and as a `Temporal.Instant` (both UTC), and `IntervalString`. Each new codec passes the codec conformance kit and the three codec probes (dod.md). +Renames outright, with no deprecated aliases before RC: `Timestamptz` → `TimestamptzTemporal`, `Timestamp` → `TimestampTemporal`, `Interval` → `IntervalParts`. New explicit names `DateTemporal` and `TimeTemporal` beside today's `Date` and `Time`, which keep their meaning until slice 4. New codecs and explicit types: `TimestampJsDate` (`pg/timestamp-date@1`, a `timestamp` read as a `Date`, UTC), `TimestampInstant` (`pg/timestamp-instant@1`, a `timestamp` read as a `Temporal.Instant`, UTC) and `IntervalString` (`pg/interval-string@1`). The Temporal codecs and generators take their types from `temporal-spec`, so a Temporal field is never typed `any`; no polyfill code is added. Each new codec passes the codec conformance kit and the three codec probes (dod.md). ### Slice 4 — The convenience types diff --git a/projects/datetime-convenience-types/spec.md b/projects/datetime-convenience-types/spec.md index 5b23ca86cb5e..b256e031d670 100644 --- a/projects/datetime-convenience-types/spec.md +++ b/projects/datetime-convenience-types/spec.md @@ -45,9 +45,9 @@ The reasoning and the alternatives rejected are in [`design-notes.md`](./design- 5. **Plain strings, no brand and no Prisma value class.** A brand or class adds friction on every write and does not survive JSON. The codec validates the format on write. 6. **Every explicit type of a column with several codecs carries a suffix.** `Timestamptz`, `Timestamp`, `Date` and `Time` become `TimestamptzTemporal`, `TimestampTemporal`, `DateTemporal` and `TimeTemporal`. `Interval` becomes `IntervalParts`, next to the new `IntervalString`. 7. **Four codecs are added:** a `timestamp` column read as a `Date` and as a `Temporal.Instant`, both treating the stored value as UTC; and an `interval` column read as an ISO 8601 duration string. (The `parts` codec already exists.) -8. **String values are ISO 8601, the same on every server.** Every string codec returns the form ADR 254 stores, not PostgreSQL's display text. -9. **A missing Temporal is reported when the client is created,** and Temporal fields are never typed `any`. -10. **`contract infer` and `contract print` write convenience types**, with arguments only where they differ from the defaults; a column no convenience type covers (`Temporal.PlainDateTime`) is written as its explicit type. **The Prisma 7 reader** maps Prisma 7's `DateTime` to `DateTime(column: timestamp)`, so an upgraded app reads `Date` values again. +8. **String values are ISO 8601, the same on every server.** Every string codec returns the form ADR 254 stores, not PostgreSQL's display text. The codec ids do not change: this is a bug fix, and nothing keeps the old format before RC. Prisma requires `DateStyle=ISO` on its connections; it sets it on the connections it opens, and a connection the user supplies with another `DateStyle` is refused with an error naming the setting. Time zones need no setting, because the codecs convert any offset to UTC. +9. **Temporal fields are never typed `any`.** The Temporal codecs and generators take their types from `temporal-spec`, a types-only package, instead of an undeclared global. No polyfill code is added anywhere. A missing global `Temporal` at runtime is still reported on first use, as today; a startup check was considered and dropped, because Temporal values are now an explicit opt-in. +10. **`contract infer` and `contract print` write convenience types**, with arguments only where they differ from the defaults; a column no convenience type covers (`Temporal.PlainDateTime`) is written as its explicit type. **The Prisma 7 reader** maps Prisma 7's `DateTime` to `TimestampJsDate(3)`, printed `DateTime(column: timestamp)`, so an upgraded app reads `Date` values again, and maps Prisma 7's `@default(now())` to `@default(now()) @onCreate(now())`, as Prisma 7 behaved ([TML-3719](https://linear.app/prisma-company/issue/TML-3719)). 11. **PostgreSQL only.** A convenience name means the same thing on every target; SQLite and MongoDB follow in [TML-3714](https://linear.app/prisma-company/issue/TML-3714). 12. **TypeScript:** this project ships the PSL types and makes the TypeScript builder's compile-time resolver evaluate a codec chosen by arguments. The `type.DateTime(...)` helper itself arrives with the `type.*` helpers of the field-preset removal project. @@ -64,7 +64,7 @@ The reasoning and the alternatives rejected are in [`design-notes.md`](./design- ## Place in the larger world - **ADR 254, data types and casts.** Codecs, data types and stored forms are unchanged. A type constructor gains the ability to choose its codec, which amends the definition there. The text codecs today keep PostgreSQL's year text (` BC`); decision 8 changes that to the ISO form, which amends the "Date and time types" section. -- **ADR 257, scalar names.** The explicit layer follows it. The `Temporal` suffix applies the suffix rule to every codec of a multi-codec column, and `Date`, `Time` and `DateTime` are reused at once, which its one-release deprecation rule cannot cover. +- **ADR 257, scalar names.** The explicit layer follows it. The `Temporal` suffix applies the suffix rule to every codec of a multi-codec column. Its one-release deprecation rule is not applied before RC: renamed names become errors at once. - **ADR 246 and ADR 275.** The `option` argument kind and the `select` template, which ADR 275 removes with the presets, are kept for type constructors. - **The PostgreSQL and SQLite rename project** (`projects/target-named-scalars-sql/`, not started) must keep `DateTime` and take this project's renames instead of its own `DateTime` and `Timestamptz` rows. - **The field-preset removal project** (`projects/remove-field-presets/`, slice 1 merged): its `type.*` helpers carry the convenience types into TypeScript, and its contributed aliases are built on `DateTime`; its `String` and `JsDate` alias variants are not needed. @@ -76,7 +76,7 @@ The reasoning and the alternatives rejected are in [`design-notes.md`](./design- 2. **Every argument combination is either a listed choice or an error at the argument** that names the accepted choices. No combination silently picks a codec. 3. **Every default works on Node 24 with no polyfill.** A test runs each convenience type's default in a process with no global `Temporal`. 4. **A string value round-trips.** A string written through a string codec reads back as the same string, on a server whose `TimeZone` is not UTC and whose `DateStyle` is not ISO. -5. **Renamed explicit names follow ADR 257's rename path** where the old name is not reused (`Timestamptz`, `Timestamp`, `Interval`: deprecated aliases, `PSL_DEPRECATED_SCALAR_NAME`). Where it is reused (`DateTime`, `Date`, `Time`), the change is breaking and the upgrade instructions say so. +5. **Renamed explicit names are renamed outright.** Before RC there are no deprecated aliases: `Timestamptz`, `Timestamp` and `Interval` become errors, and the upgrade instructions give the rewrite. `DateTime`, `Date` and `Time` change meaning when the convenience types take them. 6. **Every user-visible change ships upgrade instructions** in the same PR (`skills-contrib/record-upgrade-instructions`). 7. **Docs move with the code.** `docs/reference/scalar-types.md`, the error reference, the editor hover text and the codec authoring guide are updated in the slice that changes what they describe. @@ -87,7 +87,7 @@ The reasoning and the alternatives rejected are in [`design-notes.md`](./design- ## Contract-impact -No new contract entities or kinds. New codec ids: `pg/timestamp-date@1`, `pg/timestamp-instant@1` (names settled in slice 3) and `pg/interval-string@1`. Contracts that use the existing codecs do not change. Contracts emitted by `contract infer` and `prisma7Schema` after slice 5 use different codecs for date and time columns. +No new contract entities or kinds. New codec ids: `pg/timestamp-date@1`, `pg/timestamp-instant@1` and `pg/interval-string@1`. Contracts that use the existing codecs do not change. Contracts emitted by `contract infer` and `prisma7Schema` after slice 5 use different codecs for date and time columns. ## Adapter-impact @@ -102,15 +102,14 @@ ADR 279 (draft in this project's shaping PR, status Proposed until slice 4 merge Inherits the team floor in [`drive/calibration/dod.md`](../../drive/calibration/dod.md). In addition: - [ ] Every row of the decision 3 table works end to end: a PSL schema emits, `migration plan` produces the expected column DDL, and a query on PostgreSQL reads and writes the expected value type, with and without a global `Temporal` as each row requires. -- [ ] A new project from `prisma orm init`, and a Prisma 7 project adopted with `prisma7Schema(...)`, run their first query on Node with no polyfill and read `Date` values for their timestamps. +- [ ] A new project from `prisma orm init`, and a Prisma 7 project adopted with `prisma7Schema(...)`, run their first query on Node with no polyfill and read `Date` values for their timestamps; a create through Prisma 8 on an adopted project, on a server whose `TimeZone` is not UTC, stores UTC. - [ ] The report's use-case table (see References) re-run against the shipped surface, with each row's Prisma 8 cell updated. - [ ] ADR 279 accepted; ADR 246, 254, 257 and 275 carry notes; `docs/reference/scalar-types.md` describes both layers. - [ ] TML-3714 and TML-3715 remain open with their context. ## Open questions -- The codec ids and explicit names of the two new `timestamp` codecs (slice 3). -- Whether the runtime sets `TimeZone` and `DateStyle` on its connections, or the string codecs normalise what they read, or both (slice 1). +- None open. The new codecs are `pg/timestamp-date@1` (`TimestampJsDate`), `pg/timestamp-instant@1` (`TimestampInstant`) and `pg/interval-string@1` (`IntervalString`). ## References From e7e2354010554c38f0f4422feefc8218572284e3 Mon Sep 17 00:00:00 2001 From: willbot Date: Thu, 22 Oct 2026 17:01:14 +0200 Subject: [PATCH 005/118] TML-3709: Slice specs for the ISO string codecs, the Prisma 7 now() mapping and the explicit date and time types Co-Authored-By: Claude Opus 5.5 Signed-off-by: willbot Signed-off-by: Will Madden --- .../slices/01-iso-strings/spec.md | 55 ++++++++++++++++ .../slices/02-prisma7-now-defaults/spec.md | 50 +++++++++++++++ .../slices/03-explicit-types/spec.md | 62 +++++++++++++++++++ 3 files changed, 167 insertions(+) create mode 100644 projects/datetime-convenience-types/slices/01-iso-strings/spec.md create mode 100644 projects/datetime-convenience-types/slices/02-prisma7-now-defaults/spec.md create mode 100644 projects/datetime-convenience-types/slices/03-explicit-types/spec.md diff --git a/projects/datetime-convenience-types/slices/01-iso-strings/spec.md b/projects/datetime-convenience-types/slices/01-iso-strings/spec.md new file mode 100644 index 000000000000..b8f86cc9c5c3 --- /dev/null +++ b/projects/datetime-convenience-types/slices/01-iso-strings/spec.md @@ -0,0 +1,55 @@ +# Slice 1 — String date and time types read ISO 8601, the same on every server + +**Linear:** [TML-3418](https://linear.app/prisma-company/issue/TML-3418) · **Parent:** [project spec](../../spec.md), decision 8 · **Design record:** ADR 279, item 9 + +## At a glance + +A `TimestamptzString` column holding the instant `2024-01-01T00:00:00Z`: + +| | Today | After | +| --- | --- | --- | +| Read on a UTC server | `2024-01-01 00:00:00+00` | `2024-01-01T00:00:00Z` | +| Read on a server set to `Europe/Berlin` | `2024-01-01 01:00:00+01` | `2024-01-01T00:00:00Z` | +| Year 44 BC | `0044-03-15 12:00:00+00 BC` | `-000043-03-15T12:00:00Z` | +| Inside an `include` (JSON row) | the same display text | the same ISO text | + +Every string codec's application value is the stored form ADR 254 defines for its data type ("Date and time types"): `pg/timestamptz-string@1`, `pg/timestamp-string@1`, `pg/date-string@1`, `pg/time-string@1` and `pg/timetz@1`. What the application writes in that form reads back unchanged. + +## Chosen design + +1. **The codecs read with the stored-form readers.** `fromWire` turns PostgreSQL's ISO-DateStyle text into the stored form with the existing readers in `packages/3-targets/3-targets/postgres/src/core/data-types.ts` (`pgDateStoredText`, `pgTimeStoredText`, `pgTimetzStoredText`, `pgTimestampStoredText`, `pgTimestamptzStoredText`), which already convert any time zone offset to UTC for `timestamptz`, ` BC` years and long years to ISO, and keep `infinity`. `pg/timestamptz-date@1` already reads wire text this way. `toWire` keeps converting the stored ISO year into PostgreSQL's year text (`postgresYearText`). `fromDataTypeValue` is the stored value; `toDataTypeValue` requires the stored form. +2. **`DateStyle=ISO` is a connection requirement.** Prisma sets it on every connection it opens, as a connection startup option (`options=-c DateStyle=ISO`), which survives the Supabase role scope's `RESET ALL`. A connection the user supplies (`pgPool`, `pgClient`) is not changed. A value that is not in ISO DateStyle is refused when read, with an error that names the setting and the fix. No `TimeZone` setting is needed. +3. **The `now()` generators for these codecs write the stored form** (`timestampStringNow`, `timestamptzStringNow`, `dateStringNow`, `timeStringNow`, `timetzNow` in `date-time-now-generators.ts`), so a generated value reads back as written. +4. **Codec ids, the contract and stored defaults do not change.** It is a bug fix; nothing keeps the old format before RC. +5. **Values PostgreSQL holds beyond the stored form's range** (time `24:00:00`, dates after `+275760-09-13`, seven-digit years) are read as ISO-shaped text, not refused: the string types exist to carry values the other codecs cannot. The implementer documents the exact text for each. +6. **`enumRefusal` on these codecs is re-examined.** Where it existed only because a query returned display text, it goes; the enum test that places every codec in exactly one group decides. + +## Coherence rationale + +One reviewable change: the five string codecs, their generators, the runtime connection option and the docs that describe the old text. The JSON path is covered by the same `fromWire`, because the projections already cast to text. + +## Scope + +**In:** the five codecs, their `now` generators, the PostgreSQL runtime driver's connection setup, the error for a non-ISO DateStyle (error-reference entry), tests, the codec conformance cases, ADR 254's "Date and time types" and "Codecs" paragraphs that describe the old text, `docs/reference/scalar-types.md`, the codec authoring guide, `gotchas.md`, the `prisma-8` skill references, and an `app` upgrade instruction. + +**Out:** renaming any type (slice 3); the Prisma 7 `now()` mapping (slice 2); a database `now()` default on a `timestamp` column, which PostgreSQL evaluates in the session's time zone (slice 2 makes adopted schemas fill the value on the client instead). + +## Pre-investigated edge cases + +| Case | Disposition | +| --- | --- | +| Supabase runs `RESET ALL` when it releases a role scope | Startup option survives it; test through the Supabase runtime | +| `@prisma/dev` / PGlite shares one session between clients | A startup option or per-connection setting must not leak a changed setting to other clients; the tests already assume UTC and ISO | +| A user-supplied pool whose server uses `DateStyle=German` | Refused on read with the new error, not mis-parsed | +| Transaction-mode poolers that reject startup options | Out of scope to work around; the requirement is documented | +| `infinity` / `-infinity` | Unchanged | +| Tests that assert display text (`temporal-string-codec-roundtrip.integration.test.ts`, `date-time-text.integration.test.ts`, `now-generators.test.ts`, `prisma7-dates-without-temporal.e2e.test.ts`, conformance `cases.ts`) | Updated to the ISO form; the two tests that assert session leakage become tests that it no longer leaks | + +## Slice-specific done conditions + +- A `TimestamptzString` value written in ISO form reads back identical on a session with `TimeZone=Asia/Tokyo`, flat and inside an `include`, and a German-DateStyle user connection fails with the new error. + +## Dispatch plan + +1. **Codecs, generators and connection requirement** — outcome: the at-a-glance table holds on PostgreSQL, including JSON rows, BC years, long years and a non-UTC session; a non-ISO DateStyle is refused. Gates: `pnpm --filter @internal/target-postgres test`, `pnpm --filter @internal/adapter-postgres test`, `pnpm --filter @internal/driver-postgres test`, the testkit's conformance and date-time-text integration files, `pnpm typecheck` for the touched packages, `pnpm lint` for each touched package, `pnpm fixtures:check`. +2. **Docs and upgrade instruction** — outcome: every doc listed in Scope describes ISO values; ADR 254 amended in place; upgrade instruction recorded per `skills-contrib/record-upgrade-instructions`. Gates: `pnpm lint:docs`, `pnpm check:upgrade-coverage --mode pr`. diff --git a/projects/datetime-convenience-types/slices/02-prisma7-now-defaults/spec.md b/projects/datetime-convenience-types/slices/02-prisma7-now-defaults/spec.md new file mode 100644 index 000000000000..dc9f6bdaac7e --- /dev/null +++ b/projects/datetime-convenience-types/slices/02-prisma7-now-defaults/spec.md @@ -0,0 +1,50 @@ +# Slice 2 — `prisma7Schema` maps `@default(now())` as Prisma 7 behaved + +**Linear:** [TML-3719](https://linear.app/prisma-company/issue/TML-3719) · **Parent:** [project spec](../../spec.md), decision 10 + +## At a glance + +```prisma +// Prisma 7 schema, read by prisma7Schema('./schema.prisma') +model Post { + id Int @id @default(autoincrement()) + createdAt DateTime @default(now()) +} +``` + +| | Today | After | +| --- | --- | --- | +| Contract | database default `now()` only | database default `now()` and the `onCreate` generator `now` resolved from the column's codec | +| `create` through Prisma 8, server `TimeZone=Asia/Tokyo` | the database fills `createdAt` with Tokyo wall-clock time | the client writes the current UTC time | +| `INSERT` from outside Prisma | the database default | the database default (unchanged) | + +Prisma 7 filled `@default(now())` on the client and also wrote the database default. Omitting the client generator is a bug. + +## Chosen design + +- The Prisma 7 reader (`packages/2-sql/2-authoring/contract-prisma7`) lowers `@default(now())` on a date or time field to both the database default and an `onCreate` generator, through the generator function registry, exactly as Prisma 8 PSL's `@default(now()) @onCreate(now())` lowers. It resolves the generator by the field's codec, so it is correct for whatever codec the reader picks today and after slice 5 changes it. +- `@default(now())` together with `@updatedAt` keeps the database default and both generators. +- The storage hash does not change (the column and its default are the same); the execution hash does, because the contract gains a generator. Adopted projects re-emit and run `db sign`; the upgrade instruction says so. + +## Scope + +**In:** the Prisma 7 reader's default lowering, its tests, the Prisma 7 adoption journey test and `examples/prisma7-adoption` if its contract changes, the `contract-prisma7` README, an `app` upgrade instruction. + +**Out:** any change to which codec the reader picks (slice 5); `contract infer`, which reads a database and cannot know the default came from Prisma 7. + +## Pre-investigated edge cases + +| Case | Disposition | +| --- | --- | +| `@default(now())` on a `@db.Date` or `@db.Time` field | Generator resolved by codec, like any other date or time codec | +| Optional field with `@default(now())` | Same mapping | +| `@default(now())` and `@updatedAt` on one field | Database default plus `onCreate` and `onUpdate` generators | +| A create that supplies `createdAt` explicitly | The supplied value wins, as for any generator | + +## Slice-specific done conditions + +- A Prisma 7 schema adopted with `prisma7Schema`, on a session with `TimeZone=Asia/Tokyo`, stores UTC for a create through Prisma 8, and a raw `INSERT` still gets the database default. + +## Dispatch plan + +1. **Reader mapping, tests, journey and upgrade instruction** — outcome: the at-a-glance table holds. Gates: `pnpm --filter @internal/contract-prisma7 test` (or the package's real name), the Prisma 7 journey test files, `pnpm typecheck` and `pnpm lint` for touched packages, `pnpm fixtures:check`, `pnpm check:upgrade-coverage --mode pr`. diff --git a/projects/datetime-convenience-types/slices/03-explicit-types/spec.md b/projects/datetime-convenience-types/slices/03-explicit-types/spec.md new file mode 100644 index 000000000000..95f147f0f4b8 --- /dev/null +++ b/projects/datetime-convenience-types/slices/03-explicit-types/spec.md @@ -0,0 +1,62 @@ +# Slice 3 — The explicit date and time types: Temporal suffix, missing codecs, Temporal types from `temporal-spec` + +**Linear:** [TML-3711](https://linear.app/prisma-company/issue/TML-3711) · **Parent:** [project spec](../../spec.md), decisions 6, 7 and 9 · **Design record:** ADR 279, items 6 and 7 + +## At a glance + +```prisma +model Event { + id Int @id + at TimestamptzTemporal(3) // was Timestamptz(3): timestamptz(3), Temporal.Instant + local TimestampTemporal // was Timestamp: timestamp, Temporal.PlainDateTime + legacyAt TimestampJsDate(3) // new: timestamp(3), a JavaScript Date, stored as UTC + utcAt TimestampInstant(6) // new: timestamp(6), a Temporal.Instant, stored as UTC + day DateTemporal // new name beside Date: date, Temporal.PlainDate + opens TimeTemporal(0) // new name beside Time: time(0), Temporal.PlainTime + period IntervalParts // was Interval: interval, { months, days, micros } + span IntervalString // new: interval, "P1Y2M" +} +``` + +`Timestamptz`, `Timestamp` and `Interval` are errors after this slice, with the upgrade instruction giving the rewrite. `DateTime`, `Date` and `Time` keep today's meaning until slice 4. + +## Chosen design + +1. **Renames are outright.** No deprecated aliases before RC. The old names are removed from `postgresPslTypeConstructors` (`packages/3-targets/3-targets/postgres/src/core/type-constructors.ts`); every `.prisma` file, PSL string in tests, doc and skill reference moves to the new names. `DateTemporal` and `TimeTemporal` are added; `Date` and `Time` stay as second names until slice 4. +2. **Three codecs, each a complete citizen** (every file the codec anatomy needs: id constant, codec class, descriptor with data type, traits, JSON projection, registration, `codec-type-map`, `OutputTypeMap` where precision-refined, aggregates, `now` binding, PSL constructor, adapter column helper, conformance and aggregate-matrix cases, enum eligibility): + - `pg/timestamp-date@1`, `TimestampJsDate(p)`: data type `pg/timestamp`; reads `timestamp` text as UTC (append `Z` before `new Date`), writes the stored form without an offset; refuses `infinity` naming `TimestampString`; `now` → `timestampNow`. + - `pg/timestamp-instant@1`, `TimestampInstant(p)`: data type `pg/timestamp`; reads as `Temporal.Instant` in UTC, writes the UTC wall-clock time without an offset; refuses `infinity`; needs a global `Temporal` like the other Temporal codecs; `now` → `instantNow`. + - `pg/interval-string@1`, `IntervalString(p)`: data type `pg/interval`; application value is the ISO 8601 duration ADR 254 stores; reads every `IntervalStyle` the existing interval reader handles; `keepsWrittenForm: true` (equal intervals can differ in form); `sum`/`avg` rows like `IntervalParts`; no `now`. +3. **`inferred` marks do not move** except `Interval`'s, which moves to `IntervalParts`. No new codec is inferred. +4. **Fix the `Interval(p)` output type** while renaming: today a precision makes it a branded string although the codec reads `PgInterval` (`packages/3-targets/3-targets/postgres/src/exports/codec-types.ts`). +5. **Temporal types from `temporal-spec`.** The Temporal codecs, the Temporal `now` generators and every exported signature that names a `Temporal` type import it with `import type { Temporal } from 'temporal-spec'`; `temporal-spec` becomes a dependency of the PostgreSQL target (types only). The emitted `.d.mts` then names the type through a real import, so a project without the polyfill's global declarations gets precise types, not `any`. No runtime import of `temporal-polyfill` is added anywhere; verify the built runtime entries contain none. +6. **`contract print`** writes the new names; check it never prints a removed name. + +## Coherence rationale + +One reviewable change: the explicit layer becomes complete and uniformly named. The three codecs follow one template each, and the renames are mechanical. The large fixture regeneration (about 268 `contract.d.ts` files gain aggregate rows) is mechanical and called out in the PR. + +## Scope + +**In:** everything above, the TS adapter column helpers (`timestampJsDateColumn`, `timestampInstantColumn`, `intervalStringColumn`, renamed helpers where they carry an old type name), docs (`docs/reference/scalar-types.md` tables, the codec authoring guide, package READMEs, the `prisma-8` skill references), and an `app` upgrade instruction with the rename table. + +**Out:** the convenience types and codec choice by arguments (slice 4); `DateTime`, `Date`, `Time` changing meaning (slice 4); `contract infer` / Prisma 7 reader output (slice 5); the `field.temporal.*` presets (being removed by the field-preset removal project). + +## Pre-investigated edge cases + +| Case | Disposition | +| --- | --- | +| Precision above 3 on `TimestampJsDate` | Refused at authoring by the parameter bound, since a `Date` holds milliseconds; decide and test | +| Years before 1 and after 9999 | Through `isoYearText` / `postgresYearText`, as the `timestamptz` codecs do | +| A `timestamp` value PostgreSQL holds beyond `Date`/`Temporal` range | Refused on read, naming `TimestampString` | +| Supabase's contract (`packages/3-extensions/supabase/src/contract/contract.prisma`, 78 `Timestamptz` lines) | Renamed; its committed contract regenerated if it changes; storage hash must not change | +| `temporal-spec` version | Match the version `temporal-polyfill` 1.0.x re-exports | + +## Slice-specific done conditions + +- A project without the polyfill's global type declarations type-checks a `TimestamptzTemporal` field as `Temporal.Instant` (a `not.toBeAny()` type test), and each new codec round-trips on PostgreSQL through the conformance kit. + +## Dispatch plan + +1. **Renames and the Temporal type source** — outcome: old names gone, new names resolve, `temporal-spec` types in every exported signature, `Interval(p)` typed correctly, all call sites and docs moved, fixtures regenerated. Gates: `pnpm build`, `pnpm --filter @internal/target-postgres test`, `pnpm --filter @internal/adapter-postgres test`, the SQL `contract-psl` and postgres extension package tests, `pnpm typecheck`, per-package `pnpm lint`, `pnpm fixtures:check`, `pnpm lint:deps`. +2. **The three codecs** — builds on 1. Outcome: each codec complete with conformance and aggregate cases, and the three codec probes from `drive/calibration/dod.md`. Gates: as 1, plus the codec testkit's conformance and aggregate-resolution files, plus `pnpm check:upgrade-coverage --mode pr` after the upgrade instruction lands. From 797de786dfdd1de26ce2b04ad8f87d8b15749b8d Mon Sep 17 00:00:00 2001 From: willbot Date: Thu, 22 Oct 2026 17:10:05 +0200 Subject: [PATCH 006/118] TML-3709: ADR 279 limits UTC conversion to timestamptz; timetz keeps its stored offset Co-Authored-By: Claude Opus 5.5 Signed-off-by: willbot Signed-off-by: Will Madden --- ...d time types choose an explicit type from their arguments.md | 2 +- projects/datetime-convenience-types/spec.md | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/architecture docs/adrs/ADR 279 - Convenience date and time types choose an explicit type from their arguments.md b/docs/architecture docs/adrs/ADR 279 - Convenience date and time types choose an explicit type from their arguments.md index 3ca98bc58402..c77502f2ef57 100644 --- a/docs/architecture docs/adrs/ADR 279 - Convenience date and time types choose an explicit type from their arguments.md +++ b/docs/architecture docs/adrs/ADR 279 - Convenience date and time types choose an explicit type from their arguments.md @@ -60,7 +60,7 @@ The contract stores what the explicit type stores: the codec, its data type and 7. **Three codecs are added:** a `timestamp` column read as a JavaScript `Date`, and a `timestamp` column read as a `Temporal.Instant`, both reading and writing the stored value as UTC; and an `interval` column read as an ISO 8601 duration string. Each gets an explicit type: `TimestampJsDate(p)` for the `Date` codec, `TimestampInstant(p)` for the `Temporal.Instant` codec, and `IntervalString(p)`. `DateTime(column: timestamp)` with `value: jsDate` becomes `TimestampJsDate`, with `value: temporal` becomes `TimestampInstant`, and `Duration` with `value: string` becomes `IntervalString`. `TimestampInstant` names its `Temporal` type rather than taking the `Temporal` suffix, because the `timestamp` column has two `Temporal` codecs and `TimestampTemporal` is the one that reads `Temporal.PlainDateTime`. 8. **The Prisma 7 reader maps Prisma 7's `DateTime` to `TimestampJsDate(3)`,** which `contract print` writes as `DateTime(column: timestamp)`: a `timestamp(3)` column read as a JavaScript `Date`, instead of a string. The column and its stored values do not change; only the application value goes from a string back to the `Date` that Prisma 7 gave, so an upgraded application keeps the values its code was written for. `prisma orm init`, which creates new columns, writes plain `DateTime`, a `timestamptz(3)` column read as a `Date`. -9. **A string value is ISO 8601, the same on every server.** Every string codec of a date or time type returns the form its data type stores (see ADR 254, "Date and time types") and reads that form back unchanged. PostgreSQL's display text, such as `2024-01-01 00:00:00+00` or a ` BC` year, never reaches the application. The codecs convert any time zone offset to UTC themselves. Prisma requires `DateStyle=ISO` on its database connections: it sets it on the connections it opens, and refuses a value it reads in another style with an error naming the setting. The codec ids do not change; the old text was never a format an application could rely on. +9. **A string value is ISO 8601, the same on every server.** Every string codec of a date or time type returns the form its data type stores (see ADR 254, "Date and time types") and reads that form back unchanged. PostgreSQL's display text, such as `2024-01-01 00:00:00+00` or a year written with BC, never reaches the application. The `timestamptz` codec converts any session time zone offset to UTC itself; the `timetz` codec keeps the offset the value was stored with, because that offset is part of a `timetz` value. Prisma requires `DateStyle=ISO` on its database connections: it sets it on the connections it opens, and refuses a value it reads in another style with an error naming the setting. The codec ids do not change; the old text was never a format an application could rely on. 10. **`contract infer` and `contract print` write convenience types,** with arguments only where they differ from the defaults. A column no convenience type covers, a `timestamp` read as `Temporal.PlainDateTime`, is written as its explicit type. `prisma orm init` writes `DateTime`. 11. **A convenience name means the same thing on every target.** This decision covers PostgreSQL. Another target adds convenience types under the same names, and renames any explicit type that already uses one of them for something else. diff --git a/projects/datetime-convenience-types/spec.md b/projects/datetime-convenience-types/spec.md index b256e031d670..8a3b579514a8 100644 --- a/projects/datetime-convenience-types/spec.md +++ b/projects/datetime-convenience-types/spec.md @@ -45,7 +45,7 @@ The reasoning and the alternatives rejected are in [`design-notes.md`](./design- 5. **Plain strings, no brand and no Prisma value class.** A brand or class adds friction on every write and does not survive JSON. The codec validates the format on write. 6. **Every explicit type of a column with several codecs carries a suffix.** `Timestamptz`, `Timestamp`, `Date` and `Time` become `TimestamptzTemporal`, `TimestampTemporal`, `DateTemporal` and `TimeTemporal`. `Interval` becomes `IntervalParts`, next to the new `IntervalString`. 7. **Four codecs are added:** a `timestamp` column read as a `Date` and as a `Temporal.Instant`, both treating the stored value as UTC; and an `interval` column read as an ISO 8601 duration string. (The `parts` codec already exists.) -8. **String values are ISO 8601, the same on every server.** Every string codec returns the form ADR 254 stores, not PostgreSQL's display text. The codec ids do not change: this is a bug fix, and nothing keeps the old format before RC. Prisma requires `DateStyle=ISO` on its connections; it sets it on the connections it opens, and a connection the user supplies with another `DateStyle` is refused with an error naming the setting. Time zones need no setting, because the codecs convert any offset to UTC. +8. **String values are ISO 8601, the same on every server.** Every string codec returns the form ADR 254 stores, not PostgreSQL's display text. The codec ids do not change: this is a bug fix, and nothing keeps the old format before RC. Prisma requires `DateStyle=ISO` on its connections; it sets it on the connections it opens, and a connection the user supplies with another `DateStyle` is refused with an error naming the setting. Time zones need no setting: the `timestamptz` codec converts any session offset to UTC, and the `timetz` codec keeps the offset stored with the value. 9. **Temporal fields are never typed `any`.** The Temporal codecs and generators take their types from `temporal-spec`, a types-only package, instead of an undeclared global. No polyfill code is added anywhere. A missing global `Temporal` at runtime is still reported on first use, as today; a startup check was considered and dropped, because Temporal values are now an explicit opt-in. 10. **`contract infer` and `contract print` write convenience types**, with arguments only where they differ from the defaults; a column no convenience type covers (`Temporal.PlainDateTime`) is written as its explicit type. **The Prisma 7 reader** maps Prisma 7's `DateTime` to `TimestampJsDate(3)`, printed `DateTime(column: timestamp)`, so an upgraded app reads `Date` values again, and maps Prisma 7's `@default(now())` to `@default(now()) @onCreate(now())`, as Prisma 7 behaved ([TML-3719](https://linear.app/prisma-company/issue/TML-3719)). 11. **PostgreSQL only.** A convenience name means the same thing on every target; SQLite and MongoDB follow in [TML-3714](https://linear.app/prisma-company/issue/TML-3714). From 85f1a0aa2c40646c65382f6e58f04d6672a9c21c Mon Sep 17 00:00:00 2001 From: willbot Date: Thu, 22 Oct 2026 17:18:06 +0200 Subject: [PATCH 007/118] TML-3709: ADR 279 states that IntervalString reads every IntervalStyle and returns the stored ISO duration Co-Authored-By: Claude Opus 5.5 Signed-off-by: willbot Signed-off-by: Will Madden --- ...d time types choose an explicit type from their arguments.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/architecture docs/adrs/ADR 279 - Convenience date and time types choose an explicit type from their arguments.md b/docs/architecture docs/adrs/ADR 279 - Convenience date and time types choose an explicit type from their arguments.md index c77502f2ef57..36021b0f83b3 100644 --- a/docs/architecture docs/adrs/ADR 279 - Convenience date and time types choose an explicit type from their arguments.md +++ b/docs/architecture docs/adrs/ADR 279 - Convenience date and time types choose an explicit type from their arguments.md @@ -58,7 +58,7 @@ The contract stores what the explicit type stores: the codec, its data type and This frees `DateTime`, `Date` and `Time` for the convenience types. Without the suffix, a name such as `Timestamptz` would look like a neutral default when it is one specific kind of value. For the same reason, `Interval`, which reads the object `{ months, days, micros }`, becomes `IntervalParts`, next to the new `IntervalString`. -7. **Three codecs are added:** a `timestamp` column read as a JavaScript `Date`, and a `timestamp` column read as a `Temporal.Instant`, both reading and writing the stored value as UTC; and an `interval` column read as an ISO 8601 duration string. Each gets an explicit type: `TimestampJsDate(p)` for the `Date` codec, `TimestampInstant(p)` for the `Temporal.Instant` codec, and `IntervalString(p)`. `DateTime(column: timestamp)` with `value: jsDate` becomes `TimestampJsDate`, with `value: temporal` becomes `TimestampInstant`, and `Duration` with `value: string` becomes `IntervalString`. `TimestampInstant` names its `Temporal` type rather than taking the `Temporal` suffix, because the `timestamp` column has two `Temporal` codecs and `TimestampTemporal` is the one that reads `Temporal.PlainDateTime`. +7. **Three codecs are added:** a `timestamp` column read as a JavaScript `Date`, and a `timestamp` column read as a `Temporal.Instant`, both reading and writing the stored value as UTC; and an `interval` column read as an ISO 8601 duration string. Each gets an explicit type: `TimestampJsDate(p)` for the `Date` codec, `TimestampInstant(p)` for the `Temporal.Instant` codec, and `IntervalString(p)`. `IntervalString` reads interval text in every `IntervalStyle` PostgreSQL prints (`postgres`, `postgres_verbose`, `sql_standard`, `iso_8601`) and returns the ISO 8601 duration ADR 254 stores, so it needs no `IntervalStyle` setting. `DateTime(column: timestamp)` with `value: jsDate` becomes `TimestampJsDate`, with `value: temporal` becomes `TimestampInstant`, and `Duration` with `value: string` becomes `IntervalString`. `TimestampInstant` names its `Temporal` type rather than taking the `Temporal` suffix, because the `timestamp` column has two `Temporal` codecs and `TimestampTemporal` is the one that reads `Temporal.PlainDateTime`. 8. **The Prisma 7 reader maps Prisma 7's `DateTime` to `TimestampJsDate(3)`,** which `contract print` writes as `DateTime(column: timestamp)`: a `timestamp(3)` column read as a JavaScript `Date`, instead of a string. The column and its stored values do not change; only the application value goes from a string back to the `Date` that Prisma 7 gave, so an upgraded application keeps the values its code was written for. `prisma orm init`, which creates new columns, writes plain `DateTime`, a `timestamptz(3)` column read as a `Date`. 9. **A string value is ISO 8601, the same on every server.** Every string codec of a date or time type returns the form its data type stores (see ADR 254, "Date and time types") and reads that form back unchanged. PostgreSQL's display text, such as `2024-01-01 00:00:00+00` or a year written with BC, never reaches the application. The `timestamptz` codec converts any session time zone offset to UTC itself; the `timetz` codec keeps the offset the value was stored with, because that offset is part of a `timetz` value. Prisma requires `DateStyle=ISO` on its database connections: it sets it on the connections it opens, and refuses a value it reads in another style with an error naming the setting. The codec ids do not change; the old text was never a format an application could rely on. 10. **`contract infer` and `contract print` write convenience types,** with arguments only where they differ from the defaults. A column no convenience type covers, a `timestamp` read as `Temporal.PlainDateTime`, is written as its explicit type. `prisma orm init` writes `DateTime`. From 202419d82ab0c0482d021171037392765ff3dd22 Mon Sep 17 00:00:00 2001 From: willbot Date: Thu, 22 Oct 2026 17:21:18 +0200 Subject: [PATCH 008/118] TML-3709: Slice 5 also updates slice 2's upgrade instruction Co-Authored-By: Claude Opus 5.5 Signed-off-by: willbot Signed-off-by: Will Madden --- projects/datetime-convenience-types/plan.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/projects/datetime-convenience-types/plan.md b/projects/datetime-convenience-types/plan.md index 4a403b924a7e..43882661c3da 100644 --- a/projects/datetime-convenience-types/plan.md +++ b/projects/datetime-convenience-types/plan.md @@ -36,7 +36,7 @@ A type constructor's `codecId` may be a `select` over `option` arguments; every ### Slice 5 — The tools write convenience types -`contract infer` and `contract print` write convenience types, with arguments only where they differ from the defaults, and the explicit type only where no convenience type fits. The Prisma 7 reader maps `DateTime` to `DateTime(column: timestamp)` and `@db.*` date and time columns to the matching convenience form. `prisma orm init` writes `DateTime`. Upgrade instructions: re-emit and `db sign` for adopted projects. +`contract infer` and `contract print` write convenience types, with arguments only where they differ from the defaults, and the explicit type only where no convenience type fits. The Prisma 7 reader maps `DateTime` to `DateTime(column: timestamp)` and `@db.*` date and time columns to the matching convenience form. `prisma orm init` writes `DateTime`. Upgrade instructions: re-emit and `db sign` for adopted projects. Update slice 2's upgrade instruction (`upgrade-instructions/pending/prisma7-now-default-on-create/`), which names the type the reader picks today (`TimestampString(3)`). ## Dependencies From ab403be8a2a2a326b7079c342bb49d629586146b Mon Sep 17 00:00:00 2001 From: willbot Date: Thu, 22 Oct 2026 17:26:27 +0200 Subject: [PATCH 009/118] TML-3709: Slice 2 spec: re-emit only, no db sign, since the marker holds no execution hash Co-Authored-By: Claude Opus 5.5 Signed-off-by: willbot Signed-off-by: Will Madden --- .../slices/02-prisma7-now-defaults/spec.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/projects/datetime-convenience-types/slices/02-prisma7-now-defaults/spec.md b/projects/datetime-convenience-types/slices/02-prisma7-now-defaults/spec.md index dc9f6bdaac7e..18e9f0ebb65e 100644 --- a/projects/datetime-convenience-types/slices/02-prisma7-now-defaults/spec.md +++ b/projects/datetime-convenience-types/slices/02-prisma7-now-defaults/spec.md @@ -24,11 +24,11 @@ Prisma 7 filled `@default(now())` on the client and also wrote the database defa - The Prisma 7 reader (`packages/2-sql/2-authoring/contract-prisma7`) lowers `@default(now())` on a date or time field to both the database default and an `onCreate` generator, through the generator function registry, exactly as Prisma 8 PSL's `@default(now()) @onCreate(now())` lowers. It resolves the generator by the field's codec, so it is correct for whatever codec the reader picks today and after slice 5 changes it. - `@default(now())` together with `@updatedAt` keeps the database default and both generators. -- The storage hash does not change (the column and its default are the same); the execution hash does, because the contract gains a generator. Adopted projects re-emit and run `db sign`; the upgrade instruction says so. +- The storage hash does not change (the column and its default are the same); the execution hash does, because the contract gains a generator. Adopted projects re-emit and commit the contract; there is no migration and no `db sign`, because the database marker records only the storage and profile hashes. ## Scope -**In:** the Prisma 7 reader's default lowering, its tests, the Prisma 7 adoption journey test and `examples/prisma7-adoption` if its contract changes, the `contract-prisma7` README, an `app` upgrade instruction. +**In:** the Prisma 7 reader's default lowering, its tests, the Prisma 7 adoption journey test and `examples/prisma7-adoption` if its contract changes, the `contract-prisma7` README, an `app` upgrade instruction, and an empty `extension` declaration for the Postgres README edit. **Out:** any change to which codec the reader picks (slice 5); `contract infer`, which reads a database and cannot know the default came from Prisma 7. From 576ab6ce85965b42179abd1a3350e56d879e4934 Mon Sep 17 00:00:00 2001 From: willbot Date: Thu, 22 Oct 2026 17:13:14 +0200 Subject: [PATCH 010/118] Temporal fields take their types from temporal-spec The PostgreSQL target's Temporal codecs and now generators named Temporal through an undeclared global, so a project without the polyfill's global type declarations saw every Temporal field as any. They now import the types from temporal-spec, a types-only dependency at the version temporal-polyfill 1.0.x uses. No runtime entry imports anything new. A type test in the postgres extension, compiled without any global Temporal declaration, checks a Temporal field is precisely typed, and the target's src is type-checked without the global declarations. Co-Authored-By: Claude Opus 5.5 Signed-off-by: willbot Signed-off-by: Will Madden --- packages/3-extensions/postgres/package.json | 5 ++- .../temporal-field-types.test-d.ts | 41 +++++++++++++++++++ .../postgres/tsconfig.no-temporal-global.json | 4 ++ .../3-targets/3-targets/postgres/README.md | 2 +- .../3-targets/3-targets/postgres/package.json | 5 ++- .../src/core/date-time-now-generators.ts | 1 + .../postgres/src/core/require-temporal.ts | 10 ++++- .../src/core/temporal-codec-helpers.ts | 1 + .../postgres/src/core/temporal-codecs.ts | 1 + .../{src => test}/temporal-global.d.ts | 0 .../postgres/tsconfig.no-temporal-global.json | 4 ++ .../@prisma/orm-target-postgres/package.json | 3 +- pnpm-lock.yaml | 9 ++++ 13 files changed, 78 insertions(+), 8 deletions(-) create mode 100644 packages/3-extensions/postgres/test/no-temporal-global/temporal-field-types.test-d.ts create mode 100644 packages/3-extensions/postgres/tsconfig.no-temporal-global.json rename packages/3-targets/3-targets/postgres/{src => test}/temporal-global.d.ts (100%) create mode 100644 packages/3-targets/3-targets/postgres/tsconfig.no-temporal-global.json diff --git a/packages/3-extensions/postgres/package.json b/packages/3-extensions/postgres/package.json index 4bf454104dab..b41b62a3115c 100644 --- a/packages/3-extensions/postgres/package.json +++ b/packages/3-extensions/postgres/package.json @@ -11,7 +11,7 @@ "emit": "cd ../../../test/integration && node ../../packages/1-framework/3-tooling/cli/dist/bin.mjs contract emit --config test/sql-builder/fixtures/prisma.config.no-pgvector.ts && cp test/sql-builder/fixtures/generated-no-pgvector/contract.json test/sql-builder/fixtures/generated-no-pgvector/contract.d.ts ../../packages/3-extensions/postgres/test/fixtures/generated/", "emit:check": "pnpm emit && git diff --exit-code test/fixtures/generated/", "test": "vitest run", - "typecheck": "tsc --project tsconfig.json --noEmit && tsc --project tsconfig.no-exact-optional.json --noEmit", + "typecheck": "tsc --project tsconfig.json --noEmit && tsc --project tsconfig.no-exact-optional.json --noEmit && tsc --project tsconfig.no-temporal-global.json --noEmit", "lint": "biome check . --error-on-warnings", "lint:fix": "biome check --write .", "lint:fix:unsafe": "biome check --write --unsafe .", @@ -25,11 +25,11 @@ "@internal/driver-postgres": "workspace:8.0.0-rc.19", "@internal/family-sql": "workspace:8.0.0-rc.19", "@internal/framework-components": "workspace:8.0.0-rc.19", + "@internal/sql-builder": "workspace:8.0.0-rc.19", "@internal/sql-contract": "workspace:8.0.0-rc.19", "@internal/sql-contract-prisma7": "workspace:8.0.0-rc.19", "@internal/sql-contract-psl": "workspace:8.0.0-rc.19", "@internal/sql-contract-ts": "workspace:8.0.0-rc.19", - "@internal/sql-builder": "workspace:8.0.0-rc.19", "@internal/sql-orm-client": "workspace:8.0.0-rc.19", "@internal/sql-relational-core": "workspace:8.0.0-rc.19", "@internal/sql-runtime": "workspace:8.0.0-rc.19", @@ -52,6 +52,7 @@ "@repo/tsconfig": "workspace:8.0.0-rc.19", "@repo/tsdown": "workspace:8.0.0-rc.19", "temporal-polyfill": "^1.0.4", + "temporal-spec": "1.0.1", "tsdown": "catalog:", "typescript": "catalog:", "vitest": "catalog:" diff --git a/packages/3-extensions/postgres/test/no-temporal-global/temporal-field-types.test-d.ts b/packages/3-extensions/postgres/test/no-temporal-global/temporal-field-types.test-d.ts new file mode 100644 index 000000000000..346f4f23da20 --- /dev/null +++ b/packages/3-extensions/postgres/test/no-temporal-global/temporal-field-types.test-d.ts @@ -0,0 +1,41 @@ +import type { DefaultModelRow } from '@internal/sql-orm-client'; +import type { CodecTypes } from '@internal/target-postgres/codec-types'; +import type { Temporal } from 'temporal-spec'; +import { expectTypeOf, test } from 'vitest'; +import { defineContract } from '../../src/exports/contract-builder'; + +const contract = defineContract({}, ({ type, field, model }) => ({ + models: { + Event: model('Event', { + fields: { + id: field.id.uuidv4String(), + at: field.column(type.Timestamptz(3)), + local: field.column(type.Timestamp()), + day: field.column(type.Date()), + opens: field.column(type.Time(0)), + }, + }), + }, +})); + +type EventRow = DefaultModelRow; + +test('a Temporal field is typed precisely in a project without the global Temporal declarations', () => { + expectTypeOf().not.toBeAny(); + expectTypeOf().toEqualTypeOf(); + expectTypeOf().toEqualTypeOf(); + expectTypeOf().toEqualTypeOf(); + expectTypeOf().toEqualTypeOf(); +}); + +test('every Temporal codec names its type through an import', () => { + expectTypeOf().not.toBeAny(); + expectTypeOf< + CodecTypes['pg/timestamptz-temporal@1']['output'] + >().toEqualTypeOf(); + expectTypeOf< + CodecTypes['pg/timestamp-temporal@1']['output'] + >().toEqualTypeOf(); + expectTypeOf().toEqualTypeOf(); + expectTypeOf().toEqualTypeOf(); +}); diff --git a/packages/3-extensions/postgres/tsconfig.no-temporal-global.json b/packages/3-extensions/postgres/tsconfig.no-temporal-global.json new file mode 100644 index 000000000000..4ca6a1fd62ee --- /dev/null +++ b/packages/3-extensions/postgres/tsconfig.no-temporal-global.json @@ -0,0 +1,4 @@ +{ + "extends": "./tsconfig.json", + "include": ["test/no-temporal-global/**/*.ts"] +} diff --git a/packages/3-targets/3-targets/postgres/README.md b/packages/3-targets/3-targets/postgres/README.md index f347383d31a7..78a4f914b1d3 100644 --- a/packages/3-targets/3-targets/postgres/README.md +++ b/packages/3-targets/3-targets/postgres/README.md @@ -28,7 +28,7 @@ Provides the Postgres target descriptor (`SqlControlTargetDescriptor`) for CLI c - **Storage Type Planning**: The planner dispatches storage type hooks for `storage.types` and emits type operations before table creation when supported by the policy - **Contract-to-PSL Printing**: Implements the `buildPslContract` descriptor hook, which `prisma contract print` uses. It takes a Postgres contract the contract serializer accepted, from any source, and returns the Prisma 8 PSL document that reads back as the same contract. It does not check the contract's structure again; a broken structure is an internal error. It writes models, value objects, named types, domain and native enums, keys with their names, indexes, checks, relations, polymorphism, control policies, row-level security (`@@rls`, policies, and roles in `namespace unbound`) and defaults. A column is written as a type constructor the configured stack contributes that reads back with its codec, native type and type parameters: the constructor marked `inferred` for the column's data type, which is the one `contract infer` writes, when it does, or else the first constructor in the stack that does, such as `DateTime` or `Timestamptz(3)` for a `Temporal`-backed `timestamptz` column, or an extension's `pgvector.Vector(3)`. A value-object member has no column, so its type goes through the stack the same way: the stack's codec names the native type for the member's type parameters, and the member is written as the type constructor for that native type, called with those parameters (`Numeric(10, 2)`); a member typed by a domain enum is written by the enum's name. The contract does not record which named type a member used, so a member written `code Short`, with `types { Short = VarChar(10) }`, is printed as the type it stands for, `code VarChar(10)`; a model field keeps `Short`, because its column names the named type. A literal default is written by the data type of the column's codec. The family passes the stack's type constructors, codecs and data types to the hook for this. Anything it cannot write so that it reads back the same is refused by name with `CONTRACT.PRINT_UNSUPPORTED` (the full list is in `docs/reference/error-reference.md`, one function per case in `src/core/psl-print/refusals.ts`); nothing is dropped. It lives in `src/core/psl-print/`, the counterpart of the `inferPslContract` hook in `src/core/psl-infer/`, which writes PSL from a live database. Both build with the shared builders in `src/core/psl-build/`: literals, index and check attributes, and the native enum block. Both find the constructor marked `inferred` for a data type through the SQL family's `@internal/family-sql/psl-build`. Model and field names come from the contract; `@@map` and `@map` appear only where the name the PSL source would derive differs. A unique constraint prints as `@@unique` and a unique index as `@@index(..., unique: true)`; every index prints explicitly, and a relation writes `index: false` when nothing backs its foreign key, no `index` argument when a relation that says nothing would be backed by the same object (`defaultForeignKeyIndex`), and otherwise `index: ""` naming the index, or the key whose first columns are the foreign key's. Execution generators join back onto their columns, through whatever the configured stack contributes, extensions included. A column that is not a list and whose PSL type stands for its data type prints as the call of the field preset that writes its codec with its generators, built from the preset's print form, when the preset accepts that call and it reads back with the column's codec, type params and generators. A column typed by a named type, a value object or an enum, and a column whose call fails those checks, such as one without a value for a required argument, does not. A column with only a create generator then prints it as `@default((...))`, the call of the default function that declares `printArguments` for it; any other column is refused. - **Database-to-PSL inference**: Implements the `inferPslContract` descriptor hook, which `prisma contract infer` uses. Introspection records each column's element type as the database reports it (`format_type`, its kind, schema and name). Infer resolves that reported type through the data types of the configured stack, the target's and the extension packs', and writes the constructor marked `inferred` for the data type, with the fewest arguments that give the same parameters (`numeric(10,0)` is written `Numeric(10)`); a list column adds `[]`, and a native enum column is written `pg.enum()`. When a column's type is claimed by no data type, or by one with no marked constructor (such as `tsquery`), infer writes nothing and fails with `CONTRACT.INFER_COLUMN_TYPE_UNCLAIMED`, listing every such column as `..: `. No configured pack has a type for such a column. When the type comes from a database extension that has a Prisma 8 pack, such as `pgvector` or `postgis`, adding the pack to `extensions` in `prisma.config.ts` lets infer write it, for the forms of the type the pack supports. Otherwise, as for domains, `"char"`, `hstore` and `tsquery`, the column's type must change, or the column must go. The rule itself (resolving a reported type through the data types, the fewest-arguments call, the unclaimed error) lives in the SQL family's `@internal/family-sql/psl-build`; the target adds the walk over its schema tree and the native enum handling in `src/core/psl-infer/`. -- **`Temporal` for the target's own code**: The `Temporal`-backed codecs (`pg/date-temporal@1`, `pg/time-temporal@1`, `pg/timestamp-temporal@1` and `pg/timestamptz-temporal@1`), when they decode, and the `instantNow`, `plainDateTimeNow`, `plainDateNow` and `plainTimeNow` generators get `Temporal` from `requireTemporal()` (`src/core/require-temporal.ts`): the runtime's global `Temporal` when it has one, else the fallback `Temporal`, else it throws `RUNTIME.TEMPORAL_UNAVAILABLE`. The text codecs (`pg/date-string@1`, `pg/time-string@1`, `pg/timestamp-string@1`, `pg/timestamptz-string@1` and `pg/timetz@1`), `pg/timestamptz-date@1`, the `timestampNow` generator and the string generators (`timestampStringNow`, `timestamptzStringNow`, `dateStringNow`, `timeStringNow` and `timetzNow`) do not use `Temporal`, so an application that uses only them reads and writes its date and time columns without one. The target's control entry (`@internal/target-postgres/control`) sets the fallback `Temporal`, from `temporal-polyfill`, when it is loaded. Nothing sets `globalThis.Temporal`. No other entry of this package imports `temporal-polyfill`. `temporal-polyfill` is a required peer dependency of this package and of the published `@prisma/orm-target-postgres` and `@prisma/orm-postgres`, so a project that already installs it for its own code has one copy. npm, pnpm and bun install it automatically; a project that uses Yarn adds `temporal-polyfill` to its own dependencies. In the published package, `@prisma/orm-target-postgres/target` is the aggregate of every entry of this package, the control entry included, so it loads the control entry too; an application imports the entry it needs, for example `@prisma/orm-target-postgres/target/runtime`. The fallback is held once per process, and every `Temporal`-backed codec in the process uses it. An application process that loads no control-plane code and has no global `Temporal` gets `RUNTIME.TEMPORAL_UNAVAILABLE` when one of those codecs decodes or one of those generators runs. An application that loads control-plane code in its own process, such as server code under `vite dev` with the Prisma Vite plugin or a script that calls the control client, decodes dates without its own `Temporal`, and then fails in production. An application that uses the Temporal codecs must load its own `Temporal`. +- **`Temporal` for the target's own code**: The `Temporal`-backed codecs (`pg/date-temporal@1`, `pg/time-temporal@1`, `pg/timestamp-temporal@1` and `pg/timestamptz-temporal@1`), when they decode, and the `instantNow`, `plainDateTimeNow`, `plainDateNow` and `plainTimeNow` generators get `Temporal` from `requireTemporal()` (`src/core/require-temporal.ts`): the runtime's global `Temporal` when it has one, else the fallback `Temporal`, else it throws `RUNTIME.TEMPORAL_UNAVAILABLE`. The text codecs (`pg/date-string@1`, `pg/time-string@1`, `pg/timestamp-string@1`, `pg/timestamptz-string@1` and `pg/timetz@1`), `pg/timestamptz-date@1`, the `timestampNow` generator and the string generators (`timestampStringNow`, `timestamptzStringNow`, `dateStringNow`, `timeStringNow` and `timetzNow`) do not use `Temporal`, so an application that uses only them reads and writes its date and time columns without one. The target's control entry (`@internal/target-postgres/control`) sets the fallback `Temporal`, from `temporal-polyfill`, when it is loaded. Nothing sets `globalThis.Temporal`. No other entry of this package imports `temporal-polyfill`. `temporal-polyfill` is a required peer dependency of this package and of the published `@prisma/orm-target-postgres` and `@prisma/orm-postgres`, so a project that already installs it for its own code has one copy. npm, pnpm and bun install it automatically; a project that uses Yarn adds `temporal-polyfill` to its own dependencies. In the published package, `@prisma/orm-target-postgres/target` is the aggregate of every entry of this package, the control entry included, so it loads the control entry too; an application imports the entry it needs, for example `@prisma/orm-target-postgres/target/runtime`. The fallback is held once per process, and every `Temporal`-backed codec in the process uses it. An application process that loads no control-plane code and has no global `Temporal` gets `RUNTIME.TEMPORAL_UNAVAILABLE` when one of those codecs decodes or one of those generators runs. An application that loads control-plane code in its own process, such as server code under `vite dev` with the Prisma Vite plugin or a script that calls the control client, decodes dates without its own `Temporal`, and then fails in production. An application that uses the Temporal codecs must load its own `Temporal`. Every signature that names a `Temporal` type takes it from `temporal-spec`, a package of type declarations with no runtime code (`import type { Temporal } from 'temporal-spec'`), never from a global declaration, so a Temporal field is typed precisely in a project that has no global `Temporal` declarations. `tsconfig.no-temporal-global.json` type-checks `src` without them. - **Runtime List Framing**: Parses inbound Postgres array text for contract-declared list columns before applying the scalar element codec. Builtin arrays and enum arrays therefore share one target-owned decode path; fixed-scale `numeric(30,10)[]` reads database-normalized text such as `"1.5000000000"`, matching scalar numeric decoding rather than the previous driver numeric-array float spelling `"1.5"`. This package spans multiple planes: diff --git a/packages/3-targets/3-targets/postgres/package.json b/packages/3-targets/3-targets/postgres/package.json index 41885a764c3d..8f896c5f44a2 100644 --- a/packages/3-targets/3-targets/postgres/package.json +++ b/packages/3-targets/3-targets/postgres/package.json @@ -9,7 +9,7 @@ "scripts": { "build": "tsdown", "test": "vitest run --passWithNoTests", - "typecheck": "tsc --project tsconfig.json --noEmit", + "typecheck": "tsc --project tsconfig.json --noEmit && tsc --project tsconfig.no-temporal-global.json --noEmit", "lint": "biome check . --error-on-warnings", "lint:fix": "biome check --write .", "lint:fix:unsafe": "biome check --write --unsafe .", @@ -34,7 +34,8 @@ "@standard-schema/spec": "^1.1.0", "arktype": "^2.2.2", "pathe": "^2.0.3", - "postgres-array": "2.0.0" + "postgres-array": "2.0.0", + "temporal-spec": "1.0.1" }, "devDependencies": { "@electric-sql/pglite": "0.5.4", diff --git a/packages/3-targets/3-targets/postgres/src/core/date-time-now-generators.ts b/packages/3-targets/3-targets/postgres/src/core/date-time-now-generators.ts index f4bc162001b8..87b1aaa79da8 100644 --- a/packages/3-targets/3-targets/postgres/src/core/date-time-now-generators.ts +++ b/packages/3-targets/3-targets/postgres/src/core/date-time-now-generators.ts @@ -1,4 +1,5 @@ import type { MutationDefaultGeneratorDescriptor } from '@internal/framework-components/control'; +import type { Temporal } from 'temporal-spec'; import { requireTemporal } from './require-temporal'; /** diff --git a/packages/3-targets/3-targets/postgres/src/core/require-temporal.ts b/packages/3-targets/3-targets/postgres/src/core/require-temporal.ts index c751b1217062..330b49ba0fd5 100644 --- a/packages/3-targets/3-targets/postgres/src/core/require-temporal.ts +++ b/packages/3-targets/3-targets/postgres/src/core/require-temporal.ts @@ -1,3 +1,5 @@ +import { blindCast } from '@internal/utils/casts'; +import type { Temporal } from 'temporal-spec'; import { errorTemporalUnavailable, errorTemporalUnavailableForDefault } from './errors'; export type TemporalImplementation = typeof Temporal; @@ -22,8 +24,12 @@ export function setFallbackTemporal(implementation: TemporalImplementation): voi * `RUNTIME.TEMPORAL_UNAVAILABLE` when there is neither. */ export function requireTemporal(meta: TemporalUnavailableMeta): TemporalImplementation { - if (typeof Temporal !== 'undefined') { - return Temporal; + const runtimeTemporal = blindCast< + { readonly Temporal?: TemporalImplementation }, + 'globalThis holds the runtime Temporal when the runtime has one' + >(globalThis).Temporal; + if (runtimeTemporal !== undefined) { + return runtimeTemporal; } if (fallback !== undefined) { return fallback; diff --git a/packages/3-targets/3-targets/postgres/src/core/temporal-codec-helpers.ts b/packages/3-targets/3-targets/postgres/src/core/temporal-codec-helpers.ts index 76d90c6e7710..23fc4e3b7cc7 100644 --- a/packages/3-targets/3-targets/postgres/src/core/temporal-codec-helpers.ts +++ b/packages/3-targets/3-targets/postgres/src/core/temporal-codec-helpers.ts @@ -1,4 +1,5 @@ import { refuseJsonValue } from '@internal/framework-components/codec'; +import type { Temporal } from 'temporal-spec'; import { PG_DATE_TEMPORAL_CODEC_ID, PG_TIME_TEMPORAL_CODEC_ID, diff --git a/packages/3-targets/3-targets/postgres/src/core/temporal-codecs.ts b/packages/3-targets/3-targets/postgres/src/core/temporal-codecs.ts index b31277ac827a..51a0a5fdb90c 100644 --- a/packages/3-targets/3-targets/postgres/src/core/temporal-codecs.ts +++ b/packages/3-targets/3-targets/postgres/src/core/temporal-codecs.ts @@ -9,6 +9,7 @@ import { } from '@internal/framework-components/codec'; import { CastExpr, type ProjectionExpr } from '@internal/sql-relational-core/ast'; import type { StandardSchemaV1 } from '@standard-schema/spec'; +import type { Temporal } from 'temporal-spec'; import { PostgresCodecDescriptor } from './codec-descriptor'; import type { PrecisionParams } from './codec-helpers'; import { diff --git a/packages/3-targets/3-targets/postgres/src/temporal-global.d.ts b/packages/3-targets/3-targets/postgres/test/temporal-global.d.ts similarity index 100% rename from packages/3-targets/3-targets/postgres/src/temporal-global.d.ts rename to packages/3-targets/3-targets/postgres/test/temporal-global.d.ts diff --git a/packages/3-targets/3-targets/postgres/tsconfig.no-temporal-global.json b/packages/3-targets/3-targets/postgres/tsconfig.no-temporal-global.json new file mode 100644 index 000000000000..bdc63200b33d --- /dev/null +++ b/packages/3-targets/3-targets/postgres/tsconfig.no-temporal-global.json @@ -0,0 +1,4 @@ +{ + "extends": "./tsconfig.json", + "include": ["src/**/*"] +} diff --git a/packages/9-public/@prisma/orm-target-postgres/package.json b/packages/9-public/@prisma/orm-target-postgres/package.json index 1645d0648732..1d07b51d6763 100644 --- a/packages/9-public/@prisma/orm-target-postgres/package.json +++ b/packages/9-public/@prisma/orm-target-postgres/package.json @@ -25,7 +25,8 @@ "pathe": "^2.0.3", "pg": "catalog:", "pg-cursor": "^2.21.0", - "postgres-array": "2.0.0" + "postgres-array": "2.0.0", + "temporal-spec": "1.0.1" }, "devDependencies": { "@internal/adapter-postgres": "workspace:8.0.0-rc.19", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 3fe193e8f760..66856d54c61f 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -3557,6 +3557,9 @@ importers: temporal-polyfill: specifier: ^1.0.4 version: 1.0.4 + temporal-spec: + specifier: 1.0.1 + version: 1.0.1 tsdown: specifier: 'catalog:' version: 0.22.14(tsx@4.23.12)(typescript@5.9.3) @@ -4127,6 +4130,9 @@ importers: postgres-array: specifier: 2.0.0 version: 2.0.0 + temporal-spec: + specifier: 1.0.1 + version: 1.0.1 devDependencies: '@electric-sql/pglite': specifier: 0.5.4 @@ -5282,6 +5288,9 @@ importers: temporal-polyfill: specifier: ^1.0.4 version: 1.0.4 + temporal-spec: + specifier: 1.0.1 + version: 1.0.1 devDependencies: '@internal/adapter-postgres': specifier: workspace:8.0.0-rc.19 From 35a6cb9f6a3108b04c79bc968a7387d425e3525d Mon Sep 17 00:00:00 2001 From: willbot Date: Thu, 22 Oct 2026 17:46:53 +0200 Subject: [PATCH 011/118] Rename the Temporal date and time types and fix the IntervalParts output type Every PostgreSQL type for a column with several codecs now carries a suffix naming its kind of value. Timestamptz becomes TimestamptzTemporal, Timestamp becomes TimestampTemporal and Interval becomes IntervalParts, with no deprecated aliases: the old names are errors. DateTemporal and TimeTemporal are added beside Date and Time, which keep their meaning for now. IntervalParts keeps the inferred mark Interval had, so contract infer writes it for an interval column. An interval column with a precision was typed as a branded string although its codec reads { months, days, micros }; it now has the codec's type. The adapter's intervalColumn helper becomes intervalPartsColumn. Every PSL file, PSL string in tests, doc and skill reference moves to the new names. The Supabase contract source changes only its type names. Co-Authored-By: Claude Opus 5.5 Signed-off-by: willbot Signed-off-by: Will Madden --- docs/reference/scalar-types.md | 14 +- examples/prisma-8-demo/package.json | 1 + .../test/fragments.types.test-d.ts | 1 + .../2-sql/2-authoring/contract-psl/README.md | 2 +- .../2-authoring/contract-psl/test/fixtures.ts | 4 +- .../test/interpreter.alias-attributes.test.ts | 22 +-- .../interpreter.contributed-aliases.test.ts | 6 +- .../test/interpreter.diagnostics.test.ts | 16 +- .../test/interpreter.types.test.ts | 4 +- .../postgres/src/exports/contract-builder.ts | 1 - .../contract-builder/type-helpers.test-d.ts | 2 +- .../postgres/test/native-type-parity.test.ts | 32 ++-- .../temporal-field-types.test-d.ts | 10 +- .../postgres/test/scalar-type-parity.test.ts | 16 +- .../contract-builder/type-helpers.test-d.ts | 2 +- .../supabase/scripts/generate-contract.ts | 2 +- .../supabase/src/contract/contract.prisma | 158 +++++++++--------- .../supabase/src/contract/handles.ts | 2 +- .../3-targets/3-targets/postgres/README.md | 2 +- .../postgres/src/core/type-constructors.ts | 22 ++- .../postgres/src/exports/codec-types.ts | 4 - .../test/fixtures/namespaced-contract.d.ts | 1 - .../test/parameterized-output-types.test-d.ts | 7 +- .../test/psl-infer/infer-column-types.test.ts | 4 +- .../postgres/test/type-constructors.test.ts | 49 ++++-- .../3-targets/6-adapters/postgres/README.md | 2 +- .../postgres/src/exports/column-types.ts | 8 +- .../bit-interval-columns.integration.test.ts | 4 +- pnpm-lock.yaml | 6 + skills/prisma-8/references/contract.md | 2 +- skills/prisma-8/references/debug.md | 2 +- test/e2e/framework/test/fixtures/contract.ts | 4 +- test/integration/package.json | 1 + ...lts-on-existing-tables.integration.test.ts | 2 +- .../default-date-time-types/schema.prisma | 2 +- ...lane-commands-without-temporal.e2e.test.ts | 6 +- ...te-time-default-canonical-form.e2e.test.ts | 4 +- .../defaults-read-by-codec.e2e.test.ts | 18 +- .../test/contract-builder.types.test-d.ts | 3 +- .../rendered-output-types.test.ts | 3 +- .../contract-with-tags.prisma | 2 +- .../contract.prisma | 2 +- ...psl-date-time-defaults.integration.test.ts | 16 +- .../enum-members.postgres.test.ts | 9 +- .../contract-date-time-defaults.ts | 6 +- .../fixtures/postgres/contract.ts | 8 +- .../_fixture/contract.prisma | 4 +- ...arked-types-round-trip.integration.test.ts | 4 +- .../date-time-types-print.integration.test.ts | 14 +- .../include-codec-canonical-json.test.ts | 4 +- .../_fixture-attributes/contract.prisma | 12 +- .../fixtures/generated/sql-contract.d.ts | 1 - 52 files changed, 293 insertions(+), 240 deletions(-) diff --git a/docs/reference/scalar-types.md b/docs/reference/scalar-types.md index 6e021a903bd0..6444f70d1963 100644 --- a/docs/reference/scalar-types.md +++ b/docs/reference/scalar-types.md @@ -80,19 +80,19 @@ These are the current names. The Postgres and SQLite rename project will change | `Real` | — | `pg/float4@1` | `float4` | `number` | | `Float` | `field.float()` | `pg/float8@1` | `float8` | `number` | | `Decimal`, `Numeric(p?, s?)` | `field.decimal()` | `pg/numeric@1` | `numeric` | `string` (decimal text) | -| `DateTime`, `Timestamptz(p?)` | `field.dateTime()` | `pg/timestamptz-temporal@1` | `timestamptz` | `Temporal.Instant` | +| `DateTime`, `TimestamptzTemporal(p?)` | `field.dateTime()` | `pg/timestamptz-temporal@1` | `timestamptz` | `Temporal.Instant` | | `TimestamptzJsDate(p?)` | — | `pg/timestamptz-date@1` | `timestamptz` | `Date` | | `TimestamptzString(p?)` | — | `pg/timestamptz-string@1` | `timestamptz` | `string` | -| `Timestamp(p?)` | — | `pg/timestamp-temporal@1` | `timestamp` | `Temporal.PlainDateTime` | +| `TimestampTemporal(p?)` | — | `pg/timestamp-temporal@1` | `timestamp` | `Temporal.PlainDateTime` | | `TimestampString(p?)` | — | `pg/timestamp-string@1` | `timestamp` | `string` | -| `Date` | — | `pg/date-temporal@1` | `date` | `Temporal.PlainDate` | +| `Date`, `DateTemporal` | — | `pg/date-temporal@1` | `date` | `Temporal.PlainDate` | | `DateString` | — | `pg/date-string@1` | `date` | `string` | -| `Time(p?)` | — | `pg/time-temporal@1` | `time` | `Temporal.PlainTime` | +| `Time(p?)`, `TimeTemporal(p?)` | — | `pg/time-temporal@1` | `time` | `Temporal.PlainTime` | | `TimeString(p?)` | — | `pg/time-string@1` | `time` | `string` | | `Timetz(p?)` | — | `pg/timetz@1` | `timetz` | `string` | | `Uuid` | `field.uuidNative()` | `pg/uuid@1` | `uuid` | `string` | | `Inet` | — | `pg/inet@1` | `inet` | `string` | -| `Interval(p?)` | — | `pg/interval@1` | `interval` | `{ months: number; days: number; micros: bigint }` | +| `IntervalParts(p?)` | — | `pg/interval@1` | `interval` | `{ months: number; days: number; micros: bigint }` | | `Bit(n?)` | — | `pg/bit@1` | `bit` | `string` (the bits as `0` and `1`) | | `VarBit(n?)` | — | `pg/varbit@1` | `bit varying` | `string` (the bits as `0` and `1`) | | `Json` | — | `pg/json@1` | `json` | `JsonValue` | @@ -102,7 +102,7 @@ These are the current names. The Postgres and SQLite rename project will change A literal default of a date, time, interval or bytes type is written with the tag of its type, as in `@default(timestamptz`2024-01-01T00:00:00Z`)` or `@default(bytea`\x0102`)`; a quoted string is refused with a message that shows the tag. It is stored in the one form its data type stores, whichever codec the column uses and however the default was written, and a default with more fraction digits or more scale than its column holds is refused rather than rounded; [ADR 254](../architecture%20docs/adrs/ADR%20254%20-%20Data%20types%20and%20casts.md#date-and-time-types) states each form. -The tools that write a schema for you use the text date and time types. In a PSL schema, `prisma orm init` writes `createdAt TimestamptzString @default(now())` and `updatedAt temporal.updatedAtString()`. In a TypeScript contract, it writes `field.temporal.createdAtString()` and `field.temporal.updatedAtString()`. All of these are `TimestamptzString` columns. `prisma contract infer` and `prisma7Schema(...)` write `TimestampString(p)`, `TimestamptzString(p)`, `DateString` and `TimeString(p)`. Four types read and write `Temporal` values: `Timestamptz` (also written `DateTime`), `Timestamp`, `Date` and `Time`. An application that uses them needs a global `Temporal`: on a runtime without one, such as Node.js 24, install it, for example with `import 'temporal-polyfill/full/global'`. +The tools that write a schema for you use the text date and time types. In a PSL schema, `prisma orm init` writes `createdAt TimestamptzString @default(now())` and `updatedAt temporal.updatedAtString()`. In a TypeScript contract, it writes `field.temporal.createdAtString()` and `field.temporal.updatedAtString()`. All of these are `TimestamptzString` columns. `prisma contract infer` and `prisma7Schema(...)` write `TimestampString(p)`, `TimestamptzString(p)`, `DateString` and `TimeString(p)`. Four types read and write `Temporal` values: `TimestamptzTemporal` (also written `DateTime`), `TimestampTemporal`, `DateTemporal` (also written `Date`) and `TimeTemporal` (also written `Time`). Their TypeScript types come from `temporal-spec`, so a project needs no global `Temporal` declarations to type them. An application that uses them needs a global `Temporal`: on a runtime without one, such as Node.js 24, install it, for example with `import 'temporal-polyfill/full/global'`. ## SQLite @@ -134,7 +134,7 @@ The PSL name on each target for a concept, and the Prisma 6/7 name a migrating s | double | `Float` | `Float` | `Float` | `Double` | | decimal | `Decimal` | `Decimal`, `Numeric(p?, s?)` | `Decimal` (stored as text) | `Decimal128` | | boolean | `Boolean` | `Boolean` | — | `Bool` | -| timestamp | `DateTime` | `DateTime`, `Timestamptz(p?)` | `DateTime` | `Date` | +| timestamp | `DateTime` | `DateTime`, `TimestamptzTemporal(p?)` | `DateTime` | `Date` | | text | `String` | `String` | `String` | `String` | | bytes | `Bytes` | `Bytes` | `Bytes` | `Binary` | | JSON | `Json` | `Json`, `Jsonb` | `Json` | `Json` | diff --git a/examples/prisma-8-demo/package.json b/examples/prisma-8-demo/package.json index 68a097309768..f72ff524423a 100644 --- a/examples/prisma-8-demo/package.json +++ b/examples/prisma-8-demo/package.json @@ -45,6 +45,7 @@ "@vitejs/plugin-react-swc": "^4.3.2", "jsdom": "^29.0.2", "temporal-polyfill": "^1.0.4", + "temporal-spec": "1.0.1", "tsx": "catalog:", "typescript": "catalog:", "vite": "catalog:", diff --git a/examples/prisma-8-demo/test/fragments.types.test-d.ts b/examples/prisma-8-demo/test/fragments.types.test-d.ts index 33b482c978ff..9354ff1f9742 100644 --- a/examples/prisma-8-demo/test/fragments.types.test-d.ts +++ b/examples/prisma-8-demo/test/fragments.types.test-d.ts @@ -7,6 +7,7 @@ import { orderByField, } from '@prisma/orm-postgres/orm-client'; import { websearchToTsquery } from '@prisma/orm-postgres/target/full-text'; +import type { Temporal } from 'temporal-spec'; import { describe, expectTypeOf, test } from 'vitest'; import { createdBefore, createdSince, ownedBy, postSummary } from '../src/orm-client/fragments'; import type { ormClientGetFilteredPosts } from '../src/orm-client/get-filtered-posts'; diff --git a/packages/2-sql/2-authoring/contract-psl/README.md b/packages/2-sql/2-authoring/contract-psl/README.md index 73ab598ca7bd..5e33b1f0931f 100644 --- a/packages/2-sql/2-authoring/contract-psl/README.md +++ b/packages/2-sql/2-authoring/contract-psl/README.md @@ -18,7 +18,7 @@ This keeps core/CLI source-agnostic while giving PSL-first SQL users a one-line - Interpret SQL timestamp semantics: `DateTime @default(now())` as a storage default, and `temporal.createdAt()` / `temporal.updatedAt()` as client-side execution mutation defaults - Lower shared constructor expressions in both `types {}` blocks and inline field positions (for example `ShortName = sql.String(length: 35)` and `embedding pgvector.Vector(length: 1536)?`) - Lower supported default functions through composed registry inputs -- Resolve Postgres native storage types from bare names and constructor calls in type position (`Char`, `VarChar`, `Numeric`, `Uuid`, `Inet`, `SmallInt`, `Real`, `Timestamp`, `Timestamptz`, `Date`, `Time`, `Timetz`, `Interval`, `Bit`, `VarBit`, `Json`, `Jsonb`, `BigIntNumber`, `UnboundedInt`) +- Resolve Postgres native storage types from bare names and constructor calls in type position (`Char`, `VarChar`, `Numeric`, `Uuid`, `Inet`, `SmallInt`, `Real`, `TimestampTemporal`, `TimestamptzTemporal`, `Date`, `DateTemporal`, `Time`, `TimeTemporal`, `Timetz`, `IntervalParts`, `Bit`, `VarBit`, `Json`, `Jsonb`, `BigIntNumber`, `UnboundedInt`) - Map PSL relation action tokens to SQL contract referential actions and emit diagnostics for unsupported values - Emit deterministic relation metadata in `models..relations` - Enforce extension composition for namespaced constructor expressions and emit strict diagnostics for unsupported namespaced attributes diff --git a/packages/2-sql/2-authoring/contract-psl/test/fixtures.ts b/packages/2-sql/2-authoring/contract-psl/test/fixtures.ts index 8de356c0b303..f46b96228eda 100644 --- a/packages/2-sql/2-authoring/contract-psl/test/fixtures.ts +++ b/packages/2-sql/2-authoring/contract-psl/test/fixtures.ts @@ -279,7 +279,7 @@ export const postgresScalarAuthoringTypes: AuthoringTypeNamespace = { }, }, }, - Timestamp: { + TimestampTemporal: { kind: 'typeConstructor', args: [{ kind: 'value', dataType: 'pg/int4', name: 'precision', optional: true }], output: { @@ -287,7 +287,7 @@ export const postgresScalarAuthoringTypes: AuthoringTypeNamespace = { typeParams: { precision: { kind: 'arg', index: 0 } }, }, }, - Timestamptz: { + TimestamptzTemporal: { kind: 'typeConstructor', args: [{ kind: 'value', dataType: 'pg/int4', name: 'precision', optional: true }], output: { diff --git a/packages/2-sql/2-authoring/contract-psl/test/interpreter.alias-attributes.test.ts b/packages/2-sql/2-authoring/contract-psl/test/interpreter.alias-attributes.test.ts index c25c6c2eb17a..31989ed8e872 100644 --- a/packages/2-sql/2-authoring/contract-psl/test/interpreter.alias-attributes.test.ts +++ b/packages/2-sql/2-authoring/contract-psl/test/interpreter.alias-attributes.test.ts @@ -50,7 +50,7 @@ function diagnosticsWith(schema: string, contributions: AuthoringContributions) describe('attributes on a types {} alias', () => { it('give each field typed by the alias what the same attributes give when written on the field', () => { const throughAlias = lowered(`types { - Stamp = Timestamp(3) @default(now()) @onCreate(now()) @onUpdate(now()) + Stamp = TimestampTemporal(3) @default(now()) @onCreate(now()) @onUpdate(now()) } model Event { @@ -60,8 +60,8 @@ model Event { }`); const onTheField = lowered(`model Event { id Int @id - createdAt Timestamp(3) @default(now()) @onCreate(now()) @onUpdate(now()) - updatedAt Timestamp(3) @default(now()) @onCreate(now()) @onUpdate(now()) + createdAt TimestampTemporal(3) @default(now()) @onCreate(now()) @onUpdate(now()) + updatedAt TimestampTemporal(3) @default(now()) @onCreate(now()) @onUpdate(now()) }`); expect(throughAlias).toEqual(onTheField); expect(onTheField.defaults).toHaveLength(2); @@ -70,7 +70,7 @@ model Event { it('let a field add an attribute its alias does not set', () => { const throughAlias = lowered(`types { Key = Uuid @onCreate(uuid()) - Stamp = Timestamp(3) @onUpdate(now()) + Stamp = TimestampTemporal(3) @onUpdate(now()) } model Event { @@ -79,7 +79,7 @@ model Event { }`); const onTheField = lowered(`model Event { id Uuid @id @onCreate(uuid()) - updatedAt Timestamp(3) @default(now()) @onUpdate(now()) + updatedAt TimestampTemporal(3) @default(now()) @onUpdate(now()) }`); expect(throughAlias).toEqual(onTheField); }); @@ -133,7 +133,7 @@ model Event { it('refuse a field that repeats an attribute its alias sets, naming the alias', () => { expect( diagnostics(`types { - Stamp = Timestamp(3) @default(now()) @onCreate(now()) @onUpdate(now()) + Stamp = TimestampTemporal(3) @default(now()) @onCreate(now()) @onUpdate(now()) } model Event { @@ -167,7 +167,7 @@ model Event { it('refuse an alias that repeats a generator attribute, naming the alias', () => { expect( diagnostics(`types { - Stamp = Timestamp(3) @onCreate(now()) @onCreate(now()) + Stamp = TimestampTemporal(3) @onCreate(now()) @onCreate(now()) } model Event { @@ -187,7 +187,7 @@ model Event { expect( diagnosticsWith( `types { - Stamp = Timestamp(3) @onCreate(now()) + Stamp = TimestampTemporal(3) @onCreate(now()) } type Audit { @@ -253,7 +253,7 @@ model Event { it('refuse a list field typed by an alias that generates a value', () => { expect( diagnostics(`types { - Stamp = Timestamp(3) @onCreate(now()) + Stamp = TimestampTemporal(3) @onCreate(now()) } model Event { @@ -292,7 +292,7 @@ model Event { it('accept an optional field typed by an alias that generates a value', () => { const throughAlias = lowered(`types { - Stamp = Timestamp(3) @onCreate(now()) + Stamp = TimestampTemporal(3) @onCreate(now()) } model Event { @@ -301,7 +301,7 @@ model Event { }`); const onTheField = lowered(`model Event { id Int @id - stamped Timestamp(3)? @onCreate(now()) + stamped TimestampTemporal(3)? @onCreate(now()) }`); expect(throughAlias).toEqual(onTheField); expect(onTheField.defaults).toHaveLength(1); diff --git a/packages/2-sql/2-authoring/contract-psl/test/interpreter.contributed-aliases.test.ts b/packages/2-sql/2-authoring/contract-psl/test/interpreter.contributed-aliases.test.ts index c707989e3615..9fa60af16e32 100644 --- a/packages/2-sql/2-authoring/contract-psl/test/interpreter.contributed-aliases.test.ts +++ b/packages/2-sql/2-authoring/contract-psl/test/interpreter.contributed-aliases.test.ts @@ -64,8 +64,8 @@ describe('a contributed type alias', () => { }`); const onTheField = lowered(`model Event { id Int @id - createdAt Timestamptz @onCreate(now()) - updatedAt Timestamptz @onCreate(now()) @onUpdate(now()) + createdAt TimestamptzTemporal @onCreate(now()) + updatedAt TimestamptzTemporal @onCreate(now()) @onUpdate(now()) }`); expect(throughAlias).toEqual(onTheField); expect(throughAlias.defaults).toEqual([ @@ -90,7 +90,7 @@ describe('a contributed type alias', () => { ).toEqual( lowered(`model Event { id Int @id - updatedAt Timestamptz? @onCreate(now()) @onUpdate(now()) + updatedAt TimestamptzTemporal? @onCreate(now()) @onUpdate(now()) }`), ); }); diff --git a/packages/2-sql/2-authoring/contract-psl/test/interpreter.diagnostics.test.ts b/packages/2-sql/2-authoring/contract-psl/test/interpreter.diagnostics.test.ts index 69e72a8163df..6219af993ec3 100644 --- a/packages/2-sql/2-authoring/contract-psl/test/interpreter.diagnostics.test.ts +++ b/packages/2-sql/2-authoring/contract-psl/test/interpreter.diagnostics.test.ts @@ -418,7 +418,7 @@ model User { `types { BadChar = Char(0) BadReal = Real(1) - BadTimestamp = Timestamp(-1) + BadTimestamp = TimestampTemporal(-1) } model InvalidNativeTypes { @@ -451,7 +451,7 @@ model InvalidNativeTypes { expect.objectContaining({ code: 'PSL_INVALID_ATTRIBUTE_ARGUMENT', message: expect.stringContaining( - 'Type alias "BadTimestamp" constructor "Timestamp" Authoring helper argument at Timestamp[0] is invalid: precision must be non-negative (was -1)', + 'Type alias "BadTimestamp" constructor "TimestampTemporal" Authoring helper argument at TimestampTemporal[0] is invalid: precision must be non-negative (was -1)', ), }), ]), @@ -463,7 +463,7 @@ model InvalidNativeTypes { ['Char(10485761)', 'length must be at most 10485760 (was 10485761)'], ['Numeric(1001)', 'precision must be at most 1000 (was 1001)'], ['Numeric(10, 1001)', 'scale must be at most 1000 (was 1001)'], - ['Timestamptz(7)', 'precision must be at most 6 (was 7)'], + ['TimestamptzTemporal(7)', 'precision must be at most 6 (was 7)'], ['Time(7)', 'precision must be at most 6 (was 7)'], ['Timetz(7)', 'precision must be at most 6 (was 7)'], ])('refuses %s, outside its data type’s bounds', (call, message) => { @@ -479,8 +479,8 @@ model InvalidNativeTypes { 'Char(10485760)', 'Numeric(1000, 1000)', 'Numeric(1, 0)', - 'Timestamp(0)', - 'Timestamptz(6)', + 'TimestampTemporal(0)', + 'TimestamptzTemporal(6)', ])('accepts %s, at the edge of its data type’s bounds', (call) => { const result = interpretSqlContract(`model T {\n id Int @id\n value ${call}\n}\n`, { ...baseInput, @@ -502,7 +502,7 @@ model InvalidNativeTypes { it.each([ ['VarChar(3000000000)', '3000000000', 'Expected a number that pg/int4 can hold; got pg/int8'], - ['Timestamp(2.5)', '2.5', 'Expected a number that pg/int4 can hold; got pg/numeric'], + ['TimestampTemporal(2.5)', '2.5', 'Expected a number that pg/int4 can hold; got pg/numeric'], ['VarChar(255.0)', '255.0', 'Expected a number that pg/int4 can hold; got pg/numeric'], ['VarChar("255")', '"255"', 'Expected a number; write 255'], ['VarChar(length: true)', 'true', 'Expected a number'], @@ -549,8 +549,8 @@ model InvalidNativeTypes { }, ); - it('reports Timestamp(7) through the data type’s bounds, at the argument', () => { - const schema = 'model T {\n id Int @id\n value Timestamp(7)\n}\n'; + it('reports TimestampTemporal(7) through the data type’s bounds, at the argument', () => { + const schema = 'model T {\n id Int @id\n value TimestampTemporal(7)\n}\n'; const result = interpretSqlContract(schema, { ...baseInput, controlMutationDefaults: builtinControlMutationDefaults, diff --git a/packages/2-sql/2-authoring/contract-psl/test/interpreter.types.test.ts b/packages/2-sql/2-authoring/contract-psl/test/interpreter.types.test.ts index 5820d1952231..5d314c13dfca 100644 --- a/packages/2-sql/2-authoring/contract-psl/test/interpreter.types.test.ts +++ b/packages/2-sql/2-authoring/contract-psl/test/interpreter.types.test.ts @@ -203,8 +203,8 @@ model Event { `types { Code = Char(12) Score = Real - CreatedAt = Timestamp(3) - PublishedAt = Timestamptz(6) + CreatedAt = TimestampTemporal(3) + PublishedAt = TimestamptzTemporal(6) ReminderAt = Timetz(2) Ip = Inet } diff --git a/packages/3-extensions/postgres/src/exports/contract-builder.ts b/packages/3-extensions/postgres/src/exports/contract-builder.ts index 8b639edfdbe4..9cc5dcbf5823 100644 --- a/packages/3-extensions/postgres/src/exports/contract-builder.ts +++ b/packages/3-extensions/postgres/src/exports/contract-builder.ts @@ -30,7 +30,6 @@ export { export type { Bit, Char, - Interval, Numeric, TimeString, TimestampString, diff --git a/packages/3-extensions/postgres/test/contract-builder/type-helpers.test-d.ts b/packages/3-extensions/postgres/test/contract-builder/type-helpers.test-d.ts index a6a9f81cd380..a4f9e4f65a32 100644 --- a/packages/3-extensions/postgres/test/contract-builder/type-helpers.test-d.ts +++ b/packages/3-extensions/postgres/test/contract-builder/type-helpers.test-d.ts @@ -45,7 +45,7 @@ test('a field typed by temporal.CreatedAt is optional on create', () => { defineContract({}, ({ type, field }) => { const createdAt = field.column(type.temporal.CreatedAt()); const updatedAt = field.column(type.temporal.UpdatedAtString()).optional(); - const plain = field.column(type.Timestamptz()); + const plain = field.column(type.TimestamptzTemporal()); expectTypeOf>().toEqualTypeOf(); expectTypeOf>().toEqualTypeOf(); expectTypeOf>().toEqualTypeOf(); diff --git a/packages/3-extensions/postgres/test/native-type-parity.test.ts b/packages/3-extensions/postgres/test/native-type-parity.test.ts index 005827377c4f..0969fdc1084b 100644 --- a/packages/3-extensions/postgres/test/native-type-parity.test.ts +++ b/packages/3-extensions/postgres/test/native-type-parity.test.ts @@ -156,9 +156,9 @@ const parityCases: readonly ParityCase[] = [ expected: { ...numericOut, typeParams: {} }, }, { - title: 'Timestamp(3)', - bare: 'Timestamp(3)', - alias: 'Timestamp(3)', + title: 'TimestampTemporal(3)', + bare: 'TimestampTemporal(3)', + alias: 'TimestampTemporal(3)', expected: { dataType: 'pg/timestamp', codecId: 'pg/timestamp-temporal@1', @@ -166,15 +166,15 @@ const parityCases: readonly ParityCase[] = [ }, }, { - title: 'Timestamp — bare', - bare: 'Timestamp', - alias: 'Timestamp', + title: 'TimestampTemporal — bare', + bare: 'TimestampTemporal', + alias: 'TimestampTemporal', expected: { dataType: 'pg/timestamp', codecId: 'pg/timestamp-temporal@1', typeParams: {} }, }, { - title: 'Timestamptz(6)', - bare: 'Timestamptz(6)', - alias: 'Timestamptz(6)', + title: 'TimestamptzTemporal(6)', + bare: 'TimestamptzTemporal(6)', + alias: 'TimestamptzTemporal(6)', expected: { dataType: 'pg/timestamptz', codecId: 'pg/timestamptz-temporal@1', @@ -182,9 +182,9 @@ const parityCases: readonly ParityCase[] = [ }, }, { - title: 'Timestamptz — bare', - bare: 'Timestamptz', - alias: 'Timestamptz', + title: 'TimestamptzTemporal — bare', + bare: 'TimestamptzTemporal', + alias: 'TimestamptzTemporal', expected: { dataType: 'pg/timestamptz', codecId: 'pg/timestamptz-temporal@1', typeParams: {} }, }, { @@ -306,11 +306,11 @@ const bitAndIntervalCases = [ ['Bit(8)', 'pg/bit', 'pg/bit@1', { length: 8 }], ['VarBit', 'pg/varbit', 'pg/varbit@1', {}], ['VarBit(16)', 'pg/varbit', 'pg/varbit@1', { length: 16 }], - ['Interval', 'pg/interval', 'pg/interval@1', {}], - ['Interval(3)', 'pg/interval', 'pg/interval@1', { precision: 3 }], + ['IntervalParts', 'pg/interval', 'pg/interval@1', {}], + ['IntervalParts(3)', 'pg/interval', 'pg/interval@1', { precision: 3 }], ] as const; -describe('Bit, VarBit and Interval', () => { +describe('Bit, VarBit and IntervalParts', () => { it.each(bitAndIntervalCases)( '%s gives its codec and parameters as a scalar and as a list', (spelling, dataType, codecId, typeParams) => { @@ -501,7 +501,7 @@ model sample { it('rejects a non-integer precision, which pg/int4 cannot hold', () => { const result = emit(`model sample { id Int @id - at Timestamp(1.5) + at TimestampTemporal(1.5) } `); expect(result.ok).toBe(false); diff --git a/packages/3-extensions/postgres/test/no-temporal-global/temporal-field-types.test-d.ts b/packages/3-extensions/postgres/test/no-temporal-global/temporal-field-types.test-d.ts index 346f4f23da20..36b6f73c0198 100644 --- a/packages/3-extensions/postgres/test/no-temporal-global/temporal-field-types.test-d.ts +++ b/packages/3-extensions/postgres/test/no-temporal-global/temporal-field-types.test-d.ts @@ -9,10 +9,11 @@ const contract = defineContract({}, ({ type, field, model }) => ({ Event: model('Event', { fields: { id: field.id.uuidv4String(), - at: field.column(type.Timestamptz(3)), - local: field.column(type.Timestamp()), - day: field.column(type.Date()), - opens: field.column(type.Time(0)), + at: field.column(type.TimestamptzTemporal(3)), + local: field.column(type.TimestampTemporal()), + day: field.column(type.DateTemporal()), + opens: field.column(type.TimeTemporal(0)), + legacyDay: field.column(type.Date()), }, }), }, @@ -26,6 +27,7 @@ test('a Temporal field is typed precisely in a project without the global Tempor expectTypeOf().toEqualTypeOf(); expectTypeOf().toEqualTypeOf(); expectTypeOf().toEqualTypeOf(); + expectTypeOf().toEqualTypeOf(); }); test('every Temporal codec names its type through an import', () => { diff --git a/packages/3-extensions/postgres/test/scalar-type-parity.test.ts b/packages/3-extensions/postgres/test/scalar-type-parity.test.ts index 502dcabcf8cb..8a0b302b5501 100644 --- a/packages/3-extensions/postgres/test/scalar-type-parity.test.ts +++ b/packages/3-extensions/postgres/test/scalar-type-parity.test.ts @@ -75,14 +75,16 @@ describe('postgres scalar types derived from the unified namespace', () => { VarChar: { codecId: 'sql/varchar@1' }, Char: { codecId: 'sql/char@1' }, Numeric: { codecId: 'pg/numeric@1' }, - Timestamp: { codecId: 'pg/timestamp-temporal@1' }, - Timestamptz: { codecId: 'pg/timestamptz-temporal@1' }, + TimestampTemporal: { codecId: 'pg/timestamp-temporal@1' }, + TimestamptzTemporal: { codecId: 'pg/timestamptz-temporal@1' }, Time: { codecId: 'pg/time-temporal@1' }, + TimeTemporal: { codecId: 'pg/time-temporal@1' }, Timetz: { codecId: 'pg/timetz@1' }, Uuid: { codecId: 'pg/uuid@1' }, SmallInt: { codecId: 'pg/int2@1' }, Real: { codecId: 'pg/float4@1' }, Date: { codecId: 'pg/date-temporal@1' }, + DateTemporal: { codecId: 'pg/date-temporal@1' }, DateString: { codecId: 'pg/date-string@1' }, TimestampString: { codecId: 'pg/timestamp-string@1' }, TimestamptzString: { codecId: 'pg/timestamptz-string@1' }, @@ -90,7 +92,7 @@ describe('postgres scalar types derived from the unified namespace', () => { TimeString: { codecId: 'pg/time-string@1' }, Bit: { codecId: 'pg/bit@1' }, VarBit: { codecId: 'pg/varbit@1' }, - Interval: { codecId: 'pg/interval@1' }, + IntervalParts: { codecId: 'pg/interval@1' }, }); }); @@ -104,12 +106,13 @@ describe('postgres scalar types derived from the unified namespace', () => { 'Char', 'Date', 'DateString', + 'DateTemporal', 'DateTime', 'Decimal', 'Float', 'Inet', 'Int', - 'Interval', + 'IntervalParts', 'Json', 'Jsonb', 'Numeric', @@ -118,11 +121,12 @@ describe('postgres scalar types derived from the unified namespace', () => { 'String', 'Time', 'TimeString', - 'Timestamp', + 'TimeTemporal', 'TimestampString', - 'Timestamptz', + 'TimestampTemporal', 'TimestamptzJsDate', 'TimestamptzString', + 'TimestamptzTemporal', 'Timetz', 'UnboundedInt', 'Uuid', diff --git a/packages/3-extensions/sqlite/test/contract-builder/type-helpers.test-d.ts b/packages/3-extensions/sqlite/test/contract-builder/type-helpers.test-d.ts index 83e579f4cba7..68b2ee4a6711 100644 --- a/packages/3-extensions/sqlite/test/contract-builder/type-helpers.test-d.ts +++ b/packages/3-extensions/sqlite/test/contract-builder/type-helpers.test-d.ts @@ -4,7 +4,7 @@ import { defineContract } from '../../src/exports/contract-builder'; test('type.* has a helper for every SQLite PSL type constructor and no Postgres one', () => { defineContract({}, ({ type }) => { expectTypeOf(type.DateTime().codecId).toEqualTypeOf<'sqlite/datetime@1'>(); - expectTypeOf(type).not.toHaveProperty('Timestamptz'); + expectTypeOf(type).not.toHaveProperty('TimestamptzTemporal'); return { models: {} }; }); }); diff --git a/packages/3-extensions/supabase/scripts/generate-contract.ts b/packages/3-extensions/supabase/scripts/generate-contract.ts index 45283584499f..4ed6c9b01abb 100644 --- a/packages/3-extensions/supabase/scripts/generate-contract.ts +++ b/packages/3-extensions/supabase/scripts/generate-contract.ts @@ -297,7 +297,7 @@ const NAMED_TYPE_ALIASES: Readonly> = { Inet: 'IpAddress', Json: 'Payload', SmallInt: 'EmailChangeConfirmStatus', - Timestamp: 'CreatedAt', + TimestampTemporal: 'CreatedAt', Uuid: 'Id', 'VarChar(40)': 'Hash', 'VarChar(64)': 'IpAddress2', diff --git a/packages/3-extensions/supabase/src/contract/contract.prisma b/packages/3-extensions/supabase/src/contract/contract.prisma index 9935f0e5b630..3af4fa7b7341 100644 --- a/packages/3-extensions/supabase/src/contract/contract.prisma +++ b/packages/3-extensions/supabase/src/contract/contract.prisma @@ -2,7 +2,7 @@ // Contract inferred from the live database schema. Edit as needed, then run `prisma contract emit`. types { - CreatedAt = Timestamp + CreatedAt = TimestampTemporal EmailChangeConfirmStatus = SmallInt Hash = VarChar(40) Id = Uuid @@ -18,7 +18,7 @@ namespace auth { id Id @id(map: "audit_log_entries_pkey") instanceId Id? @map("instance_id") payload Payload? - createdAt Timestamptz? @map("created_at") + createdAt TimestamptzTemporal? @map("created_at") ipAddress IpAddress2 @default("") @map("ip_address") @@index([instanceId], map: "audit_logs_instance_id_idx") @@ -33,34 +33,34 @@ namespace auth { role Parent? email Parent? encryptedPassword Parent? @map("encrypted_password") - emailConfirmedAt Timestamptz? @map("email_confirmed_at") - invitedAt Timestamptz? @map("invited_at") + emailConfirmedAt TimestamptzTemporal? @map("email_confirmed_at") + invitedAt TimestamptzTemporal? @map("invited_at") confirmationToken Parent? @map("confirmation_token") - confirmationSentAt Timestamptz? @map("confirmation_sent_at") + confirmationSentAt TimestamptzTemporal? @map("confirmation_sent_at") recoveryToken Parent? @map("recovery_token") - recoverySentAt Timestamptz? @map("recovery_sent_at") + recoverySentAt TimestamptzTemporal? @map("recovery_sent_at") emailChangeTokenNew Parent? @map("email_change_token_new") emailChange Parent? @map("email_change") - emailChangeSentAt Timestamptz? @map("email_change_sent_at") - lastSignInAt Timestamptz? @map("last_sign_in_at") + emailChangeSentAt TimestamptzTemporal? @map("email_change_sent_at") + lastSignInAt TimestamptzTemporal? @map("last_sign_in_at") rawAppMetaData Jsonb? @map("raw_app_meta_data") rawUserMetaData Jsonb? @map("raw_user_meta_data") isSuperAdmin Boolean? @map("is_super_admin") - createdAt Timestamptz? @map("created_at") - updatedAt Timestamptz? @map("updated_at") + createdAt TimestamptzTemporal? @map("created_at") + updatedAt TimestamptzTemporal? @map("updated_at") phone String? @unique(map: "users_phone_key") - phoneConfirmedAt Timestamptz? @map("phone_confirmed_at") + phoneConfirmedAt TimestamptzTemporal? @map("phone_confirmed_at") phoneChange String? @default("") @map("phone_change") phoneChangeToken Parent? @default("") @map("phone_change_token") - phoneChangeSentAt Timestamptz? @map("phone_change_sent_at") - confirmedAt Timestamptz? @map("confirmed_at") + phoneChangeSentAt TimestamptzTemporal? @map("phone_change_sent_at") + confirmedAt TimestamptzTemporal? @map("confirmed_at") emailChangeTokenCurrent Parent? @default("") @map("email_change_token_current") emailChangeConfirmStatus EmailChangeConfirmStatus? @default(0) @map("email_change_confirm_status") - bannedUntil Timestamptz? @map("banned_until") + bannedUntil TimestamptzTemporal? @map("banned_until") reauthenticationToken Parent? @default("") @map("reauthentication_token") - reauthenticationSentAt Timestamptz? @map("reauthentication_sent_at") + reauthenticationSentAt TimestamptzTemporal? @map("reauthentication_sent_at") isSsoUser Boolean @default(false) @map("is_sso_user") - deletedAt Timestamptz? @map("deleted_at") + deletedAt TimestamptzTemporal? @map("deleted_at") isAnonymous Boolean @default(false) @map("is_anonymous") identities AuthIdentity[] mfaFactors MfaFactors[] @@ -91,9 +91,9 @@ namespace auth { userId Id @map("user_id") identityData Jsonb @map("identity_data") provider String - lastSignInAt Timestamptz? @map("last_sign_in_at") - createdAt Timestamptz? @map("created_at") - updatedAt Timestamptz? @map("updated_at") + lastSignInAt TimestamptzTemporal? @map("last_sign_in_at") + createdAt TimestamptzTemporal? @map("created_at") + updatedAt TimestamptzTemporal? @map("updated_at") email String? user AuthUser @relation(fields: [userId], references: [id], onDelete: Cascade, map: "identities_user_id_fkey") @@ -113,9 +113,9 @@ namespace auth { clientName String? @map("client_name") clientUri String? @map("client_uri") logoUri String? @map("logo_uri") - createdAt Timestamptz @default(now()) @map("created_at") - updatedAt Timestamptz @default(now()) @map("updated_at") - deletedAt Timestamptz? @map("deleted_at") + createdAt TimestamptzTemporal @default(now()) @map("created_at") + updatedAt TimestamptzTemporal @default(now()) @map("updated_at") + deletedAt TimestamptzTemporal? @map("deleted_at") clientType pg.enum(OauthClientType) @default("confidential") @map("client_type") tokenEndpointAuthMethod String @map("token_endpoint_auth_method") oauthAuthorizations OauthAuthorizations[] @@ -133,11 +133,11 @@ namespace auth { model AuthSession { id Id @id(map: "sessions_pkey") userId Id @map("user_id") - createdAt Timestamptz? @map("created_at") - updatedAt Timestamptz? @map("updated_at") + createdAt TimestamptzTemporal? @map("created_at") + updatedAt TimestamptzTemporal? @map("updated_at") factorId Id? @map("factor_id") aal pg.enum(AalLevel)? - notAfter Timestamptz? @map("not_after") + notAfter TimestamptzTemporal? @map("not_after") refreshedAt CreatedAt? @map("refreshed_at") userAgent String? @map("user_agent") ip IpAddress? @@ -178,13 +178,13 @@ namespace auth { discoveryUrl String? @map("discovery_url") skipNonceCheck Boolean @default(false) @map("skip_nonce_check") cachedDiscovery Jsonb? @map("cached_discovery") - discoveryCachedAt Timestamptz? @map("discovery_cached_at") + discoveryCachedAt TimestamptzTemporal? @map("discovery_cached_at") authorizationUrl String? @map("authorization_url") tokenUrl String? @map("token_url") userinfoUrl String? @map("userinfo_url") jwksUri String? @map("jwks_uri") - createdAt Timestamptz @default(now()) @map("created_at") - updatedAt Timestamptz @default(now()) @map("updated_at") + createdAt TimestamptzTemporal @default(now()) @map("created_at") + updatedAt TimestamptzTemporal @default(now()) @map("updated_at") @@index([createdAt], map: "custom_oauth_providers_created_at_idx") @@index([enabled], map: "custom_oauth_providers_enabled_idx") @@ -220,10 +220,10 @@ namespace auth { providerType String @map("provider_type") providerAccessToken String? @map("provider_access_token") providerRefreshToken String? @map("provider_refresh_token") - createdAt Timestamptz? @map("created_at") - updatedAt Timestamptz? @map("updated_at") + createdAt TimestamptzTemporal? @map("created_at") + updatedAt TimestamptzTemporal? @map("updated_at") authenticationMethod String @map("authentication_method") - authCodeIssuedAt Timestamptz? @map("auth_code_issued_at") + authCodeIssuedAt TimestamptzTemporal? @map("auth_code_issued_at") inviteToken String? @map("invite_token") referrer String? oauthClientStateId Id? @map("oauth_client_state_id") @@ -242,8 +242,8 @@ namespace auth { id Id @id(map: "instances_pkey") uuid Id? rawBaseConfig String? @map("raw_base_config") - createdAt Timestamptz? @map("created_at") - updatedAt Timestamptz? @map("updated_at") + createdAt TimestamptzTemporal? @map("created_at") + updatedAt TimestamptzTemporal? @map("updated_at") @@rls @@map("instances") @@ -252,8 +252,8 @@ namespace auth { model MfaAmrClaims { id Id @id(map: "amr_id_pk") sessionId Id @map("session_id") - createdAt Timestamptz @map("created_at") - updatedAt Timestamptz @map("updated_at") + createdAt TimestamptzTemporal @map("created_at") + updatedAt TimestamptzTemporal @map("updated_at") authenticationMethod String @map("authentication_method") session AuthSession @relation(fields: [sessionId], references: [id], onDelete: Cascade, map: "mfa_amr_claims_session_id_fkey", index: "mfa_amr_claims_session_id_authentication_method_pkey") @@ -268,11 +268,11 @@ namespace auth { friendlyName String? @map("friendly_name") factorType pg.enum(FactorType) @map("factor_type") status pg.enum(FactorStatus) - createdAt Timestamptz @map("created_at") - updatedAt Timestamptz @map("updated_at") + createdAt TimestamptzTemporal @map("created_at") + updatedAt TimestamptzTemporal @map("updated_at") secret String? phone String? - lastChallengedAt Timestamptz? @unique(map: "mfa_factors_last_challenged_at_key") @map("last_challenged_at") + lastChallengedAt TimestamptzTemporal? @unique(map: "mfa_factors_last_challenged_at_key") @map("last_challenged_at") webAuthnCredential Jsonb? @map("web_authn_credential") webAuthnAaguid Id? @map("web_authn_aaguid") lastWebauthnChallengeData Jsonb? @map("last_webauthn_challenge_data") @@ -290,8 +290,8 @@ namespace auth { model MfaChallenges { id Id @id(map: "mfa_challenges_pkey") factorId Id @map("factor_id") - createdAt Timestamptz @map("created_at") - verifiedAt Timestamptz? @map("verified_at") + createdAt TimestamptzTemporal @map("created_at") + verifiedAt TimestamptzTemporal? @map("verified_at") ipAddress IpAddress @map("ip_address") otpCode String? @map("otp_code") webAuthnSessionData Jsonb? @map("web_authn_session_data") @@ -316,9 +316,9 @@ namespace auth { responseType pg.enum(OauthResponseType) @default("code") @map("response_type") status pg.enum(OauthAuthorizationStatus) @default("pending") authorizationCode String? @unique(map: "oauth_authorizations_authorization_code_key") @map("authorization_code") - createdAt Timestamptz @default(now()) @map("created_at") - expiresAt Timestamptz @default(sql`(now() + '00:03:00'::interval)`) @map("expires_at") - approvedAt Timestamptz? @map("approved_at") + createdAt TimestamptzTemporal @default(now()) @map("created_at") + expiresAt TimestamptzTemporal @default(sql`(now() + '00:03:00'::interval)`) @map("expires_at") + approvedAt TimestamptzTemporal? @map("approved_at") nonce String? client OauthClients @relation(fields: [clientId], references: [id], onDelete: Cascade, map: "oauth_authorizations_client_id_fkey", index: false) user AuthUser? @relation(fields: [userId], references: [id], onDelete: Cascade, map: "oauth_authorizations_user_id_fkey", index: false) @@ -339,7 +339,7 @@ namespace auth { id Id @id(map: "oauth_client_states_pkey") providerType String @map("provider_type") codeVerifier String? @map("code_verifier") - createdAt Timestamptz @map("created_at") + createdAt TimestamptzTemporal @map("created_at") @@index([createdAt], map: "idx_oauth_client_states_created_at") @@map("oauth_client_states") @@ -350,8 +350,8 @@ namespace auth { userId Id @map("user_id") clientId Id @map("client_id") scopes String - grantedAt Timestamptz @default(now()) @map("granted_at") - revokedAt Timestamptz? @map("revoked_at") + grantedAt TimestamptzTemporal @default(now()) @map("granted_at") + revokedAt TimestamptzTemporal? @map("revoked_at") client OauthClients @relation(fields: [clientId], references: [id], onDelete: Cascade, map: "oauth_consents_client_id_fkey", index: false) user AuthUser @relation(fields: [userId], references: [id], onDelete: Cascade, map: "oauth_consents_user_id_fkey", index: "oauth_consents_user_client_unique") @@ -389,8 +389,8 @@ namespace auth { token Parent? @unique(map: "refresh_tokens_token_unique") userId Parent? @map("user_id") revoked Boolean? - createdAt Timestamptz? @map("created_at") - updatedAt Timestamptz? @map("updated_at") + createdAt TimestamptzTemporal? @map("created_at") + updatedAt TimestamptzTemporal? @map("updated_at") parent Parent? sessionId Id? @map("session_id") session AuthSession? @relation(fields: [sessionId], references: [id], onDelete: Cascade, map: "refresh_tokens_session_id_fkey", index: "refresh_tokens_session_id_revoked_idx") @@ -407,8 +407,8 @@ namespace auth { model SsoProviders { id Id @id(map: "sso_providers_pkey") resourceId String? @map("resource_id") - createdAt Timestamptz? @map("created_at") - updatedAt Timestamptz? @map("updated_at") + createdAt TimestamptzTemporal? @map("created_at") + updatedAt TimestamptzTemporal? @map("updated_at") disabled Boolean? samlProviders SamlProviders[] samlRelayStates SamlRelayStates[] @@ -428,8 +428,8 @@ namespace auth { metadataXml String @map("metadata_xml") metadataUrl String? @map("metadata_url") attributeMapping Jsonb? @map("attribute_mapping") - createdAt Timestamptz? @map("created_at") - updatedAt Timestamptz? @map("updated_at") + createdAt TimestamptzTemporal? @map("created_at") + updatedAt TimestamptzTemporal? @map("updated_at") nameIdFormat String? @map("name_id_format") ssoProvider SsoProviders @relation(fields: [ssoProviderId], references: [id], onDelete: Cascade, map: "saml_providers_sso_provider_id_fkey") @@ -447,8 +447,8 @@ namespace auth { requestId String @map("request_id") forEmail String? @map("for_email") redirectTo String? @map("redirect_to") - createdAt Timestamptz? @map("created_at") - updatedAt Timestamptz? @map("updated_at") + createdAt TimestamptzTemporal? @map("created_at") + updatedAt TimestamptzTemporal? @map("updated_at") flowStateId Id? @map("flow_state_id") flowState FlowState? @relation(fields: [flowStateId], references: [id], onDelete: Cascade, map: "saml_relay_states_flow_state_id_fkey", index: false) ssoProvider SsoProviders @relation(fields: [ssoProviderId], references: [id], onDelete: Cascade, map: "saml_relay_states_sso_provider_id_fkey") @@ -472,8 +472,8 @@ namespace auth { id Id @id(map: "sso_domains_pkey") ssoProviderId Id @map("sso_provider_id") domain String - createdAt Timestamptz? @map("created_at") - updatedAt Timestamptz? @map("updated_at") + createdAt TimestamptzTemporal? @map("created_at") + updatedAt TimestamptzTemporal? @map("updated_at") ssoProvider SsoProviders @relation(fields: [ssoProviderId], references: [id], onDelete: Cascade, map: "sso_domains_sso_provider_id_fkey") @@index(expression: sql`lower(domain)`, map: "sso_domains_domain_idx", unique: true) @@ -488,8 +488,8 @@ namespace auth { userId Id? @map("user_id") challengeType String @map("challenge_type") sessionData Jsonb @map("session_data") - createdAt Timestamptz @default(now()) @map("created_at") - expiresAt Timestamptz @map("expires_at") + createdAt TimestamptzTemporal @default(now()) @map("created_at") + expiresAt TimestamptzTemporal @map("expires_at") user AuthUser? @relation(fields: [userId], references: [id], onDelete: Cascade, map: "webauthn_challenges_user_id_fkey") @@index([expiresAt], map: "webauthn_challenges_expires_at_idx") @@ -510,9 +510,9 @@ namespace auth { backupEligible Boolean @default(false) @map("backup_eligible") backedUp Boolean @default(false) @map("backed_up") friendlyName String @default("") @map("friendly_name") - createdAt Timestamptz @default(now()) @map("created_at") - updatedAt Timestamptz @default(now()) @map("updated_at") - lastUsedAt Timestamptz? @map("last_used_at") + createdAt TimestamptzTemporal @default(now()) @map("created_at") + updatedAt TimestamptzTemporal @default(now()) @map("updated_at") + lastUsedAt TimestamptzTemporal? @map("last_used_at") user AuthUser @relation(fields: [userId], references: [id], onDelete: Cascade, map: "webauthn_credentials_user_id_fkey") @@index([credentialId], map: "webauthn_credentials_credential_id_key", unique: true) @@ -588,9 +588,9 @@ namespace storage { name String _type pg.enum(Buckettype) @default("ANALYTICS") @map("type") format String @default("ICEBERG") - createdAt Timestamptz @default(now()) @map("created_at") - updatedAt Timestamptz @default(now()) @map("updated_at") - deletedAt Timestamptz? @map("deleted_at") + createdAt TimestamptzTemporal @default(now()) @map("created_at") + updatedAt TimestamptzTemporal @default(now()) @map("updated_at") + deletedAt TimestamptzTemporal? @map("deleted_at") icebergNamespaces IcebergNamespaces[] icebergTables IcebergTables[] @@ -602,8 +602,8 @@ namespace storage { model BucketsVectors { id String @id(map: "buckets_vectors_pkey") _type pg.enum(Buckettype) @default("VECTOR") @map("type") - createdAt Timestamptz @default(now()) @map("created_at") - updatedAt Timestamptz @default(now()) @map("updated_at") + createdAt TimestamptzTemporal @default(now()) @map("created_at") + updatedAt TimestamptzTemporal @default(now()) @map("updated_at") vectorIndexes VectorIndexes[] @@rls @@ -614,8 +614,8 @@ namespace storage { id Id @id(map: "iceberg_namespaces_pkey") @default(sql`gen_random_uuid()`) bucketName String @map("bucket_name") name String - createdAt Timestamptz @default(now()) @map("created_at") - updatedAt Timestamptz @default(now()) @map("updated_at") + createdAt TimestamptzTemporal @default(now()) @map("created_at") + updatedAt TimestamptzTemporal @default(now()) @map("updated_at") metadata Jsonb @default(json`{}`) catalogId Id @map("catalog_id") icebergTables IcebergTables[] @@ -632,8 +632,8 @@ namespace storage { bucketName String @map("bucket_name") name String location String - createdAt Timestamptz @default(now()) @map("created_at") - updatedAt Timestamptz @default(now()) @map("updated_at") + createdAt TimestamptzTemporal @default(now()) @map("created_at") + updatedAt TimestamptzTemporal @default(now()) @map("updated_at") remoteTableId String? @map("remote_table_id") shardKey String? @map("shard_key") shardId String? @map("shard_id") @@ -661,8 +661,8 @@ namespace storage { id String @id(map: "buckets_pkey") name String owner Id? - createdAt Timestamptz? @default(now()) @map("created_at") - updatedAt Timestamptz? @default(now()) @map("updated_at") + createdAt TimestamptzTemporal? @default(now()) @map("created_at") + updatedAt TimestamptzTemporal? @default(now()) @map("updated_at") public Boolean? @default(false) avifAutodetection Boolean? @default(false) @map("avif_autodetection") fileSizeLimit BigInt? @map("file_size_limit") @@ -686,7 +686,7 @@ namespace storage { key String version String ownerId String? @map("owner_id") - createdAt Timestamptz @default(now()) @map("created_at") + createdAt TimestamptzTemporal @default(now()) @map("created_at") userMetadata Jsonb? @map("user_metadata") metadata Jsonb? s3MultipartUploadsParts S3MultipartUploadsParts[] @@ -707,7 +707,7 @@ namespace storage { etag String ownerId String? @map("owner_id") version String - createdAt Timestamptz @default(now()) @map("created_at") + createdAt TimestamptzTemporal @default(now()) @map("created_at") bucket StorageBucket @relation(fields: [bucketId], references: [id], map: "s3_multipart_uploads_parts_bucket_id_fkey", index: false) upload S3MultipartUploads @relation(fields: [uploadId], references: [id], onDelete: Cascade, map: "s3_multipart_uploads_parts_upload_id_fkey", index: false) @@ -720,9 +720,9 @@ namespace storage { bucketId String? @map("bucket_id") name String? owner Id? - createdAt Timestamptz? @default(now()) @map("created_at") - updatedAt Timestamptz? @default(now()) @map("updated_at") - lastAccessedAt Timestamptz? @default(now()) @map("last_accessed_at") + createdAt TimestamptzTemporal? @default(now()) @map("created_at") + updatedAt TimestamptzTemporal? @default(now()) @map("updated_at") + lastAccessedAt TimestamptzTemporal? @default(now()) @map("last_accessed_at") metadata Jsonb? pathTokens String?[]? @map("path_tokens") version String? @@ -746,8 +746,8 @@ namespace storage { dimension Int distanceMetric String @map("distance_metric") metadataConfiguration Jsonb? @map("metadata_configuration") - createdAt Timestamptz @default(now()) @map("created_at") - updatedAt Timestamptz @default(now()) @map("updated_at") + createdAt TimestamptzTemporal @default(now()) @map("created_at") + updatedAt TimestamptzTemporal @default(now()) @map("updated_at") bucket BucketsVectors @relation(fields: [bucketId], references: [id], map: "vector_indexes_bucket_id_fkey", index: false) @@index([name, bucketId], map: "vector_indexes_name_bucket_id_idx", unique: true) diff --git a/packages/3-extensions/supabase/src/contract/handles.ts b/packages/3-extensions/supabase/src/contract/handles.ts index 94e55f46e742..104070877346 100644 --- a/packages/3-extensions/supabase/src/contract/handles.ts +++ b/packages/3-extensions/supabase/src/contract/handles.ts @@ -15,7 +15,7 @@ import { extensionModel, field } from '@internal/sql-contract-ts/contract-builde const pgText = { codecId: 'pg/text@1' } as const; // Supabase's `auth` tables are read by application code, so these carry the Temporal // representation — the same choice a user authoring the column themselves would get from a bare -// `Timestamptz`. +// `TimestamptzTemporal`. const pgTimestamptz = { codecId: 'pg/timestamptz-temporal@1', } as const; diff --git a/packages/3-targets/3-targets/postgres/README.md b/packages/3-targets/3-targets/postgres/README.md index 78a4f914b1d3..5472815bc92c 100644 --- a/packages/3-targets/3-targets/postgres/README.md +++ b/packages/3-targets/3-targets/postgres/README.md @@ -26,7 +26,7 @@ Provides the Postgres target descriptor (`SqlControlTargetDescriptor`) for CLI c - **Generated Defaults Policy**: Treats client-generated defaults as non-DB defaults when emitting DDL - **Database Dependency Consumption**: The planner extracts database dependencies from the configured framework components (passed as `frameworkComponents`), verifies each dependency against the live schema, and only emits install operations when required. The runner reuses the same metadata for post-apply verification, so there are no hardcoded extension mappings—database dependencies stay component-owned. - **Storage Type Planning**: The planner dispatches storage type hooks for `storage.types` and emits type operations before table creation when supported by the policy -- **Contract-to-PSL Printing**: Implements the `buildPslContract` descriptor hook, which `prisma contract print` uses. It takes a Postgres contract the contract serializer accepted, from any source, and returns the Prisma 8 PSL document that reads back as the same contract. It does not check the contract's structure again; a broken structure is an internal error. It writes models, value objects, named types, domain and native enums, keys with their names, indexes, checks, relations, polymorphism, control policies, row-level security (`@@rls`, policies, and roles in `namespace unbound`) and defaults. A column is written as a type constructor the configured stack contributes that reads back with its codec, native type and type parameters: the constructor marked `inferred` for the column's data type, which is the one `contract infer` writes, when it does, or else the first constructor in the stack that does, such as `DateTime` or `Timestamptz(3)` for a `Temporal`-backed `timestamptz` column, or an extension's `pgvector.Vector(3)`. A value-object member has no column, so its type goes through the stack the same way: the stack's codec names the native type for the member's type parameters, and the member is written as the type constructor for that native type, called with those parameters (`Numeric(10, 2)`); a member typed by a domain enum is written by the enum's name. The contract does not record which named type a member used, so a member written `code Short`, with `types { Short = VarChar(10) }`, is printed as the type it stands for, `code VarChar(10)`; a model field keeps `Short`, because its column names the named type. A literal default is written by the data type of the column's codec. The family passes the stack's type constructors, codecs and data types to the hook for this. Anything it cannot write so that it reads back the same is refused by name with `CONTRACT.PRINT_UNSUPPORTED` (the full list is in `docs/reference/error-reference.md`, one function per case in `src/core/psl-print/refusals.ts`); nothing is dropped. It lives in `src/core/psl-print/`, the counterpart of the `inferPslContract` hook in `src/core/psl-infer/`, which writes PSL from a live database. Both build with the shared builders in `src/core/psl-build/`: literals, index and check attributes, and the native enum block. Both find the constructor marked `inferred` for a data type through the SQL family's `@internal/family-sql/psl-build`. Model and field names come from the contract; `@@map` and `@map` appear only where the name the PSL source would derive differs. A unique constraint prints as `@@unique` and a unique index as `@@index(..., unique: true)`; every index prints explicitly, and a relation writes `index: false` when nothing backs its foreign key, no `index` argument when a relation that says nothing would be backed by the same object (`defaultForeignKeyIndex`), and otherwise `index: ""` naming the index, or the key whose first columns are the foreign key's. Execution generators join back onto their columns, through whatever the configured stack contributes, extensions included. A column that is not a list and whose PSL type stands for its data type prints as the call of the field preset that writes its codec with its generators, built from the preset's print form, when the preset accepts that call and it reads back with the column's codec, type params and generators. A column typed by a named type, a value object or an enum, and a column whose call fails those checks, such as one without a value for a required argument, does not. A column with only a create generator then prints it as `@default((...))`, the call of the default function that declares `printArguments` for it; any other column is refused. +- **Contract-to-PSL Printing**: Implements the `buildPslContract` descriptor hook, which `prisma contract print` uses. It takes a Postgres contract the contract serializer accepted, from any source, and returns the Prisma 8 PSL document that reads back as the same contract. It does not check the contract's structure again; a broken structure is an internal error. It writes models, value objects, named types, domain and native enums, keys with their names, indexes, checks, relations, polymorphism, control policies, row-level security (`@@rls`, policies, and roles in `namespace unbound`) and defaults. A column is written as a type constructor the configured stack contributes that reads back with its codec, native type and type parameters: the constructor marked `inferred` for the column's data type, which is the one `contract infer` writes, when it does, or else the first constructor in the stack that does, such as `DateTime` or `TimestamptzTemporal(3)` for a `Temporal`-backed `timestamptz` column, or an extension's `pgvector.Vector(3)`. A value-object member has no column, so its type goes through the stack the same way: the stack's codec names the native type for the member's type parameters, and the member is written as the type constructor for that native type, called with those parameters (`Numeric(10, 2)`); a member typed by a domain enum is written by the enum's name. The contract does not record which named type a member used, so a member written `code Short`, with `types { Short = VarChar(10) }`, is printed as the type it stands for, `code VarChar(10)`; a model field keeps `Short`, because its column names the named type. A literal default is written by the data type of the column's codec. The family passes the stack's type constructors, codecs and data types to the hook for this. Anything it cannot write so that it reads back the same is refused by name with `CONTRACT.PRINT_UNSUPPORTED` (the full list is in `docs/reference/error-reference.md`, one function per case in `src/core/psl-print/refusals.ts`); nothing is dropped. It lives in `src/core/psl-print/`, the counterpart of the `inferPslContract` hook in `src/core/psl-infer/`, which writes PSL from a live database. Both build with the shared builders in `src/core/psl-build/`: literals, index and check attributes, and the native enum block. Both find the constructor marked `inferred` for a data type through the SQL family's `@internal/family-sql/psl-build`. Model and field names come from the contract; `@@map` and `@map` appear only where the name the PSL source would derive differs. A unique constraint prints as `@@unique` and a unique index as `@@index(..., unique: true)`; every index prints explicitly, and a relation writes `index: false` when nothing backs its foreign key, no `index` argument when a relation that says nothing would be backed by the same object (`defaultForeignKeyIndex`), and otherwise `index: ""` naming the index, or the key whose first columns are the foreign key's. Execution generators join back onto their columns, through whatever the configured stack contributes, extensions included. A column that is not a list and whose PSL type stands for its data type prints as the call of the field preset that writes its codec with its generators, built from the preset's print form, when the preset accepts that call and it reads back with the column's codec, type params and generators. A column typed by a named type, a value object or an enum, and a column whose call fails those checks, such as one without a value for a required argument, does not. A column with only a create generator then prints it as `@default((...))`, the call of the default function that declares `printArguments` for it; any other column is refused. - **Database-to-PSL inference**: Implements the `inferPslContract` descriptor hook, which `prisma contract infer` uses. Introspection records each column's element type as the database reports it (`format_type`, its kind, schema and name). Infer resolves that reported type through the data types of the configured stack, the target's and the extension packs', and writes the constructor marked `inferred` for the data type, with the fewest arguments that give the same parameters (`numeric(10,0)` is written `Numeric(10)`); a list column adds `[]`, and a native enum column is written `pg.enum()`. When a column's type is claimed by no data type, or by one with no marked constructor (such as `tsquery`), infer writes nothing and fails with `CONTRACT.INFER_COLUMN_TYPE_UNCLAIMED`, listing every such column as `.
.: `. No configured pack has a type for such a column. When the type comes from a database extension that has a Prisma 8 pack, such as `pgvector` or `postgis`, adding the pack to `extensions` in `prisma.config.ts` lets infer write it, for the forms of the type the pack supports. Otherwise, as for domains, `"char"`, `hstore` and `tsquery`, the column's type must change, or the column must go. The rule itself (resolving a reported type through the data types, the fewest-arguments call, the unclaimed error) lives in the SQL family's `@internal/family-sql/psl-build`; the target adds the walk over its schema tree and the native enum handling in `src/core/psl-infer/`. - **`Temporal` for the target's own code**: The `Temporal`-backed codecs (`pg/date-temporal@1`, `pg/time-temporal@1`, `pg/timestamp-temporal@1` and `pg/timestamptz-temporal@1`), when they decode, and the `instantNow`, `plainDateTimeNow`, `plainDateNow` and `plainTimeNow` generators get `Temporal` from `requireTemporal()` (`src/core/require-temporal.ts`): the runtime's global `Temporal` when it has one, else the fallback `Temporal`, else it throws `RUNTIME.TEMPORAL_UNAVAILABLE`. The text codecs (`pg/date-string@1`, `pg/time-string@1`, `pg/timestamp-string@1`, `pg/timestamptz-string@1` and `pg/timetz@1`), `pg/timestamptz-date@1`, the `timestampNow` generator and the string generators (`timestampStringNow`, `timestamptzStringNow`, `dateStringNow`, `timeStringNow` and `timetzNow`) do not use `Temporal`, so an application that uses only them reads and writes its date and time columns without one. The target's control entry (`@internal/target-postgres/control`) sets the fallback `Temporal`, from `temporal-polyfill`, when it is loaded. Nothing sets `globalThis.Temporal`. No other entry of this package imports `temporal-polyfill`. `temporal-polyfill` is a required peer dependency of this package and of the published `@prisma/orm-target-postgres` and `@prisma/orm-postgres`, so a project that already installs it for its own code has one copy. npm, pnpm and bun install it automatically; a project that uses Yarn adds `temporal-polyfill` to its own dependencies. In the published package, `@prisma/orm-target-postgres/target` is the aggregate of every entry of this package, the control entry included, so it loads the control entry too; an application imports the entry it needs, for example `@prisma/orm-target-postgres/target/runtime`. The fallback is held once per process, and every `Temporal`-backed codec in the process uses it. An application process that loads no control-plane code and has no global `Temporal` gets `RUNTIME.TEMPORAL_UNAVAILABLE` when one of those codecs decodes or one of those generators runs. An application that loads control-plane code in its own process, such as server code under `vite dev` with the Prisma Vite plugin or a script that calls the control client, decodes dates without its own `Temporal`, and then fails in production. An application that uses the Temporal codecs must load its own `Temporal`. Every signature that names a `Temporal` type takes it from `temporal-spec`, a package of type declarations with no runtime code (`import type { Temporal } from 'temporal-spec'`), never from a global declaration, so a Temporal field is typed precisely in a project that has no global `Temporal` declarations. `tsconfig.no-temporal-global.json` type-checks `src` without them. - **Runtime List Framing**: Parses inbound Postgres array text for contract-declared list columns before applying the scalar element codec. Builtin arrays and enum arrays therefore share one target-owned decode path; fixed-scale `numeric(30,10)[]` reads database-normalized text such as `"1.5000000000"`, matching scalar numeric decoding rather than the previous driver numeric-array float spelling `"1.5"`. diff --git a/packages/3-targets/3-targets/postgres/src/core/type-constructors.ts b/packages/3-targets/3-targets/postgres/src/core/type-constructors.ts index 23a6789c402b..741ccba93192 100644 --- a/packages/3-targets/3-targets/postgres/src/core/type-constructors.ts +++ b/packages/3-targets/3-targets/postgres/src/core/type-constructors.ts @@ -110,7 +110,7 @@ export const postgresNativeAuthoringTypes = { }, }, }, - Timestamp: { + TimestampTemporal: { kind: 'typeConstructor', documentation: 'A date and time without a time zone, represented as Temporal.PlainDateTime.', args: [{ kind: 'value', dataType: PG_SIZE_TYPE, name: 'precision', optional: true }], @@ -119,7 +119,7 @@ export const postgresNativeAuthoringTypes = { typeParams: { precision: { kind: 'arg', index: 0 } }, }, }, - Timestamptz: { + TimestamptzTemporal: { kind: 'typeConstructor', documentation: 'An instant represented as Temporal.Instant, with optional fractional-second precision.', @@ -138,6 +138,15 @@ export const postgresNativeAuthoringTypes = { typeParams: { precision: { kind: 'arg', index: 0 } }, }, }, + TimeTemporal: { + kind: 'typeConstructor', + documentation: 'A time of day without a time zone, represented as Temporal.PlainTime.', + args: [{ kind: 'value', dataType: PG_SIZE_TYPE, name: 'precision', optional: true }], + output: { + codecId: 'pg/time-temporal@1', + typeParams: { precision: { kind: 'arg', index: 0 } }, + }, + }, Timetz: { kind: 'typeConstructor', inferred: true, @@ -177,6 +186,11 @@ export const postgresNativeAuthoringTypes = { documentation: 'A calendar date represented as Temporal.PlainDate.', output: { codecId: 'pg/date-temporal@1' }, }, + DateTemporal: { + kind: 'typeConstructor', + documentation: 'A calendar date represented as Temporal.PlainDate.', + output: { codecId: 'pg/date-temporal@1' }, + }, // The representation-explicit spellings. Same columns, same precision, same native types — the // only difference is that a read hands back PostgreSQL's own text instead of a `Temporal.*`, so a // value Temporal cannot express still round-trips. @@ -246,11 +260,11 @@ export const postgresNativeAuthoringTypes = { typeParams: { length: { kind: 'arg', index: 0 } }, }, }, - Interval: { + IntervalParts: { kind: 'typeConstructor', inferred: true, documentation: - 'A span of time stored as PostgreSQL interval, with optional fractional-second precision.', + 'A span of time stored as PostgreSQL interval and represented as { months, days, micros }, with optional fractional-second precision.', args: [{ kind: 'value', dataType: PG_SIZE_TYPE, name: 'precision', optional: true }], output: { codecId: 'pg/interval@1', diff --git a/packages/3-targets/3-targets/postgres/src/exports/codec-types.ts b/packages/3-targets/3-targets/postgres/src/exports/codec-types.ts index 4a31b5552619..0cfa24e04597 100644 --- a/packages/3-targets/3-targets/postgres/src/exports/codec-types.ts +++ b/packages/3-targets/3-targets/postgres/src/exports/codec-types.ts @@ -53,9 +53,6 @@ export type TimestamptzString

= Brande export type TimeString

= BrandedString<{ __timeStringPrecision: P; }>; -export type Interval

= BrandedString<{ - __intervalPrecision: P; -}>; type LengthParam = TypeParams extends { readonly length: infer N extends number } ? N @@ -84,7 +81,6 @@ type OutputTypeMap = { readonly 'pg/bit@1': LengthOutput>>; readonly 'pg/varbit@1': LengthOutput>>; readonly 'pg/timetz@1': Timetz>; - readonly 'pg/interval@1': Interval>; readonly 'pg/time-string@1': TimeString>; readonly 'pg/timestamp-string@1': TimestampString>; readonly 'pg/timestamptz-string@1': TimestamptzString>; diff --git a/packages/3-targets/3-targets/postgres/test/fixtures/namespaced-contract.d.ts b/packages/3-targets/3-targets/postgres/test/fixtures/namespaced-contract.d.ts index f19300ad8467..89c785b46d36 100644 --- a/packages/3-targets/3-targets/postgres/test/fixtures/namespaced-contract.d.ts +++ b/packages/3-targets/3-targets/postgres/test/fixtures/namespaced-contract.d.ts @@ -6,7 +6,6 @@ import type { Bit, Char, CodecTypes as PgTypes, - Interval, JsonValue, Numeric, Time, diff --git a/packages/3-targets/3-targets/postgres/test/parameterized-output-types.test-d.ts b/packages/3-targets/3-targets/postgres/test/parameterized-output-types.test-d.ts index 3b6223649cbb..b55308dee872 100644 --- a/packages/3-targets/3-targets/postgres/test/parameterized-output-types.test-d.ts +++ b/packages/3-targets/3-targets/postgres/test/parameterized-output-types.test-d.ts @@ -4,9 +4,9 @@ import type { Bit, Char, CodecTypes, - Interval, Numeric, ParameterizedOutputTypesForTypeParams, + PgInterval, TimeString, TimestampString, TimestamptzString, @@ -54,9 +54,12 @@ describe('the output type of a Postgres column with type parameters', () => { >(); }); + test('keeps the interval parts an interval with a precision reads', () => { + expectTypeOf>().toEqualTypeOf(); + }); + test('carries a time precision, or none', () => { expectTypeOf>().toEqualTypeOf>(); - expectTypeOf>().toEqualTypeOf>(); expectTypeOf>().toEqualTypeOf< TimestampString<6> >(); diff --git a/packages/3-targets/3-targets/postgres/test/psl-infer/infer-column-types.test.ts b/packages/3-targets/3-targets/postgres/test/psl-infer/infer-column-types.test.ts index 891e54124c1c..b304447c2e26 100644 --- a/packages/3-targets/3-targets/postgres/test/psl-infer/infer-column-types.test.ts +++ b/packages/3-targets/3-targets/postgres/test/psl-infer/infer-column-types.test.ts @@ -42,8 +42,8 @@ const COLUMN_TYPES = [ ['bit(8)', 'Bit(8)'], ['bit varying', 'VarBit'], ['bit varying(16)', 'VarBit(16)'], - ['interval', 'Interval'], - ['interval(3)', 'Interval(3)'], + ['interval', 'IntervalParts'], + ['interval(3)', 'IntervalParts(3)'], ] as const; function tableWith(columns: Record) { diff --git a/packages/3-targets/3-targets/postgres/test/type-constructors.test.ts b/packages/3-targets/3-targets/postgres/test/type-constructors.test.ts index 1e8d65fbbb5b..8552de4f050d 100644 --- a/packages/3-targets/3-targets/postgres/test/type-constructors.test.ts +++ b/packages/3-targets/3-targets/postgres/test/type-constructors.test.ts @@ -148,7 +148,7 @@ const INFERRED = [ 'TimestamptzString', 'Bit', 'VarBit', - 'Interval', + 'IntervalParts', 'pg.enum', ]; @@ -241,15 +241,17 @@ describe('postgresNativeAuthoringTypes', () => { VarChar: { codecId: 'sql/varchar@1' }, Char: { codecId: 'sql/char@1' }, Numeric: { codecId: 'pg/numeric@1' }, - Timestamp: { codecId: 'pg/timestamp-temporal@1' }, - Timestamptz: { codecId: 'pg/timestamptz-temporal@1' }, + TimestampTemporal: { codecId: 'pg/timestamp-temporal@1' }, + TimestamptzTemporal: { codecId: 'pg/timestamptz-temporal@1' }, Time: { codecId: 'pg/time-temporal@1' }, + TimeTemporal: { codecId: 'pg/time-temporal@1' }, Timetz: { codecId: 'pg/timetz@1' }, Uuid: { codecId: 'pg/uuid@1' }, Inet: { codecId: 'pg/inet@1' }, SmallInt: { codecId: 'pg/int2@1' }, Real: { codecId: 'pg/float4@1' }, Date: { codecId: 'pg/date-temporal@1' }, + DateTemporal: { codecId: 'pg/date-temporal@1' }, DateString: { codecId: 'pg/date-string@1' }, TimestampString: { codecId: 'pg/timestamp-string@1' }, TimestamptzString: { codecId: 'pg/timestamptz-string@1' }, @@ -257,7 +259,7 @@ describe('postgresNativeAuthoringTypes', () => { TimeString: { codecId: 'pg/time-string@1' }, Bit: { codecId: 'pg/bit@1' }, VarBit: { codecId: 'pg/varbit@1' }, - Interval: { codecId: 'pg/interval@1' }, + IntervalParts: { codecId: 'pg/interval@1' }, }); }); @@ -291,8 +293,8 @@ describe('postgresNativeAuthoringTypes', () => { ['Bit', [8], { codecId: 'pg/bit@1', typeParams: { length: 8 } }], ['VarBit', [], { codecId: 'pg/varbit@1' }], ['VarBit', [16], { codecId: 'pg/varbit@1', typeParams: { length: 16 } }], - ['Interval', [], { codecId: 'pg/interval@1' }], - ['Interval', [3], { codecId: 'pg/interval@1', typeParams: { precision: 3 } }], + ['IntervalParts', [], { codecId: 'pg/interval@1' }], + ['IntervalParts', [3], { codecId: 'pg/interval@1', typeParams: { precision: 3 } }], ] as const)('maps %s(%s) onto the parameter of the same name', (name, args, expected) => { expect(instantiateAuthoringTypeConstructor(postgresNativeAuthoringTypes[name], args)).toEqual( expected, @@ -320,15 +322,15 @@ describe('postgresNativeAuthoringTypes', () => { ['Numeric', [0]], ['Numeric', [1001]], ['Numeric', [10, 1001]], - ['Timestamp', [-1]], - ['Timestamp', [7]], - ['Timestamptz', [7]], + ['TimestampTemporal', [-1]], + ['TimestampTemporal', [7]], + ['TimestamptzTemporal', [7]], ['Time', [7]], ['Timetz', [7]], ['TimestamptzJsDate', [7]], ['Bit', [0]], ['VarBit', [0]], - ['Interval', [7]], + ['IntervalParts', [7]], ] as const)('refuses %s(%s)', (name, args) => { expect(() => check(name, args)).toThrow( expect.objectContaining({ code: 'CONTRACT.ARGUMENT_INVALID' }), @@ -341,8 +343,8 @@ describe('postgresNativeAuthoringTypes', () => { ['Char', [10485760]], ['Numeric', [1000, 1000]], ['Numeric', [1, 0]], - ['Timestamp', [0]], - ['Timestamp', [6]], + ['TimestampTemporal', [0]], + ['TimestampTemporal', [6]], ['Timetz', [6]], ] as const)('accepts %s(%s)', (name, args) => { expect(() => check(name, args)).not.toThrow(); @@ -358,6 +360,22 @@ describe('postgresNativeAuthoringTypes', () => { }); }); + it('has no type named Timestamptz, Timestamp or Interval', () => { + for (const removed of ['Timestamptz', 'Timestamp', 'Interval']) { + expect(everyPostgresConstructor).not.toHaveProperty(removed); + } + }); + + it.each([ + ['DateTemporal', 'Date'], + ['TimeTemporal', 'Time'], + ] as const)('%s is the same type as %s', (name, sameAs) => { + expect(postgresNativeAuthoringTypes[name]).toEqual({ + ...postgresNativeAuthoringTypes[sameAs], + documentation: expect.stringMatching(/\S/), + }); + }); + it('offers only the precision-bearing TimestamptzJsDate for a Date-backed timestamptz', () => { expect(everyPostgresConstructor).not.toHaveProperty('DateTimeDate'); expect(everyPostgresConstructor).not.toHaveProperty('TimestamptzDate'); @@ -372,7 +390,7 @@ describe('postgresNativeAuthoringTypes', () => { }, }); expect(postgresScalarAuthoringTypes.DateTime.output.codecId).toBe('pg/timestamptz-temporal@1'); - expect(postgresNativeAuthoringTypes.Timestamptz.output.codecId).toBe( + expect(postgresNativeAuthoringTypes.TimestamptzTemporal.output.codecId).toBe( 'pg/timestamptz-temporal@1', ); }); @@ -380,9 +398,10 @@ describe('postgresNativeAuthoringTypes', () => { describe('precision is a value of pg/int4, bounded by the data type', () => { it.each([ - 'Timestamp', - 'Timestamptz', + 'TimestampTemporal', + 'TimestamptzTemporal', 'Time', + 'TimeTemporal', 'Timetz', 'TimestampString', 'TimestamptzString', diff --git a/packages/3-targets/6-adapters/postgres/README.md b/packages/3-targets/6-adapters/postgres/README.md index 24b01227e171..7fd42779c000 100644 --- a/packages/3-targets/6-adapters/postgres/README.md +++ b/packages/3-targets/6-adapters/postgres/README.md @@ -140,7 +140,7 @@ flowchart TD **Column Types Export (`column-types.ts`)** - Exports column descriptors for built-in types; enums are declared with `enumType` from the contract builder -- Parameterized helpers: ``rawColumn(sql`tsvector`)`` for a column of a type no other column type covers, its type written as a `sql` literal (ADR 273), `charColumn(length)`, `varcharColumn(length)`, `numericColumn(precision, scale?)`, `bitColumn(length)`, `varbitColumn(length)`, `timeColumn(precision?)`, `timetzColumn(precision?)`, `intervalColumn(precision?)` +- Parameterized helpers: ``rawColumn(sql`tsvector`)`` for a column of a type no other column type covers, its type written as a `sql` literal (ADR 273), `charColumn(length)`, `varcharColumn(length)`, `numericColumn(precision, scale?)`, `bitColumn(length)`, `varbitColumn(length)`, `timeColumn(precision?)`, `timetzColumn(precision?)`, `intervalPartsColumn(precision?)` - Exports raw JSON helpers: - `jsonColumn`, `jsonbColumn` — untyped raw JSON / JSONB column descriptors diff --git a/packages/3-targets/6-adapters/postgres/src/exports/column-types.ts b/packages/3-targets/6-adapters/postgres/src/exports/column-types.ts index 5bc52e8bf54e..bbdc51d78095 100644 --- a/packages/3-targets/6-adapters/postgres/src/exports/column-types.ts +++ b/packages/3-targets/6-adapters/postgres/src/exports/column-types.ts @@ -266,18 +266,18 @@ export const byteaColumn = { codecId: PG_BYTEA_CODEC_ID, } as const satisfies ColumnTypeDescriptor; -export function intervalColumn(): ColumnTypeDescriptor; -export function intervalColumn( +export function intervalPartsColumn(): ColumnTypeDescriptor; +export function intervalPartsColumn( precision: Precision, ): ColumnTypeDescriptor & { readonly typeParams: { readonly precision: Precision }; }; -export function intervalColumn(precision?: number): ColumnTypeDescriptor< +export function intervalPartsColumn(precision?: number): ColumnTypeDescriptor< typeof PG_INTERVAL_CODEC_ID > & { readonly typeParams?: { readonly precision: number }; }; -export function intervalColumn(precision?: number): ColumnTypeDescriptor< +export function intervalPartsColumn(precision?: number): ColumnTypeDescriptor< typeof PG_INTERVAL_CODEC_ID > & { readonly typeParams?: { readonly precision: number }; diff --git a/packages/3-targets/6-adapters/postgres/test/migrations/bit-interval-columns.integration.test.ts b/packages/3-targets/6-adapters/postgres/test/migrations/bit-interval-columns.integration.test.ts index f0d230203f29..b1e071c0352b 100644 --- a/packages/3-targets/6-adapters/postgres/test/migrations/bit-interval-columns.integration.test.ts +++ b/packages/3-targets/6-adapters/postgres/test/migrations/bit-interval-columns.integration.test.ts @@ -24,7 +24,7 @@ import { testTimeout, } from './fixtures/runner-fixtures'; -/** What `Bit`, `Bit(8)`, `VarBit`, `VarBit(16)`, `Interval` and `Interval(3)` emit. */ +/** What `Bit`, `Bit(8)`, `VarBit`, `VarBit(16)`, `IntervalParts` and `IntervalParts(3)` emit. */ const COLUMNS = { bit: { dataType: 'pg/bit', codecId: 'pg/bit@1' }, bit8: { dataType: 'pg/bit', codecId: 'pg/bit@1', typeParams: { length: 8 } }, @@ -75,7 +75,7 @@ function buildContract(): Contract { }; } -describe('Bit, VarBit and Interval columns', { concurrent: false }, () => { +describe('Bit, VarBit and IntervalParts columns', { concurrent: false }, () => { let database: Awaited>; let driver: PostgresControlDriver | undefined; diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 66856d54c61f..15218299e239 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -607,6 +607,9 @@ importers: temporal-polyfill: specifier: ^1.0.4 version: 1.0.4 + temporal-spec: + specifier: 1.0.1 + version: 1.0.1 tsx: specifier: 'catalog:' version: 4.23.12 @@ -5761,6 +5764,9 @@ importers: temporal-polyfill: specifier: ^1.0.4 version: 1.0.4 + temporal-spec: + specifier: 1.0.1 + version: 1.0.1 typescript: specifier: 'catalog:' version: 5.9.3 diff --git a/skills/prisma-8/references/contract.md b/skills/prisma-8/references/contract.md index 86a50b759657..c9ec12058c42 100644 --- a/skills/prisma-8/references/contract.md +++ b/skills/prisma-8/references/contract.md @@ -117,7 +117,7 @@ model Post { Then run `pnpm prisma contract emit` (or rely on the Vite plugin — see `references/build.md`). Specify cascade behaviour explicitly with `onDelete` / `onUpdate`; the default is `Restrict`. -**Temporal columns.** On PostgreSQL, `Date`, `Timestamp(p)`, `Timestamptz(p)` and `Time(p)` read and write `Temporal` values (`Temporal.PlainDate`, `PlainDateTime`, `Instant`, `PlainTime`), never JavaScript `Date`. They need a global `Temporal` at query time: Node.js 26.8.2 and later ship `globalThis.Temporal`; 26.8.1 and earlier — including every 22 and 24 release — do not, and the first read or write of such a column throws `RUNTIME.TEMPORAL_UNAVAILABLE`. On those runtimes either `import 'temporal-polyfill/full/global'` before the first query (add `temporal-polyfill` as a dependency) or author the column as `DateString` / `TimestampString(p)` / `TimestamptzString(p)` / `TimeString(p)`, which carry PostgreSQL's own text and need no `Temporal`. `prisma contract emit`, `prisma db init` and the other commands need no global `Temporal`: the Postgres target loads `temporal-polyfill` as a fallback `Temporal` for the control plane, and sets no global. `temporal-polyfill` is a required peer dependency of `@prisma/orm-postgres`. npm, pnpm and bun install it automatically. With Yarn, add `temporal-polyfill` (`^1.0.4`) to the project's dependencies, or the commands fail because Node.js cannot find the package. That fallback is held once per process. An application that loads control-plane code in its own process, such as server code under `vite dev` with the Prisma Vite plugin or a script that calls the control client, decodes dates without its own `Temporal`, and then fails in production. An application that uses the Temporal codecs must load its own `Temporal`. A TypeScript contract file that constructs a `Temporal` value must load an implementation too. +**Temporal columns.** On PostgreSQL, `DateTemporal`, `TimestampTemporal(p)`, `TimestamptzTemporal(p)` and `TimeTemporal(p)` (and `Date`, `DateTime` and `Time`, which today mean the same as `DateTemporal`, `TimestamptzTemporal` and `TimeTemporal`) read and write `Temporal` values (`Temporal.PlainDate`, `PlainDateTime`, `Instant`, `PlainTime`), never JavaScript `Date`. They need a global `Temporal` at query time: Node.js 26.8.2 and later ship `globalThis.Temporal`; 26.8.1 and earlier — including every 22 and 24 release — do not, and the first read or write of such a column throws `RUNTIME.TEMPORAL_UNAVAILABLE`. On those runtimes either `import 'temporal-polyfill/full/global'` before the first query (add `temporal-polyfill` as a dependency) or author the column as `DateString` / `TimestampString(p)` / `TimestamptzString(p)` / `TimeString(p)`, which carry PostgreSQL's own text and need no `Temporal`. `prisma contract emit`, `prisma db init` and the other commands need no global `Temporal`: the Postgres target loads `temporal-polyfill` as a fallback `Temporal` for the control plane, and sets no global. `temporal-polyfill` is a required peer dependency of `@prisma/orm-postgres`. npm, pnpm and bun install it automatically. With Yarn, add `temporal-polyfill` (`^1.0.4`) to the project's dependencies, or the commands fail because Node.js cannot find the package. That fallback is held once per process. An application that loads control-plane code in its own process, such as server code under `vite dev` with the Prisma Vite plugin or a script that calls the control client, decodes dates without its own `Temporal`, and then fails in production. An application that uses the Temporal codecs must load its own `Temporal`. A TypeScript contract file that constructs a `Temporal` value must load an implementation too. `@@index` also accepts `expression:` (instead of a fields list), `where:` (partial-index predicate), `unique:`, `type:`/`options:` (target-registered access method), and `name:` xor `map:`. Raw SQL in `expression:` and `where:` is a `sql` literal; a quoted string is refused with `PSL_VALUE_TYPE_INCOMPATIBLE`, whose message gives the rewrite: diff --git a/skills/prisma-8/references/debug.md b/skills/prisma-8/references/debug.md index fcdd0090dfe0..c5151f6407c8 100644 --- a/skills/prisma-8/references/debug.md +++ b/skills/prisma-8/references/debug.md @@ -110,7 +110,7 @@ The single source of truth: read the envelope, find the row by `code`, follow th | `BUDGET.ROWS_EXCEEDED` / `BUDGET.TIME_EXCEEDED` | Runtime, when the `budgets` middleware is active | Tune `budgets({ maxRows, maxLatencyMs, ... })` or rewrite the query. See `references/runtime.md`. | | `LINT.SELECT_STAR` / `LINT.NO_LIMIT` / `LINT.DELETE_WITHOUT_WHERE` / `LINT.UPDATE_WITHOUT_WHERE` / `LINT.READ_ONLY_MUTATION` | Runtime, when the `lints` middleware is active | Fix the query (add a `WHERE` / `LIMIT` / explicit columns), or relax the lint config. See `references/runtime.md`. | | `PLAN.HASH_MISMATCH` | Runtime, executing a precompiled plan | The contract the plan was built against does not match the runtime contract. Re-emit, rebuild, redeploy. | -| `RUNTIME.TEMPORAL_UNAVAILABLE` | Runtime, first read or write touching a Temporal-backed column (`Date`, `Timestamp(p)`, `Timestamptz(p)`, `Time(p)`) or a `temporal.updatedAt()` clock | No global `Temporal`. Node.js 26.8.2 and later ship `globalThis.Temporal`; 26.8.1 and earlier — including every 22 and 24 — do not. Either `import 'temporal-polyfill/full/global'` before the first query, or author the column as `DateString` / `TimestampString(p)` / `TimestamptzString(p)` / `TimeString(p)` to read PostgreSQL's own text. | +| `RUNTIME.TEMPORAL_UNAVAILABLE` | Runtime, first read or write touching a Temporal-backed column (`DateTemporal`, `TimestampTemporal(p)`, `TimestamptzTemporal(p)`, `TimeTemporal(p)`, and `Date`, `DateTime` and `Time`) or a `temporal.updatedAt()` clock | No global `Temporal`. Node.js 26.8.2 and later ship `globalThis.Temporal`; 26.8.1 and earlier — including every 22 and 24 — do not. Either `import 'temporal-polyfill/full/global'` before the first query, or author the column as `DateString` / `TimestampString(p)` / `TimestamptzString(p)` / `TimeString(p)` to read PostgreSQL's own text. | | `ORM.RELATION_MUTATION_UNSUPPORTED` | ORM nested `create` / `connect` on an N:M relation whose junction has required payload columns | Write the junction table directly or use the SQL builder (`meta.junction`). | | `RUNTIME.ABORTED` (`details.phase` = `encode\|decode\|stream\|beforeEncode\|onRow`) | Runtime, when an `AbortSignal` fires mid-execute | Cancellation, not a bug; surface to the caller. | | `SqlQueryError` (no dotted code) | Raw-SQL paths surfacing a driver error | Inspect `sqlState` + `constraint` + `table` + `column`. Postgres `23505` = unique violation, `23503` = foreign-key violation, etc. Fix the data or the schema. | diff --git a/test/e2e/framework/test/fixtures/contract.ts b/test/e2e/framework/test/fixtures/contract.ts index 9d5a52a3b260..9e39e96f901c 100644 --- a/test/e2e/framework/test/fixtures/contract.ts +++ b/test/e2e/framework/test/fixtures/contract.ts @@ -9,7 +9,7 @@ import { float8Column, int4Column, int8Column, - intervalColumn, + intervalPartsColumn, jsonbColumn, jsonColumn, numericColumn, @@ -106,7 +106,7 @@ export const contract = defineContract( .column('created_at'), startsAt: field.column(timeTemporalColumn(2)).optional().column('starts_at'), startsAtTz: field.column(timetzColumn(2)).optional().column('starts_at_tz'), - duration: field.column(intervalColumn(6)).optional().column('duration'), + duration: field.column(intervalPartsColumn(6)).optional().column('duration'), }, }).sql({ table: 'param_types' }), diff --git a/test/integration/package.json b/test/integration/package.json index 02b57a212c54..80f73cbe0c03 100644 --- a/test/integration/package.json +++ b/test/integration/package.json @@ -95,6 +95,7 @@ "pg": "catalog:", "strip-ansi": "^7.2.0", "temporal-polyfill": "^1.0.4", + "temporal-spec": "1.0.1", "typescript": "catalog:", "vite": "catalog:", "vite7": "npm:vite@7.3.1", diff --git a/test/integration/test/authoring/defaults-on-existing-tables.integration.test.ts b/test/integration/test/authoring/defaults-on-existing-tables.integration.test.ts index 37e86ec3b8e0..18500a960d2a 100644 --- a/test/integration/test/authoring/defaults-on-existing-tables.integration.test.ts +++ b/test/integration/test/authoring/defaults-on-existing-tables.integration.test.ts @@ -180,7 +180,7 @@ model Box { token Uuid @default("A0EEBC99-9C0B-4EF8-BB6D-6BB9BD380A11") addedTags String[] @default(["a", "b"]) addedEmpty String[] @default([]) - addedAt Timestamp @default(timestamp\`0044-03-15 12:00:00 BC\`) + addedAt TimestampTemporal @default(timestamp\`0044-03-15 12:00:00 BC\`) addedNumber Numeric(5, -2) @default(-9999900) addedCode Char(5) @default("ab") addedToken Uuid @default("{a0eebc99-9c0b-4ef8-bb6d-6bb9bd380a11}") diff --git a/test/integration/test/authoring/parity/default-date-time-types/schema.prisma b/test/integration/test/authoring/parity/default-date-time-types/schema.prisma index 40f28804bff3..e5cad60f1dcc 100644 --- a/test/integration/test/authoring/parity/default-date-time-types/schema.prisma +++ b/test/integration/test/authoring/parity/default-date-time-types/schema.prisma @@ -3,7 +3,7 @@ model T { id Int @id instant DateTime @default(timestamptz`2024-01-01T01:00:00.500+01:00`) - local Timestamp @default(timestamp`2024-01-01 12:34:56.500`) + local TimestampTemporal @default(timestamp`2024-01-01 12:34:56.500`) day Date @default(date`0044-03-15 BC`) clock Time @default(time`12:34`) zoned Timetz @default(timetz`12:34:56+02`) diff --git a/test/integration/test/cli-journeys/control-plane-commands-without-temporal.e2e.test.ts b/test/integration/test/cli-journeys/control-plane-commands-without-temporal.e2e.test.ts index dfd9d4a76184..492da66eb7d3 100644 --- a/test/integration/test/cli-journeys/control-plane-commands-without-temporal.e2e.test.ts +++ b/test/integration/test/cli-journeys/control-plane-commands-without-temporal.e2e.test.ts @@ -61,9 +61,9 @@ const DEFAULTS_SCHEMA = `// use prisma-8 model Event { id Int @id - localAt Timestamp(3) @default(timestamp\`2024-01-01 00:00:00\`) - instantAt Timestamptz @default(timestamptz\`2024-01-01T00:00:00Z\`) - history Timestamp(3)[] @default([timestamp\`2024-01-01 00:00:00\`, timestamp\`2024-06-30 12:34:56.789\`]) + localAt TimestampTemporal(3) @default(timestamp\`2024-01-01 00:00:00\`) + instantAt TimestamptzTemporal @default(timestamptz\`2024-01-01T00:00:00Z\`) + history TimestampTemporal(3)[] @default([timestamp\`2024-01-01 00:00:00\`, timestamp\`2024-06-30 12:34:56.789\`]) @@map("event") } diff --git a/test/integration/test/cli-journeys/date-time-default-canonical-form.e2e.test.ts b/test/integration/test/cli-journeys/date-time-default-canonical-form.e2e.test.ts index fa9772de7936..1b602e624422 100644 --- a/test/integration/test/cli-journeys/date-time-default-canonical-form.e2e.test.ts +++ b/test/integration/test/cli-journeys/date-time-default-canonical-form.e2e.test.ts @@ -180,7 +180,7 @@ model Created { id Int @id instant DateTime @default(timestamptz\`0044-03-15 00:00:00+00 BC\`) day Date @default(date\`0044-03-15 BC\`) - local Timestamp @default(timestamp\`-000043-03-15T12:00:00\`) + local TimestampTemporal @default(timestamp\`-000043-03-15T12:00:00\`) @@map("created") } @@ -189,7 +189,7 @@ model Created { id Int @id instant DateTime ${defaults ? '@default(timestamptz`-000043-03-15T00:00:00Z`)' : ''} day Date ${defaults ? '@default(date`-000043-03-15`)' : ''} - local Timestamp ${defaults ? '@default(timestamp`0044-03-15 12:00:00 BC`)' : ''} + local TimestampTemporal ${defaults ? '@default(timestamp`0044-03-15 12:00:00 BC`)' : ''} @@map("altered") } diff --git a/test/integration/test/cli-journeys/defaults-read-by-codec.e2e.test.ts b/test/integration/test/cli-journeys/defaults-read-by-codec.e2e.test.ts index 6befbf8810a6..2d7bfb98127e 100644 --- a/test/integration/test/cli-journeys/defaults-read-by-codec.e2e.test.ts +++ b/test/integration/test/cli-journeys/defaults-read-by-codec.e2e.test.ts @@ -44,8 +44,8 @@ model Sample { varcode VarChar(10) @default("vc") code sql.String(10) @default("vc") initials Char(3) @default("ab") - local Timestamp(3) @default(${tagged('timestamp', '2024-01-01T12:34:56.5')}) - precise Timestamptz(6) @default(${tagged('timestamptz', '2024-01-01T00:00:00.123456Z')}) + local TimestampTemporal(3) @default(${tagged('timestamp', '2024-01-01T12:34:56.5')}) + precise TimestamptzTemporal(6) @default(${tagged('timestamptz', '2024-01-01T00:00:00.123456Z')}) clock Time @default(${tagged('time', '12:34:56')}) zoned Timetz @default(${tagged('timetz', '12:34:56+02:00')}) token Uuid @default("a0eebc99-9c0b-4ef8-bb6d-6bb9bd380a11") @@ -60,7 +60,7 @@ model Sample { clockText TimeString @default(${tagged('time', '12:00:00')}) bits Bit(3) @default("101") varbits VarBit(5) @default("1") - span Interval @default(${tagged('interval', 'P1DT2H')}) + span IntervalParts @default(${tagged('interval', 'P1DT2H')}) tags String[] @default(["a", "b"]) counts Int[] @default([1, 2]) plan pg.enum(Plan) @default("pro") @@ -74,21 +74,21 @@ model Edge { id Int @id dayBc Date @default(${tagged('date', '0044-03-15 BC')}) dayFar Date @default(${tagged('date', '+012026-01-02')}) - localBc Timestamp(3) @default(${tagged('timestamp', '0044-03-15 12:00:00 BC')}) - instantFar Timestamptz(6) @default(${tagged('timestamptz', '+012026-01-02T03:04:05Z')}) + localBc TimestampTemporal(3) @default(${tagged('timestamp', '0044-03-15 12:00:00 BC')}) + instantFar TimestamptzTemporal(6) @default(${tagged('timestamptz', '+012026-01-02T03:04:05Z')}) forever TimestamptzString @default(${tagged('timestamptz', 'infinity')}) dawn DateString @default(${tagged('date', '-infinity')}) localForever TimestampString @default(${tagged('timestamp', 'infinity')}) - mixed Interval @default(${tagged('interval', 'P1Y-2M3DT-4H5M-6.5S')}) - backwards Interval @default(${tagged('interval', 'P-1DT-4H-5M')}) + mixed IntervalParts @default(${tagged('interval', 'P1Y-2M3DT-4H5M-6.5S')}) + backwards IntervalParts @default(${tagged('interval', 'P-1DT-4H-5M')}) quoteBytes Bytes @default(${tagged('bytea', '\\x27225c00ff')}) quoteText String @default("it's a \\\\ backslash") notANumber Float @default(NaN) above Float @default(Infinity) below Real @default(-Infinity) numericNaN Decimal @default(NaN) - stamps Timestamp(3)[] @default([${tagged('timestamp', '0044-03-15 12:00:00 BC')}, ${tagged('timestamp', '2024-01-01T00:00:00')}]) - spans Interval[] @default([${tagged('interval', 'P1Y-2M')}, ${tagged('interval', 'PT0S')}]) + stamps TimestampTemporal(3)[] @default([${tagged('timestamp', '0044-03-15 12:00:00 BC')}, ${tagged('timestamp', '2024-01-01T00:00:00')}]) + spans IntervalParts[] @default([${tagged('interval', 'P1Y-2M')}, ${tagged('interval', 'PT0S')}]) blobs Bytes[] @default([${tagged('bytea', '\\x27')}, ${tagged('bytea', '\\x5c')}]) texts String[] @default(["a'b", "c\\\\d", ""]) floats Float[] @default([NaN, Infinity, -Infinity, 1.5]) diff --git a/test/integration/test/contract-builder.types.test-d.ts b/test/integration/test/contract-builder.types.test-d.ts index 6cda22001483..ef9109f931d4 100644 --- a/test/integration/test/contract-builder.types.test-d.ts +++ b/test/integration/test/contract-builder.types.test-d.ts @@ -27,6 +27,7 @@ import type { JsonValue } from '@internal/target-postgres/codec-types'; import postgresPack from '@internal/target-postgres/pack'; import { postgresCreateNamespace } from '@internal/target-postgres/types'; import { type as arktype } from 'arktype'; +import type { Temporal as TemporalTypes } from 'temporal-spec'; import { expectTypeOf, test } from 'vitest'; import { TestSqlContractSerializer as SqlContractSerializer } from '../../../packages/2-sql/9-family/test/test-sql-contract-serializer'; import type { Contract } from './fixtures/contract.d'; @@ -598,7 +599,7 @@ test('read: non-enum fields keep their codec output, unchanged from main', () => type Row = ResultType; expectTypeOf().toEqualTypeOf(); expectTypeOf().toEqualTypeOf(); - expectTypeOf().toEqualTypeOf(); + expectTypeOf().toEqualTypeOf(); }); test('write: enum insert accepts the value union and rejects out-of-union literals', () => { diff --git a/test/integration/test/contract-parity/rendered-output-types.test.ts b/test/integration/test/contract-parity/rendered-output-types.test.ts index 19f1a5872c1f..429aa25f5f7a 100644 --- a/test/integration/test/contract-parity/rendered-output-types.test.ts +++ b/test/integration/test/contract-parity/rendered-output-types.test.ts @@ -32,7 +32,6 @@ const declaredByTypeFunction = [ 'pg/bit@1', 'pg/varbit@1', 'pg/timetz@1', - 'pg/interval@1', 'pg/time-string@1', 'pg/timestamp-string@1', 'pg/timestamptz-string@1', @@ -57,6 +56,8 @@ const stacks: Readonly is the same type for every SRID.', + 'pg/interval@1': + 'An interval reads as its months, days and microseconds whatever its precision.', 'pg/time-temporal@1': temporalPrecision, 'pg/timestamp-temporal@1': temporalPrecision, 'pg/timestamptz-temporal@1': temporalPrecision, diff --git a/test/integration/test/date-time-defaults/_fixture-before-canonical-form/contract-with-tags.prisma b/test/integration/test/date-time-defaults/_fixture-before-canonical-form/contract-with-tags.prisma index eff1589a5025..a188c9678979 100644 --- a/test/integration/test/date-time-defaults/_fixture-before-canonical-form/contract-with-tags.prisma +++ b/test/integration/test/date-time-defaults/_fixture-before-canonical-form/contract-with-tags.prisma @@ -5,7 +5,7 @@ model Event { a DateTime @default(timestamptz`2024-01-01T00:00:00Z`) b DateTime @default(timestamptz`2024-01-01T00:00:00.000Z`) c DateTime @default(timestamptz`2024-01-01T01:00:00+01:00`) - localAt Timestamp(3) @default(timestamp`2024-01-01 12:34:56.500`) + localAt TimestampTemporal(3) @default(timestamp`2024-01-01 12:34:56.500`) day Date @default(date`2024-01-01`) clock Time @default(time`12:34`) zoned Timetz @default(timetz`12:34:56+02`) diff --git a/test/integration/test/date-time-defaults/_fixture-before-canonical-form/contract.prisma b/test/integration/test/date-time-defaults/_fixture-before-canonical-form/contract.prisma index 071d505ec5b8..400a07fa0e3e 100644 --- a/test/integration/test/date-time-defaults/_fixture-before-canonical-form/contract.prisma +++ b/test/integration/test/date-time-defaults/_fixture-before-canonical-form/contract.prisma @@ -5,7 +5,7 @@ model Event { a DateTime @default("2024-01-01T00:00:00Z") b DateTime @default("2024-01-01T00:00:00.000Z") c DateTime @default("2024-01-01T01:00:00+01:00") - localAt Timestamp(3) @default("2024-01-01 12:34:56.500") + localAt TimestampTemporal(3) @default("2024-01-01 12:34:56.500") day Date @default("2024-01-01") clock Time @default("12:34") zoned Timetz @default("12:34:56+02") diff --git a/test/integration/test/date-time-defaults/psl-date-time-defaults.integration.test.ts b/test/integration/test/date-time-defaults/psl-date-time-defaults.integration.test.ts index c2674e511a6a..e3647df39d7d 100644 --- a/test/integration/test/date-time-defaults/psl-date-time-defaults.integration.test.ts +++ b/test/integration/test/date-time-defaults/psl-date-time-defaults.integration.test.ts @@ -19,7 +19,7 @@ const literal = (value: unknown) => ({ kind: 'literal', value }); /** The tag that writes a value of each Postgres column type these tests use. */ const TAG_OF: Readonly> = { DateTime: 'timestamptz', - Timestamp: 'timestamp', + TimestampTemporal: 'timestamp', Date: 'date', Time: 'time', Timetz: 'timetz', @@ -49,15 +49,15 @@ describe('a PSL date or time default is stored in its data type canonical form', await postgresDefaults( `model Event { id Int @id - instant Timestamptz(3) @default(timestamptz\`2024-01-01 01:00:00+01\`) + instant TimestamptzTemporal(3) @default(timestamptz\`2024-01-01 01:00:00+01\`) jsDate TimestamptzJsDate @default(timestamptz\`2024-01-01 00:00:00.500+00\`) - local Timestamp @default(timestamp\`2024-01-01 12:34:56.500\`) + local TimestampTemporal @default(timestamp\`2024-01-01 12:34:56.500\`) localText TimestampString @default(timestamp\`2024-01-01 12:34:56\`) day Date @default(date\`0044-03-15 BC\`) dayText DateString @default(date\`12026-01-02\`) clock Time @default(time\`12:34\`) zoned Timetz @default(timetz\`12:34:56+02\`) - history Timestamp(3)[] @default([timestamp\`2024-01-01 00:00:00\`, timestamp\`2024-06-30 12:34:56.789000\`]) + history TimestampTemporal(3)[] @default([timestamp\`2024-01-01 00:00:00\`, timestamp\`2024-06-30 12:34:56.789000\`]) }`, ['instant', 'jsDate', 'local', 'localText', 'day', 'dayText', 'clock', 'zoned', 'history'], ), @@ -87,7 +87,7 @@ describe('a PSL date or time default is stored in its data type canonical form', it.each([ [ - 'Timestamp', + 'TimestampTemporal', '2024-01-01T00:00:00Z', 'pg/timestamp holds no UTC offset, but "2024-01-01T00:00:00Z" has one. Leave it out, as in "2024-01-01T12:34:56".', ], @@ -158,11 +158,11 @@ describe('a PSL date or time default is stored in its data type canonical form', describe('a quoted string on a Postgres date, time, interval or bytes column', () => { it.each([ ['DateTime', '2024-01-01T00:00:00Z', 'timestamptz'], - ['Timestamp', '2024-01-01 12:34:56', 'timestamp'], + ['TimestampTemporal', '2024-01-01 12:34:56', 'timestamp'], ['Date', '2024-01-01', 'date'], ['Time', '12:34:56', 'time'], ['Timetz', '12:34:56+02:00', 'timetz'], - ['Interval', 'P1D', 'interval'], + ['IntervalParts', 'P1D', 'interval'], ['Bytes', '\\x68656c6c6f', 'bytea'], ])('on a %s column is refused, showing the tag to write', async (type, written, tag) => { const result = await authorSqlContractFromPsl( @@ -181,7 +181,7 @@ describe('a quoted string on a Postgres date, time, interval or bytes column', ( await postgresDefaults( `model Event { id Int @id - span Interval @default(interval\`1 day 02:00:00\`) + span IntervalParts @default(interval\`1 day 02:00:00\`) bytes Bytes @default(bytea\`\\x68656c6c6f\`) }`, ['span', 'bytes'], diff --git a/test/integration/test/enum-members/enum-members.postgres.test.ts b/test/integration/test/enum-members/enum-members.postgres.test.ts index c0c9d4cd7497..b8a7420f70a2 100644 --- a/test/integration/test/enum-members/enum-members.postgres.test.ts +++ b/test/integration/test/enum-members/enum-members.postgres.test.ts @@ -1,3 +1,4 @@ +import type { Temporal as TemporalTypes } from 'temporal-spec'; import { describe, expect, expectTypeOf, it } from 'vitest'; import { type PortContext, timeouts, withPostgresPort } from '../_harness/postgres'; import type { Contract } from './_fixture-postgres/generated/contract'; @@ -226,8 +227,12 @@ describe('db.enums on a contract emitted from PSL, against Postgres', () => { expectTypeOf().toEqualTypeOf<1>(); expectTypeOf().toEqualTypeOf<1n>(); expectTypeOf().toEqualTypeOf(); - expectTypeOf().toEqualTypeOf(); - expectTypeOf().toEqualTypeOf(); + expectTypeOf< + Levels['StampLevel']['members']['Launch'] + >().toEqualTypeOf(); + expectTypeOf< + Levels['DayLevel']['members']['Launch'] + >().toEqualTypeOf(); expectTypeOf().toEqualTypeOf<1.5>(); expectTypeOf().toEqualTypeOf(); expectTypeOf().toEqualTypeOf(); diff --git a/test/integration/test/fixtures/cli/cli-e2e-test-app/fixtures/cli-journeys/contract-date-time-defaults.ts b/test/integration/test/fixtures/cli/cli-e2e-test-app/fixtures/cli-journeys/contract-date-time-defaults.ts index 63a46fb72590..ce27258a6b8c 100644 --- a/test/integration/test/fixtures/cli/cli-e2e-test-app/fixtures/cli-journeys/contract-date-time-defaults.ts +++ b/test/integration/test/fixtures/cli/cli-e2e-test-app/fixtures/cli-journeys/contract-date-time-defaults.ts @@ -1,7 +1,7 @@ import { dateStringColumn, int4Column, - intervalColumn, + intervalPartsColumn, timeStringColumn, timestampStringColumn, timestamptzJsDateColumn, @@ -21,7 +21,9 @@ export const contract = defineContract({ dayText: field.column(dateStringColumn).default('0044-03-15 BC'), clockText: field.column(timeStringColumn()).default('12:34:56.500'), zoned: field.column(timetzColumn()).default('12:34:56+02'), - span: field.column(intervalColumn()).default({ months: 14, days: 3, micros: 14706500000n }), + span: field + .column(intervalPartsColumn()) + .default({ months: 14, days: 3, micros: 14706500000n }), }, }).sql({ table: 'moment' }), }, diff --git a/test/integration/test/planner-golden/fixtures/postgres/contract.ts b/test/integration/test/planner-golden/fixtures/postgres/contract.ts index 2d8c6147d8ed..43e3d08e5db6 100644 --- a/test/integration/test/planner-golden/fixtures/postgres/contract.ts +++ b/test/integration/test/planner-golden/fixtures/postgres/contract.ts @@ -16,7 +16,7 @@ import { int2Column, int4Column, int8Column, - intervalColumn, + intervalPartsColumn, jsonbColumn, jsonColumn, numericColumn, @@ -125,8 +125,8 @@ export const contract = defineContract({ timestampPrecision: field.column(timestamp3), timestamptz: field.column(timestamptzTemporalColumn), timestamptzPrecision: field.column(timestamptz3), - interval: field.column(intervalColumn()), - intervalPrecision: field.column(intervalColumn(3)), + interval: field.column(intervalPartsColumn()), + intervalPrecision: field.column(intervalPartsColumn(3)), mood: field.column(pg.enum(Mood)), vector: field.column(vector(3)), geometry: field.column(geometryColumn), @@ -171,7 +171,7 @@ export const contract = defineContract({ timePrecision: field.column(timeTemporalColumn(3)).many(), timestamptz: field.column(timestamptzTemporalColumn).many(), timestamptzPrecision: field.column(timestamptz3).many(), - interval: field.column(intervalColumn()).many(), + interval: field.column(intervalPartsColumn()).many(), jsonb: field.column(jsonbColumn).many(), uuid: field.column(uuid).many(), mood: field.column(pg.enum(Mood)).many(), diff --git a/test/integration/test/ports/prisma/functional/issues-28192-pg-historical-dates/_fixture/contract.prisma b/test/integration/test/ports/prisma/functional/issues-28192-pg-historical-dates/_fixture/contract.prisma index bda3634ae82c..05c765d1e5d8 100644 --- a/test/integration/test/ports/prisma/functional/issues-28192-pg-historical-dates/_fixture/contract.prisma +++ b/test/integration/test/ports/prisma/functional/issues-28192-pg-historical-dates/_fixture/contract.prisma @@ -19,7 +19,7 @@ model TestData { id String @id @onCreate(cuid(2)) date Date - timestamp Timestamp - timestamptz Timestamptz + timestamp TimestampTemporal + timestamptz TimestamptzTemporal @@map("testData") } diff --git a/test/integration/test/psl-infer/marked-types-round-trip.integration.test.ts b/test/integration/test/psl-infer/marked-types-round-trip.integration.test.ts index 727f9c5be36f..1df6f154073a 100644 --- a/test/integration/test/psl-infer/marked-types-round-trip.integration.test.ts +++ b/test/integration/test/psl-infer/marked-types-round-trip.integration.test.ts @@ -80,8 +80,8 @@ const AUTHORED_TYPES = [ 'Bit(8)', 'VarBit', 'VarBit(16)', - 'Interval', - 'Interval(3)', + 'IntervalParts', + 'IntervalParts(3)', ] as const; const AUTHORED_SCHEMA = `model Sample { diff --git a/test/integration/test/psl-print/date-time-types-print.integration.test.ts b/test/integration/test/psl-print/date-time-types-print.integration.test.ts index 7450d0c91422..fc892de17b6b 100644 --- a/test/integration/test/psl-print/date-time-types-print.integration.test.ts +++ b/test/integration/test/psl-print/date-time-types-print.integration.test.ts @@ -6,10 +6,10 @@ const SCHEMA = `// use prisma-8 model Event { id Int @id instant DateTime - zoned Timestamptz - zoned3 Timestamptz(3) - local Timestamp - local3 Timestamp(3) + zoned TimestamptzTemporal + zoned3 TimestamptzTemporal(3) + local TimestampTemporal + local3 TimestampTemporal(3) day Date clock Time clock3 Time(3) @@ -40,9 +40,9 @@ describe('contract print spells each date and time column', () => { id: 'Int', instant: 'DateTime', zoned: 'DateTime', - zoned3: 'Timestamptz(3)', - local: 'Timestamp', - local3: 'Timestamp(3)', + zoned3: 'TimestamptzTemporal(3)', + local: 'TimestampTemporal', + local3: 'TimestampTemporal(3)', day: 'Date', clock: 'Time', clock3: 'Time(3)', diff --git a/test/integration/test/sql-orm-client/include-codec-canonical-json.test.ts b/test/integration/test/sql-orm-client/include-codec-canonical-json.test.ts index 457e405300ca..d1a77550a4b7 100644 --- a/test/integration/test/sql-orm-client/include-codec-canonical-json.test.ts +++ b/test/integration/test/sql-orm-client/include-codec-canonical-json.test.ts @@ -2,7 +2,7 @@ import { byteaColumn, int4Column, int8Column, - intervalColumn, + intervalPartsColumn, numericColumn, } from '@internal/adapter-postgres/column-types'; import postgresAdapter from '@internal/adapter-postgres/runtime'; @@ -43,7 +43,7 @@ const Reading = model('Reading', { padded: field.column(numericColumn(10, 2)).optional(), counter: field.column(int8Column).optional(), payload: field.column(byteaColumn).optional(), - elapsed: field.column(intervalColumn()).optional(), + elapsed: field.column(intervalPartsColumn()).optional(), embedding: field.column(vector(3)).optional(), }, }).sql({ table: 'canonical_readings' }); diff --git a/test/integration/test/temporal-defaults/_fixture-attributes/contract.prisma b/test/integration/test/temporal-defaults/_fixture-attributes/contract.prisma index 0905389c7ff3..7ee8ec4e8baa 100644 --- a/test/integration/test/temporal-defaults/_fixture-attributes/contract.prisma +++ b/test/integration/test/temporal-defaults/_fixture-attributes/contract.prisma @@ -14,15 +14,15 @@ model Clock { timetz0 Timetz(0) @onCreate(now()) @onUpdate(now()) timetz3 Timetz(3) @onCreate(now()) @onUpdate(now()) timetz6 Timetz(6) @onCreate(now()) @onUpdate(now()) - timestamp0 Timestamp(0) @onCreate(now()) @onUpdate(now()) - timestamp3 Timestamp(3) @onCreate(now()) @onUpdate(now()) - timestamp6 Timestamp(6) @onCreate(now()) @onUpdate(now()) + timestamp0 TimestampTemporal(0) @onCreate(now()) @onUpdate(now()) + timestamp3 TimestampTemporal(3) @onCreate(now()) @onUpdate(now()) + timestamp6 TimestampTemporal(6) @onCreate(now()) @onUpdate(now()) timestampString0 TimestampString(0) @onCreate(now()) @onUpdate(now()) timestampString3 TimestampString(3) @onCreate(now()) @onUpdate(now()) timestampString6 TimestampString(6) @onCreate(now()) @onUpdate(now()) - timestamptz0 Timestamptz(0) @onCreate(now()) @onUpdate(now()) - timestamptz3 Timestamptz(3) @onCreate(now()) @onUpdate(now()) - timestamptz6 Timestamptz(6) @onCreate(now()) @onUpdate(now()) + timestamptz0 TimestamptzTemporal(0) @onCreate(now()) @onUpdate(now()) + timestamptz3 TimestamptzTemporal(3) @onCreate(now()) @onUpdate(now()) + timestamptz6 TimestamptzTemporal(6) @onCreate(now()) @onUpdate(now()) timestamptzString0 TimestamptzString(0) @onCreate(now()) @onUpdate(now()) timestamptzString3 TimestamptzString(3) @onCreate(now()) @onUpdate(now()) timestamptzString6 TimestamptzString(6) @onCreate(now()) @onUpdate(now()) diff --git a/test/integration/test/value-objects/fixtures/generated/sql-contract.d.ts b/test/integration/test/value-objects/fixtures/generated/sql-contract.d.ts index b7875c82f73d..ab85344fc941 100644 --- a/test/integration/test/value-objects/fixtures/generated/sql-contract.d.ts +++ b/test/integration/test/value-objects/fixtures/generated/sql-contract.d.ts @@ -6,7 +6,6 @@ import type { Bit, Char, CodecTypes as PgTypes, - Interval, JsonValue, Numeric, Time, From ae4345a869b4dc472eb4929453bfd5272f336943 Mon Sep 17 00:00:00 2001 From: willbot Date: Thu, 22 Oct 2026 18:04:08 +0200 Subject: [PATCH 012/118] Add the timestamp Date, timestamp Instant and interval string codecs Three PostgreSQL codecs complete the explicit date and time types: - pg/timestamp-date@1 (TimestampJsDate): a timestamp column read and written as a JavaScript Date, treating the stored date and time as UTC. - pg/timestamp-instant@1 (TimestampInstant): the same column read and written as a Temporal.Instant in UTC. - pg/interval-string@1 (IntervalString): an interval read and written as the ISO 8601 duration pg/interval stores, whatever IntervalStyle the server prints in. Both timestamp codecs refuse infinity and a value past the last instant a Date or Temporal.Instant holds, naming TimestampString, and use now() through timestampNow and instantNow. The interval codec validates the duration on write and keeps the form a value was written in. Each codec has its aggregate rows, conformance cases against PostgreSQL, and a test that reads values the runtime driver returns under another session time zone. Co-Authored-By: Claude Opus 5.5 Signed-off-by: willbot Signed-off-by: Will Madden --- .../3-targets/postgres/src/core/aggregates.ts | 16 ++ .../3-targets/postgres/src/core/codec-ids.ts | 3 + .../postgres/src/core/codec-type-map.ts | 7 +- .../3-targets/postgres/src/core/codecs.ts | 7 +- .../postgres/src/core/date-codecs.ts | 136 ++++++++++++---- .../src/core/interval-string-codec.ts | 92 +++++++++++ .../postgres/src/core/now-generators.ts | 4 + .../src/core/temporal-codec-helpers.ts | 47 +++++- .../postgres/src/core/temporal-codecs.ts | 59 +++++++ .../postgres/src/core/type-constructors.ts | 37 ++++- .../3-targets/postgres/src/exports/codecs.ts | 10 ++ .../test/codec-render-value-literal.test.ts | 3 + .../postgres/test/data-type-inventory.test.ts | 3 + .../postgres/test/data-types.test.ts | 5 +- .../test/explicit-type-codec-wiring.test.ts | 80 ++++++++++ .../postgres/test/generator-functions.test.ts | 2 + .../test/interval-string-codec.test.ts | 72 +++++++++ .../postgres/test/now-generators.test.ts | 11 +- .../temporal-representation-taxonomy.test.ts | 23 ++- .../test/timestamp-utc-codecs.test.ts | 149 ++++++++++++++++++ .../postgres/test/type-constructors.test.ts | 13 ++ .../aggregate-defaults.integration.test.ts | 4 + .../test/aggregate-matrix.ts | 11 ++ .../test/codec-conformance/cases.ts | 70 ++++++++ ...stamp-utc-runtime-read.integration.test.ts | 78 +++++++++ 25 files changed, 899 insertions(+), 43 deletions(-) create mode 100644 packages/3-targets/3-targets/postgres/src/core/interval-string-codec.ts create mode 100644 packages/3-targets/3-targets/postgres/test/explicit-type-codec-wiring.test.ts create mode 100644 packages/3-targets/3-targets/postgres/test/interval-string-codec.test.ts create mode 100644 packages/3-targets/3-targets/postgres/test/timestamp-utc-codecs.test.ts create mode 100644 packages/3-targets/6-adapters/postgres-codec-testkit/test/timestamp-utc-runtime-read.integration.test.ts diff --git a/packages/3-targets/3-targets/postgres/src/core/aggregates.ts b/packages/3-targets/3-targets/postgres/src/core/aggregates.ts index 1f9d4ccf4c8e..3529b3494cba 100644 --- a/packages/3-targets/3-targets/postgres/src/core/aggregates.ts +++ b/packages/3-targets/3-targets/postgres/src/core/aggregates.ts @@ -28,11 +28,14 @@ import { PG_INT8_CODEC_ID, PG_INT8_NUMBER_CODEC_ID, PG_INTERVAL_CODEC_ID, + PG_INTERVAL_STRING_CODEC_ID, PG_NUMERIC_CODEC_ID, PG_TEXT_ARRAY_CODEC_ID, PG_TEXT_CODEC_ID, PG_TIME_STRING_CODEC_ID, PG_TIME_TEMPORAL_CODEC_ID, + PG_TIMESTAMP_DATE_CODEC_ID, + PG_TIMESTAMP_INSTANT_CODEC_ID, PG_TIMESTAMP_STRING_CODEC_ID, PG_TIMESTAMP_TEMPORAL_CODEC_ID, PG_TIMESTAMPTZ_DATE_CODEC_ID, @@ -137,12 +140,15 @@ const MIN_MAX_PRESERVING_CODECS = [ PG_ENUM_CODEC_ID, PG_TIMETZ_CODEC_ID, PG_INTERVAL_CODEC_ID, + PG_INTERVAL_STRING_CODEC_ID, PG_INET_CODEC_ID, PG_TEXT_ARRAY_CODEC_ID, PG_DATE_STRING_CODEC_ID, PG_TIMESTAMP_STRING_CODEC_ID, PG_TIMESTAMPTZ_STRING_CODEC_ID, PG_TIMESTAMPTZ_DATE_CODEC_ID, + PG_TIMESTAMP_DATE_CODEC_ID, + PG_TIMESTAMP_INSTANT_CODEC_ID, PG_TIME_STRING_CODEC_ID, PG_DATE_TEMPORAL_CODEC_ID, PG_TIMESTAMP_TEMPORAL_CODEC_ID, @@ -219,6 +225,11 @@ export const postgresAggregateDescriptors = [ // `sum` over the unbounded integer keeps its codec: the expression's SQL type is `numeric`, and a sum of integral values is integral, so the codec's integrality-checked `bigint` decode is the right reader for the total. produces('sum', { kind: 'codec', codecId: PG_UNBOUNDED_INT_CODEC_ID }, PG_UNBOUNDED_INT_CODEC_ID), produces('sum', { kind: 'codec', codecId: PG_INTERVAL_CODEC_ID }, PG_INTERVAL_CODEC_ID), + produces( + 'sum', + { kind: 'codec', codecId: PG_INTERVAL_STRING_CODEC_ID }, + PG_INTERVAL_STRING_CODEC_ID, + ), produces('sum', { kind: 'codec', codecId: PG_TIME_STRING_CODEC_ID }, PG_INTERVAL_CODEC_ID), produces('sum', { kind: 'codec', codecId: PG_TIME_TEMPORAL_CODEC_ID }, PG_INTERVAL_CODEC_ID), @@ -234,6 +245,11 @@ export const postgresAggregateDescriptors = [ ), produces('avg', { kind: 'codec', codecId: PG_NUMERIC_CODEC_ID }, PG_NUMERIC_CODEC_ID), produces('avg', { kind: 'codec', codecId: PG_INTERVAL_CODEC_ID }, PG_INTERVAL_CODEC_ID), + produces( + 'avg', + { kind: 'codec', codecId: PG_INTERVAL_STRING_CODEC_ID }, + PG_INTERVAL_STRING_CODEC_ID, + ), produces('avg', { kind: 'codec', codecId: PG_TIME_STRING_CODEC_ID }, PG_INTERVAL_CODEC_ID), produces('avg', { kind: 'codec', codecId: PG_TIME_TEMPORAL_CODEC_ID }, PG_INTERVAL_CODEC_ID), diff --git a/packages/3-targets/3-targets/postgres/src/core/codec-ids.ts b/packages/3-targets/3-targets/postgres/src/core/codec-ids.ts index a2a83be332a2..bf17f286ce8d 100644 --- a/packages/3-targets/3-targets/postgres/src/core/codec-ids.ts +++ b/packages/3-targets/3-targets/postgres/src/core/codec-ids.ts @@ -32,9 +32,12 @@ export const PG_DATE_STRING_CODEC_ID = 'pg/date-string@1' as const; export const PG_TIMESTAMP_STRING_CODEC_ID = 'pg/timestamp-string@1' as const; export const PG_TIMESTAMPTZ_STRING_CODEC_ID = 'pg/timestamptz-string@1' as const; export const PG_TIMESTAMPTZ_DATE_CODEC_ID = 'pg/timestamptz-date@1' as const; +export const PG_TIMESTAMP_DATE_CODEC_ID = 'pg/timestamp-date@1' as const; +export const PG_TIMESTAMP_INSTANT_CODEC_ID = 'pg/timestamp-instant@1' as const; export const PG_TIME_STRING_CODEC_ID = 'pg/time-string@1' as const; export const PG_TIMETZ_CODEC_ID = 'pg/timetz@1' as const; export const PG_INTERVAL_CODEC_ID = 'pg/interval@1' as const; +export const PG_INTERVAL_STRING_CODEC_ID = 'pg/interval-string@1' as const; export const PG_JSON_CODEC_ID = 'pg/json@1' as const; export const PG_JSONB_CODEC_ID = 'pg/jsonb@1' as const; export const PG_BYTEA_CODEC_ID = 'pg/bytea@1' as const; diff --git a/packages/3-targets/3-targets/postgres/src/core/codec-type-map.ts b/packages/3-targets/3-targets/postgres/src/core/codec-type-map.ts index 10b47e32eabe..692d512fd453 100644 --- a/packages/3-targets/3-targets/postgres/src/core/codec-type-map.ts +++ b/packages/3-targets/3-targets/postgres/src/core/codec-type-map.ts @@ -41,9 +41,11 @@ import { postgresSqlTextDescriptor, postgresSqlVarcharDescriptor, } from './codecs'; -import { pgTimestamptzDateDescriptor } from './date-codecs'; +import { pgTimestampDateDescriptor, pgTimestamptzDateDescriptor } from './date-codecs'; +import { pgIntervalStringDescriptor } from './interval-string-codec'; import { pgDateTemporalDescriptor, + pgTimestampInstantDescriptor, pgTimestampTemporalDescriptor, pgTimestamptzTemporalDescriptor, pgTimeTemporalDescriptor, @@ -83,6 +85,8 @@ export const codecDescriptorMap = { 'timestamp-string': pgTimestampStringDescriptor, 'timestamptz-string': pgTimestamptzStringDescriptor, 'timestamptz-date': pgTimestamptzDateDescriptor, + 'timestamp-date': pgTimestampDateDescriptor, + 'timestamp-instant': pgTimestampInstantDescriptor, 'time-string': pgTimeStringDescriptor, timetz: pgTimetzDescriptor, bool: pgBoolDescriptor, @@ -92,6 +96,7 @@ export const codecDescriptorMap = { uuid: pgUuidDescriptor, inet: pgInetDescriptor, interval: pgIntervalDescriptor, + 'interval-string': pgIntervalStringDescriptor, json: pgJsonDescriptor, jsonb: pgJsonbDescriptor, tsquery: pgTsqueryDescriptor, diff --git a/packages/3-targets/3-targets/postgres/src/core/codecs.ts b/packages/3-targets/3-targets/postgres/src/core/codecs.ts index c485cd12fecd..1917cb84bfaa 100644 --- a/packages/3-targets/3-targets/postgres/src/core/codecs.ts +++ b/packages/3-targets/3-targets/postgres/src/core/codecs.ts @@ -135,13 +135,15 @@ import { pgVarbit, pgVarchar, } from './data-types'; -import { pgTimestamptzDateDescriptor } from './date-codecs'; +import { pgTimestampDateDescriptor, pgTimestamptzDateDescriptor } from './date-codecs'; import { postgresError } from './errors'; +import { pgIntervalStringDescriptor } from './interval-string-codec'; import { parsePostgresListText } from './list-decoder'; import { DEFAULT_NAMESPACE_ID } from './namespace-ids'; import { PostgresNativeEnum } from './postgres-native-enum'; import { pgDateTemporalDescriptor, + pgTimestampInstantDescriptor, pgTimestampTemporalDescriptor, pgTimestamptzTemporalDescriptor, pgTimeTemporalDescriptor, @@ -1539,6 +1541,8 @@ export const codecDescriptors = definePostgresCodecs([ pgTimestampStringDescriptor, pgTimestamptzStringDescriptor, pgTimestamptzDateDescriptor, + pgTimestampDateDescriptor, + pgTimestampInstantDescriptor, pgTimeStringDescriptor, pgTimetzDescriptor, pgBoolDescriptor, @@ -1548,6 +1552,7 @@ export const codecDescriptors = definePostgresCodecs([ pgUuidDescriptor, pgInetDescriptor, pgIntervalDescriptor, + pgIntervalStringDescriptor, pgJsonDescriptor, pgJsonbDescriptor, pgTextArrayDescriptor, diff --git a/packages/3-targets/3-targets/postgres/src/core/date-codecs.ts b/packages/3-targets/3-targets/postgres/src/core/date-codecs.ts index b69c91e27205..913dc2dc390c 100644 --- a/packages/3-targets/3-targets/postgres/src/core/date-codecs.ts +++ b/packages/3-targets/3-targets/postgres/src/core/date-codecs.ts @@ -13,16 +13,54 @@ import { isStructuredError } from '@internal/utils/structured-error'; import type { StandardSchemaV1 } from '@standard-schema/spec'; import { PostgresCodecDescriptor } from './codec-descriptor'; import type { PrecisionParams } from './codec-helpers'; -import { PG_TIMESTAMPTZ_DATE_CODEC_ID, PG_TIMESTAMPTZ_STRING_CODEC_ID } from './codec-ids'; -import { pgPrecisionParams, pgTimestamptz, pgTimestamptzStoredText } from './data-types'; +import { + PG_TIMESTAMP_DATE_CODEC_ID, + PG_TIMESTAMP_STRING_CODEC_ID, + PG_TIMESTAMPTZ_DATE_CODEC_ID, + PG_TIMESTAMPTZ_STRING_CODEC_ID, +} from './codec-ids'; +import { + pgPrecisionParams, + pgTimestamp, + pgTimestampStoredText, + pgTimestamptz, + pgTimestamptzStoredText, +} from './data-types'; import { EARLIEST_POSTGRES_TIMESTAMP_MILLISECONDS, + utcTimestampText, utcTimestamptzText, } from './temporal-codec-helpers'; -function invalidDate(): RangeError { +/** What a `Date` codec reads, and the text type that holds what a `Date` cannot. */ +interface DateCodecIdentity { + readonly codecId: string; + readonly typeDescription: string; + readonly stringType: string; + readonly textCodecId: string; + /** The instant, in the stored spelling of `pg/timestamptz`, that text of the codec's data type names. */ + readonly instantText: (text: string) => string; +} + +const TIMESTAMPTZ_DATE: DateCodecIdentity = { + codecId: PG_TIMESTAMPTZ_DATE_CODEC_ID, + typeDescription: 'timestamp with time zone', + stringType: 'TimestamptzString', + textCodecId: PG_TIMESTAMPTZ_STRING_CODEC_ID, + instantText: pgTimestamptzStoredText, +}; + +const TIMESTAMP_DATE: DateCodecIdentity = { + codecId: PG_TIMESTAMP_DATE_CODEC_ID, + typeDescription: 'timestamp, read as UTC', + stringType: 'TimestampString', + textCodecId: PG_TIMESTAMP_STRING_CODEC_ID, + instantText: (text) => pgTimestamptzStoredText(`${pgTimestampStoredText(text)}Z`), +}; + +function invalidDate(identity: DateCodecIdentity): RangeError { return new RangeError( - `${PG_TIMESTAMPTZ_DATE_CODEC_ID} requires a valid Date or a representable ISO PostgreSQL timestamp with time zone; use TimestamptzString for unsupported values`, + `${identity.codecId} requires a valid Date or a representable ISO PostgreSQL ${identity.typeDescription}; use ${identity.stringType} for unsupported values`, ); } @@ -34,47 +72,43 @@ function isRepresentable(value: Date): boolean { ); } -function validateDate(value: Date): Date { - if (!isRepresentable(value)) throw invalidDate(); +function validateDate(identity: DateCodecIdentity, value: Date): Date { + if (!isRepresentable(value)) throw invalidDate(identity); return value; } -function decodeDate(wire: unknown): Date { - const instant = typeof wire === 'string' ? storedInstant(wire) : undefined; - const date = instant === undefined ? undefined : new Date(instant); - if (date === undefined || !isRepresentable(date)) throw invalidDate(); - return date; -} - -/** The instant, in its stored spelling, a stored timestamp with time zone names, or `undefined` for `infinity` and an instant outside the range a `Date` holds. */ -function storedInstant(text: string): string | undefined { +/** The instant a stored or printed value names, or `undefined` for `infinity` and a value outside the range a `Date` holds. */ +function storedInstant(identity: DateCodecIdentity, text: string): string | undefined { if (text === 'infinity' || text === '-infinity') return undefined; try { - return pgTimestamptzStoredText(text); + return identity.instantText(text); } catch (error) { if (isStructuredError(error) && error.code === 'CONTRACT.CAST_REFUSED') return undefined; throw error; } } -/** The `Date` a stored timestamp with time zone names. A `Date` holds neither `infinity` nor an instant before 4714-11-24 BC or after +275760-09-13, so those are refused. */ -function dateOfStoredText(codecId: string, text: string): Date { - const instant = storedInstant(text); +function decodeDate(identity: DateCodecIdentity, wire: unknown): Date { + const instant = typeof wire === 'string' ? storedInstant(identity, wire) : undefined; + const date = instant === undefined ? undefined : new Date(instant); + if (date === undefined || !isRepresentable(date)) throw invalidDate(identity); + return date; +} + +/** The `Date` a stored value names. A `Date` holds neither `infinity` nor an instant before 4714-11-24 BC or after +275760-09-13, so those are refused. */ +function dateOfStoredText(identity: DateCodecIdentity, text: string): Date { + const instant = storedInstant(identity, text); const date = instant === undefined ? undefined : new Date(instant); if (date === undefined || !isRepresentable(date)) { return refuseJsonValue( - codecId, - `an instant a Date holds, from 4714-11-24 BC to +275760-09-13 and not infinity or -infinity, which ${PG_TIMESTAMPTZ_STRING_CODEC_ID} holds`, + identity.codecId, + `an instant a Date holds, from 4714-11-24 BC to +275760-09-13 and not infinity or -infinity, which ${identity.textCodecId} holds`, text, ); } return date; } -function encodeDate(value: Date): string { - return utcTimestamptzText(validateDate(value), PG_TIMESTAMPTZ_DATE_CODEC_ID); -} - export class PgTimestamptzDateCodec extends CodecImpl< typeof PG_TIMESTAMPTZ_DATE_CODEC_ID, readonly ['equality', 'order'], @@ -82,16 +116,16 @@ export class PgTimestamptzDateCodec extends CodecImpl< Date > { async toWire(value: Date, _ctx: CodecCallContext): Promise { - return encodeDate(value); + return utcTimestamptzText(validateDate(TIMESTAMPTZ_DATE, value), this.id); } async fromWire(wire: string, _ctx: CodecCallContext): Promise { - return decodeDate(wire); + return decodeDate(TIMESTAMPTZ_DATE, wire); } fromDataTypeValue(value: DataTypeValue): Date { - return dateOfStoredText(this.id, value.value); + return dateOfStoredText(TIMESTAMPTZ_DATE, value.value); } toDataTypeValue(input: Date): DataTypeValue { - return this.dataTypeValueOf(validateDate(input).toISOString()); + return this.dataTypeValueOf(validateDate(TIMESTAMPTZ_DATE, input).toISOString()); } } @@ -115,3 +149,47 @@ export const pgTimestamptzDateColumn = (params: PrecisionParams = {}) => pgTimestamptzDateColumn satisfies ColumnHelperFor; pgTimestamptzDateColumn satisfies ColumnHelperForStrict; + +/** + * A `timestamp` column read and written as a JavaScript `Date`, treating the stored date and time as UTC. A read appends `Z` to the stored value; a write sends the `Date`'s UTC date and time with no offset, so neither depends on the server's or the process's time zone. + */ +export class PgTimestampDateCodec extends CodecImpl< + typeof PG_TIMESTAMP_DATE_CODEC_ID, + readonly ['equality', 'order'], + string, + Date +> { + async toWire(value: Date, _ctx: CodecCallContext): Promise { + return utcTimestampText(validateDate(TIMESTAMP_DATE, value), this.id); + } + async fromWire(wire: string, _ctx: CodecCallContext): Promise { + return decodeDate(TIMESTAMP_DATE, wire); + } + fromDataTypeValue(value: DataTypeValue): Date { + return dateOfStoredText(TIMESTAMP_DATE, value.value); + } + toDataTypeValue(input: Date): DataTypeValue { + return this.dataTypeValueOf(utcTimestampText(validateDate(TIMESTAMP_DATE, input), this.id)); + } +} + +export class PgTimestampDateDescriptor extends PostgresCodecDescriptor { + protected override jsonProjection(expression: ProjectionExpr): ProjectionExpr { + return CastExpr.as(expression, 'text'); + } + override readonly dataType = pgTimestamp.id; + override readonly codecId = PG_TIMESTAMP_DATE_CODEC_ID; + override readonly traits = ['equality', 'order'] as const; + override readonly paramsSchema = pgPrecisionParams satisfies StandardSchemaV1; + override factory(params: PrecisionParams): (ctx: CodecInstanceContext) => PgTimestampDateCodec { + return () => new PgTimestampDateCodec(this, pgTimestamp, params ?? {}); + } +} + +export const pgTimestampDateDescriptor = new PgTimestampDateDescriptor(); + +export const pgTimestampDateColumn = (params: PrecisionParams = {}) => + column(pgTimestampDateDescriptor.factory(params), pgTimestampDateDescriptor.codecId, params); + +pgTimestampDateColumn satisfies ColumnHelperFor; +pgTimestampDateColumn satisfies ColumnHelperForStrict; diff --git a/packages/3-targets/3-targets/postgres/src/core/interval-string-codec.ts b/packages/3-targets/3-targets/postgres/src/core/interval-string-codec.ts new file mode 100644 index 000000000000..56bd9c26df92 --- /dev/null +++ b/packages/3-targets/3-targets/postgres/src/core/interval-string-codec.ts @@ -0,0 +1,92 @@ +import { + type CodecCallContext, + CodecImpl, + type CodecInstanceContext, + type ColumnHelperFor, + type ColumnHelperForStrict, + column, + type DataTypeValue, +} from '@internal/framework-components/codec'; +import { CastExpr, type ProjectionExpr } from '@internal/sql-relational-core/ast'; +import type { StandardSchemaV1 } from '@standard-schema/spec'; +import { PostgresCodecDescriptor } from './codec-descriptor'; +import { type PrecisionParams, pgIntervalDecode } from './codec-helpers'; +import { PG_INTERVAL_STRING_CODEC_ID } from './codec-ids'; +import { formatIsoDuration, ISO_DURATION, pgIntervalStoredText } from './data-type-values'; +import { pgInterval, pgPrecisionParams } from './data-types'; +import { postgresError } from './errors'; + +const MAX_FRACTION_DIGITS = 6; + +function describeReceived(value: unknown): string { + if (typeof value === 'string') return JSON.stringify(value); + return value === null ? 'null' : `a ${typeof value}`; +} + +function refusedDuration(value: unknown, reason: string): never { + throw postgresError( + 'RUNTIME.ENCODE_FAILED', + `${PG_INTERVAL_STRING_CODEC_ID} writes ${reason}, got ${describeReceived(value)}`, + { meta: { codecId: PG_INTERVAL_STRING_CODEC_ID, received: typeof value } }, + ); +} + +/** The duration `pg/interval` stores for an ISO 8601 duration the application wrote. */ +function storedDuration(value: unknown): string { + if (typeof value !== 'string' || !ISO_DURATION.test(value)) { + return refusedDuration(value, 'an ISO 8601 duration, as in "P1Y2M3DT4H5M6.5S"'); + } + const fractionDigits = /\.(\d+)S$/.exec(value)?.[1]?.length ?? 0; + if (fractionDigits > MAX_FRACTION_DIGITS) { + return refusedDuration( + value, + `a duration with at most ${MAX_FRACTION_DIGITS} digits after the decimal point, because pg/interval holds microseconds`, + ); + } + return pgIntervalStoredText(value); +} + +/** + * An interval read and written as the ISO 8601 duration `pg/interval` stores, such as `P1Y2M3DT4H5M6.5S`. A read gives that form whatever `IntervalStyle` the server prints in. Equal intervals can have different forms, `P1D` and `PT24H`, so the codec keeps the form a value was written in. + */ +export class PgIntervalStringCodec extends CodecImpl< + typeof PG_INTERVAL_STRING_CODEC_ID, + readonly ['equality', 'order'], + string | Record, + string +> { + async toWire(value: string, _ctx: CodecCallContext): Promise { + return storedDuration(value); + } + async fromWire(wire: string | Record, _ctx: CodecCallContext): Promise { + return formatIsoDuration(pgIntervalDecode(wire)); + } + fromDataTypeValue(value: DataTypeValue): string { + return value.value; + } + toDataTypeValue(input: string): DataTypeValue { + return this.dataTypeValueOf(storedDuration(input)); + } +} + +export class PgIntervalStringDescriptor extends PostgresCodecDescriptor { + protected override jsonProjection(expression: ProjectionExpr): ProjectionExpr { + return CastExpr.as(expression, 'text'); + } + override readonly dataType = pgInterval.id; + override readonly codecId = PG_INTERVAL_STRING_CODEC_ID; + override readonly traits = ['equality', 'order'] as const; + override readonly keepsWrittenForm = true as const; + override readonly paramsSchema = pgPrecisionParams satisfies StandardSchemaV1; + override factory(params: PrecisionParams): (ctx: CodecInstanceContext) => PgIntervalStringCodec { + return () => new PgIntervalStringCodec(this, pgInterval, params ?? {}); + } +} + +export const pgIntervalStringDescriptor = new PgIntervalStringDescriptor(); + +export const pgIntervalStringColumn = (params: PrecisionParams = {}) => + column(pgIntervalStringDescriptor.factory(params), pgIntervalStringDescriptor.codecId, params); + +pgIntervalStringColumn satisfies ColumnHelperFor; +pgIntervalStringColumn satisfies ColumnHelperForStrict; diff --git a/packages/3-targets/3-targets/postgres/src/core/now-generators.ts b/packages/3-targets/3-targets/postgres/src/core/now-generators.ts index 5b31a37b0bd4..964215258c15 100644 --- a/packages/3-targets/3-targets/postgres/src/core/now-generators.ts +++ b/packages/3-targets/3-targets/postgres/src/core/now-generators.ts @@ -6,6 +6,8 @@ import { PG_DATE_TEMPORAL_CODEC_ID, PG_TIME_STRING_CODEC_ID, PG_TIME_TEMPORAL_CODEC_ID, + PG_TIMESTAMP_DATE_CODEC_ID, + PG_TIMESTAMP_INSTANT_CODEC_ID, PG_TIMESTAMP_STRING_CODEC_ID, PG_TIMESTAMP_TEMPORAL_CODEC_ID, PG_TIMESTAMPTZ_DATE_CODEC_ID, @@ -37,6 +39,8 @@ export const postgresNowGeneratorIds = { [PG_TIMESTAMPTZ_TEMPORAL_CODEC_ID]: INSTANT_NOW_GENERATOR_ID, [PG_TIMESTAMPTZ_STRING_CODEC_ID]: TIMESTAMPTZ_STRING_NOW_GENERATOR_ID, [PG_TIMESTAMPTZ_DATE_CODEC_ID]: TIMESTAMP_NOW_GENERATOR_ID, + [PG_TIMESTAMP_DATE_CODEC_ID]: TIMESTAMP_NOW_GENERATOR_ID, + [PG_TIMESTAMP_INSTANT_CODEC_ID]: INSTANT_NOW_GENERATOR_ID, } as const; const nowGeneratorIdByCodecId: ReadonlyMap = new Map( diff --git a/packages/3-targets/3-targets/postgres/src/core/temporal-codec-helpers.ts b/packages/3-targets/3-targets/postgres/src/core/temporal-codec-helpers.ts index 23fc4e3b7cc7..cc935e399a6e 100644 --- a/packages/3-targets/3-targets/postgres/src/core/temporal-codec-helpers.ts +++ b/packages/3-targets/3-targets/postgres/src/core/temporal-codec-helpers.ts @@ -3,9 +3,11 @@ import type { Temporal } from 'temporal-spec'; import { PG_DATE_TEMPORAL_CODEC_ID, PG_TIME_TEMPORAL_CODEC_ID, + PG_TIMESTAMP_INSTANT_CODEC_ID, PG_TIMESTAMP_TEMPORAL_CODEC_ID, PG_TIMESTAMPTZ_TEMPORAL_CODEC_ID, } from './codec-ids'; +import { pgTimestampStoredText } from './data-types'; import { errorTemporalNonIsoCalendar, errorTemporalUnrepresentable, @@ -49,9 +51,15 @@ function decodeTemporalText( } } +interface TemporalValue { + readonly calendarId?: string; + toString: () => string; +} + function encodeTemporalValue( identity: TemporalCodecIdentity, - value: { readonly calendarId?: string; toString: () => string }, + value: TemporalValue, + isoText: (value: TemporalValue) => string = (temporalValue) => temporalValue.toString(), ): string { const tag: unknown = typeof value === 'object' && value !== null @@ -68,7 +76,7 @@ function encodeTemporalValue( if (value.calendarId !== undefined && value.calendarId !== 'iso8601') { throw errorTemporalNonIsoCalendar(identity.codecId, value.calendarId); } - return postgresYearText(value.toString()); + return postgresYearText(isoText(value)); } function describeEncodeInput(value: unknown, tag: unknown): string { @@ -110,6 +118,11 @@ const TIMESTAMPTZ_TEMPORAL: TemporalCodecIdentity = { stringType: 'TimestamptzString(p)', temporalTag: 'Temporal.Instant', }; +const TIMESTAMP_INSTANT: TemporalCodecIdentity = { + codecId: PG_TIMESTAMP_INSTANT_CODEC_ID, + stringType: 'TimestampString(p)', + temporalTag: 'Temporal.Instant', +}; const TIME_TEMPORAL: TemporalCodecIdentity = { codecId: PG_TIME_TEMPORAL_CODEC_ID, stringType: 'TimeString(p)', @@ -136,6 +149,18 @@ export const pgTimestamptzTemporalDecode = (wire: string): Temporal.Instant => export const pgTimestamptzTemporalEncode = (value: Temporal.Instant): string => encodeTemporalValue(TIMESTAMPTZ_TEMPORAL, value); +/** Reads a timestamp without a time zone as the instant it names in UTC. */ +export const pgTimestampInstantDecode = (wire: string): Temporal.Instant => + decodeTemporalText(TIMESTAMP_INSTANT, wire, (temporal, text) => + temporal.Instant.from(`${pgTimestampStoredText(text)}Z`), + ); + +/** Writes an instant as its UTC date and time, with no offset, as a timestamp without a time zone holds it. */ +export const pgTimestampInstantEncode = (value: Temporal.Instant): string => + encodeTemporalValue(TIMESTAMP_INSTANT, value, (instant) => + withoutUtcDesignator(instant.toString()), + ); + export const pgTimeTemporalDecode = (wire: string): Temporal.PlainTime => decodeTemporalText(TIME_TEMPORAL, wire, (temporal, text) => temporal.PlainTime.from(text)); @@ -146,12 +171,26 @@ export const EARLIEST_POSTGRES_TIMESTAMP_MILLISECONDS = new Date( '-004713-11-24T00:00:00Z', ).getTime(); -export function utcTimestamptzText(value: Date, codecId: string): string { +function utcIsoText(value: Date, codecId: string): string { const milliseconds = value.getTime(); if (!Number.isFinite(milliseconds) || milliseconds < EARLIEST_POSTGRES_TIMESTAMP_MILLISECONDS) { throw new RangeError( `${codecId} writes a Date as UTC text and needs a valid Date on or after 4714-11-24 BC, the earliest timestamp PostgreSQL holds; got ${String(value)}`, ); } - return postgresYearText(value.toISOString()); + return value.toISOString(); +} + +export function utcTimestamptzText(value: Date, codecId: string): string { + return postgresYearText(utcIsoText(value, codecId)); +} + +/** The UTC wall-clock time of `value` with no offset, as PostgreSQL reads a timestamp without a time zone. */ +export function utcTimestampText(value: Date, codecId: string): string { + return postgresYearText(withoutUtcDesignator(utcIsoText(value, codecId))); +} + +/** ISO text in UTC without its trailing `Z`. */ +export function withoutUtcDesignator(isoText: string): string { + return isoText.endsWith('Z') ? isoText.slice(0, -1) : isoText; } diff --git a/packages/3-targets/3-targets/postgres/src/core/temporal-codecs.ts b/packages/3-targets/3-targets/postgres/src/core/temporal-codecs.ts index 51a0a5fdb90c..b968aa972c81 100644 --- a/packages/3-targets/3-targets/postgres/src/core/temporal-codecs.ts +++ b/packages/3-targets/3-targets/postgres/src/core/temporal-codecs.ts @@ -16,6 +16,7 @@ import { PG_DATE_STRING_CODEC_ID, PG_DATE_TEMPORAL_CODEC_ID, PG_TIME_TEMPORAL_CODEC_ID, + PG_TIMESTAMP_INSTANT_CODEC_ID, PG_TIMESTAMP_STRING_CODEC_ID, PG_TIMESTAMP_TEMPORAL_CODEC_ID, PG_TIMESTAMPTZ_STRING_CODEC_ID, @@ -25,6 +26,8 @@ import { pgDate, pgPrecisionParams, pgTime, pgTimestamp, pgTimestamptz } from '. import { pgDateTemporalDecode, pgDateTemporalEncode, + pgTimestampInstantDecode, + pgTimestampInstantEncode, pgTimestampTemporalDecode, pgTimestampTemporalEncode, pgTimestamptzTemporalDecode, @@ -134,6 +137,62 @@ export const pgTimestampTemporalColumn = (params: PrecisionParams = {}) => pgTimestampTemporalColumn satisfies ColumnHelperFor; pgTimestampTemporalColumn satisfies ColumnHelperForStrict; +/** + * A `timestamp` column read and written as a `Temporal.Instant`, treating the stored date and time as UTC. A write sends the instant's UTC date and time with no offset. + */ +export class PgTimestampInstantCodec extends CodecImpl< + typeof PG_TIMESTAMP_INSTANT_CODEC_ID, + readonly ['equality', 'order'], + string, + Temporal.Instant +> { + async toWire(value: Temporal.Instant, _ctx: CodecCallContext): Promise { + return pgTimestampInstantEncode(value); + } + async fromWire(wire: string, _ctx: CodecCallContext): Promise { + return pgTimestampInstantDecode(wire); + } + fromDataTypeValue(value: DataTypeValue): Temporal.Instant { + refuseInfinity( + this.id, + 'an instant a Temporal.Instant holds', + PG_TIMESTAMP_STRING_CODEC_ID, + value.value, + ); + return pgTimestampInstantDecode(value.value); + } + toDataTypeValue(input: Temporal.Instant): DataTypeValue { + return this.dataTypeValueOf(pgTimestampInstantEncode(input)); + } +} + +export class PgTimestampInstantDescriptor extends PostgresCodecDescriptor { + protected override jsonProjection(expression: ProjectionExpr): ProjectionExpr { + return CastExpr.as(expression, 'text'); + } + override readonly dataType = pgTimestamp.id; + override readonly codecId = PG_TIMESTAMP_INSTANT_CODEC_ID; + override readonly traits = ['equality', 'order'] as const; + override readonly paramsSchema = pgPrecisionParams satisfies StandardSchemaV1; + override factory( + params: PrecisionParams, + ): (ctx: CodecInstanceContext) => PgTimestampInstantCodec { + return () => new PgTimestampInstantCodec(this, pgTimestamp, params ?? {}); + } +} + +export const pgTimestampInstantDescriptor = new PgTimestampInstantDescriptor(); + +export const pgTimestampInstantColumn = (params: PrecisionParams = {}) => + column( + pgTimestampInstantDescriptor.factory(params), + pgTimestampInstantDescriptor.codecId, + params, + ); + +pgTimestampInstantColumn satisfies ColumnHelperFor; +pgTimestampInstantColumn satisfies ColumnHelperForStrict; + export class PgTimestamptzTemporalCodec extends CodecImpl< typeof PG_TIMESTAMPTZ_TEMPORAL_CODEC_ID, readonly ['equality', 'order'], diff --git a/packages/3-targets/3-targets/postgres/src/core/type-constructors.ts b/packages/3-targets/3-targets/postgres/src/core/type-constructors.ts index 741ccba93192..34ef9ecc5760 100644 --- a/packages/3-targets/3-targets/postgres/src/core/type-constructors.ts +++ b/packages/3-targets/3-targets/postgres/src/core/type-constructors.ts @@ -1,5 +1,10 @@ import type { AuthoringTypeNamespace } from '@internal/framework-components/authoring'; -import { PG_TIMESTAMPTZ_DATE_CODEC_ID } from './codec-ids'; +import { + PG_INTERVAL_STRING_CODEC_ID, + PG_TIMESTAMP_DATE_CODEC_ID, + PG_TIMESTAMP_INSTANT_CODEC_ID, + PG_TIMESTAMPTZ_DATE_CODEC_ID, +} from './codec-ids'; /** The data type of every size argument on Postgres, such as a length or a precision: a typmod is an `int4`. */ export const PG_SIZE_TYPE = 'pg/int4' as const; @@ -220,6 +225,26 @@ export const postgresNativeAuthoringTypes = { typeParams: { precision: { kind: 'arg', index: 0 } }, }, }, + TimestampJsDate: { + kind: 'typeConstructor', + documentation: + 'A date and time stored as PostgreSQL timestamp and represented as a JavaScript Date, read and written as UTC.', + args: [{ kind: 'value', dataType: PG_SIZE_TYPE, name: 'precision', optional: true }], + output: { + codecId: PG_TIMESTAMP_DATE_CODEC_ID, + typeParams: { precision: { kind: 'arg', index: 0 } }, + }, + }, + TimestampInstant: { + kind: 'typeConstructor', + documentation: + 'A date and time stored as PostgreSQL timestamp and represented as Temporal.Instant, read and written as UTC.', + args: [{ kind: 'value', dataType: PG_SIZE_TYPE, name: 'precision', optional: true }], + output: { + codecId: PG_TIMESTAMP_INSTANT_CODEC_ID, + typeParams: { precision: { kind: 'arg', index: 0 } }, + }, + }, TimestamptzString: { kind: 'typeConstructor', inferred: true, @@ -271,6 +296,16 @@ export const postgresNativeAuthoringTypes = { typeParams: { precision: { kind: 'arg', index: 0 } }, }, }, + IntervalString: { + kind: 'typeConstructor', + documentation: + 'A span of time stored as PostgreSQL interval and represented as an ISO 8601 duration such as P1Y2M, with optional fractional-second precision.', + args: [{ kind: 'value', dataType: PG_SIZE_TYPE, name: 'precision', optional: true }], + output: { + codecId: PG_INTERVAL_STRING_CODEC_ID, + typeParams: { precision: { kind: 'arg', index: 0 } }, + }, + }, } as const satisfies AuthoringTypeNamespace; /** diff --git a/packages/3-targets/3-targets/postgres/src/exports/codecs.ts b/packages/3-targets/3-targets/postgres/src/exports/codecs.ts index 57303075732d..4175a27ffd22 100644 --- a/packages/3-targets/3-targets/postgres/src/exports/codecs.ts +++ b/packages/3-targets/3-targets/postgres/src/exports/codecs.ts @@ -60,11 +60,21 @@ export { } from '../core/codecs'; export type { PgInterval } from '../core/data-type-values'; export { + PgTimestampDateCodec, + PgTimestampDateDescriptor, PgTimestamptzDateCodec, PgTimestamptzDateDescriptor, + pgTimestampDateColumn, + pgTimestampDateDescriptor, pgTimestamptzDateColumn, pgTimestamptzDateDescriptor, } from '../core/date-codecs'; +export { + PgIntervalStringCodec, + PgIntervalStringDescriptor, + pgIntervalStringColumn, + pgIntervalStringDescriptor, +} from '../core/interval-string-codec'; export { parsePostgresListText } from '../core/list-decoder'; export { postgresCodecDescriptorRegistry, diff --git a/packages/3-targets/3-targets/postgres/test/codec-render-value-literal.test.ts b/packages/3-targets/3-targets/postgres/test/codec-render-value-literal.test.ts index eb911100de41..eb081cb2c37d 100644 --- a/packages/3-targets/3-targets/postgres/test/codec-render-value-literal.test.ts +++ b/packages/3-targets/3-targets/postgres/test/codec-render-value-literal.test.ts @@ -195,6 +195,8 @@ describe('the literal each registered codec renders', () => { 'pg/timestamp-string@1': undefined, 'pg/timestamptz-string@1': undefined, 'pg/timestamptz-date@1': undefined, + 'pg/timestamp-date@1': undefined, + 'pg/timestamp-instant@1': undefined, 'pg/time-string@1': '"12:34:56"', 'pg/timetz@1': undefined, 'pg/bool@1': 'true', @@ -204,6 +206,7 @@ describe('the literal each registered codec renders', () => { 'pg/uuid@1': '"a0eebc99-9c0b-4ef8-bb6d-6bb9bd380a11"', 'pg/inet@1': '"127.0.0.1"', 'pg/interval@1': undefined, + 'pg/interval-string@1': undefined, 'pg/json@1': undefined, 'pg/jsonb@1': undefined, 'pg/text-array@1': undefined, diff --git a/packages/3-targets/3-targets/postgres/test/data-type-inventory.test.ts b/packages/3-targets/3-targets/postgres/test/data-type-inventory.test.ts index 44d1d4d6c253..079dd6c0dec1 100644 --- a/packages/3-targets/3-targets/postgres/test/data-type-inventory.test.ts +++ b/packages/3-targets/3-targets/postgres/test/data-type-inventory.test.ts @@ -21,6 +21,7 @@ const EXPECTED: Readonly> = { 'pg/varbit@1': 'pg/varbit', 'pg/bytea@1': 'pg/bytea', 'pg/interval@1': 'pg/interval', + 'pg/interval-string@1': 'pg/interval', 'pg/timetz@1': 'pg/timetz', 'pg/date-temporal@1': 'pg/date', 'pg/date-string@1': 'pg/date', @@ -28,6 +29,8 @@ const EXPECTED: Readonly> = { 'pg/time-string@1': 'pg/time', 'pg/timestamp-temporal@1': 'pg/timestamp', 'pg/timestamp-string@1': 'pg/timestamp', + 'pg/timestamp-date@1': 'pg/timestamp', + 'pg/timestamp-instant@1': 'pg/timestamp', 'pg/timestamptz-temporal@1': 'pg/timestamptz', 'pg/timestamptz-string@1': 'pg/timestamptz', 'pg/timestamptz-date@1': 'pg/timestamptz', diff --git a/packages/3-targets/3-targets/postgres/test/data-types.test.ts b/packages/3-targets/3-targets/postgres/test/data-types.test.ts index 12372a2f96f1..ec68908b9560 100644 --- a/packages/3-targets/3-targets/postgres/test/data-types.test.ts +++ b/packages/3-targets/3-targets/postgres/test/data-types.test.ts @@ -264,7 +264,10 @@ describe("each codec's equality-key flag", () => { withoutKey: flagged .filter((descriptor) => types.get(descriptor.dataType)?.equalityKey === undefined) .map((descriptor) => descriptor.codecId), - }).toEqual({ flagged: ['pg/numeric@1', 'pg/interval@1'], withoutKey: [] }); + }).toEqual({ + flagged: ['pg/numeric@1', 'pg/interval@1', 'pg/interval-string@1'], + withoutKey: [], + }); }); it('is declared only by codecs that render no literal, since a read can return a member in another form', async () => { diff --git a/packages/3-targets/3-targets/postgres/test/explicit-type-codec-wiring.test.ts b/packages/3-targets/3-targets/postgres/test/explicit-type-codec-wiring.test.ts new file mode 100644 index 000000000000..cb2451195dd6 --- /dev/null +++ b/packages/3-targets/3-targets/postgres/test/explicit-type-codec-wiring.test.ts @@ -0,0 +1,80 @@ +import { describe, expect, it } from 'vitest'; +import { postgresAggregateDescriptors } from '../src/core/aggregates'; +import { codecDescriptorMap } from '../src/core/codec-type-map'; +import { codecDescriptors } from '../src/core/codecs'; +import * as codecIds from '../src/exports/codec-ids'; +import * as codecs from '../src/exports/codecs'; + +const rowsFor = (codecId: string) => + postgresAggregateDescriptors.filter( + (row) => row.input.kind === 'codec' && row.input.codecId === codecId, + ); + +const preserved = (codecId: string) => + ['min', 'max'].map((operation) => ({ + operation, + input: { kind: 'codec', codecId }, + output: { kind: 'self' }, + nullable: true, + })); + +describe.each([ + { + codecId: 'pg/timestamp-date@1', + constant: 'PG_TIMESTAMP_DATE_CODEC_ID', + mapKey: 'timestamp-date', + exported: [ + 'PgTimestampDateCodec', + 'PgTimestampDateDescriptor', + 'pgTimestampDateColumn', + 'pgTimestampDateDescriptor', + ], + }, + { + codecId: 'pg/timestamp-instant@1', + constant: 'PG_TIMESTAMP_INSTANT_CODEC_ID', + mapKey: 'timestamp-instant', + exported: [], + }, + { + codecId: 'pg/interval-string@1', + constant: 'PG_INTERVAL_STRING_CODEC_ID', + mapKey: 'interval-string', + exported: [ + 'PgIntervalStringCodec', + 'PgIntervalStringDescriptor', + 'pgIntervalStringColumn', + 'pgIntervalStringDescriptor', + ], + }, +])('$codecId', ({ codecId, constant, mapKey, exported }) => { + it('has an exported id, is registered once and is in the codec type map', () => { + const registered = codecs.postgresCodecRegistry.descriptorFor(codecId); + expect(codecIds).toHaveProperty(constant, codecId); + for (const name of exported) expect(codecs).toHaveProperty(name); + expect(codecDescriptorMap).toHaveProperty(mapKey, registered); + expect(codecDescriptors.filter((entry) => entry.codecId === codecId)).toEqual([registered]); + }); +}); + +describe('the aggregates over the explicit timestamp and interval codecs', () => { + it.each(['pg/timestamp-date@1', 'pg/timestamp-instant@1'])( + 'keep %s for min and max and offer no sum or avg', + (codecId) => { + expect(rowsFor(codecId)).toEqual(preserved(codecId)); + }, + ); + + it('sum and average an interval read as a string into the same codec, as PostgreSQL does', () => { + const codecId = 'pg/interval-string@1'; + expect(rowsFor(codecId)).toEqual([ + ...['sum', 'avg'].map((operation) => ({ + operation, + input: { kind: 'codec', codecId }, + output: { kind: 'codec', codecId }, + nullable: true, + })), + ...preserved(codecId), + ]); + }); +}); diff --git a/packages/3-targets/3-targets/postgres/test/generator-functions.test.ts b/packages/3-targets/3-targets/postgres/test/generator-functions.test.ts index 2e551c71e01b..3a809de01207 100644 --- a/packages/3-targets/3-targets/postgres/test/generator-functions.test.ts +++ b/packages/3-targets/3-targets/postgres/test/generator-functions.test.ts @@ -16,6 +16,8 @@ describe('the Postgres generator functions', () => { ['pg/timestamptz-string@1', 'timestamptzStringNow'], ['pg/timestamp-string@1', 'timestampStringNow'], ['pg/timestamptz-date@1', 'timestampNow'], + ['pg/timestamp-date@1', 'timestampNow'], + ['pg/timestamp-instant@1', 'instantNow'], ['pg/date-temporal@1', 'plainDateNow'], ['pg/date-string@1', 'dateStringNow'], ])('resolve now() on %s to %s with no params', (codecId, generatorId) => { diff --git a/packages/3-targets/3-targets/postgres/test/interval-string-codec.test.ts b/packages/3-targets/3-targets/postgres/test/interval-string-codec.test.ts new file mode 100644 index 000000000000..f11933f276cb --- /dev/null +++ b/packages/3-targets/3-targets/postgres/test/interval-string-codec.test.ts @@ -0,0 +1,72 @@ +import { describe, expect, it } from 'vitest'; +import { pgIntervalStringDescriptor } from '../src/core/interval-string-codec'; +import { fromContractJson, toContractJson } from './contract-json'; + +const codec = pgIntervalStringDescriptor.factory({})({ name: 'test' }); + +describe('pg/interval-string@1', () => { + it('represents pg/interval, keeping the form a value was written in', () => { + expect({ + dataType: pgIntervalStringDescriptor.dataType, + traits: pgIntervalStringDescriptor.traits, + keepsWrittenForm: pgIntervalStringDescriptor.keepsWrittenForm, + }).toEqual({ dataType: 'pg/interval', traits: ['equality', 'order'], keepsWrittenForm: true }); + }); + + it.each([ + ['1 year 2 mons 3 days 04:05:06.5', 'P1Y2M3DT4H5M6.5S'], + ['-1 years -2 mons +3 days -04:00:00', 'P-1Y-2M3DT-4H'], + ['00:00:00', 'PT0S'], + ['@ 1 year 2 mons -3 days 4 hours 5 mins 6.5 secs ago', 'P-1Y-2M3DT-4H-5M-6.5S'], + ['+1-2 -3 +4:05:06.5', 'P1Y2M-3DT4H5M6.5S'], + ['-1 2:03:04', 'P-1DT-2H-3M-4S'], + ['P1Y2M3DT4H5M6.5S', 'P1Y2M3DT4H5M6.5S'], + ['P0D', 'PT0S'], + ])('reads the text %s, printed under any IntervalStyle, as %s', async (wire, iso) => { + expect(await codec.fromWire(wire, {})).toBe(iso); + }); + + it('reads the fields object a driver may parse an interval into', async () => { + expect(await codec.fromWire({ years: 1, months: 2, days: 3, hours: 4 }, {})).toBe('P1Y2M3DT4H'); + }); + + it.each([ + ['P1Y2M', 'P1Y2M'], + ['P14M', 'P1Y2M'], + ['PT90M', 'PT1H30M'], + ['P1DT-1H', 'P1DT-1H'], + ['PT0.000001S', 'PT0.000001S'], + ])('writes %s as the duration pg/interval stores, %s', async (written, stored) => { + expect({ + wire: await codec.toWire(written, {}), + json: toContractJson(codec, written), + fromJson: fromContractJson(codec, stored), + }).toEqual({ wire: stored, json: stored, fromJson: stored }); + }); + + it.each([ + ['1 day', 'an ISO 8601 duration'], + ['P', 'an ISO 8601 duration'], + ['', 'an ISO 8601 duration'], + ['PT1.1234567S', 'at most 6'], + ])('refuses %s on write', async (written, reason) => { + await expect(codec.toWire(written, {})).rejects.toThrow( + expect.objectContaining({ + code: 'RUNTIME.ENCODE_FAILED', + message: expect.stringContaining(reason), + }), + ); + expect(() => toContractJson(codec, written)).toThrow( + expect.objectContaining({ code: 'RUNTIME.ENCODE_FAILED' }), + ); + }); + + it.each([42, null, { months: 1, days: 0, micros: 0n }])( + 'refuses %s, which is not a string, on write', + async (value) => { + await expect(codec.toWire(value as unknown as string, {})).rejects.toThrow( + expect.objectContaining({ code: 'RUNTIME.ENCODE_FAILED' }), + ); + }, + ); +}); diff --git a/packages/3-targets/3-targets/postgres/test/now-generators.test.ts b/packages/3-targets/3-targets/postgres/test/now-generators.test.ts index c45cba1a51e5..114c1585ff6c 100644 --- a/packages/3-targets/3-targets/postgres/test/now-generators.test.ts +++ b/packages/3-targets/3-targets/postgres/test/now-generators.test.ts @@ -42,6 +42,8 @@ const DATE_AND_TIME_CODEC_IDS = [ 'pg/timestamptz-temporal@1', 'pg/timestamptz-string@1', 'pg/timestamptz-date@1', + 'pg/timestamp-date@1', + 'pg/timestamp-instant@1', ]; function codecFor(codecId: string) { @@ -70,10 +72,9 @@ describe('the "now" generator for each codec', () => { }); it('has no generator for a codec without one', () => { - expect(['pg/text@1', 'pg/interval@1'].map(postgresNowGeneratorIdFor)).toEqual([ - undefined, - undefined, - ]); + expect( + ['pg/text@1', 'pg/interval@1', 'pg/interval-string@1'].map(postgresNowGeneratorIdFor), + ).toEqual([undefined, undefined, undefined]); }); it('pairs each date and time codec with its generator', () => { @@ -88,6 +89,8 @@ describe('the "now" generator for each codec', () => { 'pg/timestamptz-temporal@1': 'instantNow', 'pg/timestamptz-string@1': 'timestamptzStringNow', 'pg/timestamptz-date@1': 'timestampNow', + 'pg/timestamp-date@1': 'timestampNow', + 'pg/timestamp-instant@1': 'instantNow', }); }); diff --git a/packages/3-targets/3-targets/postgres/test/temporal-representation-taxonomy.test.ts b/packages/3-targets/3-targets/postgres/test/temporal-representation-taxonomy.test.ts index 226d7c78bafb..898f772667b0 100644 --- a/packages/3-targets/3-targets/postgres/test/temporal-representation-taxonomy.test.ts +++ b/packages/3-targets/3-targets/postgres/test/temporal-representation-taxonomy.test.ts @@ -1,9 +1,16 @@ import { ColumnRef, type ProjectionExpr } from '@internal/sql-relational-core/ast'; import { describe, expect, it } from 'vitest'; -import { pgTimestamptzDateColumn, pgTimestamptzDateDescriptor } from '../src/core/date-codecs'; +import { + pgTimestampDateColumn, + pgTimestampDateDescriptor, + pgTimestamptzDateColumn, + pgTimestamptzDateDescriptor, +} from '../src/core/date-codecs'; import { pgDateTemporalColumn, pgDateTemporalDescriptor, + pgTimestampInstantColumn, + pgTimestampInstantDescriptor, pgTimestampTemporalColumn, pgTimestampTemporalDescriptor, pgTimestamptzTemporalColumn, @@ -50,6 +57,7 @@ interface TaxonomyRow { readonly temporal: Representation; readonly string: Representation; readonly date?: Representation; + readonly instant?: Representation; } const TAXONOMY: readonly TaxonomyRow[] = [ @@ -82,6 +90,16 @@ const TAXONOMY: readonly TaxonomyRow[] = [ descriptor: pgTimestampStringDescriptor, column: pgTimestampStringColumn, }, + date: { + codecId: 'pg/timestamp-date@1', + descriptor: pgTimestampDateDescriptor, + column: pgTimestampDateColumn, + }, + instant: { + codecId: 'pg/timestamp-instant@1', + descriptor: pgTimestampInstantDescriptor, + column: pgTimestampInstantColumn, + }, }, { typeText: 'timestamptz', @@ -124,11 +142,12 @@ const representations = TAXONOMY.flatMap((row) => [ { row, kind: 'temporal' as const, rep: row.temporal }, { row, kind: 'string' as const, rep: row.string }, ...(row.date ? [{ row, kind: 'date' as const, rep: row.date }] : []), + ...(row.instant ? [{ row, kind: 'instant' as const, rep: row.instant }] : []), ]); const sourceExpression = ColumnRef.of('reading', 'at'); -describe('the nine representation-explicit temporal codecs', () => { +describe('the eleven representation-explicit temporal codecs', () => { describe.each(representations)('$rep.codecId', ({ row, rep }) => { it('declares the id the taxonomy names', () => { expect(rep.descriptor.codecId).toBe(rep.codecId); diff --git a/packages/3-targets/3-targets/postgres/test/timestamp-utc-codecs.test.ts b/packages/3-targets/3-targets/postgres/test/timestamp-utc-codecs.test.ts new file mode 100644 index 000000000000..470b64cdac30 --- /dev/null +++ b/packages/3-targets/3-targets/postgres/test/timestamp-utc-codecs.test.ts @@ -0,0 +1,149 @@ +import { describe, expect, it } from 'vitest'; +import { pgTimestampDateDescriptor } from '../src/core/date-codecs'; +import { pgTimestampInstantDescriptor } from '../src/core/temporal-codecs'; +import { fromContractJson, toContractJson } from './contract-json'; + +describe('pg/timestamp-date@1', () => { + const codec = pgTimestampDateDescriptor.factory({})({ name: 'test' }); + + it('represents pg/timestamp with the order traits', () => { + expect({ + dataType: pgTimestampDateDescriptor.dataType, + traits: pgTimestampDateDescriptor.traits, + }).toEqual({ dataType: 'pg/timestamp', traits: ['equality', 'order'] }); + }); + + it.each([ + ['2026-01-02 03:04:05.123456', '2026-01-02T03:04:05.123Z'], + ['2026-01-02 03:04:05', '2026-01-02T03:04:05.000Z'], + ['0001-01-01 00:00:00 BC', '0000-01-01T00:00:00.000Z'], + ['0044-03-15 12:00:00 BC', '-000043-03-15T12:00:00.000Z'], + ['4714-11-24 00:00:00 BC', '-004713-11-24T00:00:00.000Z'], + ['12026-01-02 03:04:05', '+012026-01-02T03:04:05.000Z'], + ['275760-09-13 00:00:00', '+275760-09-13T00:00:00.000Z'], + ])('reads the wire text %s as UTC', async (wire, iso) => { + expect(await codec.fromWire(wire, {})).toEqual(new Date(iso)); + }); + + it.each([ + ['2026-01-02T03:04:05.123Z', '2026-01-02T03:04:05.123', '2026-01-02T03:04:05.123'], + ['2026-01-02T03:04:05.000Z', '2026-01-02T03:04:05.000', '2026-01-02T03:04:05'], + ['-000043-03-15T12:00:00.000Z', '0044-03-15T12:00:00.000 BC', '-000043-03-15T12:00:00'], + ['+012026-01-02T03:04:05.000Z', '12026-01-02T03:04:05.000', '+012026-01-02T03:04:05'], + ])( + 'writes %s as its UTC wall-clock time, with no offset, on the wire and in JSON', + async (iso, wire, json) => { + const value = new Date(iso); + expect({ + wire: await codec.toWire(value, {}), + json: toContractJson(codec, value), + fromWire: await codec.fromWire(wire, {}), + fromJson: fromContractJson(codec, json), + }).toEqual({ wire, json, fromWire: value, fromJson: value }); + }, + ); + + it.each(['infinity', '-infinity', '275760-09-13 00:00:01'])( + 'refuses the wire text %s, which no Date holds, naming TimestampString', + async (wire) => { + await expect(codec.fromWire(wire, {})).rejects.toThrow(/TimestampString/); + }, + ); + + it.each(['infinity', '-infinity', '+275760-09-13T00:00:01'])( + 'refuses the stored value %s, which no Date holds, naming pg/timestamp-string@1', + (json) => { + expect(() => fromContractJson(codec, json)).toThrow( + expect.objectContaining({ + code: 'RUNTIME.DECODE_FAILED', + message: expect.stringContaining('pg/timestamp-string@1'), + }), + ); + }, + ); + + it('refuses wire text with an offset, which a timestamp column never prints', async () => { + await expect(codec.fromWire('2026-01-02 03:04:05+00', {})).rejects.toThrow( + 'pg/timestamp-date@1', + ); + }); + + it.each([new Date(Number.NaN), new Date('-004713-11-23T23:59:59.999Z'), '2026-01-01', null])( + 'refuses %s on write', + async (value) => { + await expect(codec.toWire(value as Date, {})).rejects.toThrow('pg/timestamp-date@1'); + expect(() => toContractJson(codec, value as Date)).toThrow('pg/timestamp-date@1'); + }, + ); +}); + +describe('pg/timestamp-instant@1', () => { + const codec = pgTimestampInstantDescriptor.factory({})({ name: 'test' }); + + it('represents pg/timestamp with the order traits', () => { + expect({ + dataType: pgTimestampInstantDescriptor.dataType, + traits: pgTimestampInstantDescriptor.traits, + }).toEqual({ dataType: 'pg/timestamp', traits: ['equality', 'order'] }); + }); + + it.each([ + ['2026-01-02 03:04:05.123456', '2026-01-02T03:04:05.123456Z'], + ['2026-01-02 03:04:05', '2026-01-02T03:04:05Z'], + ['0044-03-15 12:00:00 BC', '-000043-03-15T12:00:00Z'], + ['12026-01-02 03:04:05', '+012026-01-02T03:04:05Z'], + ['275760-09-13 00:00:00', '+275760-09-13T00:00:00Z'], + ])('reads the wire text %s as an instant in UTC', async (wire, iso) => { + const read = await codec.fromWire(wire, {}); + expect(read.equals(Temporal.Instant.from(iso))).toBe(true); + }); + + it.each([ + ['2026-01-02T03:04:05.123456Z', '2026-01-02T03:04:05.123456', '2026-01-02T03:04:05.123456'], + ['2026-01-02T03:04:05Z', '2026-01-02T03:04:05', '2026-01-02T03:04:05'], + ['-000043-03-15T12:00:00Z', '0044-03-15T12:00:00 BC', '-000043-03-15T12:00:00'], + ])( + 'writes %s as its UTC wall-clock time, with no offset, on the wire and in JSON', + async (iso, wire, json) => { + const value = Temporal.Instant.from(iso); + const read = { + wire: await codec.toWire(value, {}), + json: toContractJson(codec, value), + fromWire: (await codec.fromWire(wire, {})).equals(value), + fromJson: fromContractJson(codec, json).equals(value), + }; + expect(read).toEqual({ wire, json, fromWire: true, fromJson: true }); + }, + ); + + it.each(['infinity', '-infinity', '275760-09-13 00:00:01'])( + 'refuses the wire text %s, which no Temporal.Instant holds, naming TimestampString', + async (wire) => { + await expect(codec.fromWire(wire, {})).rejects.toThrow( + expect.objectContaining({ + code: 'RUNTIME.DECODE_FAILED', + message: expect.stringContaining('TimestampString'), + }), + ); + }, + ); + + it.each(['infinity', '-infinity'])( + 'refuses the stored value %s, naming pg/timestamp-string@1', + (json) => { + expect(() => fromContractJson(codec, json)).toThrow( + expect.objectContaining({ message: expect.stringContaining('pg/timestamp-string@1') }), + ); + }, + ); + + it.each([ + [Temporal.PlainDateTime.from('2026-01-02T03:04:05'), 'a Temporal.PlainDateTime'], + [new Date('2026-01-02T03:04:05Z'), 'a Date'], + ['2026-01-02T03:04:05Z', 'a string'], + ])('refuses %s on write, naming Temporal.Instant', async (value, received) => { + await expect(codec.toWire(value as unknown as Temporal.Instant, {})).rejects.toThrow( + `Codec 'pg/timestamp-instant@1' encodes a Temporal.Instant, but received ${received}.`, + ); + }); +}); diff --git a/packages/3-targets/3-targets/postgres/test/type-constructors.test.ts b/packages/3-targets/3-targets/postgres/test/type-constructors.test.ts index 8552de4f050d..dfb23413e88a 100644 --- a/packages/3-targets/3-targets/postgres/test/type-constructors.test.ts +++ b/packages/3-targets/3-targets/postgres/test/type-constructors.test.ts @@ -260,6 +260,9 @@ describe('postgresNativeAuthoringTypes', () => { Bit: { codecId: 'pg/bit@1' }, VarBit: { codecId: 'pg/varbit@1' }, IntervalParts: { codecId: 'pg/interval@1' }, + IntervalString: { codecId: 'pg/interval-string@1' }, + TimestampJsDate: { codecId: 'pg/timestamp-date@1' }, + TimestampInstant: { codecId: 'pg/timestamp-instant@1' }, }); }); @@ -295,6 +298,9 @@ describe('postgresNativeAuthoringTypes', () => { ['VarBit', [16], { codecId: 'pg/varbit@1', typeParams: { length: 16 } }], ['IntervalParts', [], { codecId: 'pg/interval@1' }], ['IntervalParts', [3], { codecId: 'pg/interval@1', typeParams: { precision: 3 } }], + ['IntervalString', [3], { codecId: 'pg/interval-string@1', typeParams: { precision: 3 } }], + ['TimestampJsDate', [3], { codecId: 'pg/timestamp-date@1', typeParams: { precision: 3 } }], + ['TimestampInstant', [6], { codecId: 'pg/timestamp-instant@1', typeParams: { precision: 6 } }], ] as const)('maps %s(%s) onto the parameter of the same name', (name, args, expected) => { expect(instantiateAuthoringTypeConstructor(postgresNativeAuthoringTypes[name], args)).toEqual( expected, @@ -331,6 +337,9 @@ describe('postgresNativeAuthoringTypes', () => { ['Bit', [0]], ['VarBit', [0]], ['IntervalParts', [7]], + ['IntervalString', [7]], + ['TimestampInstant', [7]], + ['TimestampJsDate', [7]], ] as const)('refuses %s(%s)', (name, args) => { expect(() => check(name, args)).toThrow( expect.objectContaining({ code: 'CONTRACT.ARGUMENT_INVALID' }), @@ -346,6 +355,10 @@ describe('postgresNativeAuthoringTypes', () => { ['TimestampTemporal', [0]], ['TimestampTemporal', [6]], ['Timetz', [6]], + ['TimestampJsDate', [0]], + ['TimestampJsDate', [6]], + ['TimestampInstant', [6]], + ['IntervalString', [6]], ] as const)('accepts %s(%s)', (name, args) => { expect(() => check(name, args)).not.toThrow(); }); diff --git a/packages/3-targets/6-adapters/postgres-codec-testkit/test/aggregate-defaults.integration.test.ts b/packages/3-targets/6-adapters/postgres-codec-testkit/test/aggregate-defaults.integration.test.ts index a567114531b0..d93f44311434 100644 --- a/packages/3-targets/6-adapters/postgres-codec-testkit/test/aggregate-defaults.integration.test.ts +++ b/packages/3-targets/6-adapters/postgres-codec-testkit/test/aggregate-defaults.integration.test.ts @@ -118,6 +118,7 @@ describe('PostgreSQL aggregate defaults', { concurrent: false }, () => { 'sum(pg/float4@1)': output('sum', 'pg/float4@1'), 'sum(pg/float8@1)': output('sum', 'pg/float8@1'), 'sum(pg/interval@1)': output('sum', 'pg/interval@1'), + 'sum(pg/interval-string@1)': output('sum', 'pg/interval-string@1'), 'sumBigInt(pg/int2@1)': output('sumBigInt', 'pg/int2@1'), 'sumBigInt(pg/int4@1)': output('sumBigInt', 'pg/int4@1'), 'sumBigInt(pg/int8@1)': output('sumBigInt', 'pg/int8@1'), @@ -130,6 +131,7 @@ describe('PostgreSQL aggregate defaults', { concurrent: false }, () => { 'avg(pg/numeric@1)': output('avg', 'pg/numeric@1'), 'avg(pg/float4@1)': output('avg', 'pg/float4@1'), 'avg(pg/interval@1)': output('avg', 'pg/interval@1'), + 'avg(pg/interval-string@1)': output('avg', 'pg/interval-string@1'), 'avgDecimal(pg/int4@1)': output('avgDecimal', 'pg/int4@1'), 'avgDecimal(pg/int8@1)': output('avgDecimal', 'pg/int8@1'), 'avgDecimal(pg/unboundedint@1)': output('avgDecimal', 'pg/unboundedint@1'), @@ -150,6 +152,7 @@ describe('PostgreSQL aggregate defaults', { concurrent: false }, () => { 'sum(pg/float4@1)': 'pg/float4@1', 'sum(pg/float8@1)': 'pg/float8@1', 'sum(pg/interval@1)': 'pg/interval@1', + 'sum(pg/interval-string@1)': 'pg/interval-string@1', 'sumBigInt(pg/int2@1)': 'pg/int8@1', 'sumBigInt(pg/int4@1)': 'pg/int8@1', 'sumBigInt(pg/int8@1)': 'pg/unboundedint@1', @@ -162,6 +165,7 @@ describe('PostgreSQL aggregate defaults', { concurrent: false }, () => { 'avg(pg/numeric@1)': 'pg/numeric@1', 'avg(pg/float4@1)': 'pg/float8@1', 'avg(pg/interval@1)': 'pg/interval@1', + 'avg(pg/interval-string@1)': 'pg/interval-string@1', 'avgDecimal(pg/int4@1)': 'pg/numeric@1', 'avgDecimal(pg/int8@1)': 'pg/numeric@1', 'avgDecimal(pg/unboundedint@1)': 'pg/numeric@1', diff --git a/packages/3-targets/6-adapters/postgres-codec-testkit/test/aggregate-matrix.ts b/packages/3-targets/6-adapters/postgres-codec-testkit/test/aggregate-matrix.ts index 84d2f3341ebc..ed7ccf080176 100644 --- a/packages/3-targets/6-adapters/postgres-codec-testkit/test/aggregate-matrix.ts +++ b/packages/3-targets/6-adapters/postgres-codec-testkit/test/aggregate-matrix.ts @@ -159,6 +159,16 @@ export const FIXTURES: readonly AggregateFixture[] = [ typeParams: { precision: 6 }, samples: ["'2024-01-01T10:00:00.123456'", "'2024-02-01T10:00:00.654321'"], }, + { + codecId: 'pg/timestamp-date@1', + typeParams: { precision: 3 }, + samples: ["'2024-01-01T10:00:00.123'", "'2024-02-01T10:00:00.654'"], + }, + { + codecId: 'pg/timestamp-instant@1', + typeParams: { precision: 6 }, + samples: ["'2024-01-01T10:00:00.123456'", "'2024-02-01T10:00:00.654321'"], + }, { codecId: 'pg/timestamptz-date@1', typeParams: { precision: 3 }, @@ -186,6 +196,7 @@ export const FIXTURES: readonly AggregateFixture[] = [ { codecId: 'pg/inet@1', samples: ["'10.0.0.1'", "'10.0.0.2'"] }, { codecId: 'pg/tsquery@1', samples: ["'graze'", "'zebra'"] }, { codecId: 'pg/interval@1', samples: ["'1 day'", "'2 days'"] }, + { codecId: 'pg/interval-string@1', samples: ["'1 day'", "'2 days 00:00:00.5'"] }, { codecId: 'pg/json@1', samples: ['\'{"a":1}\'', '\'{"b":2}\''] }, { codecId: 'pg/jsonb@1', samples: ['\'{"a":1}\'', '\'{"b":2}\''] }, { codecId: 'pg/text-array@1', samples: ["ARRAY['a']", "ARRAY['b']"] }, diff --git a/packages/3-targets/6-adapters/postgres-codec-testkit/test/codec-conformance/cases.ts b/packages/3-targets/6-adapters/postgres-codec-testkit/test/codec-conformance/cases.ts index dff64d8226e5..68d42c0e1ffd 100644 --- a/packages/3-targets/6-adapters/postgres-codec-testkit/test/codec-conformance/cases.ts +++ b/packages/3-targets/6-adapters/postgres-codec-testkit/test/codec-conformance/cases.ts @@ -29,6 +29,11 @@ const instantsEqual = (roundTripped: unknown, value: unknown): boolean => value instanceof Temporal.Instant && roundTripped.equals(value); +const datesEqual = (roundTripped: unknown, value: unknown): boolean => + roundTripped instanceof Date && + value instanceof Date && + roundTripped.getTime() === value.getTime(); + const plainDateTimesEqual = (roundTripped: unknown, value: unknown): boolean => roundTripped instanceof Temporal.PlainDateTime && value instanceof Temporal.PlainDateTime && @@ -276,6 +281,53 @@ export const postgresConformanceCases: readonly PostgresCodecConformanceCase[] = projectionEquality: (left, right) => left instanceof Date && right instanceof Date && left.getTime() === right.getTime(), }, + // A timestamp read as a Date or a Temporal.Instant names the stored date and time in UTC, so a + // hostile session time zone must not move it. + { + codecId: 'pg/timestamp-date@1', + label: 'millisecond precision', + value: new Date('2026-01-02T03:04:05.123Z'), + typeParams: { precision: 3 }, + valueEquality: datesEqual, + projectionEquality: datesEqual, + }, + { + codecId: 'pg/timestamp-date@1', + label: 'a year before Christ under a hostile time zone', + value: new Date('-000043-03-15T12:00:00.000Z'), + setupSql: ["SET TimeZone = 'Asia/Kolkata'"], + valueEquality: datesEqual, + projectionEquality: datesEqual, + }, + { + codecId: 'pg/timestamp-date@1', + label: 'null', + value: undefined, + typeParams: { precision: 3 }, + nullValue: true, + }, + { + codecId: 'pg/timestamp-instant@1', + label: 'microsecond precision', + value: Temporal.Instant.from('2026-01-02T03:04:05.123456Z'), + typeParams: { precision: 6 }, + valueEquality: instantsEqual, + projectionEquality: instantsEqual, + }, + { + codecId: 'pg/timestamp-instant@1', + label: 'a year past 9999 under a hostile time zone', + value: Temporal.Instant.from('+012026-01-02T03:04:05Z'), + setupSql: ["SET TimeZone = 'Asia/Kolkata'"], + valueEquality: instantsEqual, + projectionEquality: instantsEqual, + }, + { + codecId: 'pg/timestamp-instant@1', + label: 'null', + value: undefined, + nullValue: true, + }, // The `*-string` codecs' application value is PostgreSQL's own rendering, so each case is written // the way the server writes it — space separator, two-digit offset, microseconds. That is now // what the projection returns too, which is the whole point of the text cast: one spelling, @@ -396,6 +448,24 @@ export const postgresConformanceCases: readonly PostgresCodecConformanceCase[] = value: interval({ months: 1, days: -1 }), setupSql: HOSTILE_TEMPORAL_SESSION, }, + // An interval read as a string is the ISO 8601 duration pg/interval stores, whatever the session's IntervalStyle. + { codecId: 'pg/interval-string@1', label: 'one month', value: 'P1M' }, + { codecId: 'pg/interval-string@1', label: 'every component', value: 'P1Y2M3DT4H5M6.5S' }, + { codecId: 'pg/interval-string@1', label: 'mixed signs', value: 'P1M-1DT-1H' }, + { codecId: 'pg/interval-string@1', label: 'zero', value: 'PT0S' }, + ...(['postgres_verbose', 'sql_standard', 'iso_8601'] as const).map((style) => ({ + codecId: 'pg/interval-string@1', + label: `mixed signs under IntervalStyle ${style}`, + value: 'P1Y2M-3DT4H', + setupSql: [`SET IntervalStyle = '${style}'`], + })), + { + codecId: 'pg/interval-string@1', + label: 'every component under a hostile session', + value: 'P1Y2M3DT4H5M6.5S', + setupSql: HOSTILE_TEMPORAL_SESSION, + }, + { codecId: 'pg/interval-string@1', label: 'null', value: undefined, nullValue: true }, { codecId: 'pg/json@1', label: 'document', value: { a: 1, b: ['x'] } }, { codecId: 'pg/jsonb@1', label: 'document', value: { a: 1, b: ['x'] } }, // A document that is a string is where a JSON value and the JSON text of a document differ: the diff --git a/packages/3-targets/6-adapters/postgres-codec-testkit/test/timestamp-utc-runtime-read.integration.test.ts b/packages/3-targets/6-adapters/postgres-codec-testkit/test/timestamp-utc-runtime-read.integration.test.ts new file mode 100644 index 000000000000..4a48da993c55 --- /dev/null +++ b/packages/3-targets/6-adapters/postgres-codec-testkit/test/timestamp-utc-runtime-read.integration.test.ts @@ -0,0 +1,78 @@ +import { postgresCodecDescriptorRegistry } from '@internal/target-postgres/codecs'; +import { createDevDatabase, timeouts } from '@repo/test-utils'; +import { afterAll, beforeAll, describe, expect, it } from 'vitest'; +import { connectRuntimeDriver } from './runtime-connection'; + +const codecFor = (codecId: string) => + postgresCodecDescriptorRegistry.descriptorFor(codecId)!.factory(undefined)({ + name: 'timestamp-utc-runtime-read', + }); + +describe('the timestamp codecs that read UTC, on values the runtime driver returns', () => { + let database: Awaited> | undefined; + let runtime: Awaited> | undefined; + + beforeAll(async () => { + database = await createDevDatabase(); + runtime = await connectRuntimeDriver(database.connectionString); + await runtime.connection.query('create table stamps (label text primary key, v timestamp)'); + await runtime.connection.query(`insert into stamps values + ('ordinary', timestamp '2026-01-02 03:04:05.123456'), + ('before Christ', timestamp '0044-03-15 12:00:00 BC'), + ('infinity', 'infinity'), + ('minus infinity', '-infinity'), + ('past the last instant', timestamp '275760-09-13 12:00:00')`); + }, timeouts.spinUpPpgDev); + + afterAll(async () => { + await runtime?.close(); + await database?.close(); + database = undefined; + }, timeouts.spinUpPpgDev); + + async function wireOf(label: string): Promise { + await runtime!.connection.query("SET TimeZone = 'Asia/Kolkata'"); + const rows = await runtime!.connection.query('select v from stamps where label = $1', [label]); + return rows[0]?.['v']; + } + + it( + 'read the stored date and time as UTC under a session in another time zone', + async () => { + const ordinary = await wireOf('ordinary'); + const beforeChrist = await wireOf('before Christ'); + const date = codecFor('pg/timestamp-date@1'); + const instant = codecFor('pg/timestamp-instant@1'); + + expect({ + date: await date.fromWire(ordinary, {}), + dateBeforeChrist: await date.fromWire(beforeChrist, {}), + instant: String(await instant.fromWire(ordinary, {})), + instantBeforeChrist: String(await instant.fromWire(beforeChrist, {})), + }).toEqual({ + date: new Date('2026-01-02T03:04:05.123Z'), + dateBeforeChrist: new Date('-000043-03-15T12:00:00.000Z'), + instant: '2026-01-02T03:04:05.123456Z', + instantBeforeChrist: '-000043-03-15T12:00:00Z', + }); + }, + timeouts.spinUpPpgDev, + ); + + it.each([ + ['infinity', 'pg/timestamp-date@1'], + ['minus infinity', 'pg/timestamp-date@1'], + ['past the last instant', 'pg/timestamp-date@1'], + ['infinity', 'pg/timestamp-instant@1'], + ['minus infinity', 'pg/timestamp-instant@1'], + ['past the last instant', 'pg/timestamp-instant@1'], + ])( + 'refuse the stored %s through %s, naming TimestampString', + async (label, codecId) => { + await expect(codecFor(codecId).fromWire(await wireOf(label), {})).rejects.toThrow( + /TimestampString/, + ); + }, + timeouts.spinUpPpgDev, + ); +}); From aa2221bdaed170eed0f8ae7b68b5877c1e520c00 Mon Sep 17 00:00:00 2001 From: willbot Date: Thu, 22 Oct 2026 18:04:25 +0200 Subject: [PATCH 013/118] Column helpers for the timestamp Date, timestamp Instant and interval string types timestampJsDateColumn, timestampInstantColumn and intervalStringColumn(precision?) give a TypeScript contract the new explicit PostgreSQL types. Co-Authored-By: Claude Opus 5.5 Signed-off-by: willbot Signed-off-by: Will Madden --- .../3-targets/6-adapters/postgres/README.md | 2 +- .../postgres/src/exports/column-types.ts | 33 +++++++++++++++++++ .../test/date-time-column-types.test.ts | 30 +++++++++++++++++ 3 files changed, 64 insertions(+), 1 deletion(-) create mode 100644 packages/3-targets/6-adapters/postgres/test/date-time-column-types.test.ts diff --git a/packages/3-targets/6-adapters/postgres/README.md b/packages/3-targets/6-adapters/postgres/README.md index 7fd42779c000..ca3748e32747 100644 --- a/packages/3-targets/6-adapters/postgres/README.md +++ b/packages/3-targets/6-adapters/postgres/README.md @@ -140,7 +140,7 @@ flowchart TD **Column Types Export (`column-types.ts`)** - Exports column descriptors for built-in types; enums are declared with `enumType` from the contract builder -- Parameterized helpers: ``rawColumn(sql`tsvector`)`` for a column of a type no other column type covers, its type written as a `sql` literal (ADR 273), `charColumn(length)`, `varcharColumn(length)`, `numericColumn(precision, scale?)`, `bitColumn(length)`, `varbitColumn(length)`, `timeColumn(precision?)`, `timetzColumn(precision?)`, `intervalPartsColumn(precision?)` +- Parameterized helpers: ``rawColumn(sql`tsvector`)`` for a column of a type no other column type covers, its type written as a `sql` literal (ADR 273), `charColumn(length)`, `varcharColumn(length)`, `numericColumn(precision, scale?)`, `bitColumn(length)`, `varbitColumn(length)`, `timeColumn(precision?)`, `timetzColumn(precision?)`, `intervalPartsColumn(precision?)`, `intervalStringColumn(precision?)` - Exports raw JSON helpers: - `jsonColumn`, `jsonbColumn` — untyped raw JSON / JSONB column descriptors diff --git a/packages/3-targets/6-adapters/postgres/src/exports/column-types.ts b/packages/3-targets/6-adapters/postgres/src/exports/column-types.ts index bbdc51d78095..16ecfdafa58e 100644 --- a/packages/3-targets/6-adapters/postgres/src/exports/column-types.ts +++ b/packages/3-targets/6-adapters/postgres/src/exports/column-types.ts @@ -18,12 +18,15 @@ import { PG_INT4_CODEC_ID, PG_INT8_CODEC_ID, PG_INTERVAL_CODEC_ID, + PG_INTERVAL_STRING_CODEC_ID, PG_JSON_CODEC_ID, PG_JSONB_CODEC_ID, PG_NUMERIC_CODEC_ID, PG_TEXT_CODEC_ID, PG_TIME_STRING_CODEC_ID, PG_TIME_TEMPORAL_CODEC_ID, + PG_TIMESTAMP_DATE_CODEC_ID, + PG_TIMESTAMP_INSTANT_CODEC_ID, PG_TIMESTAMP_STRING_CODEC_ID, PG_TIMESTAMP_TEMPORAL_CODEC_ID, PG_TIMESTAMPTZ_DATE_CODEC_ID, @@ -147,6 +150,14 @@ export const timestampStringColumn = { codecId: PG_TIMESTAMP_STRING_CODEC_ID, } as const satisfies ColumnTypeDescriptor; +export const timestampJsDateColumn = { + codecId: PG_TIMESTAMP_DATE_CODEC_ID, +} as const satisfies ColumnTypeDescriptor; + +export const timestampInstantColumn = { + codecId: PG_TIMESTAMP_INSTANT_CODEC_ID, +} as const satisfies ColumnTypeDescriptor; + export const timestamptzTemporalColumn = { codecId: PG_TIMESTAMPTZ_TEMPORAL_CODEC_ID, } as const satisfies ColumnTypeDescriptor; @@ -288,6 +299,28 @@ export function intervalPartsColumn(precision?: number): ColumnTypeDescriptor< } as const; } +export function intervalStringColumn(): ColumnTypeDescriptor; +export function intervalStringColumn( + precision: Precision, +): ColumnTypeDescriptor & { + readonly typeParams: { readonly precision: Precision }; +}; +export function intervalStringColumn(precision?: number): ColumnTypeDescriptor< + typeof PG_INTERVAL_STRING_CODEC_ID +> & { + readonly typeParams?: { readonly precision: number }; +}; +export function intervalStringColumn(precision?: number): ColumnTypeDescriptor< + typeof PG_INTERVAL_STRING_CODEC_ID +> & { + readonly typeParams?: { readonly precision: number }; +} { + return { + codecId: PG_INTERVAL_STRING_CODEC_ID, + ...(precision === undefined ? {} : { typeParams: { precision } }), + } as const; +} + /** * Postgres `json` column descriptor — untyped raw JSON. * diff --git a/packages/3-targets/6-adapters/postgres/test/date-time-column-types.test.ts b/packages/3-targets/6-adapters/postgres/test/date-time-column-types.test.ts new file mode 100644 index 000000000000..be0cbca2f9ac --- /dev/null +++ b/packages/3-targets/6-adapters/postgres/test/date-time-column-types.test.ts @@ -0,0 +1,30 @@ +import { describe, expect, it } from 'vitest'; +import { + intervalPartsColumn, + intervalStringColumn, + timestampInstantColumn, + timestampJsDateColumn, +} from '../src/exports/column-types'; + +describe('the explicit timestamp and interval column helpers', () => { + it('name the codec of each explicit type', () => { + expect({ + timestampJsDate: timestampJsDateColumn, + timestampInstant: timestampInstantColumn, + intervalParts: intervalPartsColumn(), + intervalString: intervalStringColumn(), + }).toEqual({ + timestampJsDate: { codecId: 'pg/timestamp-date@1' }, + timestampInstant: { codecId: 'pg/timestamp-instant@1' }, + intervalParts: { codecId: 'pg/interval@1' }, + intervalString: { codecId: 'pg/interval-string@1' }, + }); + }); + + it('carry an interval precision when one is given', () => { + expect(intervalStringColumn(3)).toEqual({ + codecId: 'pg/interval-string@1', + typeParams: { precision: 3 }, + }); + }); +}); From 27a8242fcb04e7ff65aeab5c50ec5ed762608959 Mon Sep 17 00:00:00 2001 From: willbot Date: Thu, 22 Oct 2026 18:20:55 +0200 Subject: [PATCH 014/118] Pin the new explicit types in the scalar, output-type and print checks contract print writes TimestampJsDate, TimestampInstant, IntervalParts and IntervalString as written, and DateTemporal and TimeTemporal as Date and Time, which mean the same for now. Co-Authored-By: Claude Opus 5.5 Signed-off-by: willbot Signed-off-by: Will Madden --- .../postgres/test/scalar-type-parity.test.ts | 6 ++++++ .../contract-parity/rendered-output-types.test.ts | 4 ++++ .../date-time-types-print.integration.test.ts | 12 ++++++++++++ 3 files changed, 22 insertions(+) diff --git a/packages/3-extensions/postgres/test/scalar-type-parity.test.ts b/packages/3-extensions/postgres/test/scalar-type-parity.test.ts index 8a0b302b5501..c61dc8382f4c 100644 --- a/packages/3-extensions/postgres/test/scalar-type-parity.test.ts +++ b/packages/3-extensions/postgres/test/scalar-type-parity.test.ts @@ -93,6 +93,9 @@ describe('postgres scalar types derived from the unified namespace', () => { Bit: { codecId: 'pg/bit@1' }, VarBit: { codecId: 'pg/varbit@1' }, IntervalParts: { codecId: 'pg/interval@1' }, + IntervalString: { codecId: 'pg/interval-string@1' }, + TimestampJsDate: { codecId: 'pg/timestamp-date@1' }, + TimestampInstant: { codecId: 'pg/timestamp-instant@1' }, }); }); @@ -113,6 +116,7 @@ describe('postgres scalar types derived from the unified namespace', () => { 'Inet', 'Int', 'IntervalParts', + 'IntervalString', 'Json', 'Jsonb', 'Numeric', @@ -122,6 +126,8 @@ describe('postgres scalar types derived from the unified namespace', () => { 'Time', 'TimeString', 'TimeTemporal', + 'TimestampInstant', + 'TimestampJsDate', 'TimestampString', 'TimestampTemporal', 'TimestamptzJsDate', diff --git a/test/integration/test/contract-parity/rendered-output-types.test.ts b/test/integration/test/contract-parity/rendered-output-types.test.ts index 429aa25f5f7a..bfbd214dcba6 100644 --- a/test/integration/test/contract-parity/rendered-output-types.test.ts +++ b/test/integration/test/contract-parity/rendered-output-types.test.ts @@ -58,7 +58,11 @@ const stacks: Readonly is the same type for every SRID.', 'pg/interval@1': 'An interval reads as its months, days and microseconds whatever its precision.', + 'pg/interval-string@1': + 'An interval read as a string is an ISO 8601 duration whatever its precision.', 'pg/time-temporal@1': temporalPrecision, + 'pg/timestamp-date@1': 'A timestamp read as a Date is a Date whatever its precision.', + 'pg/timestamp-instant@1': temporalPrecision, 'pg/timestamp-temporal@1': temporalPrecision, 'pg/timestamptz-temporal@1': temporalPrecision, 'pg/unboundedint@1': 'A numeric(P, 0) column reads as a bigint whatever its precision.', diff --git a/test/integration/test/psl-print/date-time-types-print.integration.test.ts b/test/integration/test/psl-print/date-time-types-print.integration.test.ts index fc892de17b6b..27ede56fc0b0 100644 --- a/test/integration/test/psl-print/date-time-types-print.integration.test.ts +++ b/test/integration/test/psl-print/date-time-types-print.integration.test.ts @@ -10,6 +10,12 @@ model Event { zoned3 TimestamptzTemporal(3) local TimestampTemporal local3 TimestampTemporal(3) + utcDate TimestampJsDate(3) + utcAt TimestampInstant(6) + dayT DateTemporal + clockT TimeTemporal(0) + period IntervalParts + span IntervalString(3) day Date clock Time clock3 Time(3) @@ -43,6 +49,12 @@ describe('contract print spells each date and time column', () => { zoned3: 'TimestamptzTemporal(3)', local: 'TimestampTemporal', local3: 'TimestampTemporal(3)', + utcDate: 'TimestampJsDate(3)', + utcAt: 'TimestampInstant(6)', + dayT: 'Date', + clockT: 'Time(0)', + period: 'IntervalParts', + span: 'IntervalString(3)', day: 'Date', clock: 'Time', clock3: 'Time(3)', From d939a747424cddf2c45d86cd0c5d933a5b4f3041 Mon Sep 17 00:00:00 2001 From: willbot Date: Thu, 22 Oct 2026 18:20:57 +0200 Subject: [PATCH 015/118] Regenerate contract.d.ts fixtures for the new codecs' aggregate rows Every emitted PostgreSQL contract.d.ts lists the aggregates of each codec in the stack, so the three new codecs add min and max rows, and sum and avg rows for the interval string codec, to each one. contract.json does not change. Co-Authored-By: Claude Opus 5.5 Signed-off-by: willbot Signed-off-by: Will Madden --- .../src/prisma/contract.d.ts | 32 +++++++++++++++++++ .../src/postgres/generated/contract.d.ts | 32 +++++++++++++++++++ .../app/src/contract.d.ts | 32 +++++++++++++++++++ .../contract.d.ts | 32 +++++++++++++++++++ .../packages/audit/src/contract.d.ts | 32 +++++++++++++++++++ .../contract.d.ts | 32 +++++++++++++++++++ .../packages/feature-flags/src/contract.d.ts | 32 +++++++++++++++++++ .../paradedb-demo/src/prisma/contract.d.ts | 32 +++++++++++++++++++ .../src/prisma/contract.d.ts | 32 +++++++++++++++++++ .../contract.d.ts | 32 +++++++++++++++++++ .../contract.d.ts | 32 +++++++++++++++++++ .../contract.d.ts | 32 +++++++++++++++++++ .../contract.d.ts | 32 +++++++++++++++++++ .../contract.d.ts | 32 +++++++++++++++++++ .../prisma-8-demo/src/prisma/contract.d.ts | 32 +++++++++++++++++++ .../contract.d.ts | 32 +++++++++++++++++++ .../src/prisma/contract.d.ts | 32 +++++++++++++++++++ .../generated/prisma8/contract.d.ts | 32 +++++++++++++++++++ .../src/prisma/contract.d.ts | 32 +++++++++++++++++++ examples/supabase/src/contract.d.ts | 32 +++++++++++++++++++ .../test/fixtures/generated/contract.d.ts | 32 +++++++++++++++++++ .../contract.d.ts | 32 +++++++++++++++++++ .../3-extensions/paradedb/src/contract.d.ts | 32 +++++++++++++++++++ .../contract.d.ts | 32 +++++++++++++++++++ .../3-extensions/pgvector/src/contract.d.ts | 32 +++++++++++++++++++ .../contract.d.ts | 32 +++++++++++++++++++ .../3-extensions/postgis/src/contract.d.ts | 32 +++++++++++++++++++ .../test/fixtures/generated/contract.d.ts | 32 +++++++++++++++++++ .../generated/contract.d.ts | 32 +++++++++++++++++++ .../generated/contract.d.ts | 32 +++++++++++++++++++ .../test/fixtures/generated/contract.d.ts | 32 +++++++++++++++++++ .../generated/contract.d.ts | 32 +++++++++++++++++++ .../polymorphism/generated/contract.d.ts | 32 +++++++++++++++++++ .../scope-namespace/generated/contract.d.ts | 32 +++++++++++++++++++ .../soft-delete/generated/contract.d.ts | 32 +++++++++++++++++++ .../supabase/src/contract/contract.d.ts | 32 +++++++++++++++++++ .../test/fixtures/example-app/contract.d.ts | 32 +++++++++++++++++++ .../test/fixtures/no-policy/contract.d.ts | 32 +++++++++++++++++++ .../fixtures/renamed-policy/contract.d.ts | 32 +++++++++++++++++++ .../test/fixtures/generated/contract.d.ts | 32 +++++++++++++++++++ .../fixtures/postgres/generated/contract.d.ts | 32 +++++++++++++++++++ .../generated/contract.d.ts | 32 +++++++++++++++++++ .../_fixture-postgres/generated/contract.d.ts | 32 +++++++++++++++++++ .../_fixture/generated/contract.d.ts | 32 +++++++++++++++++++ test/integration/test/fixtures/contract.d.ts | 32 +++++++++++++++++++ .../fixtures/generated/contract.d.ts | 32 +++++++++++++++++++ .../fixtures/postgres/generated/contract.d.ts | 32 +++++++++++++++++++ .../_fixture/decimal/generated/contract.d.ts | 32 +++++++++++++++++++ .../_fixture/numeric/generated/contract.d.ts | 32 +++++++++++++++++++ .../count/_fixture/generated/contract.d.ts | 32 +++++++++++++++++++ .../_fixture/main/generated/contract.d.ts | 32 +++++++++++++++++++ .../regression-21789/generated/contract.d.ts | 32 +++++++++++++++++++ .../_fixture/common/generated/contract.d.ts | 32 +++++++++++++++++++ .../_fixture/decimal/generated/contract.d.ts | 32 +++++++++++++++++++ .../_fixture/compound/generated/contract.d.ts | 32 +++++++++++++++++++ .../_fixture/generated/contract.d.ts | 32 +++++++++++++++++++ .../_fixture/nested/generated/contract.d.ts | 32 +++++++++++++++++++ .../_fixture/self/generated/contract.d.ts | 32 +++++++++++++++++++ .../_fixture/common/generated/contract.d.ts | 32 +++++++++++++++++++ .../_fixture/decimal/generated/contract.d.ts | 32 +++++++++++++++++++ .../_fixture/common/generated/contract.d.ts | 32 +++++++++++++++++++ .../_fixture/decimal/generated/contract.d.ts | 32 +++++++++++++++++++ .../_fixture/decimal/generated/contract.d.ts | 32 +++++++++++++++++++ .../_fixture/numeric/generated/contract.d.ts | 32 +++++++++++++++++++ .../_fixture/base/generated/contract.d.ts | 32 +++++++++++++++++++ .../_fixture/nested/generated/contract.d.ts | 32 +++++++++++++++++++ .../bigint/_fixture/generated/contract.d.ts | 32 +++++++++++++++++++ .../bool/_fixture/generated/contract.d.ts | 32 +++++++++++++++++++ .../relations/generated/contract.d.ts | 32 +++++++++++++++++++ .../_fixture/scalars/generated/contract.d.ts | 32 +++++++++++++++++++ .../datetime/_fixture/generated/contract.d.ts | 32 +++++++++++++++++++ .../decimal/_fixture/generated/contract.d.ts | 32 +++++++++++++++++++ .../_fixture/postgres/generated/contract.d.ts | 32 +++++++++++++++++++ .../float/_fixture/generated/contract.d.ts | 32 +++++++++++++++++++ .../int/_fixture/generated/contract.d.ts | 32 +++++++++++++++++++ .../_fixture/scalar/generated/contract.d.ts | 32 +++++++++++++++++++ .../_fixture/other/generated/contract.d.ts | 32 +++++++++++++++++++ .../_fixture/string/generated/contract.d.ts | 32 +++++++++++++++++++ .../string/_fixture/generated/contract.d.ts | 32 +++++++++++++++++++ .../_fixture/common/generated/contract.d.ts | 32 +++++++++++++++++++ .../_fixture/decimal/generated/contract.d.ts | 32 +++++++++++++++++++ .../_fixture/enum/generated/contract.d.ts | 32 +++++++++++++++++++ .../_fixture/json/generated/contract.d.ts | 32 +++++++++++++++++++ .../_fixture/lists/generated/contract.d.ts | 32 +++++++++++++++++++ .../distinct/_fixture/generated/contract.d.ts | 32 +++++++++++++++++++ .../_fixture/generated/contract.d.ts | 32 +++++++++++++++++++ .../_fixture/generated/contract.d.ts | 32 +++++++++++++++++++ .../_fixture/generated/contract.d.ts | 32 +++++++++++++++++++ .../decimal-list/generated/contract.d.ts | 32 +++++++++++++++++++ .../_fixture/enum/generated/contract.d.ts | 32 +++++++++++++++++++ .../_fixture/list/generated/contract.d.ts | 32 +++++++++++++++++++ .../_fixture/mixed/generated/contract.d.ts | 32 +++++++++++++++++++ .../_fixture/generated/contract.d.ts | 32 +++++++++++++++++++ .../_fixture/generated/contract.d.ts | 32 +++++++++++++++++++ .../_fixture/generated/contract.d.ts | 32 +++++++++++++++++++ .../_fixture/generated/contract.d.ts | 32 +++++++++++++++++++ .../_fixture/common/generated/contract.d.ts | 32 +++++++++++++++++++ .../_fixture/default/generated/contract.d.ts | 32 +++++++++++++++++++ .../_fixture/list/generated/contract.d.ts | 32 +++++++++++++++++++ .../_fixture/generated/contract.d.ts | 32 +++++++++++++++++++ .../_fixture/generated/contract.d.ts | 32 +++++++++++++++++++ .../_fixture/generated/contract.d.ts | 32 +++++++++++++++++++ .../_fixture/generated/contract.d.ts | 32 +++++++++++++++++++ .../_fixture/list/generated/contract.d.ts | 32 +++++++++++++++++++ .../_fixture/complex/generated/contract.d.ts | 32 +++++++++++++++++++ .../one-to-many/generated/contract.d.ts | 32 +++++++++++++++++++ .../one-to-one-list/generated/contract.d.ts | 32 +++++++++++++++++++ .../one-to-one/generated/contract.d.ts | 32 +++++++++++++++++++ .../_fixture/generated/contract.d.ts | 32 +++++++++++++++++++ .../generated/contract.d.ts | 32 +++++++++++++++++++ .../many-to-many/generated/contract.d.ts | 32 +++++++++++++++++++ .../one-to-many/generated/contract.d.ts | 32 +++++++++++++++++++ .../filters/_fixture/generated/contract.d.ts | 32 +++++++++++++++++++ .../_fixture/optional/generated/contract.d.ts | 32 +++++++++++++++++++ .../_fixture/base/generated/contract.d.ts | 32 +++++++++++++++++++ .../_fixture/decimal/generated/contract.d.ts | 32 +++++++++++++++++++ .../_fixture/json/generated/contract.d.ts | 32 +++++++++++++++++++ .../_fixture/25103/generated/contract.d.ts | 32 +++++++++++++++++++ .../l2-to-one/generated/contract.d.ts | 32 +++++++++++++++++++ .../_fixture/generated/contract.d.ts | 32 +++++++++++++++++++ .../_fixture/21356/generated/contract.d.ts | 32 +++++++++++++++++++ .../_fixture/21366/generated/contract.d.ts | 32 +++++++++++++++++++ .../_fixture/generated/contract.d.ts | 32 +++++++++++++++++++ .../_fixture/generated/contract.d.ts | 32 +++++++++++++++++++ .../_fixture/generated/contract.d.ts | 32 +++++++++++++++++++ .../_fixture/generated/contract.d.ts | 32 +++++++++++++++++++ .../_fixture/generated/contract.d.ts | 32 +++++++++++++++++++ .../_fixture/generated/contract.d.ts | 32 +++++++++++++++++++ .../_fixture/generated/contract.d.ts | 32 +++++++++++++++++++ .../_fixture/generated/contract.d.ts | 32 +++++++++++++++++++ .../_fixture/generated/contract.d.ts | 32 +++++++++++++++++++ .../_fixture/generated/contract.d.ts | 32 +++++++++++++++++++ .../_fixture/generated/contract.d.ts | 32 +++++++++++++++++++ .../distinct/_fixture/generated/contract.d.ts | 32 +++++++++++++++++++ .../_fixture/generated/contract.d.ts | 32 +++++++++++++++++++ .../_fixture/generated/contract.d.ts | 32 +++++++++++++++++++ .../_fixture/generated/contract.d.ts | 32 +++++++++++++++++++ .../_fixture/generated/contract.d.ts | 32 +++++++++++++++++++ .../_fixture/generated/contract.d.ts | 32 +++++++++++++++++++ .../_fixture/generated/contract.d.ts | 32 +++++++++++++++++++ .../_fixture/generated/contract.d.ts | 32 +++++++++++++++++++ .../_fixture/generated/contract.d.ts | 32 +++++++++++++++++++ .../_fixture/generated/contract.d.ts | 32 +++++++++++++++++++ .../_fixture/generated/contract.d.ts | 32 +++++++++++++++++++ .../_fixture/generated/contract.d.ts | 32 +++++++++++++++++++ .../_fixture/generated/contract.d.ts | 32 +++++++++++++++++++ .../_fixture/generated/contract.d.ts | 32 +++++++++++++++++++ .../_fixture/generated/contract.d.ts | 32 +++++++++++++++++++ .../_fixture/generated/contract.d.ts | 32 +++++++++++++++++++ .../_fixture/generated/contract.d.ts | 32 +++++++++++++++++++ .../_fixture/generated/contract.d.ts | 32 +++++++++++++++++++ .../_fixture/generated/contract.d.ts | 32 +++++++++++++++++++ .../_fixture/generated/contract.d.ts | 32 +++++++++++++++++++ .../_fixture/generated/contract.d.ts | 32 +++++++++++++++++++ .../_fixture/generated/contract.d.ts | 32 +++++++++++++++++++ .../_fixture/generated/contract.d.ts | 32 +++++++++++++++++++ .../_fixture/generated/contract.d.ts | 32 +++++++++++++++++++ .../_fixture/generated/contract.d.ts | 32 +++++++++++++++++++ .../_fixture/generated/contract.d.ts | 32 +++++++++++++++++++ .../_fixture/generated/contract.d.ts | 32 +++++++++++++++++++ .../_fixture/generated/contract.d.ts | 32 +++++++++++++++++++ .../_fixture/generated/contract.d.ts | 32 +++++++++++++++++++ .../_fixture/generated/contract.d.ts | 32 +++++++++++++++++++ .../_fixture/generated/contract.d.ts | 32 +++++++++++++++++++ .../_fixture/generated/contract.d.ts | 32 +++++++++++++++++++ .../_fixture/generated/contract.d.ts | 32 +++++++++++++++++++ .../_fixture/generated/contract.d.ts | 32 +++++++++++++++++++ .../_fixture/generated/contract.d.ts | 32 +++++++++++++++++++ .../_fixture/generated/contract.d.ts | 32 +++++++++++++++++++ .../_fixture/generated/contract.d.ts | 32 +++++++++++++++++++ .../_fixture/generated/contract.d.ts | 32 +++++++++++++++++++ .../_fixture/generated/contract.d.ts | 32 +++++++++++++++++++ .../_fixture/generated/contract.d.ts | 32 +++++++++++++++++++ .../_fixture/generated/contract.d.ts | 32 +++++++++++++++++++ .../_fixture/generated/contract.d.ts | 32 +++++++++++++++++++ .../_fixture/generated/contract.d.ts | 32 +++++++++++++++++++ .../_fixture/generated/contract.d.ts | 32 +++++++++++++++++++ .../_fixture/generated/contract.d.ts | 32 +++++++++++++++++++ .../_fixture/generated/contract.d.ts | 32 +++++++++++++++++++ .../_fixture/generated/contract.d.ts | 32 +++++++++++++++++++ .../_fixture/generated/contract.d.ts | 32 +++++++++++++++++++ .../_fixture/generated/contract.d.ts | 32 +++++++++++++++++++ .../_fixture/generated/contract.d.ts | 32 +++++++++++++++++++ .../_fixture/generated/contract.d.ts | 32 +++++++++++++++++++ .../_fixture/generated/contract.d.ts | 32 +++++++++++++++++++ .../_fixture/generated/contract.d.ts | 32 +++++++++++++++++++ .../_fixture/generated/contract.d.ts | 32 +++++++++++++++++++ .../_fixture/generated/contract.d.ts | 32 +++++++++++++++++++ .../_fixture/generated/contract.d.ts | 32 +++++++++++++++++++ .../_fixture/generated/contract.d.ts | 32 +++++++++++++++++++ .../_fixture/generated/contract.d.ts | 32 +++++++++++++++++++ .../_fixture/generated/contract.d.ts | 32 +++++++++++++++++++ .../_fixture/generated/contract.d.ts | 32 +++++++++++++++++++ .../_fixture/generated/contract.d.ts | 32 +++++++++++++++++++ .../different-names/generated/contract.d.ts | 32 +++++++++++++++++++ .../identical-names/generated/contract.d.ts | 32 +++++++++++++++++++ .../_fixture/no-map/generated/contract.d.ts | 32 +++++++++++++++++++ .../_fixture/generated/contract.d.ts | 32 +++++++++++++++++++ .../_fixture/generated/contract.d.ts | 32 +++++++++++++++++++ .../_fixture/generated/contract.d.ts | 32 +++++++++++++++++++ .../_fixture/generated/contract.d.ts | 32 +++++++++++++++++++ .../_fixture/generated/contract.d.ts | 32 +++++++++++++++++++ .../_fixture/generated/contract.d.ts | 32 +++++++++++++++++++ .../cascade-map/generated/contract.d.ts | 32 +++++++++++++++++++ .../cascade-plain/generated/contract.d.ts | 32 +++++++++++++++++++ .../noaction-map/generated/contract.d.ts | 32 +++++++++++++++++++ .../noaction-plain/generated/contract.d.ts | 32 +++++++++++++++++++ .../restrict-map/generated/contract.d.ts | 32 +++++++++++++++++++ .../restrict-plain/generated/contract.d.ts | 32 +++++++++++++++++++ .../setnull-map/generated/contract.d.ts | 32 +++++++++++++++++++ .../setnull-plain/generated/contract.d.ts | 32 +++++++++++++++++++ .../cascade-map/generated/contract.d.ts | 32 +++++++++++++++++++ .../cascade-plain/generated/contract.d.ts | 32 +++++++++++++++++++ .../noaction-map/generated/contract.d.ts | 32 +++++++++++++++++++ .../noaction-plain/generated/contract.d.ts | 32 +++++++++++++++++++ .../restrict-map/generated/contract.d.ts | 32 +++++++++++++++++++ .../restrict-plain/generated/contract.d.ts | 32 +++++++++++++++++++ .../setnull-map/generated/contract.d.ts | 32 +++++++++++++++++++ .../setnull-plain/generated/contract.d.ts | 32 +++++++++++++++++++ .../cascade-nomap/generated/contract.d.ts | 32 +++++++++++++++++++ .../default-nomap/generated/contract.d.ts | 32 +++++++++++++++++++ .../noaction-nomap/generated/contract.d.ts | 32 +++++++++++++++++++ .../restrict-nomap/generated/contract.d.ts | 32 +++++++++++++++++++ .../generated-no-pgvector/contract.d.ts | 32 +++++++++++++++++++ .../fixtures/generated/contract.d.ts | 32 +++++++++++++++++++ .../generated/contract.d.ts | 32 +++++++++++++++++++ .../generated/contract.d.ts | 32 +++++++++++++++++++ .../generated/contract.d.ts | 32 +++++++++++++++++++ .../fixtures/generated/contract.d.ts | 32 +++++++++++++++++++ .../generated/contract.d.ts | 32 +++++++++++++++++++ .../generated/contract.d.ts | 32 +++++++++++++++++++ .../fixtures/mn-psl/generated/contract.d.ts | 32 +++++++++++++++++++ .../generated/contract.d.ts | 32 +++++++++++++++++++ .../generated/contract.d.ts | 32 +++++++++++++++++++ .../generated/contract.d.ts | 32 +++++++++++++++++++ .../polymorphism/generated/contract.d.ts | 32 +++++++++++++++++++ .../scalar-lists/generated/contract.d.ts | 32 +++++++++++++++++++ .../scope-namespace/generated/contract.d.ts | 32 +++++++++++++++++++ .../self-relations/generated/contract.d.ts | 32 +++++++++++++++++++ .../soft-delete/generated/contract.d.ts | 32 +++++++++++++++++++ .../_fixture-aliases/generated/contract.d.ts | 32 +++++++++++++++++++ .../generated/contract.d.ts | 32 +++++++++++++++++++ .../_fixture-now/generated/contract.d.ts | 32 +++++++++++++++++++ .../generated/contract.d.ts | 32 +++++++++++++++++++ .../_fixture/generated/contract.d.ts | 32 +++++++++++++++++++ 245 files changed, 7840 insertions(+) diff --git a/apps/telemetry-backend/src/prisma/contract.d.ts b/apps/telemetry-backend/src/prisma/contract.d.ts index 6b9db76303bb..4c48fb3b1095 100644 --- a/apps/telemetry-backend/src/prisma/contract.d.ts +++ b/apps/telemetry-backend/src/prisma/contract.d.ts @@ -42,6 +42,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; @@ -91,6 +95,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; @@ -100,6 +108,14 @@ export type AggregateTypes = { readonly output: 'pg/time-temporal@1'; readonly nullable: true; }; + readonly 'pg/timestamp-date@1': { + readonly output: 'pg/timestamp-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-instant@1': { + readonly output: 'pg/timestamp-instant@1'; + readonly nullable: true; + }; readonly 'pg/timestamp-string@1': { readonly output: 'pg/timestamp-string@1'; readonly nullable: true; @@ -151,6 +167,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; @@ -160,6 +180,14 @@ export type AggregateTypes = { readonly output: 'pg/time-temporal@1'; readonly nullable: true; }; + readonly 'pg/timestamp-date@1': { + readonly output: 'pg/timestamp-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-instant@1': { + readonly output: 'pg/timestamp-instant@1'; + readonly nullable: true; + }; readonly 'pg/timestamp-string@1': { readonly output: 'pg/timestamp-string@1'; readonly nullable: true; @@ -203,6 +231,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; diff --git a/examples/bundle-size/src/postgres/generated/contract.d.ts b/examples/bundle-size/src/postgres/generated/contract.d.ts index 8fa3efb80080..2e97bc21082b 100644 --- a/examples/bundle-size/src/postgres/generated/contract.d.ts +++ b/examples/bundle-size/src/postgres/generated/contract.d.ts @@ -43,6 +43,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; @@ -92,6 +96,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; @@ -101,6 +109,14 @@ export type AggregateTypes = { readonly output: 'pg/time-temporal@1'; readonly nullable: true; }; + readonly 'pg/timestamp-date@1': { + readonly output: 'pg/timestamp-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-instant@1': { + readonly output: 'pg/timestamp-instant@1'; + readonly nullable: true; + }; readonly 'pg/timestamp-string@1': { readonly output: 'pg/timestamp-string@1'; readonly nullable: true; @@ -152,6 +168,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; @@ -161,6 +181,14 @@ export type AggregateTypes = { readonly output: 'pg/time-temporal@1'; readonly nullable: true; }; + readonly 'pg/timestamp-date@1': { + readonly output: 'pg/timestamp-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-instant@1': { + readonly output: 'pg/timestamp-instant@1'; + readonly nullable: true; + }; readonly 'pg/timestamp-string@1': { readonly output: 'pg/timestamp-string@1'; readonly nullable: true; @@ -204,6 +232,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; diff --git a/examples/multi-extension-monorepo/app/src/contract.d.ts b/examples/multi-extension-monorepo/app/src/contract.d.ts index f824b988935d..3b6e63ce9c6a 100644 --- a/examples/multi-extension-monorepo/app/src/contract.d.ts +++ b/examples/multi-extension-monorepo/app/src/contract.d.ts @@ -42,6 +42,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; @@ -91,6 +95,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; @@ -100,6 +108,14 @@ export type AggregateTypes = { readonly output: 'pg/time-temporal@1'; readonly nullable: true; }; + readonly 'pg/timestamp-date@1': { + readonly output: 'pg/timestamp-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-instant@1': { + readonly output: 'pg/timestamp-instant@1'; + readonly nullable: true; + }; readonly 'pg/timestamp-string@1': { readonly output: 'pg/timestamp-string@1'; readonly nullable: true; @@ -151,6 +167,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; @@ -160,6 +180,14 @@ export type AggregateTypes = { readonly output: 'pg/time-temporal@1'; readonly nullable: true; }; + readonly 'pg/timestamp-date@1': { + readonly output: 'pg/timestamp-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-instant@1': { + readonly output: 'pg/timestamp-instant@1'; + readonly nullable: true; + }; readonly 'pg/timestamp-string@1': { readonly output: 'pg/timestamp-string@1'; readonly nullable: true; @@ -203,6 +231,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; diff --git a/examples/multi-extension-monorepo/packages/audit/migrations/snapshots/d60ebdd877de6283fafbbe0dc9a4fa13c1197009a53ba95ed37ba04fa3640cb1/contract.d.ts b/examples/multi-extension-monorepo/packages/audit/migrations/snapshots/d60ebdd877de6283fafbbe0dc9a4fa13c1197009a53ba95ed37ba04fa3640cb1/contract.d.ts index 3488479cd7fd..796b7442a900 100644 --- a/examples/multi-extension-monorepo/packages/audit/migrations/snapshots/d60ebdd877de6283fafbbe0dc9a4fa13c1197009a53ba95ed37ba04fa3640cb1/contract.d.ts +++ b/examples/multi-extension-monorepo/packages/audit/migrations/snapshots/d60ebdd877de6283fafbbe0dc9a4fa13c1197009a53ba95ed37ba04fa3640cb1/contract.d.ts @@ -42,6 +42,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; @@ -91,6 +95,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; @@ -100,6 +108,14 @@ export type AggregateTypes = { readonly output: 'pg/time-temporal@1'; readonly nullable: true; }; + readonly 'pg/timestamp-date@1': { + readonly output: 'pg/timestamp-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-instant@1': { + readonly output: 'pg/timestamp-instant@1'; + readonly nullable: true; + }; readonly 'pg/timestamp-string@1': { readonly output: 'pg/timestamp-string@1'; readonly nullable: true; @@ -151,6 +167,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; @@ -160,6 +180,14 @@ export type AggregateTypes = { readonly output: 'pg/time-temporal@1'; readonly nullable: true; }; + readonly 'pg/timestamp-date@1': { + readonly output: 'pg/timestamp-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-instant@1': { + readonly output: 'pg/timestamp-instant@1'; + readonly nullable: true; + }; readonly 'pg/timestamp-string@1': { readonly output: 'pg/timestamp-string@1'; readonly nullable: true; @@ -203,6 +231,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; diff --git a/examples/multi-extension-monorepo/packages/audit/src/contract.d.ts b/examples/multi-extension-monorepo/packages/audit/src/contract.d.ts index 3488479cd7fd..796b7442a900 100644 --- a/examples/multi-extension-monorepo/packages/audit/src/contract.d.ts +++ b/examples/multi-extension-monorepo/packages/audit/src/contract.d.ts @@ -42,6 +42,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; @@ -91,6 +95,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; @@ -100,6 +108,14 @@ export type AggregateTypes = { readonly output: 'pg/time-temporal@1'; readonly nullable: true; }; + readonly 'pg/timestamp-date@1': { + readonly output: 'pg/timestamp-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-instant@1': { + readonly output: 'pg/timestamp-instant@1'; + readonly nullable: true; + }; readonly 'pg/timestamp-string@1': { readonly output: 'pg/timestamp-string@1'; readonly nullable: true; @@ -151,6 +167,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; @@ -160,6 +180,14 @@ export type AggregateTypes = { readonly output: 'pg/time-temporal@1'; readonly nullable: true; }; + readonly 'pg/timestamp-date@1': { + readonly output: 'pg/timestamp-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-instant@1': { + readonly output: 'pg/timestamp-instant@1'; + readonly nullable: true; + }; readonly 'pg/timestamp-string@1': { readonly output: 'pg/timestamp-string@1'; readonly nullable: true; @@ -203,6 +231,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; diff --git a/examples/multi-extension-monorepo/packages/feature-flags/migrations/snapshots/9ea058903cdc9636107c83c1a6bbb6c3f28d92d4515cff00c31c2b4e37bb5bd4/contract.d.ts b/examples/multi-extension-monorepo/packages/feature-flags/migrations/snapshots/9ea058903cdc9636107c83c1a6bbb6c3f28d92d4515cff00c31c2b4e37bb5bd4/contract.d.ts index 24f1c76a2e72..578d9aa80d33 100644 --- a/examples/multi-extension-monorepo/packages/feature-flags/migrations/snapshots/9ea058903cdc9636107c83c1a6bbb6c3f28d92d4515cff00c31c2b4e37bb5bd4/contract.d.ts +++ b/examples/multi-extension-monorepo/packages/feature-flags/migrations/snapshots/9ea058903cdc9636107c83c1a6bbb6c3f28d92d4515cff00c31c2b4e37bb5bd4/contract.d.ts @@ -42,6 +42,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; @@ -91,6 +95,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; @@ -100,6 +108,14 @@ export type AggregateTypes = { readonly output: 'pg/time-temporal@1'; readonly nullable: true; }; + readonly 'pg/timestamp-date@1': { + readonly output: 'pg/timestamp-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-instant@1': { + readonly output: 'pg/timestamp-instant@1'; + readonly nullable: true; + }; readonly 'pg/timestamp-string@1': { readonly output: 'pg/timestamp-string@1'; readonly nullable: true; @@ -151,6 +167,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; @@ -160,6 +180,14 @@ export type AggregateTypes = { readonly output: 'pg/time-temporal@1'; readonly nullable: true; }; + readonly 'pg/timestamp-date@1': { + readonly output: 'pg/timestamp-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-instant@1': { + readonly output: 'pg/timestamp-instant@1'; + readonly nullable: true; + }; readonly 'pg/timestamp-string@1': { readonly output: 'pg/timestamp-string@1'; readonly nullable: true; @@ -203,6 +231,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; diff --git a/examples/multi-extension-monorepo/packages/feature-flags/src/contract.d.ts b/examples/multi-extension-monorepo/packages/feature-flags/src/contract.d.ts index 24f1c76a2e72..578d9aa80d33 100644 --- a/examples/multi-extension-monorepo/packages/feature-flags/src/contract.d.ts +++ b/examples/multi-extension-monorepo/packages/feature-flags/src/contract.d.ts @@ -42,6 +42,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; @@ -91,6 +95,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; @@ -100,6 +108,14 @@ export type AggregateTypes = { readonly output: 'pg/time-temporal@1'; readonly nullable: true; }; + readonly 'pg/timestamp-date@1': { + readonly output: 'pg/timestamp-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-instant@1': { + readonly output: 'pg/timestamp-instant@1'; + readonly nullable: true; + }; readonly 'pg/timestamp-string@1': { readonly output: 'pg/timestamp-string@1'; readonly nullable: true; @@ -151,6 +167,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; @@ -160,6 +180,14 @@ export type AggregateTypes = { readonly output: 'pg/time-temporal@1'; readonly nullable: true; }; + readonly 'pg/timestamp-date@1': { + readonly output: 'pg/timestamp-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-instant@1': { + readonly output: 'pg/timestamp-instant@1'; + readonly nullable: true; + }; readonly 'pg/timestamp-string@1': { readonly output: 'pg/timestamp-string@1'; readonly nullable: true; @@ -203,6 +231,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; diff --git a/examples/paradedb-demo/src/prisma/contract.d.ts b/examples/paradedb-demo/src/prisma/contract.d.ts index 88ebb99c1466..55aef7a45f14 100644 --- a/examples/paradedb-demo/src/prisma/contract.d.ts +++ b/examples/paradedb-demo/src/prisma/contract.d.ts @@ -44,6 +44,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; @@ -93,6 +97,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; @@ -102,6 +110,14 @@ export type AggregateTypes = { readonly output: 'pg/time-temporal@1'; readonly nullable: true; }; + readonly 'pg/timestamp-date@1': { + readonly output: 'pg/timestamp-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-instant@1': { + readonly output: 'pg/timestamp-instant@1'; + readonly nullable: true; + }; readonly 'pg/timestamp-string@1': { readonly output: 'pg/timestamp-string@1'; readonly nullable: true; @@ -153,6 +169,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; @@ -162,6 +182,14 @@ export type AggregateTypes = { readonly output: 'pg/time-temporal@1'; readonly nullable: true; }; + readonly 'pg/timestamp-date@1': { + readonly output: 'pg/timestamp-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-instant@1': { + readonly output: 'pg/timestamp-instant@1'; + readonly nullable: true; + }; readonly 'pg/timestamp-string@1': { readonly output: 'pg/timestamp-string@1'; readonly nullable: true; @@ -205,6 +233,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; diff --git a/examples/prisma-8-cloudflare-worker/src/prisma/contract.d.ts b/examples/prisma-8-cloudflare-worker/src/prisma/contract.d.ts index 821a1af79612..6b0d7d4a3e13 100644 --- a/examples/prisma-8-cloudflare-worker/src/prisma/contract.d.ts +++ b/examples/prisma-8-cloudflare-worker/src/prisma/contract.d.ts @@ -44,6 +44,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; @@ -93,6 +97,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; @@ -102,6 +110,14 @@ export type AggregateTypes = { readonly output: 'pg/time-temporal@1'; readonly nullable: true; }; + readonly 'pg/timestamp-date@1': { + readonly output: 'pg/timestamp-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-instant@1': { + readonly output: 'pg/timestamp-instant@1'; + readonly nullable: true; + }; readonly 'pg/timestamp-string@1': { readonly output: 'pg/timestamp-string@1'; readonly nullable: true; @@ -153,6 +169,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; @@ -162,6 +182,14 @@ export type AggregateTypes = { readonly output: 'pg/time-temporal@1'; readonly nullable: true; }; + readonly 'pg/timestamp-date@1': { + readonly output: 'pg/timestamp-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-instant@1': { + readonly output: 'pg/timestamp-instant@1'; + readonly nullable: true; + }; readonly 'pg/timestamp-string@1': { readonly output: 'pg/timestamp-string@1'; readonly nullable: true; @@ -205,6 +233,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; diff --git a/examples/prisma-8-demo/migrations/snapshots/0ce16e4140d056a3abddbe18bd7ee5198d392370f0aeb23236216c51505944de/contract.d.ts b/examples/prisma-8-demo/migrations/snapshots/0ce16e4140d056a3abddbe18bd7ee5198d392370f0aeb23236216c51505944de/contract.d.ts index 909cb96ba6bb..96499166b9d7 100644 --- a/examples/prisma-8-demo/migrations/snapshots/0ce16e4140d056a3abddbe18bd7ee5198d392370f0aeb23236216c51505944de/contract.d.ts +++ b/examples/prisma-8-demo/migrations/snapshots/0ce16e4140d056a3abddbe18bd7ee5198d392370f0aeb23236216c51505944de/contract.d.ts @@ -50,6 +50,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; @@ -99,6 +103,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; @@ -108,6 +116,14 @@ export type AggregateTypes = { readonly output: 'pg/time-temporal@1'; readonly nullable: true; }; + readonly 'pg/timestamp-date@1': { + readonly output: 'pg/timestamp-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-instant@1': { + readonly output: 'pg/timestamp-instant@1'; + readonly nullable: true; + }; readonly 'pg/timestamp-string@1': { readonly output: 'pg/timestamp-string@1'; readonly nullable: true; @@ -159,6 +175,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; @@ -168,6 +188,14 @@ export type AggregateTypes = { readonly output: 'pg/time-temporal@1'; readonly nullable: true; }; + readonly 'pg/timestamp-date@1': { + readonly output: 'pg/timestamp-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-instant@1': { + readonly output: 'pg/timestamp-instant@1'; + readonly nullable: true; + }; readonly 'pg/timestamp-string@1': { readonly output: 'pg/timestamp-string@1'; readonly nullable: true; @@ -227,6 +255,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; diff --git a/examples/prisma-8-demo/migrations/snapshots/0ce1b6cf3e03ac1498f39eb8ef08d36d73ee2f0bc7fe368b97f1497ba050501a/contract.d.ts b/examples/prisma-8-demo/migrations/snapshots/0ce1b6cf3e03ac1498f39eb8ef08d36d73ee2f0bc7fe368b97f1497ba050501a/contract.d.ts index 6e6569558199..d180da08d5e9 100644 --- a/examples/prisma-8-demo/migrations/snapshots/0ce1b6cf3e03ac1498f39eb8ef08d36d73ee2f0bc7fe368b97f1497ba050501a/contract.d.ts +++ b/examples/prisma-8-demo/migrations/snapshots/0ce1b6cf3e03ac1498f39eb8ef08d36d73ee2f0bc7fe368b97f1497ba050501a/contract.d.ts @@ -50,6 +50,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; @@ -99,6 +103,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; @@ -108,6 +116,14 @@ export type AggregateTypes = { readonly output: 'pg/time-temporal@1'; readonly nullable: true; }; + readonly 'pg/timestamp-date@1': { + readonly output: 'pg/timestamp-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-instant@1': { + readonly output: 'pg/timestamp-instant@1'; + readonly nullable: true; + }; readonly 'pg/timestamp-string@1': { readonly output: 'pg/timestamp-string@1'; readonly nullable: true; @@ -159,6 +175,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; @@ -168,6 +188,14 @@ export type AggregateTypes = { readonly output: 'pg/time-temporal@1'; readonly nullable: true; }; + readonly 'pg/timestamp-date@1': { + readonly output: 'pg/timestamp-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-instant@1': { + readonly output: 'pg/timestamp-instant@1'; + readonly nullable: true; + }; readonly 'pg/timestamp-string@1': { readonly output: 'pg/timestamp-string@1'; readonly nullable: true; @@ -227,6 +255,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; diff --git a/examples/prisma-8-demo/migrations/snapshots/58888a20cba5529ede90056ecd1318183f0e5cf280c88cd58aeadd94bbf52c1b/contract.d.ts b/examples/prisma-8-demo/migrations/snapshots/58888a20cba5529ede90056ecd1318183f0e5cf280c88cd58aeadd94bbf52c1b/contract.d.ts index 64fd0c6ba064..d80ae3a33b2b 100644 --- a/examples/prisma-8-demo/migrations/snapshots/58888a20cba5529ede90056ecd1318183f0e5cf280c88cd58aeadd94bbf52c1b/contract.d.ts +++ b/examples/prisma-8-demo/migrations/snapshots/58888a20cba5529ede90056ecd1318183f0e5cf280c88cd58aeadd94bbf52c1b/contract.d.ts @@ -50,6 +50,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; @@ -99,6 +103,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; @@ -108,6 +116,14 @@ export type AggregateTypes = { readonly output: 'pg/time-temporal@1'; readonly nullable: true; }; + readonly 'pg/timestamp-date@1': { + readonly output: 'pg/timestamp-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-instant@1': { + readonly output: 'pg/timestamp-instant@1'; + readonly nullable: true; + }; readonly 'pg/timestamp-string@1': { readonly output: 'pg/timestamp-string@1'; readonly nullable: true; @@ -159,6 +175,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; @@ -168,6 +188,14 @@ export type AggregateTypes = { readonly output: 'pg/time-temporal@1'; readonly nullable: true; }; + readonly 'pg/timestamp-date@1': { + readonly output: 'pg/timestamp-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-instant@1': { + readonly output: 'pg/timestamp-instant@1'; + readonly nullable: true; + }; readonly 'pg/timestamp-string@1': { readonly output: 'pg/timestamp-string@1'; readonly nullable: true; @@ -227,6 +255,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; diff --git a/examples/prisma-8-demo/migrations/snapshots/6e6fbbc22d1d4904fbc8668644c5cabe694d946e550a2f0cdd07aaab308b953f/contract.d.ts b/examples/prisma-8-demo/migrations/snapshots/6e6fbbc22d1d4904fbc8668644c5cabe694d946e550a2f0cdd07aaab308b953f/contract.d.ts index fc656d75473b..111a2ad19b9b 100644 --- a/examples/prisma-8-demo/migrations/snapshots/6e6fbbc22d1d4904fbc8668644c5cabe694d946e550a2f0cdd07aaab308b953f/contract.d.ts +++ b/examples/prisma-8-demo/migrations/snapshots/6e6fbbc22d1d4904fbc8668644c5cabe694d946e550a2f0cdd07aaab308b953f/contract.d.ts @@ -50,6 +50,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; @@ -99,6 +103,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; @@ -108,6 +116,14 @@ export type AggregateTypes = { readonly output: 'pg/time-temporal@1'; readonly nullable: true; }; + readonly 'pg/timestamp-date@1': { + readonly output: 'pg/timestamp-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-instant@1': { + readonly output: 'pg/timestamp-instant@1'; + readonly nullable: true; + }; readonly 'pg/timestamp-string@1': { readonly output: 'pg/timestamp-string@1'; readonly nullable: true; @@ -159,6 +175,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; @@ -168,6 +188,14 @@ export type AggregateTypes = { readonly output: 'pg/time-temporal@1'; readonly nullable: true; }; + readonly 'pg/timestamp-date@1': { + readonly output: 'pg/timestamp-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-instant@1': { + readonly output: 'pg/timestamp-instant@1'; + readonly nullable: true; + }; readonly 'pg/timestamp-string@1': { readonly output: 'pg/timestamp-string@1'; readonly nullable: true; @@ -227,6 +255,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; diff --git a/examples/prisma-8-demo/migrations/snapshots/d44ab6f7caddcc384332c669737008aec7ec8c9f5b013cb502a03c2eb04be9c0/contract.d.ts b/examples/prisma-8-demo/migrations/snapshots/d44ab6f7caddcc384332c669737008aec7ec8c9f5b013cb502a03c2eb04be9c0/contract.d.ts index bf0b5936eb55..6f22860d05c2 100644 --- a/examples/prisma-8-demo/migrations/snapshots/d44ab6f7caddcc384332c669737008aec7ec8c9f5b013cb502a03c2eb04be9c0/contract.d.ts +++ b/examples/prisma-8-demo/migrations/snapshots/d44ab6f7caddcc384332c669737008aec7ec8c9f5b013cb502a03c2eb04be9c0/contract.d.ts @@ -50,6 +50,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; @@ -99,6 +103,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; @@ -108,6 +116,14 @@ export type AggregateTypes = { readonly output: 'pg/time-temporal@1'; readonly nullable: true; }; + readonly 'pg/timestamp-date@1': { + readonly output: 'pg/timestamp-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-instant@1': { + readonly output: 'pg/timestamp-instant@1'; + readonly nullable: true; + }; readonly 'pg/timestamp-string@1': { readonly output: 'pg/timestamp-string@1'; readonly nullable: true; @@ -159,6 +175,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; @@ -168,6 +188,14 @@ export type AggregateTypes = { readonly output: 'pg/time-temporal@1'; readonly nullable: true; }; + readonly 'pg/timestamp-date@1': { + readonly output: 'pg/timestamp-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-instant@1': { + readonly output: 'pg/timestamp-instant@1'; + readonly nullable: true; + }; readonly 'pg/timestamp-string@1': { readonly output: 'pg/timestamp-string@1'; readonly nullable: true; @@ -227,6 +255,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; diff --git a/examples/prisma-8-demo/src/prisma/contract.d.ts b/examples/prisma-8-demo/src/prisma/contract.d.ts index 6e6569558199..d180da08d5e9 100644 --- a/examples/prisma-8-demo/src/prisma/contract.d.ts +++ b/examples/prisma-8-demo/src/prisma/contract.d.ts @@ -50,6 +50,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; @@ -99,6 +103,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; @@ -108,6 +116,14 @@ export type AggregateTypes = { readonly output: 'pg/time-temporal@1'; readonly nullable: true; }; + readonly 'pg/timestamp-date@1': { + readonly output: 'pg/timestamp-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-instant@1': { + readonly output: 'pg/timestamp-instant@1'; + readonly nullable: true; + }; readonly 'pg/timestamp-string@1': { readonly output: 'pg/timestamp-string@1'; readonly nullable: true; @@ -159,6 +175,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; @@ -168,6 +188,14 @@ export type AggregateTypes = { readonly output: 'pg/time-temporal@1'; readonly nullable: true; }; + readonly 'pg/timestamp-date@1': { + readonly output: 'pg/timestamp-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-instant@1': { + readonly output: 'pg/timestamp-instant@1'; + readonly nullable: true; + }; readonly 'pg/timestamp-string@1': { readonly output: 'pg/timestamp-string@1'; readonly nullable: true; @@ -227,6 +255,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; diff --git a/examples/prisma-8-postgis-demo/migrations/snapshots/28b5e5f98ce0e148f03e345518c9c53cb7a04a82e8e58de74a87c1b4ae08cf65/contract.d.ts b/examples/prisma-8-postgis-demo/migrations/snapshots/28b5e5f98ce0e148f03e345518c9c53cb7a04a82e8e58de74a87c1b4ae08cf65/contract.d.ts index 742622ac8252..f7a5c544b529 100644 --- a/examples/prisma-8-postgis-demo/migrations/snapshots/28b5e5f98ce0e148f03e345518c9c53cb7a04a82e8e58de74a87c1b4ae08cf65/contract.d.ts +++ b/examples/prisma-8-postgis-demo/migrations/snapshots/28b5e5f98ce0e148f03e345518c9c53cb7a04a82e8e58de74a87c1b4ae08cf65/contract.d.ts @@ -46,6 +46,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; @@ -95,6 +99,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; @@ -104,6 +112,14 @@ export type AggregateTypes = { readonly output: 'pg/time-temporal@1'; readonly nullable: true; }; + readonly 'pg/timestamp-date@1': { + readonly output: 'pg/timestamp-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-instant@1': { + readonly output: 'pg/timestamp-instant@1'; + readonly nullable: true; + }; readonly 'pg/timestamp-string@1': { readonly output: 'pg/timestamp-string@1'; readonly nullable: true; @@ -155,6 +171,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; @@ -164,6 +184,14 @@ export type AggregateTypes = { readonly output: 'pg/time-temporal@1'; readonly nullable: true; }; + readonly 'pg/timestamp-date@1': { + readonly output: 'pg/timestamp-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-instant@1': { + readonly output: 'pg/timestamp-instant@1'; + readonly nullable: true; + }; readonly 'pg/timestamp-string@1': { readonly output: 'pg/timestamp-string@1'; readonly nullable: true; @@ -207,6 +235,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; diff --git a/examples/prisma-8-postgis-demo/src/prisma/contract.d.ts b/examples/prisma-8-postgis-demo/src/prisma/contract.d.ts index 742622ac8252..f7a5c544b529 100644 --- a/examples/prisma-8-postgis-demo/src/prisma/contract.d.ts +++ b/examples/prisma-8-postgis-demo/src/prisma/contract.d.ts @@ -46,6 +46,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; @@ -95,6 +99,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; @@ -104,6 +112,14 @@ export type AggregateTypes = { readonly output: 'pg/time-temporal@1'; readonly nullable: true; }; + readonly 'pg/timestamp-date@1': { + readonly output: 'pg/timestamp-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-instant@1': { + readonly output: 'pg/timestamp-instant@1'; + readonly nullable: true; + }; readonly 'pg/timestamp-string@1': { readonly output: 'pg/timestamp-string@1'; readonly nullable: true; @@ -155,6 +171,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; @@ -164,6 +184,14 @@ export type AggregateTypes = { readonly output: 'pg/time-temporal@1'; readonly nullable: true; }; + readonly 'pg/timestamp-date@1': { + readonly output: 'pg/timestamp-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-instant@1': { + readonly output: 'pg/timestamp-instant@1'; + readonly nullable: true; + }; readonly 'pg/timestamp-string@1': { readonly output: 'pg/timestamp-string@1'; readonly nullable: true; @@ -207,6 +235,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; diff --git a/examples/prisma7-adoption/generated/prisma8/contract.d.ts b/examples/prisma7-adoption/generated/prisma8/contract.d.ts index 24b7812562d6..e21866441001 100644 --- a/examples/prisma7-adoption/generated/prisma8/contract.d.ts +++ b/examples/prisma7-adoption/generated/prisma8/contract.d.ts @@ -43,6 +43,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; @@ -92,6 +96,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; @@ -101,6 +109,14 @@ export type AggregateTypes = { readonly output: 'pg/time-temporal@1'; readonly nullable: true; }; + readonly 'pg/timestamp-date@1': { + readonly output: 'pg/timestamp-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-instant@1': { + readonly output: 'pg/timestamp-instant@1'; + readonly nullable: true; + }; readonly 'pg/timestamp-string@1': { readonly output: 'pg/timestamp-string@1'; readonly nullable: true; @@ -152,6 +168,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; @@ -161,6 +181,14 @@ export type AggregateTypes = { readonly output: 'pg/time-temporal@1'; readonly nullable: true; }; + readonly 'pg/timestamp-date@1': { + readonly output: 'pg/timestamp-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-instant@1': { + readonly output: 'pg/timestamp-instant@1'; + readonly nullable: true; + }; readonly 'pg/timestamp-string@1': { readonly output: 'pg/timestamp-string@1'; readonly nullable: true; @@ -204,6 +232,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; diff --git a/examples/react-router-demo/src/prisma/contract.d.ts b/examples/react-router-demo/src/prisma/contract.d.ts index 7126509cc497..3a4e6815649e 100644 --- a/examples/react-router-demo/src/prisma/contract.d.ts +++ b/examples/react-router-demo/src/prisma/contract.d.ts @@ -43,6 +43,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; @@ -92,6 +96,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; @@ -101,6 +109,14 @@ export type AggregateTypes = { readonly output: 'pg/time-temporal@1'; readonly nullable: true; }; + readonly 'pg/timestamp-date@1': { + readonly output: 'pg/timestamp-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-instant@1': { + readonly output: 'pg/timestamp-instant@1'; + readonly nullable: true; + }; readonly 'pg/timestamp-string@1': { readonly output: 'pg/timestamp-string@1'; readonly nullable: true; @@ -152,6 +168,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; @@ -161,6 +181,14 @@ export type AggregateTypes = { readonly output: 'pg/time-temporal@1'; readonly nullable: true; }; + readonly 'pg/timestamp-date@1': { + readonly output: 'pg/timestamp-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-instant@1': { + readonly output: 'pg/timestamp-instant@1'; + readonly nullable: true; + }; readonly 'pg/timestamp-string@1': { readonly output: 'pg/timestamp-string@1'; readonly nullable: true; @@ -204,6 +232,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; diff --git a/examples/supabase/src/contract.d.ts b/examples/supabase/src/contract.d.ts index 0c0c94063830..0e52c66a558c 100644 --- a/examples/supabase/src/contract.d.ts +++ b/examples/supabase/src/contract.d.ts @@ -42,6 +42,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; @@ -91,6 +95,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; @@ -100,6 +108,14 @@ export type AggregateTypes = { readonly output: 'pg/time-temporal@1'; readonly nullable: true; }; + readonly 'pg/timestamp-date@1': { + readonly output: 'pg/timestamp-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-instant@1': { + readonly output: 'pg/timestamp-instant@1'; + readonly nullable: true; + }; readonly 'pg/timestamp-string@1': { readonly output: 'pg/timestamp-string@1'; readonly nullable: true; @@ -151,6 +167,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; @@ -160,6 +180,14 @@ export type AggregateTypes = { readonly output: 'pg/time-temporal@1'; readonly nullable: true; }; + readonly 'pg/timestamp-date@1': { + readonly output: 'pg/timestamp-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-instant@1': { + readonly output: 'pg/timestamp-instant@1'; + readonly nullable: true; + }; readonly 'pg/timestamp-string@1': { readonly output: 'pg/timestamp-string@1'; readonly nullable: true; @@ -203,6 +231,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; diff --git a/packages/2-sql/4-lanes/sql-builder/test/fixtures/generated/contract.d.ts b/packages/2-sql/4-lanes/sql-builder/test/fixtures/generated/contract.d.ts index 80b3d3eda96a..7a2058a0a1fe 100644 --- a/packages/2-sql/4-lanes/sql-builder/test/fixtures/generated/contract.d.ts +++ b/packages/2-sql/4-lanes/sql-builder/test/fixtures/generated/contract.d.ts @@ -43,6 +43,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; @@ -92,6 +96,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; @@ -101,6 +109,14 @@ export type AggregateTypes = { readonly output: 'pg/time-temporal@1'; readonly nullable: true; }; + readonly 'pg/timestamp-date@1': { + readonly output: 'pg/timestamp-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-instant@1': { + readonly output: 'pg/timestamp-instant@1'; + readonly nullable: true; + }; readonly 'pg/timestamp-string@1': { readonly output: 'pg/timestamp-string@1'; readonly nullable: true; @@ -152,6 +168,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; @@ -161,6 +181,14 @@ export type AggregateTypes = { readonly output: 'pg/time-temporal@1'; readonly nullable: true; }; + readonly 'pg/timestamp-date@1': { + readonly output: 'pg/timestamp-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-instant@1': { + readonly output: 'pg/timestamp-instant@1'; + readonly nullable: true; + }; readonly 'pg/timestamp-string@1': { readonly output: 'pg/timestamp-string@1'; readonly nullable: true; @@ -204,6 +232,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; diff --git a/packages/3-extensions/paradedb/migrations/snapshots/0c0734babd6eeb868fee1f281ca96963022475611560e9f170f465daa35f8599/contract.d.ts b/packages/3-extensions/paradedb/migrations/snapshots/0c0734babd6eeb868fee1f281ca96963022475611560e9f170f465daa35f8599/contract.d.ts index fc1798b6c3b0..3f411499b2fb 100644 --- a/packages/3-extensions/paradedb/migrations/snapshots/0c0734babd6eeb868fee1f281ca96963022475611560e9f170f465daa35f8599/contract.d.ts +++ b/packages/3-extensions/paradedb/migrations/snapshots/0c0734babd6eeb868fee1f281ca96963022475611560e9f170f465daa35f8599/contract.d.ts @@ -42,6 +42,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; @@ -91,6 +95,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; @@ -100,6 +108,14 @@ export type AggregateTypes = { readonly output: 'pg/time-temporal@1'; readonly nullable: true; }; + readonly 'pg/timestamp-date@1': { + readonly output: 'pg/timestamp-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-instant@1': { + readonly output: 'pg/timestamp-instant@1'; + readonly nullable: true; + }; readonly 'pg/timestamp-string@1': { readonly output: 'pg/timestamp-string@1'; readonly nullable: true; @@ -151,6 +167,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; @@ -160,6 +180,14 @@ export type AggregateTypes = { readonly output: 'pg/time-temporal@1'; readonly nullable: true; }; + readonly 'pg/timestamp-date@1': { + readonly output: 'pg/timestamp-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-instant@1': { + readonly output: 'pg/timestamp-instant@1'; + readonly nullable: true; + }; readonly 'pg/timestamp-string@1': { readonly output: 'pg/timestamp-string@1'; readonly nullable: true; @@ -203,6 +231,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; diff --git a/packages/3-extensions/paradedb/src/contract.d.ts b/packages/3-extensions/paradedb/src/contract.d.ts index fc1798b6c3b0..3f411499b2fb 100644 --- a/packages/3-extensions/paradedb/src/contract.d.ts +++ b/packages/3-extensions/paradedb/src/contract.d.ts @@ -42,6 +42,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; @@ -91,6 +95,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; @@ -100,6 +108,14 @@ export type AggregateTypes = { readonly output: 'pg/time-temporal@1'; readonly nullable: true; }; + readonly 'pg/timestamp-date@1': { + readonly output: 'pg/timestamp-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-instant@1': { + readonly output: 'pg/timestamp-instant@1'; + readonly nullable: true; + }; readonly 'pg/timestamp-string@1': { readonly output: 'pg/timestamp-string@1'; readonly nullable: true; @@ -151,6 +167,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; @@ -160,6 +180,14 @@ export type AggregateTypes = { readonly output: 'pg/time-temporal@1'; readonly nullable: true; }; + readonly 'pg/timestamp-date@1': { + readonly output: 'pg/timestamp-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-instant@1': { + readonly output: 'pg/timestamp-instant@1'; + readonly nullable: true; + }; readonly 'pg/timestamp-string@1': { readonly output: 'pg/timestamp-string@1'; readonly nullable: true; @@ -203,6 +231,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; diff --git a/packages/3-extensions/pgvector/migrations/snapshots/4a96b488a4ce92b434e5f7d6607b6435c0955f0d36b0018077045787764240e6/contract.d.ts b/packages/3-extensions/pgvector/migrations/snapshots/4a96b488a4ce92b434e5f7d6607b6435c0955f0d36b0018077045787764240e6/contract.d.ts index 64c2ccc4f849..9f816cdefb5f 100644 --- a/packages/3-extensions/pgvector/migrations/snapshots/4a96b488a4ce92b434e5f7d6607b6435c0955f0d36b0018077045787764240e6/contract.d.ts +++ b/packages/3-extensions/pgvector/migrations/snapshots/4a96b488a4ce92b434e5f7d6607b6435c0955f0d36b0018077045787764240e6/contract.d.ts @@ -42,6 +42,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; @@ -91,6 +95,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; @@ -100,6 +108,14 @@ export type AggregateTypes = { readonly output: 'pg/time-temporal@1'; readonly nullable: true; }; + readonly 'pg/timestamp-date@1': { + readonly output: 'pg/timestamp-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-instant@1': { + readonly output: 'pg/timestamp-instant@1'; + readonly nullable: true; + }; readonly 'pg/timestamp-string@1': { readonly output: 'pg/timestamp-string@1'; readonly nullable: true; @@ -151,6 +167,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; @@ -160,6 +180,14 @@ export type AggregateTypes = { readonly output: 'pg/time-temporal@1'; readonly nullable: true; }; + readonly 'pg/timestamp-date@1': { + readonly output: 'pg/timestamp-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-instant@1': { + readonly output: 'pg/timestamp-instant@1'; + readonly nullable: true; + }; readonly 'pg/timestamp-string@1': { readonly output: 'pg/timestamp-string@1'; readonly nullable: true; @@ -203,6 +231,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; diff --git a/packages/3-extensions/pgvector/src/contract.d.ts b/packages/3-extensions/pgvector/src/contract.d.ts index 64c2ccc4f849..9f816cdefb5f 100644 --- a/packages/3-extensions/pgvector/src/contract.d.ts +++ b/packages/3-extensions/pgvector/src/contract.d.ts @@ -42,6 +42,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; @@ -91,6 +95,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; @@ -100,6 +108,14 @@ export type AggregateTypes = { readonly output: 'pg/time-temporal@1'; readonly nullable: true; }; + readonly 'pg/timestamp-date@1': { + readonly output: 'pg/timestamp-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-instant@1': { + readonly output: 'pg/timestamp-instant@1'; + readonly nullable: true; + }; readonly 'pg/timestamp-string@1': { readonly output: 'pg/timestamp-string@1'; readonly nullable: true; @@ -151,6 +167,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; @@ -160,6 +180,14 @@ export type AggregateTypes = { readonly output: 'pg/time-temporal@1'; readonly nullable: true; }; + readonly 'pg/timestamp-date@1': { + readonly output: 'pg/timestamp-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-instant@1': { + readonly output: 'pg/timestamp-instant@1'; + readonly nullable: true; + }; readonly 'pg/timestamp-string@1': { readonly output: 'pg/timestamp-string@1'; readonly nullable: true; @@ -203,6 +231,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; diff --git a/packages/3-extensions/postgis/migrations/snapshots/2db551a8a5619d0d97710f29130eaea0eccbdd5238f015ee9d18408d438e0f8a/contract.d.ts b/packages/3-extensions/postgis/migrations/snapshots/2db551a8a5619d0d97710f29130eaea0eccbdd5238f015ee9d18408d438e0f8a/contract.d.ts index b56429d1fad7..0556e3ca8e35 100644 --- a/packages/3-extensions/postgis/migrations/snapshots/2db551a8a5619d0d97710f29130eaea0eccbdd5238f015ee9d18408d438e0f8a/contract.d.ts +++ b/packages/3-extensions/postgis/migrations/snapshots/2db551a8a5619d0d97710f29130eaea0eccbdd5238f015ee9d18408d438e0f8a/contract.d.ts @@ -42,6 +42,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; @@ -91,6 +95,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; @@ -100,6 +108,14 @@ export type AggregateTypes = { readonly output: 'pg/time-temporal@1'; readonly nullable: true; }; + readonly 'pg/timestamp-date@1': { + readonly output: 'pg/timestamp-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-instant@1': { + readonly output: 'pg/timestamp-instant@1'; + readonly nullable: true; + }; readonly 'pg/timestamp-string@1': { readonly output: 'pg/timestamp-string@1'; readonly nullable: true; @@ -151,6 +167,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; @@ -160,6 +180,14 @@ export type AggregateTypes = { readonly output: 'pg/time-temporal@1'; readonly nullable: true; }; + readonly 'pg/timestamp-date@1': { + readonly output: 'pg/timestamp-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-instant@1': { + readonly output: 'pg/timestamp-instant@1'; + readonly nullable: true; + }; readonly 'pg/timestamp-string@1': { readonly output: 'pg/timestamp-string@1'; readonly nullable: true; @@ -203,6 +231,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; diff --git a/packages/3-extensions/postgis/src/contract.d.ts b/packages/3-extensions/postgis/src/contract.d.ts index b56429d1fad7..0556e3ca8e35 100644 --- a/packages/3-extensions/postgis/src/contract.d.ts +++ b/packages/3-extensions/postgis/src/contract.d.ts @@ -42,6 +42,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; @@ -91,6 +95,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; @@ -100,6 +108,14 @@ export type AggregateTypes = { readonly output: 'pg/time-temporal@1'; readonly nullable: true; }; + readonly 'pg/timestamp-date@1': { + readonly output: 'pg/timestamp-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-instant@1': { + readonly output: 'pg/timestamp-instant@1'; + readonly nullable: true; + }; readonly 'pg/timestamp-string@1': { readonly output: 'pg/timestamp-string@1'; readonly nullable: true; @@ -151,6 +167,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; @@ -160,6 +180,14 @@ export type AggregateTypes = { readonly output: 'pg/time-temporal@1'; readonly nullable: true; }; + readonly 'pg/timestamp-date@1': { + readonly output: 'pg/timestamp-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-instant@1': { + readonly output: 'pg/timestamp-instant@1'; + readonly nullable: true; + }; readonly 'pg/timestamp-string@1': { readonly output: 'pg/timestamp-string@1'; readonly nullable: true; @@ -203,6 +231,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; diff --git a/packages/3-extensions/postgres/test/fixtures/generated/contract.d.ts b/packages/3-extensions/postgres/test/fixtures/generated/contract.d.ts index 80b3d3eda96a..7a2058a0a1fe 100644 --- a/packages/3-extensions/postgres/test/fixtures/generated/contract.d.ts +++ b/packages/3-extensions/postgres/test/fixtures/generated/contract.d.ts @@ -43,6 +43,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; @@ -92,6 +96,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; @@ -101,6 +109,14 @@ export type AggregateTypes = { readonly output: 'pg/time-temporal@1'; readonly nullable: true; }; + readonly 'pg/timestamp-date@1': { + readonly output: 'pg/timestamp-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-instant@1': { + readonly output: 'pg/timestamp-instant@1'; + readonly nullable: true; + }; readonly 'pg/timestamp-string@1': { readonly output: 'pg/timestamp-string@1'; readonly nullable: true; @@ -152,6 +168,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; @@ -161,6 +181,14 @@ export type AggregateTypes = { readonly output: 'pg/time-temporal@1'; readonly nullable: true; }; + readonly 'pg/timestamp-date@1': { + readonly output: 'pg/timestamp-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-instant@1': { + readonly output: 'pg/timestamp-instant@1'; + readonly nullable: true; + }; readonly 'pg/timestamp-string@1': { readonly output: 'pg/timestamp-string@1'; readonly nullable: true; @@ -204,6 +232,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; diff --git a/packages/3-extensions/sql-orm-client/test/fixtures/collection-methods/generated/contract.d.ts b/packages/3-extensions/sql-orm-client/test/fixtures/collection-methods/generated/contract.d.ts index 98347ff79255..3a9d55796e0e 100644 --- a/packages/3-extensions/sql-orm-client/test/fixtures/collection-methods/generated/contract.d.ts +++ b/packages/3-extensions/sql-orm-client/test/fixtures/collection-methods/generated/contract.d.ts @@ -48,6 +48,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; @@ -97,6 +101,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; @@ -106,6 +114,14 @@ export type AggregateTypes = { readonly output: 'pg/time-temporal@1'; readonly nullable: true; }; + readonly 'pg/timestamp-date@1': { + readonly output: 'pg/timestamp-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-instant@1': { + readonly output: 'pg/timestamp-instant@1'; + readonly nullable: true; + }; readonly 'pg/timestamp-string@1': { readonly output: 'pg/timestamp-string@1'; readonly nullable: true; @@ -157,6 +173,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; @@ -166,6 +186,14 @@ export type AggregateTypes = { readonly output: 'pg/time-temporal@1'; readonly nullable: true; }; + readonly 'pg/timestamp-date@1': { + readonly output: 'pg/timestamp-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-instant@1': { + readonly output: 'pg/timestamp-instant@1'; + readonly nullable: true; + }; readonly 'pg/timestamp-string@1': { readonly output: 'pg/timestamp-string@1'; readonly nullable: true; @@ -209,6 +237,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; diff --git a/packages/3-extensions/sql-orm-client/test/fixtures/fragment-namespace/generated/contract.d.ts b/packages/3-extensions/sql-orm-client/test/fixtures/fragment-namespace/generated/contract.d.ts index 522738f53abe..4a0bc8f4834c 100644 --- a/packages/3-extensions/sql-orm-client/test/fixtures/fragment-namespace/generated/contract.d.ts +++ b/packages/3-extensions/sql-orm-client/test/fixtures/fragment-namespace/generated/contract.d.ts @@ -42,6 +42,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; @@ -91,6 +95,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; @@ -100,6 +108,14 @@ export type AggregateTypes = { readonly output: 'pg/time-temporal@1'; readonly nullable: true; }; + readonly 'pg/timestamp-date@1': { + readonly output: 'pg/timestamp-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-instant@1': { + readonly output: 'pg/timestamp-instant@1'; + readonly nullable: true; + }; readonly 'pg/timestamp-string@1': { readonly output: 'pg/timestamp-string@1'; readonly nullable: true; @@ -151,6 +167,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; @@ -160,6 +180,14 @@ export type AggregateTypes = { readonly output: 'pg/time-temporal@1'; readonly nullable: true; }; + readonly 'pg/timestamp-date@1': { + readonly output: 'pg/timestamp-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-instant@1': { + readonly output: 'pg/timestamp-instant@1'; + readonly nullable: true; + }; readonly 'pg/timestamp-string@1': { readonly output: 'pg/timestamp-string@1'; readonly nullable: true; @@ -203,6 +231,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; diff --git a/packages/3-extensions/sql-orm-client/test/fixtures/generated/contract.d.ts b/packages/3-extensions/sql-orm-client/test/fixtures/generated/contract.d.ts index fb429f0fef68..616e21b8f485 100644 --- a/packages/3-extensions/sql-orm-client/test/fixtures/generated/contract.d.ts +++ b/packages/3-extensions/sql-orm-client/test/fixtures/generated/contract.d.ts @@ -63,6 +63,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; @@ -112,6 +116,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; @@ -121,6 +129,14 @@ export type AggregateTypes = { readonly output: 'pg/time-temporal@1'; readonly nullable: true; }; + readonly 'pg/timestamp-date@1': { + readonly output: 'pg/timestamp-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-instant@1': { + readonly output: 'pg/timestamp-instant@1'; + readonly nullable: true; + }; readonly 'pg/timestamp-string@1': { readonly output: 'pg/timestamp-string@1'; readonly nullable: true; @@ -172,6 +188,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; @@ -181,6 +201,14 @@ export type AggregateTypes = { readonly output: 'pg/time-temporal@1'; readonly nullable: true; }; + readonly 'pg/timestamp-date@1': { + readonly output: 'pg/timestamp-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-instant@1': { + readonly output: 'pg/timestamp-instant@1'; + readonly nullable: true; + }; readonly 'pg/timestamp-string@1': { readonly output: 'pg/timestamp-string@1'; readonly nullable: true; @@ -224,6 +252,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; diff --git a/packages/3-extensions/sql-orm-client/test/fixtures/junction-namespaces/generated/contract.d.ts b/packages/3-extensions/sql-orm-client/test/fixtures/junction-namespaces/generated/contract.d.ts index 73af750f4244..cf321d22b5ac 100644 --- a/packages/3-extensions/sql-orm-client/test/fixtures/junction-namespaces/generated/contract.d.ts +++ b/packages/3-extensions/sql-orm-client/test/fixtures/junction-namespaces/generated/contract.d.ts @@ -43,6 +43,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; @@ -92,6 +96,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; @@ -101,6 +109,14 @@ export type AggregateTypes = { readonly output: 'pg/time-temporal@1'; readonly nullable: true; }; + readonly 'pg/timestamp-date@1': { + readonly output: 'pg/timestamp-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-instant@1': { + readonly output: 'pg/timestamp-instant@1'; + readonly nullable: true; + }; readonly 'pg/timestamp-string@1': { readonly output: 'pg/timestamp-string@1'; readonly nullable: true; @@ -152,6 +168,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; @@ -161,6 +181,14 @@ export type AggregateTypes = { readonly output: 'pg/time-temporal@1'; readonly nullable: true; }; + readonly 'pg/timestamp-date@1': { + readonly output: 'pg/timestamp-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-instant@1': { + readonly output: 'pg/timestamp-instant@1'; + readonly nullable: true; + }; readonly 'pg/timestamp-string@1': { readonly output: 'pg/timestamp-string@1'; readonly nullable: true; @@ -204,6 +232,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; diff --git a/packages/3-extensions/sql-orm-client/test/fixtures/polymorphism/generated/contract.d.ts b/packages/3-extensions/sql-orm-client/test/fixtures/polymorphism/generated/contract.d.ts index 90af9e6edd9b..741846a4cec7 100644 --- a/packages/3-extensions/sql-orm-client/test/fixtures/polymorphism/generated/contract.d.ts +++ b/packages/3-extensions/sql-orm-client/test/fixtures/polymorphism/generated/contract.d.ts @@ -42,6 +42,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; @@ -91,6 +95,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; @@ -100,6 +108,14 @@ export type AggregateTypes = { readonly output: 'pg/time-temporal@1'; readonly nullable: true; }; + readonly 'pg/timestamp-date@1': { + readonly output: 'pg/timestamp-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-instant@1': { + readonly output: 'pg/timestamp-instant@1'; + readonly nullable: true; + }; readonly 'pg/timestamp-string@1': { readonly output: 'pg/timestamp-string@1'; readonly nullable: true; @@ -151,6 +167,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; @@ -160,6 +180,14 @@ export type AggregateTypes = { readonly output: 'pg/time-temporal@1'; readonly nullable: true; }; + readonly 'pg/timestamp-date@1': { + readonly output: 'pg/timestamp-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-instant@1': { + readonly output: 'pg/timestamp-instant@1'; + readonly nullable: true; + }; readonly 'pg/timestamp-string@1': { readonly output: 'pg/timestamp-string@1'; readonly nullable: true; @@ -203,6 +231,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; diff --git a/packages/3-extensions/sql-orm-client/test/fixtures/scope-namespace/generated/contract.d.ts b/packages/3-extensions/sql-orm-client/test/fixtures/scope-namespace/generated/contract.d.ts index ffa93ebd92ef..bc89d2cf7208 100644 --- a/packages/3-extensions/sql-orm-client/test/fixtures/scope-namespace/generated/contract.d.ts +++ b/packages/3-extensions/sql-orm-client/test/fixtures/scope-namespace/generated/contract.d.ts @@ -42,6 +42,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; @@ -91,6 +95,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; @@ -100,6 +108,14 @@ export type AggregateTypes = { readonly output: 'pg/time-temporal@1'; readonly nullable: true; }; + readonly 'pg/timestamp-date@1': { + readonly output: 'pg/timestamp-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-instant@1': { + readonly output: 'pg/timestamp-instant@1'; + readonly nullable: true; + }; readonly 'pg/timestamp-string@1': { readonly output: 'pg/timestamp-string@1'; readonly nullable: true; @@ -151,6 +167,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; @@ -160,6 +180,14 @@ export type AggregateTypes = { readonly output: 'pg/time-temporal@1'; readonly nullable: true; }; + readonly 'pg/timestamp-date@1': { + readonly output: 'pg/timestamp-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-instant@1': { + readonly output: 'pg/timestamp-instant@1'; + readonly nullable: true; + }; readonly 'pg/timestamp-string@1': { readonly output: 'pg/timestamp-string@1'; readonly nullable: true; @@ -203,6 +231,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; diff --git a/packages/3-extensions/sql-orm-client/test/fixtures/soft-delete/generated/contract.d.ts b/packages/3-extensions/sql-orm-client/test/fixtures/soft-delete/generated/contract.d.ts index 64b6249f01d3..62f3fe8bdef7 100644 --- a/packages/3-extensions/sql-orm-client/test/fixtures/soft-delete/generated/contract.d.ts +++ b/packages/3-extensions/sql-orm-client/test/fixtures/soft-delete/generated/contract.d.ts @@ -43,6 +43,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; @@ -92,6 +96,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; @@ -101,6 +109,14 @@ export type AggregateTypes = { readonly output: 'pg/time-temporal@1'; readonly nullable: true; }; + readonly 'pg/timestamp-date@1': { + readonly output: 'pg/timestamp-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-instant@1': { + readonly output: 'pg/timestamp-instant@1'; + readonly nullable: true; + }; readonly 'pg/timestamp-string@1': { readonly output: 'pg/timestamp-string@1'; readonly nullable: true; @@ -152,6 +168,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; @@ -161,6 +181,14 @@ export type AggregateTypes = { readonly output: 'pg/time-temporal@1'; readonly nullable: true; }; + readonly 'pg/timestamp-date@1': { + readonly output: 'pg/timestamp-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-instant@1': { + readonly output: 'pg/timestamp-instant@1'; + readonly nullable: true; + }; readonly 'pg/timestamp-string@1': { readonly output: 'pg/timestamp-string@1'; readonly nullable: true; @@ -204,6 +232,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; diff --git a/packages/3-extensions/supabase/src/contract/contract.d.ts b/packages/3-extensions/supabase/src/contract/contract.d.ts index f480e822713e..dbc9c7898e78 100644 --- a/packages/3-extensions/supabase/src/contract/contract.d.ts +++ b/packages/3-extensions/supabase/src/contract/contract.d.ts @@ -42,6 +42,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; @@ -91,6 +95,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; @@ -100,6 +108,14 @@ export type AggregateTypes = { readonly output: 'pg/time-temporal@1'; readonly nullable: true; }; + readonly 'pg/timestamp-date@1': { + readonly output: 'pg/timestamp-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-instant@1': { + readonly output: 'pg/timestamp-instant@1'; + readonly nullable: true; + }; readonly 'pg/timestamp-string@1': { readonly output: 'pg/timestamp-string@1'; readonly nullable: true; @@ -151,6 +167,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; @@ -160,6 +180,14 @@ export type AggregateTypes = { readonly output: 'pg/time-temporal@1'; readonly nullable: true; }; + readonly 'pg/timestamp-date@1': { + readonly output: 'pg/timestamp-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-instant@1': { + readonly output: 'pg/timestamp-instant@1'; + readonly nullable: true; + }; readonly 'pg/timestamp-string@1': { readonly output: 'pg/timestamp-string@1'; readonly nullable: true; @@ -203,6 +231,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; diff --git a/packages/3-extensions/supabase/test/fixtures/example-app/contract.d.ts b/packages/3-extensions/supabase/test/fixtures/example-app/contract.d.ts index fecba5184bfe..771a10f2a372 100644 --- a/packages/3-extensions/supabase/test/fixtures/example-app/contract.d.ts +++ b/packages/3-extensions/supabase/test/fixtures/example-app/contract.d.ts @@ -42,6 +42,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; @@ -91,6 +95,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; @@ -100,6 +108,14 @@ export type AggregateTypes = { readonly output: 'pg/time-temporal@1'; readonly nullable: true; }; + readonly 'pg/timestamp-date@1': { + readonly output: 'pg/timestamp-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-instant@1': { + readonly output: 'pg/timestamp-instant@1'; + readonly nullable: true; + }; readonly 'pg/timestamp-string@1': { readonly output: 'pg/timestamp-string@1'; readonly nullable: true; @@ -151,6 +167,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; @@ -160,6 +180,14 @@ export type AggregateTypes = { readonly output: 'pg/time-temporal@1'; readonly nullable: true; }; + readonly 'pg/timestamp-date@1': { + readonly output: 'pg/timestamp-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-instant@1': { + readonly output: 'pg/timestamp-instant@1'; + readonly nullable: true; + }; readonly 'pg/timestamp-string@1': { readonly output: 'pg/timestamp-string@1'; readonly nullable: true; @@ -203,6 +231,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; diff --git a/packages/3-extensions/supabase/test/fixtures/no-policy/contract.d.ts b/packages/3-extensions/supabase/test/fixtures/no-policy/contract.d.ts index 87d43eff6652..e301cce5c96d 100644 --- a/packages/3-extensions/supabase/test/fixtures/no-policy/contract.d.ts +++ b/packages/3-extensions/supabase/test/fixtures/no-policy/contract.d.ts @@ -42,6 +42,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; @@ -91,6 +95,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; @@ -100,6 +108,14 @@ export type AggregateTypes = { readonly output: 'pg/time-temporal@1'; readonly nullable: true; }; + readonly 'pg/timestamp-date@1': { + readonly output: 'pg/timestamp-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-instant@1': { + readonly output: 'pg/timestamp-instant@1'; + readonly nullable: true; + }; readonly 'pg/timestamp-string@1': { readonly output: 'pg/timestamp-string@1'; readonly nullable: true; @@ -151,6 +167,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; @@ -160,6 +180,14 @@ export type AggregateTypes = { readonly output: 'pg/time-temporal@1'; readonly nullable: true; }; + readonly 'pg/timestamp-date@1': { + readonly output: 'pg/timestamp-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-instant@1': { + readonly output: 'pg/timestamp-instant@1'; + readonly nullable: true; + }; readonly 'pg/timestamp-string@1': { readonly output: 'pg/timestamp-string@1'; readonly nullable: true; @@ -203,6 +231,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; diff --git a/packages/3-extensions/supabase/test/fixtures/renamed-policy/contract.d.ts b/packages/3-extensions/supabase/test/fixtures/renamed-policy/contract.d.ts index 131f4975ebd6..da46f4bf2c33 100644 --- a/packages/3-extensions/supabase/test/fixtures/renamed-policy/contract.d.ts +++ b/packages/3-extensions/supabase/test/fixtures/renamed-policy/contract.d.ts @@ -42,6 +42,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; @@ -91,6 +95,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; @@ -100,6 +108,14 @@ export type AggregateTypes = { readonly output: 'pg/time-temporal@1'; readonly nullable: true; }; + readonly 'pg/timestamp-date@1': { + readonly output: 'pg/timestamp-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-instant@1': { + readonly output: 'pg/timestamp-instant@1'; + readonly nullable: true; + }; readonly 'pg/timestamp-string@1': { readonly output: 'pg/timestamp-string@1'; readonly nullable: true; @@ -151,6 +167,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; @@ -160,6 +180,14 @@ export type AggregateTypes = { readonly output: 'pg/time-temporal@1'; readonly nullable: true; }; + readonly 'pg/timestamp-date@1': { + readonly output: 'pg/timestamp-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-instant@1': { + readonly output: 'pg/timestamp-instant@1'; + readonly nullable: true; + }; readonly 'pg/timestamp-string@1': { readonly output: 'pg/timestamp-string@1'; readonly nullable: true; @@ -203,6 +231,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; diff --git a/test/e2e/framework/test/fixtures/generated/contract.d.ts b/test/e2e/framework/test/fixtures/generated/contract.d.ts index aacd1de40319..6969447cbf1f 100644 --- a/test/e2e/framework/test/fixtures/generated/contract.d.ts +++ b/test/e2e/framework/test/fixtures/generated/contract.d.ts @@ -50,6 +50,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; @@ -99,6 +103,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; @@ -108,6 +116,14 @@ export type AggregateTypes = { readonly output: 'pg/time-temporal@1'; readonly nullable: true; }; + readonly 'pg/timestamp-date@1': { + readonly output: 'pg/timestamp-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-instant@1': { + readonly output: 'pg/timestamp-instant@1'; + readonly nullable: true; + }; readonly 'pg/timestamp-string@1': { readonly output: 'pg/timestamp-string@1'; readonly nullable: true; @@ -159,6 +175,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; @@ -168,6 +188,14 @@ export type AggregateTypes = { readonly output: 'pg/time-temporal@1'; readonly nullable: true; }; + readonly 'pg/timestamp-date@1': { + readonly output: 'pg/timestamp-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-instant@1': { + readonly output: 'pg/timestamp-instant@1'; + readonly nullable: true; + }; readonly 'pg/timestamp-string@1': { readonly output: 'pg/timestamp-string@1'; readonly nullable: true; @@ -211,6 +239,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; diff --git a/test/integration/test/contract-parity/fixtures/postgres/generated/contract.d.ts b/test/integration/test/contract-parity/fixtures/postgres/generated/contract.d.ts index d06049f6d9e6..278e9edeeaa5 100644 --- a/test/integration/test/contract-parity/fixtures/postgres/generated/contract.d.ts +++ b/test/integration/test/contract-parity/fixtures/postgres/generated/contract.d.ts @@ -44,6 +44,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; @@ -93,6 +97,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; @@ -102,6 +110,14 @@ export type AggregateTypes = { readonly output: 'pg/time-temporal@1'; readonly nullable: true; }; + readonly 'pg/timestamp-date@1': { + readonly output: 'pg/timestamp-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-instant@1': { + readonly output: 'pg/timestamp-instant@1'; + readonly nullable: true; + }; readonly 'pg/timestamp-string@1': { readonly output: 'pg/timestamp-string@1'; readonly nullable: true; @@ -153,6 +169,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; @@ -162,6 +182,14 @@ export type AggregateTypes = { readonly output: 'pg/time-temporal@1'; readonly nullable: true; }; + readonly 'pg/timestamp-date@1': { + readonly output: 'pg/timestamp-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-instant@1': { + readonly output: 'pg/timestamp-instant@1'; + readonly nullable: true; + }; readonly 'pg/timestamp-string@1': { readonly output: 'pg/timestamp-string@1'; readonly nullable: true; @@ -205,6 +233,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; diff --git a/test/integration/test/enum-members/_fixture-native-enums/generated/contract.d.ts b/test/integration/test/enum-members/_fixture-native-enums/generated/contract.d.ts index 172eb296c6ab..49594122fd37 100644 --- a/test/integration/test/enum-members/_fixture-native-enums/generated/contract.d.ts +++ b/test/integration/test/enum-members/_fixture-native-enums/generated/contract.d.ts @@ -42,6 +42,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; @@ -91,6 +95,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; @@ -100,6 +108,14 @@ export type AggregateTypes = { readonly output: 'pg/time-temporal@1'; readonly nullable: true; }; + readonly 'pg/timestamp-date@1': { + readonly output: 'pg/timestamp-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-instant@1': { + readonly output: 'pg/timestamp-instant@1'; + readonly nullable: true; + }; readonly 'pg/timestamp-string@1': { readonly output: 'pg/timestamp-string@1'; readonly nullable: true; @@ -151,6 +167,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; @@ -160,6 +180,14 @@ export type AggregateTypes = { readonly output: 'pg/time-temporal@1'; readonly nullable: true; }; + readonly 'pg/timestamp-date@1': { + readonly output: 'pg/timestamp-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-instant@1': { + readonly output: 'pg/timestamp-instant@1'; + readonly nullable: true; + }; readonly 'pg/timestamp-string@1': { readonly output: 'pg/timestamp-string@1'; readonly nullable: true; @@ -203,6 +231,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; diff --git a/test/integration/test/enum-members/_fixture-postgres/generated/contract.d.ts b/test/integration/test/enum-members/_fixture-postgres/generated/contract.d.ts index b6ddb313c349..e186f4f346df 100644 --- a/test/integration/test/enum-members/_fixture-postgres/generated/contract.d.ts +++ b/test/integration/test/enum-members/_fixture-postgres/generated/contract.d.ts @@ -42,6 +42,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; @@ -91,6 +95,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; @@ -100,6 +108,14 @@ export type AggregateTypes = { readonly output: 'pg/time-temporal@1'; readonly nullable: true; }; + readonly 'pg/timestamp-date@1': { + readonly output: 'pg/timestamp-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-instant@1': { + readonly output: 'pg/timestamp-instant@1'; + readonly nullable: true; + }; readonly 'pg/timestamp-string@1': { readonly output: 'pg/timestamp-string@1'; readonly nullable: true; @@ -151,6 +167,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; @@ -160,6 +180,14 @@ export type AggregateTypes = { readonly output: 'pg/time-temporal@1'; readonly nullable: true; }; + readonly 'pg/timestamp-date@1': { + readonly output: 'pg/timestamp-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-instant@1': { + readonly output: 'pg/timestamp-instant@1'; + readonly nullable: true; + }; readonly 'pg/timestamp-string@1': { readonly output: 'pg/timestamp-string@1'; readonly nullable: true; @@ -203,6 +231,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; diff --git a/test/integration/test/enum-order-by/_fixture/generated/contract.d.ts b/test/integration/test/enum-order-by/_fixture/generated/contract.d.ts index 3e7648083dd0..e3aca11c0c88 100644 --- a/test/integration/test/enum-order-by/_fixture/generated/contract.d.ts +++ b/test/integration/test/enum-order-by/_fixture/generated/contract.d.ts @@ -42,6 +42,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; @@ -91,6 +95,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; @@ -100,6 +108,14 @@ export type AggregateTypes = { readonly output: 'pg/time-temporal@1'; readonly nullable: true; }; + readonly 'pg/timestamp-date@1': { + readonly output: 'pg/timestamp-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-instant@1': { + readonly output: 'pg/timestamp-instant@1'; + readonly nullable: true; + }; readonly 'pg/timestamp-string@1': { readonly output: 'pg/timestamp-string@1'; readonly nullable: true; @@ -151,6 +167,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; @@ -160,6 +180,14 @@ export type AggregateTypes = { readonly output: 'pg/time-temporal@1'; readonly nullable: true; }; + readonly 'pg/timestamp-date@1': { + readonly output: 'pg/timestamp-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-instant@1': { + readonly output: 'pg/timestamp-instant@1'; + readonly nullable: true; + }; readonly 'pg/timestamp-string@1': { readonly output: 'pg/timestamp-string@1'; readonly nullable: true; @@ -203,6 +231,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; diff --git a/test/integration/test/fixtures/contract.d.ts b/test/integration/test/fixtures/contract.d.ts index a6ed1651533f..f7f04d0c7b46 100644 --- a/test/integration/test/fixtures/contract.d.ts +++ b/test/integration/test/fixtures/contract.d.ts @@ -42,6 +42,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; @@ -91,6 +95,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; @@ -100,6 +108,14 @@ export type AggregateTypes = { readonly output: 'pg/time-temporal@1'; readonly nullable: true; }; + readonly 'pg/timestamp-date@1': { + readonly output: 'pg/timestamp-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-instant@1': { + readonly output: 'pg/timestamp-instant@1'; + readonly nullable: true; + }; readonly 'pg/timestamp-string@1': { readonly output: 'pg/timestamp-string@1'; readonly nullable: true; @@ -151,6 +167,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; @@ -160,6 +180,14 @@ export type AggregateTypes = { readonly output: 'pg/time-temporal@1'; readonly nullable: true; }; + readonly 'pg/timestamp-date@1': { + readonly output: 'pg/timestamp-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-instant@1': { + readonly output: 'pg/timestamp-instant@1'; + readonly nullable: true; + }; readonly 'pg/timestamp-string@1': { readonly output: 'pg/timestamp-string@1'; readonly nullable: true; @@ -203,6 +231,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; diff --git a/test/integration/test/namespaced-accessors/fixtures/generated/contract.d.ts b/test/integration/test/namespaced-accessors/fixtures/generated/contract.d.ts index d9eb2abfc44b..e4b9cd5dccbc 100644 --- a/test/integration/test/namespaced-accessors/fixtures/generated/contract.d.ts +++ b/test/integration/test/namespaced-accessors/fixtures/generated/contract.d.ts @@ -42,6 +42,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; @@ -91,6 +95,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; @@ -100,6 +108,14 @@ export type AggregateTypes = { readonly output: 'pg/time-temporal@1'; readonly nullable: true; }; + readonly 'pg/timestamp-date@1': { + readonly output: 'pg/timestamp-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-instant@1': { + readonly output: 'pg/timestamp-instant@1'; + readonly nullable: true; + }; readonly 'pg/timestamp-string@1': { readonly output: 'pg/timestamp-string@1'; readonly nullable: true; @@ -151,6 +167,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; @@ -160,6 +180,14 @@ export type AggregateTypes = { readonly output: 'pg/time-temporal@1'; readonly nullable: true; }; + readonly 'pg/timestamp-date@1': { + readonly output: 'pg/timestamp-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-instant@1': { + readonly output: 'pg/timestamp-instant@1'; + readonly nullable: true; + }; readonly 'pg/timestamp-string@1': { readonly output: 'pg/timestamp-string@1'; readonly nullable: true; @@ -203,6 +231,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; diff --git a/test/integration/test/planner-golden/fixtures/postgres/generated/contract.d.ts b/test/integration/test/planner-golden/fixtures/postgres/generated/contract.d.ts index 9819eca2b0a6..f57107eb2aae 100644 --- a/test/integration/test/planner-golden/fixtures/postgres/generated/contract.d.ts +++ b/test/integration/test/planner-golden/fixtures/postgres/generated/contract.d.ts @@ -51,6 +51,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; @@ -100,6 +104,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; @@ -109,6 +117,14 @@ export type AggregateTypes = { readonly output: 'pg/time-temporal@1'; readonly nullable: true; }; + readonly 'pg/timestamp-date@1': { + readonly output: 'pg/timestamp-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-instant@1': { + readonly output: 'pg/timestamp-instant@1'; + readonly nullable: true; + }; readonly 'pg/timestamp-string@1': { readonly output: 'pg/timestamp-string@1'; readonly nullable: true; @@ -160,6 +176,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; @@ -169,6 +189,14 @@ export type AggregateTypes = { readonly output: 'pg/time-temporal@1'; readonly nullable: true; }; + readonly 'pg/timestamp-date@1': { + readonly output: 'pg/timestamp-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-instant@1': { + readonly output: 'pg/timestamp-instant@1'; + readonly nullable: true; + }; readonly 'pg/timestamp-string@1': { readonly output: 'pg/timestamp-string@1'; readonly nullable: true; @@ -212,6 +240,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; diff --git a/test/integration/test/ports/engines/queries/aggregation/avg/_fixture/decimal/generated/contract.d.ts b/test/integration/test/ports/engines/queries/aggregation/avg/_fixture/decimal/generated/contract.d.ts index 1ff533a6d9a1..6c6df1fc1559 100644 --- a/test/integration/test/ports/engines/queries/aggregation/avg/_fixture/decimal/generated/contract.d.ts +++ b/test/integration/test/ports/engines/queries/aggregation/avg/_fixture/decimal/generated/contract.d.ts @@ -42,6 +42,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; @@ -91,6 +95,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; @@ -100,6 +108,14 @@ export type AggregateTypes = { readonly output: 'pg/time-temporal@1'; readonly nullable: true; }; + readonly 'pg/timestamp-date@1': { + readonly output: 'pg/timestamp-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-instant@1': { + readonly output: 'pg/timestamp-instant@1'; + readonly nullable: true; + }; readonly 'pg/timestamp-string@1': { readonly output: 'pg/timestamp-string@1'; readonly nullable: true; @@ -151,6 +167,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; @@ -160,6 +180,14 @@ export type AggregateTypes = { readonly output: 'pg/time-temporal@1'; readonly nullable: true; }; + readonly 'pg/timestamp-date@1': { + readonly output: 'pg/timestamp-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-instant@1': { + readonly output: 'pg/timestamp-instant@1'; + readonly nullable: true; + }; readonly 'pg/timestamp-string@1': { readonly output: 'pg/timestamp-string@1'; readonly nullable: true; @@ -203,6 +231,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; diff --git a/test/integration/test/ports/engines/queries/aggregation/avg/_fixture/numeric/generated/contract.d.ts b/test/integration/test/ports/engines/queries/aggregation/avg/_fixture/numeric/generated/contract.d.ts index b40f191cc247..f62f8dfc0029 100644 --- a/test/integration/test/ports/engines/queries/aggregation/avg/_fixture/numeric/generated/contract.d.ts +++ b/test/integration/test/ports/engines/queries/aggregation/avg/_fixture/numeric/generated/contract.d.ts @@ -42,6 +42,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; @@ -91,6 +95,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; @@ -100,6 +108,14 @@ export type AggregateTypes = { readonly output: 'pg/time-temporal@1'; readonly nullable: true; }; + readonly 'pg/timestamp-date@1': { + readonly output: 'pg/timestamp-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-instant@1': { + readonly output: 'pg/timestamp-instant@1'; + readonly nullable: true; + }; readonly 'pg/timestamp-string@1': { readonly output: 'pg/timestamp-string@1'; readonly nullable: true; @@ -151,6 +167,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; @@ -160,6 +180,14 @@ export type AggregateTypes = { readonly output: 'pg/time-temporal@1'; readonly nullable: true; }; + readonly 'pg/timestamp-date@1': { + readonly output: 'pg/timestamp-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-instant@1': { + readonly output: 'pg/timestamp-instant@1'; + readonly nullable: true; + }; readonly 'pg/timestamp-string@1': { readonly output: 'pg/timestamp-string@1'; readonly nullable: true; @@ -203,6 +231,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; diff --git a/test/integration/test/ports/engines/queries/aggregation/count/_fixture/generated/contract.d.ts b/test/integration/test/ports/engines/queries/aggregation/count/_fixture/generated/contract.d.ts index cf24259e9beb..f7a2f4f95ce5 100644 --- a/test/integration/test/ports/engines/queries/aggregation/count/_fixture/generated/contract.d.ts +++ b/test/integration/test/ports/engines/queries/aggregation/count/_fixture/generated/contract.d.ts @@ -42,6 +42,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; @@ -91,6 +95,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; @@ -100,6 +108,14 @@ export type AggregateTypes = { readonly output: 'pg/time-temporal@1'; readonly nullable: true; }; + readonly 'pg/timestamp-date@1': { + readonly output: 'pg/timestamp-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-instant@1': { + readonly output: 'pg/timestamp-instant@1'; + readonly nullable: true; + }; readonly 'pg/timestamp-string@1': { readonly output: 'pg/timestamp-string@1'; readonly nullable: true; @@ -151,6 +167,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; @@ -160,6 +180,14 @@ export type AggregateTypes = { readonly output: 'pg/time-temporal@1'; readonly nullable: true; }; + readonly 'pg/timestamp-date@1': { + readonly output: 'pg/timestamp-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-instant@1': { + readonly output: 'pg/timestamp-instant@1'; + readonly nullable: true; + }; readonly 'pg/timestamp-string@1': { readonly output: 'pg/timestamp-string@1'; readonly nullable: true; @@ -203,6 +231,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; diff --git a/test/integration/test/ports/engines/queries/aggregation/group_by/_fixture/main/generated/contract.d.ts b/test/integration/test/ports/engines/queries/aggregation/group_by/_fixture/main/generated/contract.d.ts index 9e45bbdfb0e0..0a3850f07ce1 100644 --- a/test/integration/test/ports/engines/queries/aggregation/group_by/_fixture/main/generated/contract.d.ts +++ b/test/integration/test/ports/engines/queries/aggregation/group_by/_fixture/main/generated/contract.d.ts @@ -42,6 +42,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; @@ -91,6 +95,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; @@ -100,6 +108,14 @@ export type AggregateTypes = { readonly output: 'pg/time-temporal@1'; readonly nullable: true; }; + readonly 'pg/timestamp-date@1': { + readonly output: 'pg/timestamp-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-instant@1': { + readonly output: 'pg/timestamp-instant@1'; + readonly nullable: true; + }; readonly 'pg/timestamp-string@1': { readonly output: 'pg/timestamp-string@1'; readonly nullable: true; @@ -151,6 +167,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; @@ -160,6 +180,14 @@ export type AggregateTypes = { readonly output: 'pg/time-temporal@1'; readonly nullable: true; }; + readonly 'pg/timestamp-date@1': { + readonly output: 'pg/timestamp-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-instant@1': { + readonly output: 'pg/timestamp-instant@1'; + readonly nullable: true; + }; readonly 'pg/timestamp-string@1': { readonly output: 'pg/timestamp-string@1'; readonly nullable: true; @@ -203,6 +231,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; diff --git a/test/integration/test/ports/engines/queries/aggregation/group_by/_fixture/regression-21789/generated/contract.d.ts b/test/integration/test/ports/engines/queries/aggregation/group_by/_fixture/regression-21789/generated/contract.d.ts index cb88044c5fa5..2f2982eabc53 100644 --- a/test/integration/test/ports/engines/queries/aggregation/group_by/_fixture/regression-21789/generated/contract.d.ts +++ b/test/integration/test/ports/engines/queries/aggregation/group_by/_fixture/regression-21789/generated/contract.d.ts @@ -42,6 +42,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; @@ -91,6 +95,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; @@ -100,6 +108,14 @@ export type AggregateTypes = { readonly output: 'pg/time-temporal@1'; readonly nullable: true; }; + readonly 'pg/timestamp-date@1': { + readonly output: 'pg/timestamp-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-instant@1': { + readonly output: 'pg/timestamp-instant@1'; + readonly nullable: true; + }; readonly 'pg/timestamp-string@1': { readonly output: 'pg/timestamp-string@1'; readonly nullable: true; @@ -151,6 +167,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; @@ -160,6 +180,14 @@ export type AggregateTypes = { readonly output: 'pg/time-temporal@1'; readonly nullable: true; }; + readonly 'pg/timestamp-date@1': { + readonly output: 'pg/timestamp-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-instant@1': { + readonly output: 'pg/timestamp-instant@1'; + readonly nullable: true; + }; readonly 'pg/timestamp-string@1': { readonly output: 'pg/timestamp-string@1'; readonly nullable: true; @@ -203,6 +231,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; diff --git a/test/integration/test/ports/engines/queries/aggregation/group_by_having/_fixture/common/generated/contract.d.ts b/test/integration/test/ports/engines/queries/aggregation/group_by_having/_fixture/common/generated/contract.d.ts index ac360de4d4d3..6ec0331c00bf 100644 --- a/test/integration/test/ports/engines/queries/aggregation/group_by_having/_fixture/common/generated/contract.d.ts +++ b/test/integration/test/ports/engines/queries/aggregation/group_by_having/_fixture/common/generated/contract.d.ts @@ -42,6 +42,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; @@ -91,6 +95,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; @@ -100,6 +108,14 @@ export type AggregateTypes = { readonly output: 'pg/time-temporal@1'; readonly nullable: true; }; + readonly 'pg/timestamp-date@1': { + readonly output: 'pg/timestamp-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-instant@1': { + readonly output: 'pg/timestamp-instant@1'; + readonly nullable: true; + }; readonly 'pg/timestamp-string@1': { readonly output: 'pg/timestamp-string@1'; readonly nullable: true; @@ -151,6 +167,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; @@ -160,6 +180,14 @@ export type AggregateTypes = { readonly output: 'pg/time-temporal@1'; readonly nullable: true; }; + readonly 'pg/timestamp-date@1': { + readonly output: 'pg/timestamp-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-instant@1': { + readonly output: 'pg/timestamp-instant@1'; + readonly nullable: true; + }; readonly 'pg/timestamp-string@1': { readonly output: 'pg/timestamp-string@1'; readonly nullable: true; @@ -203,6 +231,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; diff --git a/test/integration/test/ports/engines/queries/aggregation/group_by_having/_fixture/decimal/generated/contract.d.ts b/test/integration/test/ports/engines/queries/aggregation/group_by_having/_fixture/decimal/generated/contract.d.ts index 4d36ded14a38..6974240b4843 100644 --- a/test/integration/test/ports/engines/queries/aggregation/group_by_having/_fixture/decimal/generated/contract.d.ts +++ b/test/integration/test/ports/engines/queries/aggregation/group_by_having/_fixture/decimal/generated/contract.d.ts @@ -42,6 +42,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; @@ -91,6 +95,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; @@ -100,6 +108,14 @@ export type AggregateTypes = { readonly output: 'pg/time-temporal@1'; readonly nullable: true; }; + readonly 'pg/timestamp-date@1': { + readonly output: 'pg/timestamp-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-instant@1': { + readonly output: 'pg/timestamp-instant@1'; + readonly nullable: true; + }; readonly 'pg/timestamp-string@1': { readonly output: 'pg/timestamp-string@1'; readonly nullable: true; @@ -151,6 +167,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; @@ -160,6 +180,14 @@ export type AggregateTypes = { readonly output: 'pg/time-temporal@1'; readonly nullable: true; }; + readonly 'pg/timestamp-date@1': { + readonly output: 'pg/timestamp-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-instant@1': { + readonly output: 'pg/timestamp-instant@1'; + readonly nullable: true; + }; readonly 'pg/timestamp-string@1': { readonly output: 'pg/timestamp-string@1'; readonly nullable: true; @@ -203,6 +231,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; diff --git a/test/integration/test/ports/engines/queries/aggregation/many_count_relation/_fixture/compound/generated/contract.d.ts b/test/integration/test/ports/engines/queries/aggregation/many_count_relation/_fixture/compound/generated/contract.d.ts index dcf67076ee4f..d3af49d9e52f 100644 --- a/test/integration/test/ports/engines/queries/aggregation/many_count_relation/_fixture/compound/generated/contract.d.ts +++ b/test/integration/test/ports/engines/queries/aggregation/many_count_relation/_fixture/compound/generated/contract.d.ts @@ -42,6 +42,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; @@ -91,6 +95,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; @@ -100,6 +108,14 @@ export type AggregateTypes = { readonly output: 'pg/time-temporal@1'; readonly nullable: true; }; + readonly 'pg/timestamp-date@1': { + readonly output: 'pg/timestamp-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-instant@1': { + readonly output: 'pg/timestamp-instant@1'; + readonly nullable: true; + }; readonly 'pg/timestamp-string@1': { readonly output: 'pg/timestamp-string@1'; readonly nullable: true; @@ -151,6 +167,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; @@ -160,6 +180,14 @@ export type AggregateTypes = { readonly output: 'pg/time-temporal@1'; readonly nullable: true; }; + readonly 'pg/timestamp-date@1': { + readonly output: 'pg/timestamp-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-instant@1': { + readonly output: 'pg/timestamp-instant@1'; + readonly nullable: true; + }; readonly 'pg/timestamp-string@1': { readonly output: 'pg/timestamp-string@1'; readonly nullable: true; @@ -203,6 +231,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; diff --git a/test/integration/test/ports/engines/queries/aggregation/many_count_relation/_fixture/generated/contract.d.ts b/test/integration/test/ports/engines/queries/aggregation/many_count_relation/_fixture/generated/contract.d.ts index 6a12f4eaaacd..b2cf70e906aa 100644 --- a/test/integration/test/ports/engines/queries/aggregation/many_count_relation/_fixture/generated/contract.d.ts +++ b/test/integration/test/ports/engines/queries/aggregation/many_count_relation/_fixture/generated/contract.d.ts @@ -42,6 +42,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; @@ -91,6 +95,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; @@ -100,6 +108,14 @@ export type AggregateTypes = { readonly output: 'pg/time-temporal@1'; readonly nullable: true; }; + readonly 'pg/timestamp-date@1': { + readonly output: 'pg/timestamp-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-instant@1': { + readonly output: 'pg/timestamp-instant@1'; + readonly nullable: true; + }; readonly 'pg/timestamp-string@1': { readonly output: 'pg/timestamp-string@1'; readonly nullable: true; @@ -151,6 +167,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; @@ -160,6 +180,14 @@ export type AggregateTypes = { readonly output: 'pg/time-temporal@1'; readonly nullable: true; }; + readonly 'pg/timestamp-date@1': { + readonly output: 'pg/timestamp-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-instant@1': { + readonly output: 'pg/timestamp-instant@1'; + readonly nullable: true; + }; readonly 'pg/timestamp-string@1': { readonly output: 'pg/timestamp-string@1'; readonly nullable: true; @@ -203,6 +231,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; diff --git a/test/integration/test/ports/engines/queries/aggregation/many_count_relation/_fixture/nested/generated/contract.d.ts b/test/integration/test/ports/engines/queries/aggregation/many_count_relation/_fixture/nested/generated/contract.d.ts index 02dc11d0e377..239906d2ec26 100644 --- a/test/integration/test/ports/engines/queries/aggregation/many_count_relation/_fixture/nested/generated/contract.d.ts +++ b/test/integration/test/ports/engines/queries/aggregation/many_count_relation/_fixture/nested/generated/contract.d.ts @@ -42,6 +42,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; @@ -91,6 +95,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; @@ -100,6 +108,14 @@ export type AggregateTypes = { readonly output: 'pg/time-temporal@1'; readonly nullable: true; }; + readonly 'pg/timestamp-date@1': { + readonly output: 'pg/timestamp-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-instant@1': { + readonly output: 'pg/timestamp-instant@1'; + readonly nullable: true; + }; readonly 'pg/timestamp-string@1': { readonly output: 'pg/timestamp-string@1'; readonly nullable: true; @@ -151,6 +167,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; @@ -160,6 +180,14 @@ export type AggregateTypes = { readonly output: 'pg/time-temporal@1'; readonly nullable: true; }; + readonly 'pg/timestamp-date@1': { + readonly output: 'pg/timestamp-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-instant@1': { + readonly output: 'pg/timestamp-instant@1'; + readonly nullable: true; + }; readonly 'pg/timestamp-string@1': { readonly output: 'pg/timestamp-string@1'; readonly nullable: true; @@ -203,6 +231,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; diff --git a/test/integration/test/ports/engines/queries/aggregation/many_count_relation/_fixture/self/generated/contract.d.ts b/test/integration/test/ports/engines/queries/aggregation/many_count_relation/_fixture/self/generated/contract.d.ts index 85f7eb4d6b2c..6bc90cc15bb3 100644 --- a/test/integration/test/ports/engines/queries/aggregation/many_count_relation/_fixture/self/generated/contract.d.ts +++ b/test/integration/test/ports/engines/queries/aggregation/many_count_relation/_fixture/self/generated/contract.d.ts @@ -42,6 +42,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; @@ -91,6 +95,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; @@ -100,6 +108,14 @@ export type AggregateTypes = { readonly output: 'pg/time-temporal@1'; readonly nullable: true; }; + readonly 'pg/timestamp-date@1': { + readonly output: 'pg/timestamp-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-instant@1': { + readonly output: 'pg/timestamp-instant@1'; + readonly nullable: true; + }; readonly 'pg/timestamp-string@1': { readonly output: 'pg/timestamp-string@1'; readonly nullable: true; @@ -151,6 +167,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; @@ -160,6 +180,14 @@ export type AggregateTypes = { readonly output: 'pg/time-temporal@1'; readonly nullable: true; }; + readonly 'pg/timestamp-date@1': { + readonly output: 'pg/timestamp-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-instant@1': { + readonly output: 'pg/timestamp-instant@1'; + readonly nullable: true; + }; readonly 'pg/timestamp-string@1': { readonly output: 'pg/timestamp-string@1'; readonly nullable: true; @@ -203,6 +231,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; diff --git a/test/integration/test/ports/engines/queries/aggregation/max/_fixture/common/generated/contract.d.ts b/test/integration/test/ports/engines/queries/aggregation/max/_fixture/common/generated/contract.d.ts index b8784e99954f..341641c1724f 100644 --- a/test/integration/test/ports/engines/queries/aggregation/max/_fixture/common/generated/contract.d.ts +++ b/test/integration/test/ports/engines/queries/aggregation/max/_fixture/common/generated/contract.d.ts @@ -42,6 +42,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; @@ -91,6 +95,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; @@ -100,6 +108,14 @@ export type AggregateTypes = { readonly output: 'pg/time-temporal@1'; readonly nullable: true; }; + readonly 'pg/timestamp-date@1': { + readonly output: 'pg/timestamp-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-instant@1': { + readonly output: 'pg/timestamp-instant@1'; + readonly nullable: true; + }; readonly 'pg/timestamp-string@1': { readonly output: 'pg/timestamp-string@1'; readonly nullable: true; @@ -151,6 +167,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; @@ -160,6 +180,14 @@ export type AggregateTypes = { readonly output: 'pg/time-temporal@1'; readonly nullable: true; }; + readonly 'pg/timestamp-date@1': { + readonly output: 'pg/timestamp-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-instant@1': { + readonly output: 'pg/timestamp-instant@1'; + readonly nullable: true; + }; readonly 'pg/timestamp-string@1': { readonly output: 'pg/timestamp-string@1'; readonly nullable: true; @@ -203,6 +231,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; diff --git a/test/integration/test/ports/engines/queries/aggregation/max/_fixture/decimal/generated/contract.d.ts b/test/integration/test/ports/engines/queries/aggregation/max/_fixture/decimal/generated/contract.d.ts index 1ff533a6d9a1..6c6df1fc1559 100644 --- a/test/integration/test/ports/engines/queries/aggregation/max/_fixture/decimal/generated/contract.d.ts +++ b/test/integration/test/ports/engines/queries/aggregation/max/_fixture/decimal/generated/contract.d.ts @@ -42,6 +42,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; @@ -91,6 +95,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; @@ -100,6 +108,14 @@ export type AggregateTypes = { readonly output: 'pg/time-temporal@1'; readonly nullable: true; }; + readonly 'pg/timestamp-date@1': { + readonly output: 'pg/timestamp-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-instant@1': { + readonly output: 'pg/timestamp-instant@1'; + readonly nullable: true; + }; readonly 'pg/timestamp-string@1': { readonly output: 'pg/timestamp-string@1'; readonly nullable: true; @@ -151,6 +167,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; @@ -160,6 +180,14 @@ export type AggregateTypes = { readonly output: 'pg/time-temporal@1'; readonly nullable: true; }; + readonly 'pg/timestamp-date@1': { + readonly output: 'pg/timestamp-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-instant@1': { + readonly output: 'pg/timestamp-instant@1'; + readonly nullable: true; + }; readonly 'pg/timestamp-string@1': { readonly output: 'pg/timestamp-string@1'; readonly nullable: true; @@ -203,6 +231,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; diff --git a/test/integration/test/ports/engines/queries/aggregation/min/_fixture/common/generated/contract.d.ts b/test/integration/test/ports/engines/queries/aggregation/min/_fixture/common/generated/contract.d.ts index b8784e99954f..341641c1724f 100644 --- a/test/integration/test/ports/engines/queries/aggregation/min/_fixture/common/generated/contract.d.ts +++ b/test/integration/test/ports/engines/queries/aggregation/min/_fixture/common/generated/contract.d.ts @@ -42,6 +42,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; @@ -91,6 +95,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; @@ -100,6 +108,14 @@ export type AggregateTypes = { readonly output: 'pg/time-temporal@1'; readonly nullable: true; }; + readonly 'pg/timestamp-date@1': { + readonly output: 'pg/timestamp-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-instant@1': { + readonly output: 'pg/timestamp-instant@1'; + readonly nullable: true; + }; readonly 'pg/timestamp-string@1': { readonly output: 'pg/timestamp-string@1'; readonly nullable: true; @@ -151,6 +167,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; @@ -160,6 +180,14 @@ export type AggregateTypes = { readonly output: 'pg/time-temporal@1'; readonly nullable: true; }; + readonly 'pg/timestamp-date@1': { + readonly output: 'pg/timestamp-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-instant@1': { + readonly output: 'pg/timestamp-instant@1'; + readonly nullable: true; + }; readonly 'pg/timestamp-string@1': { readonly output: 'pg/timestamp-string@1'; readonly nullable: true; @@ -203,6 +231,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; diff --git a/test/integration/test/ports/engines/queries/aggregation/min/_fixture/decimal/generated/contract.d.ts b/test/integration/test/ports/engines/queries/aggregation/min/_fixture/decimal/generated/contract.d.ts index 1ff533a6d9a1..6c6df1fc1559 100644 --- a/test/integration/test/ports/engines/queries/aggregation/min/_fixture/decimal/generated/contract.d.ts +++ b/test/integration/test/ports/engines/queries/aggregation/min/_fixture/decimal/generated/contract.d.ts @@ -42,6 +42,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; @@ -91,6 +95,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; @@ -100,6 +108,14 @@ export type AggregateTypes = { readonly output: 'pg/time-temporal@1'; readonly nullable: true; }; + readonly 'pg/timestamp-date@1': { + readonly output: 'pg/timestamp-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-instant@1': { + readonly output: 'pg/timestamp-instant@1'; + readonly nullable: true; + }; readonly 'pg/timestamp-string@1': { readonly output: 'pg/timestamp-string@1'; readonly nullable: true; @@ -151,6 +167,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; @@ -160,6 +180,14 @@ export type AggregateTypes = { readonly output: 'pg/time-temporal@1'; readonly nullable: true; }; + readonly 'pg/timestamp-date@1': { + readonly output: 'pg/timestamp-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-instant@1': { + readonly output: 'pg/timestamp-instant@1'; + readonly nullable: true; + }; readonly 'pg/timestamp-string@1': { readonly output: 'pg/timestamp-string@1'; readonly nullable: true; @@ -203,6 +231,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; diff --git a/test/integration/test/ports/engines/queries/aggregation/sum/_fixture/decimal/generated/contract.d.ts b/test/integration/test/ports/engines/queries/aggregation/sum/_fixture/decimal/generated/contract.d.ts index 1ff533a6d9a1..6c6df1fc1559 100644 --- a/test/integration/test/ports/engines/queries/aggregation/sum/_fixture/decimal/generated/contract.d.ts +++ b/test/integration/test/ports/engines/queries/aggregation/sum/_fixture/decimal/generated/contract.d.ts @@ -42,6 +42,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; @@ -91,6 +95,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; @@ -100,6 +108,14 @@ export type AggregateTypes = { readonly output: 'pg/time-temporal@1'; readonly nullable: true; }; + readonly 'pg/timestamp-date@1': { + readonly output: 'pg/timestamp-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-instant@1': { + readonly output: 'pg/timestamp-instant@1'; + readonly nullable: true; + }; readonly 'pg/timestamp-string@1': { readonly output: 'pg/timestamp-string@1'; readonly nullable: true; @@ -151,6 +167,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; @@ -160,6 +180,14 @@ export type AggregateTypes = { readonly output: 'pg/time-temporal@1'; readonly nullable: true; }; + readonly 'pg/timestamp-date@1': { + readonly output: 'pg/timestamp-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-instant@1': { + readonly output: 'pg/timestamp-instant@1'; + readonly nullable: true; + }; readonly 'pg/timestamp-string@1': { readonly output: 'pg/timestamp-string@1'; readonly nullable: true; @@ -203,6 +231,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; diff --git a/test/integration/test/ports/engines/queries/aggregation/sum/_fixture/numeric/generated/contract.d.ts b/test/integration/test/ports/engines/queries/aggregation/sum/_fixture/numeric/generated/contract.d.ts index b40f191cc247..f62f8dfc0029 100644 --- a/test/integration/test/ports/engines/queries/aggregation/sum/_fixture/numeric/generated/contract.d.ts +++ b/test/integration/test/ports/engines/queries/aggregation/sum/_fixture/numeric/generated/contract.d.ts @@ -42,6 +42,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; @@ -91,6 +95,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; @@ -100,6 +108,14 @@ export type AggregateTypes = { readonly output: 'pg/time-temporal@1'; readonly nullable: true; }; + readonly 'pg/timestamp-date@1': { + readonly output: 'pg/timestamp-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-instant@1': { + readonly output: 'pg/timestamp-instant@1'; + readonly nullable: true; + }; readonly 'pg/timestamp-string@1': { readonly output: 'pg/timestamp-string@1'; readonly nullable: true; @@ -151,6 +167,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; @@ -160,6 +180,14 @@ export type AggregateTypes = { readonly output: 'pg/time-temporal@1'; readonly nullable: true; }; + readonly 'pg/timestamp-date@1': { + readonly output: 'pg/timestamp-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-instant@1': { + readonly output: 'pg/timestamp-instant@1'; + readonly nullable: true; + }; readonly 'pg/timestamp-string@1': { readonly output: 'pg/timestamp-string@1'; readonly nullable: true; @@ -203,6 +231,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; diff --git a/test/integration/test/ports/engines/queries/aggregation/uniq-count-relation/_fixture/base/generated/contract.d.ts b/test/integration/test/ports/engines/queries/aggregation/uniq-count-relation/_fixture/base/generated/contract.d.ts index 6a12f4eaaacd..b2cf70e906aa 100644 --- a/test/integration/test/ports/engines/queries/aggregation/uniq-count-relation/_fixture/base/generated/contract.d.ts +++ b/test/integration/test/ports/engines/queries/aggregation/uniq-count-relation/_fixture/base/generated/contract.d.ts @@ -42,6 +42,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; @@ -91,6 +95,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; @@ -100,6 +108,14 @@ export type AggregateTypes = { readonly output: 'pg/time-temporal@1'; readonly nullable: true; }; + readonly 'pg/timestamp-date@1': { + readonly output: 'pg/timestamp-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-instant@1': { + readonly output: 'pg/timestamp-instant@1'; + readonly nullable: true; + }; readonly 'pg/timestamp-string@1': { readonly output: 'pg/timestamp-string@1'; readonly nullable: true; @@ -151,6 +167,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; @@ -160,6 +180,14 @@ export type AggregateTypes = { readonly output: 'pg/time-temporal@1'; readonly nullable: true; }; + readonly 'pg/timestamp-date@1': { + readonly output: 'pg/timestamp-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-instant@1': { + readonly output: 'pg/timestamp-instant@1'; + readonly nullable: true; + }; readonly 'pg/timestamp-string@1': { readonly output: 'pg/timestamp-string@1'; readonly nullable: true; @@ -203,6 +231,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; diff --git a/test/integration/test/ports/engines/queries/aggregation/uniq-count-relation/_fixture/nested/generated/contract.d.ts b/test/integration/test/ports/engines/queries/aggregation/uniq-count-relation/_fixture/nested/generated/contract.d.ts index 02dc11d0e377..239906d2ec26 100644 --- a/test/integration/test/ports/engines/queries/aggregation/uniq-count-relation/_fixture/nested/generated/contract.d.ts +++ b/test/integration/test/ports/engines/queries/aggregation/uniq-count-relation/_fixture/nested/generated/contract.d.ts @@ -42,6 +42,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; @@ -91,6 +95,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; @@ -100,6 +108,14 @@ export type AggregateTypes = { readonly output: 'pg/time-temporal@1'; readonly nullable: true; }; + readonly 'pg/timestamp-date@1': { + readonly output: 'pg/timestamp-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-instant@1': { + readonly output: 'pg/timestamp-instant@1'; + readonly nullable: true; + }; readonly 'pg/timestamp-string@1': { readonly output: 'pg/timestamp-string@1'; readonly nullable: true; @@ -151,6 +167,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; @@ -160,6 +180,14 @@ export type AggregateTypes = { readonly output: 'pg/time-temporal@1'; readonly nullable: true; }; + readonly 'pg/timestamp-date@1': { + readonly output: 'pg/timestamp-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-instant@1': { + readonly output: 'pg/timestamp-instant@1'; + readonly nullable: true; + }; readonly 'pg/timestamp-string@1': { readonly output: 'pg/timestamp-string@1'; readonly nullable: true; @@ -203,6 +231,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; diff --git a/test/integration/test/ports/engines/queries/data_types/bigint/_fixture/generated/contract.d.ts b/test/integration/test/ports/engines/queries/data_types/bigint/_fixture/generated/contract.d.ts index cf24259e9beb..f7a2f4f95ce5 100644 --- a/test/integration/test/ports/engines/queries/data_types/bigint/_fixture/generated/contract.d.ts +++ b/test/integration/test/ports/engines/queries/data_types/bigint/_fixture/generated/contract.d.ts @@ -42,6 +42,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; @@ -91,6 +95,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; @@ -100,6 +108,14 @@ export type AggregateTypes = { readonly output: 'pg/time-temporal@1'; readonly nullable: true; }; + readonly 'pg/timestamp-date@1': { + readonly output: 'pg/timestamp-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-instant@1': { + readonly output: 'pg/timestamp-instant@1'; + readonly nullable: true; + }; readonly 'pg/timestamp-string@1': { readonly output: 'pg/timestamp-string@1'; readonly nullable: true; @@ -151,6 +167,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; @@ -160,6 +180,14 @@ export type AggregateTypes = { readonly output: 'pg/time-temporal@1'; readonly nullable: true; }; + readonly 'pg/timestamp-date@1': { + readonly output: 'pg/timestamp-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-instant@1': { + readonly output: 'pg/timestamp-instant@1'; + readonly nullable: true; + }; readonly 'pg/timestamp-string@1': { readonly output: 'pg/timestamp-string@1'; readonly nullable: true; @@ -203,6 +231,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; diff --git a/test/integration/test/ports/engines/queries/data_types/bool/_fixture/generated/contract.d.ts b/test/integration/test/ports/engines/queries/data_types/bool/_fixture/generated/contract.d.ts index cf24259e9beb..f7a2f4f95ce5 100644 --- a/test/integration/test/ports/engines/queries/data_types/bool/_fixture/generated/contract.d.ts +++ b/test/integration/test/ports/engines/queries/data_types/bool/_fixture/generated/contract.d.ts @@ -42,6 +42,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; @@ -91,6 +95,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; @@ -100,6 +108,14 @@ export type AggregateTypes = { readonly output: 'pg/time-temporal@1'; readonly nullable: true; }; + readonly 'pg/timestamp-date@1': { + readonly output: 'pg/timestamp-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-instant@1': { + readonly output: 'pg/timestamp-instant@1'; + readonly nullable: true; + }; readonly 'pg/timestamp-string@1': { readonly output: 'pg/timestamp-string@1'; readonly nullable: true; @@ -151,6 +167,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; @@ -160,6 +180,14 @@ export type AggregateTypes = { readonly output: 'pg/time-temporal@1'; readonly nullable: true; }; + readonly 'pg/timestamp-date@1': { + readonly output: 'pg/timestamp-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-instant@1': { + readonly output: 'pg/timestamp-instant@1'; + readonly nullable: true; + }; readonly 'pg/timestamp-string@1': { readonly output: 'pg/timestamp-string@1'; readonly nullable: true; @@ -203,6 +231,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; diff --git a/test/integration/test/ports/engines/queries/data_types/bytes/_fixture/relations/generated/contract.d.ts b/test/integration/test/ports/engines/queries/data_types/bytes/_fixture/relations/generated/contract.d.ts index 97890ba8fe1a..06ee8b61aed2 100644 --- a/test/integration/test/ports/engines/queries/data_types/bytes/_fixture/relations/generated/contract.d.ts +++ b/test/integration/test/ports/engines/queries/data_types/bytes/_fixture/relations/generated/contract.d.ts @@ -42,6 +42,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; @@ -91,6 +95,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; @@ -100,6 +108,14 @@ export type AggregateTypes = { readonly output: 'pg/time-temporal@1'; readonly nullable: true; }; + readonly 'pg/timestamp-date@1': { + readonly output: 'pg/timestamp-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-instant@1': { + readonly output: 'pg/timestamp-instant@1'; + readonly nullable: true; + }; readonly 'pg/timestamp-string@1': { readonly output: 'pg/timestamp-string@1'; readonly nullable: true; @@ -151,6 +167,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; @@ -160,6 +180,14 @@ export type AggregateTypes = { readonly output: 'pg/time-temporal@1'; readonly nullable: true; }; + readonly 'pg/timestamp-date@1': { + readonly output: 'pg/timestamp-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-instant@1': { + readonly output: 'pg/timestamp-instant@1'; + readonly nullable: true; + }; readonly 'pg/timestamp-string@1': { readonly output: 'pg/timestamp-string@1'; readonly nullable: true; @@ -203,6 +231,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; diff --git a/test/integration/test/ports/engines/queries/data_types/bytes/_fixture/scalars/generated/contract.d.ts b/test/integration/test/ports/engines/queries/data_types/bytes/_fixture/scalars/generated/contract.d.ts index cf24259e9beb..f7a2f4f95ce5 100644 --- a/test/integration/test/ports/engines/queries/data_types/bytes/_fixture/scalars/generated/contract.d.ts +++ b/test/integration/test/ports/engines/queries/data_types/bytes/_fixture/scalars/generated/contract.d.ts @@ -42,6 +42,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; @@ -91,6 +95,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; @@ -100,6 +108,14 @@ export type AggregateTypes = { readonly output: 'pg/time-temporal@1'; readonly nullable: true; }; + readonly 'pg/timestamp-date@1': { + readonly output: 'pg/timestamp-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-instant@1': { + readonly output: 'pg/timestamp-instant@1'; + readonly nullable: true; + }; readonly 'pg/timestamp-string@1': { readonly output: 'pg/timestamp-string@1'; readonly nullable: true; @@ -151,6 +167,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; @@ -160,6 +180,14 @@ export type AggregateTypes = { readonly output: 'pg/time-temporal@1'; readonly nullable: true; }; + readonly 'pg/timestamp-date@1': { + readonly output: 'pg/timestamp-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-instant@1': { + readonly output: 'pg/timestamp-instant@1'; + readonly nullable: true; + }; readonly 'pg/timestamp-string@1': { readonly output: 'pg/timestamp-string@1'; readonly nullable: true; @@ -203,6 +231,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; diff --git a/test/integration/test/ports/engines/queries/data_types/datetime/_fixture/generated/contract.d.ts b/test/integration/test/ports/engines/queries/data_types/datetime/_fixture/generated/contract.d.ts index cf24259e9beb..f7a2f4f95ce5 100644 --- a/test/integration/test/ports/engines/queries/data_types/datetime/_fixture/generated/contract.d.ts +++ b/test/integration/test/ports/engines/queries/data_types/datetime/_fixture/generated/contract.d.ts @@ -42,6 +42,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; @@ -91,6 +95,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; @@ -100,6 +108,14 @@ export type AggregateTypes = { readonly output: 'pg/time-temporal@1'; readonly nullable: true; }; + readonly 'pg/timestamp-date@1': { + readonly output: 'pg/timestamp-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-instant@1': { + readonly output: 'pg/timestamp-instant@1'; + readonly nullable: true; + }; readonly 'pg/timestamp-string@1': { readonly output: 'pg/timestamp-string@1'; readonly nullable: true; @@ -151,6 +167,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; @@ -160,6 +180,14 @@ export type AggregateTypes = { readonly output: 'pg/time-temporal@1'; readonly nullable: true; }; + readonly 'pg/timestamp-date@1': { + readonly output: 'pg/timestamp-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-instant@1': { + readonly output: 'pg/timestamp-instant@1'; + readonly nullable: true; + }; readonly 'pg/timestamp-string@1': { readonly output: 'pg/timestamp-string@1'; readonly nullable: true; @@ -203,6 +231,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; diff --git a/test/integration/test/ports/engines/queries/data_types/decimal/_fixture/generated/contract.d.ts b/test/integration/test/ports/engines/queries/data_types/decimal/_fixture/generated/contract.d.ts index 6636199576e6..e2d8eb53b59d 100644 --- a/test/integration/test/ports/engines/queries/data_types/decimal/_fixture/generated/contract.d.ts +++ b/test/integration/test/ports/engines/queries/data_types/decimal/_fixture/generated/contract.d.ts @@ -42,6 +42,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; @@ -91,6 +95,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; @@ -100,6 +108,14 @@ export type AggregateTypes = { readonly output: 'pg/time-temporal@1'; readonly nullable: true; }; + readonly 'pg/timestamp-date@1': { + readonly output: 'pg/timestamp-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-instant@1': { + readonly output: 'pg/timestamp-instant@1'; + readonly nullable: true; + }; readonly 'pg/timestamp-string@1': { readonly output: 'pg/timestamp-string@1'; readonly nullable: true; @@ -151,6 +167,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; @@ -160,6 +180,14 @@ export type AggregateTypes = { readonly output: 'pg/time-temporal@1'; readonly nullable: true; }; + readonly 'pg/timestamp-date@1': { + readonly output: 'pg/timestamp-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-instant@1': { + readonly output: 'pg/timestamp-instant@1'; + readonly nullable: true; + }; readonly 'pg/timestamp-string@1': { readonly output: 'pg/timestamp-string@1'; readonly nullable: true; @@ -203,6 +231,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; diff --git a/test/integration/test/ports/engines/queries/data_types/enum_type/_fixture/postgres/generated/contract.d.ts b/test/integration/test/ports/engines/queries/data_types/enum_type/_fixture/postgres/generated/contract.d.ts index 6e84ee53985e..7a627cb1b347 100644 --- a/test/integration/test/ports/engines/queries/data_types/enum_type/_fixture/postgres/generated/contract.d.ts +++ b/test/integration/test/ports/engines/queries/data_types/enum_type/_fixture/postgres/generated/contract.d.ts @@ -42,6 +42,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; @@ -91,6 +95,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; @@ -100,6 +108,14 @@ export type AggregateTypes = { readonly output: 'pg/time-temporal@1'; readonly nullable: true; }; + readonly 'pg/timestamp-date@1': { + readonly output: 'pg/timestamp-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-instant@1': { + readonly output: 'pg/timestamp-instant@1'; + readonly nullable: true; + }; readonly 'pg/timestamp-string@1': { readonly output: 'pg/timestamp-string@1'; readonly nullable: true; @@ -151,6 +167,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; @@ -160,6 +180,14 @@ export type AggregateTypes = { readonly output: 'pg/time-temporal@1'; readonly nullable: true; }; + readonly 'pg/timestamp-date@1': { + readonly output: 'pg/timestamp-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-instant@1': { + readonly output: 'pg/timestamp-instant@1'; + readonly nullable: true; + }; readonly 'pg/timestamp-string@1': { readonly output: 'pg/timestamp-string@1'; readonly nullable: true; @@ -203,6 +231,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; diff --git a/test/integration/test/ports/engines/queries/data_types/float/_fixture/generated/contract.d.ts b/test/integration/test/ports/engines/queries/data_types/float/_fixture/generated/contract.d.ts index cf24259e9beb..f7a2f4f95ce5 100644 --- a/test/integration/test/ports/engines/queries/data_types/float/_fixture/generated/contract.d.ts +++ b/test/integration/test/ports/engines/queries/data_types/float/_fixture/generated/contract.d.ts @@ -42,6 +42,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; @@ -91,6 +95,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; @@ -100,6 +108,14 @@ export type AggregateTypes = { readonly output: 'pg/time-temporal@1'; readonly nullable: true; }; + readonly 'pg/timestamp-date@1': { + readonly output: 'pg/timestamp-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-instant@1': { + readonly output: 'pg/timestamp-instant@1'; + readonly nullable: true; + }; readonly 'pg/timestamp-string@1': { readonly output: 'pg/timestamp-string@1'; readonly nullable: true; @@ -151,6 +167,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; @@ -160,6 +180,14 @@ export type AggregateTypes = { readonly output: 'pg/time-temporal@1'; readonly nullable: true; }; + readonly 'pg/timestamp-date@1': { + readonly output: 'pg/timestamp-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-instant@1': { + readonly output: 'pg/timestamp-instant@1'; + readonly nullable: true; + }; readonly 'pg/timestamp-string@1': { readonly output: 'pg/timestamp-string@1'; readonly nullable: true; @@ -203,6 +231,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; diff --git a/test/integration/test/ports/engines/queries/data_types/int/_fixture/generated/contract.d.ts b/test/integration/test/ports/engines/queries/data_types/int/_fixture/generated/contract.d.ts index cf24259e9beb..f7a2f4f95ce5 100644 --- a/test/integration/test/ports/engines/queries/data_types/int/_fixture/generated/contract.d.ts +++ b/test/integration/test/ports/engines/queries/data_types/int/_fixture/generated/contract.d.ts @@ -42,6 +42,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; @@ -91,6 +95,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; @@ -100,6 +108,14 @@ export type AggregateTypes = { readonly output: 'pg/time-temporal@1'; readonly nullable: true; }; + readonly 'pg/timestamp-date@1': { + readonly output: 'pg/timestamp-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-instant@1': { + readonly output: 'pg/timestamp-instant@1'; + readonly nullable: true; + }; readonly 'pg/timestamp-string@1': { readonly output: 'pg/timestamp-string@1'; readonly nullable: true; @@ -151,6 +167,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; @@ -160,6 +180,14 @@ export type AggregateTypes = { readonly output: 'pg/time-temporal@1'; readonly nullable: true; }; + readonly 'pg/timestamp-date@1': { + readonly output: 'pg/timestamp-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-instant@1': { + readonly output: 'pg/timestamp-instant@1'; + readonly nullable: true; + }; readonly 'pg/timestamp-string@1': { readonly output: 'pg/timestamp-string@1'; readonly nullable: true; @@ -203,6 +231,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; diff --git a/test/integration/test/ports/engines/queries/data_types/json/_fixture/scalar/generated/contract.d.ts b/test/integration/test/ports/engines/queries/data_types/json/_fixture/scalar/generated/contract.d.ts index e130b6241483..7954f1c9e0f4 100644 --- a/test/integration/test/ports/engines/queries/data_types/json/_fixture/scalar/generated/contract.d.ts +++ b/test/integration/test/ports/engines/queries/data_types/json/_fixture/scalar/generated/contract.d.ts @@ -42,6 +42,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; @@ -91,6 +95,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; @@ -100,6 +108,14 @@ export type AggregateTypes = { readonly output: 'pg/time-temporal@1'; readonly nullable: true; }; + readonly 'pg/timestamp-date@1': { + readonly output: 'pg/timestamp-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-instant@1': { + readonly output: 'pg/timestamp-instant@1'; + readonly nullable: true; + }; readonly 'pg/timestamp-string@1': { readonly output: 'pg/timestamp-string@1'; readonly nullable: true; @@ -151,6 +167,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; @@ -160,6 +180,14 @@ export type AggregateTypes = { readonly output: 'pg/time-temporal@1'; readonly nullable: true; }; + readonly 'pg/timestamp-date@1': { + readonly output: 'pg/timestamp-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-instant@1': { + readonly output: 'pg/timestamp-instant@1'; + readonly nullable: true; + }; readonly 'pg/timestamp-string@1': { readonly output: 'pg/timestamp-string@1'; readonly nullable: true; @@ -203,6 +231,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; diff --git a/test/integration/test/ports/engines/queries/data_types/native/postgres/_fixture/other/generated/contract.d.ts b/test/integration/test/ports/engines/queries/data_types/native/postgres/_fixture/other/generated/contract.d.ts index 360807b20de6..92edb32bc11f 100644 --- a/test/integration/test/ports/engines/queries/data_types/native/postgres/_fixture/other/generated/contract.d.ts +++ b/test/integration/test/ports/engines/queries/data_types/native/postgres/_fixture/other/generated/contract.d.ts @@ -42,6 +42,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; @@ -91,6 +95,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; @@ -100,6 +108,14 @@ export type AggregateTypes = { readonly output: 'pg/time-temporal@1'; readonly nullable: true; }; + readonly 'pg/timestamp-date@1': { + readonly output: 'pg/timestamp-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-instant@1': { + readonly output: 'pg/timestamp-instant@1'; + readonly nullable: true; + }; readonly 'pg/timestamp-string@1': { readonly output: 'pg/timestamp-string@1'; readonly nullable: true; @@ -151,6 +167,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; @@ -160,6 +180,14 @@ export type AggregateTypes = { readonly output: 'pg/time-temporal@1'; readonly nullable: true; }; + readonly 'pg/timestamp-date@1': { + readonly output: 'pg/timestamp-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-instant@1': { + readonly output: 'pg/timestamp-instant@1'; + readonly nullable: true; + }; readonly 'pg/timestamp-string@1': { readonly output: 'pg/timestamp-string@1'; readonly nullable: true; @@ -203,6 +231,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; diff --git a/test/integration/test/ports/engines/queries/data_types/native/postgres/_fixture/string/generated/contract.d.ts b/test/integration/test/ports/engines/queries/data_types/native/postgres/_fixture/string/generated/contract.d.ts index b30fa3aa4b8f..798ac18f2e96 100644 --- a/test/integration/test/ports/engines/queries/data_types/native/postgres/_fixture/string/generated/contract.d.ts +++ b/test/integration/test/ports/engines/queries/data_types/native/postgres/_fixture/string/generated/contract.d.ts @@ -42,6 +42,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; @@ -91,6 +95,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; @@ -100,6 +108,14 @@ export type AggregateTypes = { readonly output: 'pg/time-temporal@1'; readonly nullable: true; }; + readonly 'pg/timestamp-date@1': { + readonly output: 'pg/timestamp-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-instant@1': { + readonly output: 'pg/timestamp-instant@1'; + readonly nullable: true; + }; readonly 'pg/timestamp-string@1': { readonly output: 'pg/timestamp-string@1'; readonly nullable: true; @@ -151,6 +167,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; @@ -160,6 +180,14 @@ export type AggregateTypes = { readonly output: 'pg/time-temporal@1'; readonly nullable: true; }; + readonly 'pg/timestamp-date@1': { + readonly output: 'pg/timestamp-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-instant@1': { + readonly output: 'pg/timestamp-instant@1'; + readonly nullable: true; + }; readonly 'pg/timestamp-string@1': { readonly output: 'pg/timestamp-string@1'; readonly nullable: true; @@ -203,6 +231,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; diff --git a/test/integration/test/ports/engines/queries/data_types/string/_fixture/generated/contract.d.ts b/test/integration/test/ports/engines/queries/data_types/string/_fixture/generated/contract.d.ts index cf24259e9beb..f7a2f4f95ce5 100644 --- a/test/integration/test/ports/engines/queries/data_types/string/_fixture/generated/contract.d.ts +++ b/test/integration/test/ports/engines/queries/data_types/string/_fixture/generated/contract.d.ts @@ -42,6 +42,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; @@ -91,6 +95,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; @@ -100,6 +108,14 @@ export type AggregateTypes = { readonly output: 'pg/time-temporal@1'; readonly nullable: true; }; + readonly 'pg/timestamp-date@1': { + readonly output: 'pg/timestamp-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-instant@1': { + readonly output: 'pg/timestamp-instant@1'; + readonly nullable: true; + }; readonly 'pg/timestamp-string@1': { readonly output: 'pg/timestamp-string@1'; readonly nullable: true; @@ -151,6 +167,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; @@ -160,6 +180,14 @@ export type AggregateTypes = { readonly output: 'pg/time-temporal@1'; readonly nullable: true; }; + readonly 'pg/timestamp-date@1': { + readonly output: 'pg/timestamp-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-instant@1': { + readonly output: 'pg/timestamp-instant@1'; + readonly nullable: true; + }; readonly 'pg/timestamp-string@1': { readonly output: 'pg/timestamp-string@1'; readonly nullable: true; @@ -203,6 +231,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; diff --git a/test/integration/test/ports/engines/queries/data_types/through_relation/_fixture/common/generated/contract.d.ts b/test/integration/test/ports/engines/queries/data_types/through_relation/_fixture/common/generated/contract.d.ts index 41b383a057c8..c00dba970752 100644 --- a/test/integration/test/ports/engines/queries/data_types/through_relation/_fixture/common/generated/contract.d.ts +++ b/test/integration/test/ports/engines/queries/data_types/through_relation/_fixture/common/generated/contract.d.ts @@ -42,6 +42,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; @@ -91,6 +95,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; @@ -100,6 +108,14 @@ export type AggregateTypes = { readonly output: 'pg/time-temporal@1'; readonly nullable: true; }; + readonly 'pg/timestamp-date@1': { + readonly output: 'pg/timestamp-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-instant@1': { + readonly output: 'pg/timestamp-instant@1'; + readonly nullable: true; + }; readonly 'pg/timestamp-string@1': { readonly output: 'pg/timestamp-string@1'; readonly nullable: true; @@ -151,6 +167,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; @@ -160,6 +180,14 @@ export type AggregateTypes = { readonly output: 'pg/time-temporal@1'; readonly nullable: true; }; + readonly 'pg/timestamp-date@1': { + readonly output: 'pg/timestamp-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-instant@1': { + readonly output: 'pg/timestamp-instant@1'; + readonly nullable: true; + }; readonly 'pg/timestamp-string@1': { readonly output: 'pg/timestamp-string@1'; readonly nullable: true; @@ -203,6 +231,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; diff --git a/test/integration/test/ports/engines/queries/data_types/through_relation/_fixture/decimal/generated/contract.d.ts b/test/integration/test/ports/engines/queries/data_types/through_relation/_fixture/decimal/generated/contract.d.ts index 5a23dcace66b..776330827bce 100644 --- a/test/integration/test/ports/engines/queries/data_types/through_relation/_fixture/decimal/generated/contract.d.ts +++ b/test/integration/test/ports/engines/queries/data_types/through_relation/_fixture/decimal/generated/contract.d.ts @@ -42,6 +42,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; @@ -91,6 +95,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; @@ -100,6 +108,14 @@ export type AggregateTypes = { readonly output: 'pg/time-temporal@1'; readonly nullable: true; }; + readonly 'pg/timestamp-date@1': { + readonly output: 'pg/timestamp-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-instant@1': { + readonly output: 'pg/timestamp-instant@1'; + readonly nullable: true; + }; readonly 'pg/timestamp-string@1': { readonly output: 'pg/timestamp-string@1'; readonly nullable: true; @@ -151,6 +167,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; @@ -160,6 +180,14 @@ export type AggregateTypes = { readonly output: 'pg/time-temporal@1'; readonly nullable: true; }; + readonly 'pg/timestamp-date@1': { + readonly output: 'pg/timestamp-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-instant@1': { + readonly output: 'pg/timestamp-instant@1'; + readonly nullable: true; + }; readonly 'pg/timestamp-string@1': { readonly output: 'pg/timestamp-string@1'; readonly nullable: true; @@ -203,6 +231,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; diff --git a/test/integration/test/ports/engines/queries/data_types/through_relation/_fixture/enum/generated/contract.d.ts b/test/integration/test/ports/engines/queries/data_types/through_relation/_fixture/enum/generated/contract.d.ts index 8323e337284a..eada9b85d619 100644 --- a/test/integration/test/ports/engines/queries/data_types/through_relation/_fixture/enum/generated/contract.d.ts +++ b/test/integration/test/ports/engines/queries/data_types/through_relation/_fixture/enum/generated/contract.d.ts @@ -42,6 +42,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; @@ -91,6 +95,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; @@ -100,6 +108,14 @@ export type AggregateTypes = { readonly output: 'pg/time-temporal@1'; readonly nullable: true; }; + readonly 'pg/timestamp-date@1': { + readonly output: 'pg/timestamp-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-instant@1': { + readonly output: 'pg/timestamp-instant@1'; + readonly nullable: true; + }; readonly 'pg/timestamp-string@1': { readonly output: 'pg/timestamp-string@1'; readonly nullable: true; @@ -151,6 +167,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; @@ -160,6 +180,14 @@ export type AggregateTypes = { readonly output: 'pg/time-temporal@1'; readonly nullable: true; }; + readonly 'pg/timestamp-date@1': { + readonly output: 'pg/timestamp-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-instant@1': { + readonly output: 'pg/timestamp-instant@1'; + readonly nullable: true; + }; readonly 'pg/timestamp-string@1': { readonly output: 'pg/timestamp-string@1'; readonly nullable: true; @@ -203,6 +231,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; diff --git a/test/integration/test/ports/engines/queries/data_types/through_relation/_fixture/json/generated/contract.d.ts b/test/integration/test/ports/engines/queries/data_types/through_relation/_fixture/json/generated/contract.d.ts index c5827a746e04..505fe2600868 100644 --- a/test/integration/test/ports/engines/queries/data_types/through_relation/_fixture/json/generated/contract.d.ts +++ b/test/integration/test/ports/engines/queries/data_types/through_relation/_fixture/json/generated/contract.d.ts @@ -42,6 +42,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; @@ -91,6 +95,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; @@ -100,6 +108,14 @@ export type AggregateTypes = { readonly output: 'pg/time-temporal@1'; readonly nullable: true; }; + readonly 'pg/timestamp-date@1': { + readonly output: 'pg/timestamp-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-instant@1': { + readonly output: 'pg/timestamp-instant@1'; + readonly nullable: true; + }; readonly 'pg/timestamp-string@1': { readonly output: 'pg/timestamp-string@1'; readonly nullable: true; @@ -151,6 +167,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; @@ -160,6 +180,14 @@ export type AggregateTypes = { readonly output: 'pg/time-temporal@1'; readonly nullable: true; }; + readonly 'pg/timestamp-date@1': { + readonly output: 'pg/timestamp-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-instant@1': { + readonly output: 'pg/timestamp-instant@1'; + readonly nullable: true; + }; readonly 'pg/timestamp-string@1': { readonly output: 'pg/timestamp-string@1'; readonly nullable: true; @@ -203,6 +231,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; diff --git a/test/integration/test/ports/engines/queries/data_types/through_relation/_fixture/lists/generated/contract.d.ts b/test/integration/test/ports/engines/queries/data_types/through_relation/_fixture/lists/generated/contract.d.ts index 350d0a549ed6..ca304388dfc4 100644 --- a/test/integration/test/ports/engines/queries/data_types/through_relation/_fixture/lists/generated/contract.d.ts +++ b/test/integration/test/ports/engines/queries/data_types/through_relation/_fixture/lists/generated/contract.d.ts @@ -42,6 +42,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; @@ -91,6 +95,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; @@ -100,6 +108,14 @@ export type AggregateTypes = { readonly output: 'pg/time-temporal@1'; readonly nullable: true; }; + readonly 'pg/timestamp-date@1': { + readonly output: 'pg/timestamp-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-instant@1': { + readonly output: 'pg/timestamp-instant@1'; + readonly nullable: true; + }; readonly 'pg/timestamp-string@1': { readonly output: 'pg/timestamp-string@1'; readonly nullable: true; @@ -151,6 +167,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; @@ -160,6 +180,14 @@ export type AggregateTypes = { readonly output: 'pg/time-temporal@1'; readonly nullable: true; }; + readonly 'pg/timestamp-date@1': { + readonly output: 'pg/timestamp-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-instant@1': { + readonly output: 'pg/timestamp-instant@1'; + readonly nullable: true; + }; readonly 'pg/timestamp-string@1': { readonly output: 'pg/timestamp-string@1'; readonly nullable: true; @@ -203,6 +231,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; diff --git a/test/integration/test/ports/engines/queries/distinct/_fixture/generated/contract.d.ts b/test/integration/test/ports/engines/queries/distinct/_fixture/generated/contract.d.ts index 966a66ef2215..aa3bb4d5f89d 100644 --- a/test/integration/test/ports/engines/queries/distinct/_fixture/generated/contract.d.ts +++ b/test/integration/test/ports/engines/queries/distinct/_fixture/generated/contract.d.ts @@ -42,6 +42,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; @@ -91,6 +95,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; @@ -100,6 +108,14 @@ export type AggregateTypes = { readonly output: 'pg/time-temporal@1'; readonly nullable: true; }; + readonly 'pg/timestamp-date@1': { + readonly output: 'pg/timestamp-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-instant@1': { + readonly output: 'pg/timestamp-instant@1'; + readonly nullable: true; + }; readonly 'pg/timestamp-string@1': { readonly output: 'pg/timestamp-string@1'; readonly nullable: true; @@ -151,6 +167,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; @@ -160,6 +180,14 @@ export type AggregateTypes = { readonly output: 'pg/time-temporal@1'; readonly nullable: true; }; + readonly 'pg/timestamp-date@1': { + readonly output: 'pg/timestamp-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-instant@1': { + readonly output: 'pg/timestamp-instant@1'; + readonly nullable: true; + }; readonly 'pg/timestamp-string@1': { readonly output: 'pg/timestamp-string@1'; readonly nullable: true; @@ -203,6 +231,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; diff --git a/test/integration/test/ports/engines/queries/filters/bigint_filter/_fixture/generated/contract.d.ts b/test/integration/test/ports/engines/queries/filters/bigint_filter/_fixture/generated/contract.d.ts index cf24259e9beb..f7a2f4f95ce5 100644 --- a/test/integration/test/ports/engines/queries/filters/bigint_filter/_fixture/generated/contract.d.ts +++ b/test/integration/test/ports/engines/queries/filters/bigint_filter/_fixture/generated/contract.d.ts @@ -42,6 +42,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; @@ -91,6 +95,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; @@ -100,6 +108,14 @@ export type AggregateTypes = { readonly output: 'pg/time-temporal@1'; readonly nullable: true; }; + readonly 'pg/timestamp-date@1': { + readonly output: 'pg/timestamp-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-instant@1': { + readonly output: 'pg/timestamp-instant@1'; + readonly nullable: true; + }; readonly 'pg/timestamp-string@1': { readonly output: 'pg/timestamp-string@1'; readonly nullable: true; @@ -151,6 +167,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; @@ -160,6 +180,14 @@ export type AggregateTypes = { readonly output: 'pg/time-temporal@1'; readonly nullable: true; }; + readonly 'pg/timestamp-date@1': { + readonly output: 'pg/timestamp-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-instant@1': { + readonly output: 'pg/timestamp-instant@1'; + readonly nullable: true; + }; readonly 'pg/timestamp-string@1': { readonly output: 'pg/timestamp-string@1'; readonly nullable: true; @@ -203,6 +231,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; diff --git a/test/integration/test/ports/engines/queries/filters/bytes_filter/_fixture/generated/contract.d.ts b/test/integration/test/ports/engines/queries/filters/bytes_filter/_fixture/generated/contract.d.ts index cf24259e9beb..f7a2f4f95ce5 100644 --- a/test/integration/test/ports/engines/queries/filters/bytes_filter/_fixture/generated/contract.d.ts +++ b/test/integration/test/ports/engines/queries/filters/bytes_filter/_fixture/generated/contract.d.ts @@ -42,6 +42,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; @@ -91,6 +95,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; @@ -100,6 +108,14 @@ export type AggregateTypes = { readonly output: 'pg/time-temporal@1'; readonly nullable: true; }; + readonly 'pg/timestamp-date@1': { + readonly output: 'pg/timestamp-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-instant@1': { + readonly output: 'pg/timestamp-instant@1'; + readonly nullable: true; + }; readonly 'pg/timestamp-string@1': { readonly output: 'pg/timestamp-string@1'; readonly nullable: true; @@ -151,6 +167,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; @@ -160,6 +180,14 @@ export type AggregateTypes = { readonly output: 'pg/time-temporal@1'; readonly nullable: true; }; + readonly 'pg/timestamp-date@1': { + readonly output: 'pg/timestamp-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-instant@1': { + readonly output: 'pg/timestamp-instant@1'; + readonly nullable: true; + }; readonly 'pg/timestamp-string@1': { readonly output: 'pg/timestamp-string@1'; readonly nullable: true; @@ -203,6 +231,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; diff --git a/test/integration/test/ports/engines/queries/filters/decimal_filter/_fixture/generated/contract.d.ts b/test/integration/test/ports/engines/queries/filters/decimal_filter/_fixture/generated/contract.d.ts index 079b9b6c72ac..aaa2f4e4d2d3 100644 --- a/test/integration/test/ports/engines/queries/filters/decimal_filter/_fixture/generated/contract.d.ts +++ b/test/integration/test/ports/engines/queries/filters/decimal_filter/_fixture/generated/contract.d.ts @@ -42,6 +42,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; @@ -91,6 +95,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; @@ -100,6 +108,14 @@ export type AggregateTypes = { readonly output: 'pg/time-temporal@1'; readonly nullable: true; }; + readonly 'pg/timestamp-date@1': { + readonly output: 'pg/timestamp-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-instant@1': { + readonly output: 'pg/timestamp-instant@1'; + readonly nullable: true; + }; readonly 'pg/timestamp-string@1': { readonly output: 'pg/timestamp-string@1'; readonly nullable: true; @@ -151,6 +167,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; @@ -160,6 +180,14 @@ export type AggregateTypes = { readonly output: 'pg/time-temporal@1'; readonly nullable: true; }; + readonly 'pg/timestamp-date@1': { + readonly output: 'pg/timestamp-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-instant@1': { + readonly output: 'pg/timestamp-instant@1'; + readonly nullable: true; + }; readonly 'pg/timestamp-string@1': { readonly output: 'pg/timestamp-string@1'; readonly nullable: true; @@ -203,6 +231,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; diff --git a/test/integration/test/ports/engines/queries/filters/field_reference/_fixture/decimal-list/generated/contract.d.ts b/test/integration/test/ports/engines/queries/filters/field_reference/_fixture/decimal-list/generated/contract.d.ts index e47dcc59abd5..6374d6ff5b37 100644 --- a/test/integration/test/ports/engines/queries/filters/field_reference/_fixture/decimal-list/generated/contract.d.ts +++ b/test/integration/test/ports/engines/queries/filters/field_reference/_fixture/decimal-list/generated/contract.d.ts @@ -42,6 +42,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; @@ -91,6 +95,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; @@ -100,6 +108,14 @@ export type AggregateTypes = { readonly output: 'pg/time-temporal@1'; readonly nullable: true; }; + readonly 'pg/timestamp-date@1': { + readonly output: 'pg/timestamp-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-instant@1': { + readonly output: 'pg/timestamp-instant@1'; + readonly nullable: true; + }; readonly 'pg/timestamp-string@1': { readonly output: 'pg/timestamp-string@1'; readonly nullable: true; @@ -151,6 +167,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; @@ -160,6 +180,14 @@ export type AggregateTypes = { readonly output: 'pg/time-temporal@1'; readonly nullable: true; }; + readonly 'pg/timestamp-date@1': { + readonly output: 'pg/timestamp-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-instant@1': { + readonly output: 'pg/timestamp-instant@1'; + readonly nullable: true; + }; readonly 'pg/timestamp-string@1': { readonly output: 'pg/timestamp-string@1'; readonly nullable: true; @@ -203,6 +231,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; diff --git a/test/integration/test/ports/engines/queries/filters/field_reference/_fixture/enum/generated/contract.d.ts b/test/integration/test/ports/engines/queries/filters/field_reference/_fixture/enum/generated/contract.d.ts index a5ebbab30e24..53cef6d0078b 100644 --- a/test/integration/test/ports/engines/queries/filters/field_reference/_fixture/enum/generated/contract.d.ts +++ b/test/integration/test/ports/engines/queries/filters/field_reference/_fixture/enum/generated/contract.d.ts @@ -42,6 +42,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; @@ -91,6 +95,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; @@ -100,6 +108,14 @@ export type AggregateTypes = { readonly output: 'pg/time-temporal@1'; readonly nullable: true; }; + readonly 'pg/timestamp-date@1': { + readonly output: 'pg/timestamp-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-instant@1': { + readonly output: 'pg/timestamp-instant@1'; + readonly nullable: true; + }; readonly 'pg/timestamp-string@1': { readonly output: 'pg/timestamp-string@1'; readonly nullable: true; @@ -151,6 +167,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; @@ -160,6 +180,14 @@ export type AggregateTypes = { readonly output: 'pg/time-temporal@1'; readonly nullable: true; }; + readonly 'pg/timestamp-date@1': { + readonly output: 'pg/timestamp-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-instant@1': { + readonly output: 'pg/timestamp-instant@1'; + readonly nullable: true; + }; readonly 'pg/timestamp-string@1': { readonly output: 'pg/timestamp-string@1'; readonly nullable: true; @@ -203,6 +231,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; diff --git a/test/integration/test/ports/engines/queries/filters/field_reference/_fixture/list/generated/contract.d.ts b/test/integration/test/ports/engines/queries/filters/field_reference/_fixture/list/generated/contract.d.ts index 7c3b8d87c15b..5f972eed7d0a 100644 --- a/test/integration/test/ports/engines/queries/filters/field_reference/_fixture/list/generated/contract.d.ts +++ b/test/integration/test/ports/engines/queries/filters/field_reference/_fixture/list/generated/contract.d.ts @@ -42,6 +42,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; @@ -91,6 +95,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; @@ -100,6 +108,14 @@ export type AggregateTypes = { readonly output: 'pg/time-temporal@1'; readonly nullable: true; }; + readonly 'pg/timestamp-date@1': { + readonly output: 'pg/timestamp-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-instant@1': { + readonly output: 'pg/timestamp-instant@1'; + readonly nullable: true; + }; readonly 'pg/timestamp-string@1': { readonly output: 'pg/timestamp-string@1'; readonly nullable: true; @@ -151,6 +167,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; @@ -160,6 +180,14 @@ export type AggregateTypes = { readonly output: 'pg/time-temporal@1'; readonly nullable: true; }; + readonly 'pg/timestamp-date@1': { + readonly output: 'pg/timestamp-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-instant@1': { + readonly output: 'pg/timestamp-instant@1'; + readonly nullable: true; + }; readonly 'pg/timestamp-string@1': { readonly output: 'pg/timestamp-string@1'; readonly nullable: true; @@ -203,6 +231,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; diff --git a/test/integration/test/ports/engines/queries/filters/field_reference/_fixture/mixed/generated/contract.d.ts b/test/integration/test/ports/engines/queries/filters/field_reference/_fixture/mixed/generated/contract.d.ts index cd1448866210..6ea69e18eddb 100644 --- a/test/integration/test/ports/engines/queries/filters/field_reference/_fixture/mixed/generated/contract.d.ts +++ b/test/integration/test/ports/engines/queries/filters/field_reference/_fixture/mixed/generated/contract.d.ts @@ -42,6 +42,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; @@ -91,6 +95,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; @@ -100,6 +108,14 @@ export type AggregateTypes = { readonly output: 'pg/time-temporal@1'; readonly nullable: true; }; + readonly 'pg/timestamp-date@1': { + readonly output: 'pg/timestamp-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-instant@1': { + readonly output: 'pg/timestamp-instant@1'; + readonly nullable: true; + }; readonly 'pg/timestamp-string@1': { readonly output: 'pg/timestamp-string@1'; readonly nullable: true; @@ -151,6 +167,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; @@ -160,6 +180,14 @@ export type AggregateTypes = { readonly output: 'pg/time-temporal@1'; readonly nullable: true; }; + readonly 'pg/timestamp-date@1': { + readonly output: 'pg/timestamp-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-instant@1': { + readonly output: 'pg/timestamp-instant@1'; + readonly nullable: true; + }; readonly 'pg/timestamp-string@1': { readonly output: 'pg/timestamp-string@1'; readonly nullable: true; @@ -203,6 +231,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; diff --git a/test/integration/test/ports/engines/queries/filters/field_reference/bigint_filter/_fixture/generated/contract.d.ts b/test/integration/test/ports/engines/queries/filters/field_reference/bigint_filter/_fixture/generated/contract.d.ts index 78c4ea4ee058..24984f967a5b 100644 --- a/test/integration/test/ports/engines/queries/filters/field_reference/bigint_filter/_fixture/generated/contract.d.ts +++ b/test/integration/test/ports/engines/queries/filters/field_reference/bigint_filter/_fixture/generated/contract.d.ts @@ -42,6 +42,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; @@ -91,6 +95,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; @@ -100,6 +108,14 @@ export type AggregateTypes = { readonly output: 'pg/time-temporal@1'; readonly nullable: true; }; + readonly 'pg/timestamp-date@1': { + readonly output: 'pg/timestamp-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-instant@1': { + readonly output: 'pg/timestamp-instant@1'; + readonly nullable: true; + }; readonly 'pg/timestamp-string@1': { readonly output: 'pg/timestamp-string@1'; readonly nullable: true; @@ -151,6 +167,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; @@ -160,6 +180,14 @@ export type AggregateTypes = { readonly output: 'pg/time-temporal@1'; readonly nullable: true; }; + readonly 'pg/timestamp-date@1': { + readonly output: 'pg/timestamp-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-instant@1': { + readonly output: 'pg/timestamp-instant@1'; + readonly nullable: true; + }; readonly 'pg/timestamp-string@1': { readonly output: 'pg/timestamp-string@1'; readonly nullable: true; @@ -203,6 +231,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; diff --git a/test/integration/test/ports/engines/queries/filters/field_reference/bytes_filter/_fixture/generated/contract.d.ts b/test/integration/test/ports/engines/queries/filters/field_reference/bytes_filter/_fixture/generated/contract.d.ts index 78c4ea4ee058..24984f967a5b 100644 --- a/test/integration/test/ports/engines/queries/filters/field_reference/bytes_filter/_fixture/generated/contract.d.ts +++ b/test/integration/test/ports/engines/queries/filters/field_reference/bytes_filter/_fixture/generated/contract.d.ts @@ -42,6 +42,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; @@ -91,6 +95,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; @@ -100,6 +108,14 @@ export type AggregateTypes = { readonly output: 'pg/time-temporal@1'; readonly nullable: true; }; + readonly 'pg/timestamp-date@1': { + readonly output: 'pg/timestamp-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-instant@1': { + readonly output: 'pg/timestamp-instant@1'; + readonly nullable: true; + }; readonly 'pg/timestamp-string@1': { readonly output: 'pg/timestamp-string@1'; readonly nullable: true; @@ -151,6 +167,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; @@ -160,6 +180,14 @@ export type AggregateTypes = { readonly output: 'pg/time-temporal@1'; readonly nullable: true; }; + readonly 'pg/timestamp-date@1': { + readonly output: 'pg/timestamp-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-instant@1': { + readonly output: 'pg/timestamp-instant@1'; + readonly nullable: true; + }; readonly 'pg/timestamp-string@1': { readonly output: 'pg/timestamp-string@1'; readonly nullable: true; @@ -203,6 +231,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; diff --git a/test/integration/test/ports/engines/queries/filters/field_reference/datetime_filter/_fixture/generated/contract.d.ts b/test/integration/test/ports/engines/queries/filters/field_reference/datetime_filter/_fixture/generated/contract.d.ts index 78c4ea4ee058..24984f967a5b 100644 --- a/test/integration/test/ports/engines/queries/filters/field_reference/datetime_filter/_fixture/generated/contract.d.ts +++ b/test/integration/test/ports/engines/queries/filters/field_reference/datetime_filter/_fixture/generated/contract.d.ts @@ -42,6 +42,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; @@ -91,6 +95,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; @@ -100,6 +108,14 @@ export type AggregateTypes = { readonly output: 'pg/time-temporal@1'; readonly nullable: true; }; + readonly 'pg/timestamp-date@1': { + readonly output: 'pg/timestamp-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-instant@1': { + readonly output: 'pg/timestamp-instant@1'; + readonly nullable: true; + }; readonly 'pg/timestamp-string@1': { readonly output: 'pg/timestamp-string@1'; readonly nullable: true; @@ -151,6 +167,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; @@ -160,6 +180,14 @@ export type AggregateTypes = { readonly output: 'pg/time-temporal@1'; readonly nullable: true; }; + readonly 'pg/timestamp-date@1': { + readonly output: 'pg/timestamp-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-instant@1': { + readonly output: 'pg/timestamp-instant@1'; + readonly nullable: true; + }; readonly 'pg/timestamp-string@1': { readonly output: 'pg/timestamp-string@1'; readonly nullable: true; @@ -203,6 +231,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; diff --git a/test/integration/test/ports/engines/queries/filters/field_reference/decimal_filter/_fixture/generated/contract.d.ts b/test/integration/test/ports/engines/queries/filters/field_reference/decimal_filter/_fixture/generated/contract.d.ts index d6a5f215a045..21d4bc610dd2 100644 --- a/test/integration/test/ports/engines/queries/filters/field_reference/decimal_filter/_fixture/generated/contract.d.ts +++ b/test/integration/test/ports/engines/queries/filters/field_reference/decimal_filter/_fixture/generated/contract.d.ts @@ -42,6 +42,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; @@ -91,6 +95,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; @@ -100,6 +108,14 @@ export type AggregateTypes = { readonly output: 'pg/time-temporal@1'; readonly nullable: true; }; + readonly 'pg/timestamp-date@1': { + readonly output: 'pg/timestamp-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-instant@1': { + readonly output: 'pg/timestamp-instant@1'; + readonly nullable: true; + }; readonly 'pg/timestamp-string@1': { readonly output: 'pg/timestamp-string@1'; readonly nullable: true; @@ -151,6 +167,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; @@ -160,6 +180,14 @@ export type AggregateTypes = { readonly output: 'pg/time-temporal@1'; readonly nullable: true; }; + readonly 'pg/timestamp-date@1': { + readonly output: 'pg/timestamp-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-instant@1': { + readonly output: 'pg/timestamp-instant@1'; + readonly nullable: true; + }; readonly 'pg/timestamp-string@1': { readonly output: 'pg/timestamp-string@1'; readonly nullable: true; @@ -203,6 +231,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; diff --git a/test/integration/test/ports/engines/queries/filters/field_reference/failure/_fixture/common/generated/contract.d.ts b/test/integration/test/ports/engines/queries/filters/field_reference/failure/_fixture/common/generated/contract.d.ts index 78c4ea4ee058..24984f967a5b 100644 --- a/test/integration/test/ports/engines/queries/filters/field_reference/failure/_fixture/common/generated/contract.d.ts +++ b/test/integration/test/ports/engines/queries/filters/field_reference/failure/_fixture/common/generated/contract.d.ts @@ -42,6 +42,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; @@ -91,6 +95,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; @@ -100,6 +108,14 @@ export type AggregateTypes = { readonly output: 'pg/time-temporal@1'; readonly nullable: true; }; + readonly 'pg/timestamp-date@1': { + readonly output: 'pg/timestamp-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-instant@1': { + readonly output: 'pg/timestamp-instant@1'; + readonly nullable: true; + }; readonly 'pg/timestamp-string@1': { readonly output: 'pg/timestamp-string@1'; readonly nullable: true; @@ -151,6 +167,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; @@ -160,6 +180,14 @@ export type AggregateTypes = { readonly output: 'pg/time-temporal@1'; readonly nullable: true; }; + readonly 'pg/timestamp-date@1': { + readonly output: 'pg/timestamp-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-instant@1': { + readonly output: 'pg/timestamp-instant@1'; + readonly nullable: true; + }; readonly 'pg/timestamp-string@1': { readonly output: 'pg/timestamp-string@1'; readonly nullable: true; @@ -203,6 +231,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; diff --git a/test/integration/test/ports/engines/queries/filters/field_reference/failure/_fixture/default/generated/contract.d.ts b/test/integration/test/ports/engines/queries/filters/field_reference/failure/_fixture/default/generated/contract.d.ts index cee01631d909..8ad50d276bf8 100644 --- a/test/integration/test/ports/engines/queries/filters/field_reference/failure/_fixture/default/generated/contract.d.ts +++ b/test/integration/test/ports/engines/queries/filters/field_reference/failure/_fixture/default/generated/contract.d.ts @@ -42,6 +42,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; @@ -91,6 +95,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; @@ -100,6 +108,14 @@ export type AggregateTypes = { readonly output: 'pg/time-temporal@1'; readonly nullable: true; }; + readonly 'pg/timestamp-date@1': { + readonly output: 'pg/timestamp-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-instant@1': { + readonly output: 'pg/timestamp-instant@1'; + readonly nullable: true; + }; readonly 'pg/timestamp-string@1': { readonly output: 'pg/timestamp-string@1'; readonly nullable: true; @@ -151,6 +167,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; @@ -160,6 +180,14 @@ export type AggregateTypes = { readonly output: 'pg/time-temporal@1'; readonly nullable: true; }; + readonly 'pg/timestamp-date@1': { + readonly output: 'pg/timestamp-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-instant@1': { + readonly output: 'pg/timestamp-instant@1'; + readonly nullable: true; + }; readonly 'pg/timestamp-string@1': { readonly output: 'pg/timestamp-string@1'; readonly nullable: true; @@ -203,6 +231,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; diff --git a/test/integration/test/ports/engines/queries/filters/field_reference/failure/_fixture/list/generated/contract.d.ts b/test/integration/test/ports/engines/queries/filters/field_reference/failure/_fixture/list/generated/contract.d.ts index 8fd52a04c4a6..ea3cd3f6daa5 100644 --- a/test/integration/test/ports/engines/queries/filters/field_reference/failure/_fixture/list/generated/contract.d.ts +++ b/test/integration/test/ports/engines/queries/filters/field_reference/failure/_fixture/list/generated/contract.d.ts @@ -42,6 +42,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; @@ -91,6 +95,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; @@ -100,6 +108,14 @@ export type AggregateTypes = { readonly output: 'pg/time-temporal@1'; readonly nullable: true; }; + readonly 'pg/timestamp-date@1': { + readonly output: 'pg/timestamp-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-instant@1': { + readonly output: 'pg/timestamp-instant@1'; + readonly nullable: true; + }; readonly 'pg/timestamp-string@1': { readonly output: 'pg/timestamp-string@1'; readonly nullable: true; @@ -151,6 +167,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; @@ -160,6 +180,14 @@ export type AggregateTypes = { readonly output: 'pg/time-temporal@1'; readonly nullable: true; }; + readonly 'pg/timestamp-date@1': { + readonly output: 'pg/timestamp-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-instant@1': { + readonly output: 'pg/timestamp-instant@1'; + readonly nullable: true; + }; readonly 'pg/timestamp-string@1': { readonly output: 'pg/timestamp-string@1'; readonly nullable: true; @@ -203,6 +231,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; diff --git a/test/integration/test/ports/engines/queries/filters/field_reference/float_filter/_fixture/generated/contract.d.ts b/test/integration/test/ports/engines/queries/filters/field_reference/float_filter/_fixture/generated/contract.d.ts index 78c4ea4ee058..24984f967a5b 100644 --- a/test/integration/test/ports/engines/queries/filters/field_reference/float_filter/_fixture/generated/contract.d.ts +++ b/test/integration/test/ports/engines/queries/filters/field_reference/float_filter/_fixture/generated/contract.d.ts @@ -42,6 +42,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; @@ -91,6 +95,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; @@ -100,6 +108,14 @@ export type AggregateTypes = { readonly output: 'pg/time-temporal@1'; readonly nullable: true; }; + readonly 'pg/timestamp-date@1': { + readonly output: 'pg/timestamp-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-instant@1': { + readonly output: 'pg/timestamp-instant@1'; + readonly nullable: true; + }; readonly 'pg/timestamp-string@1': { readonly output: 'pg/timestamp-string@1'; readonly nullable: true; @@ -151,6 +167,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; @@ -160,6 +180,14 @@ export type AggregateTypes = { readonly output: 'pg/time-temporal@1'; readonly nullable: true; }; + readonly 'pg/timestamp-date@1': { + readonly output: 'pg/timestamp-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-instant@1': { + readonly output: 'pg/timestamp-instant@1'; + readonly nullable: true; + }; readonly 'pg/timestamp-string@1': { readonly output: 'pg/timestamp-string@1'; readonly nullable: true; @@ -203,6 +231,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; diff --git a/test/integration/test/ports/engines/queries/filters/field_reference/having_filter/_fixture/generated/contract.d.ts b/test/integration/test/ports/engines/queries/filters/field_reference/having_filter/_fixture/generated/contract.d.ts index 78c4ea4ee058..24984f967a5b 100644 --- a/test/integration/test/ports/engines/queries/filters/field_reference/having_filter/_fixture/generated/contract.d.ts +++ b/test/integration/test/ports/engines/queries/filters/field_reference/having_filter/_fixture/generated/contract.d.ts @@ -42,6 +42,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; @@ -91,6 +95,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; @@ -100,6 +108,14 @@ export type AggregateTypes = { readonly output: 'pg/time-temporal@1'; readonly nullable: true; }; + readonly 'pg/timestamp-date@1': { + readonly output: 'pg/timestamp-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-instant@1': { + readonly output: 'pg/timestamp-instant@1'; + readonly nullable: true; + }; readonly 'pg/timestamp-string@1': { readonly output: 'pg/timestamp-string@1'; readonly nullable: true; @@ -151,6 +167,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; @@ -160,6 +180,14 @@ export type AggregateTypes = { readonly output: 'pg/time-temporal@1'; readonly nullable: true; }; + readonly 'pg/timestamp-date@1': { + readonly output: 'pg/timestamp-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-instant@1': { + readonly output: 'pg/timestamp-instant@1'; + readonly nullable: true; + }; readonly 'pg/timestamp-string@1': { readonly output: 'pg/timestamp-string@1'; readonly nullable: true; @@ -203,6 +231,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; diff --git a/test/integration/test/ports/engines/queries/filters/field_reference/int_filter/_fixture/generated/contract.d.ts b/test/integration/test/ports/engines/queries/filters/field_reference/int_filter/_fixture/generated/contract.d.ts index 78c4ea4ee058..24984f967a5b 100644 --- a/test/integration/test/ports/engines/queries/filters/field_reference/int_filter/_fixture/generated/contract.d.ts +++ b/test/integration/test/ports/engines/queries/filters/field_reference/int_filter/_fixture/generated/contract.d.ts @@ -42,6 +42,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; @@ -91,6 +95,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; @@ -100,6 +108,14 @@ export type AggregateTypes = { readonly output: 'pg/time-temporal@1'; readonly nullable: true; }; + readonly 'pg/timestamp-date@1': { + readonly output: 'pg/timestamp-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-instant@1': { + readonly output: 'pg/timestamp-instant@1'; + readonly nullable: true; + }; readonly 'pg/timestamp-string@1': { readonly output: 'pg/timestamp-string@1'; readonly nullable: true; @@ -151,6 +167,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; @@ -160,6 +180,14 @@ export type AggregateTypes = { readonly output: 'pg/time-temporal@1'; readonly nullable: true; }; + readonly 'pg/timestamp-date@1': { + readonly output: 'pg/timestamp-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-instant@1': { + readonly output: 'pg/timestamp-instant@1'; + readonly nullable: true; + }; readonly 'pg/timestamp-string@1': { readonly output: 'pg/timestamp-string@1'; readonly nullable: true; @@ -203,6 +231,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; diff --git a/test/integration/test/ports/engines/queries/filters/field_reference/json_filter/_fixture/generated/contract.d.ts b/test/integration/test/ports/engines/queries/filters/field_reference/json_filter/_fixture/generated/contract.d.ts index f68e7448e5ed..0f8ee1734935 100644 --- a/test/integration/test/ports/engines/queries/filters/field_reference/json_filter/_fixture/generated/contract.d.ts +++ b/test/integration/test/ports/engines/queries/filters/field_reference/json_filter/_fixture/generated/contract.d.ts @@ -42,6 +42,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; @@ -91,6 +95,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; @@ -100,6 +108,14 @@ export type AggregateTypes = { readonly output: 'pg/time-temporal@1'; readonly nullable: true; }; + readonly 'pg/timestamp-date@1': { + readonly output: 'pg/timestamp-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-instant@1': { + readonly output: 'pg/timestamp-instant@1'; + readonly nullable: true; + }; readonly 'pg/timestamp-string@1': { readonly output: 'pg/timestamp-string@1'; readonly nullable: true; @@ -151,6 +167,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; @@ -160,6 +180,14 @@ export type AggregateTypes = { readonly output: 'pg/time-temporal@1'; readonly nullable: true; }; + readonly 'pg/timestamp-date@1': { + readonly output: 'pg/timestamp-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-instant@1': { + readonly output: 'pg/timestamp-instant@1'; + readonly nullable: true; + }; readonly 'pg/timestamp-string@1': { readonly output: 'pg/timestamp-string@1'; readonly nullable: true; @@ -203,6 +231,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; diff --git a/test/integration/test/ports/engines/queries/filters/field_reference/json_filter/_fixture/list/generated/contract.d.ts b/test/integration/test/ports/engines/queries/filters/field_reference/json_filter/_fixture/list/generated/contract.d.ts index ce6bdda94c3d..b6a440e20503 100644 --- a/test/integration/test/ports/engines/queries/filters/field_reference/json_filter/_fixture/list/generated/contract.d.ts +++ b/test/integration/test/ports/engines/queries/filters/field_reference/json_filter/_fixture/list/generated/contract.d.ts @@ -42,6 +42,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; @@ -91,6 +95,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; @@ -100,6 +108,14 @@ export type AggregateTypes = { readonly output: 'pg/time-temporal@1'; readonly nullable: true; }; + readonly 'pg/timestamp-date@1': { + readonly output: 'pg/timestamp-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-instant@1': { + readonly output: 'pg/timestamp-instant@1'; + readonly nullable: true; + }; readonly 'pg/timestamp-string@1': { readonly output: 'pg/timestamp-string@1'; readonly nullable: true; @@ -151,6 +167,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; @@ -160,6 +180,14 @@ export type AggregateTypes = { readonly output: 'pg/time-temporal@1'; readonly nullable: true; }; + readonly 'pg/timestamp-date@1': { + readonly output: 'pg/timestamp-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-instant@1': { + readonly output: 'pg/timestamp-instant@1'; + readonly nullable: true; + }; readonly 'pg/timestamp-string@1': { readonly output: 'pg/timestamp-string@1'; readonly nullable: true; @@ -203,6 +231,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; diff --git a/test/integration/test/ports/engines/queries/filters/field_reference/relation_filter/_fixture/complex/generated/contract.d.ts b/test/integration/test/ports/engines/queries/filters/field_reference/relation_filter/_fixture/complex/generated/contract.d.ts index 5d35b0801bda..db4f0bd497dd 100644 --- a/test/integration/test/ports/engines/queries/filters/field_reference/relation_filter/_fixture/complex/generated/contract.d.ts +++ b/test/integration/test/ports/engines/queries/filters/field_reference/relation_filter/_fixture/complex/generated/contract.d.ts @@ -42,6 +42,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; @@ -91,6 +95,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; @@ -100,6 +108,14 @@ export type AggregateTypes = { readonly output: 'pg/time-temporal@1'; readonly nullable: true; }; + readonly 'pg/timestamp-date@1': { + readonly output: 'pg/timestamp-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-instant@1': { + readonly output: 'pg/timestamp-instant@1'; + readonly nullable: true; + }; readonly 'pg/timestamp-string@1': { readonly output: 'pg/timestamp-string@1'; readonly nullable: true; @@ -151,6 +167,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; @@ -160,6 +180,14 @@ export type AggregateTypes = { readonly output: 'pg/time-temporal@1'; readonly nullable: true; }; + readonly 'pg/timestamp-date@1': { + readonly output: 'pg/timestamp-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-instant@1': { + readonly output: 'pg/timestamp-instant@1'; + readonly nullable: true; + }; readonly 'pg/timestamp-string@1': { readonly output: 'pg/timestamp-string@1'; readonly nullable: true; @@ -203,6 +231,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; diff --git a/test/integration/test/ports/engines/queries/filters/field_reference/relation_filter/_fixture/one-to-many/generated/contract.d.ts b/test/integration/test/ports/engines/queries/filters/field_reference/relation_filter/_fixture/one-to-many/generated/contract.d.ts index 69de1382debd..676b2f57ae9d 100644 --- a/test/integration/test/ports/engines/queries/filters/field_reference/relation_filter/_fixture/one-to-many/generated/contract.d.ts +++ b/test/integration/test/ports/engines/queries/filters/field_reference/relation_filter/_fixture/one-to-many/generated/contract.d.ts @@ -42,6 +42,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; @@ -91,6 +95,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; @@ -100,6 +108,14 @@ export type AggregateTypes = { readonly output: 'pg/time-temporal@1'; readonly nullable: true; }; + readonly 'pg/timestamp-date@1': { + readonly output: 'pg/timestamp-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-instant@1': { + readonly output: 'pg/timestamp-instant@1'; + readonly nullable: true; + }; readonly 'pg/timestamp-string@1': { readonly output: 'pg/timestamp-string@1'; readonly nullable: true; @@ -151,6 +167,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; @@ -160,6 +180,14 @@ export type AggregateTypes = { readonly output: 'pg/time-temporal@1'; readonly nullable: true; }; + readonly 'pg/timestamp-date@1': { + readonly output: 'pg/timestamp-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-instant@1': { + readonly output: 'pg/timestamp-instant@1'; + readonly nullable: true; + }; readonly 'pg/timestamp-string@1': { readonly output: 'pg/timestamp-string@1'; readonly nullable: true; @@ -203,6 +231,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; diff --git a/test/integration/test/ports/engines/queries/filters/field_reference/relation_filter/_fixture/one-to-one-list/generated/contract.d.ts b/test/integration/test/ports/engines/queries/filters/field_reference/relation_filter/_fixture/one-to-one-list/generated/contract.d.ts index 41b4afc38621..13a53f7c85cd 100644 --- a/test/integration/test/ports/engines/queries/filters/field_reference/relation_filter/_fixture/one-to-one-list/generated/contract.d.ts +++ b/test/integration/test/ports/engines/queries/filters/field_reference/relation_filter/_fixture/one-to-one-list/generated/contract.d.ts @@ -42,6 +42,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; @@ -91,6 +95,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; @@ -100,6 +108,14 @@ export type AggregateTypes = { readonly output: 'pg/time-temporal@1'; readonly nullable: true; }; + readonly 'pg/timestamp-date@1': { + readonly output: 'pg/timestamp-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-instant@1': { + readonly output: 'pg/timestamp-instant@1'; + readonly nullable: true; + }; readonly 'pg/timestamp-string@1': { readonly output: 'pg/timestamp-string@1'; readonly nullable: true; @@ -151,6 +167,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; @@ -160,6 +180,14 @@ export type AggregateTypes = { readonly output: 'pg/time-temporal@1'; readonly nullable: true; }; + readonly 'pg/timestamp-date@1': { + readonly output: 'pg/timestamp-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-instant@1': { + readonly output: 'pg/timestamp-instant@1'; + readonly nullable: true; + }; readonly 'pg/timestamp-string@1': { readonly output: 'pg/timestamp-string@1'; readonly nullable: true; @@ -203,6 +231,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; diff --git a/test/integration/test/ports/engines/queries/filters/field_reference/relation_filter/_fixture/one-to-one/generated/contract.d.ts b/test/integration/test/ports/engines/queries/filters/field_reference/relation_filter/_fixture/one-to-one/generated/contract.d.ts index b7e60375faf5..d9e48285c554 100644 --- a/test/integration/test/ports/engines/queries/filters/field_reference/relation_filter/_fixture/one-to-one/generated/contract.d.ts +++ b/test/integration/test/ports/engines/queries/filters/field_reference/relation_filter/_fixture/one-to-one/generated/contract.d.ts @@ -42,6 +42,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; @@ -91,6 +95,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; @@ -100,6 +108,14 @@ export type AggregateTypes = { readonly output: 'pg/time-temporal@1'; readonly nullable: true; }; + readonly 'pg/timestamp-date@1': { + readonly output: 'pg/timestamp-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-instant@1': { + readonly output: 'pg/timestamp-instant@1'; + readonly nullable: true; + }; readonly 'pg/timestamp-string@1': { readonly output: 'pg/timestamp-string@1'; readonly nullable: true; @@ -151,6 +167,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; @@ -160,6 +180,14 @@ export type AggregateTypes = { readonly output: 'pg/time-temporal@1'; readonly nullable: true; }; + readonly 'pg/timestamp-date@1': { + readonly output: 'pg/timestamp-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-instant@1': { + readonly output: 'pg/timestamp-instant@1'; + readonly nullable: true; + }; readonly 'pg/timestamp-string@1': { readonly output: 'pg/timestamp-string@1'; readonly nullable: true; @@ -203,6 +231,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; diff --git a/test/integration/test/ports/engines/queries/filters/field_reference/string_filter/_fixture/generated/contract.d.ts b/test/integration/test/ports/engines/queries/filters/field_reference/string_filter/_fixture/generated/contract.d.ts index 78c4ea4ee058..24984f967a5b 100644 --- a/test/integration/test/ports/engines/queries/filters/field_reference/string_filter/_fixture/generated/contract.d.ts +++ b/test/integration/test/ports/engines/queries/filters/field_reference/string_filter/_fixture/generated/contract.d.ts @@ -42,6 +42,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; @@ -91,6 +95,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; @@ -100,6 +108,14 @@ export type AggregateTypes = { readonly output: 'pg/time-temporal@1'; readonly nullable: true; }; + readonly 'pg/timestamp-date@1': { + readonly output: 'pg/timestamp-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-instant@1': { + readonly output: 'pg/timestamp-instant@1'; + readonly nullable: true; + }; readonly 'pg/timestamp-string@1': { readonly output: 'pg/timestamp-string@1'; readonly nullable: true; @@ -151,6 +167,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; @@ -160,6 +180,14 @@ export type AggregateTypes = { readonly output: 'pg/time-temporal@1'; readonly nullable: true; }; + readonly 'pg/timestamp-date@1': { + readonly output: 'pg/timestamp-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-instant@1': { + readonly output: 'pg/timestamp-instant@1'; + readonly nullable: true; + }; readonly 'pg/timestamp-string@1': { readonly output: 'pg/timestamp-string@1'; readonly nullable: true; @@ -203,6 +231,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; diff --git a/test/integration/test/ports/engines/queries/filters/filter_regression/_fixture/compound-one-to-many/generated/contract.d.ts b/test/integration/test/ports/engines/queries/filters/filter_regression/_fixture/compound-one-to-many/generated/contract.d.ts index 4174a8373043..b031cff28bbe 100644 --- a/test/integration/test/ports/engines/queries/filters/filter_regression/_fixture/compound-one-to-many/generated/contract.d.ts +++ b/test/integration/test/ports/engines/queries/filters/filter_regression/_fixture/compound-one-to-many/generated/contract.d.ts @@ -42,6 +42,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; @@ -91,6 +95,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; @@ -100,6 +108,14 @@ export type AggregateTypes = { readonly output: 'pg/time-temporal@1'; readonly nullable: true; }; + readonly 'pg/timestamp-date@1': { + readonly output: 'pg/timestamp-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-instant@1': { + readonly output: 'pg/timestamp-instant@1'; + readonly nullable: true; + }; readonly 'pg/timestamp-string@1': { readonly output: 'pg/timestamp-string@1'; readonly nullable: true; @@ -151,6 +167,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; @@ -160,6 +180,14 @@ export type AggregateTypes = { readonly output: 'pg/time-temporal@1'; readonly nullable: true; }; + readonly 'pg/timestamp-date@1': { + readonly output: 'pg/timestamp-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-instant@1': { + readonly output: 'pg/timestamp-instant@1'; + readonly nullable: true; + }; readonly 'pg/timestamp-string@1': { readonly output: 'pg/timestamp-string@1'; readonly nullable: true; @@ -203,6 +231,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; diff --git a/test/integration/test/ports/engines/queries/filters/filter_regression/_fixture/many-to-many/generated/contract.d.ts b/test/integration/test/ports/engines/queries/filters/filter_regression/_fixture/many-to-many/generated/contract.d.ts index 5e5fa69ad8bc..2227733b694d 100644 --- a/test/integration/test/ports/engines/queries/filters/filter_regression/_fixture/many-to-many/generated/contract.d.ts +++ b/test/integration/test/ports/engines/queries/filters/filter_regression/_fixture/many-to-many/generated/contract.d.ts @@ -42,6 +42,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; @@ -91,6 +95,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; @@ -100,6 +108,14 @@ export type AggregateTypes = { readonly output: 'pg/time-temporal@1'; readonly nullable: true; }; + readonly 'pg/timestamp-date@1': { + readonly output: 'pg/timestamp-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-instant@1': { + readonly output: 'pg/timestamp-instant@1'; + readonly nullable: true; + }; readonly 'pg/timestamp-string@1': { readonly output: 'pg/timestamp-string@1'; readonly nullable: true; @@ -151,6 +167,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; @@ -160,6 +180,14 @@ export type AggregateTypes = { readonly output: 'pg/time-temporal@1'; readonly nullable: true; }; + readonly 'pg/timestamp-date@1': { + readonly output: 'pg/timestamp-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-instant@1': { + readonly output: 'pg/timestamp-instant@1'; + readonly nullable: true; + }; readonly 'pg/timestamp-string@1': { readonly output: 'pg/timestamp-string@1'; readonly nullable: true; @@ -203,6 +231,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; diff --git a/test/integration/test/ports/engines/queries/filters/filter_regression/_fixture/one-to-many/generated/contract.d.ts b/test/integration/test/ports/engines/queries/filters/filter_regression/_fixture/one-to-many/generated/contract.d.ts index 67ae535f7b43..1f437aea1161 100644 --- a/test/integration/test/ports/engines/queries/filters/filter_regression/_fixture/one-to-many/generated/contract.d.ts +++ b/test/integration/test/ports/engines/queries/filters/filter_regression/_fixture/one-to-many/generated/contract.d.ts @@ -42,6 +42,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; @@ -91,6 +95,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; @@ -100,6 +108,14 @@ export type AggregateTypes = { readonly output: 'pg/time-temporal@1'; readonly nullable: true; }; + readonly 'pg/timestamp-date@1': { + readonly output: 'pg/timestamp-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-instant@1': { + readonly output: 'pg/timestamp-instant@1'; + readonly nullable: true; + }; readonly 'pg/timestamp-string@1': { readonly output: 'pg/timestamp-string@1'; readonly nullable: true; @@ -151,6 +167,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; @@ -160,6 +180,14 @@ export type AggregateTypes = { readonly output: 'pg/time-temporal@1'; readonly nullable: true; }; + readonly 'pg/timestamp-date@1': { + readonly output: 'pg/timestamp-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-instant@1': { + readonly output: 'pg/timestamp-instant@1'; + readonly nullable: true; + }; readonly 'pg/timestamp-string@1': { readonly output: 'pg/timestamp-string@1'; readonly nullable: true; @@ -203,6 +231,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; diff --git a/test/integration/test/ports/engines/queries/filters/filters/_fixture/generated/contract.d.ts b/test/integration/test/ports/engines/queries/filters/filters/_fixture/generated/contract.d.ts index ad1c8d24c302..2ddabd42d4d6 100644 --- a/test/integration/test/ports/engines/queries/filters/filters/_fixture/generated/contract.d.ts +++ b/test/integration/test/ports/engines/queries/filters/filters/_fixture/generated/contract.d.ts @@ -42,6 +42,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; @@ -91,6 +95,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; @@ -100,6 +108,14 @@ export type AggregateTypes = { readonly output: 'pg/time-temporal@1'; readonly nullable: true; }; + readonly 'pg/timestamp-date@1': { + readonly output: 'pg/timestamp-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-instant@1': { + readonly output: 'pg/timestamp-instant@1'; + readonly nullable: true; + }; readonly 'pg/timestamp-string@1': { readonly output: 'pg/timestamp-string@1'; readonly nullable: true; @@ -151,6 +167,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; @@ -160,6 +180,14 @@ export type AggregateTypes = { readonly output: 'pg/time-temporal@1'; readonly nullable: true; }; + readonly 'pg/timestamp-date@1': { + readonly output: 'pg/timestamp-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-instant@1': { + readonly output: 'pg/timestamp-instant@1'; + readonly nullable: true; + }; readonly 'pg/timestamp-string@1': { readonly output: 'pg/timestamp-string@1'; readonly nullable: true; @@ -203,6 +231,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; diff --git a/test/integration/test/ports/engines/queries/filters/json/_fixture/optional/generated/contract.d.ts b/test/integration/test/ports/engines/queries/filters/json/_fixture/optional/generated/contract.d.ts index e130b6241483..7954f1c9e0f4 100644 --- a/test/integration/test/ports/engines/queries/filters/json/_fixture/optional/generated/contract.d.ts +++ b/test/integration/test/ports/engines/queries/filters/json/_fixture/optional/generated/contract.d.ts @@ -42,6 +42,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; @@ -91,6 +95,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; @@ -100,6 +108,14 @@ export type AggregateTypes = { readonly output: 'pg/time-temporal@1'; readonly nullable: true; }; + readonly 'pg/timestamp-date@1': { + readonly output: 'pg/timestamp-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-instant@1': { + readonly output: 'pg/timestamp-instant@1'; + readonly nullable: true; + }; readonly 'pg/timestamp-string@1': { readonly output: 'pg/timestamp-string@1'; readonly nullable: true; @@ -151,6 +167,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; @@ -160,6 +180,14 @@ export type AggregateTypes = { readonly output: 'pg/time-temporal@1'; readonly nullable: true; }; + readonly 'pg/timestamp-date@1': { + readonly output: 'pg/timestamp-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-instant@1': { + readonly output: 'pg/timestamp-instant@1'; + readonly nullable: true; + }; readonly 'pg/timestamp-string@1': { readonly output: 'pg/timestamp-string@1'; readonly nullable: true; @@ -203,6 +231,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; diff --git a/test/integration/test/ports/engines/queries/filters/list_filters/_fixture/base/generated/contract.d.ts b/test/integration/test/ports/engines/queries/filters/list_filters/_fixture/base/generated/contract.d.ts index 098ab31609a3..30c19adfe6a7 100644 --- a/test/integration/test/ports/engines/queries/filters/list_filters/_fixture/base/generated/contract.d.ts +++ b/test/integration/test/ports/engines/queries/filters/list_filters/_fixture/base/generated/contract.d.ts @@ -42,6 +42,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; @@ -91,6 +95,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; @@ -100,6 +108,14 @@ export type AggregateTypes = { readonly output: 'pg/time-temporal@1'; readonly nullable: true; }; + readonly 'pg/timestamp-date@1': { + readonly output: 'pg/timestamp-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-instant@1': { + readonly output: 'pg/timestamp-instant@1'; + readonly nullable: true; + }; readonly 'pg/timestamp-string@1': { readonly output: 'pg/timestamp-string@1'; readonly nullable: true; @@ -151,6 +167,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; @@ -160,6 +180,14 @@ export type AggregateTypes = { readonly output: 'pg/time-temporal@1'; readonly nullable: true; }; + readonly 'pg/timestamp-date@1': { + readonly output: 'pg/timestamp-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-instant@1': { + readonly output: 'pg/timestamp-instant@1'; + readonly nullable: true; + }; readonly 'pg/timestamp-string@1': { readonly output: 'pg/timestamp-string@1'; readonly nullable: true; @@ -203,6 +231,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; diff --git a/test/integration/test/ports/engines/queries/filters/list_filters/_fixture/decimal/generated/contract.d.ts b/test/integration/test/ports/engines/queries/filters/list_filters/_fixture/decimal/generated/contract.d.ts index 5685dba19d73..dd1442dba7fc 100644 --- a/test/integration/test/ports/engines/queries/filters/list_filters/_fixture/decimal/generated/contract.d.ts +++ b/test/integration/test/ports/engines/queries/filters/list_filters/_fixture/decimal/generated/contract.d.ts @@ -42,6 +42,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; @@ -91,6 +95,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; @@ -100,6 +108,14 @@ export type AggregateTypes = { readonly output: 'pg/time-temporal@1'; readonly nullable: true; }; + readonly 'pg/timestamp-date@1': { + readonly output: 'pg/timestamp-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-instant@1': { + readonly output: 'pg/timestamp-instant@1'; + readonly nullable: true; + }; readonly 'pg/timestamp-string@1': { readonly output: 'pg/timestamp-string@1'; readonly nullable: true; @@ -151,6 +167,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; @@ -160,6 +180,14 @@ export type AggregateTypes = { readonly output: 'pg/time-temporal@1'; readonly nullable: true; }; + readonly 'pg/timestamp-date@1': { + readonly output: 'pg/timestamp-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-instant@1': { + readonly output: 'pg/timestamp-instant@1'; + readonly nullable: true; + }; readonly 'pg/timestamp-string@1': { readonly output: 'pg/timestamp-string@1'; readonly nullable: true; @@ -203,6 +231,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; diff --git a/test/integration/test/ports/engines/queries/filters/list_filters/_fixture/json/generated/contract.d.ts b/test/integration/test/ports/engines/queries/filters/list_filters/_fixture/json/generated/contract.d.ts index d45c4f6c6374..dc87644ec604 100644 --- a/test/integration/test/ports/engines/queries/filters/list_filters/_fixture/json/generated/contract.d.ts +++ b/test/integration/test/ports/engines/queries/filters/list_filters/_fixture/json/generated/contract.d.ts @@ -42,6 +42,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; @@ -91,6 +95,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; @@ -100,6 +108,14 @@ export type AggregateTypes = { readonly output: 'pg/time-temporal@1'; readonly nullable: true; }; + readonly 'pg/timestamp-date@1': { + readonly output: 'pg/timestamp-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-instant@1': { + readonly output: 'pg/timestamp-instant@1'; + readonly nullable: true; + }; readonly 'pg/timestamp-string@1': { readonly output: 'pg/timestamp-string@1'; readonly nullable: true; @@ -151,6 +167,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; @@ -160,6 +180,14 @@ export type AggregateTypes = { readonly output: 'pg/time-temporal@1'; readonly nullable: true; }; + readonly 'pg/timestamp-date@1': { + readonly output: 'pg/timestamp-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-instant@1': { + readonly output: 'pg/timestamp-instant@1'; + readonly nullable: true; + }; readonly 'pg/timestamp-string@1': { readonly output: 'pg/timestamp-string@1'; readonly nullable: true; @@ -203,6 +231,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; diff --git a/test/integration/test/ports/engines/queries/filters/many_relation/_fixture/25103/generated/contract.d.ts b/test/integration/test/ports/engines/queries/filters/many_relation/_fixture/25103/generated/contract.d.ts index 0d74fe3722f7..1f864c7b304d 100644 --- a/test/integration/test/ports/engines/queries/filters/many_relation/_fixture/25103/generated/contract.d.ts +++ b/test/integration/test/ports/engines/queries/filters/many_relation/_fixture/25103/generated/contract.d.ts @@ -42,6 +42,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; @@ -91,6 +95,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; @@ -100,6 +108,14 @@ export type AggregateTypes = { readonly output: 'pg/time-temporal@1'; readonly nullable: true; }; + readonly 'pg/timestamp-date@1': { + readonly output: 'pg/timestamp-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-instant@1': { + readonly output: 'pg/timestamp-instant@1'; + readonly nullable: true; + }; readonly 'pg/timestamp-string@1': { readonly output: 'pg/timestamp-string@1'; readonly nullable: true; @@ -151,6 +167,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; @@ -160,6 +180,14 @@ export type AggregateTypes = { readonly output: 'pg/time-temporal@1'; readonly nullable: true; }; + readonly 'pg/timestamp-date@1': { + readonly output: 'pg/timestamp-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-instant@1': { + readonly output: 'pg/timestamp-instant@1'; + readonly nullable: true; + }; readonly 'pg/timestamp-string@1': { readonly output: 'pg/timestamp-string@1'; readonly nullable: true; @@ -203,6 +231,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; diff --git a/test/integration/test/ports/engines/queries/filters/many_relation/_fixture/l2-to-one/generated/contract.d.ts b/test/integration/test/ports/engines/queries/filters/many_relation/_fixture/l2-to-one/generated/contract.d.ts index aa3036bb93bf..1c82d448e810 100644 --- a/test/integration/test/ports/engines/queries/filters/many_relation/_fixture/l2-to-one/generated/contract.d.ts +++ b/test/integration/test/ports/engines/queries/filters/many_relation/_fixture/l2-to-one/generated/contract.d.ts @@ -42,6 +42,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; @@ -91,6 +95,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; @@ -100,6 +108,14 @@ export type AggregateTypes = { readonly output: 'pg/time-temporal@1'; readonly nullable: true; }; + readonly 'pg/timestamp-date@1': { + readonly output: 'pg/timestamp-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-instant@1': { + readonly output: 'pg/timestamp-instant@1'; + readonly nullable: true; + }; readonly 'pg/timestamp-string@1': { readonly output: 'pg/timestamp-string@1'; readonly nullable: true; @@ -151,6 +167,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; @@ -160,6 +180,14 @@ export type AggregateTypes = { readonly output: 'pg/time-temporal@1'; readonly nullable: true; }; + readonly 'pg/timestamp-date@1': { + readonly output: 'pg/timestamp-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-instant@1': { + readonly output: 'pg/timestamp-instant@1'; + readonly nullable: true; + }; readonly 'pg/timestamp-string@1': { readonly output: 'pg/timestamp-string@1'; readonly nullable: true; @@ -203,6 +231,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; diff --git a/test/integration/test/ports/engines/queries/filters/one2one_regression/_fixture/generated/contract.d.ts b/test/integration/test/ports/engines/queries/filters/one2one_regression/_fixture/generated/contract.d.ts index 1c8f04641d0d..8a65c47cda27 100644 --- a/test/integration/test/ports/engines/queries/filters/one2one_regression/_fixture/generated/contract.d.ts +++ b/test/integration/test/ports/engines/queries/filters/one2one_regression/_fixture/generated/contract.d.ts @@ -42,6 +42,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; @@ -91,6 +95,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; @@ -100,6 +108,14 @@ export type AggregateTypes = { readonly output: 'pg/time-temporal@1'; readonly nullable: true; }; + readonly 'pg/timestamp-date@1': { + readonly output: 'pg/timestamp-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-instant@1': { + readonly output: 'pg/timestamp-instant@1'; + readonly nullable: true; + }; readonly 'pg/timestamp-string@1': { readonly output: 'pg/timestamp-string@1'; readonly nullable: true; @@ -151,6 +167,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; @@ -160,6 +180,14 @@ export type AggregateTypes = { readonly output: 'pg/time-temporal@1'; readonly nullable: true; }; + readonly 'pg/timestamp-date@1': { + readonly output: 'pg/timestamp-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-instant@1': { + readonly output: 'pg/timestamp-instant@1'; + readonly nullable: true; + }; readonly 'pg/timestamp-string@1': { readonly output: 'pg/timestamp-string@1'; readonly nullable: true; @@ -203,6 +231,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; diff --git a/test/integration/test/ports/engines/queries/filters/one_relation/_fixture/21356/generated/contract.d.ts b/test/integration/test/ports/engines/queries/filters/one_relation/_fixture/21356/generated/contract.d.ts index cc698102ba89..49039430a9a4 100644 --- a/test/integration/test/ports/engines/queries/filters/one_relation/_fixture/21356/generated/contract.d.ts +++ b/test/integration/test/ports/engines/queries/filters/one_relation/_fixture/21356/generated/contract.d.ts @@ -42,6 +42,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; @@ -91,6 +95,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; @@ -100,6 +108,14 @@ export type AggregateTypes = { readonly output: 'pg/time-temporal@1'; readonly nullable: true; }; + readonly 'pg/timestamp-date@1': { + readonly output: 'pg/timestamp-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-instant@1': { + readonly output: 'pg/timestamp-instant@1'; + readonly nullable: true; + }; readonly 'pg/timestamp-string@1': { readonly output: 'pg/timestamp-string@1'; readonly nullable: true; @@ -151,6 +167,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; @@ -160,6 +180,14 @@ export type AggregateTypes = { readonly output: 'pg/time-temporal@1'; readonly nullable: true; }; + readonly 'pg/timestamp-date@1': { + readonly output: 'pg/timestamp-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-instant@1': { + readonly output: 'pg/timestamp-instant@1'; + readonly nullable: true; + }; readonly 'pg/timestamp-string@1': { readonly output: 'pg/timestamp-string@1'; readonly nullable: true; @@ -203,6 +231,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; diff --git a/test/integration/test/ports/engines/queries/filters/one_relation/_fixture/21366/generated/contract.d.ts b/test/integration/test/ports/engines/queries/filters/one_relation/_fixture/21366/generated/contract.d.ts index 3817b98f7b38..7a4d85020407 100644 --- a/test/integration/test/ports/engines/queries/filters/one_relation/_fixture/21366/generated/contract.d.ts +++ b/test/integration/test/ports/engines/queries/filters/one_relation/_fixture/21366/generated/contract.d.ts @@ -42,6 +42,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; @@ -91,6 +95,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; @@ -100,6 +108,14 @@ export type AggregateTypes = { readonly output: 'pg/time-temporal@1'; readonly nullable: true; }; + readonly 'pg/timestamp-date@1': { + readonly output: 'pg/timestamp-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-instant@1': { + readonly output: 'pg/timestamp-instant@1'; + readonly nullable: true; + }; readonly 'pg/timestamp-string@1': { readonly output: 'pg/timestamp-string@1'; readonly nullable: true; @@ -151,6 +167,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; @@ -160,6 +180,14 @@ export type AggregateTypes = { readonly output: 'pg/time-temporal@1'; readonly nullable: true; }; + readonly 'pg/timestamp-date@1': { + readonly output: 'pg/timestamp-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-instant@1': { + readonly output: 'pg/timestamp-instant@1'; + readonly nullable: true; + }; readonly 'pg/timestamp-string@1': { readonly output: 'pg/timestamp-string@1'; readonly nullable: true; @@ -203,6 +231,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; diff --git a/test/integration/test/ports/engines/writes/top_level_mutations/create_many/_fixture/generated/contract.d.ts b/test/integration/test/ports/engines/writes/top_level_mutations/create_many/_fixture/generated/contract.d.ts index f7eb12bb26c1..f51ef42eef15 100644 --- a/test/integration/test/ports/engines/writes/top_level_mutations/create_many/_fixture/generated/contract.d.ts +++ b/test/integration/test/ports/engines/writes/top_level_mutations/create_many/_fixture/generated/contract.d.ts @@ -42,6 +42,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; @@ -91,6 +95,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; @@ -100,6 +108,14 @@ export type AggregateTypes = { readonly output: 'pg/time-temporal@1'; readonly nullable: true; }; + readonly 'pg/timestamp-date@1': { + readonly output: 'pg/timestamp-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-instant@1': { + readonly output: 'pg/timestamp-instant@1'; + readonly nullable: true; + }; readonly 'pg/timestamp-string@1': { readonly output: 'pg/timestamp-string@1'; readonly nullable: true; @@ -151,6 +167,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; @@ -160,6 +180,14 @@ export type AggregateTypes = { readonly output: 'pg/time-temporal@1'; readonly nullable: true; }; + readonly 'pg/timestamp-date@1': { + readonly output: 'pg/timestamp-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-instant@1': { + readonly output: 'pg/timestamp-instant@1'; + readonly nullable: true; + }; readonly 'pg/timestamp-string@1': { readonly output: 'pg/timestamp-string@1'; readonly nullable: true; @@ -203,6 +231,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; diff --git a/test/integration/test/ports/prisma/functional/batching-bigint/_fixture/generated/contract.d.ts b/test/integration/test/ports/prisma/functional/batching-bigint/_fixture/generated/contract.d.ts index 5442c2ece6c6..035c51c5faa0 100644 --- a/test/integration/test/ports/prisma/functional/batching-bigint/_fixture/generated/contract.d.ts +++ b/test/integration/test/ports/prisma/functional/batching-bigint/_fixture/generated/contract.d.ts @@ -42,6 +42,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; @@ -91,6 +95,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; @@ -100,6 +108,14 @@ export type AggregateTypes = { readonly output: 'pg/time-temporal@1'; readonly nullable: true; }; + readonly 'pg/timestamp-date@1': { + readonly output: 'pg/timestamp-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-instant@1': { + readonly output: 'pg/timestamp-instant@1'; + readonly nullable: true; + }; readonly 'pg/timestamp-string@1': { readonly output: 'pg/timestamp-string@1'; readonly nullable: true; @@ -151,6 +167,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; @@ -160,6 +180,14 @@ export type AggregateTypes = { readonly output: 'pg/time-temporal@1'; readonly nullable: true; }; + readonly 'pg/timestamp-date@1': { + readonly output: 'pg/timestamp-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-instant@1': { + readonly output: 'pg/timestamp-instant@1'; + readonly nullable: true; + }; readonly 'pg/timestamp-string@1': { readonly output: 'pg/timestamp-string@1'; readonly nullable: true; @@ -203,6 +231,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; diff --git a/test/integration/test/ports/prisma/functional/batching-bytes/_fixture/generated/contract.d.ts b/test/integration/test/ports/prisma/functional/batching-bytes/_fixture/generated/contract.d.ts index df41e3e37ff0..cd04ffed0ad3 100644 --- a/test/integration/test/ports/prisma/functional/batching-bytes/_fixture/generated/contract.d.ts +++ b/test/integration/test/ports/prisma/functional/batching-bytes/_fixture/generated/contract.d.ts @@ -42,6 +42,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; @@ -91,6 +95,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; @@ -100,6 +108,14 @@ export type AggregateTypes = { readonly output: 'pg/time-temporal@1'; readonly nullable: true; }; + readonly 'pg/timestamp-date@1': { + readonly output: 'pg/timestamp-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-instant@1': { + readonly output: 'pg/timestamp-instant@1'; + readonly nullable: true; + }; readonly 'pg/timestamp-string@1': { readonly output: 'pg/timestamp-string@1'; readonly nullable: true; @@ -151,6 +167,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; @@ -160,6 +180,14 @@ export type AggregateTypes = { readonly output: 'pg/time-temporal@1'; readonly nullable: true; }; + readonly 'pg/timestamp-date@1': { + readonly output: 'pg/timestamp-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-instant@1': { + readonly output: 'pg/timestamp-instant@1'; + readonly nullable: true; + }; readonly 'pg/timestamp-string@1': { readonly output: 'pg/timestamp-string@1'; readonly nullable: true; @@ -203,6 +231,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; diff --git a/test/integration/test/ports/prisma/functional/blog-update/_fixture/generated/contract.d.ts b/test/integration/test/ports/prisma/functional/blog-update/_fixture/generated/contract.d.ts index 212e70c23d1e..76f033b38a65 100644 --- a/test/integration/test/ports/prisma/functional/blog-update/_fixture/generated/contract.d.ts +++ b/test/integration/test/ports/prisma/functional/blog-update/_fixture/generated/contract.d.ts @@ -43,6 +43,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; @@ -92,6 +96,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; @@ -101,6 +109,14 @@ export type AggregateTypes = { readonly output: 'pg/time-temporal@1'; readonly nullable: true; }; + readonly 'pg/timestamp-date@1': { + readonly output: 'pg/timestamp-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-instant@1': { + readonly output: 'pg/timestamp-instant@1'; + readonly nullable: true; + }; readonly 'pg/timestamp-string@1': { readonly output: 'pg/timestamp-string@1'; readonly nullable: true; @@ -152,6 +168,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; @@ -161,6 +181,14 @@ export type AggregateTypes = { readonly output: 'pg/time-temporal@1'; readonly nullable: true; }; + readonly 'pg/timestamp-date@1': { + readonly output: 'pg/timestamp-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-instant@1': { + readonly output: 'pg/timestamp-instant@1'; + readonly nullable: true; + }; readonly 'pg/timestamp-string@1': { readonly output: 'pg/timestamp-string@1'; readonly nullable: true; @@ -204,6 +232,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; diff --git a/test/integration/test/ports/prisma/functional/bytes-upsert/_fixture/generated/contract.d.ts b/test/integration/test/ports/prisma/functional/bytes-upsert/_fixture/generated/contract.d.ts index 11a91f24ad19..e06dfaa7a1d4 100644 --- a/test/integration/test/ports/prisma/functional/bytes-upsert/_fixture/generated/contract.d.ts +++ b/test/integration/test/ports/prisma/functional/bytes-upsert/_fixture/generated/contract.d.ts @@ -43,6 +43,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; @@ -92,6 +96,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; @@ -101,6 +109,14 @@ export type AggregateTypes = { readonly output: 'pg/time-temporal@1'; readonly nullable: true; }; + readonly 'pg/timestamp-date@1': { + readonly output: 'pg/timestamp-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-instant@1': { + readonly output: 'pg/timestamp-instant@1'; + readonly nullable: true; + }; readonly 'pg/timestamp-string@1': { readonly output: 'pg/timestamp-string@1'; readonly nullable: true; @@ -152,6 +168,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; @@ -161,6 +181,14 @@ export type AggregateTypes = { readonly output: 'pg/time-temporal@1'; readonly nullable: true; }; + readonly 'pg/timestamp-date@1': { + readonly output: 'pg/timestamp-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-instant@1': { + readonly output: 'pg/timestamp-instant@1'; + readonly nullable: true; + }; readonly 'pg/timestamp-string@1': { readonly output: 'pg/timestamp-string@1'; readonly nullable: true; @@ -204,6 +232,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; diff --git a/test/integration/test/ports/prisma/functional/chunking-query/_fixture/generated/contract.d.ts b/test/integration/test/ports/prisma/functional/chunking-query/_fixture/generated/contract.d.ts index 4f788737d495..9f9fd7a0ef19 100644 --- a/test/integration/test/ports/prisma/functional/chunking-query/_fixture/generated/contract.d.ts +++ b/test/integration/test/ports/prisma/functional/chunking-query/_fixture/generated/contract.d.ts @@ -42,6 +42,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; @@ -91,6 +95,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; @@ -100,6 +108,14 @@ export type AggregateTypes = { readonly output: 'pg/time-temporal@1'; readonly nullable: true; }; + readonly 'pg/timestamp-date@1': { + readonly output: 'pg/timestamp-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-instant@1': { + readonly output: 'pg/timestamp-instant@1'; + readonly nullable: true; + }; readonly 'pg/timestamp-string@1': { readonly output: 'pg/timestamp-string@1'; readonly nullable: true; @@ -151,6 +167,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; @@ -160,6 +180,14 @@ export type AggregateTypes = { readonly output: 'pg/time-temporal@1'; readonly nullable: true; }; + readonly 'pg/timestamp-date@1': { + readonly output: 'pg/timestamp-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-instant@1': { + readonly output: 'pg/timestamp-instant@1'; + readonly nullable: true; + }; readonly 'pg/timestamp-string@1': { readonly output: 'pg/timestamp-string@1'; readonly nullable: true; @@ -203,6 +231,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; diff --git a/test/integration/test/ports/prisma/functional/create-default-date/_fixture/generated/contract.d.ts b/test/integration/test/ports/prisma/functional/create-default-date/_fixture/generated/contract.d.ts index 7503f6f24167..9772fe413043 100644 --- a/test/integration/test/ports/prisma/functional/create-default-date/_fixture/generated/contract.d.ts +++ b/test/integration/test/ports/prisma/functional/create-default-date/_fixture/generated/contract.d.ts @@ -42,6 +42,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; @@ -91,6 +95,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; @@ -100,6 +108,14 @@ export type AggregateTypes = { readonly output: 'pg/time-temporal@1'; readonly nullable: true; }; + readonly 'pg/timestamp-date@1': { + readonly output: 'pg/timestamp-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-instant@1': { + readonly output: 'pg/timestamp-instant@1'; + readonly nullable: true; + }; readonly 'pg/timestamp-string@1': { readonly output: 'pg/timestamp-string@1'; readonly nullable: true; @@ -151,6 +167,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; @@ -160,6 +180,14 @@ export type AggregateTypes = { readonly output: 'pg/time-temporal@1'; readonly nullable: true; }; + readonly 'pg/timestamp-date@1': { + readonly output: 'pg/timestamp-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-instant@1': { + readonly output: 'pg/timestamp-instant@1'; + readonly nullable: true; + }; readonly 'pg/timestamp-string@1': { readonly output: 'pg/timestamp-string@1'; readonly nullable: true; @@ -203,6 +231,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; diff --git a/test/integration/test/ports/prisma/functional/decimal-list/_fixture/generated/contract.d.ts b/test/integration/test/ports/prisma/functional/decimal-list/_fixture/generated/contract.d.ts index 05c908043cd3..9e9bace0da98 100644 --- a/test/integration/test/ports/prisma/functional/decimal-list/_fixture/generated/contract.d.ts +++ b/test/integration/test/ports/prisma/functional/decimal-list/_fixture/generated/contract.d.ts @@ -43,6 +43,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; @@ -92,6 +96,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; @@ -101,6 +109,14 @@ export type AggregateTypes = { readonly output: 'pg/time-temporal@1'; readonly nullable: true; }; + readonly 'pg/timestamp-date@1': { + readonly output: 'pg/timestamp-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-instant@1': { + readonly output: 'pg/timestamp-instant@1'; + readonly nullable: true; + }; readonly 'pg/timestamp-string@1': { readonly output: 'pg/timestamp-string@1'; readonly nullable: true; @@ -152,6 +168,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; @@ -161,6 +181,14 @@ export type AggregateTypes = { readonly output: 'pg/time-temporal@1'; readonly nullable: true; }; + readonly 'pg/timestamp-date@1': { + readonly output: 'pg/timestamp-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-instant@1': { + readonly output: 'pg/timestamp-instant@1'; + readonly nullable: true; + }; readonly 'pg/timestamp-string@1': { readonly output: 'pg/timestamp-string@1'; readonly nullable: true; @@ -204,6 +232,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; diff --git a/test/integration/test/ports/prisma/functional/decimal-precision/_fixture/generated/contract.d.ts b/test/integration/test/ports/prisma/functional/decimal-precision/_fixture/generated/contract.d.ts index 072b88e9d9ff..7ba79bb02b38 100644 --- a/test/integration/test/ports/prisma/functional/decimal-precision/_fixture/generated/contract.d.ts +++ b/test/integration/test/ports/prisma/functional/decimal-precision/_fixture/generated/contract.d.ts @@ -43,6 +43,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; @@ -92,6 +96,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; @@ -101,6 +109,14 @@ export type AggregateTypes = { readonly output: 'pg/time-temporal@1'; readonly nullable: true; }; + readonly 'pg/timestamp-date@1': { + readonly output: 'pg/timestamp-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-instant@1': { + readonly output: 'pg/timestamp-instant@1'; + readonly nullable: true; + }; readonly 'pg/timestamp-string@1': { readonly output: 'pg/timestamp-string@1'; readonly nullable: true; @@ -152,6 +168,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; @@ -161,6 +181,14 @@ export type AggregateTypes = { readonly output: 'pg/time-temporal@1'; readonly nullable: true; }; + readonly 'pg/timestamp-date@1': { + readonly output: 'pg/timestamp-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-instant@1': { + readonly output: 'pg/timestamp-instant@1'; + readonly nullable: true; + }; readonly 'pg/timestamp-string@1': { readonly output: 'pg/timestamp-string@1'; readonly nullable: true; @@ -204,6 +232,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; diff --git a/test/integration/test/ports/prisma/functional/decimal-scalar/_fixture/generated/contract.d.ts b/test/integration/test/ports/prisma/functional/decimal-scalar/_fixture/generated/contract.d.ts index a2efef065de8..2b9d895de131 100644 --- a/test/integration/test/ports/prisma/functional/decimal-scalar/_fixture/generated/contract.d.ts +++ b/test/integration/test/ports/prisma/functional/decimal-scalar/_fixture/generated/contract.d.ts @@ -43,6 +43,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; @@ -92,6 +96,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; @@ -101,6 +109,14 @@ export type AggregateTypes = { readonly output: 'pg/time-temporal@1'; readonly nullable: true; }; + readonly 'pg/timestamp-date@1': { + readonly output: 'pg/timestamp-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-instant@1': { + readonly output: 'pg/timestamp-instant@1'; + readonly nullable: true; + }; readonly 'pg/timestamp-string@1': { readonly output: 'pg/timestamp-string@1'; readonly nullable: true; @@ -152,6 +168,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; @@ -161,6 +181,14 @@ export type AggregateTypes = { readonly output: 'pg/time-temporal@1'; readonly nullable: true; }; + readonly 'pg/timestamp-date@1': { + readonly output: 'pg/timestamp-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-instant@1': { + readonly output: 'pg/timestamp-instant@1'; + readonly nullable: true; + }; readonly 'pg/timestamp-string@1': { readonly output: 'pg/timestamp-string@1'; readonly nullable: true; @@ -204,6 +232,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; diff --git a/test/integration/test/ports/prisma/functional/default-selection/_fixture/generated/contract.d.ts b/test/integration/test/ports/prisma/functional/default-selection/_fixture/generated/contract.d.ts index ff585733f252..fd9cc7c247a0 100644 --- a/test/integration/test/ports/prisma/functional/default-selection/_fixture/generated/contract.d.ts +++ b/test/integration/test/ports/prisma/functional/default-selection/_fixture/generated/contract.d.ts @@ -42,6 +42,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; @@ -91,6 +95,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; @@ -100,6 +108,14 @@ export type AggregateTypes = { readonly output: 'pg/time-temporal@1'; readonly nullable: true; }; + readonly 'pg/timestamp-date@1': { + readonly output: 'pg/timestamp-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-instant@1': { + readonly output: 'pg/timestamp-instant@1'; + readonly nullable: true; + }; readonly 'pg/timestamp-string@1': { readonly output: 'pg/timestamp-string@1'; readonly nullable: true; @@ -151,6 +167,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; @@ -160,6 +180,14 @@ export type AggregateTypes = { readonly output: 'pg/time-temporal@1'; readonly nullable: true; }; + readonly 'pg/timestamp-date@1': { + readonly output: 'pg/timestamp-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-instant@1': { + readonly output: 'pg/timestamp-instant@1'; + readonly nullable: true; + }; readonly 'pg/timestamp-string@1': { readonly output: 'pg/timestamp-string@1'; readonly nullable: true; @@ -203,6 +231,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; diff --git a/test/integration/test/ports/prisma/functional/distinct/_fixture/generated/contract.d.ts b/test/integration/test/ports/prisma/functional/distinct/_fixture/generated/contract.d.ts index 35a444c683c8..ed89b33c639d 100644 --- a/test/integration/test/ports/prisma/functional/distinct/_fixture/generated/contract.d.ts +++ b/test/integration/test/ports/prisma/functional/distinct/_fixture/generated/contract.d.ts @@ -42,6 +42,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; @@ -91,6 +95,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; @@ -100,6 +108,14 @@ export type AggregateTypes = { readonly output: 'pg/time-temporal@1'; readonly nullable: true; }; + readonly 'pg/timestamp-date@1': { + readonly output: 'pg/timestamp-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-instant@1': { + readonly output: 'pg/timestamp-instant@1'; + readonly nullable: true; + }; readonly 'pg/timestamp-string@1': { readonly output: 'pg/timestamp-string@1'; readonly nullable: true; @@ -151,6 +167,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; @@ -160,6 +180,14 @@ export type AggregateTypes = { readonly output: 'pg/time-temporal@1'; readonly nullable: true; }; + readonly 'pg/timestamp-date@1': { + readonly output: 'pg/timestamp-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-instant@1': { + readonly output: 'pg/timestamp-instant@1'; + readonly nullable: true; + }; readonly 'pg/timestamp-string@1': { readonly output: 'pg/timestamp-string@1'; readonly nullable: true; @@ -203,6 +231,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; diff --git a/test/integration/test/ports/prisma/functional/driver-adapters-error-forwarding/_fixture/generated/contract.d.ts b/test/integration/test/ports/prisma/functional/driver-adapters-error-forwarding/_fixture/generated/contract.d.ts index 434588009d75..2454e4dcaae8 100644 --- a/test/integration/test/ports/prisma/functional/driver-adapters-error-forwarding/_fixture/generated/contract.d.ts +++ b/test/integration/test/ports/prisma/functional/driver-adapters-error-forwarding/_fixture/generated/contract.d.ts @@ -42,6 +42,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; @@ -91,6 +95,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; @@ -100,6 +108,14 @@ export type AggregateTypes = { readonly output: 'pg/time-temporal@1'; readonly nullable: true; }; + readonly 'pg/timestamp-date@1': { + readonly output: 'pg/timestamp-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-instant@1': { + readonly output: 'pg/timestamp-instant@1'; + readonly nullable: true; + }; readonly 'pg/timestamp-string@1': { readonly output: 'pg/timestamp-string@1'; readonly nullable: true; @@ -151,6 +167,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; @@ -160,6 +180,14 @@ export type AggregateTypes = { readonly output: 'pg/time-temporal@1'; readonly nullable: true; }; + readonly 'pg/timestamp-date@1': { + readonly output: 'pg/timestamp-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-instant@1': { + readonly output: 'pg/timestamp-instant@1'; + readonly nullable: true; + }; readonly 'pg/timestamp-string@1': { readonly output: 'pg/timestamp-string@1'; readonly nullable: true; @@ -203,6 +231,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; diff --git a/test/integration/test/ports/prisma/functional/driver-adapters-team-orm-687-bytes/_fixture/generated/contract.d.ts b/test/integration/test/ports/prisma/functional/driver-adapters-team-orm-687-bytes/_fixture/generated/contract.d.ts index cb4957706b68..f616735d3de3 100644 --- a/test/integration/test/ports/prisma/functional/driver-adapters-team-orm-687-bytes/_fixture/generated/contract.d.ts +++ b/test/integration/test/ports/prisma/functional/driver-adapters-team-orm-687-bytes/_fixture/generated/contract.d.ts @@ -42,6 +42,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; @@ -91,6 +95,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; @@ -100,6 +108,14 @@ export type AggregateTypes = { readonly output: 'pg/time-temporal@1'; readonly nullable: true; }; + readonly 'pg/timestamp-date@1': { + readonly output: 'pg/timestamp-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-instant@1': { + readonly output: 'pg/timestamp-instant@1'; + readonly nullable: true; + }; readonly 'pg/timestamp-string@1': { readonly output: 'pg/timestamp-string@1'; readonly nullable: true; @@ -151,6 +167,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; @@ -160,6 +180,14 @@ export type AggregateTypes = { readonly output: 'pg/time-temporal@1'; readonly nullable: true; }; + readonly 'pg/timestamp-date@1': { + readonly output: 'pg/timestamp-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-instant@1': { + readonly output: 'pg/timestamp-instant@1'; + readonly nullable: true; + }; readonly 'pg/timestamp-string@1': { readonly output: 'pg/timestamp-string@1'; readonly nullable: true; @@ -203,6 +231,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; diff --git a/test/integration/test/ports/prisma/functional/driver-adapters-validate-active-provider/_fixture/generated/contract.d.ts b/test/integration/test/ports/prisma/functional/driver-adapters-validate-active-provider/_fixture/generated/contract.d.ts index 8246d093e8e6..6ff68a7d15e8 100644 --- a/test/integration/test/ports/prisma/functional/driver-adapters-validate-active-provider/_fixture/generated/contract.d.ts +++ b/test/integration/test/ports/prisma/functional/driver-adapters-validate-active-provider/_fixture/generated/contract.d.ts @@ -42,6 +42,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; @@ -91,6 +95,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; @@ -100,6 +108,14 @@ export type AggregateTypes = { readonly output: 'pg/time-temporal@1'; readonly nullable: true; }; + readonly 'pg/timestamp-date@1': { + readonly output: 'pg/timestamp-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-instant@1': { + readonly output: 'pg/timestamp-instant@1'; + readonly nullable: true; + }; readonly 'pg/timestamp-string@1': { readonly output: 'pg/timestamp-string@1'; readonly nullable: true; @@ -151,6 +167,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; @@ -160,6 +180,14 @@ export type AggregateTypes = { readonly output: 'pg/time-temporal@1'; readonly nullable: true; }; + readonly 'pg/timestamp-date@1': { + readonly output: 'pg/timestamp-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-instant@1': { + readonly output: 'pg/timestamp-instant@1'; + readonly nullable: true; + }; readonly 'pg/timestamp-string@1': { readonly output: 'pg/timestamp-string@1'; readonly nullable: true; @@ -203,6 +231,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; diff --git a/test/integration/test/ports/prisma/functional/enum-array/_fixture/generated/contract.d.ts b/test/integration/test/ports/prisma/functional/enum-array/_fixture/generated/contract.d.ts index 558b18e08eea..282b6df62c34 100644 --- a/test/integration/test/ports/prisma/functional/enum-array/_fixture/generated/contract.d.ts +++ b/test/integration/test/ports/prisma/functional/enum-array/_fixture/generated/contract.d.ts @@ -43,6 +43,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; @@ -92,6 +96,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; @@ -101,6 +109,14 @@ export type AggregateTypes = { readonly output: 'pg/time-temporal@1'; readonly nullable: true; }; + readonly 'pg/timestamp-date@1': { + readonly output: 'pg/timestamp-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-instant@1': { + readonly output: 'pg/timestamp-instant@1'; + readonly nullable: true; + }; readonly 'pg/timestamp-string@1': { readonly output: 'pg/timestamp-string@1'; readonly nullable: true; @@ -152,6 +168,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; @@ -161,6 +181,14 @@ export type AggregateTypes = { readonly output: 'pg/time-temporal@1'; readonly nullable: true; }; + readonly 'pg/timestamp-date@1': { + readonly output: 'pg/timestamp-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-instant@1': { + readonly output: 'pg/timestamp-instant@1'; + readonly nullable: true; + }; readonly 'pg/timestamp-string@1': { readonly output: 'pg/timestamp-string@1'; readonly nullable: true; @@ -204,6 +232,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; diff --git a/test/integration/test/ports/prisma/functional/extended-where/_fixture/generated/contract.d.ts b/test/integration/test/ports/prisma/functional/extended-where/_fixture/generated/contract.d.ts index 0f252678bacf..e52a151f5303 100644 --- a/test/integration/test/ports/prisma/functional/extended-where/_fixture/generated/contract.d.ts +++ b/test/integration/test/ports/prisma/functional/extended-where/_fixture/generated/contract.d.ts @@ -43,6 +43,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; @@ -92,6 +96,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; @@ -101,6 +109,14 @@ export type AggregateTypes = { readonly output: 'pg/time-temporal@1'; readonly nullable: true; }; + readonly 'pg/timestamp-date@1': { + readonly output: 'pg/timestamp-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-instant@1': { + readonly output: 'pg/timestamp-instant@1'; + readonly nullable: true; + }; readonly 'pg/timestamp-string@1': { readonly output: 'pg/timestamp-string@1'; readonly nullable: true; @@ -152,6 +168,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; @@ -161,6 +181,14 @@ export type AggregateTypes = { readonly output: 'pg/time-temporal@1'; readonly nullable: true; }; + readonly 'pg/timestamp-date@1': { + readonly output: 'pg/timestamp-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-instant@1': { + readonly output: 'pg/timestamp-instant@1'; + readonly nullable: true; + }; readonly 'pg/timestamp-string@1': { readonly output: 'pg/timestamp-string@1'; readonly nullable: true; @@ -204,6 +232,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; diff --git a/test/integration/test/ports/prisma/functional/filter-count-relations/_fixture/generated/contract.d.ts b/test/integration/test/ports/prisma/functional/filter-count-relations/_fixture/generated/contract.d.ts index 7220221939cd..bba4c91b2db8 100644 --- a/test/integration/test/ports/prisma/functional/filter-count-relations/_fixture/generated/contract.d.ts +++ b/test/integration/test/ports/prisma/functional/filter-count-relations/_fixture/generated/contract.d.ts @@ -42,6 +42,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; @@ -91,6 +95,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; @@ -100,6 +108,14 @@ export type AggregateTypes = { readonly output: 'pg/time-temporal@1'; readonly nullable: true; }; + readonly 'pg/timestamp-date@1': { + readonly output: 'pg/timestamp-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-instant@1': { + readonly output: 'pg/timestamp-instant@1'; + readonly nullable: true; + }; readonly 'pg/timestamp-string@1': { readonly output: 'pg/timestamp-string@1'; readonly nullable: true; @@ -151,6 +167,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; @@ -160,6 +180,14 @@ export type AggregateTypes = { readonly output: 'pg/time-temporal@1'; readonly nullable: true; }; + readonly 'pg/timestamp-date@1': { + readonly output: 'pg/timestamp-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-instant@1': { + readonly output: 'pg/timestamp-instant@1'; + readonly nullable: true; + }; readonly 'pg/timestamp-string@1': { readonly output: 'pg/timestamp-string@1'; readonly nullable: true; @@ -203,6 +231,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; diff --git a/test/integration/test/ports/prisma/functional/find-unique-or-throw-batching/_fixture/generated/contract.d.ts b/test/integration/test/ports/prisma/functional/find-unique-or-throw-batching/_fixture/generated/contract.d.ts index ad374b9e2c05..f13dfe64bf20 100644 --- a/test/integration/test/ports/prisma/functional/find-unique-or-throw-batching/_fixture/generated/contract.d.ts +++ b/test/integration/test/ports/prisma/functional/find-unique-or-throw-batching/_fixture/generated/contract.d.ts @@ -42,6 +42,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; @@ -91,6 +95,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; @@ -100,6 +108,14 @@ export type AggregateTypes = { readonly output: 'pg/time-temporal@1'; readonly nullable: true; }; + readonly 'pg/timestamp-date@1': { + readonly output: 'pg/timestamp-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-instant@1': { + readonly output: 'pg/timestamp-instant@1'; + readonly nullable: true; + }; readonly 'pg/timestamp-string@1': { readonly output: 'pg/timestamp-string@1'; readonly nullable: true; @@ -151,6 +167,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; @@ -160,6 +180,14 @@ export type AggregateTypes = { readonly output: 'pg/time-temporal@1'; readonly nullable: true; }; + readonly 'pg/timestamp-date@1': { + readonly output: 'pg/timestamp-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-instant@1': { + readonly output: 'pg/timestamp-instant@1'; + readonly nullable: true; + }; readonly 'pg/timestamp-string@1': { readonly output: 'pg/timestamp-string@1'; readonly nullable: true; @@ -203,6 +231,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; diff --git a/test/integration/test/ports/prisma/functional/handle-int-overflow/_fixture/generated/contract.d.ts b/test/integration/test/ports/prisma/functional/handle-int-overflow/_fixture/generated/contract.d.ts index 441e47645949..f8fe71b68df8 100644 --- a/test/integration/test/ports/prisma/functional/handle-int-overflow/_fixture/generated/contract.d.ts +++ b/test/integration/test/ports/prisma/functional/handle-int-overflow/_fixture/generated/contract.d.ts @@ -43,6 +43,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; @@ -92,6 +96,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; @@ -101,6 +109,14 @@ export type AggregateTypes = { readonly output: 'pg/time-temporal@1'; readonly nullable: true; }; + readonly 'pg/timestamp-date@1': { + readonly output: 'pg/timestamp-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-instant@1': { + readonly output: 'pg/timestamp-instant@1'; + readonly nullable: true; + }; readonly 'pg/timestamp-string@1': { readonly output: 'pg/timestamp-string@1'; readonly nullable: true; @@ -152,6 +168,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; @@ -161,6 +181,14 @@ export type AggregateTypes = { readonly output: 'pg/time-temporal@1'; readonly nullable: true; }; + readonly 'pg/timestamp-date@1': { + readonly output: 'pg/timestamp-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-instant@1': { + readonly output: 'pg/timestamp-instant@1'; + readonly nullable: true; + }; readonly 'pg/timestamp-string@1': { readonly output: 'pg/timestamp-string@1'; readonly nullable: true; @@ -204,6 +232,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; diff --git a/test/integration/test/ports/prisma/functional/interactive-transactions/_fixture/generated/contract.d.ts b/test/integration/test/ports/prisma/functional/interactive-transactions/_fixture/generated/contract.d.ts index ea2602b6aaf1..86b47698325b 100644 --- a/test/integration/test/ports/prisma/functional/interactive-transactions/_fixture/generated/contract.d.ts +++ b/test/integration/test/ports/prisma/functional/interactive-transactions/_fixture/generated/contract.d.ts @@ -43,6 +43,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; @@ -92,6 +96,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; @@ -101,6 +109,14 @@ export type AggregateTypes = { readonly output: 'pg/time-temporal@1'; readonly nullable: true; }; + readonly 'pg/timestamp-date@1': { + readonly output: 'pg/timestamp-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-instant@1': { + readonly output: 'pg/timestamp-instant@1'; + readonly nullable: true; + }; readonly 'pg/timestamp-string@1': { readonly output: 'pg/timestamp-string@1'; readonly nullable: true; @@ -152,6 +168,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; @@ -161,6 +181,14 @@ export type AggregateTypes = { readonly output: 'pg/time-temporal@1'; readonly nullable: true; }; + readonly 'pg/timestamp-date@1': { + readonly output: 'pg/timestamp-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-instant@1': { + readonly output: 'pg/timestamp-instant@1'; + readonly nullable: true; + }; readonly 'pg/timestamp-string@1': { readonly output: 'pg/timestamp-string@1'; readonly nullable: true; @@ -204,6 +232,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; diff --git a/test/integration/test/ports/prisma/functional/issues-11974/_fixture/generated/contract.d.ts b/test/integration/test/ports/prisma/functional/issues-11974/_fixture/generated/contract.d.ts index 67625d8cd789..59c8bad32294 100644 --- a/test/integration/test/ports/prisma/functional/issues-11974/_fixture/generated/contract.d.ts +++ b/test/integration/test/ports/prisma/functional/issues-11974/_fixture/generated/contract.d.ts @@ -42,6 +42,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; @@ -91,6 +95,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; @@ -100,6 +108,14 @@ export type AggregateTypes = { readonly output: 'pg/time-temporal@1'; readonly nullable: true; }; + readonly 'pg/timestamp-date@1': { + readonly output: 'pg/timestamp-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-instant@1': { + readonly output: 'pg/timestamp-instant@1'; + readonly nullable: true; + }; readonly 'pg/timestamp-string@1': { readonly output: 'pg/timestamp-string@1'; readonly nullable: true; @@ -151,6 +167,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; @@ -160,6 +180,14 @@ export type AggregateTypes = { readonly output: 'pg/time-temporal@1'; readonly nullable: true; }; + readonly 'pg/timestamp-date@1': { + readonly output: 'pg/timestamp-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-instant@1': { + readonly output: 'pg/timestamp-instant@1'; + readonly nullable: true; + }; readonly 'pg/timestamp-string@1': { readonly output: 'pg/timestamp-string@1'; readonly nullable: true; @@ -203,6 +231,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; diff --git a/test/integration/test/ports/prisma/functional/issues-12378/_fixture/generated/contract.d.ts b/test/integration/test/ports/prisma/functional/issues-12378/_fixture/generated/contract.d.ts index 27fdf0e9c7c9..df42b2a1f928 100644 --- a/test/integration/test/ports/prisma/functional/issues-12378/_fixture/generated/contract.d.ts +++ b/test/integration/test/ports/prisma/functional/issues-12378/_fixture/generated/contract.d.ts @@ -43,6 +43,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; @@ -92,6 +96,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; @@ -101,6 +109,14 @@ export type AggregateTypes = { readonly output: 'pg/time-temporal@1'; readonly nullable: true; }; + readonly 'pg/timestamp-date@1': { + readonly output: 'pg/timestamp-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-instant@1': { + readonly output: 'pg/timestamp-instant@1'; + readonly nullable: true; + }; readonly 'pg/timestamp-string@1': { readonly output: 'pg/timestamp-string@1'; readonly nullable: true; @@ -152,6 +168,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; @@ -161,6 +181,14 @@ export type AggregateTypes = { readonly output: 'pg/time-temporal@1'; readonly nullable: true; }; + readonly 'pg/timestamp-date@1': { + readonly output: 'pg/timestamp-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-instant@1': { + readonly output: 'pg/timestamp-instant@1'; + readonly nullable: true; + }; readonly 'pg/timestamp-string@1': { readonly output: 'pg/timestamp-string@1'; readonly nullable: true; @@ -204,6 +232,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; diff --git a/test/integration/test/ports/prisma/functional/issues-12557/_fixture/generated/contract.d.ts b/test/integration/test/ports/prisma/functional/issues-12557/_fixture/generated/contract.d.ts index 404aed9799da..5d359deead5d 100644 --- a/test/integration/test/ports/prisma/functional/issues-12557/_fixture/generated/contract.d.ts +++ b/test/integration/test/ports/prisma/functional/issues-12557/_fixture/generated/contract.d.ts @@ -42,6 +42,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; @@ -91,6 +95,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; @@ -100,6 +108,14 @@ export type AggregateTypes = { readonly output: 'pg/time-temporal@1'; readonly nullable: true; }; + readonly 'pg/timestamp-date@1': { + readonly output: 'pg/timestamp-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-instant@1': { + readonly output: 'pg/timestamp-instant@1'; + readonly nullable: true; + }; readonly 'pg/timestamp-string@1': { readonly output: 'pg/timestamp-string@1'; readonly nullable: true; @@ -151,6 +167,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; @@ -160,6 +180,14 @@ export type AggregateTypes = { readonly output: 'pg/time-temporal@1'; readonly nullable: true; }; + readonly 'pg/timestamp-date@1': { + readonly output: 'pg/timestamp-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-instant@1': { + readonly output: 'pg/timestamp-instant@1'; + readonly nullable: true; + }; readonly 'pg/timestamp-string@1': { readonly output: 'pg/timestamp-string@1'; readonly nullable: true; @@ -203,6 +231,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; diff --git a/test/integration/test/ports/prisma/functional/issues-12572/_fixture/generated/contract.d.ts b/test/integration/test/ports/prisma/functional/issues-12572/_fixture/generated/contract.d.ts index 828696196906..63180d3470cc 100644 --- a/test/integration/test/ports/prisma/functional/issues-12572/_fixture/generated/contract.d.ts +++ b/test/integration/test/ports/prisma/functional/issues-12572/_fixture/generated/contract.d.ts @@ -43,6 +43,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; @@ -92,6 +96,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; @@ -101,6 +109,14 @@ export type AggregateTypes = { readonly output: 'pg/time-temporal@1'; readonly nullable: true; }; + readonly 'pg/timestamp-date@1': { + readonly output: 'pg/timestamp-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-instant@1': { + readonly output: 'pg/timestamp-instant@1'; + readonly nullable: true; + }; readonly 'pg/timestamp-string@1': { readonly output: 'pg/timestamp-string@1'; readonly nullable: true; @@ -152,6 +168,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; @@ -161,6 +181,14 @@ export type AggregateTypes = { readonly output: 'pg/time-temporal@1'; readonly nullable: true; }; + readonly 'pg/timestamp-date@1': { + readonly output: 'pg/timestamp-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-instant@1': { + readonly output: 'pg/timestamp-instant@1'; + readonly nullable: true; + }; readonly 'pg/timestamp-string@1': { readonly output: 'pg/timestamp-string@1'; readonly nullable: true; @@ -204,6 +232,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; diff --git a/test/integration/test/ports/prisma/functional/issues-14271/_fixture/generated/contract.d.ts b/test/integration/test/ports/prisma/functional/issues-14271/_fixture/generated/contract.d.ts index 30f3e55f8532..4fdb7a1300e7 100644 --- a/test/integration/test/ports/prisma/functional/issues-14271/_fixture/generated/contract.d.ts +++ b/test/integration/test/ports/prisma/functional/issues-14271/_fixture/generated/contract.d.ts @@ -42,6 +42,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; @@ -91,6 +95,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; @@ -100,6 +108,14 @@ export type AggregateTypes = { readonly output: 'pg/time-temporal@1'; readonly nullable: true; }; + readonly 'pg/timestamp-date@1': { + readonly output: 'pg/timestamp-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-instant@1': { + readonly output: 'pg/timestamp-instant@1'; + readonly nullable: true; + }; readonly 'pg/timestamp-string@1': { readonly output: 'pg/timestamp-string@1'; readonly nullable: true; @@ -151,6 +167,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; @@ -160,6 +180,14 @@ export type AggregateTypes = { readonly output: 'pg/time-temporal@1'; readonly nullable: true; }; + readonly 'pg/timestamp-date@1': { + readonly output: 'pg/timestamp-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-instant@1': { + readonly output: 'pg/timestamp-instant@1'; + readonly nullable: true; + }; readonly 'pg/timestamp-string@1': { readonly output: 'pg/timestamp-string@1'; readonly nullable: true; @@ -203,6 +231,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; diff --git a/test/integration/test/ports/prisma/functional/issues-14954-date-batch/_fixture/generated/contract.d.ts b/test/integration/test/ports/prisma/functional/issues-14954-date-batch/_fixture/generated/contract.d.ts index 5ed008a091e6..b0b0c13f94ea 100644 --- a/test/integration/test/ports/prisma/functional/issues-14954-date-batch/_fixture/generated/contract.d.ts +++ b/test/integration/test/ports/prisma/functional/issues-14954-date-batch/_fixture/generated/contract.d.ts @@ -43,6 +43,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; @@ -92,6 +96,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; @@ -101,6 +109,14 @@ export type AggregateTypes = { readonly output: 'pg/time-temporal@1'; readonly nullable: true; }; + readonly 'pg/timestamp-date@1': { + readonly output: 'pg/timestamp-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-instant@1': { + readonly output: 'pg/timestamp-instant@1'; + readonly nullable: true; + }; readonly 'pg/timestamp-string@1': { readonly output: 'pg/timestamp-string@1'; readonly nullable: true; @@ -152,6 +168,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; @@ -161,6 +181,14 @@ export type AggregateTypes = { readonly output: 'pg/time-temporal@1'; readonly nullable: true; }; + readonly 'pg/timestamp-date@1': { + readonly output: 'pg/timestamp-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-instant@1': { + readonly output: 'pg/timestamp-instant@1'; + readonly nullable: true; + }; readonly 'pg/timestamp-string@1': { readonly output: 'pg/timestamp-string@1'; readonly nullable: true; @@ -204,6 +232,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; diff --git a/test/integration/test/ports/prisma/functional/issues-15044/_fixture/generated/contract.d.ts b/test/integration/test/ports/prisma/functional/issues-15044/_fixture/generated/contract.d.ts index 367761783b63..8e0aece2b546 100644 --- a/test/integration/test/ports/prisma/functional/issues-15044/_fixture/generated/contract.d.ts +++ b/test/integration/test/ports/prisma/functional/issues-15044/_fixture/generated/contract.d.ts @@ -43,6 +43,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; @@ -92,6 +96,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; @@ -101,6 +109,14 @@ export type AggregateTypes = { readonly output: 'pg/time-temporal@1'; readonly nullable: true; }; + readonly 'pg/timestamp-date@1': { + readonly output: 'pg/timestamp-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-instant@1': { + readonly output: 'pg/timestamp-instant@1'; + readonly nullable: true; + }; readonly 'pg/timestamp-string@1': { readonly output: 'pg/timestamp-string@1'; readonly nullable: true; @@ -152,6 +168,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; @@ -161,6 +181,14 @@ export type AggregateTypes = { readonly output: 'pg/time-temporal@1'; readonly nullable: true; }; + readonly 'pg/timestamp-date@1': { + readonly output: 'pg/timestamp-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-instant@1': { + readonly output: 'pg/timestamp-instant@1'; + readonly nullable: true; + }; readonly 'pg/timestamp-string@1': { readonly output: 'pg/timestamp-string@1'; readonly nullable: true; @@ -204,6 +232,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; diff --git a/test/integration/test/ports/prisma/functional/issues-16535-select-enum/_fixture/generated/contract.d.ts b/test/integration/test/ports/prisma/functional/issues-16535-select-enum/_fixture/generated/contract.d.ts index c7fa2039b90d..f1d1e114145d 100644 --- a/test/integration/test/ports/prisma/functional/issues-16535-select-enum/_fixture/generated/contract.d.ts +++ b/test/integration/test/ports/prisma/functional/issues-16535-select-enum/_fixture/generated/contract.d.ts @@ -43,6 +43,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; @@ -92,6 +96,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; @@ -101,6 +109,14 @@ export type AggregateTypes = { readonly output: 'pg/time-temporal@1'; readonly nullable: true; }; + readonly 'pg/timestamp-date@1': { + readonly output: 'pg/timestamp-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-instant@1': { + readonly output: 'pg/timestamp-instant@1'; + readonly nullable: true; + }; readonly 'pg/timestamp-string@1': { readonly output: 'pg/timestamp-string@1'; readonly nullable: true; @@ -152,6 +168,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; @@ -161,6 +181,14 @@ export type AggregateTypes = { readonly output: 'pg/time-temporal@1'; readonly nullable: true; }; + readonly 'pg/timestamp-date@1': { + readonly output: 'pg/timestamp-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-instant@1': { + readonly output: 'pg/timestamp-instant@1'; + readonly nullable: true; + }; readonly 'pg/timestamp-string@1': { readonly output: 'pg/timestamp-string@1'; readonly nullable: true; @@ -204,6 +232,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; diff --git a/test/integration/test/ports/prisma/functional/issues-17005-args-type-conflict/_fixture/generated/contract.d.ts b/test/integration/test/ports/prisma/functional/issues-17005-args-type-conflict/_fixture/generated/contract.d.ts index 82b29c6c5c8b..1f3a4e2a4e8b 100644 --- a/test/integration/test/ports/prisma/functional/issues-17005-args-type-conflict/_fixture/generated/contract.d.ts +++ b/test/integration/test/ports/prisma/functional/issues-17005-args-type-conflict/_fixture/generated/contract.d.ts @@ -43,6 +43,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; @@ -92,6 +96,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; @@ -101,6 +109,14 @@ export type AggregateTypes = { readonly output: 'pg/time-temporal@1'; readonly nullable: true; }; + readonly 'pg/timestamp-date@1': { + readonly output: 'pg/timestamp-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-instant@1': { + readonly output: 'pg/timestamp-instant@1'; + readonly nullable: true; + }; readonly 'pg/timestamp-string@1': { readonly output: 'pg/timestamp-string@1'; readonly nullable: true; @@ -152,6 +168,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; @@ -161,6 +181,14 @@ export type AggregateTypes = { readonly output: 'pg/time-temporal@1'; readonly nullable: true; }; + readonly 'pg/timestamp-date@1': { + readonly output: 'pg/timestamp-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-instant@1': { + readonly output: 'pg/timestamp-instant@1'; + readonly nullable: true; + }; readonly 'pg/timestamp-string@1': { readonly output: 'pg/timestamp-string@1'; readonly nullable: true; @@ -204,6 +232,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; diff --git a/test/integration/test/ports/prisma/functional/issues-17030-args-type-conflict/_fixture/generated/contract.d.ts b/test/integration/test/ports/prisma/functional/issues-17030-args-type-conflict/_fixture/generated/contract.d.ts index 9efcd4e85870..aa9ece970405 100644 --- a/test/integration/test/ports/prisma/functional/issues-17030-args-type-conflict/_fixture/generated/contract.d.ts +++ b/test/integration/test/ports/prisma/functional/issues-17030-args-type-conflict/_fixture/generated/contract.d.ts @@ -42,6 +42,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; @@ -91,6 +95,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; @@ -100,6 +108,14 @@ export type AggregateTypes = { readonly output: 'pg/time-temporal@1'; readonly nullable: true; }; + readonly 'pg/timestamp-date@1': { + readonly output: 'pg/timestamp-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-instant@1': { + readonly output: 'pg/timestamp-instant@1'; + readonly nullable: true; + }; readonly 'pg/timestamp-string@1': { readonly output: 'pg/timestamp-string@1'; readonly nullable: true; @@ -151,6 +167,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; @@ -160,6 +180,14 @@ export type AggregateTypes = { readonly output: 'pg/time-temporal@1'; readonly nullable: true; }; + readonly 'pg/timestamp-date@1': { + readonly output: 'pg/timestamp-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-instant@1': { + readonly output: 'pg/timestamp-instant@1'; + readonly nullable: true; + }; readonly 'pg/timestamp-string@1': { readonly output: 'pg/timestamp-string@1'; readonly nullable: true; @@ -203,6 +231,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; diff --git a/test/integration/test/ports/prisma/functional/issues-18970-invalid-date/_fixture/generated/contract.d.ts b/test/integration/test/ports/prisma/functional/issues-18970-invalid-date/_fixture/generated/contract.d.ts index 0d78e286070e..143d0f821149 100644 --- a/test/integration/test/ports/prisma/functional/issues-18970-invalid-date/_fixture/generated/contract.d.ts +++ b/test/integration/test/ports/prisma/functional/issues-18970-invalid-date/_fixture/generated/contract.d.ts @@ -43,6 +43,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; @@ -92,6 +96,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; @@ -101,6 +109,14 @@ export type AggregateTypes = { readonly output: 'pg/time-temporal@1'; readonly nullable: true; }; + readonly 'pg/timestamp-date@1': { + readonly output: 'pg/timestamp-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-instant@1': { + readonly output: 'pg/timestamp-instant@1'; + readonly nullable: true; + }; readonly 'pg/timestamp-string@1': { readonly output: 'pg/timestamp-string@1'; readonly nullable: true; @@ -152,6 +168,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; @@ -161,6 +181,14 @@ export type AggregateTypes = { readonly output: 'pg/time-temporal@1'; readonly nullable: true; }; + readonly 'pg/timestamp-date@1': { + readonly output: 'pg/timestamp-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-instant@1': { + readonly output: 'pg/timestamp-instant@1'; + readonly nullable: true; + }; readonly 'pg/timestamp-string@1': { readonly output: 'pg/timestamp-string@1'; readonly nullable: true; @@ -204,6 +232,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; diff --git a/test/integration/test/ports/prisma/functional/issues-20261-group-by-shortcut/_fixture/generated/contract.d.ts b/test/integration/test/ports/prisma/functional/issues-20261-group-by-shortcut/_fixture/generated/contract.d.ts index 1039b8a3e3c5..66b887bb68c9 100644 --- a/test/integration/test/ports/prisma/functional/issues-20261-group-by-shortcut/_fixture/generated/contract.d.ts +++ b/test/integration/test/ports/prisma/functional/issues-20261-group-by-shortcut/_fixture/generated/contract.d.ts @@ -43,6 +43,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; @@ -92,6 +96,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; @@ -101,6 +109,14 @@ export type AggregateTypes = { readonly output: 'pg/time-temporal@1'; readonly nullable: true; }; + readonly 'pg/timestamp-date@1': { + readonly output: 'pg/timestamp-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-instant@1': { + readonly output: 'pg/timestamp-instant@1'; + readonly nullable: true; + }; readonly 'pg/timestamp-string@1': { readonly output: 'pg/timestamp-string@1'; readonly nullable: true; @@ -152,6 +168,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; @@ -161,6 +181,14 @@ export type AggregateTypes = { readonly output: 'pg/time-temporal@1'; readonly nullable: true; }; + readonly 'pg/timestamp-date@1': { + readonly output: 'pg/timestamp-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-instant@1': { + readonly output: 'pg/timestamp-instant@1'; + readonly nullable: true; + }; readonly 'pg/timestamp-string@1': { readonly output: 'pg/timestamp-string@1'; readonly nullable: true; @@ -204,6 +232,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; diff --git a/test/integration/test/ports/prisma/functional/issues-21352-id-does-not-exist/_fixture/generated/contract.d.ts b/test/integration/test/ports/prisma/functional/issues-21352-id-does-not-exist/_fixture/generated/contract.d.ts index 93fd9e1aea26..24dc948b1014 100644 --- a/test/integration/test/ports/prisma/functional/issues-21352-id-does-not-exist/_fixture/generated/contract.d.ts +++ b/test/integration/test/ports/prisma/functional/issues-21352-id-does-not-exist/_fixture/generated/contract.d.ts @@ -42,6 +42,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; @@ -91,6 +95,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; @@ -100,6 +108,14 @@ export type AggregateTypes = { readonly output: 'pg/time-temporal@1'; readonly nullable: true; }; + readonly 'pg/timestamp-date@1': { + readonly output: 'pg/timestamp-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-instant@1': { + readonly output: 'pg/timestamp-instant@1'; + readonly nullable: true; + }; readonly 'pg/timestamp-string@1': { readonly output: 'pg/timestamp-string@1'; readonly nullable: true; @@ -151,6 +167,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; @@ -160,6 +180,14 @@ export type AggregateTypes = { readonly output: 'pg/time-temporal@1'; readonly nullable: true; }; + readonly 'pg/timestamp-date@1': { + readonly output: 'pg/timestamp-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-instant@1': { + readonly output: 'pg/timestamp-instant@1'; + readonly nullable: true; + }; readonly 'pg/timestamp-string@1': { readonly output: 'pg/timestamp-string@1'; readonly nullable: true; @@ -203,6 +231,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; diff --git a/test/integration/test/ports/prisma/functional/issues-21454-type-in-json/_fixture/generated/contract.d.ts b/test/integration/test/ports/prisma/functional/issues-21454-type-in-json/_fixture/generated/contract.d.ts index a75262d5b7a2..dc14977ff0e4 100644 --- a/test/integration/test/ports/prisma/functional/issues-21454-type-in-json/_fixture/generated/contract.d.ts +++ b/test/integration/test/ports/prisma/functional/issues-21454-type-in-json/_fixture/generated/contract.d.ts @@ -43,6 +43,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; @@ -92,6 +96,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; @@ -101,6 +109,14 @@ export type AggregateTypes = { readonly output: 'pg/time-temporal@1'; readonly nullable: true; }; + readonly 'pg/timestamp-date@1': { + readonly output: 'pg/timestamp-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-instant@1': { + readonly output: 'pg/timestamp-instant@1'; + readonly nullable: true; + }; readonly 'pg/timestamp-string@1': { readonly output: 'pg/timestamp-string@1'; readonly nullable: true; @@ -152,6 +168,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; @@ -161,6 +181,14 @@ export type AggregateTypes = { readonly output: 'pg/time-temporal@1'; readonly nullable: true; }; + readonly 'pg/timestamp-date@1': { + readonly output: 'pg/timestamp-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-instant@1': { + readonly output: 'pg/timestamp-instant@1'; + readonly nullable: true; + }; readonly 'pg/timestamp-string@1': { readonly output: 'pg/timestamp-string@1'; readonly nullable: true; @@ -204,6 +232,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; diff --git a/test/integration/test/ports/prisma/functional/issues-21631-batching-in-transaction/_fixture/generated/contract.d.ts b/test/integration/test/ports/prisma/functional/issues-21631-batching-in-transaction/_fixture/generated/contract.d.ts index 2ce42f9c0bba..8da6309cc989 100644 --- a/test/integration/test/ports/prisma/functional/issues-21631-batching-in-transaction/_fixture/generated/contract.d.ts +++ b/test/integration/test/ports/prisma/functional/issues-21631-batching-in-transaction/_fixture/generated/contract.d.ts @@ -43,6 +43,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; @@ -92,6 +96,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; @@ -101,6 +109,14 @@ export type AggregateTypes = { readonly output: 'pg/time-temporal@1'; readonly nullable: true; }; + readonly 'pg/timestamp-date@1': { + readonly output: 'pg/timestamp-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-instant@1': { + readonly output: 'pg/timestamp-instant@1'; + readonly nullable: true; + }; readonly 'pg/timestamp-string@1': { readonly output: 'pg/timestamp-string@1'; readonly nullable: true; @@ -152,6 +168,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; @@ -161,6 +181,14 @@ export type AggregateTypes = { readonly output: 'pg/time-temporal@1'; readonly nullable: true; }; + readonly 'pg/timestamp-date@1': { + readonly output: 'pg/timestamp-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-instant@1': { + readonly output: 'pg/timestamp-instant@1'; + readonly nullable: true; + }; readonly 'pg/timestamp-string@1': { readonly output: 'pg/timestamp-string@1'; readonly nullable: true; @@ -204,6 +232,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; diff --git a/test/integration/test/ports/prisma/functional/issues-22098-column-does-not-exist/_fixture/generated/contract.d.ts b/test/integration/test/ports/prisma/functional/issues-22098-column-does-not-exist/_fixture/generated/contract.d.ts index 2dc93b76bc2a..4d666e42e710 100644 --- a/test/integration/test/ports/prisma/functional/issues-22098-column-does-not-exist/_fixture/generated/contract.d.ts +++ b/test/integration/test/ports/prisma/functional/issues-22098-column-does-not-exist/_fixture/generated/contract.d.ts @@ -43,6 +43,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; @@ -92,6 +96,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; @@ -101,6 +109,14 @@ export type AggregateTypes = { readonly output: 'pg/time-temporal@1'; readonly nullable: true; }; + readonly 'pg/timestamp-date@1': { + readonly output: 'pg/timestamp-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-instant@1': { + readonly output: 'pg/timestamp-instant@1'; + readonly nullable: true; + }; readonly 'pg/timestamp-string@1': { readonly output: 'pg/timestamp-string@1'; readonly nullable: true; @@ -152,6 +168,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; @@ -161,6 +181,14 @@ export type AggregateTypes = { readonly output: 'pg/time-temporal@1'; readonly nullable: true; }; + readonly 'pg/timestamp-date@1': { + readonly output: 'pg/timestamp-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-instant@1': { + readonly output: 'pg/timestamp-instant@1'; + readonly nullable: true; + }; readonly 'pg/timestamp-string@1': { readonly output: 'pg/timestamp-string@1'; readonly nullable: true; @@ -204,6 +232,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; diff --git a/test/integration/test/ports/prisma/functional/issues-22610-parallel-batch/_fixture/generated/contract.d.ts b/test/integration/test/ports/prisma/functional/issues-22610-parallel-batch/_fixture/generated/contract.d.ts index 1ae9b1d22e8a..e5d958e9dbc1 100644 --- a/test/integration/test/ports/prisma/functional/issues-22610-parallel-batch/_fixture/generated/contract.d.ts +++ b/test/integration/test/ports/prisma/functional/issues-22610-parallel-batch/_fixture/generated/contract.d.ts @@ -43,6 +43,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; @@ -92,6 +96,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; @@ -101,6 +109,14 @@ export type AggregateTypes = { readonly output: 'pg/time-temporal@1'; readonly nullable: true; }; + readonly 'pg/timestamp-date@1': { + readonly output: 'pg/timestamp-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-instant@1': { + readonly output: 'pg/timestamp-instant@1'; + readonly nullable: true; + }; readonly 'pg/timestamp-string@1': { readonly output: 'pg/timestamp-string@1'; readonly nullable: true; @@ -152,6 +168,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; @@ -161,6 +181,14 @@ export type AggregateTypes = { readonly output: 'pg/time-temporal@1'; readonly nullable: true; }; + readonly 'pg/timestamp-date@1': { + readonly output: 'pg/timestamp-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-instant@1': { + readonly output: 'pg/timestamp-instant@1'; + readonly nullable: true; + }; readonly 'pg/timestamp-string@1': { readonly output: 'pg/timestamp-string@1'; readonly nullable: true; @@ -204,6 +232,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; diff --git a/test/integration/test/ports/prisma/functional/issues-23201-non-ascii-comments/_fixture/generated/contract.d.ts b/test/integration/test/ports/prisma/functional/issues-23201-non-ascii-comments/_fixture/generated/contract.d.ts index 54b45c9f1231..2249158354d1 100644 --- a/test/integration/test/ports/prisma/functional/issues-23201-non-ascii-comments/_fixture/generated/contract.d.ts +++ b/test/integration/test/ports/prisma/functional/issues-23201-non-ascii-comments/_fixture/generated/contract.d.ts @@ -43,6 +43,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; @@ -92,6 +96,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; @@ -101,6 +109,14 @@ export type AggregateTypes = { readonly output: 'pg/time-temporal@1'; readonly nullable: true; }; + readonly 'pg/timestamp-date@1': { + readonly output: 'pg/timestamp-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-instant@1': { + readonly output: 'pg/timestamp-instant@1'; + readonly nullable: true; + }; readonly 'pg/timestamp-string@1': { readonly output: 'pg/timestamp-string@1'; readonly nullable: true; @@ -152,6 +168,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; @@ -161,6 +181,14 @@ export type AggregateTypes = { readonly output: 'pg/time-temporal@1'; readonly nullable: true; }; + readonly 'pg/timestamp-date@1': { + readonly output: 'pg/timestamp-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-instant@1': { + readonly output: 'pg/timestamp-instant@1'; + readonly nullable: true; + }; readonly 'pg/timestamp-string@1': { readonly output: 'pg/timestamp-string@1'; readonly nullable: true; @@ -204,6 +232,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; diff --git a/test/integration/test/ports/prisma/functional/issues-23902/_fixture/generated/contract.d.ts b/test/integration/test/ports/prisma/functional/issues-23902/_fixture/generated/contract.d.ts index a2db97613ab5..f5072b4acbee 100644 --- a/test/integration/test/ports/prisma/functional/issues-23902/_fixture/generated/contract.d.ts +++ b/test/integration/test/ports/prisma/functional/issues-23902/_fixture/generated/contract.d.ts @@ -43,6 +43,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; @@ -92,6 +96,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; @@ -101,6 +109,14 @@ export type AggregateTypes = { readonly output: 'pg/time-temporal@1'; readonly nullable: true; }; + readonly 'pg/timestamp-date@1': { + readonly output: 'pg/timestamp-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-instant@1': { + readonly output: 'pg/timestamp-instant@1'; + readonly nullable: true; + }; readonly 'pg/timestamp-string@1': { readonly output: 'pg/timestamp-string@1'; readonly nullable: true; @@ -152,6 +168,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; @@ -161,6 +181,14 @@ export type AggregateTypes = { readonly output: 'pg/time-temporal@1'; readonly nullable: true; }; + readonly 'pg/timestamp-date@1': { + readonly output: 'pg/timestamp-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-instant@1': { + readonly output: 'pg/timestamp-instant@1'; + readonly nullable: true; + }; readonly 'pg/timestamp-string@1': { readonly output: 'pg/timestamp-string@1'; readonly nullable: true; @@ -204,6 +232,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; diff --git a/test/integration/test/ports/prisma/functional/issues-25404/_fixture/generated/contract.d.ts b/test/integration/test/ports/prisma/functional/issues-25404/_fixture/generated/contract.d.ts index 8b48e0f8fb34..af2d9d860e20 100644 --- a/test/integration/test/ports/prisma/functional/issues-25404/_fixture/generated/contract.d.ts +++ b/test/integration/test/ports/prisma/functional/issues-25404/_fixture/generated/contract.d.ts @@ -42,6 +42,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; @@ -91,6 +95,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; @@ -100,6 +108,14 @@ export type AggregateTypes = { readonly output: 'pg/time-temporal@1'; readonly nullable: true; }; + readonly 'pg/timestamp-date@1': { + readonly output: 'pg/timestamp-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-instant@1': { + readonly output: 'pg/timestamp-instant@1'; + readonly nullable: true; + }; readonly 'pg/timestamp-string@1': { readonly output: 'pg/timestamp-string@1'; readonly nullable: true; @@ -151,6 +167,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; @@ -160,6 +180,14 @@ export type AggregateTypes = { readonly output: 'pg/time-temporal@1'; readonly nullable: true; }; + readonly 'pg/timestamp-date@1': { + readonly output: 'pg/timestamp-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-instant@1': { + readonly output: 'pg/timestamp-instant@1'; + readonly nullable: true; + }; readonly 'pg/timestamp-string@1': { readonly output: 'pg/timestamp-string@1'; readonly nullable: true; @@ -203,6 +231,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; diff --git a/test/integration/test/ports/prisma/functional/issues-27455-bytes-id/_fixture/generated/contract.d.ts b/test/integration/test/ports/prisma/functional/issues-27455-bytes-id/_fixture/generated/contract.d.ts index 4ee1796fcbc1..e2639fb6cce9 100644 --- a/test/integration/test/ports/prisma/functional/issues-27455-bytes-id/_fixture/generated/contract.d.ts +++ b/test/integration/test/ports/prisma/functional/issues-27455-bytes-id/_fixture/generated/contract.d.ts @@ -42,6 +42,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; @@ -91,6 +95,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; @@ -100,6 +108,14 @@ export type AggregateTypes = { readonly output: 'pg/time-temporal@1'; readonly nullable: true; }; + readonly 'pg/timestamp-date@1': { + readonly output: 'pg/timestamp-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-instant@1': { + readonly output: 'pg/timestamp-instant@1'; + readonly nullable: true; + }; readonly 'pg/timestamp-string@1': { readonly output: 'pg/timestamp-string@1'; readonly nullable: true; @@ -151,6 +167,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; @@ -160,6 +180,14 @@ export type AggregateTypes = { readonly output: 'pg/time-temporal@1'; readonly nullable: true; }; + readonly 'pg/timestamp-date@1': { + readonly output: 'pg/timestamp-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-instant@1': { + readonly output: 'pg/timestamp-instant@1'; + readonly nullable: true; + }; readonly 'pg/timestamp-string@1': { readonly output: 'pg/timestamp-string@1'; readonly nullable: true; @@ -203,6 +231,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; diff --git a/test/integration/test/ports/prisma/functional/issues-27511-include-enum-array/_fixture/generated/contract.d.ts b/test/integration/test/ports/prisma/functional/issues-27511-include-enum-array/_fixture/generated/contract.d.ts index bef4771ee869..3f3119168e4f 100644 --- a/test/integration/test/ports/prisma/functional/issues-27511-include-enum-array/_fixture/generated/contract.d.ts +++ b/test/integration/test/ports/prisma/functional/issues-27511-include-enum-array/_fixture/generated/contract.d.ts @@ -42,6 +42,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; @@ -91,6 +95,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; @@ -100,6 +108,14 @@ export type AggregateTypes = { readonly output: 'pg/time-temporal@1'; readonly nullable: true; }; + readonly 'pg/timestamp-date@1': { + readonly output: 'pg/timestamp-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-instant@1': { + readonly output: 'pg/timestamp-instant@1'; + readonly nullable: true; + }; readonly 'pg/timestamp-string@1': { readonly output: 'pg/timestamp-string@1'; readonly nullable: true; @@ -151,6 +167,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; @@ -160,6 +180,14 @@ export type AggregateTypes = { readonly output: 'pg/time-temporal@1'; readonly nullable: true; }; + readonly 'pg/timestamp-date@1': { + readonly output: 'pg/timestamp-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-instant@1': { + readonly output: 'pg/timestamp-instant@1'; + readonly nullable: true; + }; readonly 'pg/timestamp-string@1': { readonly output: 'pg/timestamp-string@1'; readonly nullable: true; @@ -203,6 +231,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; diff --git a/test/integration/test/ports/prisma/functional/issues-28151-broken-nested-set/_fixture/generated/contract.d.ts b/test/integration/test/ports/prisma/functional/issues-28151-broken-nested-set/_fixture/generated/contract.d.ts index 8461b89b101d..0a61ca91bb45 100644 --- a/test/integration/test/ports/prisma/functional/issues-28151-broken-nested-set/_fixture/generated/contract.d.ts +++ b/test/integration/test/ports/prisma/functional/issues-28151-broken-nested-set/_fixture/generated/contract.d.ts @@ -43,6 +43,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; @@ -92,6 +96,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; @@ -101,6 +109,14 @@ export type AggregateTypes = { readonly output: 'pg/time-temporal@1'; readonly nullable: true; }; + readonly 'pg/timestamp-date@1': { + readonly output: 'pg/timestamp-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-instant@1': { + readonly output: 'pg/timestamp-instant@1'; + readonly nullable: true; + }; readonly 'pg/timestamp-string@1': { readonly output: 'pg/timestamp-string@1'; readonly nullable: true; @@ -152,6 +168,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; @@ -161,6 +181,14 @@ export type AggregateTypes = { readonly output: 'pg/time-temporal@1'; readonly nullable: true; }; + readonly 'pg/timestamp-date@1': { + readonly output: 'pg/timestamp-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-instant@1': { + readonly output: 'pg/timestamp-instant@1'; + readonly nullable: true; + }; readonly 'pg/timestamp-string@1': { readonly output: 'pg/timestamp-string@1'; readonly nullable: true; @@ -204,6 +232,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; diff --git a/test/integration/test/ports/prisma/functional/issues-28192-pg-historical-dates/_fixture/generated/contract.d.ts b/test/integration/test/ports/prisma/functional/issues-28192-pg-historical-dates/_fixture/generated/contract.d.ts index e03e2576bda1..a24e89d7cf7a 100644 --- a/test/integration/test/ports/prisma/functional/issues-28192-pg-historical-dates/_fixture/generated/contract.d.ts +++ b/test/integration/test/ports/prisma/functional/issues-28192-pg-historical-dates/_fixture/generated/contract.d.ts @@ -43,6 +43,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; @@ -92,6 +96,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; @@ -101,6 +109,14 @@ export type AggregateTypes = { readonly output: 'pg/time-temporal@1'; readonly nullable: true; }; + readonly 'pg/timestamp-date@1': { + readonly output: 'pg/timestamp-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-instant@1': { + readonly output: 'pg/timestamp-instant@1'; + readonly nullable: true; + }; readonly 'pg/timestamp-string@1': { readonly output: 'pg/timestamp-string@1'; readonly nullable: true; @@ -152,6 +168,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; @@ -161,6 +181,14 @@ export type AggregateTypes = { readonly output: 'pg/time-temporal@1'; readonly nullable: true; }; + readonly 'pg/timestamp-date@1': { + readonly output: 'pg/timestamp-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-instant@1': { + readonly output: 'pg/timestamp-instant@1'; + readonly nullable: true; + }; readonly 'pg/timestamp-string@1': { readonly output: 'pg/timestamp-string@1'; readonly nullable: true; @@ -204,6 +232,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; diff --git a/test/integration/test/ports/prisma/functional/issues-28591-mapped-enums/_fixture/generated/contract.d.ts b/test/integration/test/ports/prisma/functional/issues-28591-mapped-enums/_fixture/generated/contract.d.ts index 6bb3ef95362c..27636c708a42 100644 --- a/test/integration/test/ports/prisma/functional/issues-28591-mapped-enums/_fixture/generated/contract.d.ts +++ b/test/integration/test/ports/prisma/functional/issues-28591-mapped-enums/_fixture/generated/contract.d.ts @@ -42,6 +42,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; @@ -91,6 +95,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; @@ -100,6 +108,14 @@ export type AggregateTypes = { readonly output: 'pg/time-temporal@1'; readonly nullable: true; }; + readonly 'pg/timestamp-date@1': { + readonly output: 'pg/timestamp-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-instant@1': { + readonly output: 'pg/timestamp-instant@1'; + readonly nullable: true; + }; readonly 'pg/timestamp-string@1': { readonly output: 'pg/timestamp-string@1'; readonly nullable: true; @@ -151,6 +167,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; @@ -160,6 +180,14 @@ export type AggregateTypes = { readonly output: 'pg/time-temporal@1'; readonly nullable: true; }; + readonly 'pg/timestamp-date@1': { + readonly output: 'pg/timestamp-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-instant@1': { + readonly output: 'pg/timestamp-instant@1'; + readonly nullable: true; + }; readonly 'pg/timestamp-string@1': { readonly output: 'pg/timestamp-string@1'; readonly nullable: true; @@ -203,6 +231,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; diff --git a/test/integration/test/ports/prisma/functional/issues-29010-bigint-precision-relation-joins/_fixture/generated/contract.d.ts b/test/integration/test/ports/prisma/functional/issues-29010-bigint-precision-relation-joins/_fixture/generated/contract.d.ts index 9b8b0da55d2a..ffaf088a463d 100644 --- a/test/integration/test/ports/prisma/functional/issues-29010-bigint-precision-relation-joins/_fixture/generated/contract.d.ts +++ b/test/integration/test/ports/prisma/functional/issues-29010-bigint-precision-relation-joins/_fixture/generated/contract.d.ts @@ -42,6 +42,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; @@ -91,6 +95,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; @@ -100,6 +108,14 @@ export type AggregateTypes = { readonly output: 'pg/time-temporal@1'; readonly nullable: true; }; + readonly 'pg/timestamp-date@1': { + readonly output: 'pg/timestamp-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-instant@1': { + readonly output: 'pg/timestamp-instant@1'; + readonly nullable: true; + }; readonly 'pg/timestamp-string@1': { readonly output: 'pg/timestamp-string@1'; readonly nullable: true; @@ -151,6 +167,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; @@ -160,6 +180,14 @@ export type AggregateTypes = { readonly output: 'pg/time-temporal@1'; readonly nullable: true; }; + readonly 'pg/timestamp-date@1': { + readonly output: 'pg/timestamp-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-instant@1': { + readonly output: 'pg/timestamp-instant@1'; + readonly nullable: true; + }; readonly 'pg/timestamp-string@1': { readonly output: 'pg/timestamp-string@1'; readonly nullable: true; @@ -203,6 +231,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; diff --git a/test/integration/test/ports/prisma/functional/issues-29174-jsonb-parameter-regression/_fixture/generated/contract.d.ts b/test/integration/test/ports/prisma/functional/issues-29174-jsonb-parameter-regression/_fixture/generated/contract.d.ts index 604b7d5230d5..a59c62e5a743 100644 --- a/test/integration/test/ports/prisma/functional/issues-29174-jsonb-parameter-regression/_fixture/generated/contract.d.ts +++ b/test/integration/test/ports/prisma/functional/issues-29174-jsonb-parameter-regression/_fixture/generated/contract.d.ts @@ -43,6 +43,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; @@ -92,6 +96,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; @@ -101,6 +109,14 @@ export type AggregateTypes = { readonly output: 'pg/time-temporal@1'; readonly nullable: true; }; + readonly 'pg/timestamp-date@1': { + readonly output: 'pg/timestamp-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-instant@1': { + readonly output: 'pg/timestamp-instant@1'; + readonly nullable: true; + }; readonly 'pg/timestamp-string@1': { readonly output: 'pg/timestamp-string@1'; readonly nullable: true; @@ -152,6 +168,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; @@ -161,6 +181,14 @@ export type AggregateTypes = { readonly output: 'pg/time-temporal@1'; readonly nullable: true; }; + readonly 'pg/timestamp-date@1': { + readonly output: 'pg/timestamp-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-instant@1': { + readonly output: 'pg/timestamp-instant@1'; + readonly nullable: true; + }; readonly 'pg/timestamp-string@1': { readonly output: 'pg/timestamp-string@1'; readonly nullable: true; @@ -204,6 +232,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; diff --git a/test/integration/test/ports/prisma/functional/issues-29176-cursor-parameter-regression/_fixture/generated/contract.d.ts b/test/integration/test/ports/prisma/functional/issues-29176-cursor-parameter-regression/_fixture/generated/contract.d.ts index deaf3380d778..803bdb98459f 100644 --- a/test/integration/test/ports/prisma/functional/issues-29176-cursor-parameter-regression/_fixture/generated/contract.d.ts +++ b/test/integration/test/ports/prisma/functional/issues-29176-cursor-parameter-regression/_fixture/generated/contract.d.ts @@ -43,6 +43,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; @@ -92,6 +96,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; @@ -101,6 +109,14 @@ export type AggregateTypes = { readonly output: 'pg/time-temporal@1'; readonly nullable: true; }; + readonly 'pg/timestamp-date@1': { + readonly output: 'pg/timestamp-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-instant@1': { + readonly output: 'pg/timestamp-instant@1'; + readonly nullable: true; + }; readonly 'pg/timestamp-string@1': { readonly output: 'pg/timestamp-string@1'; readonly nullable: true; @@ -152,6 +168,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; @@ -161,6 +181,14 @@ export type AggregateTypes = { readonly output: 'pg/time-temporal@1'; readonly nullable: true; }; + readonly 'pg/timestamp-date@1': { + readonly output: 'pg/timestamp-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-instant@1': { + readonly output: 'pg/timestamp-instant@1'; + readonly nullable: true; + }; readonly 'pg/timestamp-string@1': { readonly output: 'pg/timestamp-string@1'; readonly nullable: true; @@ -204,6 +232,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; diff --git a/test/integration/test/ports/prisma/functional/issues-29254-query-plan-cache-mutation/_fixture/generated/contract.d.ts b/test/integration/test/ports/prisma/functional/issues-29254-query-plan-cache-mutation/_fixture/generated/contract.d.ts index 6f5957de50a0..0324e153c7d3 100644 --- a/test/integration/test/ports/prisma/functional/issues-29254-query-plan-cache-mutation/_fixture/generated/contract.d.ts +++ b/test/integration/test/ports/prisma/functional/issues-29254-query-plan-cache-mutation/_fixture/generated/contract.d.ts @@ -43,6 +43,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; @@ -92,6 +96,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; @@ -101,6 +109,14 @@ export type AggregateTypes = { readonly output: 'pg/time-temporal@1'; readonly nullable: true; }; + readonly 'pg/timestamp-date@1': { + readonly output: 'pg/timestamp-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-instant@1': { + readonly output: 'pg/timestamp-instant@1'; + readonly nullable: true; + }; readonly 'pg/timestamp-string@1': { readonly output: 'pg/timestamp-string@1'; readonly nullable: true; @@ -152,6 +168,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; @@ -161,6 +181,14 @@ export type AggregateTypes = { readonly output: 'pg/time-temporal@1'; readonly nullable: true; }; + readonly 'pg/timestamp-date@1': { + readonly output: 'pg/timestamp-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-instant@1': { + readonly output: 'pg/timestamp-instant@1'; + readonly nullable: true; + }; readonly 'pg/timestamp-string@1': { readonly output: 'pg/timestamp-string@1'; readonly nullable: true; @@ -204,6 +232,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; diff --git a/test/integration/test/ports/prisma/functional/issues-29267-uint8array-in-json/_fixture/generated/contract.d.ts b/test/integration/test/ports/prisma/functional/issues-29267-uint8array-in-json/_fixture/generated/contract.d.ts index 6fbfd0302bfd..3e501b676faa 100644 --- a/test/integration/test/ports/prisma/functional/issues-29267-uint8array-in-json/_fixture/generated/contract.d.ts +++ b/test/integration/test/ports/prisma/functional/issues-29267-uint8array-in-json/_fixture/generated/contract.d.ts @@ -43,6 +43,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; @@ -92,6 +96,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; @@ -101,6 +109,14 @@ export type AggregateTypes = { readonly output: 'pg/time-temporal@1'; readonly nullable: true; }; + readonly 'pg/timestamp-date@1': { + readonly output: 'pg/timestamp-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-instant@1': { + readonly output: 'pg/timestamp-instant@1'; + readonly nullable: true; + }; readonly 'pg/timestamp-string@1': { readonly output: 'pg/timestamp-string@1'; readonly nullable: true; @@ -152,6 +168,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; @@ -161,6 +181,14 @@ export type AggregateTypes = { readonly output: 'pg/time-temporal@1'; readonly nullable: true; }; + readonly 'pg/timestamp-date@1': { + readonly output: 'pg/timestamp-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-instant@1': { + readonly output: 'pg/timestamp-instant@1'; + readonly nullable: true; + }; readonly 'pg/timestamp-string@1': { readonly output: 'pg/timestamp-string@1'; readonly nullable: true; @@ -204,6 +232,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; diff --git a/test/integration/test/ports/prisma/functional/issues-29309-datetime-cursor/_fixture/generated/contract.d.ts b/test/integration/test/ports/prisma/functional/issues-29309-datetime-cursor/_fixture/generated/contract.d.ts index 6930fcdd7f47..0ded7d823444 100644 --- a/test/integration/test/ports/prisma/functional/issues-29309-datetime-cursor/_fixture/generated/contract.d.ts +++ b/test/integration/test/ports/prisma/functional/issues-29309-datetime-cursor/_fixture/generated/contract.d.ts @@ -42,6 +42,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; @@ -91,6 +95,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; @@ -100,6 +108,14 @@ export type AggregateTypes = { readonly output: 'pg/time-temporal@1'; readonly nullable: true; }; + readonly 'pg/timestamp-date@1': { + readonly output: 'pg/timestamp-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-instant@1': { + readonly output: 'pg/timestamp-instant@1'; + readonly nullable: true; + }; readonly 'pg/timestamp-string@1': { readonly output: 'pg/timestamp-string@1'; readonly nullable: true; @@ -151,6 +167,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; @@ -160,6 +180,14 @@ export type AggregateTypes = { readonly output: 'pg/time-temporal@1'; readonly nullable: true; }; + readonly 'pg/timestamp-date@1': { + readonly output: 'pg/timestamp-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-instant@1': { + readonly output: 'pg/timestamp-instant@1'; + readonly nullable: true; + }; readonly 'pg/timestamp-string@1': { readonly output: 'pg/timestamp-string@1'; readonly nullable: true; @@ -203,6 +231,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; diff --git a/test/integration/test/ports/prisma/functional/issues-29331-query-plan-cache-bloat/_fixture/generated/contract.d.ts b/test/integration/test/ports/prisma/functional/issues-29331-query-plan-cache-bloat/_fixture/generated/contract.d.ts index 2366985dc1c5..cc4c03475d9b 100644 --- a/test/integration/test/ports/prisma/functional/issues-29331-query-plan-cache-bloat/_fixture/generated/contract.d.ts +++ b/test/integration/test/ports/prisma/functional/issues-29331-query-plan-cache-bloat/_fixture/generated/contract.d.ts @@ -43,6 +43,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; @@ -92,6 +96,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; @@ -101,6 +109,14 @@ export type AggregateTypes = { readonly output: 'pg/time-temporal@1'; readonly nullable: true; }; + readonly 'pg/timestamp-date@1': { + readonly output: 'pg/timestamp-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-instant@1': { + readonly output: 'pg/timestamp-instant@1'; + readonly nullable: true; + }; readonly 'pg/timestamp-string@1': { readonly output: 'pg/timestamp-string@1'; readonly nullable: true; @@ -152,6 +168,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; @@ -161,6 +181,14 @@ export type AggregateTypes = { readonly output: 'pg/time-temporal@1'; readonly nullable: true; }; + readonly 'pg/timestamp-date@1': { + readonly output: 'pg/timestamp-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-instant@1': { + readonly output: 'pg/timestamp-instant@1'; + readonly nullable: true; + }; readonly 'pg/timestamp-string@1': { readonly output: 'pg/timestamp-string@1'; readonly nullable: true; @@ -204,6 +232,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; diff --git a/test/integration/test/ports/prisma/functional/issues-4004/_fixture/generated/contract.d.ts b/test/integration/test/ports/prisma/functional/issues-4004/_fixture/generated/contract.d.ts index 44d3afa049b7..0caeb9f277d3 100644 --- a/test/integration/test/ports/prisma/functional/issues-4004/_fixture/generated/contract.d.ts +++ b/test/integration/test/ports/prisma/functional/issues-4004/_fixture/generated/contract.d.ts @@ -43,6 +43,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; @@ -92,6 +96,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; @@ -101,6 +109,14 @@ export type AggregateTypes = { readonly output: 'pg/time-temporal@1'; readonly nullable: true; }; + readonly 'pg/timestamp-date@1': { + readonly output: 'pg/timestamp-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-instant@1': { + readonly output: 'pg/timestamp-instant@1'; + readonly nullable: true; + }; readonly 'pg/timestamp-string@1': { readonly output: 'pg/timestamp-string@1'; readonly nullable: true; @@ -152,6 +168,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; @@ -161,6 +181,14 @@ export type AggregateTypes = { readonly output: 'pg/time-temporal@1'; readonly nullable: true; }; + readonly 'pg/timestamp-date@1': { + readonly output: 'pg/timestamp-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-instant@1': { + readonly output: 'pg/timestamp-instant@1'; + readonly nullable: true; + }; readonly 'pg/timestamp-string@1': { readonly output: 'pg/timestamp-string@1'; readonly nullable: true; @@ -204,6 +232,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; diff --git a/test/integration/test/ports/prisma/functional/issues-5952-decimal-batch/_fixture/generated/contract.d.ts b/test/integration/test/ports/prisma/functional/issues-5952-decimal-batch/_fixture/generated/contract.d.ts index 4809252859be..aeeec55736a7 100644 --- a/test/integration/test/ports/prisma/functional/issues-5952-decimal-batch/_fixture/generated/contract.d.ts +++ b/test/integration/test/ports/prisma/functional/issues-5952-decimal-batch/_fixture/generated/contract.d.ts @@ -43,6 +43,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; @@ -92,6 +96,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; @@ -101,6 +109,14 @@ export type AggregateTypes = { readonly output: 'pg/time-temporal@1'; readonly nullable: true; }; + readonly 'pg/timestamp-date@1': { + readonly output: 'pg/timestamp-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-instant@1': { + readonly output: 'pg/timestamp-instant@1'; + readonly nullable: true; + }; readonly 'pg/timestamp-string@1': { readonly output: 'pg/timestamp-string@1'; readonly nullable: true; @@ -152,6 +168,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; @@ -161,6 +181,14 @@ export type AggregateTypes = { readonly output: 'pg/time-temporal@1'; readonly nullable: true; }; + readonly 'pg/timestamp-date@1': { + readonly output: 'pg/timestamp-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-instant@1': { + readonly output: 'pg/timestamp-instant@1'; + readonly nullable: true; + }; readonly 'pg/timestamp-string@1': { readonly output: 'pg/timestamp-string@1'; readonly nullable: true; @@ -204,6 +232,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; diff --git a/test/integration/test/ports/prisma/functional/issues-TML-1664-invalid-enum-value-error/_fixture/generated/contract.d.ts b/test/integration/test/ports/prisma/functional/issues-TML-1664-invalid-enum-value-error/_fixture/generated/contract.d.ts index bd59d3e2460a..c970ea08f99c 100644 --- a/test/integration/test/ports/prisma/functional/issues-TML-1664-invalid-enum-value-error/_fixture/generated/contract.d.ts +++ b/test/integration/test/ports/prisma/functional/issues-TML-1664-invalid-enum-value-error/_fixture/generated/contract.d.ts @@ -42,6 +42,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; @@ -91,6 +95,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; @@ -100,6 +108,14 @@ export type AggregateTypes = { readonly output: 'pg/time-temporal@1'; readonly nullable: true; }; + readonly 'pg/timestamp-date@1': { + readonly output: 'pg/timestamp-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-instant@1': { + readonly output: 'pg/timestamp-instant@1'; + readonly nullable: true; + }; readonly 'pg/timestamp-string@1': { readonly output: 'pg/timestamp-string@1'; readonly nullable: true; @@ -151,6 +167,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; @@ -160,6 +180,14 @@ export type AggregateTypes = { readonly output: 'pg/time-temporal@1'; readonly nullable: true; }; + readonly 'pg/timestamp-date@1': { + readonly output: 'pg/timestamp-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-instant@1': { + readonly output: 'pg/timestamp-instant@1'; + readonly nullable: true; + }; readonly 'pg/timestamp-string@1': { readonly output: 'pg/timestamp-string@1'; readonly nullable: true; @@ -203,6 +231,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; diff --git a/test/integration/test/ports/prisma/functional/issues-TML-1664-unknown-enum-value-read-error/_fixture/generated/contract.d.ts b/test/integration/test/ports/prisma/functional/issues-TML-1664-unknown-enum-value-read-error/_fixture/generated/contract.d.ts index 2997b4fb9517..e7b6774e6178 100644 --- a/test/integration/test/ports/prisma/functional/issues-TML-1664-unknown-enum-value-read-error/_fixture/generated/contract.d.ts +++ b/test/integration/test/ports/prisma/functional/issues-TML-1664-unknown-enum-value-read-error/_fixture/generated/contract.d.ts @@ -42,6 +42,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; @@ -91,6 +95,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; @@ -100,6 +108,14 @@ export type AggregateTypes = { readonly output: 'pg/time-temporal@1'; readonly nullable: true; }; + readonly 'pg/timestamp-date@1': { + readonly output: 'pg/timestamp-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-instant@1': { + readonly output: 'pg/timestamp-instant@1'; + readonly nullable: true; + }; readonly 'pg/timestamp-string@1': { readonly output: 'pg/timestamp-string@1'; readonly nullable: true; @@ -151,6 +167,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; @@ -160,6 +180,14 @@ export type AggregateTypes = { readonly output: 'pg/time-temporal@1'; readonly nullable: true; }; + readonly 'pg/timestamp-date@1': { + readonly output: 'pg/timestamp-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-instant@1': { + readonly output: 'pg/timestamp-instant@1'; + readonly nullable: true; + }; readonly 'pg/timestamp-string@1': { readonly output: 'pg/timestamp-string@1'; readonly nullable: true; @@ -203,6 +231,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; diff --git a/test/integration/test/ports/prisma/functional/issues-unmapped-driver-error-user-facing/_fixture/generated/contract.d.ts b/test/integration/test/ports/prisma/functional/issues-unmapped-driver-error-user-facing/_fixture/generated/contract.d.ts index ec2866dccc62..e4a6e00150ba 100644 --- a/test/integration/test/ports/prisma/functional/issues-unmapped-driver-error-user-facing/_fixture/generated/contract.d.ts +++ b/test/integration/test/ports/prisma/functional/issues-unmapped-driver-error-user-facing/_fixture/generated/contract.d.ts @@ -42,6 +42,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; @@ -91,6 +95,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; @@ -100,6 +108,14 @@ export type AggregateTypes = { readonly output: 'pg/time-temporal@1'; readonly nullable: true; }; + readonly 'pg/timestamp-date@1': { + readonly output: 'pg/timestamp-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-instant@1': { + readonly output: 'pg/timestamp-instant@1'; + readonly nullable: true; + }; readonly 'pg/timestamp-string@1': { readonly output: 'pg/timestamp-string@1'; readonly nullable: true; @@ -151,6 +167,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; @@ -160,6 +180,14 @@ export type AggregateTypes = { readonly output: 'pg/time-temporal@1'; readonly nullable: true; }; + readonly 'pg/timestamp-date@1': { + readonly output: 'pg/timestamp-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-instant@1': { + readonly output: 'pg/timestamp-instant@1'; + readonly nullable: true; + }; readonly 'pg/timestamp-string@1': { readonly output: 'pg/timestamp-string@1'; readonly nullable: true; @@ -203,6 +231,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; diff --git a/test/integration/test/ports/prisma/functional/json-fields/_fixture/generated/contract.d.ts b/test/integration/test/ports/prisma/functional/json-fields/_fixture/generated/contract.d.ts index e3ec8dcf5995..a47ae2c73a53 100644 --- a/test/integration/test/ports/prisma/functional/json-fields/_fixture/generated/contract.d.ts +++ b/test/integration/test/ports/prisma/functional/json-fields/_fixture/generated/contract.d.ts @@ -42,6 +42,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; @@ -91,6 +95,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; @@ -100,6 +108,14 @@ export type AggregateTypes = { readonly output: 'pg/time-temporal@1'; readonly nullable: true; }; + readonly 'pg/timestamp-date@1': { + readonly output: 'pg/timestamp-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-instant@1': { + readonly output: 'pg/timestamp-instant@1'; + readonly nullable: true; + }; readonly 'pg/timestamp-string@1': { readonly output: 'pg/timestamp-string@1'; readonly nullable: true; @@ -151,6 +167,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; @@ -160,6 +180,14 @@ export type AggregateTypes = { readonly output: 'pg/time-temporal@1'; readonly nullable: true; }; + readonly 'pg/timestamp-date@1': { + readonly output: 'pg/timestamp-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-instant@1': { + readonly output: 'pg/timestamp-instant@1'; + readonly nullable: true; + }; readonly 'pg/timestamp-string@1': { readonly output: 'pg/timestamp-string@1'; readonly nullable: true; @@ -203,6 +231,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; diff --git a/test/integration/test/ports/prisma/functional/large-floats/_fixture/generated/contract.d.ts b/test/integration/test/ports/prisma/functional/large-floats/_fixture/generated/contract.d.ts index 56f66ee21946..a1d64a266ab5 100644 --- a/test/integration/test/ports/prisma/functional/large-floats/_fixture/generated/contract.d.ts +++ b/test/integration/test/ports/prisma/functional/large-floats/_fixture/generated/contract.d.ts @@ -42,6 +42,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; @@ -91,6 +95,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; @@ -100,6 +108,14 @@ export type AggregateTypes = { readonly output: 'pg/time-temporal@1'; readonly nullable: true; }; + readonly 'pg/timestamp-date@1': { + readonly output: 'pg/timestamp-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-instant@1': { + readonly output: 'pg/timestamp-instant@1'; + readonly nullable: true; + }; readonly 'pg/timestamp-string@1': { readonly output: 'pg/timestamp-string@1'; readonly nullable: true; @@ -151,6 +167,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; @@ -160,6 +180,14 @@ export type AggregateTypes = { readonly output: 'pg/time-temporal@1'; readonly nullable: true; }; + readonly 'pg/timestamp-date@1': { + readonly output: 'pg/timestamp-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-instant@1': { + readonly output: 'pg/timestamp-instant@1'; + readonly nullable: true; + }; readonly 'pg/timestamp-string@1': { readonly output: 'pg/timestamp-string@1'; readonly nullable: true; @@ -203,6 +231,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; diff --git a/test/integration/test/ports/prisma/functional/legacy-aggregations/_fixture/generated/contract.d.ts b/test/integration/test/ports/prisma/functional/legacy-aggregations/_fixture/generated/contract.d.ts index a39a62ca4873..d12c84abfeb2 100644 --- a/test/integration/test/ports/prisma/functional/legacy-aggregations/_fixture/generated/contract.d.ts +++ b/test/integration/test/ports/prisma/functional/legacy-aggregations/_fixture/generated/contract.d.ts @@ -43,6 +43,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; @@ -92,6 +96,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; @@ -101,6 +109,14 @@ export type AggregateTypes = { readonly output: 'pg/time-temporal@1'; readonly nullable: true; }; + readonly 'pg/timestamp-date@1': { + readonly output: 'pg/timestamp-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-instant@1': { + readonly output: 'pg/timestamp-instant@1'; + readonly nullable: true; + }; readonly 'pg/timestamp-string@1': { readonly output: 'pg/timestamp-string@1'; readonly nullable: true; @@ -152,6 +168,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; @@ -161,6 +181,14 @@ export type AggregateTypes = { readonly output: 'pg/time-temporal@1'; readonly nullable: true; }; + readonly 'pg/timestamp-date@1': { + readonly output: 'pg/timestamp-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-instant@1': { + readonly output: 'pg/timestamp-instant@1'; + readonly nullable: true; + }; readonly 'pg/timestamp-string@1': { readonly output: 'pg/timestamp-string@1'; readonly nullable: true; @@ -204,6 +232,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; diff --git a/test/integration/test/ports/prisma/functional/legacy-json/_fixture/generated/contract.d.ts b/test/integration/test/ports/prisma/functional/legacy-json/_fixture/generated/contract.d.ts index cfebb938dd67..3af051032f68 100644 --- a/test/integration/test/ports/prisma/functional/legacy-json/_fixture/generated/contract.d.ts +++ b/test/integration/test/ports/prisma/functional/legacy-json/_fixture/generated/contract.d.ts @@ -42,6 +42,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; @@ -91,6 +95,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; @@ -100,6 +108,14 @@ export type AggregateTypes = { readonly output: 'pg/time-temporal@1'; readonly nullable: true; }; + readonly 'pg/timestamp-date@1': { + readonly output: 'pg/timestamp-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-instant@1': { + readonly output: 'pg/timestamp-instant@1'; + readonly nullable: true; + }; readonly 'pg/timestamp-string@1': { readonly output: 'pg/timestamp-string@1'; readonly nullable: true; @@ -151,6 +167,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; @@ -160,6 +180,14 @@ export type AggregateTypes = { readonly output: 'pg/time-temporal@1'; readonly nullable: true; }; + readonly 'pg/timestamp-date@1': { + readonly output: 'pg/timestamp-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-instant@1': { + readonly output: 'pg/timestamp-instant@1'; + readonly nullable: true; + }; readonly 'pg/timestamp-string@1': { readonly output: 'pg/timestamp-string@1'; readonly nullable: true; @@ -203,6 +231,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; diff --git a/test/integration/test/ports/prisma/functional/legacy-optional-relation-filters/_fixture/generated/contract.d.ts b/test/integration/test/ports/prisma/functional/legacy-optional-relation-filters/_fixture/generated/contract.d.ts index e151fbd40ecf..0d9e93acb690 100644 --- a/test/integration/test/ports/prisma/functional/legacy-optional-relation-filters/_fixture/generated/contract.d.ts +++ b/test/integration/test/ports/prisma/functional/legacy-optional-relation-filters/_fixture/generated/contract.d.ts @@ -43,6 +43,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; @@ -92,6 +96,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; @@ -101,6 +109,14 @@ export type AggregateTypes = { readonly output: 'pg/time-temporal@1'; readonly nullable: true; }; + readonly 'pg/timestamp-date@1': { + readonly output: 'pg/timestamp-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-instant@1': { + readonly output: 'pg/timestamp-instant@1'; + readonly nullable: true; + }; readonly 'pg/timestamp-string@1': { readonly output: 'pg/timestamp-string@1'; readonly nullable: true; @@ -152,6 +168,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; @@ -161,6 +181,14 @@ export type AggregateTypes = { readonly output: 'pg/time-temporal@1'; readonly nullable: true; }; + readonly 'pg/timestamp-date@1': { + readonly output: 'pg/timestamp-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-instant@1': { + readonly output: 'pg/timestamp-instant@1'; + readonly nullable: true; + }; readonly 'pg/timestamp-string@1': { readonly output: 'pg/timestamp-string@1'; readonly nullable: true; @@ -204,6 +232,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; diff --git a/test/integration/test/ports/prisma/functional/methods-count/_fixture/generated/contract.d.ts b/test/integration/test/ports/prisma/functional/methods-count/_fixture/generated/contract.d.ts index ce9f177f0828..e0c5667dfb02 100644 --- a/test/integration/test/ports/prisma/functional/methods-count/_fixture/generated/contract.d.ts +++ b/test/integration/test/ports/prisma/functional/methods-count/_fixture/generated/contract.d.ts @@ -43,6 +43,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; @@ -92,6 +96,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; @@ -101,6 +109,14 @@ export type AggregateTypes = { readonly output: 'pg/time-temporal@1'; readonly nullable: true; }; + readonly 'pg/timestamp-date@1': { + readonly output: 'pg/timestamp-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-instant@1': { + readonly output: 'pg/timestamp-instant@1'; + readonly nullable: true; + }; readonly 'pg/timestamp-string@1': { readonly output: 'pg/timestamp-string@1'; readonly nullable: true; @@ -152,6 +168,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; @@ -161,6 +181,14 @@ export type AggregateTypes = { readonly output: 'pg/time-temporal@1'; readonly nullable: true; }; + readonly 'pg/timestamp-date@1': { + readonly output: 'pg/timestamp-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-instant@1': { + readonly output: 'pg/timestamp-instant@1'; + readonly nullable: true; + }; readonly 'pg/timestamp-string@1': { readonly output: 'pg/timestamp-string@1'; readonly nullable: true; @@ -204,6 +232,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; diff --git a/test/integration/test/ports/prisma/functional/methods-createMany/_fixture/generated/contract.d.ts b/test/integration/test/ports/prisma/functional/methods-createMany/_fixture/generated/contract.d.ts index 7957de406432..ddd77f3cec1c 100644 --- a/test/integration/test/ports/prisma/functional/methods-createMany/_fixture/generated/contract.d.ts +++ b/test/integration/test/ports/prisma/functional/methods-createMany/_fixture/generated/contract.d.ts @@ -43,6 +43,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; @@ -92,6 +96,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; @@ -101,6 +109,14 @@ export type AggregateTypes = { readonly output: 'pg/time-temporal@1'; readonly nullable: true; }; + readonly 'pg/timestamp-date@1': { + readonly output: 'pg/timestamp-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-instant@1': { + readonly output: 'pg/timestamp-instant@1'; + readonly nullable: true; + }; readonly 'pg/timestamp-string@1': { readonly output: 'pg/timestamp-string@1'; readonly nullable: true; @@ -152,6 +168,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; @@ -161,6 +181,14 @@ export type AggregateTypes = { readonly output: 'pg/time-temporal@1'; readonly nullable: true; }; + readonly 'pg/timestamp-date@1': { + readonly output: 'pg/timestamp-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-instant@1': { + readonly output: 'pg/timestamp-instant@1'; + readonly nullable: true; + }; readonly 'pg/timestamp-string@1': { readonly output: 'pg/timestamp-string@1'; readonly nullable: true; @@ -204,6 +232,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; diff --git a/test/integration/test/ports/prisma/functional/methods-createManyAndReturn/_fixture/generated/contract.d.ts b/test/integration/test/ports/prisma/functional/methods-createManyAndReturn/_fixture/generated/contract.d.ts index 7957de406432..ddd77f3cec1c 100644 --- a/test/integration/test/ports/prisma/functional/methods-createManyAndReturn/_fixture/generated/contract.d.ts +++ b/test/integration/test/ports/prisma/functional/methods-createManyAndReturn/_fixture/generated/contract.d.ts @@ -43,6 +43,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; @@ -92,6 +96,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; @@ -101,6 +109,14 @@ export type AggregateTypes = { readonly output: 'pg/time-temporal@1'; readonly nullable: true; }; + readonly 'pg/timestamp-date@1': { + readonly output: 'pg/timestamp-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-instant@1': { + readonly output: 'pg/timestamp-instant@1'; + readonly nullable: true; + }; readonly 'pg/timestamp-string@1': { readonly output: 'pg/timestamp-string@1'; readonly nullable: true; @@ -152,6 +168,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; @@ -161,6 +181,14 @@ export type AggregateTypes = { readonly output: 'pg/time-temporal@1'; readonly nullable: true; }; + readonly 'pg/timestamp-date@1': { + readonly output: 'pg/timestamp-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-instant@1': { + readonly output: 'pg/timestamp-instant@1'; + readonly nullable: true; + }; readonly 'pg/timestamp-string@1': { readonly output: 'pg/timestamp-string@1'; readonly nullable: true; @@ -204,6 +232,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; diff --git a/test/integration/test/ports/prisma/functional/methods-findFirstOrThrow/_fixture/generated/contract.d.ts b/test/integration/test/ports/prisma/functional/methods-findFirstOrThrow/_fixture/generated/contract.d.ts index e63c1fa50725..d212ca906fff 100644 --- a/test/integration/test/ports/prisma/functional/methods-findFirstOrThrow/_fixture/generated/contract.d.ts +++ b/test/integration/test/ports/prisma/functional/methods-findFirstOrThrow/_fixture/generated/contract.d.ts @@ -43,6 +43,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; @@ -92,6 +96,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; @@ -101,6 +109,14 @@ export type AggregateTypes = { readonly output: 'pg/time-temporal@1'; readonly nullable: true; }; + readonly 'pg/timestamp-date@1': { + readonly output: 'pg/timestamp-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-instant@1': { + readonly output: 'pg/timestamp-instant@1'; + readonly nullable: true; + }; readonly 'pg/timestamp-string@1': { readonly output: 'pg/timestamp-string@1'; readonly nullable: true; @@ -152,6 +168,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; @@ -161,6 +181,14 @@ export type AggregateTypes = { readonly output: 'pg/time-temporal@1'; readonly nullable: true; }; + readonly 'pg/timestamp-date@1': { + readonly output: 'pg/timestamp-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-instant@1': { + readonly output: 'pg/timestamp-instant@1'; + readonly nullable: true; + }; readonly 'pg/timestamp-string@1': { readonly output: 'pg/timestamp-string@1'; readonly nullable: true; @@ -204,6 +232,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; diff --git a/test/integration/test/ports/prisma/functional/methods-findUniqueOrThrow/_fixture/generated/contract.d.ts b/test/integration/test/ports/prisma/functional/methods-findUniqueOrThrow/_fixture/generated/contract.d.ts index eb999fd63451..664ab8fcdeeb 100644 --- a/test/integration/test/ports/prisma/functional/methods-findUniqueOrThrow/_fixture/generated/contract.d.ts +++ b/test/integration/test/ports/prisma/functional/methods-findUniqueOrThrow/_fixture/generated/contract.d.ts @@ -43,6 +43,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; @@ -92,6 +96,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; @@ -101,6 +109,14 @@ export type AggregateTypes = { readonly output: 'pg/time-temporal@1'; readonly nullable: true; }; + readonly 'pg/timestamp-date@1': { + readonly output: 'pg/timestamp-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-instant@1': { + readonly output: 'pg/timestamp-instant@1'; + readonly nullable: true; + }; readonly 'pg/timestamp-string@1': { readonly output: 'pg/timestamp-string@1'; readonly nullable: true; @@ -152,6 +168,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; @@ -161,6 +181,14 @@ export type AggregateTypes = { readonly output: 'pg/time-temporal@1'; readonly nullable: true; }; + readonly 'pg/timestamp-date@1': { + readonly output: 'pg/timestamp-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-instant@1': { + readonly output: 'pg/timestamp-instant@1'; + readonly nullable: true; + }; readonly 'pg/timestamp-string@1': { readonly output: 'pg/timestamp-string@1'; readonly nullable: true; @@ -204,6 +232,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; diff --git a/test/integration/test/ports/prisma/functional/methods-updateManyAndReturn/_fixture/generated/contract.d.ts b/test/integration/test/ports/prisma/functional/methods-updateManyAndReturn/_fixture/generated/contract.d.ts index 7957de406432..ddd77f3cec1c 100644 --- a/test/integration/test/ports/prisma/functional/methods-updateManyAndReturn/_fixture/generated/contract.d.ts +++ b/test/integration/test/ports/prisma/functional/methods-updateManyAndReturn/_fixture/generated/contract.d.ts @@ -43,6 +43,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; @@ -92,6 +96,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; @@ -101,6 +109,14 @@ export type AggregateTypes = { readonly output: 'pg/time-temporal@1'; readonly nullable: true; }; + readonly 'pg/timestamp-date@1': { + readonly output: 'pg/timestamp-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-instant@1': { + readonly output: 'pg/timestamp-instant@1'; + readonly nullable: true; + }; readonly 'pg/timestamp-string@1': { readonly output: 'pg/timestamp-string@1'; readonly nullable: true; @@ -152,6 +168,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; @@ -161,6 +181,14 @@ export type AggregateTypes = { readonly output: 'pg/time-temporal@1'; readonly nullable: true; }; + readonly 'pg/timestamp-date@1': { + readonly output: 'pg/timestamp-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-instant@1': { + readonly output: 'pg/timestamp-instant@1'; + readonly nullable: true; + }; readonly 'pg/timestamp-string@1': { readonly output: 'pg/timestamp-string@1'; readonly nullable: true; @@ -204,6 +232,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; diff --git a/test/integration/test/ports/prisma/functional/methods-upsert-native-atomic/_fixture/generated/contract.d.ts b/test/integration/test/ports/prisma/functional/methods-upsert-native-atomic/_fixture/generated/contract.d.ts index 728eb727f31b..80593281bafc 100644 --- a/test/integration/test/ports/prisma/functional/methods-upsert-native-atomic/_fixture/generated/contract.d.ts +++ b/test/integration/test/ports/prisma/functional/methods-upsert-native-atomic/_fixture/generated/contract.d.ts @@ -43,6 +43,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; @@ -92,6 +96,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; @@ -101,6 +109,14 @@ export type AggregateTypes = { readonly output: 'pg/time-temporal@1'; readonly nullable: true; }; + readonly 'pg/timestamp-date@1': { + readonly output: 'pg/timestamp-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-instant@1': { + readonly output: 'pg/timestamp-instant@1'; + readonly nullable: true; + }; readonly 'pg/timestamp-string@1': { readonly output: 'pg/timestamp-string@1'; readonly nullable: true; @@ -152,6 +168,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; @@ -161,6 +181,14 @@ export type AggregateTypes = { readonly output: 'pg/time-temporal@1'; readonly nullable: true; }; + readonly 'pg/timestamp-date@1': { + readonly output: 'pg/timestamp-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-instant@1': { + readonly output: 'pg/timestamp-instant@1'; + readonly nullable: true; + }; readonly 'pg/timestamp-string@1': { readonly output: 'pg/timestamp-string@1'; readonly nullable: true; @@ -204,6 +232,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; diff --git a/test/integration/test/ports/prisma/functional/methods-upsert-simple/_fixture/generated/contract.d.ts b/test/integration/test/ports/prisma/functional/methods-upsert-simple/_fixture/generated/contract.d.ts index 10df8d913b94..6d5cc4f4b852 100644 --- a/test/integration/test/ports/prisma/functional/methods-upsert-simple/_fixture/generated/contract.d.ts +++ b/test/integration/test/ports/prisma/functional/methods-upsert-simple/_fixture/generated/contract.d.ts @@ -43,6 +43,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; @@ -92,6 +96,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; @@ -101,6 +109,14 @@ export type AggregateTypes = { readonly output: 'pg/time-temporal@1'; readonly nullable: true; }; + readonly 'pg/timestamp-date@1': { + readonly output: 'pg/timestamp-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-instant@1': { + readonly output: 'pg/timestamp-instant@1'; + readonly nullable: true; + }; readonly 'pg/timestamp-string@1': { readonly output: 'pg/timestamp-string@1'; readonly nullable: true; @@ -152,6 +168,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; @@ -161,6 +181,14 @@ export type AggregateTypes = { readonly output: 'pg/time-temporal@1'; readonly nullable: true; }; + readonly 'pg/timestamp-date@1': { + readonly output: 'pg/timestamp-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-instant@1': { + readonly output: 'pg/timestamp-instant@1'; + readonly nullable: true; + }; readonly 'pg/timestamp-string@1': { readonly output: 'pg/timestamp-string@1'; readonly nullable: true; @@ -204,6 +232,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; diff --git a/test/integration/test/ports/prisma/functional/mixed-string-uuid-datetime-list-inputs/_fixture/generated/contract.d.ts b/test/integration/test/ports/prisma/functional/mixed-string-uuid-datetime-list-inputs/_fixture/generated/contract.d.ts index a34339912d4e..26424054f0f8 100644 --- a/test/integration/test/ports/prisma/functional/mixed-string-uuid-datetime-list-inputs/_fixture/generated/contract.d.ts +++ b/test/integration/test/ports/prisma/functional/mixed-string-uuid-datetime-list-inputs/_fixture/generated/contract.d.ts @@ -43,6 +43,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; @@ -92,6 +96,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; @@ -101,6 +109,14 @@ export type AggregateTypes = { readonly output: 'pg/time-temporal@1'; readonly nullable: true; }; + readonly 'pg/timestamp-date@1': { + readonly output: 'pg/timestamp-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-instant@1': { + readonly output: 'pg/timestamp-instant@1'; + readonly nullable: true; + }; readonly 'pg/timestamp-string@1': { readonly output: 'pg/timestamp-string@1'; readonly nullable: true; @@ -152,6 +168,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; @@ -161,6 +181,14 @@ export type AggregateTypes = { readonly output: 'pg/time-temporal@1'; readonly nullable: true; }; + readonly 'pg/timestamp-date@1': { + readonly output: 'pg/timestamp-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-instant@1': { + readonly output: 'pg/timestamp-instant@1'; + readonly nullable: true; + }; readonly 'pg/timestamp-string@1': { readonly output: 'pg/timestamp-string@1'; readonly nullable: true; @@ -204,6 +232,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; diff --git a/test/integration/test/ports/prisma/functional/multi-schema/_fixture/different-names/generated/contract.d.ts b/test/integration/test/ports/prisma/functional/multi-schema/_fixture/different-names/generated/contract.d.ts index a2ba82ad58c5..3b33f9ee76b7 100644 --- a/test/integration/test/ports/prisma/functional/multi-schema/_fixture/different-names/generated/contract.d.ts +++ b/test/integration/test/ports/prisma/functional/multi-schema/_fixture/different-names/generated/contract.d.ts @@ -43,6 +43,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; @@ -92,6 +96,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; @@ -101,6 +109,14 @@ export type AggregateTypes = { readonly output: 'pg/time-temporal@1'; readonly nullable: true; }; + readonly 'pg/timestamp-date@1': { + readonly output: 'pg/timestamp-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-instant@1': { + readonly output: 'pg/timestamp-instant@1'; + readonly nullable: true; + }; readonly 'pg/timestamp-string@1': { readonly output: 'pg/timestamp-string@1'; readonly nullable: true; @@ -152,6 +168,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; @@ -161,6 +181,14 @@ export type AggregateTypes = { readonly output: 'pg/time-temporal@1'; readonly nullable: true; }; + readonly 'pg/timestamp-date@1': { + readonly output: 'pg/timestamp-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-instant@1': { + readonly output: 'pg/timestamp-instant@1'; + readonly nullable: true; + }; readonly 'pg/timestamp-string@1': { readonly output: 'pg/timestamp-string@1'; readonly nullable: true; @@ -204,6 +232,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; diff --git a/test/integration/test/ports/prisma/functional/multi-schema/_fixture/identical-names/generated/contract.d.ts b/test/integration/test/ports/prisma/functional/multi-schema/_fixture/identical-names/generated/contract.d.ts index 73c3316d7dbe..3f09775ce37b 100644 --- a/test/integration/test/ports/prisma/functional/multi-schema/_fixture/identical-names/generated/contract.d.ts +++ b/test/integration/test/ports/prisma/functional/multi-schema/_fixture/identical-names/generated/contract.d.ts @@ -43,6 +43,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; @@ -92,6 +96,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; @@ -101,6 +109,14 @@ export type AggregateTypes = { readonly output: 'pg/time-temporal@1'; readonly nullable: true; }; + readonly 'pg/timestamp-date@1': { + readonly output: 'pg/timestamp-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-instant@1': { + readonly output: 'pg/timestamp-instant@1'; + readonly nullable: true; + }; readonly 'pg/timestamp-string@1': { readonly output: 'pg/timestamp-string@1'; readonly nullable: true; @@ -152,6 +168,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; @@ -161,6 +181,14 @@ export type AggregateTypes = { readonly output: 'pg/time-temporal@1'; readonly nullable: true; }; + readonly 'pg/timestamp-date@1': { + readonly output: 'pg/timestamp-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-instant@1': { + readonly output: 'pg/timestamp-instant@1'; + readonly nullable: true; + }; readonly 'pg/timestamp-string@1': { readonly output: 'pg/timestamp-string@1'; readonly nullable: true; @@ -204,6 +232,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; diff --git a/test/integration/test/ports/prisma/functional/multi-schema/_fixture/no-map/generated/contract.d.ts b/test/integration/test/ports/prisma/functional/multi-schema/_fixture/no-map/generated/contract.d.ts index c2051d019797..01f327375997 100644 --- a/test/integration/test/ports/prisma/functional/multi-schema/_fixture/no-map/generated/contract.d.ts +++ b/test/integration/test/ports/prisma/functional/multi-schema/_fixture/no-map/generated/contract.d.ts @@ -43,6 +43,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; @@ -92,6 +96,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; @@ -101,6 +109,14 @@ export type AggregateTypes = { readonly output: 'pg/time-temporal@1'; readonly nullable: true; }; + readonly 'pg/timestamp-date@1': { + readonly output: 'pg/timestamp-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-instant@1': { + readonly output: 'pg/timestamp-instant@1'; + readonly nullable: true; + }; readonly 'pg/timestamp-string@1': { readonly output: 'pg/timestamp-string@1'; readonly nullable: true; @@ -152,6 +168,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; @@ -161,6 +181,14 @@ export type AggregateTypes = { readonly output: 'pg/time-temporal@1'; readonly nullable: true; }; + readonly 'pg/timestamp-date@1': { + readonly output: 'pg/timestamp-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-instant@1': { + readonly output: 'pg/timestamp-instant@1'; + readonly nullable: true; + }; readonly 'pg/timestamp-string@1': { readonly output: 'pg/timestamp-string@1'; readonly nullable: true; @@ -204,6 +232,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; diff --git a/test/integration/test/ports/prisma/functional/multiple-types/_fixture/generated/contract.d.ts b/test/integration/test/ports/prisma/functional/multiple-types/_fixture/generated/contract.d.ts index 6607bbc7e2ec..c61eb1355df1 100644 --- a/test/integration/test/ports/prisma/functional/multiple-types/_fixture/generated/contract.d.ts +++ b/test/integration/test/ports/prisma/functional/multiple-types/_fixture/generated/contract.d.ts @@ -43,6 +43,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; @@ -92,6 +96,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; @@ -101,6 +109,14 @@ export type AggregateTypes = { readonly output: 'pg/time-temporal@1'; readonly nullable: true; }; + readonly 'pg/timestamp-date@1': { + readonly output: 'pg/timestamp-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-instant@1': { + readonly output: 'pg/timestamp-instant@1'; + readonly nullable: true; + }; readonly 'pg/timestamp-string@1': { readonly output: 'pg/timestamp-string@1'; readonly nullable: true; @@ -152,6 +168,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; @@ -161,6 +181,14 @@ export type AggregateTypes = { readonly output: 'pg/time-temporal@1'; readonly nullable: true; }; + readonly 'pg/timestamp-date@1': { + readonly output: 'pg/timestamp-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-instant@1': { + readonly output: 'pg/timestamp-instant@1'; + readonly nullable: true; + }; readonly 'pg/timestamp-string@1': { readonly output: 'pg/timestamp-string@1'; readonly nullable: true; @@ -204,6 +232,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; diff --git a/test/integration/test/ports/prisma/functional/optimistic-concurrency-control/_fixture/generated/contract.d.ts b/test/integration/test/ports/prisma/functional/optimistic-concurrency-control/_fixture/generated/contract.d.ts index c2ac9b36d543..872dbe4b6486 100644 --- a/test/integration/test/ports/prisma/functional/optimistic-concurrency-control/_fixture/generated/contract.d.ts +++ b/test/integration/test/ports/prisma/functional/optimistic-concurrency-control/_fixture/generated/contract.d.ts @@ -43,6 +43,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; @@ -92,6 +96,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; @@ -101,6 +109,14 @@ export type AggregateTypes = { readonly output: 'pg/time-temporal@1'; readonly nullable: true; }; + readonly 'pg/timestamp-date@1': { + readonly output: 'pg/timestamp-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-instant@1': { + readonly output: 'pg/timestamp-instant@1'; + readonly nullable: true; + }; readonly 'pg/timestamp-string@1': { readonly output: 'pg/timestamp-string@1'; readonly nullable: true; @@ -152,6 +168,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; @@ -161,6 +181,14 @@ export type AggregateTypes = { readonly output: 'pg/time-temporal@1'; readonly nullable: true; }; + readonly 'pg/timestamp-date@1': { + readonly output: 'pg/timestamp-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-instant@1': { + readonly output: 'pg/timestamp-instant@1'; + readonly nullable: true; + }; readonly 'pg/timestamp-string@1': { readonly output: 'pg/timestamp-string@1'; readonly nullable: true; @@ -204,6 +232,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; diff --git a/test/integration/test/ports/prisma/functional/referential-actions-set-default-1to1/_fixture/generated/contract.d.ts b/test/integration/test/ports/prisma/functional/referential-actions-set-default-1to1/_fixture/generated/contract.d.ts index b8d42779829b..76b7076e2082 100644 --- a/test/integration/test/ports/prisma/functional/referential-actions-set-default-1to1/_fixture/generated/contract.d.ts +++ b/test/integration/test/ports/prisma/functional/referential-actions-set-default-1to1/_fixture/generated/contract.d.ts @@ -42,6 +42,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; @@ -91,6 +95,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; @@ -100,6 +108,14 @@ export type AggregateTypes = { readonly output: 'pg/time-temporal@1'; readonly nullable: true; }; + readonly 'pg/timestamp-date@1': { + readonly output: 'pg/timestamp-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-instant@1': { + readonly output: 'pg/timestamp-instant@1'; + readonly nullable: true; + }; readonly 'pg/timestamp-string@1': { readonly output: 'pg/timestamp-string@1'; readonly nullable: true; @@ -151,6 +167,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; @@ -160,6 +180,14 @@ export type AggregateTypes = { readonly output: 'pg/time-temporal@1'; readonly nullable: true; }; + readonly 'pg/timestamp-date@1': { + readonly output: 'pg/timestamp-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-instant@1': { + readonly output: 'pg/timestamp-instant@1'; + readonly nullable: true; + }; readonly 'pg/timestamp-string@1': { readonly output: 'pg/timestamp-string@1'; readonly nullable: true; @@ -203,6 +231,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; diff --git a/test/integration/test/ports/prisma/functional/referential-actions-set-default-1ton/_fixture/generated/contract.d.ts b/test/integration/test/ports/prisma/functional/referential-actions-set-default-1ton/_fixture/generated/contract.d.ts index b8d42779829b..76b7076e2082 100644 --- a/test/integration/test/ports/prisma/functional/referential-actions-set-default-1ton/_fixture/generated/contract.d.ts +++ b/test/integration/test/ports/prisma/functional/referential-actions-set-default-1ton/_fixture/generated/contract.d.ts @@ -42,6 +42,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; @@ -91,6 +95,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; @@ -100,6 +108,14 @@ export type AggregateTypes = { readonly output: 'pg/time-temporal@1'; readonly nullable: true; }; + readonly 'pg/timestamp-date@1': { + readonly output: 'pg/timestamp-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-instant@1': { + readonly output: 'pg/timestamp-instant@1'; + readonly nullable: true; + }; readonly 'pg/timestamp-string@1': { readonly output: 'pg/timestamp-string@1'; readonly nullable: true; @@ -151,6 +167,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; @@ -160,6 +180,14 @@ export type AggregateTypes = { readonly output: 'pg/time-temporal@1'; readonly nullable: true; }; + readonly 'pg/timestamp-date@1': { + readonly output: 'pg/timestamp-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-instant@1': { + readonly output: 'pg/timestamp-instant@1'; + readonly nullable: true; + }; readonly 'pg/timestamp-string@1': { readonly output: 'pg/timestamp-string@1'; readonly nullable: true; @@ -203,6 +231,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; diff --git a/test/integration/test/ports/prisma/functional/relation-mode-17255-mixed-actions/_fixture/generated/contract.d.ts b/test/integration/test/ports/prisma/functional/relation-mode-17255-mixed-actions/_fixture/generated/contract.d.ts index 738396e1fd8c..fa52219d1e56 100644 --- a/test/integration/test/ports/prisma/functional/relation-mode-17255-mixed-actions/_fixture/generated/contract.d.ts +++ b/test/integration/test/ports/prisma/functional/relation-mode-17255-mixed-actions/_fixture/generated/contract.d.ts @@ -42,6 +42,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; @@ -91,6 +95,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; @@ -100,6 +108,14 @@ export type AggregateTypes = { readonly output: 'pg/time-temporal@1'; readonly nullable: true; }; + readonly 'pg/timestamp-date@1': { + readonly output: 'pg/timestamp-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-instant@1': { + readonly output: 'pg/timestamp-instant@1'; + readonly nullable: true; + }; readonly 'pg/timestamp-string@1': { readonly output: 'pg/timestamp-string@1'; readonly nullable: true; @@ -151,6 +167,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; @@ -160,6 +180,14 @@ export type AggregateTypes = { readonly output: 'pg/time-temporal@1'; readonly nullable: true; }; + readonly 'pg/timestamp-date@1': { + readonly output: 'pg/timestamp-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-instant@1': { + readonly output: 'pg/timestamp-instant@1'; + readonly nullable: true; + }; readonly 'pg/timestamp-string@1': { readonly output: 'pg/timestamp-string@1'; readonly nullable: true; @@ -203,6 +231,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; diff --git a/test/integration/test/ports/prisma/functional/relation-mode-17255-same-actions/_fixture/generated/contract.d.ts b/test/integration/test/ports/prisma/functional/relation-mode-17255-same-actions/_fixture/generated/contract.d.ts index 0858344c6c1a..531c1685a072 100644 --- a/test/integration/test/ports/prisma/functional/relation-mode-17255-same-actions/_fixture/generated/contract.d.ts +++ b/test/integration/test/ports/prisma/functional/relation-mode-17255-same-actions/_fixture/generated/contract.d.ts @@ -42,6 +42,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; @@ -91,6 +95,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; @@ -100,6 +108,14 @@ export type AggregateTypes = { readonly output: 'pg/time-temporal@1'; readonly nullable: true; }; + readonly 'pg/timestamp-date@1': { + readonly output: 'pg/timestamp-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-instant@1': { + readonly output: 'pg/timestamp-instant@1'; + readonly nullable: true; + }; readonly 'pg/timestamp-string@1': { readonly output: 'pg/timestamp-string@1'; readonly nullable: true; @@ -151,6 +167,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; @@ -160,6 +180,14 @@ export type AggregateTypes = { readonly output: 'pg/time-temporal@1'; readonly nullable: true; }; + readonly 'pg/timestamp-date@1': { + readonly output: 'pg/timestamp-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-instant@1': { + readonly output: 'pg/timestamp-instant@1'; + readonly nullable: true; + }; readonly 'pg/timestamp-string@1': { readonly output: 'pg/timestamp-string@1'; readonly nullable: true; @@ -203,6 +231,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; diff --git a/test/integration/test/ports/prisma/functional/relation-mode-gh-1-to-1/_fixture/cascade-map/generated/contract.d.ts b/test/integration/test/ports/prisma/functional/relation-mode-gh-1-to-1/_fixture/cascade-map/generated/contract.d.ts index 138f4c7f85a6..99c0e3aed834 100644 --- a/test/integration/test/ports/prisma/functional/relation-mode-gh-1-to-1/_fixture/cascade-map/generated/contract.d.ts +++ b/test/integration/test/ports/prisma/functional/relation-mode-gh-1-to-1/_fixture/cascade-map/generated/contract.d.ts @@ -42,6 +42,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; @@ -91,6 +95,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; @@ -100,6 +108,14 @@ export type AggregateTypes = { readonly output: 'pg/time-temporal@1'; readonly nullable: true; }; + readonly 'pg/timestamp-date@1': { + readonly output: 'pg/timestamp-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-instant@1': { + readonly output: 'pg/timestamp-instant@1'; + readonly nullable: true; + }; readonly 'pg/timestamp-string@1': { readonly output: 'pg/timestamp-string@1'; readonly nullable: true; @@ -151,6 +167,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; @@ -160,6 +180,14 @@ export type AggregateTypes = { readonly output: 'pg/time-temporal@1'; readonly nullable: true; }; + readonly 'pg/timestamp-date@1': { + readonly output: 'pg/timestamp-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-instant@1': { + readonly output: 'pg/timestamp-instant@1'; + readonly nullable: true; + }; readonly 'pg/timestamp-string@1': { readonly output: 'pg/timestamp-string@1'; readonly nullable: true; @@ -203,6 +231,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; diff --git a/test/integration/test/ports/prisma/functional/relation-mode-gh-1-to-1/_fixture/cascade-plain/generated/contract.d.ts b/test/integration/test/ports/prisma/functional/relation-mode-gh-1-to-1/_fixture/cascade-plain/generated/contract.d.ts index df73f0165817..a03b3273edef 100644 --- a/test/integration/test/ports/prisma/functional/relation-mode-gh-1-to-1/_fixture/cascade-plain/generated/contract.d.ts +++ b/test/integration/test/ports/prisma/functional/relation-mode-gh-1-to-1/_fixture/cascade-plain/generated/contract.d.ts @@ -42,6 +42,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; @@ -91,6 +95,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; @@ -100,6 +108,14 @@ export type AggregateTypes = { readonly output: 'pg/time-temporal@1'; readonly nullable: true; }; + readonly 'pg/timestamp-date@1': { + readonly output: 'pg/timestamp-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-instant@1': { + readonly output: 'pg/timestamp-instant@1'; + readonly nullable: true; + }; readonly 'pg/timestamp-string@1': { readonly output: 'pg/timestamp-string@1'; readonly nullable: true; @@ -151,6 +167,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; @@ -160,6 +180,14 @@ export type AggregateTypes = { readonly output: 'pg/time-temporal@1'; readonly nullable: true; }; + readonly 'pg/timestamp-date@1': { + readonly output: 'pg/timestamp-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-instant@1': { + readonly output: 'pg/timestamp-instant@1'; + readonly nullable: true; + }; readonly 'pg/timestamp-string@1': { readonly output: 'pg/timestamp-string@1'; readonly nullable: true; @@ -203,6 +231,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; diff --git a/test/integration/test/ports/prisma/functional/relation-mode-gh-1-to-1/_fixture/noaction-map/generated/contract.d.ts b/test/integration/test/ports/prisma/functional/relation-mode-gh-1-to-1/_fixture/noaction-map/generated/contract.d.ts index 75e7514706de..078cf0fc57b3 100644 --- a/test/integration/test/ports/prisma/functional/relation-mode-gh-1-to-1/_fixture/noaction-map/generated/contract.d.ts +++ b/test/integration/test/ports/prisma/functional/relation-mode-gh-1-to-1/_fixture/noaction-map/generated/contract.d.ts @@ -42,6 +42,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; @@ -91,6 +95,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; @@ -100,6 +108,14 @@ export type AggregateTypes = { readonly output: 'pg/time-temporal@1'; readonly nullable: true; }; + readonly 'pg/timestamp-date@1': { + readonly output: 'pg/timestamp-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-instant@1': { + readonly output: 'pg/timestamp-instant@1'; + readonly nullable: true; + }; readonly 'pg/timestamp-string@1': { readonly output: 'pg/timestamp-string@1'; readonly nullable: true; @@ -151,6 +167,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; @@ -160,6 +180,14 @@ export type AggregateTypes = { readonly output: 'pg/time-temporal@1'; readonly nullable: true; }; + readonly 'pg/timestamp-date@1': { + readonly output: 'pg/timestamp-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-instant@1': { + readonly output: 'pg/timestamp-instant@1'; + readonly nullable: true; + }; readonly 'pg/timestamp-string@1': { readonly output: 'pg/timestamp-string@1'; readonly nullable: true; @@ -203,6 +231,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; diff --git a/test/integration/test/ports/prisma/functional/relation-mode-gh-1-to-1/_fixture/noaction-plain/generated/contract.d.ts b/test/integration/test/ports/prisma/functional/relation-mode-gh-1-to-1/_fixture/noaction-plain/generated/contract.d.ts index cbb89d73881b..65c54d1a5e47 100644 --- a/test/integration/test/ports/prisma/functional/relation-mode-gh-1-to-1/_fixture/noaction-plain/generated/contract.d.ts +++ b/test/integration/test/ports/prisma/functional/relation-mode-gh-1-to-1/_fixture/noaction-plain/generated/contract.d.ts @@ -42,6 +42,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; @@ -91,6 +95,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; @@ -100,6 +108,14 @@ export type AggregateTypes = { readonly output: 'pg/time-temporal@1'; readonly nullable: true; }; + readonly 'pg/timestamp-date@1': { + readonly output: 'pg/timestamp-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-instant@1': { + readonly output: 'pg/timestamp-instant@1'; + readonly nullable: true; + }; readonly 'pg/timestamp-string@1': { readonly output: 'pg/timestamp-string@1'; readonly nullable: true; @@ -151,6 +167,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; @@ -160,6 +180,14 @@ export type AggregateTypes = { readonly output: 'pg/time-temporal@1'; readonly nullable: true; }; + readonly 'pg/timestamp-date@1': { + readonly output: 'pg/timestamp-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-instant@1': { + readonly output: 'pg/timestamp-instant@1'; + readonly nullable: true; + }; readonly 'pg/timestamp-string@1': { readonly output: 'pg/timestamp-string@1'; readonly nullable: true; @@ -203,6 +231,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; diff --git a/test/integration/test/ports/prisma/functional/relation-mode-gh-1-to-1/_fixture/restrict-map/generated/contract.d.ts b/test/integration/test/ports/prisma/functional/relation-mode-gh-1-to-1/_fixture/restrict-map/generated/contract.d.ts index ed8629506426..b2faee52f5c5 100644 --- a/test/integration/test/ports/prisma/functional/relation-mode-gh-1-to-1/_fixture/restrict-map/generated/contract.d.ts +++ b/test/integration/test/ports/prisma/functional/relation-mode-gh-1-to-1/_fixture/restrict-map/generated/contract.d.ts @@ -42,6 +42,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; @@ -91,6 +95,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; @@ -100,6 +108,14 @@ export type AggregateTypes = { readonly output: 'pg/time-temporal@1'; readonly nullable: true; }; + readonly 'pg/timestamp-date@1': { + readonly output: 'pg/timestamp-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-instant@1': { + readonly output: 'pg/timestamp-instant@1'; + readonly nullable: true; + }; readonly 'pg/timestamp-string@1': { readonly output: 'pg/timestamp-string@1'; readonly nullable: true; @@ -151,6 +167,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; @@ -160,6 +180,14 @@ export type AggregateTypes = { readonly output: 'pg/time-temporal@1'; readonly nullable: true; }; + readonly 'pg/timestamp-date@1': { + readonly output: 'pg/timestamp-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-instant@1': { + readonly output: 'pg/timestamp-instant@1'; + readonly nullable: true; + }; readonly 'pg/timestamp-string@1': { readonly output: 'pg/timestamp-string@1'; readonly nullable: true; @@ -203,6 +231,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; diff --git a/test/integration/test/ports/prisma/functional/relation-mode-gh-1-to-1/_fixture/restrict-plain/generated/contract.d.ts b/test/integration/test/ports/prisma/functional/relation-mode-gh-1-to-1/_fixture/restrict-plain/generated/contract.d.ts index 5c964cdeb42d..ace4d9dea95e 100644 --- a/test/integration/test/ports/prisma/functional/relation-mode-gh-1-to-1/_fixture/restrict-plain/generated/contract.d.ts +++ b/test/integration/test/ports/prisma/functional/relation-mode-gh-1-to-1/_fixture/restrict-plain/generated/contract.d.ts @@ -42,6 +42,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; @@ -91,6 +95,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; @@ -100,6 +108,14 @@ export type AggregateTypes = { readonly output: 'pg/time-temporal@1'; readonly nullable: true; }; + readonly 'pg/timestamp-date@1': { + readonly output: 'pg/timestamp-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-instant@1': { + readonly output: 'pg/timestamp-instant@1'; + readonly nullable: true; + }; readonly 'pg/timestamp-string@1': { readonly output: 'pg/timestamp-string@1'; readonly nullable: true; @@ -151,6 +167,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; @@ -160,6 +180,14 @@ export type AggregateTypes = { readonly output: 'pg/time-temporal@1'; readonly nullable: true; }; + readonly 'pg/timestamp-date@1': { + readonly output: 'pg/timestamp-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-instant@1': { + readonly output: 'pg/timestamp-instant@1'; + readonly nullable: true; + }; readonly 'pg/timestamp-string@1': { readonly output: 'pg/timestamp-string@1'; readonly nullable: true; @@ -203,6 +231,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; diff --git a/test/integration/test/ports/prisma/functional/relation-mode-gh-1-to-1/_fixture/setnull-map/generated/contract.d.ts b/test/integration/test/ports/prisma/functional/relation-mode-gh-1-to-1/_fixture/setnull-map/generated/contract.d.ts index 83c9487f03db..385624aeaa1e 100644 --- a/test/integration/test/ports/prisma/functional/relation-mode-gh-1-to-1/_fixture/setnull-map/generated/contract.d.ts +++ b/test/integration/test/ports/prisma/functional/relation-mode-gh-1-to-1/_fixture/setnull-map/generated/contract.d.ts @@ -42,6 +42,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; @@ -91,6 +95,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; @@ -100,6 +108,14 @@ export type AggregateTypes = { readonly output: 'pg/time-temporal@1'; readonly nullable: true; }; + readonly 'pg/timestamp-date@1': { + readonly output: 'pg/timestamp-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-instant@1': { + readonly output: 'pg/timestamp-instant@1'; + readonly nullable: true; + }; readonly 'pg/timestamp-string@1': { readonly output: 'pg/timestamp-string@1'; readonly nullable: true; @@ -151,6 +167,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; @@ -160,6 +180,14 @@ export type AggregateTypes = { readonly output: 'pg/time-temporal@1'; readonly nullable: true; }; + readonly 'pg/timestamp-date@1': { + readonly output: 'pg/timestamp-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-instant@1': { + readonly output: 'pg/timestamp-instant@1'; + readonly nullable: true; + }; readonly 'pg/timestamp-string@1': { readonly output: 'pg/timestamp-string@1'; readonly nullable: true; @@ -203,6 +231,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; diff --git a/test/integration/test/ports/prisma/functional/relation-mode-gh-1-to-1/_fixture/setnull-plain/generated/contract.d.ts b/test/integration/test/ports/prisma/functional/relation-mode-gh-1-to-1/_fixture/setnull-plain/generated/contract.d.ts index 6d6ceb4b0560..e76f025db203 100644 --- a/test/integration/test/ports/prisma/functional/relation-mode-gh-1-to-1/_fixture/setnull-plain/generated/contract.d.ts +++ b/test/integration/test/ports/prisma/functional/relation-mode-gh-1-to-1/_fixture/setnull-plain/generated/contract.d.ts @@ -42,6 +42,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; @@ -91,6 +95,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; @@ -100,6 +108,14 @@ export type AggregateTypes = { readonly output: 'pg/time-temporal@1'; readonly nullable: true; }; + readonly 'pg/timestamp-date@1': { + readonly output: 'pg/timestamp-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-instant@1': { + readonly output: 'pg/timestamp-instant@1'; + readonly nullable: true; + }; readonly 'pg/timestamp-string@1': { readonly output: 'pg/timestamp-string@1'; readonly nullable: true; @@ -151,6 +167,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; @@ -160,6 +180,14 @@ export type AggregateTypes = { readonly output: 'pg/time-temporal@1'; readonly nullable: true; }; + readonly 'pg/timestamp-date@1': { + readonly output: 'pg/timestamp-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-instant@1': { + readonly output: 'pg/timestamp-instant@1'; + readonly nullable: true; + }; readonly 'pg/timestamp-string@1': { readonly output: 'pg/timestamp-string@1'; readonly nullable: true; @@ -203,6 +231,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; diff --git a/test/integration/test/ports/prisma/functional/relation-mode-gh-1-to-n/_fixture/cascade-map/generated/contract.d.ts b/test/integration/test/ports/prisma/functional/relation-mode-gh-1-to-n/_fixture/cascade-map/generated/contract.d.ts index 61bcead75c74..9ab36dcedf89 100644 --- a/test/integration/test/ports/prisma/functional/relation-mode-gh-1-to-n/_fixture/cascade-map/generated/contract.d.ts +++ b/test/integration/test/ports/prisma/functional/relation-mode-gh-1-to-n/_fixture/cascade-map/generated/contract.d.ts @@ -42,6 +42,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; @@ -91,6 +95,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; @@ -100,6 +108,14 @@ export type AggregateTypes = { readonly output: 'pg/time-temporal@1'; readonly nullable: true; }; + readonly 'pg/timestamp-date@1': { + readonly output: 'pg/timestamp-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-instant@1': { + readonly output: 'pg/timestamp-instant@1'; + readonly nullable: true; + }; readonly 'pg/timestamp-string@1': { readonly output: 'pg/timestamp-string@1'; readonly nullable: true; @@ -151,6 +167,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; @@ -160,6 +180,14 @@ export type AggregateTypes = { readonly output: 'pg/time-temporal@1'; readonly nullable: true; }; + readonly 'pg/timestamp-date@1': { + readonly output: 'pg/timestamp-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-instant@1': { + readonly output: 'pg/timestamp-instant@1'; + readonly nullable: true; + }; readonly 'pg/timestamp-string@1': { readonly output: 'pg/timestamp-string@1'; readonly nullable: true; @@ -203,6 +231,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; diff --git a/test/integration/test/ports/prisma/functional/relation-mode-gh-1-to-n/_fixture/cascade-plain/generated/contract.d.ts b/test/integration/test/ports/prisma/functional/relation-mode-gh-1-to-n/_fixture/cascade-plain/generated/contract.d.ts index 559c884fa183..3eb731c6caaf 100644 --- a/test/integration/test/ports/prisma/functional/relation-mode-gh-1-to-n/_fixture/cascade-plain/generated/contract.d.ts +++ b/test/integration/test/ports/prisma/functional/relation-mode-gh-1-to-n/_fixture/cascade-plain/generated/contract.d.ts @@ -42,6 +42,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; @@ -91,6 +95,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; @@ -100,6 +108,14 @@ export type AggregateTypes = { readonly output: 'pg/time-temporal@1'; readonly nullable: true; }; + readonly 'pg/timestamp-date@1': { + readonly output: 'pg/timestamp-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-instant@1': { + readonly output: 'pg/timestamp-instant@1'; + readonly nullable: true; + }; readonly 'pg/timestamp-string@1': { readonly output: 'pg/timestamp-string@1'; readonly nullable: true; @@ -151,6 +167,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; @@ -160,6 +180,14 @@ export type AggregateTypes = { readonly output: 'pg/time-temporal@1'; readonly nullable: true; }; + readonly 'pg/timestamp-date@1': { + readonly output: 'pg/timestamp-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-instant@1': { + readonly output: 'pg/timestamp-instant@1'; + readonly nullable: true; + }; readonly 'pg/timestamp-string@1': { readonly output: 'pg/timestamp-string@1'; readonly nullable: true; @@ -203,6 +231,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; diff --git a/test/integration/test/ports/prisma/functional/relation-mode-gh-1-to-n/_fixture/noaction-map/generated/contract.d.ts b/test/integration/test/ports/prisma/functional/relation-mode-gh-1-to-n/_fixture/noaction-map/generated/contract.d.ts index c76fe9a7e6f7..4956bcf0ba41 100644 --- a/test/integration/test/ports/prisma/functional/relation-mode-gh-1-to-n/_fixture/noaction-map/generated/contract.d.ts +++ b/test/integration/test/ports/prisma/functional/relation-mode-gh-1-to-n/_fixture/noaction-map/generated/contract.d.ts @@ -42,6 +42,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; @@ -91,6 +95,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; @@ -100,6 +108,14 @@ export type AggregateTypes = { readonly output: 'pg/time-temporal@1'; readonly nullable: true; }; + readonly 'pg/timestamp-date@1': { + readonly output: 'pg/timestamp-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-instant@1': { + readonly output: 'pg/timestamp-instant@1'; + readonly nullable: true; + }; readonly 'pg/timestamp-string@1': { readonly output: 'pg/timestamp-string@1'; readonly nullable: true; @@ -151,6 +167,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; @@ -160,6 +180,14 @@ export type AggregateTypes = { readonly output: 'pg/time-temporal@1'; readonly nullable: true; }; + readonly 'pg/timestamp-date@1': { + readonly output: 'pg/timestamp-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-instant@1': { + readonly output: 'pg/timestamp-instant@1'; + readonly nullable: true; + }; readonly 'pg/timestamp-string@1': { readonly output: 'pg/timestamp-string@1'; readonly nullable: true; @@ -203,6 +231,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; diff --git a/test/integration/test/ports/prisma/functional/relation-mode-gh-1-to-n/_fixture/noaction-plain/generated/contract.d.ts b/test/integration/test/ports/prisma/functional/relation-mode-gh-1-to-n/_fixture/noaction-plain/generated/contract.d.ts index 522ec12a381a..9184e173d9b7 100644 --- a/test/integration/test/ports/prisma/functional/relation-mode-gh-1-to-n/_fixture/noaction-plain/generated/contract.d.ts +++ b/test/integration/test/ports/prisma/functional/relation-mode-gh-1-to-n/_fixture/noaction-plain/generated/contract.d.ts @@ -42,6 +42,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; @@ -91,6 +95,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; @@ -100,6 +108,14 @@ export type AggregateTypes = { readonly output: 'pg/time-temporal@1'; readonly nullable: true; }; + readonly 'pg/timestamp-date@1': { + readonly output: 'pg/timestamp-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-instant@1': { + readonly output: 'pg/timestamp-instant@1'; + readonly nullable: true; + }; readonly 'pg/timestamp-string@1': { readonly output: 'pg/timestamp-string@1'; readonly nullable: true; @@ -151,6 +167,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; @@ -160,6 +180,14 @@ export type AggregateTypes = { readonly output: 'pg/time-temporal@1'; readonly nullable: true; }; + readonly 'pg/timestamp-date@1': { + readonly output: 'pg/timestamp-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-instant@1': { + readonly output: 'pg/timestamp-instant@1'; + readonly nullable: true; + }; readonly 'pg/timestamp-string@1': { readonly output: 'pg/timestamp-string@1'; readonly nullable: true; @@ -203,6 +231,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; diff --git a/test/integration/test/ports/prisma/functional/relation-mode-gh-1-to-n/_fixture/restrict-map/generated/contract.d.ts b/test/integration/test/ports/prisma/functional/relation-mode-gh-1-to-n/_fixture/restrict-map/generated/contract.d.ts index 51cf3ca209c9..2816cc23aad5 100644 --- a/test/integration/test/ports/prisma/functional/relation-mode-gh-1-to-n/_fixture/restrict-map/generated/contract.d.ts +++ b/test/integration/test/ports/prisma/functional/relation-mode-gh-1-to-n/_fixture/restrict-map/generated/contract.d.ts @@ -42,6 +42,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; @@ -91,6 +95,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; @@ -100,6 +108,14 @@ export type AggregateTypes = { readonly output: 'pg/time-temporal@1'; readonly nullable: true; }; + readonly 'pg/timestamp-date@1': { + readonly output: 'pg/timestamp-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-instant@1': { + readonly output: 'pg/timestamp-instant@1'; + readonly nullable: true; + }; readonly 'pg/timestamp-string@1': { readonly output: 'pg/timestamp-string@1'; readonly nullable: true; @@ -151,6 +167,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; @@ -160,6 +180,14 @@ export type AggregateTypes = { readonly output: 'pg/time-temporal@1'; readonly nullable: true; }; + readonly 'pg/timestamp-date@1': { + readonly output: 'pg/timestamp-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-instant@1': { + readonly output: 'pg/timestamp-instant@1'; + readonly nullable: true; + }; readonly 'pg/timestamp-string@1': { readonly output: 'pg/timestamp-string@1'; readonly nullable: true; @@ -203,6 +231,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; diff --git a/test/integration/test/ports/prisma/functional/relation-mode-gh-1-to-n/_fixture/restrict-plain/generated/contract.d.ts b/test/integration/test/ports/prisma/functional/relation-mode-gh-1-to-n/_fixture/restrict-plain/generated/contract.d.ts index ffd708b3abd3..961b9213d4b1 100644 --- a/test/integration/test/ports/prisma/functional/relation-mode-gh-1-to-n/_fixture/restrict-plain/generated/contract.d.ts +++ b/test/integration/test/ports/prisma/functional/relation-mode-gh-1-to-n/_fixture/restrict-plain/generated/contract.d.ts @@ -42,6 +42,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; @@ -91,6 +95,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; @@ -100,6 +108,14 @@ export type AggregateTypes = { readonly output: 'pg/time-temporal@1'; readonly nullable: true; }; + readonly 'pg/timestamp-date@1': { + readonly output: 'pg/timestamp-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-instant@1': { + readonly output: 'pg/timestamp-instant@1'; + readonly nullable: true; + }; readonly 'pg/timestamp-string@1': { readonly output: 'pg/timestamp-string@1'; readonly nullable: true; @@ -151,6 +167,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; @@ -160,6 +180,14 @@ export type AggregateTypes = { readonly output: 'pg/time-temporal@1'; readonly nullable: true; }; + readonly 'pg/timestamp-date@1': { + readonly output: 'pg/timestamp-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-instant@1': { + readonly output: 'pg/timestamp-instant@1'; + readonly nullable: true; + }; readonly 'pg/timestamp-string@1': { readonly output: 'pg/timestamp-string@1'; readonly nullable: true; @@ -203,6 +231,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; diff --git a/test/integration/test/ports/prisma/functional/relation-mode-gh-1-to-n/_fixture/setnull-map/generated/contract.d.ts b/test/integration/test/ports/prisma/functional/relation-mode-gh-1-to-n/_fixture/setnull-map/generated/contract.d.ts index f38479f32b90..aea97820b877 100644 --- a/test/integration/test/ports/prisma/functional/relation-mode-gh-1-to-n/_fixture/setnull-map/generated/contract.d.ts +++ b/test/integration/test/ports/prisma/functional/relation-mode-gh-1-to-n/_fixture/setnull-map/generated/contract.d.ts @@ -42,6 +42,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; @@ -91,6 +95,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; @@ -100,6 +108,14 @@ export type AggregateTypes = { readonly output: 'pg/time-temporal@1'; readonly nullable: true; }; + readonly 'pg/timestamp-date@1': { + readonly output: 'pg/timestamp-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-instant@1': { + readonly output: 'pg/timestamp-instant@1'; + readonly nullable: true; + }; readonly 'pg/timestamp-string@1': { readonly output: 'pg/timestamp-string@1'; readonly nullable: true; @@ -151,6 +167,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; @@ -160,6 +180,14 @@ export type AggregateTypes = { readonly output: 'pg/time-temporal@1'; readonly nullable: true; }; + readonly 'pg/timestamp-date@1': { + readonly output: 'pg/timestamp-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-instant@1': { + readonly output: 'pg/timestamp-instant@1'; + readonly nullable: true; + }; readonly 'pg/timestamp-string@1': { readonly output: 'pg/timestamp-string@1'; readonly nullable: true; @@ -203,6 +231,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; diff --git a/test/integration/test/ports/prisma/functional/relation-mode-gh-1-to-n/_fixture/setnull-plain/generated/contract.d.ts b/test/integration/test/ports/prisma/functional/relation-mode-gh-1-to-n/_fixture/setnull-plain/generated/contract.d.ts index 9ee3083bc309..df9d8fca45a4 100644 --- a/test/integration/test/ports/prisma/functional/relation-mode-gh-1-to-n/_fixture/setnull-plain/generated/contract.d.ts +++ b/test/integration/test/ports/prisma/functional/relation-mode-gh-1-to-n/_fixture/setnull-plain/generated/contract.d.ts @@ -42,6 +42,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; @@ -91,6 +95,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; @@ -100,6 +108,14 @@ export type AggregateTypes = { readonly output: 'pg/time-temporal@1'; readonly nullable: true; }; + readonly 'pg/timestamp-date@1': { + readonly output: 'pg/timestamp-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-instant@1': { + readonly output: 'pg/timestamp-instant@1'; + readonly nullable: true; + }; readonly 'pg/timestamp-string@1': { readonly output: 'pg/timestamp-string@1'; readonly nullable: true; @@ -151,6 +167,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; @@ -160,6 +180,14 @@ export type AggregateTypes = { readonly output: 'pg/time-temporal@1'; readonly nullable: true; }; + readonly 'pg/timestamp-date@1': { + readonly output: 'pg/timestamp-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-instant@1': { + readonly output: 'pg/timestamp-instant@1'; + readonly nullable: true; + }; readonly 'pg/timestamp-string@1': { readonly output: 'pg/timestamp-string@1'; readonly nullable: true; @@ -203,6 +231,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; diff --git a/test/integration/test/ports/prisma/functional/relation-mode-gh-m-to-n/_fixture/cascade-nomap/generated/contract.d.ts b/test/integration/test/ports/prisma/functional/relation-mode-gh-m-to-n/_fixture/cascade-nomap/generated/contract.d.ts index 08f1413bc117..02ec8d7fdc5e 100644 --- a/test/integration/test/ports/prisma/functional/relation-mode-gh-m-to-n/_fixture/cascade-nomap/generated/contract.d.ts +++ b/test/integration/test/ports/prisma/functional/relation-mode-gh-m-to-n/_fixture/cascade-nomap/generated/contract.d.ts @@ -42,6 +42,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; @@ -91,6 +95,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; @@ -100,6 +108,14 @@ export type AggregateTypes = { readonly output: 'pg/time-temporal@1'; readonly nullable: true; }; + readonly 'pg/timestamp-date@1': { + readonly output: 'pg/timestamp-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-instant@1': { + readonly output: 'pg/timestamp-instant@1'; + readonly nullable: true; + }; readonly 'pg/timestamp-string@1': { readonly output: 'pg/timestamp-string@1'; readonly nullable: true; @@ -151,6 +167,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; @@ -160,6 +180,14 @@ export type AggregateTypes = { readonly output: 'pg/time-temporal@1'; readonly nullable: true; }; + readonly 'pg/timestamp-date@1': { + readonly output: 'pg/timestamp-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-instant@1': { + readonly output: 'pg/timestamp-instant@1'; + readonly nullable: true; + }; readonly 'pg/timestamp-string@1': { readonly output: 'pg/timestamp-string@1'; readonly nullable: true; @@ -203,6 +231,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; diff --git a/test/integration/test/ports/prisma/functional/relation-mode-gh-m-to-n/_fixture/default-nomap/generated/contract.d.ts b/test/integration/test/ports/prisma/functional/relation-mode-gh-m-to-n/_fixture/default-nomap/generated/contract.d.ts index 0fb7ff132e86..a7d95996d526 100644 --- a/test/integration/test/ports/prisma/functional/relation-mode-gh-m-to-n/_fixture/default-nomap/generated/contract.d.ts +++ b/test/integration/test/ports/prisma/functional/relation-mode-gh-m-to-n/_fixture/default-nomap/generated/contract.d.ts @@ -42,6 +42,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; @@ -91,6 +95,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; @@ -100,6 +108,14 @@ export type AggregateTypes = { readonly output: 'pg/time-temporal@1'; readonly nullable: true; }; + readonly 'pg/timestamp-date@1': { + readonly output: 'pg/timestamp-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-instant@1': { + readonly output: 'pg/timestamp-instant@1'; + readonly nullable: true; + }; readonly 'pg/timestamp-string@1': { readonly output: 'pg/timestamp-string@1'; readonly nullable: true; @@ -151,6 +167,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; @@ -160,6 +180,14 @@ export type AggregateTypes = { readonly output: 'pg/time-temporal@1'; readonly nullable: true; }; + readonly 'pg/timestamp-date@1': { + readonly output: 'pg/timestamp-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-instant@1': { + readonly output: 'pg/timestamp-instant@1'; + readonly nullable: true; + }; readonly 'pg/timestamp-string@1': { readonly output: 'pg/timestamp-string@1'; readonly nullable: true; @@ -203,6 +231,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; diff --git a/test/integration/test/ports/prisma/functional/relation-mode-gh-m-to-n/_fixture/noaction-nomap/generated/contract.d.ts b/test/integration/test/ports/prisma/functional/relation-mode-gh-m-to-n/_fixture/noaction-nomap/generated/contract.d.ts index 0fb7ff132e86..a7d95996d526 100644 --- a/test/integration/test/ports/prisma/functional/relation-mode-gh-m-to-n/_fixture/noaction-nomap/generated/contract.d.ts +++ b/test/integration/test/ports/prisma/functional/relation-mode-gh-m-to-n/_fixture/noaction-nomap/generated/contract.d.ts @@ -42,6 +42,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; @@ -91,6 +95,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; @@ -100,6 +108,14 @@ export type AggregateTypes = { readonly output: 'pg/time-temporal@1'; readonly nullable: true; }; + readonly 'pg/timestamp-date@1': { + readonly output: 'pg/timestamp-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-instant@1': { + readonly output: 'pg/timestamp-instant@1'; + readonly nullable: true; + }; readonly 'pg/timestamp-string@1': { readonly output: 'pg/timestamp-string@1'; readonly nullable: true; @@ -151,6 +167,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; @@ -160,6 +180,14 @@ export type AggregateTypes = { readonly output: 'pg/time-temporal@1'; readonly nullable: true; }; + readonly 'pg/timestamp-date@1': { + readonly output: 'pg/timestamp-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-instant@1': { + readonly output: 'pg/timestamp-instant@1'; + readonly nullable: true; + }; readonly 'pg/timestamp-string@1': { readonly output: 'pg/timestamp-string@1'; readonly nullable: true; @@ -203,6 +231,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; diff --git a/test/integration/test/ports/prisma/functional/relation-mode-gh-m-to-n/_fixture/restrict-nomap/generated/contract.d.ts b/test/integration/test/ports/prisma/functional/relation-mode-gh-m-to-n/_fixture/restrict-nomap/generated/contract.d.ts index c94b4d2e9421..dad3483bacc2 100644 --- a/test/integration/test/ports/prisma/functional/relation-mode-gh-m-to-n/_fixture/restrict-nomap/generated/contract.d.ts +++ b/test/integration/test/ports/prisma/functional/relation-mode-gh-m-to-n/_fixture/restrict-nomap/generated/contract.d.ts @@ -42,6 +42,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; @@ -91,6 +95,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; @@ -100,6 +108,14 @@ export type AggregateTypes = { readonly output: 'pg/time-temporal@1'; readonly nullable: true; }; + readonly 'pg/timestamp-date@1': { + readonly output: 'pg/timestamp-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-instant@1': { + readonly output: 'pg/timestamp-instant@1'; + readonly nullable: true; + }; readonly 'pg/timestamp-string@1': { readonly output: 'pg/timestamp-string@1'; readonly nullable: true; @@ -151,6 +167,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; @@ -160,6 +180,14 @@ export type AggregateTypes = { readonly output: 'pg/time-temporal@1'; readonly nullable: true; }; + readonly 'pg/timestamp-date@1': { + readonly output: 'pg/timestamp-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-instant@1': { + readonly output: 'pg/timestamp-instant@1'; + readonly nullable: true; + }; readonly 'pg/timestamp-string@1': { readonly output: 'pg/timestamp-string@1'; readonly nullable: true; @@ -203,6 +231,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; diff --git a/test/integration/test/sql-builder/fixtures/generated-no-pgvector/contract.d.ts b/test/integration/test/sql-builder/fixtures/generated-no-pgvector/contract.d.ts index 80b3d3eda96a..7a2058a0a1fe 100644 --- a/test/integration/test/sql-builder/fixtures/generated-no-pgvector/contract.d.ts +++ b/test/integration/test/sql-builder/fixtures/generated-no-pgvector/contract.d.ts @@ -43,6 +43,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; @@ -92,6 +96,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; @@ -101,6 +109,14 @@ export type AggregateTypes = { readonly output: 'pg/time-temporal@1'; readonly nullable: true; }; + readonly 'pg/timestamp-date@1': { + readonly output: 'pg/timestamp-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-instant@1': { + readonly output: 'pg/timestamp-instant@1'; + readonly nullable: true; + }; readonly 'pg/timestamp-string@1': { readonly output: 'pg/timestamp-string@1'; readonly nullable: true; @@ -152,6 +168,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; @@ -161,6 +181,14 @@ export type AggregateTypes = { readonly output: 'pg/time-temporal@1'; readonly nullable: true; }; + readonly 'pg/timestamp-date@1': { + readonly output: 'pg/timestamp-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-instant@1': { + readonly output: 'pg/timestamp-instant@1'; + readonly nullable: true; + }; readonly 'pg/timestamp-string@1': { readonly output: 'pg/timestamp-string@1'; readonly nullable: true; @@ -204,6 +232,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; diff --git a/test/integration/test/sql-builder/fixtures/generated/contract.d.ts b/test/integration/test/sql-builder/fixtures/generated/contract.d.ts index 937ed7662d5c..b7a660e27d7a 100644 --- a/test/integration/test/sql-builder/fixtures/generated/contract.d.ts +++ b/test/integration/test/sql-builder/fixtures/generated/contract.d.ts @@ -49,6 +49,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; @@ -98,6 +102,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; @@ -107,6 +115,14 @@ export type AggregateTypes = { readonly output: 'pg/time-temporal@1'; readonly nullable: true; }; + readonly 'pg/timestamp-date@1': { + readonly output: 'pg/timestamp-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-instant@1': { + readonly output: 'pg/timestamp-instant@1'; + readonly nullable: true; + }; readonly 'pg/timestamp-string@1': { readonly output: 'pg/timestamp-string@1'; readonly nullable: true; @@ -158,6 +174,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; @@ -167,6 +187,14 @@ export type AggregateTypes = { readonly output: 'pg/time-temporal@1'; readonly nullable: true; }; + readonly 'pg/timestamp-date@1': { + readonly output: 'pg/timestamp-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-instant@1': { + readonly output: 'pg/timestamp-instant@1'; + readonly nullable: true; + }; readonly 'pg/timestamp-string@1': { readonly output: 'pg/timestamp-string@1'; readonly nullable: true; @@ -210,6 +238,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; diff --git a/test/integration/test/sql-orm-client/fixtures/collection-methods/generated/contract.d.ts b/test/integration/test/sql-orm-client/fixtures/collection-methods/generated/contract.d.ts index 98347ff79255..3a9d55796e0e 100644 --- a/test/integration/test/sql-orm-client/fixtures/collection-methods/generated/contract.d.ts +++ b/test/integration/test/sql-orm-client/fixtures/collection-methods/generated/contract.d.ts @@ -48,6 +48,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; @@ -97,6 +101,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; @@ -106,6 +114,14 @@ export type AggregateTypes = { readonly output: 'pg/time-temporal@1'; readonly nullable: true; }; + readonly 'pg/timestamp-date@1': { + readonly output: 'pg/timestamp-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-instant@1': { + readonly output: 'pg/timestamp-instant@1'; + readonly nullable: true; + }; readonly 'pg/timestamp-string@1': { readonly output: 'pg/timestamp-string@1'; readonly nullable: true; @@ -157,6 +173,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; @@ -166,6 +186,14 @@ export type AggregateTypes = { readonly output: 'pg/time-temporal@1'; readonly nullable: true; }; + readonly 'pg/timestamp-date@1': { + readonly output: 'pg/timestamp-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-instant@1': { + readonly output: 'pg/timestamp-instant@1'; + readonly nullable: true; + }; readonly 'pg/timestamp-string@1': { readonly output: 'pg/timestamp-string@1'; readonly nullable: true; @@ -209,6 +237,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; diff --git a/test/integration/test/sql-orm-client/fixtures/execution-defaulted-tags/generated/contract.d.ts b/test/integration/test/sql-orm-client/fixtures/execution-defaulted-tags/generated/contract.d.ts index 97e1d6846948..daee95a0333f 100644 --- a/test/integration/test/sql-orm-client/fixtures/execution-defaulted-tags/generated/contract.d.ts +++ b/test/integration/test/sql-orm-client/fixtures/execution-defaulted-tags/generated/contract.d.ts @@ -43,6 +43,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; @@ -92,6 +96,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; @@ -101,6 +109,14 @@ export type AggregateTypes = { readonly output: 'pg/time-temporal@1'; readonly nullable: true; }; + readonly 'pg/timestamp-date@1': { + readonly output: 'pg/timestamp-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-instant@1': { + readonly output: 'pg/timestamp-instant@1'; + readonly nullable: true; + }; readonly 'pg/timestamp-string@1': { readonly output: 'pg/timestamp-string@1'; readonly nullable: true; @@ -152,6 +168,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; @@ -161,6 +181,14 @@ export type AggregateTypes = { readonly output: 'pg/time-temporal@1'; readonly nullable: true; }; + readonly 'pg/timestamp-date@1': { + readonly output: 'pg/timestamp-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-instant@1': { + readonly output: 'pg/timestamp-instant@1'; + readonly nullable: true; + }; readonly 'pg/timestamp-string@1': { readonly output: 'pg/timestamp-string@1'; readonly nullable: true; @@ -204,6 +232,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; diff --git a/test/integration/test/sql-orm-client/fixtures/fragment-namespace/generated/contract.d.ts b/test/integration/test/sql-orm-client/fixtures/fragment-namespace/generated/contract.d.ts index 522738f53abe..4a0bc8f4834c 100644 --- a/test/integration/test/sql-orm-client/fixtures/fragment-namespace/generated/contract.d.ts +++ b/test/integration/test/sql-orm-client/fixtures/fragment-namespace/generated/contract.d.ts @@ -42,6 +42,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; @@ -91,6 +95,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; @@ -100,6 +108,14 @@ export type AggregateTypes = { readonly output: 'pg/time-temporal@1'; readonly nullable: true; }; + readonly 'pg/timestamp-date@1': { + readonly output: 'pg/timestamp-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-instant@1': { + readonly output: 'pg/timestamp-instant@1'; + readonly nullable: true; + }; readonly 'pg/timestamp-string@1': { readonly output: 'pg/timestamp-string@1'; readonly nullable: true; @@ -151,6 +167,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; @@ -160,6 +180,14 @@ export type AggregateTypes = { readonly output: 'pg/time-temporal@1'; readonly nullable: true; }; + readonly 'pg/timestamp-date@1': { + readonly output: 'pg/timestamp-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-instant@1': { + readonly output: 'pg/timestamp-instant@1'; + readonly nullable: true; + }; readonly 'pg/timestamp-string@1': { readonly output: 'pg/timestamp-string@1'; readonly nullable: true; @@ -203,6 +231,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; diff --git a/test/integration/test/sql-orm-client/fixtures/generated/contract.d.ts b/test/integration/test/sql-orm-client/fixtures/generated/contract.d.ts index e4e7b0d65f35..1f2a81a3b2f8 100644 --- a/test/integration/test/sql-orm-client/fixtures/generated/contract.d.ts +++ b/test/integration/test/sql-orm-client/fixtures/generated/contract.d.ts @@ -50,6 +50,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; @@ -99,6 +103,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; @@ -108,6 +116,14 @@ export type AggregateTypes = { readonly output: 'pg/time-temporal@1'; readonly nullable: true; }; + readonly 'pg/timestamp-date@1': { + readonly output: 'pg/timestamp-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-instant@1': { + readonly output: 'pg/timestamp-instant@1'; + readonly nullable: true; + }; readonly 'pg/timestamp-string@1': { readonly output: 'pg/timestamp-string@1'; readonly nullable: true; @@ -159,6 +175,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; @@ -168,6 +188,14 @@ export type AggregateTypes = { readonly output: 'pg/time-temporal@1'; readonly nullable: true; }; + readonly 'pg/timestamp-date@1': { + readonly output: 'pg/timestamp-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-instant@1': { + readonly output: 'pg/timestamp-instant@1'; + readonly nullable: true; + }; readonly 'pg/timestamp-string@1': { readonly output: 'pg/timestamp-string@1'; readonly nullable: true; @@ -211,6 +239,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; diff --git a/test/integration/test/sql-orm-client/fixtures/integer-representation/generated/contract.d.ts b/test/integration/test/sql-orm-client/fixtures/integer-representation/generated/contract.d.ts index 9002874effb4..c29c79d99878 100644 --- a/test/integration/test/sql-orm-client/fixtures/integer-representation/generated/contract.d.ts +++ b/test/integration/test/sql-orm-client/fixtures/integer-representation/generated/contract.d.ts @@ -42,6 +42,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; @@ -91,6 +95,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; @@ -100,6 +108,14 @@ export type AggregateTypes = { readonly output: 'pg/time-temporal@1'; readonly nullable: true; }; + readonly 'pg/timestamp-date@1': { + readonly output: 'pg/timestamp-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-instant@1': { + readonly output: 'pg/timestamp-instant@1'; + readonly nullable: true; + }; readonly 'pg/timestamp-string@1': { readonly output: 'pg/timestamp-string@1'; readonly nullable: true; @@ -151,6 +167,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; @@ -160,6 +180,14 @@ export type AggregateTypes = { readonly output: 'pg/time-temporal@1'; readonly nullable: true; }; + readonly 'pg/timestamp-date@1': { + readonly output: 'pg/timestamp-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-instant@1': { + readonly output: 'pg/timestamp-instant@1'; + readonly nullable: true; + }; readonly 'pg/timestamp-string@1': { readonly output: 'pg/timestamp-string@1'; readonly nullable: true; @@ -203,6 +231,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; diff --git a/test/integration/test/sql-orm-client/fixtures/junction-namespaces/generated/contract.d.ts b/test/integration/test/sql-orm-client/fixtures/junction-namespaces/generated/contract.d.ts index 73af750f4244..cf321d22b5ac 100644 --- a/test/integration/test/sql-orm-client/fixtures/junction-namespaces/generated/contract.d.ts +++ b/test/integration/test/sql-orm-client/fixtures/junction-namespaces/generated/contract.d.ts @@ -43,6 +43,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; @@ -92,6 +96,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; @@ -101,6 +109,14 @@ export type AggregateTypes = { readonly output: 'pg/time-temporal@1'; readonly nullable: true; }; + readonly 'pg/timestamp-date@1': { + readonly output: 'pg/timestamp-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-instant@1': { + readonly output: 'pg/timestamp-instant@1'; + readonly nullable: true; + }; readonly 'pg/timestamp-string@1': { readonly output: 'pg/timestamp-string@1'; readonly nullable: true; @@ -152,6 +168,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; @@ -161,6 +181,14 @@ export type AggregateTypes = { readonly output: 'pg/time-temporal@1'; readonly nullable: true; }; + readonly 'pg/timestamp-date@1': { + readonly output: 'pg/timestamp-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-instant@1': { + readonly output: 'pg/timestamp-instant@1'; + readonly nullable: true; + }; readonly 'pg/timestamp-string@1': { readonly output: 'pg/timestamp-string@1'; readonly nullable: true; @@ -204,6 +232,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; diff --git a/test/integration/test/sql-orm-client/fixtures/mn-psl/generated/contract.d.ts b/test/integration/test/sql-orm-client/fixtures/mn-psl/generated/contract.d.ts index 93efa01d2b11..24c71ae44ad8 100644 --- a/test/integration/test/sql-orm-client/fixtures/mn-psl/generated/contract.d.ts +++ b/test/integration/test/sql-orm-client/fixtures/mn-psl/generated/contract.d.ts @@ -42,6 +42,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; @@ -91,6 +95,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; @@ -100,6 +108,14 @@ export type AggregateTypes = { readonly output: 'pg/time-temporal@1'; readonly nullable: true; }; + readonly 'pg/timestamp-date@1': { + readonly output: 'pg/timestamp-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-instant@1': { + readonly output: 'pg/timestamp-instant@1'; + readonly nullable: true; + }; readonly 'pg/timestamp-string@1': { readonly output: 'pg/timestamp-string@1'; readonly nullable: true; @@ -151,6 +167,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; @@ -160,6 +180,14 @@ export type AggregateTypes = { readonly output: 'pg/time-temporal@1'; readonly nullable: true; }; + readonly 'pg/timestamp-date@1': { + readonly output: 'pg/timestamp-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-instant@1': { + readonly output: 'pg/timestamp-instant@1'; + readonly nullable: true; + }; readonly 'pg/timestamp-string@1': { readonly output: 'pg/timestamp-string@1'; readonly nullable: true; @@ -203,6 +231,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; diff --git a/test/integration/test/sql-orm-client/fixtures/non-identifier-names/generated/contract.d.ts b/test/integration/test/sql-orm-client/fixtures/non-identifier-names/generated/contract.d.ts index 7918c3eda57a..ddc8a0f51c8b 100644 --- a/test/integration/test/sql-orm-client/fixtures/non-identifier-names/generated/contract.d.ts +++ b/test/integration/test/sql-orm-client/fixtures/non-identifier-names/generated/contract.d.ts @@ -42,6 +42,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; @@ -91,6 +95,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; @@ -100,6 +108,14 @@ export type AggregateTypes = { readonly output: 'pg/time-temporal@1'; readonly nullable: true; }; + readonly 'pg/timestamp-date@1': { + readonly output: 'pg/timestamp-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-instant@1': { + readonly output: 'pg/timestamp-instant@1'; + readonly nullable: true; + }; readonly 'pg/timestamp-string@1': { readonly output: 'pg/timestamp-string@1'; readonly nullable: true; @@ -151,6 +167,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; @@ -160,6 +180,14 @@ export type AggregateTypes = { readonly output: 'pg/time-temporal@1'; readonly nullable: true; }; + readonly 'pg/timestamp-date@1': { + readonly output: 'pg/timestamp-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-instant@1': { + readonly output: 'pg/timestamp-instant@1'; + readonly nullable: true; + }; readonly 'pg/timestamp-string@1': { readonly output: 'pg/timestamp-string@1'; readonly nullable: true; @@ -203,6 +231,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; diff --git a/test/integration/test/sql-orm-client/fixtures/polymorphism-namespaces/generated/contract.d.ts b/test/integration/test/sql-orm-client/fixtures/polymorphism-namespaces/generated/contract.d.ts index d77236c61c04..cd940d8aece6 100644 --- a/test/integration/test/sql-orm-client/fixtures/polymorphism-namespaces/generated/contract.d.ts +++ b/test/integration/test/sql-orm-client/fixtures/polymorphism-namespaces/generated/contract.d.ts @@ -42,6 +42,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; @@ -91,6 +95,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; @@ -100,6 +108,14 @@ export type AggregateTypes = { readonly output: 'pg/time-temporal@1'; readonly nullable: true; }; + readonly 'pg/timestamp-date@1': { + readonly output: 'pg/timestamp-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-instant@1': { + readonly output: 'pg/timestamp-instant@1'; + readonly nullable: true; + }; readonly 'pg/timestamp-string@1': { readonly output: 'pg/timestamp-string@1'; readonly nullable: true; @@ -151,6 +167,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; @@ -160,6 +180,14 @@ export type AggregateTypes = { readonly output: 'pg/time-temporal@1'; readonly nullable: true; }; + readonly 'pg/timestamp-date@1': { + readonly output: 'pg/timestamp-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-instant@1': { + readonly output: 'pg/timestamp-instant@1'; + readonly nullable: true; + }; readonly 'pg/timestamp-string@1': { readonly output: 'pg/timestamp-string@1'; readonly nullable: true; @@ -203,6 +231,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; diff --git a/test/integration/test/sql-orm-client/fixtures/polymorphism-self-relation/generated/contract.d.ts b/test/integration/test/sql-orm-client/fixtures/polymorphism-self-relation/generated/contract.d.ts index 22a16e54d56a..f78b0d918171 100644 --- a/test/integration/test/sql-orm-client/fixtures/polymorphism-self-relation/generated/contract.d.ts +++ b/test/integration/test/sql-orm-client/fixtures/polymorphism-self-relation/generated/contract.d.ts @@ -42,6 +42,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; @@ -91,6 +95,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; @@ -100,6 +108,14 @@ export type AggregateTypes = { readonly output: 'pg/time-temporal@1'; readonly nullable: true; }; + readonly 'pg/timestamp-date@1': { + readonly output: 'pg/timestamp-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-instant@1': { + readonly output: 'pg/timestamp-instant@1'; + readonly nullable: true; + }; readonly 'pg/timestamp-string@1': { readonly output: 'pg/timestamp-string@1'; readonly nullable: true; @@ -151,6 +167,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; @@ -160,6 +180,14 @@ export type AggregateTypes = { readonly output: 'pg/time-temporal@1'; readonly nullable: true; }; + readonly 'pg/timestamp-date@1': { + readonly output: 'pg/timestamp-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-instant@1': { + readonly output: 'pg/timestamp-instant@1'; + readonly nullable: true; + }; readonly 'pg/timestamp-string@1': { readonly output: 'pg/timestamp-string@1'; readonly nullable: true; @@ -203,6 +231,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; diff --git a/test/integration/test/sql-orm-client/fixtures/polymorphism/generated/contract.d.ts b/test/integration/test/sql-orm-client/fixtures/polymorphism/generated/contract.d.ts index 90af9e6edd9b..741846a4cec7 100644 --- a/test/integration/test/sql-orm-client/fixtures/polymorphism/generated/contract.d.ts +++ b/test/integration/test/sql-orm-client/fixtures/polymorphism/generated/contract.d.ts @@ -42,6 +42,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; @@ -91,6 +95,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; @@ -100,6 +108,14 @@ export type AggregateTypes = { readonly output: 'pg/time-temporal@1'; readonly nullable: true; }; + readonly 'pg/timestamp-date@1': { + readonly output: 'pg/timestamp-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-instant@1': { + readonly output: 'pg/timestamp-instant@1'; + readonly nullable: true; + }; readonly 'pg/timestamp-string@1': { readonly output: 'pg/timestamp-string@1'; readonly nullable: true; @@ -151,6 +167,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; @@ -160,6 +180,14 @@ export type AggregateTypes = { readonly output: 'pg/time-temporal@1'; readonly nullable: true; }; + readonly 'pg/timestamp-date@1': { + readonly output: 'pg/timestamp-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-instant@1': { + readonly output: 'pg/timestamp-instant@1'; + readonly nullable: true; + }; readonly 'pg/timestamp-string@1': { readonly output: 'pg/timestamp-string@1'; readonly nullable: true; @@ -203,6 +231,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; diff --git a/test/integration/test/sql-orm-client/fixtures/scalar-lists/generated/contract.d.ts b/test/integration/test/sql-orm-client/fixtures/scalar-lists/generated/contract.d.ts index 1d4e5a338ded..42b4486e6950 100644 --- a/test/integration/test/sql-orm-client/fixtures/scalar-lists/generated/contract.d.ts +++ b/test/integration/test/sql-orm-client/fixtures/scalar-lists/generated/contract.d.ts @@ -42,6 +42,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; @@ -91,6 +95,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; @@ -100,6 +108,14 @@ export type AggregateTypes = { readonly output: 'pg/time-temporal@1'; readonly nullable: true; }; + readonly 'pg/timestamp-date@1': { + readonly output: 'pg/timestamp-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-instant@1': { + readonly output: 'pg/timestamp-instant@1'; + readonly nullable: true; + }; readonly 'pg/timestamp-string@1': { readonly output: 'pg/timestamp-string@1'; readonly nullable: true; @@ -151,6 +167,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; @@ -160,6 +180,14 @@ export type AggregateTypes = { readonly output: 'pg/time-temporal@1'; readonly nullable: true; }; + readonly 'pg/timestamp-date@1': { + readonly output: 'pg/timestamp-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-instant@1': { + readonly output: 'pg/timestamp-instant@1'; + readonly nullable: true; + }; readonly 'pg/timestamp-string@1': { readonly output: 'pg/timestamp-string@1'; readonly nullable: true; @@ -203,6 +231,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; diff --git a/test/integration/test/sql-orm-client/fixtures/scope-namespace/generated/contract.d.ts b/test/integration/test/sql-orm-client/fixtures/scope-namespace/generated/contract.d.ts index ffa93ebd92ef..bc89d2cf7208 100644 --- a/test/integration/test/sql-orm-client/fixtures/scope-namespace/generated/contract.d.ts +++ b/test/integration/test/sql-orm-client/fixtures/scope-namespace/generated/contract.d.ts @@ -42,6 +42,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; @@ -91,6 +95,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; @@ -100,6 +108,14 @@ export type AggregateTypes = { readonly output: 'pg/time-temporal@1'; readonly nullable: true; }; + readonly 'pg/timestamp-date@1': { + readonly output: 'pg/timestamp-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-instant@1': { + readonly output: 'pg/timestamp-instant@1'; + readonly nullable: true; + }; readonly 'pg/timestamp-string@1': { readonly output: 'pg/timestamp-string@1'; readonly nullable: true; @@ -151,6 +167,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; @@ -160,6 +180,14 @@ export type AggregateTypes = { readonly output: 'pg/time-temporal@1'; readonly nullable: true; }; + readonly 'pg/timestamp-date@1': { + readonly output: 'pg/timestamp-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-instant@1': { + readonly output: 'pg/timestamp-instant@1'; + readonly nullable: true; + }; readonly 'pg/timestamp-string@1': { readonly output: 'pg/timestamp-string@1'; readonly nullable: true; @@ -203,6 +231,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; diff --git a/test/integration/test/sql-orm-client/fixtures/self-relations/generated/contract.d.ts b/test/integration/test/sql-orm-client/fixtures/self-relations/generated/contract.d.ts index 1611d36bb3ba..2657cb8f7381 100644 --- a/test/integration/test/sql-orm-client/fixtures/self-relations/generated/contract.d.ts +++ b/test/integration/test/sql-orm-client/fixtures/self-relations/generated/contract.d.ts @@ -42,6 +42,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; @@ -91,6 +95,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; @@ -100,6 +108,14 @@ export type AggregateTypes = { readonly output: 'pg/time-temporal@1'; readonly nullable: true; }; + readonly 'pg/timestamp-date@1': { + readonly output: 'pg/timestamp-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-instant@1': { + readonly output: 'pg/timestamp-instant@1'; + readonly nullable: true; + }; readonly 'pg/timestamp-string@1': { readonly output: 'pg/timestamp-string@1'; readonly nullable: true; @@ -151,6 +167,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; @@ -160,6 +180,14 @@ export type AggregateTypes = { readonly output: 'pg/time-temporal@1'; readonly nullable: true; }; + readonly 'pg/timestamp-date@1': { + readonly output: 'pg/timestamp-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-instant@1': { + readonly output: 'pg/timestamp-instant@1'; + readonly nullable: true; + }; readonly 'pg/timestamp-string@1': { readonly output: 'pg/timestamp-string@1'; readonly nullable: true; @@ -203,6 +231,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; diff --git a/test/integration/test/sql-orm-client/fixtures/soft-delete/generated/contract.d.ts b/test/integration/test/sql-orm-client/fixtures/soft-delete/generated/contract.d.ts index 64b6249f01d3..62f3fe8bdef7 100644 --- a/test/integration/test/sql-orm-client/fixtures/soft-delete/generated/contract.d.ts +++ b/test/integration/test/sql-orm-client/fixtures/soft-delete/generated/contract.d.ts @@ -43,6 +43,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; @@ -92,6 +96,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; @@ -101,6 +109,14 @@ export type AggregateTypes = { readonly output: 'pg/time-temporal@1'; readonly nullable: true; }; + readonly 'pg/timestamp-date@1': { + readonly output: 'pg/timestamp-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-instant@1': { + readonly output: 'pg/timestamp-instant@1'; + readonly nullable: true; + }; readonly 'pg/timestamp-string@1': { readonly output: 'pg/timestamp-string@1'; readonly nullable: true; @@ -152,6 +168,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; @@ -161,6 +181,14 @@ export type AggregateTypes = { readonly output: 'pg/time-temporal@1'; readonly nullable: true; }; + readonly 'pg/timestamp-date@1': { + readonly output: 'pg/timestamp-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-instant@1': { + readonly output: 'pg/timestamp-instant@1'; + readonly nullable: true; + }; readonly 'pg/timestamp-string@1': { readonly output: 'pg/timestamp-string@1'; readonly nullable: true; @@ -204,6 +232,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; diff --git a/test/integration/test/temporal-defaults/_fixture-aliases/generated/contract.d.ts b/test/integration/test/temporal-defaults/_fixture-aliases/generated/contract.d.ts index c9d5de5e4971..7f140d7dc8a0 100644 --- a/test/integration/test/temporal-defaults/_fixture-aliases/generated/contract.d.ts +++ b/test/integration/test/temporal-defaults/_fixture-aliases/generated/contract.d.ts @@ -43,6 +43,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; @@ -92,6 +96,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; @@ -101,6 +109,14 @@ export type AggregateTypes = { readonly output: 'pg/time-temporal@1'; readonly nullable: true; }; + readonly 'pg/timestamp-date@1': { + readonly output: 'pg/timestamp-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-instant@1': { + readonly output: 'pg/timestamp-instant@1'; + readonly nullable: true; + }; readonly 'pg/timestamp-string@1': { readonly output: 'pg/timestamp-string@1'; readonly nullable: true; @@ -152,6 +168,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; @@ -161,6 +181,14 @@ export type AggregateTypes = { readonly output: 'pg/time-temporal@1'; readonly nullable: true; }; + readonly 'pg/timestamp-date@1': { + readonly output: 'pg/timestamp-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-instant@1': { + readonly output: 'pg/timestamp-instant@1'; + readonly nullable: true; + }; readonly 'pg/timestamp-string@1': { readonly output: 'pg/timestamp-string@1'; readonly nullable: true; @@ -204,6 +232,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; diff --git a/test/integration/test/temporal-defaults/_fixture-attributes/generated/contract.d.ts b/test/integration/test/temporal-defaults/_fixture-attributes/generated/contract.d.ts index e476373c4fac..186a16dccec4 100644 --- a/test/integration/test/temporal-defaults/_fixture-attributes/generated/contract.d.ts +++ b/test/integration/test/temporal-defaults/_fixture-attributes/generated/contract.d.ts @@ -43,6 +43,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; @@ -92,6 +96,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; @@ -101,6 +109,14 @@ export type AggregateTypes = { readonly output: 'pg/time-temporal@1'; readonly nullable: true; }; + readonly 'pg/timestamp-date@1': { + readonly output: 'pg/timestamp-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-instant@1': { + readonly output: 'pg/timestamp-instant@1'; + readonly nullable: true; + }; readonly 'pg/timestamp-string@1': { readonly output: 'pg/timestamp-string@1'; readonly nullable: true; @@ -152,6 +168,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; @@ -161,6 +181,14 @@ export type AggregateTypes = { readonly output: 'pg/time-temporal@1'; readonly nullable: true; }; + readonly 'pg/timestamp-date@1': { + readonly output: 'pg/timestamp-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-instant@1': { + readonly output: 'pg/timestamp-instant@1'; + readonly nullable: true; + }; readonly 'pg/timestamp-string@1': { readonly output: 'pg/timestamp-string@1'; readonly nullable: true; @@ -204,6 +232,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; diff --git a/test/integration/test/temporal-defaults/_fixture-now/generated/contract.d.ts b/test/integration/test/temporal-defaults/_fixture-now/generated/contract.d.ts index e476373c4fac..186a16dccec4 100644 --- a/test/integration/test/temporal-defaults/_fixture-now/generated/contract.d.ts +++ b/test/integration/test/temporal-defaults/_fixture-now/generated/contract.d.ts @@ -43,6 +43,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; @@ -92,6 +96,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; @@ -101,6 +109,14 @@ export type AggregateTypes = { readonly output: 'pg/time-temporal@1'; readonly nullable: true; }; + readonly 'pg/timestamp-date@1': { + readonly output: 'pg/timestamp-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-instant@1': { + readonly output: 'pg/timestamp-instant@1'; + readonly nullable: true; + }; readonly 'pg/timestamp-string@1': { readonly output: 'pg/timestamp-string@1'; readonly nullable: true; @@ -152,6 +168,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; @@ -161,6 +181,14 @@ export type AggregateTypes = { readonly output: 'pg/time-temporal@1'; readonly nullable: true; }; + readonly 'pg/timestamp-date@1': { + readonly output: 'pg/timestamp-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-instant@1': { + readonly output: 'pg/timestamp-instant@1'; + readonly nullable: true; + }; readonly 'pg/timestamp-string@1': { readonly output: 'pg/timestamp-string@1'; readonly nullable: true; @@ -204,6 +232,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; diff --git a/test/integration/test/temporal-defaults/_fixture-timestamp/generated/contract.d.ts b/test/integration/test/temporal-defaults/_fixture-timestamp/generated/contract.d.ts index b1278162199d..207db1337b24 100644 --- a/test/integration/test/temporal-defaults/_fixture-timestamp/generated/contract.d.ts +++ b/test/integration/test/temporal-defaults/_fixture-timestamp/generated/contract.d.ts @@ -43,6 +43,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; @@ -92,6 +96,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; @@ -101,6 +109,14 @@ export type AggregateTypes = { readonly output: 'pg/time-temporal@1'; readonly nullable: true; }; + readonly 'pg/timestamp-date@1': { + readonly output: 'pg/timestamp-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-instant@1': { + readonly output: 'pg/timestamp-instant@1'; + readonly nullable: true; + }; readonly 'pg/timestamp-string@1': { readonly output: 'pg/timestamp-string@1'; readonly nullable: true; @@ -152,6 +168,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; @@ -161,6 +181,14 @@ export type AggregateTypes = { readonly output: 'pg/time-temporal@1'; readonly nullable: true; }; + readonly 'pg/timestamp-date@1': { + readonly output: 'pg/timestamp-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-instant@1': { + readonly output: 'pg/timestamp-instant@1'; + readonly nullable: true; + }; readonly 'pg/timestamp-string@1': { readonly output: 'pg/timestamp-string@1'; readonly nullable: true; @@ -204,6 +232,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; diff --git a/test/integration/test/temporal-defaults/_fixture/generated/contract.d.ts b/test/integration/test/temporal-defaults/_fixture/generated/contract.d.ts index c9d5de5e4971..7f140d7dc8a0 100644 --- a/test/integration/test/temporal-defaults/_fixture/generated/contract.d.ts +++ b/test/integration/test/temporal-defaults/_fixture/generated/contract.d.ts @@ -43,6 +43,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; @@ -92,6 +96,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; @@ -101,6 +109,14 @@ export type AggregateTypes = { readonly output: 'pg/time-temporal@1'; readonly nullable: true; }; + readonly 'pg/timestamp-date@1': { + readonly output: 'pg/timestamp-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-instant@1': { + readonly output: 'pg/timestamp-instant@1'; + readonly nullable: true; + }; readonly 'pg/timestamp-string@1': { readonly output: 'pg/timestamp-string@1'; readonly nullable: true; @@ -152,6 +168,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; @@ -161,6 +181,14 @@ export type AggregateTypes = { readonly output: 'pg/time-temporal@1'; readonly nullable: true; }; + readonly 'pg/timestamp-date@1': { + readonly output: 'pg/timestamp-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-instant@1': { + readonly output: 'pg/timestamp-instant@1'; + readonly nullable: true; + }; readonly 'pg/timestamp-string@1': { readonly output: 'pg/timestamp-string@1'; readonly nullable: true; @@ -204,6 +232,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; From 592d07c1a9ebde1f120b521eefaeba7fb524c4d3 Mon Sep 17 00:00:00 2001 From: willbot Date: Thu, 22 Oct 2026 18:22:36 +0200 Subject: [PATCH 016/118] Upgrade instructions for the explicit date and time type renames The app and extension guides give the rename table for Timestamptz, Timestamp and Interval, the renamed TypeScript helpers, the interval output type, and the Temporal types now taken from temporal-spec. Co-Authored-By: Claude Opus 5.5 Signed-off-by: willbot Signed-off-by: Will Madden --- .../app/instructions.md | 89 +++++++++++++++++++ .../extension/instructions.md | 69 ++++++++++++++ 2 files changed, 158 insertions(+) create mode 100644 upgrade-instructions/pending/explicit-date-time-types/app/instructions.md create mode 100644 upgrade-instructions/pending/explicit-date-time-types/extension/instructions.md diff --git a/upgrade-instructions/pending/explicit-date-time-types/app/instructions.md b/upgrade-instructions/pending/explicit-date-time-types/app/instructions.md new file mode 100644 index 000000000000..7dcb45725487 --- /dev/null +++ b/upgrade-instructions/pending/explicit-date-time-types/app/instructions.md @@ -0,0 +1,89 @@ +--- +changes: + - id: psl-temporal-type-renames + summary: | + On PostgreSQL, the PSL types `Timestamptz`, `Timestamp` and `Interval` are renamed to `TimestamptzTemporal`, `TimestampTemporal` and `IntervalParts`, and the old names are errors. Rename each use in your `.prisma` files, keeping its arguments: `Timestamptz(3)` becomes `TimestamptzTemporal(3)`. Each new name reads the same value with the same codec, so the contract and its hashes do not change. `@db.Timestamp` and `@db.Timestamptz` in a Prisma 7 schema are not affected. + detection: + glob: "**/*.prisma" + matches: + - '(?().toEqualTypeOf(); +``` + +If the test file also creates `Temporal` values, import the type under another name, such as `import type { Temporal as TemporalTypes } from 'temporal-spec'`, so the runtime `Temporal` stays in scope. diff --git a/upgrade-instructions/pending/explicit-date-time-types/extension/instructions.md b/upgrade-instructions/pending/explicit-date-time-types/extension/instructions.md new file mode 100644 index 000000000000..17e01f6694a0 --- /dev/null +++ b/upgrade-instructions/pending/explicit-date-time-types/extension/instructions.md @@ -0,0 +1,69 @@ +--- +changes: + - id: psl-temporal-type-renames + summary: | + On PostgreSQL, the PSL types `Timestamptz`, `Timestamp` and `Interval` are renamed to `TimestamptzTemporal`, `TimestampTemporal` and `IntervalParts`, and the old names are errors. Rename each use in your `.prisma` files, keeping its arguments: `Timestamptz(3)` becomes `TimestamptzTemporal(3)`. Each new name reads the same value with the same codec, so the contract and its hashes do not change. `@db.Timestamp` and `@db.Timestamptz` in a Prisma 7 schema are not affected. + detection: + glob: "**/*.prisma" + matches: + - '(? Date: Thu, 22 Oct 2026 18:23:13 +0200 Subject: [PATCH 017/118] Document the timestamp Date, timestamp Instant and interval string types The scalar types reference lists the three new explicit types and how they read and write, and the codec authoring guide, the target README and the prisma-8 skill references name them. Co-Authored-By: Claude Opus 5.5 Signed-off-by: willbot Signed-off-by: Will Madden --- docs/reference/codec-authoring-guide.md | 2 +- docs/reference/scalar-types.md | 7 ++++++- packages/3-targets/3-targets/postgres/README.md | 2 +- skills/prisma-8/references/contract.md | 2 +- skills/prisma-8/references/debug.md | 2 +- 5 files changed, 10 insertions(+), 5 deletions(-) diff --git a/docs/reference/codec-authoring-guide.md b/docs/reference/codec-authoring-guide.md index 0ead26232272..f92e35f9ea49 100644 --- a/docs/reference/codec-authoring-guide.md +++ b/docs/reference/codec-authoring-guide.md @@ -55,7 +55,7 @@ An enum compares values with its members, so every enum authoring surface (`enum The consumer-facing [`BigInt`, `BigIntNumber`, and `UnboundedInt` representation choices](./integer-representation-types.md), are documented separately from this contributor guide. -The PostgreSQL temporal codecs pair a `Temporal`-valued codec with a raw-text one for each native type — `pg/timestamptz-temporal@1` reads a `Temporal.Instant`, `pg/timestamptz-string@1` reads PostgreSQL's own text, and the contract names which one a column uses. A text codec's `fromWire` keeps PostgreSQL's year text, ` BC` included, and its `toWire` converts the stored ISO year into it. +The PostgreSQL temporal codecs pair a `Temporal`-valued codec with a raw-text one for each native type — `pg/timestamptz-temporal@1` reads a `Temporal.Instant`, `pg/timestamptz-string@1` reads PostgreSQL's own text, and the contract names which one a column uses. A type may have more codecs: `timestamptz` and `timestamp` also have a `Date` codec, `timestamp` a second `Temporal` codec, `pg/timestamp-instant@1`, that reads the stored date and time as a UTC `Temporal.Instant`, and `interval` a codec that reads an ISO 8601 duration string. A codec of one type differs from its siblings only in the application value, so a new one is a codec, never a new data type. A text codec's `fromWire` keeps PostgreSQL's year text, ` BC` included, and its `toWire` converts the stored ISO year into it. ## Three case studies diff --git a/docs/reference/scalar-types.md b/docs/reference/scalar-types.md index 6444f70d1963..9ccd69ccad36 100644 --- a/docs/reference/scalar-types.md +++ b/docs/reference/scalar-types.md @@ -85,6 +85,8 @@ These are the current names. The Postgres and SQLite rename project will change | `TimestamptzString(p?)` | — | `pg/timestamptz-string@1` | `timestamptz` | `string` | | `TimestampTemporal(p?)` | — | `pg/timestamp-temporal@1` | `timestamp` | `Temporal.PlainDateTime` | | `TimestampString(p?)` | — | `pg/timestamp-string@1` | `timestamp` | `string` | +| `TimestampJsDate(p?)` | — | `pg/timestamp-date@1` | `timestamp` | `Date`, the stored date and time read as UTC | +| `TimestampInstant(p?)` | — | `pg/timestamp-instant@1` | `timestamp` | `Temporal.Instant`, the stored date and time read as UTC | | `Date`, `DateTemporal` | — | `pg/date-temporal@1` | `date` | `Temporal.PlainDate` | | `DateString` | — | `pg/date-string@1` | `date` | `string` | | `Time(p?)`, `TimeTemporal(p?)` | — | `pg/time-temporal@1` | `time` | `Temporal.PlainTime` | @@ -93,6 +95,7 @@ These are the current names. The Postgres and SQLite rename project will change | `Uuid` | `field.uuidNative()` | `pg/uuid@1` | `uuid` | `string` | | `Inet` | — | `pg/inet@1` | `inet` | `string` | | `IntervalParts(p?)` | — | `pg/interval@1` | `interval` | `{ months: number; days: number; micros: bigint }` | +| `IntervalString(p?)` | — | `pg/interval-string@1` | `interval` | `string`, the ISO 8601 duration, such as `P1Y2M` | | `Bit(n?)` | — | `pg/bit@1` | `bit` | `string` (the bits as `0` and `1`) | | `VarBit(n?)` | — | `pg/varbit@1` | `bit varying` | `string` (the bits as `0` and `1`) | | `Json` | — | `pg/json@1` | `json` | `JsonValue` | @@ -100,9 +103,11 @@ These are the current names. The Postgres and SQLite rename project will change | `Bytes` | `field.bytes()` | `pg/bytea@1` | `bytea` | `Uint8Array` | | `pg.enum(Name)` | — | `pg/enum@1` | the native enum type | `string` | +`TimestampJsDate` and `TimestampInstant` read the date and time a `timestamp` column stores as UTC, and write a value's UTC date and time with no offset, so neither depends on the server's or the application's time zone. They refuse `infinity`, `-infinity` and a value after `+275760-09-13T00:00:00`, which no `Date` or `Temporal.Instant` holds, and name `TimestampString`, which holds them. `IntervalString` reads every `IntervalStyle` and writes an ISO 8601 duration; it refuses any other text on write. + A literal default of a date, time, interval or bytes type is written with the tag of its type, as in `@default(timestamptz`2024-01-01T00:00:00Z`)` or `@default(bytea`\x0102`)`; a quoted string is refused with a message that shows the tag. It is stored in the one form its data type stores, whichever codec the column uses and however the default was written, and a default with more fraction digits or more scale than its column holds is refused rather than rounded; [ADR 254](../architecture%20docs/adrs/ADR%20254%20-%20Data%20types%20and%20casts.md#date-and-time-types) states each form. -The tools that write a schema for you use the text date and time types. In a PSL schema, `prisma orm init` writes `createdAt TimestamptzString @default(now())` and `updatedAt temporal.updatedAtString()`. In a TypeScript contract, it writes `field.temporal.createdAtString()` and `field.temporal.updatedAtString()`. All of these are `TimestamptzString` columns. `prisma contract infer` and `prisma7Schema(...)` write `TimestampString(p)`, `TimestamptzString(p)`, `DateString` and `TimeString(p)`. Four types read and write `Temporal` values: `TimestamptzTemporal` (also written `DateTime`), `TimestampTemporal`, `DateTemporal` (also written `Date`) and `TimeTemporal` (also written `Time`). Their TypeScript types come from `temporal-spec`, so a project needs no global `Temporal` declarations to type them. An application that uses them needs a global `Temporal`: on a runtime without one, such as Node.js 24, install it, for example with `import 'temporal-polyfill/full/global'`. +The tools that write a schema for you use the text date and time types. In a PSL schema, `prisma orm init` writes `createdAt TimestamptzString @default(now())` and `updatedAt temporal.updatedAtString()`. In a TypeScript contract, it writes `field.temporal.createdAtString()` and `field.temporal.updatedAtString()`. All of these are `TimestamptzString` columns. `prisma contract infer` and `prisma7Schema(...)` write `TimestampString(p)`, `TimestamptzString(p)`, `DateString` and `TimeString(p)`. Five types read and write `Temporal` values: `TimestamptzTemporal` (also written `DateTime`), `TimestampTemporal`, `TimestampInstant`, `DateTemporal` (also written `Date`) and `TimeTemporal` (also written `Time`). Their TypeScript types come from `temporal-spec`, so a project needs no global `Temporal` declarations to type them. An application that uses them needs a global `Temporal`: on a runtime without one, such as Node.js 24, install it, for example with `import 'temporal-polyfill/full/global'`. ## SQLite diff --git a/packages/3-targets/3-targets/postgres/README.md b/packages/3-targets/3-targets/postgres/README.md index 5472815bc92c..57d882db25fb 100644 --- a/packages/3-targets/3-targets/postgres/README.md +++ b/packages/3-targets/3-targets/postgres/README.md @@ -28,7 +28,7 @@ Provides the Postgres target descriptor (`SqlControlTargetDescriptor`) for CLI c - **Storage Type Planning**: The planner dispatches storage type hooks for `storage.types` and emits type operations before table creation when supported by the policy - **Contract-to-PSL Printing**: Implements the `buildPslContract` descriptor hook, which `prisma contract print` uses. It takes a Postgres contract the contract serializer accepted, from any source, and returns the Prisma 8 PSL document that reads back as the same contract. It does not check the contract's structure again; a broken structure is an internal error. It writes models, value objects, named types, domain and native enums, keys with their names, indexes, checks, relations, polymorphism, control policies, row-level security (`@@rls`, policies, and roles in `namespace unbound`) and defaults. A column is written as a type constructor the configured stack contributes that reads back with its codec, native type and type parameters: the constructor marked `inferred` for the column's data type, which is the one `contract infer` writes, when it does, or else the first constructor in the stack that does, such as `DateTime` or `TimestamptzTemporal(3)` for a `Temporal`-backed `timestamptz` column, or an extension's `pgvector.Vector(3)`. A value-object member has no column, so its type goes through the stack the same way: the stack's codec names the native type for the member's type parameters, and the member is written as the type constructor for that native type, called with those parameters (`Numeric(10, 2)`); a member typed by a domain enum is written by the enum's name. The contract does not record which named type a member used, so a member written `code Short`, with `types { Short = VarChar(10) }`, is printed as the type it stands for, `code VarChar(10)`; a model field keeps `Short`, because its column names the named type. A literal default is written by the data type of the column's codec. The family passes the stack's type constructors, codecs and data types to the hook for this. Anything it cannot write so that it reads back the same is refused by name with `CONTRACT.PRINT_UNSUPPORTED` (the full list is in `docs/reference/error-reference.md`, one function per case in `src/core/psl-print/refusals.ts`); nothing is dropped. It lives in `src/core/psl-print/`, the counterpart of the `inferPslContract` hook in `src/core/psl-infer/`, which writes PSL from a live database. Both build with the shared builders in `src/core/psl-build/`: literals, index and check attributes, and the native enum block. Both find the constructor marked `inferred` for a data type through the SQL family's `@internal/family-sql/psl-build`. Model and field names come from the contract; `@@map` and `@map` appear only where the name the PSL source would derive differs. A unique constraint prints as `@@unique` and a unique index as `@@index(..., unique: true)`; every index prints explicitly, and a relation writes `index: false` when nothing backs its foreign key, no `index` argument when a relation that says nothing would be backed by the same object (`defaultForeignKeyIndex`), and otherwise `index: ""` naming the index, or the key whose first columns are the foreign key's. Execution generators join back onto their columns, through whatever the configured stack contributes, extensions included. A column that is not a list and whose PSL type stands for its data type prints as the call of the field preset that writes its codec with its generators, built from the preset's print form, when the preset accepts that call and it reads back with the column's codec, type params and generators. A column typed by a named type, a value object or an enum, and a column whose call fails those checks, such as one without a value for a required argument, does not. A column with only a create generator then prints it as `@default((...))`, the call of the default function that declares `printArguments` for it; any other column is refused. - **Database-to-PSL inference**: Implements the `inferPslContract` descriptor hook, which `prisma contract infer` uses. Introspection records each column's element type as the database reports it (`format_type`, its kind, schema and name). Infer resolves that reported type through the data types of the configured stack, the target's and the extension packs', and writes the constructor marked `inferred` for the data type, with the fewest arguments that give the same parameters (`numeric(10,0)` is written `Numeric(10)`); a list column adds `[]`, and a native enum column is written `pg.enum()`. When a column's type is claimed by no data type, or by one with no marked constructor (such as `tsquery`), infer writes nothing and fails with `CONTRACT.INFER_COLUMN_TYPE_UNCLAIMED`, listing every such column as `.

.: `. No configured pack has a type for such a column. When the type comes from a database extension that has a Prisma 8 pack, such as `pgvector` or `postgis`, adding the pack to `extensions` in `prisma.config.ts` lets infer write it, for the forms of the type the pack supports. Otherwise, as for domains, `"char"`, `hstore` and `tsquery`, the column's type must change, or the column must go. The rule itself (resolving a reported type through the data types, the fewest-arguments call, the unclaimed error) lives in the SQL family's `@internal/family-sql/psl-build`; the target adds the walk over its schema tree and the native enum handling in `src/core/psl-infer/`. -- **`Temporal` for the target's own code**: The `Temporal`-backed codecs (`pg/date-temporal@1`, `pg/time-temporal@1`, `pg/timestamp-temporal@1` and `pg/timestamptz-temporal@1`), when they decode, and the `instantNow`, `plainDateTimeNow`, `plainDateNow` and `plainTimeNow` generators get `Temporal` from `requireTemporal()` (`src/core/require-temporal.ts`): the runtime's global `Temporal` when it has one, else the fallback `Temporal`, else it throws `RUNTIME.TEMPORAL_UNAVAILABLE`. The text codecs (`pg/date-string@1`, `pg/time-string@1`, `pg/timestamp-string@1`, `pg/timestamptz-string@1` and `pg/timetz@1`), `pg/timestamptz-date@1`, the `timestampNow` generator and the string generators (`timestampStringNow`, `timestamptzStringNow`, `dateStringNow`, `timeStringNow` and `timetzNow`) do not use `Temporal`, so an application that uses only them reads and writes its date and time columns without one. The target's control entry (`@internal/target-postgres/control`) sets the fallback `Temporal`, from `temporal-polyfill`, when it is loaded. Nothing sets `globalThis.Temporal`. No other entry of this package imports `temporal-polyfill`. `temporal-polyfill` is a required peer dependency of this package and of the published `@prisma/orm-target-postgres` and `@prisma/orm-postgres`, so a project that already installs it for its own code has one copy. npm, pnpm and bun install it automatically; a project that uses Yarn adds `temporal-polyfill` to its own dependencies. In the published package, `@prisma/orm-target-postgres/target` is the aggregate of every entry of this package, the control entry included, so it loads the control entry too; an application imports the entry it needs, for example `@prisma/orm-target-postgres/target/runtime`. The fallback is held once per process, and every `Temporal`-backed codec in the process uses it. An application process that loads no control-plane code and has no global `Temporal` gets `RUNTIME.TEMPORAL_UNAVAILABLE` when one of those codecs decodes or one of those generators runs. An application that loads control-plane code in its own process, such as server code under `vite dev` with the Prisma Vite plugin or a script that calls the control client, decodes dates without its own `Temporal`, and then fails in production. An application that uses the Temporal codecs must load its own `Temporal`. Every signature that names a `Temporal` type takes it from `temporal-spec`, a package of type declarations with no runtime code (`import type { Temporal } from 'temporal-spec'`), never from a global declaration, so a Temporal field is typed precisely in a project that has no global `Temporal` declarations. `tsconfig.no-temporal-global.json` type-checks `src` without them. +- **`Temporal` for the target's own code**: The `Temporal`-backed codecs (`pg/date-temporal@1`, `pg/time-temporal@1`, `pg/timestamp-temporal@1`, `pg/timestamp-instant@1` and `pg/timestamptz-temporal@1`), when they decode, and the `instantNow`, `plainDateTimeNow`, `plainDateNow` and `plainTimeNow` generators get `Temporal` from `requireTemporal()` (`src/core/require-temporal.ts`): the runtime's global `Temporal` when it has one, else the fallback `Temporal`, else it throws `RUNTIME.TEMPORAL_UNAVAILABLE`. The text codecs (`pg/date-string@1`, `pg/time-string@1`, `pg/timestamp-string@1`, `pg/timestamptz-string@1` and `pg/timetz@1`), `pg/timestamptz-date@1`, `pg/timestamp-date@1`, `pg/interval@1`, `pg/interval-string@1`, the `timestampNow` generator and the string generators (`timestampStringNow`, `timestamptzStringNow`, `dateStringNow`, `timeStringNow` and `timetzNow`) do not use `Temporal`, so an application that uses only them reads and writes its date and time columns without one. The target's control entry (`@internal/target-postgres/control`) sets the fallback `Temporal`, from `temporal-polyfill`, when it is loaded. Nothing sets `globalThis.Temporal`. No other entry of this package imports `temporal-polyfill`. `temporal-polyfill` is a required peer dependency of this package and of the published `@prisma/orm-target-postgres` and `@prisma/orm-postgres`, so a project that already installs it for its own code has one copy. npm, pnpm and bun install it automatically; a project that uses Yarn adds `temporal-polyfill` to its own dependencies. In the published package, `@prisma/orm-target-postgres/target` is the aggregate of every entry of this package, the control entry included, so it loads the control entry too; an application imports the entry it needs, for example `@prisma/orm-target-postgres/target/runtime`. The fallback is held once per process, and every `Temporal`-backed codec in the process uses it. An application process that loads no control-plane code and has no global `Temporal` gets `RUNTIME.TEMPORAL_UNAVAILABLE` when one of those codecs decodes or one of those generators runs. An application that loads control-plane code in its own process, such as server code under `vite dev` with the Prisma Vite plugin or a script that calls the control client, decodes dates without its own `Temporal`, and then fails in production. An application that uses the Temporal codecs must load its own `Temporal`. Every signature that names a `Temporal` type takes it from `temporal-spec`, a package of type declarations with no runtime code (`import type { Temporal } from 'temporal-spec'`), never from a global declaration, so a Temporal field is typed precisely in a project that has no global `Temporal` declarations. `tsconfig.no-temporal-global.json` type-checks `src` without them. - **Runtime List Framing**: Parses inbound Postgres array text for contract-declared list columns before applying the scalar element codec. Builtin arrays and enum arrays therefore share one target-owned decode path; fixed-scale `numeric(30,10)[]` reads database-normalized text such as `"1.5000000000"`, matching scalar numeric decoding rather than the previous driver numeric-array float spelling `"1.5"`. This package spans multiple planes: diff --git a/skills/prisma-8/references/contract.md b/skills/prisma-8/references/contract.md index c9ec12058c42..689a15a63693 100644 --- a/skills/prisma-8/references/contract.md +++ b/skills/prisma-8/references/contract.md @@ -117,7 +117,7 @@ model Post { Then run `pnpm prisma contract emit` (or rely on the Vite plugin — see `references/build.md`). Specify cascade behaviour explicitly with `onDelete` / `onUpdate`; the default is `Restrict`. -**Temporal columns.** On PostgreSQL, `DateTemporal`, `TimestampTemporal(p)`, `TimestamptzTemporal(p)` and `TimeTemporal(p)` (and `Date`, `DateTime` and `Time`, which today mean the same as `DateTemporal`, `TimestamptzTemporal` and `TimeTemporal`) read and write `Temporal` values (`Temporal.PlainDate`, `PlainDateTime`, `Instant`, `PlainTime`), never JavaScript `Date`. They need a global `Temporal` at query time: Node.js 26.8.2 and later ship `globalThis.Temporal`; 26.8.1 and earlier — including every 22 and 24 release — do not, and the first read or write of such a column throws `RUNTIME.TEMPORAL_UNAVAILABLE`. On those runtimes either `import 'temporal-polyfill/full/global'` before the first query (add `temporal-polyfill` as a dependency) or author the column as `DateString` / `TimestampString(p)` / `TimestamptzString(p)` / `TimeString(p)`, which carry PostgreSQL's own text and need no `Temporal`. `prisma contract emit`, `prisma db init` and the other commands need no global `Temporal`: the Postgres target loads `temporal-polyfill` as a fallback `Temporal` for the control plane, and sets no global. `temporal-polyfill` is a required peer dependency of `@prisma/orm-postgres`. npm, pnpm and bun install it automatically. With Yarn, add `temporal-polyfill` (`^1.0.4`) to the project's dependencies, or the commands fail because Node.js cannot find the package. That fallback is held once per process. An application that loads control-plane code in its own process, such as server code under `vite dev` with the Prisma Vite plugin or a script that calls the control client, decodes dates without its own `Temporal`, and then fails in production. An application that uses the Temporal codecs must load its own `Temporal`. A TypeScript contract file that constructs a `Temporal` value must load an implementation too. +**Temporal columns.** On PostgreSQL, `DateTemporal`, `TimestampTemporal(p)`, `TimestampInstant(p)`, `TimestamptzTemporal(p)` and `TimeTemporal(p)` (and `Date`, `DateTime` and `Time`, which today mean the same as `DateTemporal`, `TimestamptzTemporal` and `TimeTemporal`) read and write `Temporal` values (`Temporal.PlainDate`, `PlainDateTime`, `Instant` for both instant types, `PlainTime`), never JavaScript `Date`. They need a global `Temporal` at query time: Node.js 26.8.2 and later ship `globalThis.Temporal`; 26.8.1 and earlier — including every 22 and 24 release — do not, and the first read or write of such a column throws `RUNTIME.TEMPORAL_UNAVAILABLE`. On those runtimes either `import 'temporal-polyfill/full/global'` before the first query (add `temporal-polyfill` as a dependency) or author the column as `DateString` / `TimestampString(p)` / `TimestamptzString(p)` / `TimeString(p)`, which carry text and need no `Temporal`, or as `TimestamptzJsDate(p)` / `TimestampJsDate(p)`, which read a JavaScript `Date`. `IntervalParts(p)` reads `{ months, days, micros }` and `IntervalString(p)` an ISO 8601 duration such as `P1Y2M`. `prisma contract emit`, `prisma db init` and the other commands need no global `Temporal`: the Postgres target loads `temporal-polyfill` as a fallback `Temporal` for the control plane, and sets no global. `temporal-polyfill` is a required peer dependency of `@prisma/orm-postgres`. npm, pnpm and bun install it automatically. With Yarn, add `temporal-polyfill` (`^1.0.4`) to the project's dependencies, or the commands fail because Node.js cannot find the package. That fallback is held once per process. An application that loads control-plane code in its own process, such as server code under `vite dev` with the Prisma Vite plugin or a script that calls the control client, decodes dates without its own `Temporal`, and then fails in production. An application that uses the Temporal codecs must load its own `Temporal`. A TypeScript contract file that constructs a `Temporal` value must load an implementation too. `@@index` also accepts `expression:` (instead of a fields list), `where:` (partial-index predicate), `unique:`, `type:`/`options:` (target-registered access method), and `name:` xor `map:`. Raw SQL in `expression:` and `where:` is a `sql` literal; a quoted string is refused with `PSL_VALUE_TYPE_INCOMPATIBLE`, whose message gives the rewrite: diff --git a/skills/prisma-8/references/debug.md b/skills/prisma-8/references/debug.md index c5151f6407c8..1924419b2ef5 100644 --- a/skills/prisma-8/references/debug.md +++ b/skills/prisma-8/references/debug.md @@ -110,7 +110,7 @@ The single source of truth: read the envelope, find the row by `code`, follow th | `BUDGET.ROWS_EXCEEDED` / `BUDGET.TIME_EXCEEDED` | Runtime, when the `budgets` middleware is active | Tune `budgets({ maxRows, maxLatencyMs, ... })` or rewrite the query. See `references/runtime.md`. | | `LINT.SELECT_STAR` / `LINT.NO_LIMIT` / `LINT.DELETE_WITHOUT_WHERE` / `LINT.UPDATE_WITHOUT_WHERE` / `LINT.READ_ONLY_MUTATION` | Runtime, when the `lints` middleware is active | Fix the query (add a `WHERE` / `LIMIT` / explicit columns), or relax the lint config. See `references/runtime.md`. | | `PLAN.HASH_MISMATCH` | Runtime, executing a precompiled plan | The contract the plan was built against does not match the runtime contract. Re-emit, rebuild, redeploy. | -| `RUNTIME.TEMPORAL_UNAVAILABLE` | Runtime, first read or write touching a Temporal-backed column (`DateTemporal`, `TimestampTemporal(p)`, `TimestamptzTemporal(p)`, `TimeTemporal(p)`, and `Date`, `DateTime` and `Time`) or a `temporal.updatedAt()` clock | No global `Temporal`. Node.js 26.8.2 and later ship `globalThis.Temporal`; 26.8.1 and earlier — including every 22 and 24 — do not. Either `import 'temporal-polyfill/full/global'` before the first query, or author the column as `DateString` / `TimestampString(p)` / `TimestamptzString(p)` / `TimeString(p)` to read PostgreSQL's own text. | +| `RUNTIME.TEMPORAL_UNAVAILABLE` | Runtime, first read or write touching a Temporal-backed column (`DateTemporal`, `TimestampTemporal(p)`, `TimestampInstant(p)`, `TimestamptzTemporal(p)`, `TimeTemporal(p)`, and `Date`, `DateTime` and `Time`) or a `temporal.updatedAt()` clock | No global `Temporal`. Node.js 26.8.2 and later ship `globalThis.Temporal`; 26.8.1 and earlier — including every 22 and 24 — do not. Either `import 'temporal-polyfill/full/global'` before the first query, or author the column as `DateString` / `TimestampString(p)` / `TimestamptzString(p)` / `TimeString(p)` to read PostgreSQL's own text. | | `ORM.RELATION_MUTATION_UNSUPPORTED` | ORM nested `create` / `connect` on an N:M relation whose junction has required payload columns | Write the junction table directly or use the SQL builder (`meta.junction`). | | `RUNTIME.ABORTED` (`details.phase` = `encode\|decode\|stream\|beforeEncode\|onRow`) | Runtime, when an `AbortSignal` fires mid-execute | Cancellation, not a bug; surface to the caller. | | `SqlQueryError` (no dotted code) | Raw-SQL paths surfacing a driver error | Inspect `sqlState` + `constraint` + `table` + `column`. Postgres `23505` = unique violation, `23503` = foreign-key violation, etc. Fix the data or the schema. | From e7bcbd94aa4030c9eb13344385c5ee93cdc85040 Mon Sep 17 00:00:00 2001 From: willbot Date: Thu, 22 Oct 2026 18:57:49 +0200 Subject: [PATCH 018/118] Declare temporal-spec where the published postgres types name it The postgres extension's declarations name temporal-spec types, so it and the published @prisma/orm-postgres that bundles it declare temporal-spec as a dependency. check:publish-deps refused the package before. Co-Authored-By: Claude Opus 5.5 Signed-off-by: willbot Signed-off-by: Will Madden --- packages/3-extensions/postgres/package.json | 2 +- packages/9-public/@prisma/orm-postgres/package.json | 1 + pnpm-lock.yaml | 9 ++++++--- 3 files changed, 8 insertions(+), 4 deletions(-) diff --git a/packages/3-extensions/postgres/package.json b/packages/3-extensions/postgres/package.json index b41b62a3115c..f10cf904c768 100644 --- a/packages/3-extensions/postgres/package.json +++ b/packages/3-extensions/postgres/package.json @@ -40,6 +40,7 @@ "arktype": "catalog:", "pathe": "^2.0.3", "pg": "catalog:", + "temporal-spec": "1.0.1", "tinyglobby": "catalog:" }, "devDependencies": { @@ -52,7 +53,6 @@ "@repo/tsconfig": "workspace:8.0.0-rc.19", "@repo/tsdown": "workspace:8.0.0-rc.19", "temporal-polyfill": "^1.0.4", - "temporal-spec": "1.0.1", "tsdown": "catalog:", "typescript": "catalog:", "vitest": "catalog:" diff --git a/packages/9-public/@prisma/orm-postgres/package.json b/packages/9-public/@prisma/orm-postgres/package.json index 05f63bc92397..793ffb311f76 100644 --- a/packages/9-public/@prisma/orm-postgres/package.json +++ b/packages/9-public/@prisma/orm-postgres/package.json @@ -25,6 +25,7 @@ "arktype": "catalog:", "pathe": "^2.0.3", "pg": "catalog:", + "temporal-spec": "1.0.1", "tinyglobby": "catalog:" }, "devDependencies": { diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 15218299e239..301098bdd880 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -3529,6 +3529,9 @@ importers: pg: specifier: 'catalog:' version: 8.22.0 + temporal-spec: + specifier: 1.0.1 + version: 1.0.1 tinyglobby: specifier: 'catalog:' version: 0.2.17 @@ -3560,9 +3563,6 @@ importers: temporal-polyfill: specifier: ^1.0.4 version: 1.0.4 - temporal-spec: - specifier: 1.0.1 - version: 1.0.1 tsdown: specifier: 'catalog:' version: 0.22.14(tsx@4.23.12)(typescript@5.9.3) @@ -5153,6 +5153,9 @@ importers: temporal-polyfill: specifier: ^1.0.4 version: 1.0.4 + temporal-spec: + specifier: 1.0.1 + version: 1.0.1 tinyglobby: specifier: 'catalog:' version: 0.2.17 From c4df676370cc0d3ef12d274666ff85b1e7c89401 Mon Sep 17 00:00:00 2001 From: willbot Date: Thu, 22 Oct 2026 18:59:41 +0200 Subject: [PATCH 019/118] Remove the unused branded Timestamp, Timestamptz and Time types No column was typed by them. Every contract.d.ts that still imported them, or the removed Interval, drops those import names: two hand-kept fixtures and the migration snapshots under examples and the telemetry backend. The upgrade instructions tell consumers to do the same, with a detection that also matches an inline type import. Co-Authored-By: Claude Opus 5.5 Signed-off-by: willbot Signed-off-by: Will Madden --- .../contract.d.ts | 4 ---- .../contract.d.ts | 4 ---- .../contract.d.ts | 4 ---- .../contract.d.ts | 4 ---- .../contract.d.ts | 4 ---- .../contract.d.ts | 4 ---- .../contract.d.ts | 4 ---- .../contract.d.ts | 4 ---- .../contract.d.ts | 4 ---- .../contract.d.ts | 4 ---- .../contract.d.ts | 4 ---- .../contract.d.ts | 4 ---- .../contract.d.ts | 4 ---- .../contract.d.ts | 4 ---- .../contract.d.ts | 4 ---- .../contract.d.ts | 4 ---- .../contract.d.ts | 4 ---- .../contract.d.ts | 4 ---- .../contract.d.ts | 4 ---- .../contract.d.ts | 4 ---- .../contract.d.ts | 4 ---- .../contract.d.ts | 4 ---- .../contract.d.ts | 4 ---- .../contract.d.ts | 4 ---- .../contract.d.ts | 4 ---- .../contract.d.ts | 4 ---- .../contract.d.ts | 4 ---- .../contract.d.ts | 4 ---- .../contract.d.ts | 4 ---- .../contract.d.ts | 4 ---- .../contract.d.ts | 4 ---- .../contract.d.ts | 4 ---- .../contract.d.ts | 4 ---- .../contract.d.ts | 4 ---- .../contract.d.ts | 4 ---- .../contract.d.ts | 4 ---- .../contract.d.ts | 4 ---- .../contract.d.ts | 4 ---- .../contract.d.ts | 4 ---- .../contract.d.ts | 4 ---- .../contract.d.ts | 4 ---- .../contract.d.ts | 4 ---- .../contract.d.ts | 4 ---- .../contract.d.ts | 4 ---- .../contract.d.ts | 4 ---- .../contract.d.ts | 4 ---- .../contract.d.ts | 4 ---- .../contract.d.ts | 4 ---- .../contract.d.ts | 4 ---- .../postgres/src/exports/codec-types.ts | 7 ------ .../test/fixtures/namespaced-contract.d.ts | 3 --- .../fixtures/generated/sql-contract.d.ts | 3 --- .../app/instructions.md | 24 ++++++++++++++++--- .../extension/instructions.md | 22 +++++++++++++++-- 54 files changed, 41 insertions(+), 214 deletions(-) diff --git a/apps/telemetry-backend/migrations/snapshots/269d0430c7c70fa502e75feb52d9219c0339daa041c6cc9fb4ecb5ad8d0373b6/contract.d.ts b/apps/telemetry-backend/migrations/snapshots/269d0430c7c70fa502e75feb52d9219c0339daa041c6cc9fb4ecb5ad8d0373b6/contract.d.ts index 30ca7a3ef8e6..5702ea4de0b9 100644 --- a/apps/telemetry-backend/migrations/snapshots/269d0430c7c70fa502e75feb52d9219c0339daa041c6cc9fb4ecb5ad8d0373b6/contract.d.ts +++ b/apps/telemetry-backend/migrations/snapshots/269d0430c7c70fa502e75feb52d9219c0339daa041c6cc9fb4ecb5ad8d0373b6/contract.d.ts @@ -6,12 +6,8 @@ import type { Bit, Char, CodecTypes as PgTypes, - Interval, JsonValue, Numeric, - Time, - Timestamp, - Timestamptz, Timetz, VarBit, Varchar, diff --git a/examples/prisma-8-demo/fixtures/converging-branches/migrations/snapshots/6c26c85a74b9429d6bb929df0c3e5035352fdb4cc2ee0e711a7a215e490041b1/contract.d.ts b/examples/prisma-8-demo/fixtures/converging-branches/migrations/snapshots/6c26c85a74b9429d6bb929df0c3e5035352fdb4cc2ee0e711a7a215e490041b1/contract.d.ts index 15023119ed7a..ab185e4a2d53 100644 --- a/examples/prisma-8-demo/fixtures/converging-branches/migrations/snapshots/6c26c85a74b9429d6bb929df0c3e5035352fdb4cc2ee0e711a7a215e490041b1/contract.d.ts +++ b/examples/prisma-8-demo/fixtures/converging-branches/migrations/snapshots/6c26c85a74b9429d6bb929df0c3e5035352fdb4cc2ee0e711a7a215e490041b1/contract.d.ts @@ -6,12 +6,8 @@ import type { Bit, Char, CodecTypes as PgTypes, - Interval, JsonValue, Numeric, - Time, - Timestamp, - Timestamptz, Timetz, VarBit, Varchar, diff --git a/examples/prisma-8-demo/fixtures/converging-branches/migrations/snapshots/7b7a028bf17c118bdec714439df6456964c13d3d4a4dbd14869b888412a0bd01/contract.d.ts b/examples/prisma-8-demo/fixtures/converging-branches/migrations/snapshots/7b7a028bf17c118bdec714439df6456964c13d3d4a4dbd14869b888412a0bd01/contract.d.ts index 5fe6a104599a..eaac5bbcadb5 100644 --- a/examples/prisma-8-demo/fixtures/converging-branches/migrations/snapshots/7b7a028bf17c118bdec714439df6456964c13d3d4a4dbd14869b888412a0bd01/contract.d.ts +++ b/examples/prisma-8-demo/fixtures/converging-branches/migrations/snapshots/7b7a028bf17c118bdec714439df6456964c13d3d4a4dbd14869b888412a0bd01/contract.d.ts @@ -6,12 +6,8 @@ import type { Bit, Char, CodecTypes as PgTypes, - Interval, JsonValue, Numeric, - Time, - Timestamp, - Timestamptz, Timetz, VarBit, Varchar, diff --git a/examples/prisma-8-demo/fixtures/converging-branches/migrations/snapshots/df82ed1c905933192eca7c1d92ac6f1f8ada22d1d712865e5afd4400c554de2e/contract.d.ts b/examples/prisma-8-demo/fixtures/converging-branches/migrations/snapshots/df82ed1c905933192eca7c1d92ac6f1f8ada22d1d712865e5afd4400c554de2e/contract.d.ts index 468ba2b96c8d..abfa79686b19 100644 --- a/examples/prisma-8-demo/fixtures/converging-branches/migrations/snapshots/df82ed1c905933192eca7c1d92ac6f1f8ada22d1d712865e5afd4400c554de2e/contract.d.ts +++ b/examples/prisma-8-demo/fixtures/converging-branches/migrations/snapshots/df82ed1c905933192eca7c1d92ac6f1f8ada22d1d712865e5afd4400c554de2e/contract.d.ts @@ -6,12 +6,8 @@ import type { Bit, Char, CodecTypes as PgTypes, - Interval, JsonValue, Numeric, - Time, - Timestamp, - Timestamptz, Timetz, VarBit, Varchar, diff --git a/examples/prisma-8-demo/fixtures/converging-branches/migrations/snapshots/f6b0a58b2f0675a0d020673b1ac1713f6794c66773c1b33e2a2014c6a61f9a06/contract.d.ts b/examples/prisma-8-demo/fixtures/converging-branches/migrations/snapshots/f6b0a58b2f0675a0d020673b1ac1713f6794c66773c1b33e2a2014c6a61f9a06/contract.d.ts index 86d9111e492d..c847c1e829c8 100644 --- a/examples/prisma-8-demo/fixtures/converging-branches/migrations/snapshots/f6b0a58b2f0675a0d020673b1ac1713f6794c66773c1b33e2a2014c6a61f9a06/contract.d.ts +++ b/examples/prisma-8-demo/fixtures/converging-branches/migrations/snapshots/f6b0a58b2f0675a0d020673b1ac1713f6794c66773c1b33e2a2014c6a61f9a06/contract.d.ts @@ -6,12 +6,8 @@ import type { Bit, Char, CodecTypes as PgTypes, - Interval, JsonValue, Numeric, - Time, - Timestamp, - Timestamptz, Timetz, VarBit, Varchar, diff --git a/examples/prisma-8-demo/fixtures/converging-branches/migrations/snapshots/f984b41c72c9ae805561bd27891ef309f95bb5eda852892fc3e37b620f897b38/contract.d.ts b/examples/prisma-8-demo/fixtures/converging-branches/migrations/snapshots/f984b41c72c9ae805561bd27891ef309f95bb5eda852892fc3e37b620f897b38/contract.d.ts index 39fb6c3f61d6..2c64de55deb8 100644 --- a/examples/prisma-8-demo/fixtures/converging-branches/migrations/snapshots/f984b41c72c9ae805561bd27891ef309f95bb5eda852892fc3e37b620f897b38/contract.d.ts +++ b/examples/prisma-8-demo/fixtures/converging-branches/migrations/snapshots/f984b41c72c9ae805561bd27891ef309f95bb5eda852892fc3e37b620f897b38/contract.d.ts @@ -6,12 +6,8 @@ import type { Bit, Char, CodecTypes as PgTypes, - Interval, JsonValue, Numeric, - Time, - Timestamp, - Timestamptz, Timetz, VarBit, Varchar, diff --git a/examples/prisma-8-demo/fixtures/diamond/migrations/snapshots/6b4636f85cc2d7b2f8d4a70bd2c520246b4bbe786aa55d3b77592fae5c4d458f/contract.d.ts b/examples/prisma-8-demo/fixtures/diamond/migrations/snapshots/6b4636f85cc2d7b2f8d4a70bd2c520246b4bbe786aa55d3b77592fae5c4d458f/contract.d.ts index c61e89011f50..bc9abcd0a92f 100644 --- a/examples/prisma-8-demo/fixtures/diamond/migrations/snapshots/6b4636f85cc2d7b2f8d4a70bd2c520246b4bbe786aa55d3b77592fae5c4d458f/contract.d.ts +++ b/examples/prisma-8-demo/fixtures/diamond/migrations/snapshots/6b4636f85cc2d7b2f8d4a70bd2c520246b4bbe786aa55d3b77592fae5c4d458f/contract.d.ts @@ -6,12 +6,8 @@ import type { Bit, Char, CodecTypes as PgTypes, - Interval, JsonValue, Numeric, - Time, - Timestamp, - Timestamptz, Timetz, VarBit, Varchar, diff --git a/examples/prisma-8-demo/fixtures/diamond/migrations/snapshots/6c26c85a74b9429d6bb929df0c3e5035352fdb4cc2ee0e711a7a215e490041b1/contract.d.ts b/examples/prisma-8-demo/fixtures/diamond/migrations/snapshots/6c26c85a74b9429d6bb929df0c3e5035352fdb4cc2ee0e711a7a215e490041b1/contract.d.ts index 15023119ed7a..ab185e4a2d53 100644 --- a/examples/prisma-8-demo/fixtures/diamond/migrations/snapshots/6c26c85a74b9429d6bb929df0c3e5035352fdb4cc2ee0e711a7a215e490041b1/contract.d.ts +++ b/examples/prisma-8-demo/fixtures/diamond/migrations/snapshots/6c26c85a74b9429d6bb929df0c3e5035352fdb4cc2ee0e711a7a215e490041b1/contract.d.ts @@ -6,12 +6,8 @@ import type { Bit, Char, CodecTypes as PgTypes, - Interval, JsonValue, Numeric, - Time, - Timestamp, - Timestamptz, Timetz, VarBit, Varchar, diff --git a/examples/prisma-8-demo/fixtures/diamond/migrations/snapshots/df82ed1c905933192eca7c1d92ac6f1f8ada22d1d712865e5afd4400c554de2e/contract.d.ts b/examples/prisma-8-demo/fixtures/diamond/migrations/snapshots/df82ed1c905933192eca7c1d92ac6f1f8ada22d1d712865e5afd4400c554de2e/contract.d.ts index 468ba2b96c8d..abfa79686b19 100644 --- a/examples/prisma-8-demo/fixtures/diamond/migrations/snapshots/df82ed1c905933192eca7c1d92ac6f1f8ada22d1d712865e5afd4400c554de2e/contract.d.ts +++ b/examples/prisma-8-demo/fixtures/diamond/migrations/snapshots/df82ed1c905933192eca7c1d92ac6f1f8ada22d1d712865e5afd4400c554de2e/contract.d.ts @@ -6,12 +6,8 @@ import type { Bit, Char, CodecTypes as PgTypes, - Interval, JsonValue, Numeric, - Time, - Timestamp, - Timestamptz, Timetz, VarBit, Varchar, diff --git a/examples/prisma-8-demo/fixtures/diamond/migrations/snapshots/f984b41c72c9ae805561bd27891ef309f95bb5eda852892fc3e37b620f897b38/contract.d.ts b/examples/prisma-8-demo/fixtures/diamond/migrations/snapshots/f984b41c72c9ae805561bd27891ef309f95bb5eda852892fc3e37b620f897b38/contract.d.ts index 39fb6c3f61d6..2c64de55deb8 100644 --- a/examples/prisma-8-demo/fixtures/diamond/migrations/snapshots/f984b41c72c9ae805561bd27891ef309f95bb5eda852892fc3e37b620f897b38/contract.d.ts +++ b/examples/prisma-8-demo/fixtures/diamond/migrations/snapshots/f984b41c72c9ae805561bd27891ef309f95bb5eda852892fc3e37b620f897b38/contract.d.ts @@ -6,12 +6,8 @@ import type { Bit, Char, CodecTypes as PgTypes, - Interval, JsonValue, Numeric, - Time, - Timestamp, - Timestamptz, Timetz, VarBit, Varchar, diff --git a/examples/prisma-8-demo/fixtures/long-spine/migrations/snapshots/3539138011365388d750c8a37a053403de07308eb1d872451a33d9655c9f1dde/contract.d.ts b/examples/prisma-8-demo/fixtures/long-spine/migrations/snapshots/3539138011365388d750c8a37a053403de07308eb1d872451a33d9655c9f1dde/contract.d.ts index 2a8a9c82aced..7221cd7ca875 100644 --- a/examples/prisma-8-demo/fixtures/long-spine/migrations/snapshots/3539138011365388d750c8a37a053403de07308eb1d872451a33d9655c9f1dde/contract.d.ts +++ b/examples/prisma-8-demo/fixtures/long-spine/migrations/snapshots/3539138011365388d750c8a37a053403de07308eb1d872451a33d9655c9f1dde/contract.d.ts @@ -6,12 +6,8 @@ import type { Bit, Char, CodecTypes as PgTypes, - Interval, JsonValue, Numeric, - Time, - Timestamp, - Timestamptz, Timetz, VarBit, Varchar, diff --git a/examples/prisma-8-demo/fixtures/long-spine/migrations/snapshots/4ae263c38bb0abf30b3daf57d2b002997c15260b0e5528bd924969c25f3d00bb/contract.d.ts b/examples/prisma-8-demo/fixtures/long-spine/migrations/snapshots/4ae263c38bb0abf30b3daf57d2b002997c15260b0e5528bd924969c25f3d00bb/contract.d.ts index 810513406e1c..4273a713670d 100644 --- a/examples/prisma-8-demo/fixtures/long-spine/migrations/snapshots/4ae263c38bb0abf30b3daf57d2b002997c15260b0e5528bd924969c25f3d00bb/contract.d.ts +++ b/examples/prisma-8-demo/fixtures/long-spine/migrations/snapshots/4ae263c38bb0abf30b3daf57d2b002997c15260b0e5528bd924969c25f3d00bb/contract.d.ts @@ -6,12 +6,8 @@ import type { Bit, Char, CodecTypes as PgTypes, - Interval, JsonValue, Numeric, - Time, - Timestamp, - Timestamptz, Timetz, VarBit, Varchar, diff --git a/examples/prisma-8-demo/fixtures/long-spine/migrations/snapshots/6c26c85a74b9429d6bb929df0c3e5035352fdb4cc2ee0e711a7a215e490041b1/contract.d.ts b/examples/prisma-8-demo/fixtures/long-spine/migrations/snapshots/6c26c85a74b9429d6bb929df0c3e5035352fdb4cc2ee0e711a7a215e490041b1/contract.d.ts index 15023119ed7a..ab185e4a2d53 100644 --- a/examples/prisma-8-demo/fixtures/long-spine/migrations/snapshots/6c26c85a74b9429d6bb929df0c3e5035352fdb4cc2ee0e711a7a215e490041b1/contract.d.ts +++ b/examples/prisma-8-demo/fixtures/long-spine/migrations/snapshots/6c26c85a74b9429d6bb929df0c3e5035352fdb4cc2ee0e711a7a215e490041b1/contract.d.ts @@ -6,12 +6,8 @@ import type { Bit, Char, CodecTypes as PgTypes, - Interval, JsonValue, Numeric, - Time, - Timestamp, - Timestamptz, Timetz, VarBit, Varchar, diff --git a/examples/prisma-8-demo/fixtures/long-spine/migrations/snapshots/6dbf0d57fcfe6e0235a0dfa6cc07b1162c6f1f7656b281db92a5d7fb522b2e22/contract.d.ts b/examples/prisma-8-demo/fixtures/long-spine/migrations/snapshots/6dbf0d57fcfe6e0235a0dfa6cc07b1162c6f1f7656b281db92a5d7fb522b2e22/contract.d.ts index 639dffa2f228..29efeb739d50 100644 --- a/examples/prisma-8-demo/fixtures/long-spine/migrations/snapshots/6dbf0d57fcfe6e0235a0dfa6cc07b1162c6f1f7656b281db92a5d7fb522b2e22/contract.d.ts +++ b/examples/prisma-8-demo/fixtures/long-spine/migrations/snapshots/6dbf0d57fcfe6e0235a0dfa6cc07b1162c6f1f7656b281db92a5d7fb522b2e22/contract.d.ts @@ -6,12 +6,8 @@ import type { Bit, Char, CodecTypes as PgTypes, - Interval, JsonValue, Numeric, - Time, - Timestamp, - Timestamptz, Timetz, VarBit, Varchar, diff --git a/examples/prisma-8-demo/fixtures/long-spine/migrations/snapshots/b0a91f687675c5a96fb07e17b02fbb9ed5b873e2c0152b9985020f24f12a6109/contract.d.ts b/examples/prisma-8-demo/fixtures/long-spine/migrations/snapshots/b0a91f687675c5a96fb07e17b02fbb9ed5b873e2c0152b9985020f24f12a6109/contract.d.ts index d0aef3a7d109..d3e70ef6400d 100644 --- a/examples/prisma-8-demo/fixtures/long-spine/migrations/snapshots/b0a91f687675c5a96fb07e17b02fbb9ed5b873e2c0152b9985020f24f12a6109/contract.d.ts +++ b/examples/prisma-8-demo/fixtures/long-spine/migrations/snapshots/b0a91f687675c5a96fb07e17b02fbb9ed5b873e2c0152b9985020f24f12a6109/contract.d.ts @@ -6,12 +6,8 @@ import type { Bit, Char, CodecTypes as PgTypes, - Interval, JsonValue, Numeric, - Time, - Timestamp, - Timestamptz, Timetz, VarBit, Varchar, diff --git a/examples/prisma-8-demo/fixtures/long-spine/migrations/snapshots/c5aeaafc168786f8e7ac8bb9b14b6469610f67cd640c6f2d289922104545d7a0/contract.d.ts b/examples/prisma-8-demo/fixtures/long-spine/migrations/snapshots/c5aeaafc168786f8e7ac8bb9b14b6469610f67cd640c6f2d289922104545d7a0/contract.d.ts index 86ec73885572..f0e6a7969417 100644 --- a/examples/prisma-8-demo/fixtures/long-spine/migrations/snapshots/c5aeaafc168786f8e7ac8bb9b14b6469610f67cd640c6f2d289922104545d7a0/contract.d.ts +++ b/examples/prisma-8-demo/fixtures/long-spine/migrations/snapshots/c5aeaafc168786f8e7ac8bb9b14b6469610f67cd640c6f2d289922104545d7a0/contract.d.ts @@ -6,12 +6,8 @@ import type { Bit, Char, CodecTypes as PgTypes, - Interval, JsonValue, Numeric, - Time, - Timestamp, - Timestamptz, Timetz, VarBit, Varchar, diff --git a/examples/prisma-8-demo/fixtures/long-spine/migrations/snapshots/d5fdae79e33040f9cc33f8c56b9a65f1ce1f86d941198f9d6281abe88d762a55/contract.d.ts b/examples/prisma-8-demo/fixtures/long-spine/migrations/snapshots/d5fdae79e33040f9cc33f8c56b9a65f1ce1f86d941198f9d6281abe88d762a55/contract.d.ts index 52a7f1183e58..e20f335898ce 100644 --- a/examples/prisma-8-demo/fixtures/long-spine/migrations/snapshots/d5fdae79e33040f9cc33f8c56b9a65f1ce1f86d941198f9d6281abe88d762a55/contract.d.ts +++ b/examples/prisma-8-demo/fixtures/long-spine/migrations/snapshots/d5fdae79e33040f9cc33f8c56b9a65f1ce1f86d941198f9d6281abe88d762a55/contract.d.ts @@ -6,12 +6,8 @@ import type { Bit, Char, CodecTypes as PgTypes, - Interval, JsonValue, Numeric, - Time, - Timestamp, - Timestamptz, Timetz, VarBit, Varchar, diff --git a/examples/prisma-8-demo/fixtures/long-spine/migrations/snapshots/df82ed1c905933192eca7c1d92ac6f1f8ada22d1d712865e5afd4400c554de2e/contract.d.ts b/examples/prisma-8-demo/fixtures/long-spine/migrations/snapshots/df82ed1c905933192eca7c1d92ac6f1f8ada22d1d712865e5afd4400c554de2e/contract.d.ts index 468ba2b96c8d..abfa79686b19 100644 --- a/examples/prisma-8-demo/fixtures/long-spine/migrations/snapshots/df82ed1c905933192eca7c1d92ac6f1f8ada22d1d712865e5afd4400c554de2e/contract.d.ts +++ b/examples/prisma-8-demo/fixtures/long-spine/migrations/snapshots/df82ed1c905933192eca7c1d92ac6f1f8ada22d1d712865e5afd4400c554de2e/contract.d.ts @@ -6,12 +6,8 @@ import type { Bit, Char, CodecTypes as PgTypes, - Interval, JsonValue, Numeric, - Time, - Timestamp, - Timestamptz, Timetz, VarBit, Varchar, diff --git a/examples/prisma-8-demo/fixtures/long-spine/migrations/snapshots/f778f751881b602d067b69950a97081f5850d76e8edeee9c16cf14245114224e/contract.d.ts b/examples/prisma-8-demo/fixtures/long-spine/migrations/snapshots/f778f751881b602d067b69950a97081f5850d76e8edeee9c16cf14245114224e/contract.d.ts index 6e3557a8ee1d..1899c3b1d2e4 100644 --- a/examples/prisma-8-demo/fixtures/long-spine/migrations/snapshots/f778f751881b602d067b69950a97081f5850d76e8edeee9c16cf14245114224e/contract.d.ts +++ b/examples/prisma-8-demo/fixtures/long-spine/migrations/snapshots/f778f751881b602d067b69950a97081f5850d76e8edeee9c16cf14245114224e/contract.d.ts @@ -6,12 +6,8 @@ import type { Bit, Char, CodecTypes as PgTypes, - Interval, JsonValue, Numeric, - Time, - Timestamp, - Timestamptz, Timetz, VarBit, Varchar, diff --git a/examples/prisma-8-demo/fixtures/multi-branch/migrations/snapshots/10cdd020e0f2e31ec61d010c58b77fbed5fae1c1919e21839ba1d2bb82b6aef6/contract.d.ts b/examples/prisma-8-demo/fixtures/multi-branch/migrations/snapshots/10cdd020e0f2e31ec61d010c58b77fbed5fae1c1919e21839ba1d2bb82b6aef6/contract.d.ts index 72ecfe3fb4a0..d473d9ae02cd 100644 --- a/examples/prisma-8-demo/fixtures/multi-branch/migrations/snapshots/10cdd020e0f2e31ec61d010c58b77fbed5fae1c1919e21839ba1d2bb82b6aef6/contract.d.ts +++ b/examples/prisma-8-demo/fixtures/multi-branch/migrations/snapshots/10cdd020e0f2e31ec61d010c58b77fbed5fae1c1919e21839ba1d2bb82b6aef6/contract.d.ts @@ -6,12 +6,8 @@ import type { Bit, Char, CodecTypes as PgTypes, - Interval, JsonValue, Numeric, - Time, - Timestamp, - Timestamptz, Timetz, VarBit, Varchar, diff --git a/examples/prisma-8-demo/fixtures/multi-branch/migrations/snapshots/4ae263c38bb0abf30b3daf57d2b002997c15260b0e5528bd924969c25f3d00bb/contract.d.ts b/examples/prisma-8-demo/fixtures/multi-branch/migrations/snapshots/4ae263c38bb0abf30b3daf57d2b002997c15260b0e5528bd924969c25f3d00bb/contract.d.ts index 810513406e1c..4273a713670d 100644 --- a/examples/prisma-8-demo/fixtures/multi-branch/migrations/snapshots/4ae263c38bb0abf30b3daf57d2b002997c15260b0e5528bd924969c25f3d00bb/contract.d.ts +++ b/examples/prisma-8-demo/fixtures/multi-branch/migrations/snapshots/4ae263c38bb0abf30b3daf57d2b002997c15260b0e5528bd924969c25f3d00bb/contract.d.ts @@ -6,12 +6,8 @@ import type { Bit, Char, CodecTypes as PgTypes, - Interval, JsonValue, Numeric, - Time, - Timestamp, - Timestamptz, Timetz, VarBit, Varchar, diff --git a/examples/prisma-8-demo/fixtures/multi-branch/migrations/snapshots/6c26c85a74b9429d6bb929df0c3e5035352fdb4cc2ee0e711a7a215e490041b1/contract.d.ts b/examples/prisma-8-demo/fixtures/multi-branch/migrations/snapshots/6c26c85a74b9429d6bb929df0c3e5035352fdb4cc2ee0e711a7a215e490041b1/contract.d.ts index 15023119ed7a..ab185e4a2d53 100644 --- a/examples/prisma-8-demo/fixtures/multi-branch/migrations/snapshots/6c26c85a74b9429d6bb929df0c3e5035352fdb4cc2ee0e711a7a215e490041b1/contract.d.ts +++ b/examples/prisma-8-demo/fixtures/multi-branch/migrations/snapshots/6c26c85a74b9429d6bb929df0c3e5035352fdb4cc2ee0e711a7a215e490041b1/contract.d.ts @@ -6,12 +6,8 @@ import type { Bit, Char, CodecTypes as PgTypes, - Interval, JsonValue, Numeric, - Time, - Timestamp, - Timestamptz, Timetz, VarBit, Varchar, diff --git a/examples/prisma-8-demo/fixtures/multi-branch/migrations/snapshots/7b7a028bf17c118bdec714439df6456964c13d3d4a4dbd14869b888412a0bd01/contract.d.ts b/examples/prisma-8-demo/fixtures/multi-branch/migrations/snapshots/7b7a028bf17c118bdec714439df6456964c13d3d4a4dbd14869b888412a0bd01/contract.d.ts index 5fe6a104599a..eaac5bbcadb5 100644 --- a/examples/prisma-8-demo/fixtures/multi-branch/migrations/snapshots/7b7a028bf17c118bdec714439df6456964c13d3d4a4dbd14869b888412a0bd01/contract.d.ts +++ b/examples/prisma-8-demo/fixtures/multi-branch/migrations/snapshots/7b7a028bf17c118bdec714439df6456964c13d3d4a4dbd14869b888412a0bd01/contract.d.ts @@ -6,12 +6,8 @@ import type { Bit, Char, CodecTypes as PgTypes, - Interval, JsonValue, Numeric, - Time, - Timestamp, - Timestamptz, Timetz, VarBit, Varchar, diff --git a/examples/prisma-8-demo/fixtures/multi-branch/migrations/snapshots/df82ed1c905933192eca7c1d92ac6f1f8ada22d1d712865e5afd4400c554de2e/contract.d.ts b/examples/prisma-8-demo/fixtures/multi-branch/migrations/snapshots/df82ed1c905933192eca7c1d92ac6f1f8ada22d1d712865e5afd4400c554de2e/contract.d.ts index 468ba2b96c8d..abfa79686b19 100644 --- a/examples/prisma-8-demo/fixtures/multi-branch/migrations/snapshots/df82ed1c905933192eca7c1d92ac6f1f8ada22d1d712865e5afd4400c554de2e/contract.d.ts +++ b/examples/prisma-8-demo/fixtures/multi-branch/migrations/snapshots/df82ed1c905933192eca7c1d92ac6f1f8ada22d1d712865e5afd4400c554de2e/contract.d.ts @@ -6,12 +6,8 @@ import type { Bit, Char, CodecTypes as PgTypes, - Interval, JsonValue, Numeric, - Time, - Timestamp, - Timestamptz, Timetz, VarBit, Varchar, diff --git a/examples/prisma-8-demo/fixtures/multi-branch/migrations/snapshots/f984b41c72c9ae805561bd27891ef309f95bb5eda852892fc3e37b620f897b38/contract.d.ts b/examples/prisma-8-demo/fixtures/multi-branch/migrations/snapshots/f984b41c72c9ae805561bd27891ef309f95bb5eda852892fc3e37b620f897b38/contract.d.ts index 39fb6c3f61d6..2c64de55deb8 100644 --- a/examples/prisma-8-demo/fixtures/multi-branch/migrations/snapshots/f984b41c72c9ae805561bd27891ef309f95bb5eda852892fc3e37b620f897b38/contract.d.ts +++ b/examples/prisma-8-demo/fixtures/multi-branch/migrations/snapshots/f984b41c72c9ae805561bd27891ef309f95bb5eda852892fc3e37b620f897b38/contract.d.ts @@ -6,12 +6,8 @@ import type { Bit, Char, CodecTypes as PgTypes, - Interval, JsonValue, Numeric, - Time, - Timestamp, - Timestamptz, Timetz, VarBit, Varchar, diff --git a/examples/prisma-8-demo/fixtures/showcase/migrations/snapshots/0ad62eb8f8ec2138d5d3a1be39a3e6f4221b4652b3fad9ef35d2e2a35404714f/contract.d.ts b/examples/prisma-8-demo/fixtures/showcase/migrations/snapshots/0ad62eb8f8ec2138d5d3a1be39a3e6f4221b4652b3fad9ef35d2e2a35404714f/contract.d.ts index 8c0f986f5bde..eb1223b34f5e 100644 --- a/examples/prisma-8-demo/fixtures/showcase/migrations/snapshots/0ad62eb8f8ec2138d5d3a1be39a3e6f4221b4652b3fad9ef35d2e2a35404714f/contract.d.ts +++ b/examples/prisma-8-demo/fixtures/showcase/migrations/snapshots/0ad62eb8f8ec2138d5d3a1be39a3e6f4221b4652b3fad9ef35d2e2a35404714f/contract.d.ts @@ -6,12 +6,8 @@ import type { Bit, Char, CodecTypes as PgTypes, - Interval, JsonValue, Numeric, - Time, - Timestamp, - Timestamptz, Timetz, VarBit, Varchar, diff --git a/examples/prisma-8-demo/fixtures/showcase/migrations/snapshots/0d01a7e1c00acc052445624ecfc1542bbee85bdbd8d531c69b826b0a6698e14e/contract.d.ts b/examples/prisma-8-demo/fixtures/showcase/migrations/snapshots/0d01a7e1c00acc052445624ecfc1542bbee85bdbd8d531c69b826b0a6698e14e/contract.d.ts index 19f78df89635..70ff59f5eeae 100644 --- a/examples/prisma-8-demo/fixtures/showcase/migrations/snapshots/0d01a7e1c00acc052445624ecfc1542bbee85bdbd8d531c69b826b0a6698e14e/contract.d.ts +++ b/examples/prisma-8-demo/fixtures/showcase/migrations/snapshots/0d01a7e1c00acc052445624ecfc1542bbee85bdbd8d531c69b826b0a6698e14e/contract.d.ts @@ -6,12 +6,8 @@ import type { Bit, Char, CodecTypes as PgTypes, - Interval, JsonValue, Numeric, - Time, - Timestamp, - Timestamptz, Timetz, VarBit, Varchar, diff --git a/examples/prisma-8-demo/fixtures/showcase/migrations/snapshots/1fb95fa184d8fbead2f8147baf16641eaeabc51459616611882ee00d85addea3/contract.d.ts b/examples/prisma-8-demo/fixtures/showcase/migrations/snapshots/1fb95fa184d8fbead2f8147baf16641eaeabc51459616611882ee00d85addea3/contract.d.ts index e05beecf4cd3..39ff7691057d 100644 --- a/examples/prisma-8-demo/fixtures/showcase/migrations/snapshots/1fb95fa184d8fbead2f8147baf16641eaeabc51459616611882ee00d85addea3/contract.d.ts +++ b/examples/prisma-8-demo/fixtures/showcase/migrations/snapshots/1fb95fa184d8fbead2f8147baf16641eaeabc51459616611882ee00d85addea3/contract.d.ts @@ -6,12 +6,8 @@ import type { Bit, Char, CodecTypes as PgTypes, - Interval, JsonValue, Numeric, - Time, - Timestamp, - Timestamptz, Timetz, VarBit, Varchar, diff --git a/examples/prisma-8-demo/fixtures/showcase/migrations/snapshots/23371687751c218d354034a564d7709da7ac6af8dd54b37f1a2b9af4e5140073/contract.d.ts b/examples/prisma-8-demo/fixtures/showcase/migrations/snapshots/23371687751c218d354034a564d7709da7ac6af8dd54b37f1a2b9af4e5140073/contract.d.ts index f75c72e008a9..c399f5a0971f 100644 --- a/examples/prisma-8-demo/fixtures/showcase/migrations/snapshots/23371687751c218d354034a564d7709da7ac6af8dd54b37f1a2b9af4e5140073/contract.d.ts +++ b/examples/prisma-8-demo/fixtures/showcase/migrations/snapshots/23371687751c218d354034a564d7709da7ac6af8dd54b37f1a2b9af4e5140073/contract.d.ts @@ -6,12 +6,8 @@ import type { Bit, Char, CodecTypes as PgTypes, - Interval, JsonValue, Numeric, - Time, - Timestamp, - Timestamptz, Timetz, VarBit, Varchar, diff --git a/examples/prisma-8-demo/fixtures/showcase/migrations/snapshots/76f9a7585ee104f29508fd0ba31f78665ef252d0a42d9d78e1f0ad1ff4dfbe01/contract.d.ts b/examples/prisma-8-demo/fixtures/showcase/migrations/snapshots/76f9a7585ee104f29508fd0ba31f78665ef252d0a42d9d78e1f0ad1ff4dfbe01/contract.d.ts index 7d75c309c276..26c865aad5ab 100644 --- a/examples/prisma-8-demo/fixtures/showcase/migrations/snapshots/76f9a7585ee104f29508fd0ba31f78665ef252d0a42d9d78e1f0ad1ff4dfbe01/contract.d.ts +++ b/examples/prisma-8-demo/fixtures/showcase/migrations/snapshots/76f9a7585ee104f29508fd0ba31f78665ef252d0a42d9d78e1f0ad1ff4dfbe01/contract.d.ts @@ -6,12 +6,8 @@ import type { Bit, Char, CodecTypes as PgTypes, - Interval, JsonValue, Numeric, - Time, - Timestamp, - Timestamptz, Timetz, VarBit, Varchar, diff --git a/examples/prisma-8-demo/fixtures/showcase/migrations/snapshots/7999fa221ce9c00f04321344eb006a39bd4485ba5a913c8964ae6c1fb7a8049d/contract.d.ts b/examples/prisma-8-demo/fixtures/showcase/migrations/snapshots/7999fa221ce9c00f04321344eb006a39bd4485ba5a913c8964ae6c1fb7a8049d/contract.d.ts index 4fc48a4c4047..507c2b9b6254 100644 --- a/examples/prisma-8-demo/fixtures/showcase/migrations/snapshots/7999fa221ce9c00f04321344eb006a39bd4485ba5a913c8964ae6c1fb7a8049d/contract.d.ts +++ b/examples/prisma-8-demo/fixtures/showcase/migrations/snapshots/7999fa221ce9c00f04321344eb006a39bd4485ba5a913c8964ae6c1fb7a8049d/contract.d.ts @@ -6,12 +6,8 @@ import type { Bit, Char, CodecTypes as PgTypes, - Interval, JsonValue, Numeric, - Time, - Timestamp, - Timestamptz, Timetz, VarBit, Varchar, diff --git a/examples/prisma-8-demo/fixtures/showcase/migrations/snapshots/d82c665884d8cf0745edfcc24be8b374ccbe89dffb5e3e6ab10357cad4d5f03f/contract.d.ts b/examples/prisma-8-demo/fixtures/showcase/migrations/snapshots/d82c665884d8cf0745edfcc24be8b374ccbe89dffb5e3e6ab10357cad4d5f03f/contract.d.ts index 30764addb972..cfb0de1a5b4c 100644 --- a/examples/prisma-8-demo/fixtures/showcase/migrations/snapshots/d82c665884d8cf0745edfcc24be8b374ccbe89dffb5e3e6ab10357cad4d5f03f/contract.d.ts +++ b/examples/prisma-8-demo/fixtures/showcase/migrations/snapshots/d82c665884d8cf0745edfcc24be8b374ccbe89dffb5e3e6ab10357cad4d5f03f/contract.d.ts @@ -6,12 +6,8 @@ import type { Bit, Char, CodecTypes as PgTypes, - Interval, JsonValue, Numeric, - Time, - Timestamp, - Timestamptz, Timetz, VarBit, Varchar, diff --git a/examples/prisma-8-demo/fixtures/showcase/migrations/snapshots/f2501f92fe261b6e89bfcf335d7647ea5bdec8492e0587ed17dcbd63bfe81ef9/contract.d.ts b/examples/prisma-8-demo/fixtures/showcase/migrations/snapshots/f2501f92fe261b6e89bfcf335d7647ea5bdec8492e0587ed17dcbd63bfe81ef9/contract.d.ts index 45f46ad29094..89fd3af49cfd 100644 --- a/examples/prisma-8-demo/fixtures/showcase/migrations/snapshots/f2501f92fe261b6e89bfcf335d7647ea5bdec8492e0587ed17dcbd63bfe81ef9/contract.d.ts +++ b/examples/prisma-8-demo/fixtures/showcase/migrations/snapshots/f2501f92fe261b6e89bfcf335d7647ea5bdec8492e0587ed17dcbd63bfe81ef9/contract.d.ts @@ -6,12 +6,8 @@ import type { Bit, Char, CodecTypes as PgTypes, - Interval, JsonValue, Numeric, - Time, - Timestamp, - Timestamptz, Timetz, VarBit, Varchar, diff --git a/examples/prisma-8-demo/fixtures/showcase/migrations/snapshots/f894c02864e197d491f40111ceb9ec48d78de76f4462835c22a3c1f9d68f881f/contract.d.ts b/examples/prisma-8-demo/fixtures/showcase/migrations/snapshots/f894c02864e197d491f40111ceb9ec48d78de76f4462835c22a3c1f9d68f881f/contract.d.ts index b56c5c97159a..e40e51cea306 100644 --- a/examples/prisma-8-demo/fixtures/showcase/migrations/snapshots/f894c02864e197d491f40111ceb9ec48d78de76f4462835c22a3c1f9d68f881f/contract.d.ts +++ b/examples/prisma-8-demo/fixtures/showcase/migrations/snapshots/f894c02864e197d491f40111ceb9ec48d78de76f4462835c22a3c1f9d68f881f/contract.d.ts @@ -6,12 +6,8 @@ import type { Bit, Char, CodecTypes as PgTypes, - Interval, JsonValue, Numeric, - Time, - Timestamp, - Timestamptz, Timetz, VarBit, Varchar, diff --git a/examples/prisma-8-demo/fixtures/showcase/migrations/snapshots/fa509ad2ef15e0d1cd65b6b858d4729d9ac7c50ea28df3dab7cb790bc15d791d/contract.d.ts b/examples/prisma-8-demo/fixtures/showcase/migrations/snapshots/fa509ad2ef15e0d1cd65b6b858d4729d9ac7c50ea28df3dab7cb790bc15d791d/contract.d.ts index e450961c98ce..3b6411d0e500 100644 --- a/examples/prisma-8-demo/fixtures/showcase/migrations/snapshots/fa509ad2ef15e0d1cd65b6b858d4729d9ac7c50ea28df3dab7cb790bc15d791d/contract.d.ts +++ b/examples/prisma-8-demo/fixtures/showcase/migrations/snapshots/fa509ad2ef15e0d1cd65b6b858d4729d9ac7c50ea28df3dab7cb790bc15d791d/contract.d.ts @@ -6,12 +6,8 @@ import type { Bit, Char, CodecTypes as PgTypes, - Interval, JsonValue, Numeric, - Time, - Timestamp, - Timestamptz, Timetz, VarBit, Varchar, diff --git a/examples/prisma-8-demo/fixtures/skip-rollback/migrations/snapshots/3539138011365388d750c8a37a053403de07308eb1d872451a33d9655c9f1dde/contract.d.ts b/examples/prisma-8-demo/fixtures/skip-rollback/migrations/snapshots/3539138011365388d750c8a37a053403de07308eb1d872451a33d9655c9f1dde/contract.d.ts index 2a8a9c82aced..7221cd7ca875 100644 --- a/examples/prisma-8-demo/fixtures/skip-rollback/migrations/snapshots/3539138011365388d750c8a37a053403de07308eb1d872451a33d9655c9f1dde/contract.d.ts +++ b/examples/prisma-8-demo/fixtures/skip-rollback/migrations/snapshots/3539138011365388d750c8a37a053403de07308eb1d872451a33d9655c9f1dde/contract.d.ts @@ -6,12 +6,8 @@ import type { Bit, Char, CodecTypes as PgTypes, - Interval, JsonValue, Numeric, - Time, - Timestamp, - Timestamptz, Timetz, VarBit, Varchar, diff --git a/examples/prisma-8-demo/fixtures/skip-rollback/migrations/snapshots/4ae263c38bb0abf30b3daf57d2b002997c15260b0e5528bd924969c25f3d00bb/contract.d.ts b/examples/prisma-8-demo/fixtures/skip-rollback/migrations/snapshots/4ae263c38bb0abf30b3daf57d2b002997c15260b0e5528bd924969c25f3d00bb/contract.d.ts index 810513406e1c..4273a713670d 100644 --- a/examples/prisma-8-demo/fixtures/skip-rollback/migrations/snapshots/4ae263c38bb0abf30b3daf57d2b002997c15260b0e5528bd924969c25f3d00bb/contract.d.ts +++ b/examples/prisma-8-demo/fixtures/skip-rollback/migrations/snapshots/4ae263c38bb0abf30b3daf57d2b002997c15260b0e5528bd924969c25f3d00bb/contract.d.ts @@ -6,12 +6,8 @@ import type { Bit, Char, CodecTypes as PgTypes, - Interval, JsonValue, Numeric, - Time, - Timestamp, - Timestamptz, Timetz, VarBit, Varchar, diff --git a/examples/prisma-8-demo/fixtures/skip-rollback/migrations/snapshots/6c26c85a74b9429d6bb929df0c3e5035352fdb4cc2ee0e711a7a215e490041b1/contract.d.ts b/examples/prisma-8-demo/fixtures/skip-rollback/migrations/snapshots/6c26c85a74b9429d6bb929df0c3e5035352fdb4cc2ee0e711a7a215e490041b1/contract.d.ts index 15023119ed7a..ab185e4a2d53 100644 --- a/examples/prisma-8-demo/fixtures/skip-rollback/migrations/snapshots/6c26c85a74b9429d6bb929df0c3e5035352fdb4cc2ee0e711a7a215e490041b1/contract.d.ts +++ b/examples/prisma-8-demo/fixtures/skip-rollback/migrations/snapshots/6c26c85a74b9429d6bb929df0c3e5035352fdb4cc2ee0e711a7a215e490041b1/contract.d.ts @@ -6,12 +6,8 @@ import type { Bit, Char, CodecTypes as PgTypes, - Interval, JsonValue, Numeric, - Time, - Timestamp, - Timestamptz, Timetz, VarBit, Varchar, diff --git a/examples/prisma-8-demo/fixtures/skip-rollback/migrations/snapshots/df82ed1c905933192eca7c1d92ac6f1f8ada22d1d712865e5afd4400c554de2e/contract.d.ts b/examples/prisma-8-demo/fixtures/skip-rollback/migrations/snapshots/df82ed1c905933192eca7c1d92ac6f1f8ada22d1d712865e5afd4400c554de2e/contract.d.ts index 468ba2b96c8d..abfa79686b19 100644 --- a/examples/prisma-8-demo/fixtures/skip-rollback/migrations/snapshots/df82ed1c905933192eca7c1d92ac6f1f8ada22d1d712865e5afd4400c554de2e/contract.d.ts +++ b/examples/prisma-8-demo/fixtures/skip-rollback/migrations/snapshots/df82ed1c905933192eca7c1d92ac6f1f8ada22d1d712865e5afd4400c554de2e/contract.d.ts @@ -6,12 +6,8 @@ import type { Bit, Char, CodecTypes as PgTypes, - Interval, JsonValue, Numeric, - Time, - Timestamp, - Timestamptz, Timetz, VarBit, Varchar, diff --git a/examples/prisma-8-demo/fixtures/wide-fan/migrations/snapshots/6c26c85a74b9429d6bb929df0c3e5035352fdb4cc2ee0e711a7a215e490041b1/contract.d.ts b/examples/prisma-8-demo/fixtures/wide-fan/migrations/snapshots/6c26c85a74b9429d6bb929df0c3e5035352fdb4cc2ee0e711a7a215e490041b1/contract.d.ts index 15023119ed7a..ab185e4a2d53 100644 --- a/examples/prisma-8-demo/fixtures/wide-fan/migrations/snapshots/6c26c85a74b9429d6bb929df0c3e5035352fdb4cc2ee0e711a7a215e490041b1/contract.d.ts +++ b/examples/prisma-8-demo/fixtures/wide-fan/migrations/snapshots/6c26c85a74b9429d6bb929df0c3e5035352fdb4cc2ee0e711a7a215e490041b1/contract.d.ts @@ -6,12 +6,8 @@ import type { Bit, Char, CodecTypes as PgTypes, - Interval, JsonValue, Numeric, - Time, - Timestamp, - Timestamptz, Timetz, VarBit, Varchar, diff --git a/examples/prisma-8-demo/fixtures/wide-fan/migrations/snapshots/7161e6c8c74305ee59e584a6225cb1a60624664aef09069e2d6080ff9a91d5c0/contract.d.ts b/examples/prisma-8-demo/fixtures/wide-fan/migrations/snapshots/7161e6c8c74305ee59e584a6225cb1a60624664aef09069e2d6080ff9a91d5c0/contract.d.ts index c6835ed0be75..855c84ffbfab 100644 --- a/examples/prisma-8-demo/fixtures/wide-fan/migrations/snapshots/7161e6c8c74305ee59e584a6225cb1a60624664aef09069e2d6080ff9a91d5c0/contract.d.ts +++ b/examples/prisma-8-demo/fixtures/wide-fan/migrations/snapshots/7161e6c8c74305ee59e584a6225cb1a60624664aef09069e2d6080ff9a91d5c0/contract.d.ts @@ -6,12 +6,8 @@ import type { Bit, Char, CodecTypes as PgTypes, - Interval, JsonValue, Numeric, - Time, - Timestamp, - Timestamptz, Timetz, VarBit, Varchar, diff --git a/examples/prisma-8-demo/fixtures/wide-fan/migrations/snapshots/7b7a028bf17c118bdec714439df6456964c13d3d4a4dbd14869b888412a0bd01/contract.d.ts b/examples/prisma-8-demo/fixtures/wide-fan/migrations/snapshots/7b7a028bf17c118bdec714439df6456964c13d3d4a4dbd14869b888412a0bd01/contract.d.ts index 5fe6a104599a..eaac5bbcadb5 100644 --- a/examples/prisma-8-demo/fixtures/wide-fan/migrations/snapshots/7b7a028bf17c118bdec714439df6456964c13d3d4a4dbd14869b888412a0bd01/contract.d.ts +++ b/examples/prisma-8-demo/fixtures/wide-fan/migrations/snapshots/7b7a028bf17c118bdec714439df6456964c13d3d4a4dbd14869b888412a0bd01/contract.d.ts @@ -6,12 +6,8 @@ import type { Bit, Char, CodecTypes as PgTypes, - Interval, JsonValue, Numeric, - Time, - Timestamp, - Timestamptz, Timetz, VarBit, Varchar, diff --git a/examples/prisma-8-demo/fixtures/wide-fan/migrations/snapshots/d02f6207d52b6e854cac5d642d5514d5f2864ede49db9bb4ee78d45d8a95d406/contract.d.ts b/examples/prisma-8-demo/fixtures/wide-fan/migrations/snapshots/d02f6207d52b6e854cac5d642d5514d5f2864ede49db9bb4ee78d45d8a95d406/contract.d.ts index 02080882ca14..87f29bd46f5e 100644 --- a/examples/prisma-8-demo/fixtures/wide-fan/migrations/snapshots/d02f6207d52b6e854cac5d642d5514d5f2864ede49db9bb4ee78d45d8a95d406/contract.d.ts +++ b/examples/prisma-8-demo/fixtures/wide-fan/migrations/snapshots/d02f6207d52b6e854cac5d642d5514d5f2864ede49db9bb4ee78d45d8a95d406/contract.d.ts @@ -6,12 +6,8 @@ import type { Bit, Char, CodecTypes as PgTypes, - Interval, JsonValue, Numeric, - Time, - Timestamp, - Timestamptz, Timetz, VarBit, Varchar, diff --git a/examples/prisma-8-demo/fixtures/wide-fan/migrations/snapshots/df82ed1c905933192eca7c1d92ac6f1f8ada22d1d712865e5afd4400c554de2e/contract.d.ts b/examples/prisma-8-demo/fixtures/wide-fan/migrations/snapshots/df82ed1c905933192eca7c1d92ac6f1f8ada22d1d712865e5afd4400c554de2e/contract.d.ts index 468ba2b96c8d..abfa79686b19 100644 --- a/examples/prisma-8-demo/fixtures/wide-fan/migrations/snapshots/df82ed1c905933192eca7c1d92ac6f1f8ada22d1d712865e5afd4400c554de2e/contract.d.ts +++ b/examples/prisma-8-demo/fixtures/wide-fan/migrations/snapshots/df82ed1c905933192eca7c1d92ac6f1f8ada22d1d712865e5afd4400c554de2e/contract.d.ts @@ -6,12 +6,8 @@ import type { Bit, Char, CodecTypes as PgTypes, - Interval, JsonValue, Numeric, - Time, - Timestamp, - Timestamptz, Timetz, VarBit, Varchar, diff --git a/examples/prisma-8-demo/fixtures/wide-fan/migrations/snapshots/f984b41c72c9ae805561bd27891ef309f95bb5eda852892fc3e37b620f897b38/contract.d.ts b/examples/prisma-8-demo/fixtures/wide-fan/migrations/snapshots/f984b41c72c9ae805561bd27891ef309f95bb5eda852892fc3e37b620f897b38/contract.d.ts index 39fb6c3f61d6..2c64de55deb8 100644 --- a/examples/prisma-8-demo/fixtures/wide-fan/migrations/snapshots/f984b41c72c9ae805561bd27891ef309f95bb5eda852892fc3e37b620f897b38/contract.d.ts +++ b/examples/prisma-8-demo/fixtures/wide-fan/migrations/snapshots/f984b41c72c9ae805561bd27891ef309f95bb5eda852892fc3e37b620f897b38/contract.d.ts @@ -6,12 +6,8 @@ import type { Bit, Char, CodecTypes as PgTypes, - Interval, JsonValue, Numeric, - Time, - Timestamp, - Timestamptz, Timetz, VarBit, Varchar, diff --git a/examples/prisma-8-demo/migrations/snapshots/4a96b488a4ce92b434e5f7d6607b6435c0955f0d36b0018077045787764240e6/contract.d.ts b/examples/prisma-8-demo/migrations/snapshots/4a96b488a4ce92b434e5f7d6607b6435c0955f0d36b0018077045787764240e6/contract.d.ts index a9653b545f1b..bf7e7d5c048a 100644 --- a/examples/prisma-8-demo/migrations/snapshots/4a96b488a4ce92b434e5f7d6607b6435c0955f0d36b0018077045787764240e6/contract.d.ts +++ b/examples/prisma-8-demo/migrations/snapshots/4a96b488a4ce92b434e5f7d6607b6435c0955f0d36b0018077045787764240e6/contract.d.ts @@ -6,12 +6,8 @@ import type { Bit, Char, CodecTypes as PgTypes, - Interval, JsonValue, Numeric, - Time, - Timestamp, - Timestamptz, Timetz, VarBit, Varchar, diff --git a/examples/prisma-8-demo/migrations/snapshots/9fc3736e0efd980892a19f4d704ca30bcd0de243080e78ed1279b1f3a50f9a7e/contract.d.ts b/examples/prisma-8-demo/migrations/snapshots/9fc3736e0efd980892a19f4d704ca30bcd0de243080e78ed1279b1f3a50f9a7e/contract.d.ts index 987c5a7e3213..80013a282b41 100644 --- a/examples/prisma-8-demo/migrations/snapshots/9fc3736e0efd980892a19f4d704ca30bcd0de243080e78ed1279b1f3a50f9a7e/contract.d.ts +++ b/examples/prisma-8-demo/migrations/snapshots/9fc3736e0efd980892a19f4d704ca30bcd0de243080e78ed1279b1f3a50f9a7e/contract.d.ts @@ -11,14 +11,10 @@ import type { Bit, Char, CodecTypes as PgTypes, - Interval, JsonValue, Numeric, - Time, TimeString, - Timestamp, TimestampString, - Timestamptz, TimestamptzString, Timetz, VarBit, diff --git a/examples/prisma-8-demo/migrations/snapshots/a6e86cb536b51ac3b7542cb02dbf9f5004139e8dc5ee1b33a554c436d8cd2b2a/contract.d.ts b/examples/prisma-8-demo/migrations/snapshots/a6e86cb536b51ac3b7542cb02dbf9f5004139e8dc5ee1b33a554c436d8cd2b2a/contract.d.ts index 7d7a41f04eeb..b894bf47069b 100644 --- a/examples/prisma-8-demo/migrations/snapshots/a6e86cb536b51ac3b7542cb02dbf9f5004139e8dc5ee1b33a554c436d8cd2b2a/contract.d.ts +++ b/examples/prisma-8-demo/migrations/snapshots/a6e86cb536b51ac3b7542cb02dbf9f5004139e8dc5ee1b33a554c436d8cd2b2a/contract.d.ts @@ -11,14 +11,10 @@ import type { Bit, Char, CodecTypes as PgTypes, - Interval, JsonValue, Numeric, - Time, TimeString, - Timestamp, TimestampString, - Timestamptz, TimestamptzString, Timetz, VarBit, diff --git a/examples/prisma-8-postgis-demo/migrations/snapshots/2db551a8a5619d0d97710f29130eaea0eccbdd5238f015ee9d18408d438e0f8a/contract.d.ts b/examples/prisma-8-postgis-demo/migrations/snapshots/2db551a8a5619d0d97710f29130eaea0eccbdd5238f015ee9d18408d438e0f8a/contract.d.ts index 61d64f29226d..c442ec1a3e59 100644 --- a/examples/prisma-8-postgis-demo/migrations/snapshots/2db551a8a5619d0d97710f29130eaea0eccbdd5238f015ee9d18408d438e0f8a/contract.d.ts +++ b/examples/prisma-8-postgis-demo/migrations/snapshots/2db551a8a5619d0d97710f29130eaea0eccbdd5238f015ee9d18408d438e0f8a/contract.d.ts @@ -6,12 +6,8 @@ import type { Bit, Char, CodecTypes as PgTypes, - Interval, JsonValue, Numeric, - Time, - Timestamp, - Timestamptz, Timetz, VarBit, Varchar, diff --git a/packages/3-targets/3-targets/postgres/src/exports/codec-types.ts b/packages/3-targets/3-targets/postgres/src/exports/codec-types.ts index 0cfa24e04597..f426c3e5b7c3 100644 --- a/packages/3-targets/3-targets/postgres/src/exports/codec-types.ts +++ b/packages/3-targets/3-targets/postgres/src/exports/codec-types.ts @@ -34,13 +34,6 @@ export type Numeric

}>; export type Bit = BrandedString<{ __bitLength: N }>; export type VarBit = BrandedString<{ __varbitLength: N }>; -export type Timestamp

= BrandedString<{ - __timestampPrecision: P; -}>; -export type Timestamptz

= BrandedString<{ - __timestamptzPrecision: P; -}>; -export type Time

= BrandedString<{ __timePrecision: P }>; export type Timetz

= BrandedString<{ __timetzPrecision: P; }>; diff --git a/packages/3-targets/3-targets/postgres/test/fixtures/namespaced-contract.d.ts b/packages/3-targets/3-targets/postgres/test/fixtures/namespaced-contract.d.ts index 89c785b46d36..573fff273ee9 100644 --- a/packages/3-targets/3-targets/postgres/test/fixtures/namespaced-contract.d.ts +++ b/packages/3-targets/3-targets/postgres/test/fixtures/namespaced-contract.d.ts @@ -8,9 +8,6 @@ import type { CodecTypes as PgTypes, JsonValue, Numeric, - Time, - Timestamp, - Timestamptz, Timetz, VarBit, Varchar, diff --git a/test/integration/test/value-objects/fixtures/generated/sql-contract.d.ts b/test/integration/test/value-objects/fixtures/generated/sql-contract.d.ts index ab85344fc941..2e317e716bdc 100644 --- a/test/integration/test/value-objects/fixtures/generated/sql-contract.d.ts +++ b/test/integration/test/value-objects/fixtures/generated/sql-contract.d.ts @@ -8,9 +8,6 @@ import type { CodecTypes as PgTypes, JsonValue, Numeric, - Time, - Timestamp, - Timestamptz, Timetz, VarBit, Varchar, diff --git a/upgrade-instructions/pending/explicit-date-time-types/app/instructions.md b/upgrade-instructions/pending/explicit-date-time-types/app/instructions.md index 7dcb45725487..20271c179436 100644 --- a/upgrade-instructions/pending/explicit-date-time-types/app/instructions.md +++ b/upgrade-instructions/pending/explicit-date-time-types/app/instructions.md @@ -17,11 +17,18 @@ changes: - '(? Date: Thu, 22 Oct 2026 19:00:28 +0200 Subject: [PATCH 020/118] Say how a database now() default reads through the UTC timestamp codecs PostgreSQL fills a timestamp column's now() default in the session's time zone, so under a TimeZone other than UTC it reads back shifted through TimestampJsDate and TimestampInstant, while @onCreate(now()) writes UTC from the client. The docs claimed neither depended on the time zone. A test under an Asia/Kolkata session pins both behaviours. The lowering is unchanged. Co-Authored-By: Claude Opus 5.5 Signed-off-by: willbot Signed-off-by: Will Madden --- docs/reference/scalar-types.md | 2 +- .../postgres/src/core/date-codecs.ts | 2 +- .../postgres/src/core/temporal-codecs.ts | 2 +- ...stamp-utc-now-defaults.integration.test.ts | 77 +++++++++++++++++++ 4 files changed, 80 insertions(+), 3 deletions(-) create mode 100644 packages/3-targets/6-adapters/postgres-codec-testkit/test/timestamp-utc-now-defaults.integration.test.ts diff --git a/docs/reference/scalar-types.md b/docs/reference/scalar-types.md index 9ccd69ccad36..867c754bb1e8 100644 --- a/docs/reference/scalar-types.md +++ b/docs/reference/scalar-types.md @@ -103,7 +103,7 @@ These are the current names. The Postgres and SQLite rename project will change | `Bytes` | `field.bytes()` | `pg/bytea@1` | `bytea` | `Uint8Array` | | `pg.enum(Name)` | — | `pg/enum@1` | the native enum type | `string` | -`TimestampJsDate` and `TimestampInstant` read the date and time a `timestamp` column stores as UTC, and write a value's UTC date and time with no offset, so neither depends on the server's or the application's time zone. They refuse `infinity`, `-infinity` and a value after `+275760-09-13T00:00:00`, which no `Date` or `Temporal.Instant` holds, and name `TimestampString`, which holds them. `IntervalString` reads every `IntervalStyle` and writes an ISO 8601 duration; it refuses any other text on write. +`TimestampJsDate` and `TimestampInstant` read the date and time a `timestamp` column stores as UTC, and write a value's UTC date and time with no offset, so a value the application writes reads back as the same instant whatever the time zone of the server, the session or the application. A database default is different: PostgreSQL fills a `timestamp` column's `@default(now())` in the session's time zone, so on a server whose `TimeZone` is not UTC that value reads back shifted by the zone's offset. `@onCreate(now())` writes the client's time in UTC and reads back unshifted. They refuse `infinity`, `-infinity` and a value after `+275760-09-13T00:00:00`, which no `Date` or `Temporal.Instant` holds, and name `TimestampString`, which holds them. `IntervalString` reads every `IntervalStyle` and writes an ISO 8601 duration; it refuses any other text on write. A literal default of a date, time, interval or bytes type is written with the tag of its type, as in `@default(timestamptz`2024-01-01T00:00:00Z`)` or `@default(bytea`\x0102`)`; a quoted string is refused with a message that shows the tag. It is stored in the one form its data type stores, whichever codec the column uses and however the default was written, and a default with more fraction digits or more scale than its column holds is refused rather than rounded; [ADR 254](../architecture%20docs/adrs/ADR%20254%20-%20Data%20types%20and%20casts.md#date-and-time-types) states each form. diff --git a/packages/3-targets/3-targets/postgres/src/core/date-codecs.ts b/packages/3-targets/3-targets/postgres/src/core/date-codecs.ts index 913dc2dc390c..0317c589213f 100644 --- a/packages/3-targets/3-targets/postgres/src/core/date-codecs.ts +++ b/packages/3-targets/3-targets/postgres/src/core/date-codecs.ts @@ -151,7 +151,7 @@ pgTimestamptzDateColumn satisfies ColumnHelperFor; pgTimestamptzDateColumn satisfies ColumnHelperForStrict; /** - * A `timestamp` column read and written as a JavaScript `Date`, treating the stored date and time as UTC. A read appends `Z` to the stored value; a write sends the `Date`'s UTC date and time with no offset, so neither depends on the server's or the process's time zone. + * A `timestamp` column read and written as a JavaScript `Date`, treating the stored date and time as UTC. A read appends `Z` to the stored value; a write sends the `Date`'s UTC date and time with no offset, so a written value reads back unchanged whatever the server's, the session's or the process's time zone. PostgreSQL fills a database default `now()` in the session's time zone, so under a `TimeZone` other than UTC that value reads back shifted; `@onCreate(now())` writes UTC from the client. */ export class PgTimestampDateCodec extends CodecImpl< typeof PG_TIMESTAMP_DATE_CODEC_ID, diff --git a/packages/3-targets/3-targets/postgres/src/core/temporal-codecs.ts b/packages/3-targets/3-targets/postgres/src/core/temporal-codecs.ts index b968aa972c81..dd5fba1d7b96 100644 --- a/packages/3-targets/3-targets/postgres/src/core/temporal-codecs.ts +++ b/packages/3-targets/3-targets/postgres/src/core/temporal-codecs.ts @@ -138,7 +138,7 @@ pgTimestampTemporalColumn satisfies ColumnHelperFor; /** - * A `timestamp` column read and written as a `Temporal.Instant`, treating the stored date and time as UTC. A write sends the instant's UTC date and time with no offset. + * A `timestamp` column read and written as a `Temporal.Instant`, treating the stored date and time as UTC. A write sends the instant's UTC date and time with no offset. PostgreSQL fills a database default `now()` in the session's time zone, so under a `TimeZone` other than UTC that value reads back shifted; `@onCreate(now())` writes UTC from the client. */ export class PgTimestampInstantCodec extends CodecImpl< typeof PG_TIMESTAMP_INSTANT_CODEC_ID, diff --git a/packages/3-targets/6-adapters/postgres-codec-testkit/test/timestamp-utc-now-defaults.integration.test.ts b/packages/3-targets/6-adapters/postgres-codec-testkit/test/timestamp-utc-now-defaults.integration.test.ts new file mode 100644 index 000000000000..d1f3279ae92a --- /dev/null +++ b/packages/3-targets/6-adapters/postgres-codec-testkit/test/timestamp-utc-now-defaults.integration.test.ts @@ -0,0 +1,77 @@ +import { postgresCodecDescriptorRegistry } from '@internal/target-postgres/codecs'; +import { createDevDatabase, timeouts } from '@repo/test-utils'; +import { afterAll, beforeAll, describe, expect, it } from 'vitest'; +import { connectRuntimeDriver } from './runtime-connection'; + +const KOLKATA_OFFSET_MILLISECONDS = (5 * 60 + 30) * 60 * 1000; +const SLACK_MILLISECONDS = 5_000; + +const codecFor = (codecId: string) => + postgresCodecDescriptorRegistry.descriptorFor(codecId)!.factory(undefined)({ + name: 'timestamp-utc-now-defaults', + }); + +const epochMillisecondsOf = (value: unknown): number => + value instanceof Date ? value.getTime() : Number(Reflect.get(Object(value), 'epochMilliseconds')); + +describe.each(['pg/timestamp-date@1', 'pg/timestamp-instant@1'])( + '%s under a session whose TimeZone is Asia/Kolkata', + (codecId) => { + let database: Awaited> | undefined; + let runtime: Awaited> | undefined; + const codec = codecFor(codecId); + + beforeAll(async () => { + database = await createDevDatabase(); + runtime = await connectRuntimeDriver(database.connectionString); + await runtime.connection.query("SET TimeZone = 'Asia/Kolkata'"); + await runtime.connection.query( + 'create table stamps (id int primary key, v timestamp(3) not null default now())', + ); + }, timeouts.spinUpPpgDev); + + afterAll(async () => { + await runtime?.close(); + await database?.close(); + database = undefined; + }, timeouts.spinUpPpgDev); + + async function readBack(id: number): Promise { + const rows = await runtime!.connection.query('select v from stamps where id = $1', [id]); + return epochMillisecondsOf(await codec.fromWire(rows[0]?.['v'], {})); + } + + it( + 'reads the database default now(), which PostgreSQL fills in the session time zone, shifted by that zone’s offset', + async () => { + const before = Date.now(); + await runtime!.connection.query('insert into stamps (id) values (1)'); + const after = Date.now(); + + const shift = (await readBack(1)) - after; + expect(shift).toBeGreaterThan( + KOLKATA_OFFSET_MILLISECONDS - (after - before) - SLACK_MILLISECONDS, + ); + expect(shift).toBeLessThan(KOLKATA_OFFSET_MILLISECONDS + SLACK_MILLISECONDS); + }, + timeouts.spinUpPpgDev, + ); + + it( + 'reads a client now(), written through the codec as @onCreate(now()) writes it, as the same instant', + async () => { + const written = new Date(Date.now() - (Date.now() % 1000)); + const value = + codecId === 'pg/timestamp-date@1' + ? written + : Temporal.Instant.fromEpochMilliseconds(written.getTime()); + await runtime!.connection.query('insert into stamps (id, v) values (2, $1)', [ + await codec.toWire(value, {}), + ]); + + expect(await readBack(2)).toBe(written.getTime()); + }, + timeouts.spinUpPpgDev, + ); + }, +); From da14f2072309782fe3613d48f434f908e29d537c Mon Sep 17 00:00:00 2001 From: willbot Date: Thu, 22 Oct 2026 19:01:05 +0200 Subject: [PATCH 021/118] Pin the UTC timestamp codecs under a non-UTC host time zone The codecs read and write the same text under TZ=UTC, Asia/Kolkata and America/Los_Angeles, so a read or write that used the host's local time fails. TimestampInstant's refusal of a stored value past the last instant is tested too. Co-Authored-By: Claude Opus 5.5 Signed-off-by: willbot Signed-off-by: Will Madden --- .../test/timestamp-utc-codecs.test.ts | 9 ++++ .../timestamp-utc-codecs.time-zone.test.ts | 43 +++++++++++++++++++ 2 files changed, 52 insertions(+) create mode 100644 packages/3-targets/3-targets/postgres/test/timestamp-utc-codecs.time-zone.test.ts diff --git a/packages/3-targets/3-targets/postgres/test/timestamp-utc-codecs.test.ts b/packages/3-targets/3-targets/postgres/test/timestamp-utc-codecs.test.ts index 470b64cdac30..fd3d5a22458e 100644 --- a/packages/3-targets/3-targets/postgres/test/timestamp-utc-codecs.test.ts +++ b/packages/3-targets/3-targets/postgres/test/timestamp-utc-codecs.test.ts @@ -137,6 +137,15 @@ describe('pg/timestamp-instant@1', () => { }, ); + it('refuses the stored value +275760-09-13T00:00:01, past the last Temporal.Instant, naming TimestampString', () => { + expect(() => fromContractJson(codec, '+275760-09-13T00:00:01')).toThrow( + expect.objectContaining({ + code: 'RUNTIME.DECODE_FAILED', + message: expect.stringContaining('TimestampString'), + }), + ); + }); + it.each([ [Temporal.PlainDateTime.from('2026-01-02T03:04:05'), 'a Temporal.PlainDateTime'], [new Date('2026-01-02T03:04:05Z'), 'a Date'], diff --git a/packages/3-targets/3-targets/postgres/test/timestamp-utc-codecs.time-zone.test.ts b/packages/3-targets/3-targets/postgres/test/timestamp-utc-codecs.time-zone.test.ts new file mode 100644 index 000000000000..7103785d865d --- /dev/null +++ b/packages/3-targets/3-targets/postgres/test/timestamp-utc-codecs.time-zone.test.ts @@ -0,0 +1,43 @@ +import { afterEach, describe, expect, it } from 'vitest'; +import { pgTimestampDateDescriptor } from '../src/core/date-codecs'; +import { pgTimestampInstantDescriptor } from '../src/core/temporal-codecs'; +import { fromContractJson, toContractJson } from './contract-json'; + +const dateCodec = pgTimestampDateDescriptor.factory({})({ name: 'at' }); +const instantCodec = pgTimestampInstantDescriptor.factory({})({ name: 'at' }); +const originalTimeZone = process.env['TZ']; + +afterEach(() => { + if (originalTimeZone === undefined) delete process.env['TZ']; + else process.env['TZ'] = originalTimeZone; +}); + +describe.each(['UTC', 'Asia/Kolkata', 'America/Los_Angeles'])( + 'the UTC timestamp codecs under TZ=%s', + (timeZone) => { + it('read and write the stored date and time as UTC, whatever the host time zone', async () => { + process.env['TZ'] = timeZone; + const date = new Date('2026-01-02T03:04:05.123Z'); + const instant = Temporal.Instant.from('2026-01-02T03:04:05.123456Z'); + expect({ + dateRead: (await dateCodec.fromWire('2026-01-02 03:04:05.123', {})).toISOString(), + dateWire: await dateCodec.toWire(date, {}), + dateJson: toContractJson(dateCodec, date), + dateFromJson: fromContractJson(dateCodec, '2026-01-02T03:04:05.123').toISOString(), + instantRead: String(await instantCodec.fromWire('2026-01-02 03:04:05.123456', {})), + instantWire: await instantCodec.toWire(instant, {}), + instantJson: toContractJson(instantCodec, instant), + instantFromJson: String(fromContractJson(instantCodec, '2026-01-02T03:04:05.123456')), + }).toEqual({ + dateRead: '2026-01-02T03:04:05.123Z', + dateWire: '2026-01-02T03:04:05.123', + dateJson: '2026-01-02T03:04:05.123', + dateFromJson: '2026-01-02T03:04:05.123Z', + instantRead: '2026-01-02T03:04:05.123456Z', + instantWire: '2026-01-02T03:04:05.123456', + instantJson: '2026-01-02T03:04:05.123456', + instantFromJson: '2026-01-02T03:04:05.123456Z', + }); + }); + }, +); From 9090661a9c7cfb625aed37cb40ba6d62490bf8e0 Mon Sep 17 00:00:00 2001 From: willbot Date: Thu, 22 Oct 2026 19:01:44 +0200 Subject: [PATCH 022/118] Key the Supabase timestamp alias by the name contract infer writes contract infer writes TimestampString for a timestamp column, so the generator's CreatedAt alias is keyed by that name. Co-Authored-By: Claude Opus 5.5 Signed-off-by: willbot Signed-off-by: Will Madden --- packages/3-extensions/supabase/scripts/generate-contract.ts | 2 +- packages/3-extensions/supabase/src/contract/handles.ts | 3 +-- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/packages/3-extensions/supabase/scripts/generate-contract.ts b/packages/3-extensions/supabase/scripts/generate-contract.ts index 4ed6c9b01abb..926f8c48d0e2 100644 --- a/packages/3-extensions/supabase/scripts/generate-contract.ts +++ b/packages/3-extensions/supabase/scripts/generate-contract.ts @@ -297,7 +297,7 @@ const NAMED_TYPE_ALIASES: Readonly> = { Inet: 'IpAddress', Json: 'Payload', SmallInt: 'EmailChangeConfirmStatus', - TimestampTemporal: 'CreatedAt', + TimestampString: 'CreatedAt', Uuid: 'Id', 'VarChar(40)': 'Hash', 'VarChar(64)': 'IpAddress2', diff --git a/packages/3-extensions/supabase/src/contract/handles.ts b/packages/3-extensions/supabase/src/contract/handles.ts index 104070877346..f967a2d9c9ac 100644 --- a/packages/3-extensions/supabase/src/contract/handles.ts +++ b/packages/3-extensions/supabase/src/contract/handles.ts @@ -14,8 +14,7 @@ import { extensionModel, field } from '@internal/sql-contract-ts/contract-builde const pgText = { codecId: 'pg/text@1' } as const; // Supabase's `auth` tables are read by application code, so these carry the Temporal -// representation — the same choice a user authoring the column themselves would get from a bare -// `TimestamptzTemporal`. +// representation, the codec of the explicit type `TimestamptzTemporal`. const pgTimestamptz = { codecId: 'pg/timestamptz-temporal@1', } as const; From 56752f3441b572bd936b997829ddcb4e19c82a01 Mon Sep 17 00:00:00 2001 From: willbot Date: Thu, 22 Oct 2026 19:02:25 +0200 Subject: [PATCH 023/118] Use the renamed type names in the remaining docs and comment Co-Authored-By: Claude Opus 5.5 Signed-off-by: willbot Signed-off-by: Will Madden --- docs/reference/error-reference.md | 2 +- gotchas.md | 2 +- packages/2-sql/2-authoring/contract-psl/README.md | 2 +- .../3-targets/postgres/src/core/psl-print/column-types.ts | 2 +- skills/prisma-8/references/contract.md | 2 +- 5 files changed, 5 insertions(+), 5 deletions(-) diff --git a/docs/reference/error-reference.md b/docs/reference/error-reference.md index 9ac6c4e100db..f97eab009bb3 100644 --- a/docs/reference/error-reference.md +++ b/docs/reference/error-reference.md @@ -986,7 +986,7 @@ A type alias in a `types {}` block has no base, or its base is not a type constr ### PSL_INVALID_ATTRIBUTE_ARGUMENT -An argument of an attribute or a type constructor is invalid. One case is a contributed type alias written with arguments, as in `temporal.CreatedAt(3)`: `temporal.CreatedAt is a type alias and takes no arguments.` Write it bare, `temporal.CreatedAt`, or write the type with the attributes, as in `Timestamptz(3) @onCreate(now())`. Reported at the call, in SQL and Mongo schemas. In a Mongo schema, a `types {}` alias written with arguments is `PSL_UNSUPPORTED_FIELD_TYPE`: `Field "." calls "", which is a type alias, not a type constructor. Remove the arguments.` +An argument of an attribute or a type constructor is invalid. One case is a contributed type alias written with arguments, as in `temporal.CreatedAt(3)`: `temporal.CreatedAt is a type alias and takes no arguments.` Write it bare, `temporal.CreatedAt`, or write the type with the attributes, as in `TimestamptzTemporal(3) @onCreate(now())`. Reported at the call, in SQL and Mongo schemas. In a Mongo schema, a `types {}` alias written with arguments is `PSL_UNSUPPORTED_FIELD_TYPE`: `Field "." calls "", which is a type alias, not a type constructor. Remove the arguments.` ### PSL_UNSUPPORTED_FIELD_ATTRIBUTE diff --git a/gotchas.md b/gotchas.md index 748b8f3656ee..de99e7b1a20e 100644 --- a/gotchas.md +++ b/gotchas.md @@ -132,7 +132,7 @@ The same command with `--from 20260707T1005_init` (a migration directory name) s **Workaround.** `import 'temporal-polyfill/full/global'` before the client is created (the example did so in `src/db.ts` before the fix), or author the column with the `*String` presets to receive PostgreSQL's text. -**Resolved.** `prisma7Schema(...)` and `contract infer` now write `TimestampString(3)`, `TimestamptzString(p)`, `DateString` and `TimeString(p)` for these columns, so they read as PostgreSQL's text, `@updatedAt` writes UTC text, and the application needs no `Temporal`. A column written by hand as `DateTime`, `Timestamp(p)`, `Date` or `Time(p)` still reads as a `Temporal` value and still needs one. +**Resolved.** `prisma7Schema(...)` and `contract infer` now write `TimestampString(3)`, `TimestamptzString(p)`, `DateString` and `TimeString(p)` for these columns, so they read as PostgreSQL's text, `@updatedAt` writes UTC text, and the application needs no `Temporal`. A column written by hand as `DateTime`, `TimestampTemporal(p)`, `Date` or `Time(p)` still reads as a `Temporal` value and still needs one. **Reproduction.** 1. `cd examples/prisma7-adoption && pnpm db:start`, then `pnpm v7:migrate && pnpm emit && pnpm sign && pnpm seed`. diff --git a/packages/2-sql/2-authoring/contract-psl/README.md b/packages/2-sql/2-authoring/contract-psl/README.md index 5e33b1f0931f..79fc885802d2 100644 --- a/packages/2-sql/2-authoring/contract-psl/README.md +++ b/packages/2-sql/2-authoring/contract-psl/README.md @@ -115,7 +115,7 @@ model Post { - An alias's base is a type constructor, called or bare, or an enum. It may not name another alias or a contributed alias (`PSL_UNSUPPORTED_NAMED_TYPE_BASE`). - An alias may carry `@default`, `@onCreate` and `@onUpdate`. They are lowered once, at the declaration, so a diagnostic is reported once, on the alias. Every field typed by the alias takes them. `@id` and `@unique` on an alias are `PSL_UNSUPPORTED_NAMED_TYPE_ATTRIBUTE`, because they belong to a model's field, not to its type; write them on the field. A refused attribute is dropped and the alias is kept. - A field may add any attribute its alias does not set, including `@id` and `@unique`. A field that writes an attribute its alias already sets is `PSL_DUPLICATE_ATTRIBUTE`, naming the alias. A list field typed by an alias that generates a value is `PSL_LIST_EXECUTION_DEFAULT_UNSUPPORTED`; an optional field may use one. -- The targets contribute aliases with no arguments, written bare: `temporal.CreatedAt` (`now()` on create) and `temporal.UpdatedAt` (`now()` on create and on update). On Postgres they are `Timestamptz`, with `temporal.CreatedAtString` / `temporal.UpdatedAtString` on `TimestamptzString` and `temporal.CreatedAtJsDate` / `temporal.UpdatedAtJsDate` on `TimestamptzJsDate`; on SQLite they are `DateTime`. Each stores what the matching `temporal.createdAt()` or `temporal.updatedAt()` preset stores. `temporal.CreatedAt(3)` is `PSL_INVALID_ATTRIBUTE_ARGUMENT`; write a precision with the attributes, as in `Timestamptz(3) @onCreate(now())`. +- The targets contribute aliases with no arguments, written bare: `temporal.CreatedAt` (`now()` on create) and `temporal.UpdatedAt` (`now()` on create and on update). On Postgres they are `TimestamptzTemporal`, with `temporal.CreatedAtString` / `temporal.UpdatedAtString` on `TimestamptzString` and `temporal.CreatedAtJsDate` / `temporal.UpdatedAtJsDate` on `TimestamptzJsDate`; on SQLite they are `DateTime`. Each stores what the matching `temporal.createdAt()` or `temporal.updatedAt()` preset stores. `temporal.CreatedAt(3)` is `PSL_INVALID_ATTRIBUTE_ARGUMENT`; write a precision with the attributes, as in `TimestamptzTemporal(3) @onCreate(now())`. Integer representation authoring surface: diff --git a/packages/3-targets/3-targets/postgres/src/core/psl-print/column-types.ts b/packages/3-targets/3-targets/postgres/src/core/psl-print/column-types.ts index 8f4ae0a2eb0e..b48d005483dd 100644 --- a/packages/3-targets/3-targets/postgres/src/core/psl-print/column-types.ts +++ b/packages/3-targets/3-targets/postgres/src/core/psl-print/column-types.ts @@ -20,7 +20,7 @@ import { positionalArg, SYNTHETIC_SPAN } from '../psl-build/psl-literals'; import { refuseColumnWithoutPslType, refuseUnwritableTypeArgument } from './refusals'; /** - * What a column's PSL type stands for. `dataType` is a type that stands for the column's codec and type params alone, written as a bare name such as `String` or a call such as `Timestamptz(3)`. A value object or an enum, including a native enum written `pg.enum()`, names something more. + * What a column's PSL type stands for. `dataType` is a type that stands for the column's codec and type params alone, written as a bare name such as `String` or a call such as `TimestamptzTemporal(3)`. A value object or an enum, including a native enum written `pg.enum()`, names something more. */ export type PslColumnTypeKind = 'dataType' | 'valueObject' | 'enum'; diff --git a/skills/prisma-8/references/contract.md b/skills/prisma-8/references/contract.md index 689a15a63693..c8202045d4c7 100644 --- a/skills/prisma-8/references/contract.md +++ b/skills/prisma-8/references/contract.md @@ -156,7 +156,7 @@ model User { } ``` -An alias is expanded into each field that uses it: the column stores the alias's codec and type params, and the contract stores no alias. An alias may carry `@default`, `@onCreate` and `@onUpdate`, which every field typed by it takes: `UpdatedAt = TimestampString(3) @onCreate(now()) @onUpdate(now())`. It may not carry `@id` or `@unique`; write those on the field. A field may add attributes its alias does not set (`updatedAt UpdatedAt @default(now())`), but repeating one the alias sets is `PSL_DUPLICATE_ATTRIBUTE`. An alias may not name another alias. The targets contribute `temporal.CreatedAt` (generated on create) and `temporal.UpdatedAt` (on create and on update), written bare: `createdAt temporal.CreatedAt`. On Postgres they are `Timestamptz` columns, with `temporal.CreatedAtString` / `UpdatedAtString` and `CreatedAtJsDate` / `UpdatedAtJsDate` variants; on SQLite `DateTime`; on Mongo `Date`. They take no arguments; for a precision, write the type and attributes, as in `Timestamptz(3) @onCreate(now())`. +An alias is expanded into each field that uses it: the column stores the alias's codec and type params, and the contract stores no alias. An alias may carry `@default`, `@onCreate` and `@onUpdate`, which every field typed by it takes: `UpdatedAt = TimestampString(3) @onCreate(now()) @onUpdate(now())`. It may not carry `@id` or `@unique`; write those on the field. A field may add attributes its alias does not set (`updatedAt UpdatedAt @default(now())`), but repeating one the alias sets is `PSL_DUPLICATE_ATTRIBUTE`. An alias may not name another alias. The targets contribute `temporal.CreatedAt` (generated on create) and `temporal.UpdatedAt` (on create and on update), written bare: `createdAt temporal.CreatedAt`. On Postgres they are `TimestamptzTemporal` columns, with `temporal.CreatedAtString` / `UpdatedAtString` and `CreatedAtJsDate` / `UpdatedAtJsDate` variants; on SQLite `DateTime`; on Mongo `Date`. They take no arguments; for a precision, write the type and attributes, as in `TimestamptzTemporal(3) @onCreate(now())`. Values the client generates are written `@onCreate(fn())` and `@onUpdate(fn())`: `id String @id @onCreate(uuid(7))`, `slug String @onCreate(nanoid(12))`, `updatedAt DateTime @onCreate(now()) @onUpdate(now())`. `@default(...)` is a database default only (`now()`, `autoincrement()`, a literal, or `` sql`...` ``); `@default(uuid())` is refused with `PSL_GENERATOR_IN_DEFAULT`. In the TS builder, write `.onCreate(gen.uuid(7))` and `.onUpdate(gen.now())`. Column defaults and generators can sit on one field: `@default(now()) @onCreate(now())`. From 18a43c73da8177556dc1f32e29a62d1a2248ef90 Mon Sep 17 00:00:00 2001 From: willbot Date: Thu, 22 Oct 2026 19:02:48 +0200 Subject: [PATCH 024/118] Type-test the timestamp Instant, timestamp Date and interval string fields Co-Authored-By: Claude Opus 5.5 Signed-off-by: willbot Signed-off-by: Will Madden --- .../temporal-field-types.test-d.ts | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/packages/3-extensions/postgres/test/no-temporal-global/temporal-field-types.test-d.ts b/packages/3-extensions/postgres/test/no-temporal-global/temporal-field-types.test-d.ts index 36b6f73c0198..0f7e47614d34 100644 --- a/packages/3-extensions/postgres/test/no-temporal-global/temporal-field-types.test-d.ts +++ b/packages/3-extensions/postgres/test/no-temporal-global/temporal-field-types.test-d.ts @@ -14,6 +14,9 @@ const contract = defineContract({}, ({ type, field, model }) => ({ day: field.column(type.DateTemporal()), opens: field.column(type.TimeTemporal(0)), legacyDay: field.column(type.Date()), + utcAt: field.column(type.TimestampInstant(6)), + utcDate: field.column(type.TimestampJsDate(3)), + span: field.column(type.IntervalString(3)), }, }), }, @@ -28,6 +31,8 @@ test('a Temporal field is typed precisely in a project without the global Tempor expectTypeOf().toEqualTypeOf(); expectTypeOf().toEqualTypeOf(); expectTypeOf().toEqualTypeOf(); + expectTypeOf().not.toBeAny(); + expectTypeOf().toEqualTypeOf(); }); test('every Temporal codec names its type through an import', () => { @@ -40,4 +45,11 @@ test('every Temporal codec names its type through an import', () => { >().toEqualTypeOf(); expectTypeOf().toEqualTypeOf(); expectTypeOf().toEqualTypeOf(); + expectTypeOf().not.toBeAny(); + expectTypeOf().toEqualTypeOf(); +}); + +test('a timestamp read as a Date reads a Date, and an interval read as a string reads a string', () => { + expectTypeOf().toEqualTypeOf(); + expectTypeOf().toEqualTypeOf(); }); From 1d23c71014e58315b348dfb125a8a043b97000db Mon Sep 17 00:00:00 2001 From: willbot Date: Thu, 22 Oct 2026 19:03:39 +0200 Subject: [PATCH 025/118] Test the explicit date and time upgrade detection patterns Covers the PSL and TypeScript renames, both forms of a type import of a removed branded type, and the whole-file temporal-spec pattern. Co-Authored-By: Claude Opus 5.5 Signed-off-by: willbot Signed-off-by: Will Madden --- .../detection.test.ts | 135 ++++++++++++++++++ 1 file changed, 135 insertions(+) create mode 100644 test/integration/test/upgrade-instructions/explicit-date-time-types/detection.test.ts diff --git a/test/integration/test/upgrade-instructions/explicit-date-time-types/detection.test.ts b/test/integration/test/upgrade-instructions/explicit-date-time-types/detection.test.ts new file mode 100644 index 000000000000..79b00cb572ce --- /dev/null +++ b/test/integration/test/upgrade-instructions/explicit-date-time-types/detection.test.ts @@ -0,0 +1,135 @@ +import { readFileSync } from 'node:fs'; +import { fileURLToPath } from 'node:url'; +import { dirname, join } from 'pathe'; +import { describe, expect, it } from 'vitest'; + +const here = dirname(fileURLToPath(import.meta.url)); +const repoRoot = join(here, '../../../../..'); + +function patternsOf(audience: string, id: string): readonly RegExp[] { + const guide = readFileSync( + join( + repoRoot, + 'upgrade-instructions/pending/explicit-date-time-types', + audience, + 'instructions.md', + ), + 'utf-8', + ); + const block = guide.split(/\n {2}- id: /).find((entry) => entry.startsWith(`${id}\n`)) ?? ''; + return [...block.matchAll(/^ {8}- '(.*)'$/gm)].map( + ([, pattern]) => new RegExp((pattern ?? '').replaceAll("''", "'")), + ); +} + +describe.each(['app', 'extension'])('the %s explicit date and time type detection', (audience) => { + const matches = (id: string, content: string) => + patternsOf(audience, id).some((pattern) => pattern.test(content)); + + it('has a pattern for each change it detects', () => { + for (const id of [ + 'psl-temporal-type-renames', + 'ts-temporal-type-helpers-renamed', + 'interval-with-precision-typed-as-parts', + 'branded-time-types-removed', + ]) { + expect(patternsOf(audience, id).length).toBeGreaterThan(0); + } + }); + + it('matches a PSL field or alias written with a renamed type, and nothing else', () => { + const psl = (line: string) => matches('psl-temporal-type-renames', `model A {\n${line}\n}`); + expect({ + timestamptz: psl(' at Timestamptz(3)'), + timestamp: psl(' local Timestamp?'), + interval: psl(' span Interval[]'), + alias: psl('types {\n Stamp = Timestamp(3)\n}'), + prisma7NativeType: psl(' at DateTime @db.Timestamptz(3)'), + prisma7Timestamp: psl(' at DateTime @db.Timestamp'), + newName: psl(' at TimestamptzTemporal(3)'), + stringType: psl(' at TimestampString(3)'), + tag: psl(' at TimestamptzTemporal @default(timestamptz`2024-01-01T00:00:00Z`)'), + }).toEqual({ + timestamptz: true, + timestamp: true, + interval: true, + alias: true, + prisma7NativeType: false, + prisma7Timestamp: false, + newName: false, + stringType: false, + tag: false, + }); + }); + + it('matches the renamed TypeScript helpers only', () => { + const ts = (content: string) => matches('ts-temporal-type-helpers-renamed', content); + expect({ + helper: ts('field.column(type.Timestamptz(3))'), + interval: ts('field.column(type.Interval())'), + columnHelper: ts('field.column(intervalColumn(6))'), + renamedHelper: ts('field.column(type.TimestamptzTemporal(3))'), + renamedColumnHelper: ts('field.column(intervalPartsColumn(6))'), + }).toEqual({ + helper: true, + interval: true, + columnHelper: true, + renamedHelper: false, + renamedColumnHelper: false, + }); + }); + + it('matches an import of a removed branded type, in either type import form', () => { + const branded = (content: string) => matches('branded-time-types-removed', content); + const interval = (content: string) => + matches('interval-with-precision-typed-as-parts', content); + const snapshotImport = + "import type {\n CodecTypes as PgTypes,\n Interval,\n Time,\n Timestamp,\n Timestamptz,\n} from '@prisma/orm-postgres/target/codec-types';"; + expect({ + snapshot: branded(snapshotImport), + inlineType: branded("import { type Time } from '@prisma/orm-postgres/target/codec-types';"), + interval: interval("import type { Interval } from '@prisma/orm-postgres/contract-builder';"), + inlineInterval: interval( + "import { field, type Interval } from '@prisma/orm-postgres/contract-builder';", + ), + keptName: branded( + "import type { TimestampString } from '@prisma/orm-postgres/target/codec-types';", + ), + otherPackage: branded("import { Timestamp } from 'bson';"), + }).toEqual({ + snapshot: true, + inlineType: true, + interval: true, + inlineInterval: true, + keptName: false, + otherPackage: false, + }); + }); +}); + +describe('the app temporal-spec detection', () => { + const matches = (content: string) => + patternsOf('app', 'temporal-types-from-temporal-spec').some((pattern) => pattern.test(content)); + + it('matches a type test that compares with a global Temporal type, until it imports temporal-spec', () => { + expect({ + global: matches( + "import { expectTypeOf } from 'vitest';\n\nexpectTypeOf().toEqualTypeOf();", + ), + valueForm: matches('expectTypeOf(row.at).toEqualTypeOf();'), + importsTemporalSpec: matches( + "import type { Temporal } from 'temporal-spec';\n\nexpectTypeOf().toEqualTypeOf();", + ), + importsAfterUse: matches( + "expectTypeOf().toEqualTypeOf();\nimport type { Temporal } from 'temporal-spec';", + ), + noComparison: matches('const at: Temporal.Instant = Temporal.Now.instant();'), + }).toEqual({ + global: true, + valueForm: true, + importsTemporalSpec: false, + importsAfterUse: false, + noComparison: false, + }); + }); +}); From 24fb2a433d25a594236b768b23c1fd18958332e6 Mon Sep 17 00:00:00 2001 From: willbot Date: Thu, 22 Oct 2026 19:09:09 +0200 Subject: [PATCH 026/118] Keep the published package name out of the detection test's import text The single-import-root lint reads import-shaped text in a test as an import, so the sample imports build the published name at run time. Co-Authored-By: Claude Opus 5.5 Signed-off-by: willbot Signed-off-by: Will Madden --- .../explicit-date-time-types/detection.test.ts | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) diff --git a/test/integration/test/upgrade-instructions/explicit-date-time-types/detection.test.ts b/test/integration/test/upgrade-instructions/explicit-date-time-types/detection.test.ts index 79b00cb572ce..aa0ee0d12bb3 100644 --- a/test/integration/test/upgrade-instructions/explicit-date-time-types/detection.test.ts +++ b/test/integration/test/upgrade-instructions/explicit-date-time-types/detection.test.ts @@ -3,6 +3,8 @@ import { fileURLToPath } from 'node:url'; import { dirname, join } from 'pathe'; import { describe, expect, it } from 'vitest'; +/** The published package an application imports from, kept out of literal import text so the import-root lint reads this file as test data. */ +const published = ['@prisma', 'orm-postgres'].join('/'); const here = dirname(fileURLToPath(import.meta.url)); const repoRoot = join(here, '../../../../..'); @@ -83,18 +85,15 @@ describe.each(['app', 'extension'])('the %s explicit date and time type detectio const branded = (content: string) => matches('branded-time-types-removed', content); const interval = (content: string) => matches('interval-with-precision-typed-as-parts', content); - const snapshotImport = - "import type {\n CodecTypes as PgTypes,\n Interval,\n Time,\n Timestamp,\n Timestamptz,\n} from '@prisma/orm-postgres/target/codec-types';"; + const snapshotImport = `import type {\n CodecTypes as PgTypes,\n Interval,\n Time,\n Timestamp,\n Timestamptz,\n} from '${published}/target/codec-types';`; expect({ snapshot: branded(snapshotImport), - inlineType: branded("import { type Time } from '@prisma/orm-postgres/target/codec-types';"), - interval: interval("import type { Interval } from '@prisma/orm-postgres/contract-builder';"), + inlineType: branded(`import { type Time } from '${published}/target/codec-types';`), + interval: interval(`import type { Interval } from '${published}/contract-builder';`), inlineInterval: interval( - "import { field, type Interval } from '@prisma/orm-postgres/contract-builder';", - ), - keptName: branded( - "import type { TimestampString } from '@prisma/orm-postgres/target/codec-types';", + `import { field, type Interval } from '${published}/contract-builder';`, ), + keptName: branded(`import type { TimestampString } from '${published}/target/codec-types';`), otherPackage: branded("import { Timestamp } from 'bson';"), }).toEqual({ snapshot: true, From 203352a6d00c4e9f1272a0ff379b05b313f52d3e Mon Sep 17 00:00:00 2001 From: willbot Date: Thu, 22 Oct 2026 19:17:14 +0200 Subject: [PATCH 027/118] Drop the removed branded type imports from the last telemetry snapshot Co-Authored-By: Claude Opus 5.5 Signed-off-by: willbot Signed-off-by: Will Madden --- .../contract.d.ts | 4 ---- 1 file changed, 4 deletions(-) diff --git a/apps/telemetry-backend/migrations/snapshots/2c0677a6b6e5b60bb328a78d882483d8a00804e00c1e05fba259ff8304882558/contract.d.ts b/apps/telemetry-backend/migrations/snapshots/2c0677a6b6e5b60bb328a78d882483d8a00804e00c1e05fba259ff8304882558/contract.d.ts index 1f1826c5a46c..a7769384d106 100644 --- a/apps/telemetry-backend/migrations/snapshots/2c0677a6b6e5b60bb328a78d882483d8a00804e00c1e05fba259ff8304882558/contract.d.ts +++ b/apps/telemetry-backend/migrations/snapshots/2c0677a6b6e5b60bb328a78d882483d8a00804e00c1e05fba259ff8304882558/contract.d.ts @@ -8,11 +8,7 @@ import type { Varchar } from '@prisma/orm-postgres/target/codec-types'; import type { Numeric } from '@prisma/orm-postgres/target/codec-types'; import type { Bit } from '@prisma/orm-postgres/target/codec-types'; import type { VarBit } from '@prisma/orm-postgres/target/codec-types'; -import type { Timestamp } from '@prisma/orm-postgres/target/codec-types'; -import type { Timestamptz } from '@prisma/orm-postgres/target/codec-types'; -import type { Time } from '@prisma/orm-postgres/target/codec-types'; import type { Timetz } from '@prisma/orm-postgres/target/codec-types'; -import type { Interval } from '@prisma/orm-postgres/target/codec-types'; import type { QueryOperationTypes as PgAdapterQueryOps } from '@prisma/orm-postgres/adapter/operation-types'; import type { From 2f1984f7ccd41ce80c1c98df25eac3b380b2690d Mon Sep 17 00:00:00 2001 From: willbot Date: Thu, 22 Oct 2026 19:30:45 +0200 Subject: [PATCH 028/118] contract print writes the explicit Temporal type names The target now lists TimestamptzTemporal, DateTemporal and TimeTemporal before their second names DateTime, Date and Time, so contract print writes the explicit names. The second names will become convenience types with other defaults, and a schema print wrote with them would then change codec. contract infer is unchanged: it writes the constructor marked inferred. Co-Authored-By: Claude Opus 5.5 Signed-off-by: willbot Signed-off-by: Will Madden --- docs/reference/codec-authoring-guide.md | 2 +- packages/3-targets/3-targets/postgres/README.md | 2 +- .../postgres/src/core/type-constructors.ts | 10 +++++----- .../test/psl-print/generated-values.test.ts | 6 +++--- .../postgres/test/type-constructors.test.ts | 17 +++++++++++++++++ .../date-time-types-print.integration.test.ts | 16 ++++++++-------- ...nerated-values-roundtrip.integration.test.ts | 4 ++-- 7 files changed, 37 insertions(+), 20 deletions(-) diff --git a/docs/reference/codec-authoring-guide.md b/docs/reference/codec-authoring-guide.md index f92e35f9ea49..4ba3be54154a 100644 --- a/docs/reference/codec-authoring-guide.md +++ b/docs/reference/codec-authoring-guide.md @@ -588,7 +588,7 @@ A codec's `paramsSchema` is its data type's `params`, referenced and never resta A codec with parameters of its own sets `paramsSchema` to `dataType.params.and(ownKeys)`, or to its own keys alone when the data type declares no `params`, as `arktype/json@1` does. `postgresCodec(...)` and `sqliteCodec(...)` take the adapted codec's params schema from the data type they are given. -A type constructor maps its arguments onto the parameters and names only the codec; the data type follows from the codec. Every argument of a type constructor is a value argument, `{ kind: 'value', dataType }`, and assembly refuses any other kind with `CONTRACT.PACK_CONTRIBUTION_INVALID`; field presets keep their own argument kinds until they are removed. A value argument is a value of the named data type, read and cast as an attribute argument is (ADR 254), so `Vector(1536)` reads `1536` as a `pg/int2` and casts it to `pg/int4`, and `Vector("1536")` is refused. On Postgres every length, precision, scale and SRID is a `pg/int4`, the type of a typmod. A value argument carries no bounds, because the parameter's schema checks the value it gives. Assembly refuses a value argument whose data type no component registers (`CONTRACT.DATA_TYPE_UNREGISTERED`) or that no contract source can write (`CONTRACT.DATA_TYPE_NOT_WRITABLE`). In the TypeScript builder, the pack that registers a data type declares the TypeScript value an argument of it takes as `__dataTypeArguments` in its type, such as `{ 'pg/int4': { argument: number; parameter: number } }`: `argument` is what an author passes, and `parameter` is the type parameter the data type's reader gives for it, and `contract print` writes each argument as a literal of its data type. `inferred: true` marks the constructor written for a column of the data type when no schema names one: `contract infer` and the Prisma 7 reader write it. `contract print` writes it when it produces the column's codec, and otherwise the first constructor in the stack that does, so a `timestamptz` column with the `Temporal` codec prints `DateTime`. It is called with the fewest arguments that give the column's parameters in normal form, so a `numeric(10,0)` column is written `Numeric(10)`. The Prisma 7 reader builds a contract rather than printing PSL, so it passes every parameter the Prisma 7 column gives. At most one constructor per data type carries the mark. A data type with no marked constructor, such as `pg/tsquery`, is still recognised, but `contract infer` cannot write a column of it and fails with `CONTRACT.INFER_COLUMN_TYPE_UNCLAIMED`. +A type constructor maps its arguments onto the parameters and names only the codec; the data type follows from the codec. Every argument of a type constructor is a value argument, `{ kind: 'value', dataType }`, and assembly refuses any other kind with `CONTRACT.PACK_CONTRIBUTION_INVALID`; field presets keep their own argument kinds until they are removed. A value argument is a value of the named data type, read and cast as an attribute argument is (ADR 254), so `Vector(1536)` reads `1536` as a `pg/int2` and casts it to `pg/int4`, and `Vector("1536")` is refused. On Postgres every length, precision, scale and SRID is a `pg/int4`, the type of a typmod. A value argument carries no bounds, because the parameter's schema checks the value it gives. Assembly refuses a value argument whose data type no component registers (`CONTRACT.DATA_TYPE_UNREGISTERED`) or that no contract source can write (`CONTRACT.DATA_TYPE_NOT_WRITABLE`). In the TypeScript builder, the pack that registers a data type declares the TypeScript value an argument of it takes as `__dataTypeArguments` in its type, such as `{ 'pg/int4': { argument: number; parameter: number } }`: `argument` is what an author passes, and `parameter` is the type parameter the data type's reader gives for it, and `contract print` writes each argument as a literal of its data type. `inferred: true` marks the constructor written for a column of the data type when no schema names one: `contract infer` and the Prisma 7 reader write it. `contract print` writes it when it produces the column's codec, and otherwise the first constructor in the stack that does, so a `timestamptz` column with the `Temporal` codec prints `TimestamptzTemporal`, which the PostgreSQL target lists before its second name `DateTime`. It is called with the fewest arguments that give the column's parameters in normal form, so a `numeric(10,0)` column is written `Numeric(10)`. The Prisma 7 reader builds a contract rather than printing PSL, so it passes every parameter the Prisma 7 column gives. At most one constructor per data type carries the mark. A data type with no marked constructor, such as `pg/tsquery`, is still recognised, but `contract infer` cannot write a column of it and fails with `CONTRACT.INFER_COLUMN_TYPE_UNCLAIMED`. ```ts import type { AuthoringTypeNamespace } from '@internal/framework-components/authoring'; diff --git a/packages/3-targets/3-targets/postgres/README.md b/packages/3-targets/3-targets/postgres/README.md index 57d882db25fb..1c13ae89e6d9 100644 --- a/packages/3-targets/3-targets/postgres/README.md +++ b/packages/3-targets/3-targets/postgres/README.md @@ -26,7 +26,7 @@ Provides the Postgres target descriptor (`SqlControlTargetDescriptor`) for CLI c - **Generated Defaults Policy**: Treats client-generated defaults as non-DB defaults when emitting DDL - **Database Dependency Consumption**: The planner extracts database dependencies from the configured framework components (passed as `frameworkComponents`), verifies each dependency against the live schema, and only emits install operations when required. The runner reuses the same metadata for post-apply verification, so there are no hardcoded extension mappings—database dependencies stay component-owned. - **Storage Type Planning**: The planner dispatches storage type hooks for `storage.types` and emits type operations before table creation when supported by the policy -- **Contract-to-PSL Printing**: Implements the `buildPslContract` descriptor hook, which `prisma contract print` uses. It takes a Postgres contract the contract serializer accepted, from any source, and returns the Prisma 8 PSL document that reads back as the same contract. It does not check the contract's structure again; a broken structure is an internal error. It writes models, value objects, named types, domain and native enums, keys with their names, indexes, checks, relations, polymorphism, control policies, row-level security (`@@rls`, policies, and roles in `namespace unbound`) and defaults. A column is written as a type constructor the configured stack contributes that reads back with its codec, native type and type parameters: the constructor marked `inferred` for the column's data type, which is the one `contract infer` writes, when it does, or else the first constructor in the stack that does, such as `DateTime` or `TimestamptzTemporal(3)` for a `Temporal`-backed `timestamptz` column, or an extension's `pgvector.Vector(3)`. A value-object member has no column, so its type goes through the stack the same way: the stack's codec names the native type for the member's type parameters, and the member is written as the type constructor for that native type, called with those parameters (`Numeric(10, 2)`); a member typed by a domain enum is written by the enum's name. The contract does not record which named type a member used, so a member written `code Short`, with `types { Short = VarChar(10) }`, is printed as the type it stands for, `code VarChar(10)`; a model field keeps `Short`, because its column names the named type. A literal default is written by the data type of the column's codec. The family passes the stack's type constructors, codecs and data types to the hook for this. Anything it cannot write so that it reads back the same is refused by name with `CONTRACT.PRINT_UNSUPPORTED` (the full list is in `docs/reference/error-reference.md`, one function per case in `src/core/psl-print/refusals.ts`); nothing is dropped. It lives in `src/core/psl-print/`, the counterpart of the `inferPslContract` hook in `src/core/psl-infer/`, which writes PSL from a live database. Both build with the shared builders in `src/core/psl-build/`: literals, index and check attributes, and the native enum block. Both find the constructor marked `inferred` for a data type through the SQL family's `@internal/family-sql/psl-build`. Model and field names come from the contract; `@@map` and `@map` appear only where the name the PSL source would derive differs. A unique constraint prints as `@@unique` and a unique index as `@@index(..., unique: true)`; every index prints explicitly, and a relation writes `index: false` when nothing backs its foreign key, no `index` argument when a relation that says nothing would be backed by the same object (`defaultForeignKeyIndex`), and otherwise `index: ""` naming the index, or the key whose first columns are the foreign key's. Execution generators join back onto their columns, through whatever the configured stack contributes, extensions included. A column that is not a list and whose PSL type stands for its data type prints as the call of the field preset that writes its codec with its generators, built from the preset's print form, when the preset accepts that call and it reads back with the column's codec, type params and generators. A column typed by a named type, a value object or an enum, and a column whose call fails those checks, such as one without a value for a required argument, does not. A column with only a create generator then prints it as `@default((...))`, the call of the default function that declares `printArguments` for it; any other column is refused. +- **Contract-to-PSL Printing**: Implements the `buildPslContract` descriptor hook, which `prisma contract print` uses. It takes a Postgres contract the contract serializer accepted, from any source, and returns the Prisma 8 PSL document that reads back as the same contract. It does not check the contract's structure again; a broken structure is an internal error. It writes models, value objects, named types, domain and native enums, keys with their names, indexes, checks, relations, polymorphism, control policies, row-level security (`@@rls`, policies, and roles in `namespace unbound`) and defaults. A column is written as a type constructor the configured stack contributes that reads back with its codec, native type and type parameters: the constructor marked `inferred` for the column's data type, which is the one `contract infer` writes, when it does, or else the first constructor in the stack that does, such as `TimestamptzTemporal` or `TimestamptzTemporal(3)` for a `Temporal`-backed `timestamptz` column, because the target lists each explicit type before a second name for it such as `DateTime`, or an extension's `pgvector.Vector(3)`. A value-object member has no column, so its type goes through the stack the same way: the stack's codec names the native type for the member's type parameters, and the member is written as the type constructor for that native type, called with those parameters (`Numeric(10, 2)`); a member typed by a domain enum is written by the enum's name. The contract does not record which named type a member used, so a member written `code Short`, with `types { Short = VarChar(10) }`, is printed as the type it stands for, `code VarChar(10)`; a model field keeps `Short`, because its column names the named type. A literal default is written by the data type of the column's codec. The family passes the stack's type constructors, codecs and data types to the hook for this. Anything it cannot write so that it reads back the same is refused by name with `CONTRACT.PRINT_UNSUPPORTED` (the full list is in `docs/reference/error-reference.md`, one function per case in `src/core/psl-print/refusals.ts`); nothing is dropped. It lives in `src/core/psl-print/`, the counterpart of the `inferPslContract` hook in `src/core/psl-infer/`, which writes PSL from a live database. Both build with the shared builders in `src/core/psl-build/`: literals, index and check attributes, and the native enum block. Both find the constructor marked `inferred` for a data type through the SQL family's `@internal/family-sql/psl-build`. Model and field names come from the contract; `@@map` and `@map` appear only where the name the PSL source would derive differs. A unique constraint prints as `@@unique` and a unique index as `@@index(..., unique: true)`; every index prints explicitly, and a relation writes `index: false` when nothing backs its foreign key, no `index` argument when a relation that says nothing would be backed by the same object (`defaultForeignKeyIndex`), and otherwise `index: ""` naming the index, or the key whose first columns are the foreign key's. Execution generators join back onto their columns, through whatever the configured stack contributes, extensions included. A column that is not a list and whose PSL type stands for its data type prints as the call of the field preset that writes its codec with its generators, built from the preset's print form, when the preset accepts that call and it reads back with the column's codec, type params and generators. A column typed by a named type, a value object or an enum, and a column whose call fails those checks, such as one without a value for a required argument, does not. A column with only a create generator then prints it as `@default((...))`, the call of the default function that declares `printArguments` for it; any other column is refused. - **Database-to-PSL inference**: Implements the `inferPslContract` descriptor hook, which `prisma contract infer` uses. Introspection records each column's element type as the database reports it (`format_type`, its kind, schema and name). Infer resolves that reported type through the data types of the configured stack, the target's and the extension packs', and writes the constructor marked `inferred` for the data type, with the fewest arguments that give the same parameters (`numeric(10,0)` is written `Numeric(10)`); a list column adds `[]`, and a native enum column is written `pg.enum()`. When a column's type is claimed by no data type, or by one with no marked constructor (such as `tsquery`), infer writes nothing and fails with `CONTRACT.INFER_COLUMN_TYPE_UNCLAIMED`, listing every such column as `.

.: `. No configured pack has a type for such a column. When the type comes from a database extension that has a Prisma 8 pack, such as `pgvector` or `postgis`, adding the pack to `extensions` in `prisma.config.ts` lets infer write it, for the forms of the type the pack supports. Otherwise, as for domains, `"char"`, `hstore` and `tsquery`, the column's type must change, or the column must go. The rule itself (resolving a reported type through the data types, the fewest-arguments call, the unclaimed error) lives in the SQL family's `@internal/family-sql/psl-build`; the target adds the walk over its schema tree and the native enum handling in `src/core/psl-infer/`. - **`Temporal` for the target's own code**: The `Temporal`-backed codecs (`pg/date-temporal@1`, `pg/time-temporal@1`, `pg/timestamp-temporal@1`, `pg/timestamp-instant@1` and `pg/timestamptz-temporal@1`), when they decode, and the `instantNow`, `plainDateTimeNow`, `plainDateNow` and `plainTimeNow` generators get `Temporal` from `requireTemporal()` (`src/core/require-temporal.ts`): the runtime's global `Temporal` when it has one, else the fallback `Temporal`, else it throws `RUNTIME.TEMPORAL_UNAVAILABLE`. The text codecs (`pg/date-string@1`, `pg/time-string@1`, `pg/timestamp-string@1`, `pg/timestamptz-string@1` and `pg/timetz@1`), `pg/timestamptz-date@1`, `pg/timestamp-date@1`, `pg/interval@1`, `pg/interval-string@1`, the `timestampNow` generator and the string generators (`timestampStringNow`, `timestamptzStringNow`, `dateStringNow`, `timeStringNow` and `timetzNow`) do not use `Temporal`, so an application that uses only them reads and writes its date and time columns without one. The target's control entry (`@internal/target-postgres/control`) sets the fallback `Temporal`, from `temporal-polyfill`, when it is loaded. Nothing sets `globalThis.Temporal`. No other entry of this package imports `temporal-polyfill`. `temporal-polyfill` is a required peer dependency of this package and of the published `@prisma/orm-target-postgres` and `@prisma/orm-postgres`, so a project that already installs it for its own code has one copy. npm, pnpm and bun install it automatically; a project that uses Yarn adds `temporal-polyfill` to its own dependencies. In the published package, `@prisma/orm-target-postgres/target` is the aggregate of every entry of this package, the control entry included, so it loads the control entry too; an application imports the entry it needs, for example `@prisma/orm-target-postgres/target/runtime`. The fallback is held once per process, and every `Temporal`-backed codec in the process uses it. An application process that loads no control-plane code and has no global `Temporal` gets `RUNTIME.TEMPORAL_UNAVAILABLE` when one of those codecs decodes or one of those generators runs. An application that loads control-plane code in its own process, such as server code under `vite dev` with the Prisma Vite plugin or a script that calls the control client, decodes dates without its own `Temporal`, and then fails in production. An application that uses the Temporal codecs must load its own `Temporal`. Every signature that names a `Temporal` type takes it from `temporal-spec`, a package of type declarations with no runtime code (`import type { Temporal } from 'temporal-spec'`), never from a global declaration, so a Temporal field is typed precisely in a project that has no global `Temporal` declarations. `tsconfig.no-temporal-global.json` type-checks `src` without them. - **Runtime List Framing**: Parses inbound Postgres array text for contract-declared list columns before applying the scalar element codec. Builtin arrays and enum arrays therefore share one target-owned decode path; fixed-scale `numeric(30,10)[]` reads database-normalized text such as `"1.5000000000"`, matching scalar numeric decoding rather than the previous driver numeric-array float spelling `"1.5"`. diff --git a/packages/3-targets/3-targets/postgres/src/core/type-constructors.ts b/packages/3-targets/3-targets/postgres/src/core/type-constructors.ts index 34ef9ecc5760..0cda1602c106 100644 --- a/packages/3-targets/3-targets/postgres/src/core/type-constructors.ts +++ b/packages/3-targets/3-targets/postgres/src/core/type-constructors.ts @@ -134,7 +134,7 @@ export const postgresNativeAuthoringTypes = { typeParams: { precision: { kind: 'arg', index: 0 } }, }, }, - Time: { + TimeTemporal: { kind: 'typeConstructor', documentation: 'A time of day without a time zone, represented as Temporal.PlainTime.', args: [{ kind: 'value', dataType: PG_SIZE_TYPE, name: 'precision', optional: true }], @@ -143,7 +143,7 @@ export const postgresNativeAuthoringTypes = { typeParams: { precision: { kind: 'arg', index: 0 } }, }, }, - TimeTemporal: { + Time: { kind: 'typeConstructor', documentation: 'A time of day without a time zone, represented as Temporal.PlainTime.', args: [{ kind: 'value', dataType: PG_SIZE_TYPE, name: 'precision', optional: true }], @@ -186,12 +186,12 @@ export const postgresNativeAuthoringTypes = { documentation: 'A single-precision floating-point number.', output: { codecId: 'pg/float4@1' }, }, - Date: { + DateTemporal: { kind: 'typeConstructor', documentation: 'A calendar date represented as Temporal.PlainDate.', output: { codecId: 'pg/date-temporal@1' }, }, - DateTemporal: { + Date: { kind: 'typeConstructor', documentation: 'A calendar date represented as Temporal.PlainDate.', output: { codecId: 'pg/date-temporal@1' }, @@ -312,6 +312,6 @@ export const postgresNativeAuthoringTypes = { * The base scalars and native types as type constructors. The target contributes them, so PSL and the TypeScript builder's `type.*` see the same names. */ export const postgresScalarAndNativeTypeConstructors = { - ...postgresScalarAuthoringTypes, ...postgresNativeAuthoringTypes, + ...postgresScalarAuthoringTypes, } as const satisfies AuthoringTypeNamespace; diff --git a/packages/3-targets/3-targets/postgres/test/psl-print/generated-values.test.ts b/packages/3-targets/3-targets/postgres/test/psl-print/generated-values.test.ts index 269cbcac0948..5a3d69c2c60c 100644 --- a/packages/3-targets/3-targets/postgres/test/psl-print/generated-values.test.ts +++ b/packages/3-targets/3-targets/postgres/test/psl-print/generated-values.test.ts @@ -124,13 +124,13 @@ describe('generated values', () => { it('writes a now generator on create and on update', () => { const now = generator(postgresNowGeneratorIds[PG_TIMESTAMPTZ_TEMPORAL_CODEC_ID]); expect(withGenerator(timestamptz, { onCreate: now, onUpdate: now })).toBe( - 'value DateTime @onCreate(now()) @onUpdate(now())', + 'value TimestamptzTemporal @onCreate(now()) @onUpdate(now())', ); }); it("writes a time now generator whose precision is the column's as @onUpdate(now())", () => { expect(withGenerator(time3, { onUpdate: generator('plainTimeNow', { precision: 3 }) })).toBe( - 'value Time(3) @onUpdate(now())', + 'value TimeTemporal(3) @onUpdate(now())', ); }); @@ -140,7 +140,7 @@ describe('generated values', () => { { ...timestamptz, default: { kind: 'function', expression: 'now()' } }, { onCreate: generator('instantNow') }, ), - ).toBe('value DateTime @default(now()) @onCreate(now())'); + ).toBe('value TimestamptzTemporal @default(now()) @onCreate(now())'); }); it('writes a generator through the generator function an extension contributes', () => { diff --git a/packages/3-targets/3-targets/postgres/test/type-constructors.test.ts b/packages/3-targets/3-targets/postgres/test/type-constructors.test.ts index dfb23413e88a..0f70d1958bbe 100644 --- a/packages/3-targets/3-targets/postgres/test/type-constructors.test.ts +++ b/packages/3-targets/3-targets/postgres/test/type-constructors.test.ts @@ -1,6 +1,7 @@ import type { AuthoringTypeNamespace } from '@internal/framework-components/authoring'; import { collectScalarTypeConstructors, + findAuthoringTypeConstructorCall, instantiateAuthoringTypeConstructor, validateAuthoringTypeParams, } from '@internal/framework-components/authoring'; @@ -17,6 +18,22 @@ import { } from '../src/core/type-constructors'; import postgresTargetPack from '../src/exports/pack'; +describe('the constructor contract print writes for a Temporal column', () => { + it.each([ + [{ codecId: 'pg/timestamptz-temporal@1' }, ['TimestamptzTemporal'], []], + [{ codecId: 'pg/date-temporal@1' }, ['DateTemporal'], []], + [{ codecId: 'pg/time-temporal@1', typeParams: { precision: 0 } }, ['TimeTemporal'], [0]], + ])( + 'is the explicit type for %o, not a name the convenience types will take', + (output, path, args) => { + expect(findAuthoringTypeConstructorCall(postgresTargetPack.authoring.type, output)).toEqual({ + path, + args, + }); + }, + ); +}); + describe('the type constructors the target contributes', () => { it('are the scalar, native and target-only ones, so PSL and type.* see the same names', () => { expect(postgresTargetPack.authoring.type).toEqual({ diff --git a/test/integration/test/psl-print/date-time-types-print.integration.test.ts b/test/integration/test/psl-print/date-time-types-print.integration.test.ts index 27ede56fc0b0..15c65f9b93a1 100644 --- a/test/integration/test/psl-print/date-time-types-print.integration.test.ts +++ b/test/integration/test/psl-print/date-time-types-print.integration.test.ts @@ -39,25 +39,25 @@ function fieldTypes(printed: string): Record { } describe('contract print spells each date and time column', () => { - it('as the type contract infer writes, or the first constructor that reads the column back', async () => { + it('as the type contract infer writes, or else as the explicit type that reads the column back', async () => { const contract = await readPsl(SCHEMA); expect(fieldTypes(printContract(contract).text)).toEqual({ id: 'Int', - instant: 'DateTime', - zoned: 'DateTime', + instant: 'TimestamptzTemporal', + zoned: 'TimestamptzTemporal', zoned3: 'TimestamptzTemporal(3)', local: 'TimestampTemporal', local3: 'TimestampTemporal(3)', utcDate: 'TimestampJsDate(3)', utcAt: 'TimestampInstant(6)', - dayT: 'Date', - clockT: 'Time(0)', + dayT: 'DateTemporal', + clockT: 'TimeTemporal(0)', period: 'IntervalParts', span: 'IntervalString(3)', - day: 'Date', - clock: 'Time', - clock3: 'Time(3)', + day: 'DateTemporal', + clock: 'TimeTemporal', + clock3: 'TimeTemporal(3)', zonedText: 'TimestamptzString(3)', localText: 'TimestampString(3)', dayText: 'DateString', diff --git a/test/integration/test/psl-print/generated-values-roundtrip.integration.test.ts b/test/integration/test/psl-print/generated-values-roundtrip.integration.test.ts index b277988e4afe..c6e32f32c0ef 100644 --- a/test/integration/test/psl-print/generated-values-roundtrip.integration.test.ts +++ b/test/integration/test/psl-print/generated-values-roundtrip.integration.test.ts @@ -166,8 +166,8 @@ model Event { fieldLines: [ 'id String @id @onCreate(uuid(7))', 'code VarChar(32) @onCreate(nanoid())', - 'seenAt Time(3) @onUpdate(now())', - 'createdAt DateTime @default(now()) @onCreate(now())', + 'seenAt TimeTemporal(3) @onUpdate(now())', + 'createdAt TimestamptzTemporal @default(now()) @onCreate(now())', ], roundTrip: serializedWithoutCapabilities(authored), }); From 51806ec7bb94fb5c6c2b0fde7d0f91cbe7298d6c Mon Sep 17 00:00:00 2001 From: willbot Date: Thu, 22 Oct 2026 19:32:02 +0200 Subject: [PATCH 029/118] Final review fixes for the explicit date and time types - The adapter README names the time column helpers that exist. - The extension upgrade instructions also cover the temporal-spec types and the new aggregate rows in an emitted contract.d.ts, and their detection is tested. - The scalar types reference says a Date drops digits past milliseconds, so an equality filter with a value read back can miss a row, and names TimestampInstant and TimestampString to keep them. - withoutUtcDesignator is no longer exported. Co-Authored-By: Claude Opus 5.5 Signed-off-by: willbot Signed-off-by: Will Madden --- docs/reference/scalar-types.md | 2 +- .../src/core/temporal-codec-helpers.ts | 2 +- .../3-targets/6-adapters/postgres/README.md | 2 +- .../detection.test.ts | 6 ++++-- .../extension/instructions.md | 20 +++++++++++++++++++ 5 files changed, 27 insertions(+), 5 deletions(-) diff --git a/docs/reference/scalar-types.md b/docs/reference/scalar-types.md index 867c754bb1e8..0bee42f30f4f 100644 --- a/docs/reference/scalar-types.md +++ b/docs/reference/scalar-types.md @@ -103,7 +103,7 @@ These are the current names. The Postgres and SQLite rename project will change | `Bytes` | `field.bytes()` | `pg/bytea@1` | `bytea` | `Uint8Array` | | `pg.enum(Name)` | — | `pg/enum@1` | the native enum type | `string` | -`TimestampJsDate` and `TimestampInstant` read the date and time a `timestamp` column stores as UTC, and write a value's UTC date and time with no offset, so a value the application writes reads back as the same instant whatever the time zone of the server, the session or the application. A database default is different: PostgreSQL fills a `timestamp` column's `@default(now())` in the session's time zone, so on a server whose `TimeZone` is not UTC that value reads back shifted by the zone's offset. `@onCreate(now())` writes the client's time in UTC and reads back unshifted. They refuse `infinity`, `-infinity` and a value after `+275760-09-13T00:00:00`, which no `Date` or `Temporal.Instant` holds, and name `TimestampString`, which holds them. `IntervalString` reads every `IntervalStyle` and writes an ISO 8601 duration; it refuses any other text on write. +`TimestampJsDate` and `TimestampInstant` read the date and time a `timestamp` column stores as UTC, and write a value's UTC date and time with no offset, so a value the application writes reads back as the same instant whatever the time zone of the server, the session or the application. A database default is different: PostgreSQL fills a `timestamp` column's `@default(now())` in the session's time zone, so on a server whose `TimeZone` is not UTC that value reads back shifted by the zone's offset. `@onCreate(now())` writes the client's time in UTC and reads back unshifted. A `Date` holds milliseconds, so on a `TimestampJsDate` column with a precision above 3 a read drops the extra digits, and an equality filter with a value read back misses a row stored with microseconds. Use `TimestampInstant` or `TimestampString` to keep them. They refuse `infinity`, `-infinity` and a value after `+275760-09-13T00:00:00`, which no `Date` or `Temporal.Instant` holds, and name `TimestampString`, which holds them. `IntervalString` reads every `IntervalStyle` and writes an ISO 8601 duration; it refuses any other text on write. A literal default of a date, time, interval or bytes type is written with the tag of its type, as in `@default(timestamptz`2024-01-01T00:00:00Z`)` or `@default(bytea`\x0102`)`; a quoted string is refused with a message that shows the tag. It is stored in the one form its data type stores, whichever codec the column uses and however the default was written, and a default with more fraction digits or more scale than its column holds is refused rather than rounded; [ADR 254](../architecture%20docs/adrs/ADR%20254%20-%20Data%20types%20and%20casts.md#date-and-time-types) states each form. diff --git a/packages/3-targets/3-targets/postgres/src/core/temporal-codec-helpers.ts b/packages/3-targets/3-targets/postgres/src/core/temporal-codec-helpers.ts index cc935e399a6e..e82e7903b291 100644 --- a/packages/3-targets/3-targets/postgres/src/core/temporal-codec-helpers.ts +++ b/packages/3-targets/3-targets/postgres/src/core/temporal-codec-helpers.ts @@ -191,6 +191,6 @@ export function utcTimestampText(value: Date, codecId: string): string { } /** ISO text in UTC without its trailing `Z`. */ -export function withoutUtcDesignator(isoText: string): string { +function withoutUtcDesignator(isoText: string): string { return isoText.endsWith('Z') ? isoText.slice(0, -1) : isoText; } diff --git a/packages/3-targets/6-adapters/postgres/README.md b/packages/3-targets/6-adapters/postgres/README.md index ca3748e32747..42980c75cfb2 100644 --- a/packages/3-targets/6-adapters/postgres/README.md +++ b/packages/3-targets/6-adapters/postgres/README.md @@ -140,7 +140,7 @@ flowchart TD **Column Types Export (`column-types.ts`)** - Exports column descriptors for built-in types; enums are declared with `enumType` from the contract builder -- Parameterized helpers: ``rawColumn(sql`tsvector`)`` for a column of a type no other column type covers, its type written as a `sql` literal (ADR 273), `charColumn(length)`, `varcharColumn(length)`, `numericColumn(precision, scale?)`, `bitColumn(length)`, `varbitColumn(length)`, `timeColumn(precision?)`, `timetzColumn(precision?)`, `intervalPartsColumn(precision?)`, `intervalStringColumn(precision?)` +- Parameterized helpers: ``rawColumn(sql`tsvector`)`` for a column of a type no other column type covers, its type written as a `sql` literal (ADR 273), `charColumn(length)`, `varcharColumn(length)`, `numericColumn(precision, scale?)`, `bitColumn(length)`, `varbitColumn(length)`, `timeTemporalColumn(precision?)`, `timeStringColumn(precision?)`, `timetzColumn(precision?)`, `intervalPartsColumn(precision?)`, `intervalStringColumn(precision?)` - Exports raw JSON helpers: - `jsonColumn`, `jsonbColumn` — untyped raw JSON / JSONB column descriptors diff --git a/test/integration/test/upgrade-instructions/explicit-date-time-types/detection.test.ts b/test/integration/test/upgrade-instructions/explicit-date-time-types/detection.test.ts index aa0ee0d12bb3..a0466dea0b3e 100644 --- a/test/integration/test/upgrade-instructions/explicit-date-time-types/detection.test.ts +++ b/test/integration/test/upgrade-instructions/explicit-date-time-types/detection.test.ts @@ -106,9 +106,11 @@ describe.each(['app', 'extension'])('the %s explicit date and time type detectio }); }); -describe('the app temporal-spec detection', () => { +describe.each(['app', 'extension'])('the %s temporal-spec detection', (audience) => { const matches = (content: string) => - patternsOf('app', 'temporal-types-from-temporal-spec').some((pattern) => pattern.test(content)); + patternsOf(audience, 'temporal-types-from-temporal-spec').some((pattern) => + pattern.test(content), + ); it('matches a type test that compares with a global Temporal type, until it imports temporal-spec', () => { expect({ diff --git a/upgrade-instructions/pending/explicit-date-time-types/extension/instructions.md b/upgrade-instructions/pending/explicit-date-time-types/extension/instructions.md index d7f289e91663..cc6a9dccf20a 100644 --- a/upgrade-instructions/pending/explicit-date-time-types/extension/instructions.md +++ b/upgrade-instructions/pending/explicit-date-time-types/extension/instructions.md @@ -29,6 +29,16 @@ changes: glob: "**/*.{ts,mts,cts,tsx}" matches: - 'import\s+(?:type\s+)?\{[^}]*(?().toEqualTypeOf(); +``` + +If the test file also creates `Temporal` values, import the type under another name, such as `import type { Temporal as TemporalTypes } from 'temporal-spec'`, so the runtime `Temporal` stays in scope. From a082295b6cdfffca9a0571a52c60670d32c6e0e2 Mon Sep 17 00:00:00 2001 From: willbot Date: Thu, 22 Oct 2026 19:48:27 +0200 Subject: [PATCH 030/118] TML-3709: Convenience types are type choices that name their explicit constructors; slice 4 spec Co-Authored-By: Claude Opus 5.5 Signed-off-by: willbot Signed-off-by: Will Madden --- ...e an explicit type from their arguments.md | 35 ++++++---- .../design-notes.md | 2 +- .../slices/04-convenience-types/spec.md | 68 +++++++++++++++++++ projects/datetime-convenience-types/spec.md | 2 +- 4 files changed, 90 insertions(+), 17 deletions(-) create mode 100644 projects/datetime-convenience-types/slices/04-convenience-types/spec.md diff --git a/docs/architecture docs/adrs/ADR 279 - Convenience date and time types choose an explicit type from their arguments.md b/docs/architecture docs/adrs/ADR 279 - Convenience date and time types choose an explicit type from their arguments.md index 36021b0f83b3..673e94a7ceef 100644 --- a/docs/architecture docs/adrs/ADR 279 - Convenience date and time types choose an explicit type from their arguments.md +++ b/docs/architecture docs/adrs/ADR 279 - Convenience date and time types choose an explicit type from their arguments.md @@ -3,7 +3,6 @@ **Status:** Proposed **Builds on:** [ADR 241 — Scalar types use the authoring type-constructor channel](ADR%20241%20-%20Scalar%20types%20use%20the%20authoring%20type-constructor%20channel.md) **Amends:** [ADR 254 — Data types and casts](ADR%20254%20-%20Data%20types%20and%20casts.md), [ADR 257 — Scalar types are named after the target on every surface](ADR%20257%20-%20Scalar%20types%20are%20named%20after%20the%20target%20on%20every%20surface.md), [ADR 275 — Client generators are field attributes, and a type alias is expanded into each column](ADR%20275%20-%20Client%20generators%20are%20field%20attributes%2C%20and%20a%20type%20alias%20is%20expanded%20into%20each%20column.md) -**Brings back:** [ADR 246 — Option arguments and select templates for authoring helpers](ADR%20246%20-%20Option%20arguments%20and%20select%20templates%20for%20authoring%20helpers.md), which ADR 275 supersedes, for type constructors only ## At a glance @@ -34,7 +33,7 @@ The contract stores what the explicit type stores: the codec, its data type and 1. **Date and time types have two layers.** Each explicit type uses one codec, so it fixes one column type and one application value, and is named after the database as ADR 257 says. Each convenience type is named for what a field means in the application, and becomes exactly one explicit type. 2. **A convenience type's arguments choose its explicit type.** `value` says what the application reads: a JavaScript `Date`, a `Temporal` object or a string. `column` says the PostgreSQL column type. Each has a default that the convenience type declares. Any other argument, such as `precision`, is passed on to the chosen explicit type. -3. **A convenience type is a type constructor whose arguments choose its codec.** A type constructor is how PSL defines a type name that can take arguments, such as `Timestamptz(3)`. Today each one always uses the same codec. A type constructor may now choose its codec from its arguments. +3. **A convenience type is a type choice: a type declaration whose arguments name the explicit type it becomes.** It declares its option arguments, each with a fixed set of values written as bare names and a default, the arguments it passes on (such as `precision`), and a table from each allowed combination of option values to the name of an explicit type constructor, with any parameter defaults for that combination. An unlisted combination is refused. 4. **The contract stores only the explicit type.** The contract format, the codecs, the data types, the storage hash and the execution hash are the same whether a schema writes the convenience type or the explicit type it becomes. 5. **The PostgreSQL target provides four convenience types:** @@ -76,14 +75,13 @@ The convenience types give that user one name per concept, with defaults that wo ## How it works -### A type constructor chooses its codec +### A type choice names its explicit types -A type constructor's output is an `AuthoringStorageTypeTemplate`: a `codecId`, and a `typeParams` template whose values may refer to the constructor's arguments. Today `codecId` is a plain string: +An explicit type is a type constructor: it names one codec, and maps its arguments onto that codec's parameters. ```ts -Timestamptz: { +TimestamptzTemporal: { kind: 'typeConstructor', - documentation: 'An instant represented as Temporal.Instant, with optional fractional-second precision.', args: [{ kind: 'number', name: 'precision', integer: true, optional: true }], output: { codecId: 'pg/timestamptz-temporal@1', @@ -92,17 +90,22 @@ Timestamptz: { }, ``` -A convenience constructor's `codecId` is chosen by its `value` and `column` arguments. ADR 246 introduced the two pieces this needs, for the `temporal.*` field presets such as `temporal.timestamp(3, onCreate: now)`: +A convenience type is a different kind of declaration in the same namespace, a type choice. It names no codec. It declares its option arguments and a table whose rows each name an explicit type constructor: -- an argument that takes one of a fixed set of bare names (`AuthoringOption`, `kind: 'option'`); -- a template value that picks one case by the value of such an argument (`AuthoringSelectRef`, `kind: 'select'`). +| `value` | `column` | Explicit type | Parameter defaults | +| --- | --- | --- | --- | +| `jsDate` (default) | `timestamptz` (default) | `TimestamptzJsDate` | precision 3 | +| `temporal` | `timestamptz` | `TimestamptzTemporal` | | +| `string` | `timestamptz` | `TimestamptzString` | | +| `jsDate` | `timestamp` | `TimestampJsDate` | precision 3 | +| `temporal` | `timestamp` | `TimestampInstant` | | +| `string` | `timestamp` | `TimestampString` | | -ADR 275 removes both together with the presets. This decision keeps them, for type constructors, and lets a `select` give a type constructor's `codecId`. That is more than a new case: +That is `DateTime`'s table; the other three are declared the same way. Resolving a call looks up the row for its option values, then instantiates the named explicit constructor with the passed-on arguments, so the result is exactly what writing the explicit type gives. A combination with no row, such as `Date(value: jsDate)`, is refused at the argument that makes it invalid, and the message lists the values that argument accepts with the others as written. Assembly refuses a table that names a constructor no component registers. -- `codecId` is typed `string` in `AuthoringStorageTypeTemplate`, and many places read it as a plain string: the control stack, the PSL column resolution, the attribute specs, the generator attributes, the inferred-constructor map, the Prisma 7 reader and the MongoDB interpreter. Each must resolve the template against the call's arguments first. -- The TypeScript contract builder works out a column's application type at compile time from its `codecId`. Its type-level resolver, `ResolveTemplateValue`, cannot evaluate a `select`; ADR 246 bars `select` from a preset's `codecId` for that reason. The resolver must learn to evaluate it, so that a convenience type written in a TypeScript contract gets an application type as precise as an explicit one. A TypeScript contract reaches the convenience types through the `type.*` helpers that ADR 275 gives every PSL type constructor. +The field-preset option arguments and select templates (ADR 246) are not used: a select resolves a template value and has no defaults and no refusal, while a type choice resolves to a named constructor, which is what "becomes one explicit type" means. -A combination a convenience type does not list, such as `Date(value: jsDate)`, is an error. The PSL interpreter reports it at the argument when it reads the schema, and the message lists the choices the type accepts. +Every place that reads a field's codec reads the codec its call resolves to, including through a `types {}` alias: the PSL column resolution, the `@default` value check and the `@onCreate`/`@onUpdate` generator lookup, the Prisma 7 reader and the TypeScript builder, whose `type.*` helpers (ADR 275) include the type choices and resolve their application type at compile time from literal arguments. ### What the contract and the tools see @@ -131,9 +134,9 @@ The `String` and `JsDate` variants are not needed. A contributed alias takes no - **String values change form.** An application that reads a string date or time type gets ISO 8601 text instead of PostgreSQL's display text, for example `2024-01-01T00:00:00Z` instead of `2024-01-01 00:00:00+00`. The codec ids and the contract do not change. - **Temporal fields are typed from `temporal-spec`,** a package of TypeScript declarations with no runtime code, instead of an undeclared global `Temporal`. A Temporal field is then never typed `any` in a project without the polyfill's global declarations, and no polyfill code reaches an application that does not opt into Temporal values. A missing global `Temporal` at runtime is still reported when a Temporal codec or generator first runs. - **`Interval` reads the same object as before, under the name `IntervalParts`;** `Duration` reads an ISO string by default. -- ADR 254 says a type constructor names one codec and maps its arguments onto parameters. A type constructor may now also choose the codec. +- ADR 254 says a type constructor names one codec and maps its arguments onto parameters. That stays true; a type choice is a second kind of type declaration that resolves to a type constructor. - ADR 257 says the second codec of a stored type adds a suffix. Where a stored type has several codecs, every one now has a suffix, and the unsuffixed name of the concept belongs to a convenience type. -- ADR 275's removal of the `option` argument kind and the `select` template does not apply to type constructors. Its `String` and `JsDate` alias variants are not built. +- ADR 275's `String` and `JsDate` variants of the contributed `temporal.*` aliases are not built. - The planned rename of the PostgreSQL and SQLite types, which applies ADR 257 to those targets (for example `Int` to `Int4`), keeps `DateTime` as the convenience type instead of removing it, and adds the renames in item 6. - The editor's completion offers each convenience type's `value` and `column` choices, and its hover text gives the explicit type each choice becomes. @@ -148,6 +151,8 @@ The `String` and `JsDate` variants are not needed. A contributed alias takes no **One codec for `DateTime`, with the column type as a parameter.** A codec converts values of exactly one data type, and a column stores the data type its codec represents (ADR 254). A codec whose data type depends on a parameter breaks both rules. Choosing among existing codecs in the type constructor gives the same schema syntax without changing codecs. +**A type constructor whose codec is chosen by a select template over option arguments** (the field-preset machinery of ADR 246). A select has no default case and no way to refuse a combination, so bare `DateTime` would resolve to no codec and `Time(value: temporal, column: timetz)` could not be refused; and the machinery is being removed with the presets. A type choice that names explicit constructors has defaults and refusals by construction. + **Convenience types as contributed type aliases with arguments.** A type alias attaches attributes such as `@onCreate` to one fixed type (ADR 275). Choosing which type is what a type constructor does, and a type constructor already takes arguments. **Keep only the explicit types and document which to choose.** This adds nothing to learn, but the schema offers no default, so every user must understand every choice before writing a first field. diff --git a/projects/datetime-convenience-types/design-notes.md b/projects/datetime-convenience-types/design-notes.md index ab5207f372ab..f3ff610c0ec1 100644 --- a/projects/datetime-convenience-types/design-notes.md +++ b/projects/datetime-convenience-types/design-notes.md @@ -21,7 +21,7 @@ Record of the design discussion between the operator and the orchestrator on 202 **Convenience types over explicit types, not a renaming.** An early version of the proposal renamed every type after the application's meaning, which would have reversed ADR 257. The operator reframed it as layering: the database-named types stay as the precise form, and the new types are the short form built on them. Nothing is reversed, and a user who needs a specific column type and value kind keeps writing the explicit type. -**A type constructor chooses the codec.** One codec for `DateTime` with the database type as a parameter was rejected: a codec represents values of exactly one data type, and a column stores the data type its codec represents (ADR 254). A type alias with arguments was rejected: an alias attaches attributes to one fixed type, while choosing a type from arguments is what a type constructor does. Type constructors already take arguments. +**A type choice names the explicit type.** (Refined when slice 4 was grounded: the first plan let a type constructor's codec be a select template over option arguments, but selects have no defaults and no refusals, and that machinery is being removed with the presets. A type choice is a table from argument values to explicit constructor names.) One codec for `DateTime` with the database type as a parameter was rejected: a codec represents values of exactly one data type, and a column stores the data type its codec represents (ADR 254). A type alias with arguments was rejected: an alias attaches attributes to one fixed type, while choosing a type from arguments is what a type constructor does. Type constructors already take arguments. **Names follow the application's concepts: `DateTime`, `Date`, `Time`, `Duration`.** Naming them after the Temporal types (`Instant`, `PlainDate`, `PlainTime`) was rejected as library vocabulary, not domain vocabulary: a user thinks "a birthday", not "a plain date". The mapping from each name to its value must be obvious. diff --git a/projects/datetime-convenience-types/slices/04-convenience-types/spec.md b/projects/datetime-convenience-types/slices/04-convenience-types/spec.md new file mode 100644 index 000000000000..0d44697f7848 --- /dev/null +++ b/projects/datetime-convenience-types/slices/04-convenience-types/spec.md @@ -0,0 +1,68 @@ +# Slice 4 — PSL convenience types `DateTime`, `Date`, `Time` and `Duration` + +**Linear:** [TML-3712](https://linear.app/prisma-company/issue/TML-3712) · **Parent:** [project spec](../../spec.md), decisions 1–5 · **Design record:** ADR 279 · **Builds on:** slice 3 (the explicit types) + +## At a glance + +```prisma +model Order { + id Int @id + paidAt DateTime // TimestamptzJsDate(3): timestamptz(3), a JavaScript Date + settledAt DateTime(value: temporal) // TimestamptzTemporal: timestamptz, Temporal.Instant + legacyAt DateTime(column: timestamp) // TimestampJsDate(3): timestamp(3), a Date stored as UTC + textAt DateTime(value: string, precision: 6) // TimestamptzString(6) + shipBy Date // DateString: date, "2024-02-29" + opensAt Time(precision: 0) // TimeString(0): time(0), "09:30:00" + localAt Time(column: timetz) // Timetz: timetz, "09:30:00+02:00" + period Duration // IntervalString: interval, "P1M" + parts Duration(value: parts) // IntervalParts: interval, { months, days, micros } + updatedAt DateTime @onCreate(now()) @onUpdate(now()) // generator chosen for TimestamptzJsDate +} +``` + +`Time(value: temporal, column: timetz)` and `Date(value: jsDate)` are refused at the argument, naming the accepted choices. The contract stores only the explicit type each field becomes. + +## Chosen design + +1. **A convenience type is a new authoring descriptor kind, a type choice.** It declares its arguments (option arguments with a fixed set of bare-name values and a default each, plus value arguments passed through such as `precision`) and a table from each allowed combination of option values to the **name of an explicit type constructor** in the same namespace, with any parameter defaults for that combination (for example precision 3 when `value: jsDate`). Resolving a call looks up the combination, then instantiates the named explicit constructor with the passed-through arguments. A combination missing from the table is refused at the argument that makes it invalid, listing the values that would be accepted. + - Rationale: ADR 279 says each convenience type becomes exactly one explicit type; naming the explicit constructor makes that literal, supports defaults and refusals directly, and needs none of the field-preset `option`/`select` template machinery that the field-preset removal project deletes. ADR 279's "How it works" is updated to this shape (an orchestrator representation decision). +2. **Every reader resolves the field's call.** The PSL column resolution, the TypeScript builder's runtime and type-level resolution (`type.*` helpers), the contract-prisma7 values path, and every place that today reads a constructor's fixed `output.codecId` without the call's arguments: `@default` literal admission (`receivingDataType`) and the `@onCreate`/`@onUpdate` generator lookup (`generatorCallArm`) must use the codec the field's call resolves to, including through a `types {}` alias. The control stack checks every reachable explicit constructor at assembly. +3. **Bare names are scalars.** `DateTime`, `Date`, `Time` and `Duration` work bare and called; the scalar view treats them like any all-optional constructor. +4. **`inferred` marks stay on the explicit constructors** in this slice; a type choice is never inferred. `contract print` and `contract infer` keep writing explicit names until slice 5. +5. **The four PostgreSQL types and their tables** are exactly ADR 279 item 5 and the project spec's decision 3, with defaults: `DateTime` → `jsDate`, `timestamptz`, precision 3 for `jsDate` else none; `Date` → `string`; `Time` → `string`, `time`; `Duration` → `string`. +6. **The old meanings move to explicit names.** Every existing Prisma 8 PostgreSQL schema in the repository that writes `DateTime`, `Date` or `Time` (examples, apps, integration fixtures, test PSL strings, docs) is rewritten mechanically to the explicit type it means today (`TimestamptzTemporal`, `DateTemporal`, `TimeTemporal`), so no committed `contract.json` or hash changes. The `Date` and `Time` second names and `DateTime`'s current constructor are removed. The upgrade instruction gives users the same rewrite and says the bare names now choose different values. +7. **TypeScript builder.** `type.DateTime(...)`, `type.Date(...)`, `type.Time(...)`, `type.Duration(...)` exist through the `type.*` helpers, positional, with a type-level resolution that yields the explicit type's codec and application type for literal arguments (a type test per table row). +8. **Language server.** Hover on a convenience type shows each argument's choices as PSL writes them (bare names) and which explicit type each combination becomes; completion of a bare convenience type inserts the bare name. +9. **ADR updates in the same PR:** ADR 279 to Accepted, its "How it works" describing the type-choice descriptor; notes in ADR 254 (a type constructor may resolve to another constructor), ADR 257 (unsuffixed names belong to convenience types), ADR 275 (the contributed `temporal.*` aliases are built on `DateTime`; the `String`/`JsDate` variants are not needed). + +## Coherence rationale + +One PR: the descriptor kind and its resolution in every reader exist only to carry these four types, and the four types are untestable without it. The fixture rewrite is mechanical and keeps every contract byte-identical, which the reviewer can verify with `pnpm fixtures:check`. + +## Scope + +**In:** the type-choice descriptor (framework), its assembly checks, resolution in every reader listed above, the four PostgreSQL types, the TypeScript type-level resolution and `type.*` helpers, hover and completion, the fixture and doc rewrite, upgrade instructions, the ADR updates. + +**Out:** what `contract infer`, `contract print`, the Prisma 7 reader and `orm init` write (slice 5); SQLite and MongoDB (TML-3714); other scalars (TML-3715); completion inside a constructor's argument list (no constructor has it today). + +## Pre-investigated edge cases + +| Case | Disposition | +| --- | --- | +| Field typed by a `types {}` alias whose right-hand side is a convenience call | Resolves through the alias; `@default` and `@onCreate` use the resolved codec | +| `DateTime[]`, `DateTime?` | Same resolution as any constructor | +| `@default(timestamptz\`…\`)` on `DateTime(column: timestamp)` | Refused by the existing cast rule (wrong tag), naming `timestamp` | +| `@onCreate(now())` on each convenience default | Generator resolved for the explicit codec (`timestampNow` for `TimestamptzJsDate`, `dateStringNow` for `DateString`, …) | +| Positional arguments, e.g. `DateTime(temporal)` | Accepted in declaration order, like any constructor | +| Precision on `Date` or `Duration` with `value: string` | Refused unless the explicit type takes it (`IntervalString(p)` does, `DateString` does not) | +| A pack declaring a choice that names a constructor no component registers | Assembly error naming the pack and the name | + +## Slice-specific done conditions + +- Every row of the at-a-glance block emits the stated explicit type's contract column (asserted equal to writing the explicit type), and a query on PostgreSQL reads the stated value type, with the defaults working in a process without a global `Temporal`. + +## Dispatch plan + +1. **The type-choice mechanism** — outcome: a test-only pack declaring a choice type resolves bare, positional and named calls, refuses an unlisted combination at the argument, and every reader in decision 2 uses the resolved codec (`@default` admission and `@onCreate` arm tests included); TypeScript type-level resolution covered by type tests. Gates: framework-components, psl-parser, contract-psl, contract-ts, family-sql package tests; typecheck; lint; `pnpm lint:deps`. +2. **The four PostgreSQL types and the rewrite** — builds on 1. Outcome: the at-a-glance block holds; existing schemas rewritten to explicit names with `pnpm fixtures:check` clean and no `contract.json` changed; upgrade instruction. Gates: target-postgres, adapter-postgres, the postgres extension, contract-prisma7 package tests; the date-time integration and print tests; `pnpm build`; `pnpm typecheck`; `pnpm fixtures:check`; `pnpm check:upgrade-coverage --mode pr`. +3. **Language server, docs and ADRs** — builds on 2. Outcome: hover shows choices and resulting explicit types; `docs/reference/scalar-types.md` describes both layers; skill references updated; ADR updates per decision 9. Gates: language-server package tests; `pnpm lint:docs`. diff --git a/projects/datetime-convenience-types/spec.md b/projects/datetime-convenience-types/spec.md index 8a3b579514a8..ca60b5540d86 100644 --- a/projects/datetime-convenience-types/spec.md +++ b/projects/datetime-convenience-types/spec.md @@ -30,7 +30,7 @@ model Order { The reasoning and the alternatives rejected are in [`design-notes.md`](./design-notes.md) and ADR 279. 1. **Two layers.** The explicit types stay, named after the database as ADR 257 says, each fixing one codec. The convenience types are named for what a field means in the application, and each becomes exactly one explicit type. Nothing is reversed: the explicit layer is the precise form, the convenience layer is the short form. -2. **A convenience type is a type constructor whose arguments choose its codec.** Not a codec parameterized by database type (a codec represents one data type, ADR 254), and not a type alias (an alias attaches attributes to one fixed type). +2. **A convenience type is a type choice: a declaration whose arguments name the explicit type constructor it becomes**, through a table with defaults and refusals. Not a codec parameterized by database type (a codec represents one data type, ADR 254), not a type alias (an alias attaches attributes to one fixed type), and not a select template over option arguments (no defaults or refusals; being removed with the presets). Settled 2026-10-22 when slice 4 was grounded. 3. **The four PostgreSQL convenience types and their choices:** | Type | `value:` | `column:` | Default | From aa9ff21237e0d9a9a49160160a6fee3d5623fbcb Mon Sep 17 00:00:00 2001 From: willbot Date: Thu, 22 Oct 2026 19:49:27 +0200 Subject: [PATCH 031/118] Bring in the current convenience type design and slice spec Co-Authored-By: Claude Opus 5.5 Signed-off-by: willbot Signed-off-by: Will Madden --- ...e an explicit type from their arguments.md | 35 ++++++---- .../design-notes.md | 2 +- .../slices/04-convenience-types/spec.md | 68 +++++++++++++++++++ projects/datetime-convenience-types/spec.md | 2 +- 4 files changed, 90 insertions(+), 17 deletions(-) create mode 100644 projects/datetime-convenience-types/slices/04-convenience-types/spec.md diff --git a/docs/architecture docs/adrs/ADR 279 - Convenience date and time types choose an explicit type from their arguments.md b/docs/architecture docs/adrs/ADR 279 - Convenience date and time types choose an explicit type from their arguments.md index 36021b0f83b3..673e94a7ceef 100644 --- a/docs/architecture docs/adrs/ADR 279 - Convenience date and time types choose an explicit type from their arguments.md +++ b/docs/architecture docs/adrs/ADR 279 - Convenience date and time types choose an explicit type from their arguments.md @@ -3,7 +3,6 @@ **Status:** Proposed **Builds on:** [ADR 241 — Scalar types use the authoring type-constructor channel](ADR%20241%20-%20Scalar%20types%20use%20the%20authoring%20type-constructor%20channel.md) **Amends:** [ADR 254 — Data types and casts](ADR%20254%20-%20Data%20types%20and%20casts.md), [ADR 257 — Scalar types are named after the target on every surface](ADR%20257%20-%20Scalar%20types%20are%20named%20after%20the%20target%20on%20every%20surface.md), [ADR 275 — Client generators are field attributes, and a type alias is expanded into each column](ADR%20275%20-%20Client%20generators%20are%20field%20attributes%2C%20and%20a%20type%20alias%20is%20expanded%20into%20each%20column.md) -**Brings back:** [ADR 246 — Option arguments and select templates for authoring helpers](ADR%20246%20-%20Option%20arguments%20and%20select%20templates%20for%20authoring%20helpers.md), which ADR 275 supersedes, for type constructors only ## At a glance @@ -34,7 +33,7 @@ The contract stores what the explicit type stores: the codec, its data type and 1. **Date and time types have two layers.** Each explicit type uses one codec, so it fixes one column type and one application value, and is named after the database as ADR 257 says. Each convenience type is named for what a field means in the application, and becomes exactly one explicit type. 2. **A convenience type's arguments choose its explicit type.** `value` says what the application reads: a JavaScript `Date`, a `Temporal` object or a string. `column` says the PostgreSQL column type. Each has a default that the convenience type declares. Any other argument, such as `precision`, is passed on to the chosen explicit type. -3. **A convenience type is a type constructor whose arguments choose its codec.** A type constructor is how PSL defines a type name that can take arguments, such as `Timestamptz(3)`. Today each one always uses the same codec. A type constructor may now choose its codec from its arguments. +3. **A convenience type is a type choice: a type declaration whose arguments name the explicit type it becomes.** It declares its option arguments, each with a fixed set of values written as bare names and a default, the arguments it passes on (such as `precision`), and a table from each allowed combination of option values to the name of an explicit type constructor, with any parameter defaults for that combination. An unlisted combination is refused. 4. **The contract stores only the explicit type.** The contract format, the codecs, the data types, the storage hash and the execution hash are the same whether a schema writes the convenience type or the explicit type it becomes. 5. **The PostgreSQL target provides four convenience types:** @@ -76,14 +75,13 @@ The convenience types give that user one name per concept, with defaults that wo ## How it works -### A type constructor chooses its codec +### A type choice names its explicit types -A type constructor's output is an `AuthoringStorageTypeTemplate`: a `codecId`, and a `typeParams` template whose values may refer to the constructor's arguments. Today `codecId` is a plain string: +An explicit type is a type constructor: it names one codec, and maps its arguments onto that codec's parameters. ```ts -Timestamptz: { +TimestamptzTemporal: { kind: 'typeConstructor', - documentation: 'An instant represented as Temporal.Instant, with optional fractional-second precision.', args: [{ kind: 'number', name: 'precision', integer: true, optional: true }], output: { codecId: 'pg/timestamptz-temporal@1', @@ -92,17 +90,22 @@ Timestamptz: { }, ``` -A convenience constructor's `codecId` is chosen by its `value` and `column` arguments. ADR 246 introduced the two pieces this needs, for the `temporal.*` field presets such as `temporal.timestamp(3, onCreate: now)`: +A convenience type is a different kind of declaration in the same namespace, a type choice. It names no codec. It declares its option arguments and a table whose rows each name an explicit type constructor: -- an argument that takes one of a fixed set of bare names (`AuthoringOption`, `kind: 'option'`); -- a template value that picks one case by the value of such an argument (`AuthoringSelectRef`, `kind: 'select'`). +| `value` | `column` | Explicit type | Parameter defaults | +| --- | --- | --- | --- | +| `jsDate` (default) | `timestamptz` (default) | `TimestamptzJsDate` | precision 3 | +| `temporal` | `timestamptz` | `TimestamptzTemporal` | | +| `string` | `timestamptz` | `TimestamptzString` | | +| `jsDate` | `timestamp` | `TimestampJsDate` | precision 3 | +| `temporal` | `timestamp` | `TimestampInstant` | | +| `string` | `timestamp` | `TimestampString` | | -ADR 275 removes both together with the presets. This decision keeps them, for type constructors, and lets a `select` give a type constructor's `codecId`. That is more than a new case: +That is `DateTime`'s table; the other three are declared the same way. Resolving a call looks up the row for its option values, then instantiates the named explicit constructor with the passed-on arguments, so the result is exactly what writing the explicit type gives. A combination with no row, such as `Date(value: jsDate)`, is refused at the argument that makes it invalid, and the message lists the values that argument accepts with the others as written. Assembly refuses a table that names a constructor no component registers. -- `codecId` is typed `string` in `AuthoringStorageTypeTemplate`, and many places read it as a plain string: the control stack, the PSL column resolution, the attribute specs, the generator attributes, the inferred-constructor map, the Prisma 7 reader and the MongoDB interpreter. Each must resolve the template against the call's arguments first. -- The TypeScript contract builder works out a column's application type at compile time from its `codecId`. Its type-level resolver, `ResolveTemplateValue`, cannot evaluate a `select`; ADR 246 bars `select` from a preset's `codecId` for that reason. The resolver must learn to evaluate it, so that a convenience type written in a TypeScript contract gets an application type as precise as an explicit one. A TypeScript contract reaches the convenience types through the `type.*` helpers that ADR 275 gives every PSL type constructor. +The field-preset option arguments and select templates (ADR 246) are not used: a select resolves a template value and has no defaults and no refusal, while a type choice resolves to a named constructor, which is what "becomes one explicit type" means. -A combination a convenience type does not list, such as `Date(value: jsDate)`, is an error. The PSL interpreter reports it at the argument when it reads the schema, and the message lists the choices the type accepts. +Every place that reads a field's codec reads the codec its call resolves to, including through a `types {}` alias: the PSL column resolution, the `@default` value check and the `@onCreate`/`@onUpdate` generator lookup, the Prisma 7 reader and the TypeScript builder, whose `type.*` helpers (ADR 275) include the type choices and resolve their application type at compile time from literal arguments. ### What the contract and the tools see @@ -131,9 +134,9 @@ The `String` and `JsDate` variants are not needed. A contributed alias takes no - **String values change form.** An application that reads a string date or time type gets ISO 8601 text instead of PostgreSQL's display text, for example `2024-01-01T00:00:00Z` instead of `2024-01-01 00:00:00+00`. The codec ids and the contract do not change. - **Temporal fields are typed from `temporal-spec`,** a package of TypeScript declarations with no runtime code, instead of an undeclared global `Temporal`. A Temporal field is then never typed `any` in a project without the polyfill's global declarations, and no polyfill code reaches an application that does not opt into Temporal values. A missing global `Temporal` at runtime is still reported when a Temporal codec or generator first runs. - **`Interval` reads the same object as before, under the name `IntervalParts`;** `Duration` reads an ISO string by default. -- ADR 254 says a type constructor names one codec and maps its arguments onto parameters. A type constructor may now also choose the codec. +- ADR 254 says a type constructor names one codec and maps its arguments onto parameters. That stays true; a type choice is a second kind of type declaration that resolves to a type constructor. - ADR 257 says the second codec of a stored type adds a suffix. Where a stored type has several codecs, every one now has a suffix, and the unsuffixed name of the concept belongs to a convenience type. -- ADR 275's removal of the `option` argument kind and the `select` template does not apply to type constructors. Its `String` and `JsDate` alias variants are not built. +- ADR 275's `String` and `JsDate` variants of the contributed `temporal.*` aliases are not built. - The planned rename of the PostgreSQL and SQLite types, which applies ADR 257 to those targets (for example `Int` to `Int4`), keeps `DateTime` as the convenience type instead of removing it, and adds the renames in item 6. - The editor's completion offers each convenience type's `value` and `column` choices, and its hover text gives the explicit type each choice becomes. @@ -148,6 +151,8 @@ The `String` and `JsDate` variants are not needed. A contributed alias takes no **One codec for `DateTime`, with the column type as a parameter.** A codec converts values of exactly one data type, and a column stores the data type its codec represents (ADR 254). A codec whose data type depends on a parameter breaks both rules. Choosing among existing codecs in the type constructor gives the same schema syntax without changing codecs. +**A type constructor whose codec is chosen by a select template over option arguments** (the field-preset machinery of ADR 246). A select has no default case and no way to refuse a combination, so bare `DateTime` would resolve to no codec and `Time(value: temporal, column: timetz)` could not be refused; and the machinery is being removed with the presets. A type choice that names explicit constructors has defaults and refusals by construction. + **Convenience types as contributed type aliases with arguments.** A type alias attaches attributes such as `@onCreate` to one fixed type (ADR 275). Choosing which type is what a type constructor does, and a type constructor already takes arguments. **Keep only the explicit types and document which to choose.** This adds nothing to learn, but the schema offers no default, so every user must understand every choice before writing a first field. diff --git a/projects/datetime-convenience-types/design-notes.md b/projects/datetime-convenience-types/design-notes.md index ab5207f372ab..f3ff610c0ec1 100644 --- a/projects/datetime-convenience-types/design-notes.md +++ b/projects/datetime-convenience-types/design-notes.md @@ -21,7 +21,7 @@ Record of the design discussion between the operator and the orchestrator on 202 **Convenience types over explicit types, not a renaming.** An early version of the proposal renamed every type after the application's meaning, which would have reversed ADR 257. The operator reframed it as layering: the database-named types stay as the precise form, and the new types are the short form built on them. Nothing is reversed, and a user who needs a specific column type and value kind keeps writing the explicit type. -**A type constructor chooses the codec.** One codec for `DateTime` with the database type as a parameter was rejected: a codec represents values of exactly one data type, and a column stores the data type its codec represents (ADR 254). A type alias with arguments was rejected: an alias attaches attributes to one fixed type, while choosing a type from arguments is what a type constructor does. Type constructors already take arguments. +**A type choice names the explicit type.** (Refined when slice 4 was grounded: the first plan let a type constructor's codec be a select template over option arguments, but selects have no defaults and no refusals, and that machinery is being removed with the presets. A type choice is a table from argument values to explicit constructor names.) One codec for `DateTime` with the database type as a parameter was rejected: a codec represents values of exactly one data type, and a column stores the data type its codec represents (ADR 254). A type alias with arguments was rejected: an alias attaches attributes to one fixed type, while choosing a type from arguments is what a type constructor does. Type constructors already take arguments. **Names follow the application's concepts: `DateTime`, `Date`, `Time`, `Duration`.** Naming them after the Temporal types (`Instant`, `PlainDate`, `PlainTime`) was rejected as library vocabulary, not domain vocabulary: a user thinks "a birthday", not "a plain date". The mapping from each name to its value must be obvious. diff --git a/projects/datetime-convenience-types/slices/04-convenience-types/spec.md b/projects/datetime-convenience-types/slices/04-convenience-types/spec.md new file mode 100644 index 000000000000..0d44697f7848 --- /dev/null +++ b/projects/datetime-convenience-types/slices/04-convenience-types/spec.md @@ -0,0 +1,68 @@ +# Slice 4 — PSL convenience types `DateTime`, `Date`, `Time` and `Duration` + +**Linear:** [TML-3712](https://linear.app/prisma-company/issue/TML-3712) · **Parent:** [project spec](../../spec.md), decisions 1–5 · **Design record:** ADR 279 · **Builds on:** slice 3 (the explicit types) + +## At a glance + +```prisma +model Order { + id Int @id + paidAt DateTime // TimestamptzJsDate(3): timestamptz(3), a JavaScript Date + settledAt DateTime(value: temporal) // TimestamptzTemporal: timestamptz, Temporal.Instant + legacyAt DateTime(column: timestamp) // TimestampJsDate(3): timestamp(3), a Date stored as UTC + textAt DateTime(value: string, precision: 6) // TimestamptzString(6) + shipBy Date // DateString: date, "2024-02-29" + opensAt Time(precision: 0) // TimeString(0): time(0), "09:30:00" + localAt Time(column: timetz) // Timetz: timetz, "09:30:00+02:00" + period Duration // IntervalString: interval, "P1M" + parts Duration(value: parts) // IntervalParts: interval, { months, days, micros } + updatedAt DateTime @onCreate(now()) @onUpdate(now()) // generator chosen for TimestamptzJsDate +} +``` + +`Time(value: temporal, column: timetz)` and `Date(value: jsDate)` are refused at the argument, naming the accepted choices. The contract stores only the explicit type each field becomes. + +## Chosen design + +1. **A convenience type is a new authoring descriptor kind, a type choice.** It declares its arguments (option arguments with a fixed set of bare-name values and a default each, plus value arguments passed through such as `precision`) and a table from each allowed combination of option values to the **name of an explicit type constructor** in the same namespace, with any parameter defaults for that combination (for example precision 3 when `value: jsDate`). Resolving a call looks up the combination, then instantiates the named explicit constructor with the passed-through arguments. A combination missing from the table is refused at the argument that makes it invalid, listing the values that would be accepted. + - Rationale: ADR 279 says each convenience type becomes exactly one explicit type; naming the explicit constructor makes that literal, supports defaults and refusals directly, and needs none of the field-preset `option`/`select` template machinery that the field-preset removal project deletes. ADR 279's "How it works" is updated to this shape (an orchestrator representation decision). +2. **Every reader resolves the field's call.** The PSL column resolution, the TypeScript builder's runtime and type-level resolution (`type.*` helpers), the contract-prisma7 values path, and every place that today reads a constructor's fixed `output.codecId` without the call's arguments: `@default` literal admission (`receivingDataType`) and the `@onCreate`/`@onUpdate` generator lookup (`generatorCallArm`) must use the codec the field's call resolves to, including through a `types {}` alias. The control stack checks every reachable explicit constructor at assembly. +3. **Bare names are scalars.** `DateTime`, `Date`, `Time` and `Duration` work bare and called; the scalar view treats them like any all-optional constructor. +4. **`inferred` marks stay on the explicit constructors** in this slice; a type choice is never inferred. `contract print` and `contract infer` keep writing explicit names until slice 5. +5. **The four PostgreSQL types and their tables** are exactly ADR 279 item 5 and the project spec's decision 3, with defaults: `DateTime` → `jsDate`, `timestamptz`, precision 3 for `jsDate` else none; `Date` → `string`; `Time` → `string`, `time`; `Duration` → `string`. +6. **The old meanings move to explicit names.** Every existing Prisma 8 PostgreSQL schema in the repository that writes `DateTime`, `Date` or `Time` (examples, apps, integration fixtures, test PSL strings, docs) is rewritten mechanically to the explicit type it means today (`TimestamptzTemporal`, `DateTemporal`, `TimeTemporal`), so no committed `contract.json` or hash changes. The `Date` and `Time` second names and `DateTime`'s current constructor are removed. The upgrade instruction gives users the same rewrite and says the bare names now choose different values. +7. **TypeScript builder.** `type.DateTime(...)`, `type.Date(...)`, `type.Time(...)`, `type.Duration(...)` exist through the `type.*` helpers, positional, with a type-level resolution that yields the explicit type's codec and application type for literal arguments (a type test per table row). +8. **Language server.** Hover on a convenience type shows each argument's choices as PSL writes them (bare names) and which explicit type each combination becomes; completion of a bare convenience type inserts the bare name. +9. **ADR updates in the same PR:** ADR 279 to Accepted, its "How it works" describing the type-choice descriptor; notes in ADR 254 (a type constructor may resolve to another constructor), ADR 257 (unsuffixed names belong to convenience types), ADR 275 (the contributed `temporal.*` aliases are built on `DateTime`; the `String`/`JsDate` variants are not needed). + +## Coherence rationale + +One PR: the descriptor kind and its resolution in every reader exist only to carry these four types, and the four types are untestable without it. The fixture rewrite is mechanical and keeps every contract byte-identical, which the reviewer can verify with `pnpm fixtures:check`. + +## Scope + +**In:** the type-choice descriptor (framework), its assembly checks, resolution in every reader listed above, the four PostgreSQL types, the TypeScript type-level resolution and `type.*` helpers, hover and completion, the fixture and doc rewrite, upgrade instructions, the ADR updates. + +**Out:** what `contract infer`, `contract print`, the Prisma 7 reader and `orm init` write (slice 5); SQLite and MongoDB (TML-3714); other scalars (TML-3715); completion inside a constructor's argument list (no constructor has it today). + +## Pre-investigated edge cases + +| Case | Disposition | +| --- | --- | +| Field typed by a `types {}` alias whose right-hand side is a convenience call | Resolves through the alias; `@default` and `@onCreate` use the resolved codec | +| `DateTime[]`, `DateTime?` | Same resolution as any constructor | +| `@default(timestamptz\`…\`)` on `DateTime(column: timestamp)` | Refused by the existing cast rule (wrong tag), naming `timestamp` | +| `@onCreate(now())` on each convenience default | Generator resolved for the explicit codec (`timestampNow` for `TimestamptzJsDate`, `dateStringNow` for `DateString`, …) | +| Positional arguments, e.g. `DateTime(temporal)` | Accepted in declaration order, like any constructor | +| Precision on `Date` or `Duration` with `value: string` | Refused unless the explicit type takes it (`IntervalString(p)` does, `DateString` does not) | +| A pack declaring a choice that names a constructor no component registers | Assembly error naming the pack and the name | + +## Slice-specific done conditions + +- Every row of the at-a-glance block emits the stated explicit type's contract column (asserted equal to writing the explicit type), and a query on PostgreSQL reads the stated value type, with the defaults working in a process without a global `Temporal`. + +## Dispatch plan + +1. **The type-choice mechanism** — outcome: a test-only pack declaring a choice type resolves bare, positional and named calls, refuses an unlisted combination at the argument, and every reader in decision 2 uses the resolved codec (`@default` admission and `@onCreate` arm tests included); TypeScript type-level resolution covered by type tests. Gates: framework-components, psl-parser, contract-psl, contract-ts, family-sql package tests; typecheck; lint; `pnpm lint:deps`. +2. **The four PostgreSQL types and the rewrite** — builds on 1. Outcome: the at-a-glance block holds; existing schemas rewritten to explicit names with `pnpm fixtures:check` clean and no `contract.json` changed; upgrade instruction. Gates: target-postgres, adapter-postgres, the postgres extension, contract-prisma7 package tests; the date-time integration and print tests; `pnpm build`; `pnpm typecheck`; `pnpm fixtures:check`; `pnpm check:upgrade-coverage --mode pr`. +3. **Language server, docs and ADRs** — builds on 2. Outcome: hover shows choices and resulting explicit types; `docs/reference/scalar-types.md` describes both layers; skill references updated; ADR updates per decision 9. Gates: language-server package tests; `pnpm lint:docs`. diff --git a/projects/datetime-convenience-types/spec.md b/projects/datetime-convenience-types/spec.md index 8a3b579514a8..ca60b5540d86 100644 --- a/projects/datetime-convenience-types/spec.md +++ b/projects/datetime-convenience-types/spec.md @@ -30,7 +30,7 @@ model Order { The reasoning and the alternatives rejected are in [`design-notes.md`](./design-notes.md) and ADR 279. 1. **Two layers.** The explicit types stay, named after the database as ADR 257 says, each fixing one codec. The convenience types are named for what a field means in the application, and each becomes exactly one explicit type. Nothing is reversed: the explicit layer is the precise form, the convenience layer is the short form. -2. **A convenience type is a type constructor whose arguments choose its codec.** Not a codec parameterized by database type (a codec represents one data type, ADR 254), and not a type alias (an alias attaches attributes to one fixed type). +2. **A convenience type is a type choice: a declaration whose arguments name the explicit type constructor it becomes**, through a table with defaults and refusals. Not a codec parameterized by database type (a codec represents one data type, ADR 254), not a type alias (an alias attaches attributes to one fixed type), and not a select template over option arguments (no defaults or refusals; being removed with the presets). Settled 2026-10-22 when slice 4 was grounded. 3. **The four PostgreSQL convenience types and their choices:** | Type | `value:` | `column:` | Default | From 13f1672cfb92761568435d298d2dce360169a1ef Mon Sep 17 00:00:00 2001 From: willbot Date: Thu, 22 Oct 2026 19:51:05 +0200 Subject: [PATCH 032/118] Detect a Temporal type test even when temporal-spec is imported under an alias The temporal-spec detection stopped matching a file that imported anything from temporal-spec. It now stops only when the file imports Temporal itself, unaliased, so a file that imports Temporal as TemporalTypes but still compares a field with the global Temporal.Instant is found. Co-Authored-By: Claude Opus 5.5 Signed-off-by: willbot Signed-off-by: Will Madden --- .../explicit-date-time-types/detection.test.ts | 14 +++++++++++++- .../explicit-date-time-types/app/instructions.md | 2 +- .../extension/instructions.md | 2 +- 3 files changed, 15 insertions(+), 3 deletions(-) diff --git a/test/integration/test/upgrade-instructions/explicit-date-time-types/detection.test.ts b/test/integration/test/upgrade-instructions/explicit-date-time-types/detection.test.ts index a0466dea0b3e..c772734a236c 100644 --- a/test/integration/test/upgrade-instructions/explicit-date-time-types/detection.test.ts +++ b/test/integration/test/upgrade-instructions/explicit-date-time-types/detection.test.ts @@ -112,7 +112,7 @@ describe.each(['app', 'extension'])('the %s temporal-spec detection', (audience) pattern.test(content), ); - it('matches a type test that compares with a global Temporal type, until it imports temporal-spec', () => { + it('matches a type test that compares with a global Temporal type, until it imports Temporal itself from temporal-spec', () => { expect({ global: matches( "import { expectTypeOf } from 'vitest';\n\nexpectTypeOf().toEqualTypeOf();", @@ -125,12 +125,24 @@ describe.each(['app', 'extension'])('the %s temporal-spec detection', (audience) "expectTypeOf().toEqualTypeOf();\nimport type { Temporal } from 'temporal-spec';", ), noComparison: matches('const at: Temporal.Instant = Temporal.Now.instant();'), + inlineTypeImport: matches( + "import { type Temporal } from 'temporal-spec';\n\nexpectTypeOf().toEqualTypeOf();", + ), + aliasedImport: matches( + "import type { Temporal as TemporalTypes } from 'temporal-spec';\n\nexpectTypeOf().toEqualTypeOf();", + ), + aliasedImportCompared: matches( + "import type { Temporal as TemporalTypes } from 'temporal-spec';\n\nexpectTypeOf().toEqualTypeOf();", + ), }).toEqual({ global: true, valueForm: true, importsTemporalSpec: false, importsAfterUse: false, noComparison: false, + inlineTypeImport: false, + aliasedImport: true, + aliasedImportCompared: false, }); }); }); diff --git a/upgrade-instructions/pending/explicit-date-time-types/app/instructions.md b/upgrade-instructions/pending/explicit-date-time-types/app/instructions.md index 20271c179436..667530d7b986 100644 --- a/upgrade-instructions/pending/explicit-date-time-types/app/instructions.md +++ b/upgrade-instructions/pending/explicit-date-time-types/app/instructions.md @@ -35,7 +35,7 @@ changes: detection: glob: "**/*.{ts,mts,cts,tsx}" matches: - - '(? Date: Thu, 22 Oct 2026 19:57:49 +0200 Subject: [PATCH 033/118] Add the type choice, a type whose arguments choose a type constructor A type choice declares option arguments, the arguments it passes on, and a table from each allowed combination of option values to a type constructor in the same namespace. Resolving a call refuses an unlisted value or combination at the argument that makes it invalid. Assembly refuses a table that names no registered constructor. Co-Authored-By: Claude Opus 5.5 Signed-off-by: willbot Signed-off-by: Will Madden --- .../src/control/control-stack.ts | 5 + .../src/exports/authoring.ts | 18 ++ .../src/shared/authoring-type-choice.ts | 260 ++++++++++++++++++ .../shared/authoring-type-constructor-call.ts | 4 +- .../src/shared/framework-authoring.ts | 103 +++++-- .../test/authoring-type-choice.test.ts | 130 +++++++++ .../test/type-choice-assembly.test.ts | 147 ++++++++++ 7 files changed, 649 insertions(+), 18 deletions(-) create mode 100644 packages/1-framework/1-core/framework-components/src/shared/authoring-type-choice.ts create mode 100644 packages/1-framework/1-core/framework-components/test/authoring-type-choice.test.ts create mode 100644 packages/1-framework/1-core/framework-components/test/type-choice-assembly.test.ts diff --git a/packages/1-framework/1-core/framework-components/src/control/control-stack.ts b/packages/1-framework/1-core/framework-components/src/control/control-stack.ts index d8b37607089b..8cf7176e8859 100644 --- a/packages/1-framework/1-core/framework-components/src/control/control-stack.ts +++ b/packages/1-framework/1-core/framework-components/src/control/control-stack.ts @@ -3,6 +3,7 @@ import { blindCast } from '@internal/utils/casts'; import { InternalError, isInternalError } from '@internal/utils/internal-error'; import type { AggregateDescriptor } from '../shared/aggregate-descriptor'; import { aggregateDescriptorKey, isAggregateDescriptor } from '../shared/aggregate-descriptor'; +import { assertTypeChoicesValid } from '../shared/authoring-type-choice'; import type { CapabilityMatrix } from '../shared/capabilities'; import { mergeCapabilityMatrices } from '../shared/capabilities'; import type { Codec } from '../shared/codec'; @@ -353,6 +354,10 @@ export function assembleAuthoringContributions( pslBlockDescriptorNamespace, modelAttributeNamespace, ); + for (const descriptor of descriptors) { + if (descriptor.authoring?.type === undefined) continue; + assertTypeChoicesValid(descriptor.authoring.type, descriptor.id ?? '', typeNamespace); + } if ( valueObjectStorageDeclaration !== undefined && diff --git a/packages/1-framework/1-core/framework-components/src/exports/authoring.ts b/packages/1-framework/1-core/framework-components/src/exports/authoring.ts index 9c45088eaf84..e30dfa176dd4 100644 --- a/packages/1-framework/1-core/framework-components/src/exports/authoring.ts +++ b/packages/1-framework/1-core/framework-components/src/exports/authoring.ts @@ -1,3 +1,17 @@ +export type { + AuthoringTypeChoiceArgument, + AuthoringTypeChoiceDescriptor, + AuthoringTypeChoiceOption, + AuthoringTypeChoicePassedArgument, + AuthoringTypeChoiceRow, + ResolvedAuthoringTypeChoice, +} from '../shared/authoring-type-choice'; +export { + assertTypeChoicesValid, + isAuthoringTypeChoiceOption, + resolveAuthoringTypeChoice, + typeChoiceCombinationText, +} from '../shared/authoring-type-choice'; export type { AuthoringTypeConstructorCall, AuthoringTypeConstructorOutput, @@ -69,6 +83,7 @@ export type { AuthoringTypeConstructorDescriptor, AuthoringTypeConstructorEntityRef, AuthoringTypeNamespace, + AuthoringTypeNamespaceMember, AuthoringTypeParamsIssue, AuthoringValueArgument, AuthoringWarning, @@ -89,6 +104,7 @@ export { instantiateAuthoringEntityType, instantiateAuthoringFieldPreset, instantiateAuthoringTypeAlias, + instantiateAuthoringTypeCall, instantiateAuthoringTypeConstructor, instantiateAuthoringTypeConstructorFromValues, isAuthoringArgRef, @@ -98,12 +114,14 @@ export { isAuthoringPslBlockDescriptor, isAuthoringSelectRef, isAuthoringTypeAliasDescriptor, + isAuthoringTypeChoiceDescriptor, isAuthoringTypeConstructorDescriptor, isAuthoringTypeDescriptor, mergeAuthoringNamespaces, resolveAuthoringTemplateValue, resolveEnumCodecId, resolveTypeAliasGenerators, + typeConstructorLookup, validateAuthoringHelperArguments, validateAuthoringTypeParams, } from '../shared/framework-authoring'; diff --git a/packages/1-framework/1-core/framework-components/src/shared/authoring-type-choice.ts b/packages/1-framework/1-core/framework-components/src/shared/authoring-type-choice.ts new file mode 100644 index 000000000000..f0478544c40b --- /dev/null +++ b/packages/1-framework/1-core/framework-components/src/shared/authoring-type-choice.ts @@ -0,0 +1,260 @@ +import type { JsonValue } from '@internal/contract/types'; +import type { + AuthoringTypeConstructorDescriptor, + AuthoringValueArgument, +} from './framework-authoring'; +import type { AuthoringOption } from './option-descriptor'; +import { runtimeError } from './runtime-error'; + +/** An argument of a type choice that picks its row: one of `values`, written as a bare name, and `default` when it is left out. */ +export interface AuthoringTypeChoiceOption extends AuthoringOption { + readonly name: string; + readonly default: string; + readonly optional: true; +} + +/** An argument a type choice passes on, by name, to the constructor its row names. */ +export type AuthoringTypeChoicePassedArgument = AuthoringValueArgument & { + readonly name: string; + readonly optional: true; +}; + +export type AuthoringTypeChoiceArgument = + | AuthoringTypeChoiceOption + | AuthoringTypeChoicePassedArgument; + +/** One allowed combination of option values, the type constructor it chooses, and the values of passed-on arguments when they are left out. */ +export interface AuthoringTypeChoiceRow { + readonly when: Readonly>; + /** A type constructor in the same namespace as the type choice. */ + readonly type: string; + readonly defaults?: Readonly>; +} + +/** + * A type whose arguments choose the type constructor it becomes. Every argument is optional, so it is also written bare. A combination of option values with no row is refused. ADR 279. + */ +export interface AuthoringTypeChoiceDescriptor { + readonly kind: 'typeChoice'; + readonly documentation?: string; + readonly args: readonly AuthoringTypeChoiceArgument[]; + readonly choices: readonly AuthoringTypeChoiceRow[]; +} + +/** The constructor a type choice call chooses, by its name, and the arguments it is called with. */ +export interface ResolvedAuthoringTypeChoice { + readonly type: string; + readonly descriptor: AuthoringTypeConstructorDescriptor; + readonly args: readonly unknown[]; +} + +export function isAuthoringTypeChoiceOption( + argument: AuthoringTypeChoiceArgument, +): argument is AuthoringTypeChoiceOption { + return argument.kind === 'option'; +} + +function rowMatches( + row: AuthoringTypeChoiceRow, + chosen: ReadonlyMap, + names: readonly string[], +): boolean { + return names.every((name) => row.when[name] === chosen.get(name)); +} + +/** How a combination of option values is written, as in `value: date, column: narrow`. */ +export function typeChoiceCombinationText(row: AuthoringTypeChoiceRow): string { + return Object.entries(row.when) + .map(([name, value]) => `${name}: ${value}`) + .join(', '); +} + +/** + * The type constructor a call of the type choice `descriptor` chooses, and the arguments it passes on. `args` are the call's values by the choice's argument positions; an option left out takes its default. A value an option does not list, a combination with no row, and a passed-on argument the chosen constructor does not take are refused with `CONTRACT.ARGUMENT_INVALID`, naming the argument by its index. + */ +export function resolveAuthoringTypeChoice(input: { + readonly helperPath: string; + readonly descriptor: AuthoringTypeChoiceDescriptor; + readonly args: readonly unknown[]; + readonly constructorNamed: (name: string) => AuthoringTypeConstructorDescriptor | undefined; +}): ResolvedAuthoringTypeChoice { + const { helperPath, descriptor, args } = input; + const refuse = (argumentIndex: number, message: string): never => { + throw runtimeError('CONTRACT.ARGUMENT_INVALID', message, { helperPath, argumentIndex }); + }; + const label = (index: number) => `Argument "${descriptor.args[index]?.name}" of ${helperPath}`; + + const options = descriptor.args.flatMap((argument, index) => + isAuthoringTypeChoiceOption(argument) ? [{ argument, index }] : [], + ); + const written = options.filter(({ index }) => args[index] !== undefined); + const defaulted = options.filter(({ index }) => args[index] === undefined); + + const chosen = new Map(); + const decided: string[] = []; + for (const { argument, index } of [...written, ...defaulted]) { + const value = args[index] ?? argument.default; + if (typeof value !== 'string' || !argument.values.includes(value)) { + refuse( + index, + `${label(index)} is ${String(value)}; write one of: ${argument.values.join(', ')}.`, + ); + } + const fitting = descriptor.choices.filter((row) => rowMatches(row, chosen, decided)); + const accepted = argument.values.filter((candidate) => + fitting.some((row) => row.when[argument.name] === candidate), + ); + if (!accepted.includes(String(value))) { + const others = decided.map((name) => `${name} is ${chosen.get(name)}`).join(' and '); + refuse( + index, + `${label(index)} cannot be ${String(value)} when ${others}; write one of: ${accepted.join(', ')}.`, + ); + } + chosen.set(argument.name, String(value)); + decided.push(argument.name); + } + + const row = descriptor.choices.find((candidate) => rowMatches(candidate, chosen, decided)); + const chosenType = row === undefined ? undefined : input.constructorNamed(row.type); + if (row === undefined || chosenType === undefined) { + throw runtimeError( + 'CONTRACT.PACK_CONTRIBUTION_INVALID', + `Type choice "${helperPath}" chooses no registered type constructor for ${[...chosen].map(([name, value]) => `${name}: ${value}`).join(', ')}.`, + { helperPath }, + ); + } + + const parameters = chosenType.args ?? []; + descriptor.args.forEach((argument, index) => { + if (isAuthoringTypeChoiceOption(argument) || args[index] === undefined) return; + if (!parameters.some((parameter) => parameter.name === argument.name)) { + refuse( + index, + `${label(index)} does not apply to ${row.type}, the type that ${typeChoiceCombinationText(row)} chooses.`, + ); + } + }); + + return { + type: row.type, + descriptor: chosenType, + args: parameters.map((parameter) => { + const index = descriptor.args.findIndex( + (argument) => !isAuthoringTypeChoiceOption(argument) && argument.name === parameter.name, + ); + const passed = index < 0 ? undefined : args[index]; + return passed ?? (parameter.name === undefined ? undefined : row.defaults?.[parameter.name]); + }), + }; +} + +type TypeNamespaceLevel = { readonly [name: string]: unknown }; + +function isTypeChoice(value: unknown): value is AuthoringTypeChoiceDescriptor { + return ( + typeof value === 'object' && value !== null && 'kind' in value && value.kind === 'typeChoice' + ); +} + +function isTypeConstructor(value: unknown): value is AuthoringTypeConstructorDescriptor { + return ( + typeof value === 'object' && + value !== null && + 'kind' in value && + value.kind === 'typeConstructor' + ); +} + +function isSubNamespace(value: unknown): value is TypeNamespaceLevel { + return typeof value === 'object' && value !== null && !('kind' in value); +} + +function assertTypeChoiceValid( + path: string, + descriptor: AuthoringTypeChoiceDescriptor, + contributedBy: string, + level: TypeNamespaceLevel, +): void { + const invalidError = (detail: string) => + runtimeError( + 'CONTRACT.PACK_CONTRIBUTION_INVALID', + `Type choice "${path}" contributed by "${contributedBy}" ${detail}`, + { packId: contributedBy, contribution: path }, + ); + const invalid = (detail: string): never => { + throw invalidError(detail); + }; + const options = descriptor.args.filter(isAuthoringTypeChoiceOption); + const passed = descriptor.args.filter( + (argument): argument is AuthoringTypeChoicePassedArgument => + !isAuthoringTypeChoiceOption(argument), + ); + for (const option of options) { + if (!option.values.includes(option.default)) { + invalid(`defaults option "${option.name}" to ${option.default}, which it does not list.`); + } + } + for (const row of descriptor.choices) { + const covered = + Object.keys(row.when).length === options.length && + options.every((option) => option.values.includes(row.when[option.name] ?? '')); + if (!covered) { + invalid( + `has a row for ${typeChoiceCombinationText(row)}, which does not give every option one of its values.`, + ); + } + const chosenType = Object.hasOwn(level, row.type) ? level[row.type] : undefined; + if (!isTypeConstructor(chosenType)) { + throw invalidError( + `chooses "${row.type}", which is not a type constructor any component registers beside it.`, + ); + } + for (const name of Object.keys(row.defaults ?? {})) { + if (!passed.some((argument) => argument.name === name)) { + invalid(`gives a default for "${name}", which is not an argument it passes on.`); + } + } + for (const argument of passed) { + const parameter = chosenType.args?.find((candidate) => candidate.name === argument.name); + if (parameter?.kind === 'value' && parameter.dataType !== argument.dataType) { + invalid( + `passes "${argument.name}" as a value of data type "${argument.dataType}" to "${row.type}", which takes "${parameter.dataType}".`, + ); + } + } + } + const defaults: AuthoringTypeChoiceRow = { + when: Object.fromEntries(options.map((option) => [option.name, option.default])), + type: '', + }; + const names = options.map((option) => option.name); + const chosen = new Map(Object.entries(defaults.when)); + if (!descriptor.choices.some((row) => rowMatches(row, chosen, names))) { + invalid( + `has no row for its defaults ${typeChoiceCombinationText(defaults)}, so it cannot be written bare.`, + ); + } +} + +/** + * Refuses a type choice of a component's `contributed` type namespace whose table cannot be resolved: a row that names no type constructor at the same level of the `assembled` namespace, a combination or default an option does not list, defaults with no row, a parameter default for an argument it does not pass on, or a passed-on argument of another data type than the constructor takes. + */ +export function assertTypeChoicesValid( + contributed: TypeNamespaceLevel, + contributedBy: string, + assembled: TypeNamespaceLevel, + path: readonly string[] = [], +): void { + for (const [name, value] of Object.entries(contributed)) { + const currentPath = [...path, name]; + if (isTypeChoice(value)) { + assertTypeChoiceValid(currentPath.join('.'), value, contributedBy, assembled); + continue; + } + const assembledLevel = Object.hasOwn(assembled, name) ? assembled[name] : undefined; + if (isSubNamespace(value) && isSubNamespace(assembledLevel)) { + assertTypeChoicesValid(value, contributedBy, assembledLevel, currentPath); + } + } +} diff --git a/packages/1-framework/1-core/framework-components/src/shared/authoring-type-constructor-call.ts b/packages/1-framework/1-core/framework-components/src/shared/authoring-type-constructor-call.ts index efcfce7cf861..0b4a15327081 100644 --- a/packages/1-framework/1-core/framework-components/src/shared/authoring-type-constructor-call.ts +++ b/packages/1-framework/1-core/framework-components/src/shared/authoring-type-constructor-call.ts @@ -4,8 +4,8 @@ import { type AuthoringTypeNamespace, instantiateAuthoringTypeConstructor, isAuthoringArgRef, - isAuthoringTypeAliasDescriptor, isAuthoringTypeConstructorDescriptor, + isAuthoringTypeDescriptor, } from './framework-authoring'; /** What a type constructor call produces for a column. */ @@ -28,7 +28,7 @@ export function* authoringTypeConstructors( for (const [name, value] of Object.entries(namespace)) { if (isAuthoringTypeConstructorDescriptor(value)) { yield { path: [...path, name], descriptor: value }; - } else if (!isAuthoringTypeAliasDescriptor(value)) { + } else if (!isAuthoringTypeDescriptor(value)) { yield* authoringTypeConstructors(value, [...path, name]); } } diff --git a/packages/1-framework/1-core/framework-components/src/shared/framework-authoring.ts b/packages/1-framework/1-core/framework-components/src/shared/framework-authoring.ts index e7bf053cfa47..80503ee90b2d 100644 --- a/packages/1-framework/1-core/framework-components/src/shared/framework-authoring.ts +++ b/packages/1-framework/1-core/framework-components/src/shared/framework-authoring.ts @@ -15,6 +15,10 @@ import { InternalError } from '@internal/utils/internal-error'; import { notOk, ok, type Result } from '@internal/utils/result'; import type { StandardSchemaV1 } from '@standard-schema/spec'; import type { Type } from 'arktype'; +import { + type AuthoringTypeChoiceDescriptor, + resolveAuthoringTypeChoice, +} from './authoring-type-choice'; import type { CodecLookupWithDescriptors } from './codec-types'; import type { DataTypeId, DataTypeLookup } from './data-type'; import type { @@ -193,14 +197,22 @@ export interface AuthoringTypeAliasDescriptor { } export type AuthoringTypeNamespace = { - readonly [name: string]: - | AuthoringTypeConstructorDescriptor - | AuthoringTypeAliasDescriptor - | AuthoringTypeNamespace; + readonly [name: string]: AuthoringTypeNamespaceMember; }; +/** A leaf of a `type` namespace, or a sub-namespace. */ +export type AuthoringTypeNamespaceMember = + | AuthoringTypeConstructorDescriptor + | AuthoringTypeAliasDescriptor + | AuthoringTypeChoiceDescriptor + | AuthoringTypeNamespace; + /** The descriptor kinds a `type` namespace holds as leaves. */ -export const AUTHORING_TYPE_DESCRIPTOR_KINDS = ['typeConstructor', 'typeAlias'] as const; +export const AUTHORING_TYPE_DESCRIPTOR_KINDS = [ + 'typeConstructor', + 'typeAlias', + 'typeChoice', +] as const; export type AuthoringFieldNamespace = { readonly [name: string]: AuthoringFieldPresetDescriptor | AuthoringFieldNamespace; @@ -727,22 +739,47 @@ function readTemplateArgumentValue( } export function isAuthoringTypeConstructorDescriptor( - value: AuthoringTypeConstructorDescriptor | AuthoringTypeAliasDescriptor | AuthoringTypeNamespace, + value: AuthoringTypeNamespaceMember, ): value is AuthoringTypeConstructorDescriptor { return 'kind' in value && value.kind === 'typeConstructor'; } export function isAuthoringTypeAliasDescriptor( - value: AuthoringTypeConstructorDescriptor | AuthoringTypeAliasDescriptor | AuthoringTypeNamespace, + value: AuthoringTypeNamespaceMember, ): value is AuthoringTypeAliasDescriptor { return 'kind' in value && value.kind === 'typeAlias'; } -/** Whether `value` is a leaf of a `type` namespace: a type constructor or a type alias. */ +export function isAuthoringTypeChoiceDescriptor( + value: AuthoringTypeNamespaceMember, +): value is AuthoringTypeChoiceDescriptor { + return 'kind' in value && value.kind === 'typeChoice'; +} + +/** Whether `value` is a leaf of a `type` namespace: a type constructor, a type alias or a type choice. */ export function isAuthoringTypeDescriptor( - value: AuthoringTypeConstructorDescriptor | AuthoringTypeAliasDescriptor | AuthoringTypeNamespace, -): value is AuthoringTypeConstructorDescriptor | AuthoringTypeAliasDescriptor { - return isAuthoringTypeConstructorDescriptor(value) || isAuthoringTypeAliasDescriptor(value); + value: AuthoringTypeNamespaceMember, +): value is + | AuthoringTypeConstructorDescriptor + | AuthoringTypeAliasDescriptor + | AuthoringTypeChoiceDescriptor { + return ( + isAuthoringTypeConstructorDescriptor(value) || + isAuthoringTypeAliasDescriptor(value) || + isAuthoringTypeChoiceDescriptor(value) + ); +} + +/** Looks a type constructor up by name in one level of a type namespace, as a type choice's rows name them. */ +export function typeConstructorLookup( + namespace: AuthoringTypeNamespace, +): (name: string) => AuthoringTypeConstructorDescriptor | undefined { + return (name) => { + const member = Object.hasOwn(namespace, name) ? namespace[name] : undefined; + return member !== undefined && isAuthoringTypeConstructorDescriptor(member) + ? member + : undefined; + }; } /** The type constructor at `path` in the contributions' type namespace, or `undefined` when none is there. */ @@ -750,11 +787,7 @@ export function getAuthoringTypeConstructor( contributions: { readonly type?: AuthoringTypeNamespace } | undefined, path: readonly string[], ): AuthoringTypeConstructorDescriptor | undefined { - let current: - | AuthoringTypeConstructorDescriptor - | AuthoringTypeAliasDescriptor - | AuthoringTypeNamespace - | undefined = contributions?.type; + let current: AuthoringTypeNamespaceMember | undefined = contributions?.type; for (const segment of path) { if (typeof current !== 'object' || current === null || 'kind' in current) { return undefined; @@ -827,6 +860,13 @@ function isWellFormedDescriptor( const output = value.output; return typeof output === 'object' && output !== null; } + case 'typeChoice': + return ( + 'args' in value && + Array.isArray(value.args) && + 'choices' in value && + Array.isArray(value.choices) + ); case 'typeAlias': { if (!('output' in value)) return false; const output = value.output; @@ -1081,6 +1121,10 @@ export function collectScalarTypeConstructors( ): ReadonlyMap { const result = new Map(); for (const [name, value] of Object.entries(namespace)) { + if (isAuthoringTypeChoiceDescriptor(value)) { + result.set(name, instantiateAuthoringTypeCall(namespace, name, [])); + continue; + } if (!isAuthoringTypeConstructorDescriptor(value)) continue; if (value.entityRefArg !== undefined) continue; if (value.args?.some((arg) => arg.optional !== true)) continue; @@ -1140,6 +1184,7 @@ export function assertResolvableTypeConstructorTemplates( } continue; } + if (isAuthoringTypeChoiceDescriptor(value)) continue; if (!isAuthoringTypeConstructorDescriptor(value)) { assertResolvableTypeConstructorTemplates(value, contributedBy, currentPath); continue; @@ -1950,6 +1995,32 @@ export function instantiateAuthoringTypeConstructor( return resolveAuthoringStorageTypeTemplate(descriptor.output, args); } +/** + * The column a call of the type constructor or type choice `name`, in one level of a type namespace, makes from its arguments. A type choice's arguments choose a constructor of the same level, which is instantiated with the arguments it passes on. + */ +export function instantiateAuthoringTypeCall( + namespace: AuthoringTypeNamespace, + name: string, + args: readonly unknown[], +): ScalarTypeConstructorOutput { + const member = Object.hasOwn(namespace, name) ? namespace[name] : undefined; + if (member !== undefined && isAuthoringTypeConstructorDescriptor(member)) { + return instantiateAuthoringTypeConstructor(member, args); + } + if (member === undefined || !isAuthoringTypeChoiceDescriptor(member)) { + throw new InternalError( + `"${name}" is not a type constructor or type choice of this namespace.`, + ); + } + const chosen = resolveAuthoringTypeChoice({ + helperPath: name, + descriptor: member, + args, + constructorNamed: typeConstructorLookup(namespace), + }); + return instantiateAuthoringTypeConstructor(chosen.descriptor, chosen.args); +} + /** What a field typed by a contributed type alias takes: the type, and the alias's generator calls. */ export interface AuthoringTypeAliasOutput { readonly type: ScalarTypeConstructorOutput; diff --git a/packages/1-framework/1-core/framework-components/test/authoring-type-choice.test.ts b/packages/1-framework/1-core/framework-components/test/authoring-type-choice.test.ts new file mode 100644 index 000000000000..1f4cc98ca064 --- /dev/null +++ b/packages/1-framework/1-core/framework-components/test/authoring-type-choice.test.ts @@ -0,0 +1,130 @@ +import { describe, expect, it } from 'vitest'; +import { resolveAuthoringTypeChoice } from '../src/shared/authoring-type-choice'; +import { + type AuthoringTypeNamespace, + collectScalarTypeConstructors, + instantiateAuthoringTypeCall, + typeConstructorLookup, +} from '../src/shared/framework-authoring'; + +const precision = { + kind: 'value', + dataType: 'test/int', + name: 'precision', + optional: true, +} as const; + +const namespace = { + Moment: { + kind: 'typeChoice', + args: [ + { kind: 'option', name: 'value', values: ['date', 'text'], default: 'date', optional: true }, + { + kind: 'option', + name: 'column', + values: ['wide', 'narrow'], + default: 'wide', + optional: true, + }, + precision, + ], + choices: [ + { when: { value: 'date', column: 'wide' }, type: 'WideDate', defaults: { precision: 3 } }, + { when: { value: 'text', column: 'wide' }, type: 'WideText' }, + { when: { value: 'date', column: 'narrow' }, type: 'NarrowDate' }, + ], + }, + WideDate: { + kind: 'typeConstructor', + args: [precision], + output: { codecId: 'test/wide-date@1', typeParams: { precision: { kind: 'arg', index: 0 } } }, + }, + WideText: { + kind: 'typeConstructor', + args: [precision], + output: { codecId: 'test/wide-text@1', typeParams: { precision: { kind: 'arg', index: 0 } } }, + }, + NarrowDate: { + kind: 'typeConstructor', + output: { codecId: 'test/narrow-date@1' }, + }, +} as const satisfies AuthoringTypeNamespace; + +const resolve = (args: readonly unknown[]) => + resolveAuthoringTypeChoice({ + helperPath: 'Moment', + descriptor: namespace.Moment, + args, + constructorNamed: typeConstructorLookup(namespace), + }); + +function refusal(args: readonly unknown[]) { + try { + resolve(args); + } catch (error) { + const { message, details } = error as { message: string; details: unknown }; + return { message, details }; + } + throw new Error('expected a refusal'); +} + +describe('resolveAuthoringTypeChoice', () => { + it('chooses the default row, with its parameter defaults, for a bare call', () => { + expect(resolve([])).toEqual({ + type: 'WideDate', + descriptor: namespace.WideDate, + args: [3], + }); + }); + + it('reads option arguments by position', () => { + expect(resolve(['text'])).toMatchObject({ type: 'WideText', args: [undefined] }); + }); + + it('fills an option argument left out before a written one with its default', () => { + expect(resolve([undefined, 'narrow'])).toMatchObject({ type: 'NarrowDate', args: [] }); + }); + + it('passes a written argument on by name, over the row default', () => { + expect(resolve(['date', undefined, 6])).toMatchObject({ type: 'WideDate', args: [6] }); + }); + + it('refuses a value the option does not list, at that argument, naming its values', () => { + expect(refusal(['bogus'])).toEqual({ + message: 'Argument "value" of Moment is bogus; write one of: date, text.', + details: { helperPath: 'Moment', argumentIndex: 0 }, + }); + }); + + it('refuses an unlisted combination at the argument that makes it invalid, naming what it accepts', () => { + expect(refusal(['text', 'narrow'])).toEqual({ + message: + 'Argument "column" of Moment cannot be narrow when value is text; write one of: wide.', + details: { helperPath: 'Moment', argumentIndex: 1 }, + }); + }); + + it('refuses a passed-on argument the chosen type does not take', () => { + expect(refusal([undefined, 'narrow', 6])).toEqual({ + message: + 'Argument "precision" of Moment does not apply to NarrowDate, the type that value: date, column: narrow chooses.', + details: { helperPath: 'Moment', argumentIndex: 2 }, + }); + }); +}); + +describe('a type choice in the type namespace', () => { + it('instantiates to the column its chosen constructor makes', () => { + expect(instantiateAuthoringTypeCall(namespace, 'Moment', ['text', undefined, 6])).toEqual({ + codecId: 'test/wide-text@1', + typeParams: { precision: 6 }, + }); + }); + + it('is a scalar, written bare as its default choice', () => { + expect(collectScalarTypeConstructors(namespace).get('Moment')).toEqual({ + codecId: 'test/wide-date@1', + typeParams: { precision: 3 }, + }); + }); +}); diff --git a/packages/1-framework/1-core/framework-components/test/type-choice-assembly.test.ts b/packages/1-framework/1-core/framework-components/test/type-choice-assembly.test.ts new file mode 100644 index 000000000000..b81c814a4544 --- /dev/null +++ b/packages/1-framework/1-core/framework-components/test/type-choice-assembly.test.ts @@ -0,0 +1,147 @@ +import { describe, expect, it } from 'vitest'; +import type { CreateControlStackInput } from '../src/control/control-stack'; +import { createControlStack } from '../src/control/control-stack'; +import { + type AuthoringTypeChoiceDescriptor, + assertTypeChoicesValid, +} from '../src/shared/authoring-type-choice'; +import type { AuthoringTypeNamespace } from '../src/shared/framework-authoring'; + +const precision = { + kind: 'value', + dataType: 'test/int', + name: 'precision', + optional: true, +} as const; + +const valueOption = { + kind: 'option', + name: 'value', + values: ['date', 'text'], + default: 'date', + optional: true, +} as const; + +const choice = (overrides: Partial = {}) => + ({ + kind: 'typeChoice', + args: [valueOption, precision], + choices: [ + { when: { value: 'date' }, type: 'WideDate', defaults: { precision: 3 } }, + { when: { value: 'text' }, type: 'WideText' }, + ], + ...overrides, + }) satisfies AuthoringTypeChoiceDescriptor; + +const constructors = { + WideDate: { + kind: 'typeConstructor', + args: [precision], + output: { codecId: 'test/wide-date@1', typeParams: { precision: { kind: 'arg', index: 0 } } }, + }, + WideText: { kind: 'typeConstructor', output: { codecId: 'test/wide-text@1' } }, +} as const satisfies AuthoringTypeNamespace; + +const check = (moment: AuthoringTypeChoiceDescriptor, assembled: AuthoringTypeNamespace = {}) => { + const contributed = { Moment: moment }; + return () => + assertTypeChoicesValid(contributed, 'x-pack', { + ...constructors, + ...contributed, + ...assembled, + }); +}; + +describe('assertTypeChoicesValid', () => { + it('passes a choice whose rows name constructors of its namespace', () => { + expect(check(choice())).not.toThrow(); + }); + + it('passes a choice naming a constructor another component registers', () => { + const contributed = { Moment: choice() }; + expect(() => + assertTypeChoicesValid(contributed, 'x-pack', { ...contributed, ...constructors }), + ).not.toThrow(); + }); + + it('refuses a row naming a constructor no component registers, naming the pack and the name', () => { + expect(check(choice({ choices: [{ when: { value: 'date' }, type: 'Missing' }] }))).toThrow( + 'Type choice "Moment" contributed by "x-pack" chooses "Missing", which is not a type constructor any component registers beside it.', + ); + }); + + it('refuses a default no row matches', () => { + expect(check(choice({ choices: [{ when: { value: 'text' }, type: 'WideText' }] }))).toThrow( + 'Type choice "Moment" contributed by "x-pack" has no row for its defaults value: date, so it cannot be written bare.', + ); + }); + + it('refuses a default the option does not list', () => { + expect( + check( + choice({ + args: [ + { kind: 'option', name: 'value', values: ['text'], default: 'date', optional: true }, + ], + }), + ), + ).toThrow( + 'Type choice "Moment" contributed by "x-pack" defaults option "value" to date, which it does not list.', + ); + }); + + it('refuses a row whose combination names a value an option does not list', () => { + expect( + check( + choice({ + choices: [ + { when: { value: 'date' }, type: 'WideDate' }, + { when: { value: 'json' }, type: 'WideText' }, + ], + }), + ), + ).toThrow( + 'Type choice "Moment" contributed by "x-pack" has a row for value: json, which does not give every option one of its values.', + ); + }); + + it('refuses a parameter default for an argument the choice does not pass on', () => { + expect( + check( + choice({ + choices: [ + { when: { value: 'date' }, type: 'WideDate', defaults: { scale: 2 } }, + { when: { value: 'text' }, type: 'WideText' }, + ], + }), + ), + ).toThrow( + 'Type choice "Moment" contributed by "x-pack" gives a default for "scale", which is not an argument it passes on.', + ); + }); + + it('refuses a passed-on argument of another data type than the chosen constructor takes', () => { + expect(check(choice({ args: [valueOption, { ...precision, dataType: 'test/text' }] }))).toThrow( + 'Type choice "Moment" contributed by "x-pack" passes "precision" as a value of data type "test/text" to "WideDate", which takes "test/int".', + ); + }); +}); + +describe('createControlStack', () => { + it('refuses a type choice naming a constructor no component registers', () => { + const input = { + family: { kind: 'family', id: 'fam', version: '0.0.1' }, + target: { + kind: 'target', + id: 'tgt', + version: '0.0.1', + authoring: { + type: { Moment: choice({ choices: [{ when: { value: 'date' }, type: 'Missing' }] }) }, + }, + }, + extensions: [], + } as unknown as CreateControlStackInput; + + expect(() => createControlStack(input)).toThrow(/"Moment" contributed by "tgt".*"Missing"/); + }); +}); From 02cfe3fc0ce1e4e8ecb4dc4aa92942f7181d92b2 Mon Sep 17 00:00:00 2001 From: willbot Date: Thu, 22 Oct 2026 20:17:24 +0200 Subject: [PATCH 034/118] PSL readers resolve a type choice to the constructor its call chooses A field or types {} alias typed by a type choice gets the column of the constructor its arguments choose. @default admission and the @onCreate/@onUpdate generator lookup read the codec the field's call, or its alias's call, chooses rather than a fixed codec of the type. Co-Authored-By: Claude Opus 5.5 Signed-off-by: willbot Signed-off-by: Will Madden --- .../src/attribute-spec/generator-attribute.ts | 4 +- .../src/attribute-spec/spec-context.ts | 10 +- .../2-authoring/psl-parser/src/binder.ts | 27 +- .../psl-parser/src/contributed-type-scope.ts | 110 +++++++- .../psl-parser/src/exports/index.ts | 3 +- .../psl-parser/src/type-aliases.ts | 23 +- .../test/binder-contributed-types.test.ts | 103 ++++++- .../psl-parser/test/binder.mixin.test.ts | 2 +- .../psl-parser/test/type-aliases.test.ts | 2 +- .../src/attribute-spec-resolution.ts | 6 +- .../language-server/src/completion-scope.ts | 6 +- .../contract-psl/src/interpreter.ts | 55 +++- .../contract-psl/src/type-aliases.ts | 23 ++ .../contract-psl/src/psl-column-resolution.ts | 107 ++++++- .../src/psl-named-type-resolution.ts | 14 + .../contract-psl/src/sql-attribute-specs.ts | 9 +- .../test/interpreter.type-choices.test.ts | 261 ++++++++++++++++++ 17 files changed, 702 insertions(+), 63 deletions(-) create mode 100644 packages/2-sql/2-authoring/contract-psl/test/interpreter.type-choices.test.ts diff --git a/packages/1-framework/2-authoring/psl-parser/src/attribute-spec/generator-attribute.ts b/packages/1-framework/2-authoring/psl-parser/src/attribute-spec/generator-attribute.ts index f7fda2aa1ca2..ba187f48f7d5 100644 --- a/packages/1-framework/2-authoring/psl-parser/src/attribute-spec/generator-attribute.ts +++ b/packages/1-framework/2-authoring/psl-parser/src/attribute-spec/generator-attribute.ts @@ -69,9 +69,7 @@ export function generatorFunctionCall( */ function generatorCallArm(ctx: TypeAttributeSpecContext) { const codecId = - ctx.typeConstructor === undefined - ? undefined - : contributedTypeCodecId(ctx.typeConstructor.descriptor); + ctx.typeConstructor === undefined ? undefined : contributedTypeCodecId(ctx.typeConstructor); const functions = codecId === undefined ? [] : ctx.generatorFunctions.functionsFor(codecId); const subject = typeAttributeSubjectLabel(ctx.subject); const target = codecId === undefined ? "the field's type" : `codec "${codecId}"`; diff --git a/packages/1-framework/2-authoring/psl-parser/src/attribute-spec/spec-context.ts b/packages/1-framework/2-authoring/psl-parser/src/attribute-spec/spec-context.ts index 1a7ca5bafdf5..ee1f6009e517 100644 --- a/packages/1-framework/2-authoring/psl-parser/src/attribute-spec/spec-context.ts +++ b/packages/1-framework/2-authoring/psl-parser/src/attribute-spec/spec-context.ts @@ -4,7 +4,7 @@ import type { GeneratorFunctionRegistry } from '@internal/framework-components/c import type { ControlMutationDefaultRegistry } from '@internal/framework-components/control'; import type { Resolution } from '../binder'; import type { BlockSpecContext } from '../block-spec/types'; -import type { ContributedTypeSymbol } from '../contributed-type-scope'; +import type { ContributedTypeUse } from '../contributed-type-scope'; import type { FieldSymbol, MixinSymbol, ModelSymbol, SymbolTable } from '../symbol-table'; import type { AttributeSpec, @@ -31,8 +31,8 @@ export const EMPTY_DATA_TYPES: DataTypeSupport = { export interface FieldAttributeSpecContext extends AttributeSpecContext { readonly field: FieldSymbol; readonly typeResolution: Resolution | undefined; - /** The type constructor or field preset the field's type resolves to, through a named type when it is declared with one. */ - readonly typeConstructor: ContributedTypeSymbol | undefined; + /** The contributed type the field's type resolves to, through a named type when it is declared with one, with the call it is written with. */ + readonly typeConstructor: ContributedTypeUse | undefined; } /** The declaration an attribute that reads its type is written on: a field, or a type alias whose fields take the attribute. */ @@ -54,8 +54,8 @@ export interface TypeAttributeSpecContext extends Omit binder.symbolForNode(base)); + call: ResolvedTypeConstructorCall | undefined, +): ContributedTypeUse | undefined { + return contributedTypeOfResolution(resolution, (base) => binder.symbolForNode(base), call); } function contributedTypeOfResolution( resolution: Resolution | undefined, resolve: (node: SyntaxNode) => Resolution | undefined, -): ContributedTypeSymbol | undefined { - const base = resolution?.kind === 'namedType' ? typeReferenceNode(resolution.symbol) : undefined; + call: ResolvedTypeConstructorCall | undefined, +): ContributedTypeUse | undefined { + const named = resolution?.kind === 'namedType' ? resolution.symbol : undefined; + const base = named === undefined ? undefined : typeReferenceNode(named); const target = base === undefined ? resolution : resolve(base); - return target?.kind === 'contributedType' ? target.symbol : undefined; + if (target?.kind !== 'contributedType') return undefined; + return { symbol: target.symbol, call: named === undefined ? call : named.typeConstructor }; } class PslBinder implements Binder { @@ -506,8 +513,10 @@ function bind(options: BindingInputs): BinderResult { if (specContext === undefined) return undefined; const node = typeReferenceNode(field); const typeResolution = node === undefined ? undefined : references.get(node); - const typeConstructor = contributedTypeOfResolution(typeResolution, (base) => - references.get(base), + const typeConstructor = contributedTypeOfResolution( + typeResolution, + (base) => references.get(base), + field.typeConstructor, ); return factory({ ...specContext, field, typeResolution, typeConstructor }); }, diff --git a/packages/1-framework/2-authoring/psl-parser/src/contributed-type-scope.ts b/packages/1-framework/2-authoring/psl-parser/src/contributed-type-scope.ts index f49ddc45df91..f45c30767e60 100644 --- a/packages/1-framework/2-authoring/psl-parser/src/contributed-type-scope.ts +++ b/packages/1-framework/2-authoring/psl-parser/src/contributed-type-scope.ts @@ -1,12 +1,19 @@ -import type { - AuthoringFieldPresetDescriptor, - AuthoringTypeAliasDescriptor, - AuthoringTypeConstructorDescriptor, +import { + type AuthoringFieldPresetDescriptor, + type AuthoringTypeAliasDescriptor, + type AuthoringTypeChoiceDescriptor, + type AuthoringTypeConstructorDescriptor, + isAuthoringTypeChoiceOption, + resolveAuthoringTypeChoice, } from '@internal/framework-components/authoring'; +import { isInternalError } from '@internal/utils/internal-error'; +import { notOk, ok, type Result } from '@internal/utils/result'; +import type { ResolvedTypeConstructorCall } from './resolve'; export type ContributedTypeDescriptor = | AuthoringTypeConstructorDescriptor | AuthoringTypeAliasDescriptor + | AuthoringTypeChoiceDescriptor | AuthoringFieldPresetDescriptor; export type ContributedTypeNamespace = { @@ -18,6 +25,14 @@ export interface ContributedTypeSymbol { readonly name: string; readonly path: readonly string[]; readonly descriptor: ContributedTypeDescriptor; + /** For a type choice, the type constructors its rows name, by name, from the namespace that holds it. */ + readonly choiceTypes?: ReadonlyMap; +} + +/** A contributed type as a field or type alias uses it: the type, and the call it is written with, `undefined` when it is written bare. */ +export interface ContributedTypeUse { + readonly symbol: ContributedTypeSymbol; + readonly call: ResolvedTypeConstructorCall | undefined; } export interface ContributedNamespaceSymbol { @@ -39,15 +54,62 @@ export function isContributedTypeDescriptor( ): value is ContributedTypeDescriptor { return ( 'kind' in value && - (value.kind === 'typeConstructor' || value.kind === 'typeAlias' || value.kind === 'fieldPreset') + (value.kind === 'typeConstructor' || + value.kind === 'typeAlias' || + value.kind === 'typeChoice' || + value.kind === 'fieldPreset') ); } -/** The codec a column typed by `descriptor` is stored with. */ -export function contributedTypeCodecId(descriptor: ContributedTypeDescriptor): string { - return descriptor.kind === 'typeAlias' - ? descriptor.output.type.codecId - : descriptor.output.codecId; +/** The bare-name values a call writes for the options of a type choice, by the choice's argument positions; every other argument is left out. */ +function writtenOptions( + descriptor: AuthoringTypeChoiceDescriptor, + call: ResolvedTypeConstructorCall | undefined, +): readonly unknown[] { + const positional = call?.args.filter((arg) => arg.kind === 'positional') ?? []; + return descriptor.args.map((argument, index) => { + if (!isAuthoringTypeChoiceOption(argument)) return undefined; + const written = + call?.args.find((arg) => arg.kind === 'named' && arg.name === argument.name) ?? + positional[index]; + return written?.value.trim(); + }); +} + +/** The type constructor a use of the type choice `descriptor` chooses from its options, or why it chooses none. */ +export function chosenTypeConstructor( + use: ContributedTypeUse, + descriptor: AuthoringTypeChoiceDescriptor, +): Result { + const { choiceTypes, path } = use.symbol; + try { + return ok( + resolveAuthoringTypeChoice({ + helperPath: path.join('.'), + descriptor, + args: writtenOptions(descriptor, use.call), + constructorNamed: (name) => choiceTypes?.get(name), + }).descriptor, + ); + } catch (error) { + if (isInternalError(error) || !(error instanceof Error)) throw error; + return notOk(error.message); + } +} + +/** The codec a column typed by `use` is stored with; `undefined` for a type choice whose call chooses no type, which the column's resolution reports. */ +export function contributedTypeCodecId(use: ContributedTypeUse): string | undefined { + const { descriptor } = use.symbol; + switch (descriptor.kind) { + case 'typeAlias': + return descriptor.output.type.codecId; + case 'typeChoice': { + const chosen = chosenTypeConstructor(use, descriptor); + return chosen.ok ? chosen.value.output.codecId : undefined; + } + default: + return descriptor.output.codecId; + } } export function mergeContributedTypes( @@ -92,9 +154,35 @@ function collect( members.set( name, isContributedTypeDescriptor(value) - ? { kind: 'contributedType', name, path, descriptor: value } + ? { + kind: 'contributedType', + name, + path, + descriptor: value, + ...(value.kind === 'typeChoice' + ? { choiceTypes: choiceTypesOf(value, namespace) } + : {}), + } : { kind: 'contributedNamespace', name, path, members: collect(value, path) }, ); } return members; } + +function choiceTypesOf( + descriptor: AuthoringTypeChoiceDescriptor, + namespace: ContributedTypeNamespace, +): ReadonlyMap { + const types = new Map(); + for (const { type } of descriptor.choices) { + const member = Object.hasOwn(namespace, type) ? namespace[type] : undefined; + if ( + member !== undefined && + isContributedTypeDescriptor(member) && + member.kind === 'typeConstructor' + ) { + types.set(type, member); + } + } + return types; +} diff --git a/packages/1-framework/2-authoring/psl-parser/src/exports/index.ts b/packages/1-framework/2-authoring/psl-parser/src/exports/index.ts index a0f23947d69d..d7367a5035a4 100644 --- a/packages/1-framework/2-authoring/psl-parser/src/exports/index.ts +++ b/packages/1-framework/2-authoring/psl-parser/src/exports/index.ts @@ -176,8 +176,9 @@ export type { ContributedTypeNamespace, ContributedTypeScope, ContributedTypeSymbol, + ContributedTypeUse, } from '../contributed-type-scope'; -export { contributedTypeCodecId } from '../contributed-type-scope'; +export { chosenTypeConstructor, contributedTypeCodecId } from '../contributed-type-scope'; export type { DiagnosticSource, PslDiagnostic, PslDiagnosticCollector } from '../diagnostic'; export { createPslDiagnosticCollector, diff --git a/packages/1-framework/2-authoring/psl-parser/src/type-aliases.ts b/packages/1-framework/2-authoring/psl-parser/src/type-aliases.ts index 58918463b2bd..b166b387e3a0 100644 --- a/packages/1-framework/2-authoring/psl-parser/src/type-aliases.ts +++ b/packages/1-framework/2-authoring/psl-parser/src/type-aliases.ts @@ -1,5 +1,6 @@ import { type AuthoringTypeAliasDescriptor, + type AuthoringTypeChoiceDescriptor, type AuthoringTypeConstructorDescriptor, type AuthoringTypeNamespace, type DataTypeSupport, @@ -10,6 +11,7 @@ import type { GeneratorFunctionRegistry } from '@internal/framework-components/c import type { ControlMutationDefaultRegistry } from '@internal/framework-components/control'; import type { TypeAttributeSpecContext } from './attribute-spec/spec-context'; import { type Binder, contributedTypeOf, type Resolution, typeReferenceNode } from './binder'; +import type { ContributedTypeSymbol } from './contributed-type-scope'; import { diagnosticSource, type PslDiagnosticCollector } from './diagnostic'; import type { PslSources } from './source-file'; import type { @@ -40,6 +42,13 @@ export type TypeAliasBase = /** `undefined` when the constructor is written bare. */ readonly call: ResolvedTypeConstructorCall | undefined; } + | { + readonly kind: 'typeChoice'; + readonly descriptor: AuthoringTypeChoiceDescriptor; + readonly symbol: ContributedTypeSymbol; + /** `undefined` when the type choice is written bare. */ + readonly call: ResolvedTypeConstructorCall | undefined; + } | { readonly kind: 'block'; readonly symbol: BlockSymbol }; /** A `types {}` declaration read as a type alias: its base, one level deep, and the attributes its fields take. */ @@ -185,6 +194,14 @@ function baseOf( } if (resolution?.kind !== 'contributedType') return undefined; const { descriptor } = resolution.symbol; + if (descriptor.kind === 'typeChoice') { + return { + kind: 'typeChoice', + descriptor, + symbol: resolution.symbol, + call: declaration.typeConstructor, + }; + } if (descriptor.kind !== 'typeConstructor') return undefined; if (declaration.isConstructor) { return { kind: 'typeConstructor', descriptor, call: declaration.typeConstructor }; @@ -343,7 +360,11 @@ export function typeAliasSpecContext( list: false, typeName: alias.baseName, typeResolution: alias.baseResolution, - typeConstructor: contributedTypeOf(alias.baseResolution, input.binder), + typeConstructor: contributedTypeOf( + alias.baseResolution, + input.binder, + alias.symbol.typeConstructor, + ), }; } diff --git a/packages/1-framework/2-authoring/psl-parser/test/binder-contributed-types.test.ts b/packages/1-framework/2-authoring/psl-parser/test/binder-contributed-types.test.ts index 9d43d2c9862e..f7b895309e19 100644 --- a/packages/1-framework/2-authoring/psl-parser/test/binder-contributed-types.test.ts +++ b/packages/1-framework/2-authoring/psl-parser/test/binder-contributed-types.test.ts @@ -1,5 +1,6 @@ import type { AuthoringFieldPresetDescriptor, + AuthoringTypeChoiceDescriptor, AuthoringTypeConstructorDescriptor, } from '@internal/framework-components/authoring'; import { EMPTY_GENERATOR_FUNCTIONS } from '@internal/framework-components/components'; @@ -11,6 +12,7 @@ import { createBinder, typeReferenceNode, } from '../src/binder'; +import { contributedTypeCodecId } from '../src/contributed-type-scope'; import { parse } from '../src/parse'; import { PslSources } from '../src/source-file'; import { buildSymbolTable, type FieldSymbol, type NamedTypeSymbol } from '../src/symbol-table'; @@ -51,10 +53,45 @@ const secondTagged: AuthoringTypeConstructorDescriptor = { output: { codecId: 'fixture/tagged@1' }, }; +const wideDate: AuthoringTypeConstructorDescriptor = { + kind: 'typeConstructor', + output: { codecId: 'fixture/wide-date@1' }, +}; + +const wideText: AuthoringTypeConstructorDescriptor = { + kind: 'typeConstructor', + output: { codecId: 'fixture/wide-text@1' }, +}; + +const narrowDate: AuthoringTypeConstructorDescriptor = { + kind: 'typeConstructor', + output: { codecId: 'fixture/narrow-date@1' }, +}; + +const moment: AuthoringTypeChoiceDescriptor = { + kind: 'typeChoice', + args: [ + { kind: 'option', name: 'value', values: ['date', 'text'], default: 'date', optional: true }, + { kind: 'option', name: 'column', values: ['wide', 'narrow'], default: 'wide', optional: true }, + ], + choices: [ + { when: { value: 'date', column: 'wide' }, type: 'WideDate' }, + { when: { value: 'text', column: 'wide' }, type: 'WideText' }, + { when: { value: 'date', column: 'narrow' }, type: 'NarrowDate' }, + ], +}; + function context(): BinderContext { return { authoringContributions: { - type: { String: text, db: { VarChar: varchar, tagged, secondTagged } }, + type: { + String: text, + Moment: moment, + WideDate: wideDate, + WideText: wideText, + NarrowDate: narrowDate, + db: { VarChar: varchar, tagged, secondTagged }, + }, field: { temporal: { createdAt }, db: { uuid } }, entityTypes: {}, attributeSpecs: { model: {}, field: {} }, @@ -157,11 +194,9 @@ describe('createBinder — contributed types', () => { it('returns the contributed type a field resolves to', () => { const { resolve, binder } = bindProject('model Post {\n title String\n}'); - expect(contributedTypeOf(resolve('title'), binder)).toEqual({ - kind: 'contributedType', - name: 'String', - path: ['String'], - descriptor: text, + expect(contributedTypeOf(resolve('title'), binder, undefined)).toEqual({ + symbol: { kind: 'contributedType', name: 'String', path: ['String'], descriptor: text }, + call: undefined, }); }); @@ -171,11 +206,9 @@ describe('createBinder — contributed types', () => { ); expect(resolve('slug')?.kind).toBe('namedType'); - expect(contributedTypeOf(resolve('slug'), binder)).toEqual({ - kind: 'contributedType', - name: 'VarChar', - path: ['db', 'VarChar'], - descriptor: varchar, + expect(contributedTypeOf(resolve('slug'), binder, undefined)).toMatchObject({ + symbol: { kind: 'contributedType', name: 'VarChar', path: ['db', 'VarChar'] }, + call: { path: ['db', 'VarChar'] }, }); }); @@ -184,9 +217,51 @@ describe('createBinder — contributed types', () => { 'model Author {\n id String\n}\nmodel Post {\n author Author\n missing Nope\n}', ); - expect(contributedTypeOf(resolve('author'), binder)).toBeUndefined(); - expect(contributedTypeOf(resolve('missing'), binder)).toBeUndefined(); - expect(contributedTypeOf(undefined, binder)).toBeUndefined(); + expect(contributedTypeOf(resolve('author'), binder, undefined)).toBeUndefined(); + expect(contributedTypeOf(resolve('missing'), binder, undefined)).toBeUndefined(); + expect(contributedTypeOf(undefined, binder, undefined)).toBeUndefined(); + }); + }); + + describe('contributedTypeCodecId of a type choice', () => { + const { resolve, binder, symbolTable } = bindProject( + [ + 'types {', + ' Stamp = Moment(value: text)', + '}', + 'model Post {', + ' bare Moment', + ' named Moment(column: narrow)', + ' positional Moment(text)', + ' aliased Stamp', + ' refused Moment(value: text, column: narrow)', + '}', + ].join('\n'), + ); + const codecOf = (fieldName: string) => { + const field = symbolTable.defaultNamespace.models['Post']?.fields[fieldName]; + const use = contributedTypeOf(resolve(fieldName), binder, field?.typeConstructor); + return use === undefined ? 'no contributed type' : contributedTypeCodecId(use); + }; + + it('is the codec of the constructor the call chooses, bare, named or positional', () => { + expect({ + bare: codecOf('bare'), + named: codecOf('named'), + positional: codecOf('positional'), + }).toEqual({ + bare: 'fixture/wide-date@1', + named: 'fixture/narrow-date@1', + positional: 'fixture/wide-text@1', + }); + }); + + it('follows a type alias to the call it is written as', () => { + expect(codecOf('aliased')).toBe('fixture/wide-text@1'); + }); + + it('is unknown for a combination the choice refuses', () => { + expect(codecOf('refused')).toBeUndefined(); }); }); }); diff --git a/packages/1-framework/2-authoring/psl-parser/test/binder.mixin.test.ts b/packages/1-framework/2-authoring/psl-parser/test/binder.mixin.test.ts index f5703c26f75c..0bf1a1655285 100644 --- a/packages/1-framework/2-authoring/psl-parser/test/binder.mixin.test.ts +++ b/packages/1-framework/2-authoring/psl-parser/test/binder.mixin.test.ts @@ -602,7 +602,7 @@ describe('createBinder — binding a mixin body once', () => { ); expect(diagnostics).toEqual([]); - expect(fieldContexts.map((ctx) => ctx.typeConstructor?.name)).toEqual([ + expect(fieldContexts.map((ctx) => ctx.typeConstructor?.symbol.name)).toEqual([ 'Int', 'Int', undefined, diff --git a/packages/1-framework/2-authoring/psl-parser/test/type-aliases.test.ts b/packages/1-framework/2-authoring/psl-parser/test/type-aliases.test.ts index 29b94653d9e1..8a77cd91981b 100644 --- a/packages/1-framework/2-authoring/psl-parser/test/type-aliases.test.ts +++ b/packages/1-framework/2-authoring/psl-parser/test/type-aliases.test.ts @@ -365,7 +365,7 @@ model Post { codecId: context?.typeConstructor === undefined ? undefined - : contributedTypeCodecId(context.typeConstructor.descriptor), + : contributedTypeCodecId(context.typeConstructor), }).toEqual({ alias: 'Slug', untyped: undefined, diff --git a/packages/1-framework/3-tooling/language-server/src/attribute-spec-resolution.ts b/packages/1-framework/3-tooling/language-server/src/attribute-spec-resolution.ts index dfc482863c13..f6e82a5a90d9 100644 --- a/packages/1-framework/3-tooling/language-server/src/attribute-spec-resolution.ts +++ b/packages/1-framework/3-tooling/language-server/src/attribute-spec-resolution.ts @@ -193,7 +193,11 @@ export function attributeSpecResolver( }; const node = typeReferenceNode(field); const typeResolution = node === undefined ? undefined : source.binder.symbolForNode(node); - const typeConstructor = contributedTypeOf(typeResolution, source.binder); + const typeConstructor = contributedTypeOf( + typeResolution, + source.binder, + field.typeConstructor, + ); return (name) => specs.field[name]?.({ ...specContext, field, typeResolution, typeConstructor }); } diff --git a/packages/1-framework/3-tooling/language-server/src/completion-scope.ts b/packages/1-framework/3-tooling/language-server/src/completion-scope.ts index 0789ead1b79d..fe9ef4c0eb0b 100644 --- a/packages/1-framework/3-tooling/language-server/src/completion-scope.ts +++ b/packages/1-framework/3-tooling/language-server/src/completion-scope.ts @@ -85,9 +85,11 @@ export function scopeCompletionItems( break; case 'contributedType': { const descriptor = resolution.symbol.descriptor; - if (descriptor.kind === 'typeAlias') { + if (descriptor.kind === 'typeAlias' || descriptor.kind === 'typeChoice') { item.kind = CompletionItemKind.Class; - item.detail = descriptor.documentation || 'Type alias'; + item.detail = + descriptor.documentation || + (descriptor.kind === 'typeAlias' ? 'Type alias' : 'Configured scalar type'); break; } const callable = diff --git a/packages/2-mongo-family/2-authoring/contract-psl/src/interpreter.ts b/packages/2-mongo-family/2-authoring/contract-psl/src/interpreter.ts index 16dcdc8bd1dc..8662000ec7b5 100644 --- a/packages/2-mongo-family/2-authoring/contract-psl/src/interpreter.ts +++ b/packages/2-mongo-family/2-authoring/contract-psl/src/interpreter.ts @@ -123,6 +123,7 @@ import { type ResolvedTypeAlias, type ResolvedTypeAliases, resolveMongoTypeAliases, + scalarTypeOfChoice, scalarTypeOfConstructor, } from './type-aliases'; @@ -296,7 +297,11 @@ function resolvePhysicalNames(input: { ...specContext, field, typeResolution: fieldTypeResolution(field, binder), - typeConstructor: contributedTypeOf(fieldTypeResolution(field, binder), binder), + typeConstructor: contributedTypeOf( + fieldTypeResolution(field, binder), + binder, + field.typeConstructor, + ), }), model, field, @@ -349,11 +354,15 @@ function isStringTyped( stringCodecId: string | undefined, binder: Binder, ): boolean { - const scalar = contributedTypeOf(fieldTypeResolution(field, binder), binder); + const scalar = contributedTypeOf( + fieldTypeResolution(field, binder), + binder, + field.typeConstructor, + ); return ( stringCodecId !== undefined && - scalar?.descriptor.kind === 'typeConstructor' && - scalar.descriptor.output.codecId === stringCodecId + scalar?.symbol.descriptor.kind === 'typeConstructor' && + scalar.symbol.descriptor.output.codecId === stringCodecId ); } @@ -1004,7 +1013,11 @@ function collectIndexes( ...specContext, field, typeResolution: fieldTypeResolution(field, binder), - typeConstructor: contributedTypeOf(fieldTypeResolution(field, binder), binder), + typeConstructor: contributedTypeOf( + fieldTypeResolution(field, binder), + binder, + field.typeConstructor, + ), }), model: pslModel, field, @@ -1413,6 +1426,24 @@ function resolveNonRelationField( ...ifDefined('executionDefaults', preset.executionDefaults), }; } + if (descriptor.kind === 'typeChoice') { + const type = scalarTypeOfChoice({ symbol: resolution.symbol, call }, descriptor); + if (!type.ok) { + presetContext.diagnostics.push({ + code: 'PSL_INVALID_ATTRIBUTE_ARGUMENT', + message: `${entityLabel} type "${path.join('.')}": ${type.failure}`, + ...source.at(call?.span ?? field.span), + }); + return undefined; + } + return { + field: { + type: type.value, + nullable: field.optional, + many: field.list ? { elementNullable: field.elementOptional } : false, + }, + }; + } if (call === undefined && !isBareTypeConstructor(descriptor)) { reportTypeConstructorNotCalled({ entityLabel, @@ -1734,7 +1765,11 @@ export function interpretPslDocumentToMongoContract( ...specContext, field, typeResolution: fieldTypeResolution(field, binder), - typeConstructor: contributedTypeOf(fieldTypeResolution(field, binder), binder), + typeConstructor: contributedTypeOf( + fieldTypeResolution(field, binder), + binder, + field.typeConstructor, + ), }), model: pslModel, field, @@ -1849,7 +1884,7 @@ export function interpretPslDocumentToMongoContract( ...specContext, field, typeResolution: resolution, - typeConstructor: contributedTypeOf(resolution, binder), + typeConstructor: contributedTypeOf(resolution, binder, field.typeConstructor), }, generatorFunctions, sources, @@ -1881,7 +1916,11 @@ export function interpretPslDocumentToMongoContract( ...specContext, field, typeResolution: fieldTypeResolution(field, binder), - typeConstructor: contributedTypeOf(fieldTypeResolution(field, binder), binder), + typeConstructor: contributedTypeOf( + fieldTypeResolution(field, binder), + binder, + field.typeConstructor, + ), }), model: pslModel, field, diff --git a/packages/2-mongo-family/2-authoring/contract-psl/src/type-aliases.ts b/packages/2-mongo-family/2-authoring/contract-psl/src/type-aliases.ts index 56455d72d6d1..1ea0db5e8671 100644 --- a/packages/2-mongo-family/2-authoring/contract-psl/src/type-aliases.ts +++ b/packages/2-mongo-family/2-authoring/contract-psl/src/type-aliases.ts @@ -5,6 +5,7 @@ import type { ValueSetRef, } from '@internal/contract/types'; import { + type AuthoringTypeChoiceDescriptor, type AuthoringTypeConstructorDescriptor, type AuthoringTypeNamespace, type DataTypeSupport, @@ -17,6 +18,8 @@ import type { ControlMutationDefaultRegistry } from '@internal/framework-compone import { type Binder, type ContributedTypeAlias, + type ContributedTypeUse, + chosenTypeConstructor, contributedTypeAliasOf, diagnosticSource, interpretAttribute, @@ -35,10 +38,20 @@ import { } from '@internal/psl-parser'; import type { FieldAttributeAst, PslSources } from '@internal/psl-parser/syntax'; import { ifDefined } from '@internal/utils/defined'; +import { ok, type Result } from '@internal/utils/result'; import { unloweredAttributeHint } from './mongo-attribute-specs'; type ScalarFieldType = Extract; +/** The field type a use of a type choice gives: the type of the constructor its call chooses, or why it chooses none. */ +export function scalarTypeOfChoice( + use: ContributedTypeUse, + descriptor: AuthoringTypeChoiceDescriptor, +): Result { + const chosen = chosenTypeConstructor(use, descriptor); + return chosen.ok ? ok(scalarTypeOfConstructor(chosen.value, use.call)) : chosen; +} + /** The field type a type constructor gives, keeping a bare constructor's fixed type params. */ export function scalarTypeOfConstructor( descriptor: AuthoringTypeConstructorDescriptor, @@ -145,6 +158,16 @@ function aliasType( if (base.kind === 'typeConstructor') { return { type: scalarTypeOfConstructor(base.descriptor, base.call), valueSet: undefined }; } + if (base.kind === 'typeChoice') { + const type = scalarTypeOfChoice(base, base.descriptor); + if (type.ok) return { type: type.value, valueSet: undefined }; + input.diagnostics.push({ + code: 'PSL_INVALID_ATTRIBUTE_ARGUMENT', + message: `Type alias "${symbol.name}" type "${base.symbol.path.join('.')}": ${type.failure}`, + ...diagnosticSource(input.sources, symbol.node.syntax).at(base.call?.span ?? symbol.span), + }); + return undefined; + } if (base.symbol.keyword !== 'enum') { input.diagnostics.push({ code: 'PSL_UNSUPPORTED_NAMED_TYPE_BASE', diff --git a/packages/2-sql/2-authoring/contract-psl/src/psl-column-resolution.ts b/packages/2-sql/2-authoring/contract-psl/src/psl-column-resolution.ts index df69050809ae..bedad382ab5e 100644 --- a/packages/2-sql/2-authoring/contract-psl/src/psl-column-resolution.ts +++ b/packages/2-sql/2-authoring/contract-psl/src/psl-column-resolution.ts @@ -12,6 +12,7 @@ import type { AuthoringEntityTypeNamespace, AuthoringFieldPresetDescriptor, AuthoringStorageTypeTemplate, + AuthoringTypeChoiceDescriptor, AuthoringTypeConstructorDescriptor, ScalarTypeConstructorOutput, } from '@internal/framework-components/authoring'; @@ -20,7 +21,9 @@ import { instantiateAuthoringTypeAlias, instantiateAuthoringTypeConstructor, isAuthoringEntityTypeDescriptor, + isAuthoringTypeChoiceOption, readWrittenValue, + resolveAuthoringTypeChoice, validateAuthoringHelperArguments, validateAuthoringTypeParams, type WrittenScalar, @@ -40,6 +43,7 @@ import type { ContributedPslDiagnosticCode } from '@internal/framework-component import type { Binder, BlockSymbol, + ContributedTypeSymbol, FieldSymbol, ModelSymbol, NamedTypeSymbol, @@ -138,6 +142,8 @@ export function checkPslTypeParams(input: { readonly codecLookup: CodecLookupWithDescriptors; readonly diagnostics: PslDiagnosticCollector; readonly source: DiagnosticSource; + /** The index in `args` of the argument at `index` of the constructor whose parameters are checked, when the call is written with other arguments than the constructor's. */ + readonly callArgumentIndex?: (index: number) => number | undefined; }): boolean { const helperPath = input.call.path.join('.'); try { @@ -153,12 +159,26 @@ export function checkPslTypeParams(input: { input.diagnostics.push({ code: 'PSL_INVALID_ATTRIBUTE_ARGUMENT', message: `${input.subject} ${message}`, - ...input.source.at(argumentSpan(input.call, input.args, argumentIndexOf(error))), + ...input.source.at( + argumentSpan( + input.call, + input.args, + callIndex(argumentIndexOf(error), input.callArgumentIndex), + ), + ), }); return false; } } +function callIndex( + index: number | undefined, + callArgumentIndex: ((index: number) => number | undefined) | undefined, +): number | undefined { + if (index === undefined || callArgumentIndex === undefined) return index; + return callArgumentIndex(index); +} + function argumentIndexOf(error: unknown): number | undefined { if (typeof error !== 'object' || error === null || !('details' in error)) return undefined; const { details } = error; @@ -247,6 +267,77 @@ export function instantiatePslTypeConstructor(input: { return passed ? output : undefined; } +/** + * The column a field or type alias typed by the type choice `symbol` gets: its call's arguments, read as the choice declares them, choose a type constructor, which is instantiated with the arguments the choice passes on. A refusal is reported at the argument that makes it. `span` is where a bare use is written. + */ +export function instantiatePslTypeChoice(input: { + readonly symbol: ContributedTypeSymbol; + readonly descriptor: AuthoringTypeChoiceDescriptor; + readonly call: ResolvedTypeConstructorCall | undefined; + readonly span: PslSpan; + readonly codecLookup: CodecLookupWithDescriptors; + readonly diagnostics: PslDiagnosticCollector; + readonly source: DiagnosticSource; + readonly entityLabel: string; + readonly dataTypes: DataTypeSupport; +}): ScalarTypeConstructorOutput | undefined { + const { descriptor, symbol } = input; + const helperPath = symbol.path.join('.'); + const call = input.call ?? { path: symbol.path, args: [], span: input.span }; + const subject = `${input.entityLabel} type "${helperPath}"`; + const args = mapPslHelperArgs({ + args: call.args, + descriptors: descriptor.args, + helperLabel: `type "${helperPath}"`, + span: call.span, + diagnostics: input.diagnostics, + source: input.source, + entityLabel: input.entityLabel, + dataTypes: input.dataTypes, + }); + if (!args) return undefined; + + let chosen: ReturnType; + try { + chosen = resolveAuthoringTypeChoice({ + helperPath, + descriptor, + args, + constructorNamed: (name) => symbol.choiceTypes?.get(name), + }); + } catch (error) { + if (isInternalError(error)) throw error; + input.diagnostics.push({ + code: 'PSL_INVALID_ATTRIBUTE_ARGUMENT', + message: `${subject}: ${error instanceof Error ? error.message : String(error)}`, + ...input.source.at(argumentSpan(call, descriptor.args, argumentIndexOf(error))), + }); + return undefined; + } + + const output = instantiateAuthoringTypeConstructor(chosen.descriptor, chosen.args); + const passedOn = (chosen.descriptor.args ?? []).map((parameter) => + descriptor.args.findIndex( + (argument) => !isAuthoringTypeChoiceOption(argument) && argument.name === parameter.name, + ), + ); + const passed = checkPslTypeParams({ + call, + subject, + args: descriptor.args, + template: chosen.descriptor.output, + typeParams: output.typeParams, + codecLookup: input.codecLookup, + diagnostics: input.diagnostics, + source: input.source, + callArgumentIndex: (index) => { + const choiceIndex = passedOn[index]; + return choiceIndex === undefined || choiceIndex < 0 ? undefined : choiceIndex; + }, + }); + return passed ? output : undefined; +} + /** * Result of a codec descriptor's `columnFromEntity` authoring hook — the * per-column params derived from the entity a type constructor's @@ -571,6 +662,20 @@ export function resolveFieldTypeDescriptor( } return { ok: true, descriptor: instantiateAuthoringTypeAlias(descriptor).type }; } + if (descriptor.kind === 'typeChoice') { + const instantiated = instantiatePslTypeChoice({ + symbol: resolution.symbol, + descriptor, + call, + span: field.span, + codecLookup: input.codecLookup, + diagnostics, + source, + entityLabel, + dataTypes: input.dataTypes, + }); + return instantiated === undefined ? NOT_RESOLVED : { ok: true, descriptor: instantiated }; + } if (descriptor.kind === 'fieldPreset') { if (call === undefined) { reportPresetNotCalled({ diff --git a/packages/2-sql/2-authoring/contract-psl/src/psl-named-type-resolution.ts b/packages/2-sql/2-authoring/contract-psl/src/psl-named-type-resolution.ts index 84d40e7d80c4..bd620fe92e12 100644 --- a/packages/2-sql/2-authoring/contract-psl/src/psl-named-type-resolution.ts +++ b/packages/2-sql/2-authoring/contract-psl/src/psl-named-type-resolution.ts @@ -35,6 +35,7 @@ import type { import { formatDbAttributeMigrationMessage } from './psl-attribute-parsing'; import { type ColumnDescriptor, + instantiatePslTypeChoice, instantiatePslTypeConstructor, lowerEnumDefault, lowerTypeDefault, @@ -91,6 +92,19 @@ function aliasDescriptor( ): ColumnDescriptor | undefined { const { base, symbol } = alias; const source = diagnosticSource(input.sources, symbol.node.syntax); + if (base.kind === 'typeChoice') { + return instantiatePslTypeChoice({ + symbol: base.symbol, + descriptor: base.descriptor, + call: base.call, + span: symbol.span, + codecLookup: input.codecLookup, + diagnostics: input.diagnostics, + source, + entityLabel: `Type alias "${symbol.name}"`, + dataTypes: input.dataTypes, + }); + } if (base.kind === 'typeConstructor') { if (base.call === undefined) return instantiateAuthoringTypeConstructor(base.descriptor, []); return instantiatePslTypeConstructor({ diff --git a/packages/2-sql/2-authoring/contract-psl/src/sql-attribute-specs.ts b/packages/2-sql/2-authoring/contract-psl/src/sql-attribute-specs.ts index 685a672e064e..718e720d3fa5 100644 --- a/packages/2-sql/2-authoring/contract-psl/src/sql-attribute-specs.ts +++ b/packages/2-sql/2-authoring/contract-psl/src/sql-attribute-specs.ts @@ -298,10 +298,9 @@ export function defaultFunctionCall( /** The data type of the column a field declares, when its type, or the named type it is declared with, is a constructor or preset the stack knows. */ function receivingDataType(ctx: TypeAttributeSpecContext): string | undefined { - const typeConstructor = ctx.typeConstructor; - return typeConstructor === undefined - ? undefined - : ctx.dataTypes.dataTypeOfCodec(contributedTypeCodecId(typeConstructor.descriptor)); + const codecId = + ctx.typeConstructor === undefined ? undefined : contributedTypeCodecId(ctx.typeConstructor); + return codecId === undefined ? undefined : ctx.dataTypes.dataTypeOfCodec(codecId); } /** Whether a value of `type` can be written for a column of `receiving`: it is that type, or the column's type casts from it. An unknown column takes every type. */ @@ -876,7 +875,7 @@ export function fieldSpecContext(input: { model: input.model, field: input.field, typeResolution, - typeConstructor: contributedTypeOf(typeResolution, input.binder), + typeConstructor: contributedTypeOf(typeResolution, input.binder, input.field.typeConstructor), defaultFunctionRegistry: input.defaultFunctionRegistry, generatorFunctions: input.generatorFunctions, dataTypes: input.dataTypes, diff --git a/packages/2-sql/2-authoring/contract-psl/test/interpreter.type-choices.test.ts b/packages/2-sql/2-authoring/contract-psl/test/interpreter.type-choices.test.ts new file mode 100644 index 000000000000..b5846c07d44c --- /dev/null +++ b/packages/2-sql/2-authoring/contract-psl/test/interpreter.type-choices.test.ts @@ -0,0 +1,261 @@ +import type { + AuthoringContributions, + AuthoringTypeNamespace, +} from '@internal/framework-components/authoring'; +import { describe, expect, it } from 'vitest'; +import { postgresNativeScalarTypeDescriptors, postgresScalarAuthoringTypes } from './fixtures'; +import { sqlStorageFromSuccessfulSqlInterpretation } from './interpret-sql-contract-storage'; +import { + builtinControlMutationDefaults, + interpretPostgresSchema, +} from './interpreter-defaults-support'; +import { unboundTables } from './unbound-tables'; + +const precision = { + kind: 'value', + dataType: 'pg/int4', + name: 'precision', + optional: true, +} as const; + +const types = { + ...postgresScalarAuthoringTypes, + TimestampString: { + kind: 'typeConstructor', + args: [precision], + output: { + codecId: 'pg/timestamp-string@1', + typeParams: { precision: { kind: 'arg', index: 0 } }, + }, + }, + DateTemporal: { kind: 'typeConstructor', output: { codecId: 'pg/date-temporal@1' } }, + DateString: { kind: 'typeConstructor', output: { codecId: 'pg/date-string@1' } }, + Moment: { + kind: 'typeChoice', + args: [ + { + kind: 'option', + name: 'value', + values: ['temporal', 'string'], + default: 'temporal', + optional: true, + }, + { kind: 'option', name: 'column', values: ['tz', 'local'], default: 'tz', optional: true }, + precision, + ], + choices: [ + { + when: { value: 'temporal', column: 'tz' }, + type: 'TimestamptzTemporal', + defaults: { precision: 3 }, + }, + { when: { value: 'temporal', column: 'local' }, type: 'TimestampTemporal' }, + { when: { value: 'string', column: 'local' }, type: 'TimestampString' }, + ], + }, + Day: { + kind: 'typeChoice', + args: [ + { + kind: 'option', + name: 'value', + values: ['string', 'temporal'], + default: 'string', + optional: true, + }, + precision, + ], + choices: [ + { when: { value: 'string' }, type: 'DateString' }, + { when: { value: 'temporal' }, type: 'DateTemporal' }, + ], + }, + Label: { + kind: 'typeChoice', + args: [ + { + kind: 'option', + name: 'value', + values: ['text', 'number'], + default: 'text', + optional: true, + }, + ], + choices: [ + { when: { value: 'text' }, type: 'String' }, + { when: { value: 'number' }, type: 'Int' }, + ], + }, +} satisfies AuthoringTypeNamespace; + +const authoringContributions = { + entityTypes: {}, + field: {}, + pslBlockDescriptors: {}, + modelAttributes: {}, + type: types, +} satisfies AuthoringContributions; + +function interpret(schema: string) { + return interpretPostgresSchema(schema, { + scalarColumnDescriptors: postgresNativeScalarTypeDescriptors, + controlMutationDefaults: builtinControlMutationDefaults, + authoringContributions, + }); +} + +function lowered(schema: string) { + const result = interpret(schema); + if (!result.ok) throw new Error(JSON.stringify(result.failure.diagnostics, null, 2)); + return { + tables: unboundTables(sqlStorageFromSuccessfulSqlInterpretation(result.value)), + defaults: result.value.execution?.mutations.defaults ?? [], + }; +} + +function diagnostics(schema: string) { + const result = interpret(schema); + if (result.ok) throw new Error('expected the schema to be refused'); + return result.failure.diagnostics.map(({ code, message, span }) => ({ + code, + message, + columns: [span?.start.column, span?.end.column], + })); +} + +const model = (...fields: readonly string[]) => + ['model T {', ' id Int @id', ...fields.map((field) => ` ${field}`), '}'].join('\n'); + +describe('a type choice in a field type', () => { + it('emits what writing the type it chooses emits, bare, named and positional', () => { + expect( + lowered( + model( + 'bare Moment', + 'named Moment(value: string, column: local)', + 'positional Moment(temporal, local, 6)', + 'day Day(value: temporal)', + 'list Moment[]', + 'maybe Moment?', + ), + ), + ).toEqual( + lowered( + model( + 'bare TimestamptzTemporal(3)', + 'named TimestampString', + 'positional TimestampTemporal(6)', + 'day DateTemporal', + 'list TimestamptzTemporal(3)[]', + 'maybe TimestamptzTemporal(3)?', + ), + ), + ); + }); + + it('passes a written precision over the row default', () => { + expect(lowered(model('at Moment(precision: 6)'))).toEqual( + lowered(model('at TimestamptzTemporal(6)')), + ); + }); + + it('refuses an unlisted combination at the argument that makes it invalid', () => { + const line = 'at Moment(value: string, column: tz)'; + const start = ' '.length + line.indexOf('column'); + expect(diagnostics(model(line))).toEqual([ + { + code: 'PSL_INVALID_ATTRIBUTE_ARGUMENT', + message: + 'Field "T.at" type "Moment": Argument "column" of Moment cannot be tz when value is string; write one of: local.', + columns: [start + 1, start + 1 + 'column: tz'.length], + }, + ]); + }); + + it('refuses a value the option does not list, naming the values it does', () => { + expect(diagnostics(model('at Day(value: jsDate)'))).toMatchObject([ + { + code: 'PSL_INVALID_ATTRIBUTE_ARGUMENT', + message: + 'Field "T.at" type "Day": Argument "value" of Day is jsDate; write one of: string, temporal.', + }, + ]); + }); + + it('refuses a passed-on argument the chosen type does not take', () => { + expect(diagnostics(model('at Day(precision: 3)'))).toMatchObject([ + { + code: 'PSL_INVALID_ATTRIBUTE_ARGUMENT', + message: + 'Field "T.at" type "Day": Argument "precision" of Day does not apply to DateString, the type that value: string chooses.', + }, + ]); + }); + + it('refuses a precision the chosen type’s codec refuses, at the precision argument', () => { + expect(diagnostics(model('at Moment(precision: 7)'))).toMatchObject([ + { code: 'PSL_INVALID_ATTRIBUTE_ARGUMENT' }, + ]); + }); +}); + +describe('a type choice behind a types {} alias', () => { + it('emits what the type it chooses emits', () => { + expect( + lowered(`types { + Stamp = Moment(value: string, column: local) + Plain = Moment +} +${model('stamp Stamp', 'plain Plain')}`), + ).toEqual(lowered(model('stamp TimestampString', 'plain TimestamptzTemporal(3)'))); + }); +}); + +describe('attributes on a field typed by a type choice', () => { + it('admit a @default by the codec the call chooses', () => { + expect(lowered(model('label Label @default("a")'))).toEqual( + lowered(model('label String @default("a")')), + ); + expect(diagnostics(model('label Label(value: number) @default("a")'))).toMatchObject([ + { code: 'PSL_VALUE_TYPE_INCOMPATIBLE' }, + ]); + }); + + it('admit a @default by the codec the alias’s call chooses', () => { + expect( + diagnostics(`types { + Count = Label(value: number) +} +${model('count Count @default("a")')}`), + ).toMatchObject([{ code: 'PSL_VALUE_TYPE_INCOMPATIBLE' }]); + }); + + it('resolve @onCreate and @onUpdate generators for the codec the call chooses', () => { + expect( + lowered( + model( + 'a Moment @onCreate(now()) @onUpdate(now())', + 'b Moment(value: string, column: local) @onCreate(now())', + 'c Day @onCreate(now())', + ), + ), + ).toEqual( + lowered( + model( + 'a TimestamptzTemporal(3) @onCreate(now()) @onUpdate(now())', + 'b TimestampString @onCreate(now())', + 'c DateString @onCreate(now())', + ), + ), + ); + }); + + it('resolve generators through an alias written as a type choice', () => { + expect( + lowered(`types { + Stamp = Moment(value: string, column: local) @onCreate(now()) +} +${model('stamp Stamp')}`), + ).toEqual(lowered(model('stamp TimestampString @onCreate(now())'))); + }); +}); From 1352d64bc1fa6e176ba64b1bdca82662cbc86975 Mon Sep 17 00:00:00 2001 From: willbot Date: Thu, 22 Oct 2026 20:17:31 +0200 Subject: [PATCH 035/118] type.* helpers for type choices, typed by the row their arguments choose Each row of a type choice is one overload of its helper, so a call is typed as the explicit constructor it chooses, and a combination with no row does not compile. Composing helpers refuses a choice that names no registered constructor. Co-Authored-By: Claude Opus 5.5 Signed-off-by: willbot Signed-off-by: Will Madden --- .../src/authoring-helper-runtime.ts | 30 +++ .../src/composed-authoring-helpers.ts | 180 +++++++++++++++++- .../test/helpers/type-choice-packs.ts | 93 +++++++++ .../test/type-choice-helpers.test-d.ts | 39 ++++ .../test/type-choice-helpers.test.ts | 62 ++++++ 5 files changed, 401 insertions(+), 3 deletions(-) create mode 100644 packages/2-sql/2-authoring/contract-ts/test/helpers/type-choice-packs.ts create mode 100644 packages/2-sql/2-authoring/contract-ts/test/type-choice-helpers.test-d.ts create mode 100644 packages/2-sql/2-authoring/contract-ts/test/type-choice-helpers.test.ts diff --git a/packages/2-sql/2-authoring/contract-ts/src/authoring-helper-runtime.ts b/packages/2-sql/2-authoring/contract-ts/src/authoring-helper-runtime.ts index 87aab63d92d2..b73bb247d0d1 100644 --- a/packages/2-sql/2-authoring/contract-ts/src/authoring-helper-runtime.ts +++ b/packages/2-sql/2-authoring/contract-ts/src/authoring-helper-runtime.ts @@ -12,8 +12,11 @@ import { instantiateAuthoringTypeAlias, instantiateAuthoringTypeConstructor, isAuthoringTypeAliasDescriptor, + isAuthoringTypeChoiceDescriptor, isAuthoringTypeConstructorDescriptor, readWrittenValueAs, + resolveAuthoringTypeChoice, + typeConstructorLookup, validateAuthoringHelperArguments, validateAuthoringTypeParams, } from '@internal/framework-components/authoring'; @@ -138,6 +141,33 @@ export function createTypeHelpersFromNamespace( continue; } + if (isAuthoringTypeChoiceDescriptor(value)) { + const helperPath = currentPath.join('.'); + const constructorNamed = typeConstructorLookup(namespace); + helpers[key] = (...written: readonly unknown[]): AuthoredStorageTypeInstance => { + validateAuthoringHelperArguments(helperPath, value.args, written); + const chosen = resolveAuthoringTypeChoice({ + helperPath, + descriptor: value, + args: readValueArguments(helperPath, value.args, written, lookups.dataTypes), + constructorNamed, + }); + const output = instantiateAuthoringTypeConstructor(chosen.descriptor, chosen.args); + validateAuthoringTypeParams( + helperPath, + chosen.descriptor.output, + output.typeParams, + lookups.codecLookup.descriptorFor(output.codecId)?.paramsSchema, + ); + return { + kind: CODEC_INSTANCE_KIND, + codecId: output.codecId, + typeParams: output.typeParams ?? {}, + }; + }; + continue; + } + if (isAuthoringTypeAliasDescriptor(value)) { const helperPath = currentPath.join('.'); helpers[key] = (...args: readonly unknown[]): TypeAliasColumnType => { diff --git a/packages/2-sql/2-authoring/contract-ts/src/composed-authoring-helpers.ts b/packages/2-sql/2-authoring/contract-ts/src/composed-authoring-helpers.ts index 0bd5e91043d4..229349acb614 100644 --- a/packages/2-sql/2-authoring/contract-ts/src/composed-authoring-helpers.ts +++ b/packages/2-sql/2-authoring/contract-ts/src/composed-authoring-helpers.ts @@ -1,4 +1,5 @@ import { + type ArgTypeFromDescriptor, type CanonicalArguments, type CodecTypeMap, composePackAuthoringNamespace, @@ -18,6 +19,9 @@ import type { AuthoringEntityTypeNamespace, AuthoringFieldNamespace, AuthoringTypeAliasDescriptor, + AuthoringTypeChoiceArgument, + AuthoringTypeChoiceDescriptor, + AuthoringTypeChoiceRow, AuthoringTypeConstructorDescriptor, AuthoringTypeNamespace, DataTypeComponent, @@ -25,6 +29,7 @@ import type { import { assembleDataTypeSupport, assertNoCrossRegistryCollisions, + assertTypeChoicesValid, } from '@internal/framework-components/authoring'; import type { CodecLookupWithDescriptors } from '@internal/framework-components/codec'; import { @@ -113,6 +118,166 @@ type TypeHelperFunction< ) => StorageTypeFromDescriptor> : () => StorageTypeFromDescriptor; +/** The value a call writes for each argument of a type choice, by the argument's name; `undefined` for one it leaves out. */ +type WrittenChoiceArguments< + Args extends readonly AuthoringTypeChoiceArgument[], + Params extends readonly unknown[], +> = Args extends readonly [ + infer Head extends AuthoringTypeChoiceArgument, + ...infer Tail extends readonly AuthoringTypeChoiceArgument[], +] + ? { + readonly [Name in Head['name']]: Params extends readonly [infer Written, ...unknown[]] + ? Written + : undefined; + } & WrittenChoiceArguments< + Tail, + Params extends readonly [unknown, ...infer Rest] ? Rest : readonly [] + > + : Record; + +type RowDefault = Row extends { + readonly defaults: infer Defaults; +} + ? Name extends keyof Defaults + ? Defaults[Name] + : undefined + : undefined; + +/** The arguments a type choice passes on to the constructor its row chooses, by that constructor's argument positions: the written value, or the row's default. */ +type PassedOnArguments< + Parameters extends readonly AuthoringArgumentDescriptor[], + Written extends Record, + Row extends AuthoringTypeChoiceRow, +> = { + readonly [Index in keyof Parameters]: Parameters[Index] extends { + readonly name: infer Name extends string; + } + ? Name extends keyof Written + ? Written[Name] extends undefined + ? RowDefault + : Written[Name] + : RowDefault + : undefined; +}; + +type ChosenStorageType< + Chosen, + Written extends Record, + Row extends AuthoringTypeChoiceRow, + ValueTypes extends DataTypeArgumentTypes, +> = Chosen extends AuthoringTypeConstructorDescriptor + ? Chosen extends { + readonly args: infer Parameters extends readonly AuthoringArgumentDescriptor[]; + } + ? StorageTypeFromDescriptor< + Chosen, + CanonicalArguments, ValueTypes> + > + : StorageTypeFromDescriptor + : never; + +type ParameterNames = Chosen extends { + readonly args: readonly (infer Parameter)[]; +} + ? Parameter extends { readonly name: infer Name extends string } + ? Name + : never + : never; + +type RowArgument< + Head extends AuthoringTypeChoiceArgument, + Row extends AuthoringTypeChoiceRow, + Chosen, + ValueTypes extends DataTypeArgumentTypes, +> = Head extends { readonly kind: 'option'; readonly default: infer Default } + ? Head['name'] extends keyof Row['when'] + ? Row['when'][Head['name']] | (Row['when'][Head['name']] extends Default ? undefined : never) + : never + : Head['name'] extends ParameterNames + ? ArgTypeFromDescriptor | undefined + : undefined; + +/** Whether a call must write `Head` to choose `Row`: an option whose value in the row is not its default. */ +type RowRequires< + Head extends AuthoringTypeChoiceArgument, + Row extends AuthoringTypeChoiceRow, +> = Head extends { readonly kind: 'option'; readonly default: infer Default } + ? Head['name'] extends keyof Row['when'] + ? Row['when'][Head['name']] extends Default + ? false + : true + : false + : false; + +type RowArguments< + Args extends readonly AuthoringTypeChoiceArgument[], + Row extends AuthoringTypeChoiceRow, + Chosen, + ValueTypes extends DataTypeArgumentTypes, +> = { + readonly [Index in keyof Args]: RowArgument; +}; + +type RowRequirements< + Args extends readonly AuthoringTypeChoiceArgument[], + Row extends AuthoringTypeChoiceRow, +> = { readonly [Index in keyof Args]: RowRequires }; + +/** The arguments as a tuple whose trailing elements, after the last one a call must write, are optional. */ +type TrailingOptional< + Elements extends readonly unknown[], + Requirements extends readonly boolean[], +> = Requirements extends readonly [...infer RestRequirements extends readonly boolean[], infer Last] + ? Last extends true + ? Elements + : Elements extends readonly [...infer RestElements, infer LastElement] + ? readonly [...TrailingOptional, LastElement?] + : readonly [] + : readonly []; + +/** What a call choosing `Row` may write for each argument: the row's value for an option, left out only when that is the option's default, and a value for an argument the chosen constructor takes. */ +type RowParameters< + Args extends readonly AuthoringTypeChoiceArgument[], + Row extends AuthoringTypeChoiceRow, + Chosen, + ValueTypes extends DataTypeArgumentTypes, +> = TrailingOptional, RowRequirements>; + +type RowHelperFunction< + Args extends readonly AuthoringTypeChoiceArgument[], + Row extends AuthoringTypeChoiceRow, + Chosen, + ValueTypes extends DataTypeArgumentTypes, +> = >( + ...args: Params +) => ChosenStorageType, Row, ValueTypes>; + +type RowOverloads< + Args extends readonly AuthoringTypeChoiceArgument[], + Rows extends readonly AuthoringTypeChoiceRow[], + Namespace, + ValueTypes extends DataTypeArgumentTypes, +> = Rows extends readonly [ + infer Row extends AuthoringTypeChoiceRow, + ...infer Rest extends readonly AuthoringTypeChoiceRow[], +] + ? RowHelperFunction< + Args, + Row, + Row['type'] extends keyof Namespace ? Namespace[Row['type']] : never, + ValueTypes + > & + RowOverloads + : unknown; + +/** A type choice's helper: one overload per row, so a call is typed by the row its option values choose, and a combination with no row does not compile. */ +type TypeChoiceHelperFunction< + Descriptor extends AuthoringTypeChoiceDescriptor, + Namespace, + ValueTypes extends DataTypeArgumentTypes, +> = RowOverloads; + type AliasGeneratorCalls = (Output extends { readonly onCreate: GeneratorFunctionCall } ? { readonly onCreate: GeneratorFunctionCall } : unknown) & @@ -139,9 +304,11 @@ type TypeHelpersFromNamespace : Namespace[K] extends AuthoringTypeAliasDescriptor ? TypeAliasHelperFunction - : Namespace[K] extends Record - ? TypeHelpersFromNamespace - : never; + : Namespace[K] extends AuthoringTypeChoiceDescriptor + ? TypeChoiceHelperFunction + : Namespace[K] extends Record + ? TypeHelpersFromNamespace + : never; }; type MergeExtensionDataTypeArgumentTypes = @@ -356,6 +523,13 @@ export function createComposedAuthoringHelpers< // `createControlStack`, the TS DSL composes here. Both seams need the guard. assertNoCrossRegistryCollisions(typeNamespace, fieldNamespace, entityNamespace); assertNoBuiltInEntityCollisions(entityNamespace); + for (const component of components) { + assertTypeChoicesValid( + component.authoring?.type ?? {}, + component.id ?? '', + typeNamespace, + ); + } const { support } = assembleDataTypeSupport( components, (codecId) => options.codecLookup.descriptorFor(codecId)?.dataType, diff --git a/packages/2-sql/2-authoring/contract-ts/test/helpers/type-choice-packs.ts b/packages/2-sql/2-authoring/contract-ts/test/helpers/type-choice-packs.ts new file mode 100644 index 000000000000..fee661734e9f --- /dev/null +++ b/packages/2-sql/2-authoring/contract-ts/test/helpers/type-choice-packs.ts @@ -0,0 +1,93 @@ +import type { FamilyPackRef, TargetPackRef } from '@internal/framework-components/components'; +import { + fixtureDataTypeEntries, + fixtureDataTypes, +} from '../../../contract-psl/test/fixture-data-types'; +import { + familyPack as generatorFamilyPack, + targetPack as generatorTargetPack, +} from './generator-function-packs'; + +const familyPackBase = { + ...generatorFamilyPack, + dataTypes: fixtureDataTypes, + authoring: { dataTypes: fixtureDataTypeEntries }, +} as const satisfies FamilyPackRef<'sql'>; + +export const familyPack: typeof familyPackBase & { + readonly __dataTypeArguments?: { + readonly 'pg/int4': { readonly argument: number; readonly parameter: number }; + }; +} = familyPackBase; + +const precision = { + kind: 'value', + dataType: 'pg/int4', + name: 'precision', + optional: true, +} as const; + +const withPrecision = (codecId: CodecId) => + ({ + kind: 'typeConstructor', + args: [precision], + output: { codecId, typeParams: { precision: { kind: 'arg', index: 0 } } }, + }) as const; + +export const targetPack = { + ...generatorTargetPack, + authoring: { + type: { + TimestamptzTemporal: withPrecision('pg/timestamptz-temporal@1'), + TimestampTemporal: withPrecision('pg/timestamp-temporal@1'), + TimestampString: withPrecision('pg/timestamp-string@1'), + DateString: { kind: 'typeConstructor', output: { codecId: 'pg/date-string@1' } }, + DateTemporal: { kind: 'typeConstructor', output: { codecId: 'pg/date-temporal@1' } }, + Moment: { + kind: 'typeChoice', + args: [ + { + kind: 'option', + name: 'value', + values: ['temporal', 'string'], + default: 'temporal', + optional: true, + }, + { + kind: 'option', + name: 'column', + values: ['tz', 'local'], + default: 'tz', + optional: true, + }, + precision, + ], + choices: [ + { + when: { value: 'temporal', column: 'tz' }, + type: 'TimestamptzTemporal', + defaults: { precision: 3 }, + }, + { when: { value: 'temporal', column: 'local' }, type: 'TimestampTemporal' }, + { when: { value: 'string', column: 'local' }, type: 'TimestampString' }, + ], + }, + Day: { + kind: 'typeChoice', + args: [ + { + kind: 'option', + name: 'value', + values: ['string', 'temporal'], + default: 'string', + optional: true, + }, + ], + choices: [ + { when: { value: 'string' }, type: 'DateString' }, + { when: { value: 'temporal' }, type: 'DateTemporal' }, + ], + }, + }, + }, +} as const satisfies TargetPackRef<'sql', 'postgres'>; diff --git a/packages/2-sql/2-authoring/contract-ts/test/type-choice-helpers.test-d.ts b/packages/2-sql/2-authoring/contract-ts/test/type-choice-helpers.test-d.ts new file mode 100644 index 000000000000..3c1c7ca2ae2b --- /dev/null +++ b/packages/2-sql/2-authoring/contract-ts/test/type-choice-helpers.test-d.ts @@ -0,0 +1,39 @@ +import { expectTypeOf, test } from 'vitest'; +import { testTypeLookups } from '../../../1-core/contract/test/test-type-lookups'; +import { createComposedAuthoringHelpers } from '../src/composed-authoring-helpers'; +import { familyPack, targetPack } from './helpers/type-choice-packs'; + +const { type } = createComposedAuthoringHelpers({ + ...testTypeLookups, + family: familyPack, + target: targetPack, +}); + +test('a bare call resolves to the default row, with its parameter defaults', () => { + expectTypeOf(type.Moment()).toEqualTypeOf(type.TimestamptzTemporal(3)); + expectTypeOf(type.Moment().codecId).toEqualTypeOf<'pg/timestamptz-temporal@1'>(); +}); + +test('a written precision replaces the row default', () => { + expectTypeOf(type.Moment('temporal', 'tz', 6)).toEqualTypeOf(type.TimestamptzTemporal(6)); +}); + +test('each row resolves to the type it names', () => { + expectTypeOf(type.Moment('temporal', 'local')).toEqualTypeOf(type.TimestampTemporal()); + expectTypeOf(type.Moment('temporal', 'local', 6)).toEqualTypeOf(type.TimestampTemporal(6)); + expectTypeOf(type.Moment('string', 'local')).toEqualTypeOf(type.TimestampString()); + expectTypeOf(type.Day()).toEqualTypeOf(type.DateString()); + expectTypeOf(type.Day('temporal')).toEqualTypeOf(type.DateTemporal()); +}); + +test('an option argument takes only the values it lists', () => { + // @ts-expect-error Moment's value is temporal or string + type.Moment('jsDate'); +}); + +test('an unlisted combination does not compile', () => { + // @ts-expect-error value: string chooses no type with the default column tz + type.Moment('string'); + // @ts-expect-error DateString takes no precision + type.Day('string', 3); +}); diff --git a/packages/2-sql/2-authoring/contract-ts/test/type-choice-helpers.test.ts b/packages/2-sql/2-authoring/contract-ts/test/type-choice-helpers.test.ts new file mode 100644 index 000000000000..b417587036ac --- /dev/null +++ b/packages/2-sql/2-authoring/contract-ts/test/type-choice-helpers.test.ts @@ -0,0 +1,62 @@ +import { describe, expect, it } from 'vitest'; +import { testTypeLookups } from '../../../1-core/contract/test/test-type-lookups'; +import { createComposedAuthoringHelpers } from '../src/composed-authoring-helpers'; +import { familyPack, targetPack } from './helpers/type-choice-packs'; + +const { type } = createComposedAuthoringHelpers({ + ...testTypeLookups, + family: familyPack, + target: targetPack, +}); + +describe('a type.* helper of a type choice', () => { + it('gives what the helper of the type it chooses gives, for each row', () => { + expect({ + bare: type.Moment(), + local: type.Moment('temporal', 'local', 6), + string: type.Moment('string', 'local'), + day: type.Day(), + temporalDay: type.Day('temporal'), + }).toEqual({ + bare: type.TimestamptzTemporal(3), + local: type.TimestampTemporal(6), + string: type.TimestampString(), + day: type.DateString(), + temporalDay: type.DateTemporal(), + }); + }); + + it('gives the explicit codec and parameters', () => { + expect(type.Moment()).toEqual({ + kind: 'codec-instance', + codecId: 'pg/timestamptz-temporal@1', + typeParams: { precision: 3 }, + }); + }); + + it('refuses an unlisted combination at the argument that makes it invalid', () => { + const untyped = type.Moment as (...args: readonly unknown[]) => unknown; + expect(() => untyped('string')).toThrow( + 'Argument "column" of Moment cannot be tz when value is string; write one of: local.', + ); + }); +}); + +describe('composing helpers for a pack with a type choice', () => { + it('refuses a choice naming a constructor no pack registers', () => { + const broken = { + ...targetPack, + authoring: { + type: { + Moment: { + ...targetPack.authoring.type.Moment, + choices: [{ when: { value: 'temporal', column: 'tz' }, type: 'Missing' }], + }, + }, + }, + } as const; + expect(() => + createComposedAuthoringHelpers({ ...testTypeLookups, family: familyPack, target: broken }), + ).toThrow(/Type choice "Moment" contributed by "postgres" chooses "Missing"/); + }); +}); From 7fb8b219b173c29b90569b2136438b629b00f8b3 Mon Sep 17 00:00:00 2001 From: willbot Date: Thu, 22 Oct 2026 20:23:44 +0200 Subject: [PATCH 036/118] PostgreSQL DateTime, Date, Time and Duration are convenience types Each is a type choice over the explicit date and time types: value and column choose which, with defaults that need no Temporal. DateTime, Date and Time no longer name the Temporal types; those are TimestamptzTemporal, DateTemporal and TimeTemporal. Co-Authored-By: Claude Opus 5.5 Signed-off-by: willbot Signed-off-by: Will Madden --- .../postgres/src/core/type-constructors.ts | 138 +++++++++++++++--- ...ting-column-types-without-temporal.test.ts | 12 +- .../postgres/test/type-constructors.test.ts | 27 ++-- .../convenience-types.integration.test.ts | 120 +++++++++++++++ 4 files changed, 260 insertions(+), 37 deletions(-) create mode 100644 test/integration/test/convenience-date-time-types/convenience-types.integration.test.ts diff --git a/packages/3-targets/3-targets/postgres/src/core/type-constructors.ts b/packages/3-targets/3-targets/postgres/src/core/type-constructors.ts index 0cda1602c106..0bcab774b183 100644 --- a/packages/3-targets/3-targets/postgres/src/core/type-constructors.ts +++ b/packages/3-targets/3-targets/postgres/src/core/type-constructors.ts @@ -52,12 +52,6 @@ export const postgresScalarAuthoringTypes = { documentation: 'An exact decimal value stored as PostgreSQL numeric.', output: { codecId: 'pg/numeric@1' }, }, - DateTime: { - kind: 'typeConstructor', - documentation: - 'An instant stored as PostgreSQL timestamptz and represented as Temporal.Instant.', - output: { codecId: 'pg/timestamptz-temporal@1' }, - }, Json: { kind: 'typeConstructor', inferred: true, @@ -143,15 +137,6 @@ export const postgresNativeAuthoringTypes = { typeParams: { precision: { kind: 'arg', index: 0 } }, }, }, - Time: { - kind: 'typeConstructor', - documentation: 'A time of day without a time zone, represented as Temporal.PlainTime.', - args: [{ kind: 'value', dataType: PG_SIZE_TYPE, name: 'precision', optional: true }], - output: { - codecId: 'pg/time-temporal@1', - typeParams: { precision: { kind: 'arg', index: 0 } }, - }, - }, Timetz: { kind: 'typeConstructor', inferred: true, @@ -191,11 +176,6 @@ export const postgresNativeAuthoringTypes = { documentation: 'A calendar date represented as Temporal.PlainDate.', output: { codecId: 'pg/date-temporal@1' }, }, - Date: { - kind: 'typeConstructor', - documentation: 'A calendar date represented as Temporal.PlainDate.', - output: { codecId: 'pg/date-temporal@1' }, - }, // The representation-explicit spellings. Same columns, same precision, same native types — the // only difference is that a read hands back PostgreSQL's own text instead of a `Temporal.*`, so a // value Temporal cannot express still round-trips. @@ -308,10 +288,128 @@ export const postgresNativeAuthoringTypes = { }, } as const satisfies AuthoringTypeNamespace; +const precision = { + kind: 'value', + dataType: PG_SIZE_TYPE, + name: 'precision', + optional: true, +} as const; + +/** + * The convenience date and time types. Each is a type choice: its `value` and `column` arguments choose the explicit type it becomes, and `precision` is passed on to that type. ADR 279. + */ +export const postgresConvenienceDateTimeTypes = { + DateTime: { + kind: 'typeChoice', + documentation: + 'A moment in time. `value` is what the application reads: jsDate (a JavaScript Date), temporal (Temporal.Instant) or string. `column` is timestamptz or timestamp, a timestamp read and written as UTC.', + args: [ + { + kind: 'option', + name: 'value', + values: ['jsDate', 'temporal', 'string'], + default: 'jsDate', + optional: true, + }, + { + kind: 'option', + name: 'column', + values: ['timestamptz', 'timestamp'], + default: 'timestamptz', + optional: true, + }, + precision, + ], + choices: [ + { + when: { value: 'jsDate', column: 'timestamptz' }, + type: 'TimestamptzJsDate', + defaults: { precision: 3 }, + }, + { when: { value: 'temporal', column: 'timestamptz' }, type: 'TimestamptzTemporal' }, + { when: { value: 'string', column: 'timestamptz' }, type: 'TimestamptzString' }, + { + when: { value: 'jsDate', column: 'timestamp' }, + type: 'TimestampJsDate', + defaults: { precision: 3 }, + }, + { when: { value: 'temporal', column: 'timestamp' }, type: 'TimestampInstant' }, + { when: { value: 'string', column: 'timestamp' }, type: 'TimestampString' }, + ], + }, + Date: { + kind: 'typeChoice', + documentation: + 'A calendar date. `value` is what the application reads: string, such as "2024-02-29", or temporal (Temporal.PlainDate).', + args: [ + { + kind: 'option', + name: 'value', + values: ['string', 'temporal'], + default: 'string', + optional: true, + }, + { kind: 'option', name: 'column', values: ['date'], default: 'date', optional: true }, + ], + choices: [ + { when: { value: 'string', column: 'date' }, type: 'DateString' }, + { when: { value: 'temporal', column: 'date' }, type: 'DateTemporal' }, + ], + }, + Time: { + kind: 'typeChoice', + documentation: + 'A time of day. `value` is what the application reads: string, such as "09:30:00", or temporal (Temporal.PlainTime). `column` is time, or timetz with value string.', + args: [ + { + kind: 'option', + name: 'value', + values: ['string', 'temporal'], + default: 'string', + optional: true, + }, + { + kind: 'option', + name: 'column', + values: ['time', 'timetz'], + default: 'time', + optional: true, + }, + precision, + ], + choices: [ + { when: { value: 'string', column: 'time' }, type: 'TimeString' }, + { when: { value: 'temporal', column: 'time' }, type: 'TimeTemporal' }, + { when: { value: 'string', column: 'timetz' }, type: 'Timetz' }, + ], + }, + Duration: { + kind: 'typeChoice', + documentation: + 'A length of time. `value` is what the application reads: string, an ISO 8601 duration such as "P1M", or parts, the object { months, days, micros }.', + args: [ + { + kind: 'option', + name: 'value', + values: ['string', 'parts'], + default: 'string', + optional: true, + }, + { kind: 'option', name: 'column', values: ['interval'], default: 'interval', optional: true }, + precision, + ], + choices: [ + { when: { value: 'string', column: 'interval' }, type: 'IntervalString' }, + { when: { value: 'parts', column: 'interval' }, type: 'IntervalParts' }, + ], + }, +} as const satisfies AuthoringTypeNamespace; + /** * The base scalars and native types as type constructors. The target contributes them, so PSL and the TypeScript builder's `type.*` see the same names. */ export const postgresScalarAndNativeTypeConstructors = { ...postgresNativeAuthoringTypes, ...postgresScalarAuthoringTypes, + ...postgresConvenienceDateTimeTypes, } as const satisfies AuthoringTypeNamespace; diff --git a/packages/3-targets/3-targets/postgres/test/existing-column-types-without-temporal.test.ts b/packages/3-targets/3-targets/postgres/test/existing-column-types-without-temporal.test.ts index 93eedb547989..0c97fb742584 100644 --- a/packages/3-targets/3-targets/postgres/test/existing-column-types-without-temporal.test.ts +++ b/packages/3-targets/3-targets/postgres/test/existing-column-types-without-temporal.test.ts @@ -20,11 +20,12 @@ const constructorNames = [ ]; function codecIdOf(name: string): string | undefined { - return Object.hasOwn(postgresScalarAndNativeTypeConstructors, name) + const descriptor = Object.hasOwn(postgresScalarAndNativeTypeConstructors, name) ? postgresScalarAndNativeTypeConstructors[ name as keyof typeof postgresScalarAndNativeTypeConstructors - ].output.codecId + ] : undefined; + return descriptor?.kind === 'typeConstructor' ? descriptor.output.codecId : undefined; } describe('the types contract infer and prisma7Schema write', () => { @@ -46,8 +47,11 @@ describe('the constructors marked inferred', () => { it('name no codec that needs Temporal', () => { expect( Object.entries(postgresScalarAndNativeTypeConstructors) - .filter(([, descriptor]) => 'inferred' in descriptor) - .map(([name, descriptor]) => ({ name, codecId: descriptor.output.codecId })) + .flatMap(([name, descriptor]) => + descriptor.kind === 'typeConstructor' && 'inferred' in descriptor + ? [{ name, codecId: descriptor.output.codecId }] + : [], + ) .filter(({ codecId }) => TEMPORAL_CODEC_IDS.includes(codecId)), ).toEqual([]); }); diff --git a/packages/3-targets/3-targets/postgres/test/type-constructors.test.ts b/packages/3-targets/3-targets/postgres/test/type-constructors.test.ts index 0f70d1958bbe..8542893aa8ac 100644 --- a/packages/3-targets/3-targets/postgres/test/type-constructors.test.ts +++ b/packages/3-targets/3-targets/postgres/test/type-constructors.test.ts @@ -219,7 +219,6 @@ describe('postgresScalarAuthoringTypes', () => { ['BigInt', 'pg/int8@1', 'int8'], ['Float', 'pg/float8@1', 'float8'], ['Decimal', 'pg/numeric@1', 'numeric'], - ['DateTime', 'pg/timestamptz-temporal@1', 'timestamptz'], ['Json', 'pg/json@1', 'json'], ['Jsonb', 'pg/jsonb@1', 'jsonb'], ['Bytes', 'pg/bytea@1', 'bytea'], @@ -260,14 +259,12 @@ describe('postgresNativeAuthoringTypes', () => { Numeric: { codecId: 'pg/numeric@1' }, TimestampTemporal: { codecId: 'pg/timestamp-temporal@1' }, TimestamptzTemporal: { codecId: 'pg/timestamptz-temporal@1' }, - Time: { codecId: 'pg/time-temporal@1' }, TimeTemporal: { codecId: 'pg/time-temporal@1' }, Timetz: { codecId: 'pg/timetz@1' }, Uuid: { codecId: 'pg/uuid@1' }, Inet: { codecId: 'pg/inet@1' }, SmallInt: { codecId: 'pg/int2@1' }, Real: { codecId: 'pg/float4@1' }, - Date: { codecId: 'pg/date-temporal@1' }, DateTemporal: { codecId: 'pg/date-temporal@1' }, DateString: { codecId: 'pg/date-string@1' }, TimestampString: { codecId: 'pg/timestamp-string@1' }, @@ -348,7 +345,7 @@ describe('postgresNativeAuthoringTypes', () => { ['TimestampTemporal', [-1]], ['TimestampTemporal', [7]], ['TimestamptzTemporal', [7]], - ['Time', [7]], + ['TimeTemporal', [7]], ['Timetz', [7]], ['TimestamptzJsDate', [7]], ['Bit', [0]], @@ -396,13 +393,19 @@ describe('postgresNativeAuthoringTypes', () => { } }); - it.each([ - ['DateTemporal', 'Date'], - ['TimeTemporal', 'Time'], - ] as const)('%s is the same type as %s', (name, sameAs) => { - expect(postgresNativeAuthoringTypes[name]).toEqual({ - ...postgresNativeAuthoringTypes[sameAs], - documentation: expect.stringMatching(/\S/), + it('gives DateTime, Date, Time and Duration to the convenience types, not to explicit types', () => { + expect( + Object.fromEntries( + ['DateTime', 'Date', 'Time', 'Duration'].map((name) => [ + name, + (everyPostgresConstructor as Record)[name]?.kind, + ]), + ), + ).toEqual({ + DateTime: 'typeChoice', + Date: 'typeChoice', + Time: 'typeChoice', + Duration: 'typeChoice', }); }); @@ -419,7 +422,6 @@ describe('postgresNativeAuthoringTypes', () => { typeParams: { precision: { kind: 'arg', index: 0 } }, }, }); - expect(postgresScalarAuthoringTypes.DateTime.output.codecId).toBe('pg/timestamptz-temporal@1'); expect(postgresNativeAuthoringTypes.TimestamptzTemporal.output.codecId).toBe( 'pg/timestamptz-temporal@1', ); @@ -430,7 +432,6 @@ describe('precision is a value of pg/int4, bounded by the data type', () => { it.each([ 'TimestampTemporal', 'TimestamptzTemporal', - 'Time', 'TimeTemporal', 'Timetz', 'TimestampString', diff --git a/test/integration/test/convenience-date-time-types/convenience-types.integration.test.ts b/test/integration/test/convenience-date-time-types/convenience-types.integration.test.ts new file mode 100644 index 000000000000..87c8e23cb0bc --- /dev/null +++ b/test/integration/test/convenience-date-time-types/convenience-types.integration.test.ts @@ -0,0 +1,120 @@ +import { describe, expect, it } from 'vitest'; +import { readPsl } from '../psl-print/print-and-read-back'; + +const model = (...fields: readonly string[]) => + [ + '// use prisma-8', + '', + 'model Order {', + ' id Int @id', + ...fields.map((f) => ` ${f}`), + '}', + '', + ].join('\n'); + +const rows: readonly (readonly [convenience: string, explicit: string])[] = [ + ['DateTime', 'TimestamptzJsDate(3)'], + ['DateTime(value: temporal)', 'TimestamptzTemporal'], + ['DateTime(value: string)', 'TimestamptzString'], + ['DateTime(column: timestamp)', 'TimestampJsDate(3)'], + ['DateTime(value: temporal, column: timestamp)', 'TimestampInstant'], + ['DateTime(value: string, column: timestamp)', 'TimestampString'], + ['DateTime(value: string, precision: 6)', 'TimestamptzString(6)'], + ['DateTime(precision: 6)', 'TimestamptzJsDate(6)'], + ['DateTime(temporal, timestamptz, 3)', 'TimestamptzTemporal(3)'], + ['Date', 'DateString'], + ['Date(value: temporal)', 'DateTemporal'], + ['Time', 'TimeString'], + ['Time(precision: 0)', 'TimeString(0)'], + ['Time(value: temporal)', 'TimeTemporal'], + ['Time(column: timetz)', 'Timetz'], + ['Duration', 'IntervalString'], + ['Duration(precision: 3)', 'IntervalString(3)'], + ['Duration(value: parts)', 'IntervalParts'], + ['DateTime[]', 'TimestamptzJsDate(3)[]'], + ['DateTime?', 'TimestamptzJsDate(3)?'], +]; + +describe('a convenience date and time type', () => { + it.each(rows)('%s emits the contract %s emits', async (convenience, explicit) => { + expect(await readPsl(model(`at ${convenience}`))).toEqual( + await readPsl(model(`at ${explicit}`)), + ); + }); + + it('chooses the generator now() writes for the type it becomes', async () => { + expect( + await readPsl( + model( + 'createdAt DateTime @onCreate(now())', + 'updatedAt DateTime @onCreate(now()) @onUpdate(now())', + 'day Date @onCreate(now())', + 'clock Time(precision: 0) @onCreate(now())', + 'instant DateTime(value: temporal) @onCreate(now())', + ), + ), + ).toEqual( + await readPsl( + model( + 'createdAt TimestamptzJsDate(3) @onCreate(now())', + 'updatedAt TimestamptzJsDate(3) @onCreate(now()) @onUpdate(now())', + 'day DateString @onCreate(now())', + 'clock TimeString(0) @onCreate(now())', + 'instant TimestamptzTemporal @onCreate(now())', + ), + ), + ); + }); + + it('reads a @default as the type it becomes, through a types {} alias too', async () => { + const convenience = `// use prisma-8 + +types { + Stamp = DateTime(column: timestamp) @default(timestamp\`2024-01-01 00:00:00\`) +} + +model Order { + id Int @id + placed Stamp + day Date @default(date\`2024-02-29\`) +} +`; + expect(await readPsl(convenience)).toEqual( + await readPsl( + model( + 'placed TimestampJsDate(3) @default(timestamp`2024-01-01 00:00:00`)', + 'day DateString @default(date`2024-02-29`)', + ), + ), + ); + }); + + it('refuses a default of another date and time type, naming the column’s', async () => { + await expect( + readPsl(model('at DateTime(column: timestamp) @default(timestamptz`2024-01-01T00:00:00Z`)')), + ).rejects.toThrow(/PSL_VALUE_TYPE_INCOMPATIBLE.*Expected timestamp`\.\.\.`/); + }); + + it.each([ + [ + 'Time(value: temporal, column: timetz)', + 'Argument \\"column\\" of Time cannot be timetz when value is temporal; write one of: time.', + ], + [ + 'Date(value: jsDate)', + 'Argument \\"value\\" of Date is jsDate; write one of: string, temporal.', + ], + [ + 'Duration(value: parts, column: date)', + 'Argument \\"column\\" of Duration is date; write one of: interval.', + ], + ])('refuses %s at the argument, naming the accepted choices', async (written, message) => { + await expect(readPsl(model(`at ${written}`))).rejects.toThrow(message); + }); + + it('refuses a precision the type it becomes does not take', async () => { + await expect(readPsl(model('at Date(precision: 3)'))).rejects.toThrow( + 'received unknown named argument \\"precision\\"', + ); + }); +}); From 8f7bbd622888b3c01d1d512db2c095da201e2300 Mon Sep 17 00:00:00 2001 From: willbot Date: Thu, 22 Oct 2026 20:28:57 +0200 Subject: [PATCH 037/118] Prisma 8 Postgres schemas write the explicit Temporal type names DateTime, Date and Time now choose a convenience type's default, so every schema that meant the Temporal types writes TimestamptzTemporal, DateTemporal and TimeTemporal. No contract changes. Co-Authored-By: Claude Opus 5.5 Signed-off-by: willbot Signed-off-by: Will Madden --- apps/telemetry-backend/src/prisma/contract.prisma | 2 +- .../src/prisma/contract.prisma | 6 +++--- .../app/20260422T0720_initial/contract.prisma | 6 +++--- .../contract.prisma | 6 +++--- .../contract.prisma | 8 ++++---- .../contract.prisma | 8 ++++---- .../contract.prisma | 8 ++++---- examples/prisma-8-demo/src/prisma/contract.prisma | 8 ++++---- .../react-router-demo/src/prisma/contract.prisma | 4 ++-- .../parity/callback-mode-scalars/schema.prisma | 2 +- .../authoring/parity/core-surface/schema.prisma | 2 +- .../parity/default-date-time-types/schema.prisma | 6 +++--- .../parity/default-sql-literal/schema.prisma | 4 ++-- .../parity/generator-on-update-time/schema.prisma | 2 +- .../authoring/parity/sql-expressions/schema.prisma | 2 +- .../side-by-side/postgres/contract.prisma | 2 +- .../contract-with-tags.prisma | 12 ++++++------ .../_fixture-before-canonical-form/contract.prisma | 12 ++++++------ .../aggregation/count/_fixture/contract.prisma | 2 +- .../_fixture/compound/contract.prisma | 2 +- .../data_types/bigint/_fixture/contract.prisma | 2 +- .../data_types/bool/_fixture/contract.prisma | 2 +- .../bytes/_fixture/scalars/contract.prisma | 2 +- .../data_types/datetime/_fixture/contract.prisma | 2 +- .../data_types/float/_fixture/contract.prisma | 2 +- .../data_types/int/_fixture/contract.prisma | 2 +- .../data_types/string/_fixture/contract.prisma | 2 +- .../_fixture/common/contract.prisma | 2 +- .../_fixture/lists/contract.prisma | 2 +- .../queries/distinct/_fixture/contract.prisma | 2 +- .../filters/bigint_filter/_fixture/contract.prisma | 2 +- .../filters/bytes_filter/_fixture/contract.prisma | 2 +- .../field_reference/_fixture/list/contract.prisma | 6 +++--- .../field_reference/_fixture/mixed/contract.prisma | 4 ++-- .../bigint_filter/_fixture/contract.prisma | 4 ++-- .../bytes_filter/_fixture/contract.prisma | 4 ++-- .../datetime_filter/_fixture/contract.prisma | 4 ++-- .../failure/_fixture/common/contract.prisma | 4 ++-- .../float_filter/_fixture/contract.prisma | 4 ++-- .../having_filter/_fixture/contract.prisma | 4 ++-- .../int_filter/_fixture/contract.prisma | 4 ++-- .../string_filter/_fixture/contract.prisma | 4 ++-- .../list_filters/_fixture/base/contract.prisma | 2 +- .../many_relation/_fixture/25103/contract.prisma | 4 ++-- .../blog-update/_fixture/contract.prisma | 14 +++++++------- .../create-default-date/_fixture/contract.prisma | 2 +- .../extended-where/_fixture/contract.prisma | 2 +- .../_fixture/contract.prisma | 2 +- .../issues-12572/_fixture/contract.prisma | 2 +- .../_fixture/contract.prisma | 2 +- .../_fixture/contract.prisma | 2 +- .../issues-23902/_fixture/contract.prisma | 2 +- .../_fixture/contract.prisma | 2 +- .../_fixture/contract.prisma | 2 +- .../_fixture/contract.prisma | 14 +++++++------- .../legacy-aggregations/_fixture/contract.prisma | 2 +- .../methods-count/_fixture/contract.prisma | 2 +- .../multiple-types/_fixture/contract.prisma | 2 +- .../fixtures/soft-delete/contract.prisma | 6 +++--- .../_fixture-attributes/contract.prisma | 8 ++++---- 60 files changed, 121 insertions(+), 121 deletions(-) diff --git a/apps/telemetry-backend/src/prisma/contract.prisma b/apps/telemetry-backend/src/prisma/contract.prisma index c1da9b437032..c90daf667202 100644 --- a/apps/telemetry-backend/src/prisma/contract.prisma +++ b/apps/telemetry-backend/src/prisma/contract.prisma @@ -2,7 +2,7 @@ model TelemetryEvent { id BigInt @id @default(autoincrement()) - ingestedAt DateTime @default(now()) + ingestedAt TimestamptzTemporal @default(now()) installationId String version String command String diff --git a/examples/prisma-8-cloudflare-worker/src/prisma/contract.prisma b/examples/prisma-8-cloudflare-worker/src/prisma/contract.prisma index af383df7f55d..c2e2e927af44 100644 --- a/examples/prisma-8-cloudflare-worker/src/prisma/contract.prisma +++ b/examples/prisma-8-cloudflare-worker/src/prisma/contract.prisma @@ -17,7 +17,7 @@ model User { id String @id @onCreate(uuid()) email String displayName String - createdAt DateTime @default(now()) + createdAt TimestamptzTemporal @default(now()) kind user_type address Address? posts Post[] @@ -30,7 +30,7 @@ model Post { id String @id @onCreate(uuid()) title String userId String - createdAt DateTime @default(now()) + createdAt TimestamptzTemporal @default(now()) user User @relation(fields: [userId], references: [id]) @@ -44,7 +44,7 @@ model Task { status String @default("open") type String userId String - createdAt DateTime @default(now()) + createdAt TimestamptzTemporal @default(now()) user User @relation(fields: [userId], references: [id]) diff --git a/examples/prisma-8-demo/migrations/app/20260422T0720_initial/contract.prisma b/examples/prisma-8-demo/migrations/app/20260422T0720_initial/contract.prisma index c9993fb036b1..93051368ceb8 100644 --- a/examples/prisma-8-demo/migrations/app/20260422T0720_initial/contract.prisma +++ b/examples/prisma-8-demo/migrations/app/20260422T0720_initial/contract.prisma @@ -28,7 +28,7 @@ model User { id Uuid @id @onCreate(uuid()) email String displayName String - createdAt DateTime @default(now()) + createdAt TimestamptzTemporal @default(now()) kind user_type address Address? posts Post[] @@ -42,7 +42,7 @@ model Post { title String userId Uuid priority Priority @default(Low) - createdAt DateTime @default(now()) + createdAt TimestamptzTemporal @default(now()) embedding Embedding1536? user User @relation(fields: [userId], references: [id]) @@ -78,7 +78,7 @@ model Task { status String @default("open") type String userId Uuid - createdAt DateTime @default(now()) + createdAt TimestamptzTemporal @default(now()) user User @relation(fields: [userId], references: [id]) diff --git a/examples/prisma-8-demo/migrations/app/20260810T1108_add_post_engagement_counters/contract.prisma b/examples/prisma-8-demo/migrations/app/20260810T1108_add_post_engagement_counters/contract.prisma index 9ecc57adb666..5abc32952db2 100644 --- a/examples/prisma-8-demo/migrations/app/20260810T1108_add_post_engagement_counters/contract.prisma +++ b/examples/prisma-8-demo/migrations/app/20260810T1108_add_post_engagement_counters/contract.prisma @@ -28,7 +28,7 @@ model User { id Uuid @id @onCreate(uuid()) email String displayName String - createdAt DateTime @default(now()) + createdAt TimestamptzTemporal @default(now()) kind user_type address Address? posts Post[] @@ -42,7 +42,7 @@ model Post { title String userId Uuid priority Priority @default(Low) - createdAt DateTime @default(now()) + createdAt TimestamptzTemporal @default(now()) embedding Embedding1536? // Three engagement counters, one per integer representation. They are @@ -94,7 +94,7 @@ model Task { status String @default("open") type String userId Uuid - createdAt DateTime @default(now()) + createdAt TimestamptzTemporal @default(now()) user User @relation(fields: [userId], references: [id]) diff --git a/examples/prisma-8-demo/migrations/app/20260917T0818_add_post_expires_at/contract.prisma b/examples/prisma-8-demo/migrations/app/20260917T0818_add_post_expires_at/contract.prisma index b03d39bd5c99..07dbc8df3d69 100644 --- a/examples/prisma-8-demo/migrations/app/20260917T0818_add_post_expires_at/contract.prisma +++ b/examples/prisma-8-demo/migrations/app/20260917T0818_add_post_expires_at/contract.prisma @@ -28,7 +28,7 @@ model User { id Uuid @id @onCreate(uuid()) email String displayName String - createdAt DateTime @default(now()) + createdAt TimestamptzTemporal @default(now()) kind user_type address Address? posts Post[] @@ -42,8 +42,8 @@ model Post { title String userId Uuid priority Priority @default(Low) - createdAt DateTime @default(now()) - expiresAt DateTime @default(sql`(now() + '7 days'::interval)`) + createdAt TimestamptzTemporal @default(now()) + expiresAt TimestamptzTemporal @default(sql`(now() + '7 days'::interval)`) embedding Embedding1536? // Three engagement counters, one per integer representation. They are @@ -95,7 +95,7 @@ model Task { status String @default("open") type String userId Uuid - createdAt DateTime @default(now()) + createdAt TimestamptzTemporal @default(now()) user User @relation(fields: [userId], references: [id]) diff --git a/examples/prisma-8-demo/migrations/app/20260922T1218_add_post_title_search/contract.prisma b/examples/prisma-8-demo/migrations/app/20260922T1218_add_post_title_search/contract.prisma index cff400263429..e20083f1f09f 100644 --- a/examples/prisma-8-demo/migrations/app/20260922T1218_add_post_title_search/contract.prisma +++ b/examples/prisma-8-demo/migrations/app/20260922T1218_add_post_title_search/contract.prisma @@ -28,7 +28,7 @@ model User { id Uuid @id @onCreate(uuid()) email String displayName String - createdAt DateTime @default(now()) + createdAt TimestamptzTemporal @default(now()) kind user_type address Address? posts Post[] @@ -42,8 +42,8 @@ model Post { title String userId Uuid priority Priority @default(Low) - createdAt DateTime @default(now()) - expiresAt DateTime @default(sql`(now() + '7 days'::interval)`) + createdAt TimestamptzTemporal @default(now()) + expiresAt TimestamptzTemporal @default(sql`(now() + '7 days'::interval)`) embedding Embedding1536? // Three engagement counters, one per integer representation. They are @@ -96,7 +96,7 @@ model Task { status String @default("open") type String userId Uuid - createdAt DateTime @default(now()) + createdAt TimestamptzTemporal @default(now()) user User @relation(fields: [userId], references: [id]) diff --git a/examples/prisma-8-demo/migrations/app/20261009T1354_add_post_body_search/contract.prisma b/examples/prisma-8-demo/migrations/app/20261009T1354_add_post_body_search/contract.prisma index 875756472ef7..67dc00cb3245 100644 --- a/examples/prisma-8-demo/migrations/app/20261009T1354_add_post_body_search/contract.prisma +++ b/examples/prisma-8-demo/migrations/app/20261009T1354_add_post_body_search/contract.prisma @@ -28,7 +28,7 @@ model User { id Uuid @id @onCreate(uuid()) email String displayName String - createdAt DateTime @default(now()) + createdAt TimestamptzTemporal @default(now()) kind user_type address Address? posts Post[] @@ -43,8 +43,8 @@ model Post { body String? userId Uuid priority Priority @default(Low) - createdAt DateTime @default(now()) - expiresAt DateTime @default(sql`(now() + '7 days'::interval)`) + createdAt TimestamptzTemporal @default(now()) + expiresAt TimestamptzTemporal @default(sql`(now() + '7 days'::interval)`) embedding Embedding1536? // Three engagement counters, one per integer representation. They are @@ -100,7 +100,7 @@ model Task { status String @default("open") type String userId Uuid - createdAt DateTime @default(now()) + createdAt TimestamptzTemporal @default(now()) user User @relation(fields: [userId], references: [id]) diff --git a/examples/prisma-8-demo/src/prisma/contract.prisma b/examples/prisma-8-demo/src/prisma/contract.prisma index 437af5685bf5..7ca2ccd41957 100644 --- a/examples/prisma-8-demo/src/prisma/contract.prisma +++ b/examples/prisma-8-demo/src/prisma/contract.prisma @@ -28,7 +28,7 @@ model User { id Uuid @id @onCreate(uuid()) email String displayName String - createdAt DateTime @default(now()) + createdAt TimestamptzTemporal @default(now()) kind user_type address Address? posts Post[] @@ -43,8 +43,8 @@ model Post { body String? userId Uuid priority Priority @default(Low) - createdAt DateTime @default(now()) - expiresAt DateTime @default(sql`(now() + '7 days'::interval)`) + createdAt TimestamptzTemporal @default(now()) + expiresAt TimestamptzTemporal @default(sql`(now() + '7 days'::interval)`) embedding Embedding1536? // Three engagement counters, one per integer representation. They are @@ -100,7 +100,7 @@ model Task { status String @default("open") type String userId Uuid - createdAt DateTime @default(now()) + createdAt TimestamptzTemporal @default(now()) user User @relation(fields: [userId], references: [id]) diff --git a/examples/react-router-demo/src/prisma/contract.prisma b/examples/react-router-demo/src/prisma/contract.prisma index 6dbca00db14c..f4f18eefde3d 100644 --- a/examples/react-router-demo/src/prisma/contract.prisma +++ b/examples/react-router-demo/src/prisma/contract.prisma @@ -3,7 +3,7 @@ model User { id Char(36) @id @onCreate(uuid()) email String - createdAt DateTime @default(now()) + createdAt TimestamptzTemporal @default(now()) posts Post[] @@map("user") @@ -13,7 +13,7 @@ model Post { id Char(36) @id @onCreate(uuid()) title String userId String - createdAt DateTime @default(now()) + createdAt TimestamptzTemporal @default(now()) user User @relation(fields: [userId], references: [id]) diff --git a/test/integration/test/authoring/parity/callback-mode-scalars/schema.prisma b/test/integration/test/authoring/parity/callback-mode-scalars/schema.prisma index 8005abf00965..eca801bf9e73 100644 --- a/test/integration/test/authoring/parity/callback-mode-scalars/schema.prisma +++ b/test/integration/test/authoring/parity/callback-mode-scalars/schema.prisma @@ -12,7 +12,7 @@ model User { score Float? profile Jsonb? embedding Embedding? - createdAt DateTime @default(now()) + createdAt TimestamptzTemporal @default(now()) @@map("user") } diff --git a/test/integration/test/authoring/parity/core-surface/schema.prisma b/test/integration/test/authoring/parity/core-surface/schema.prisma index 5407e408d2b0..b15c4a213b28 100644 --- a/test/integration/test/authoring/parity/core-surface/schema.prisma +++ b/test/integration/test/authoring/parity/core-surface/schema.prisma @@ -14,7 +14,7 @@ model User { id Int @id @default(autoincrement()) email Email @unique role Role - createdAt DateTime @default(now()) + createdAt TimestamptzTemporal @default(now()) isActive Boolean @default(true) profile Jsonb? @@map("user") diff --git a/test/integration/test/authoring/parity/default-date-time-types/schema.prisma b/test/integration/test/authoring/parity/default-date-time-types/schema.prisma index e5cad60f1dcc..15835e7aa9c1 100644 --- a/test/integration/test/authoring/parity/default-date-time-types/schema.prisma +++ b/test/integration/test/authoring/parity/default-date-time-types/schema.prisma @@ -2,10 +2,10 @@ model T { id Int @id - instant DateTime @default(timestamptz`2024-01-01T01:00:00.500+01:00`) + instant TimestamptzTemporal @default(timestamptz`2024-01-01T01:00:00.500+01:00`) local TimestampTemporal @default(timestamp`2024-01-01 12:34:56.500`) - day Date @default(date`0044-03-15 BC`) - clock Time @default(time`12:34`) + day DateTemporal @default(date`0044-03-15 BC`) + clock TimeTemporal @default(time`12:34`) zoned Timetz @default(timetz`12:34:56+02`) big BigInt @default(9007199254740993) bytes Bytes @default(bytea`\x010203`) diff --git a/test/integration/test/authoring/parity/default-sql-literal/schema.prisma b/test/integration/test/authoring/parity/default-sql-literal/schema.prisma index c2c412f7137c..8141f3abdea7 100644 --- a/test/integration/test/authoring/parity/default-sql-literal/schema.prisma +++ b/test/integration/test/authoring/parity/default-sql-literal/schema.prisma @@ -3,8 +3,8 @@ model T { id String @id @default(sql`gen_random_uuid()`) digest String @default(sql`md5(random()::text)`) - expires DateTime @default(sql"(now() + '00:03:00'::interval)") - createdAt DateTime @default(sql`(now() + interval '1 hour')`) + expires TimestamptzTemporal @default(sql"(now() + '00:03:00'::interval)") + createdAt TimestamptzTemporal @default(sql`(now() + interval '1 hour')`) tags String[] @default(sql`'{}'::text[]`) escaped String @default(sql`E'\n'`) diff --git a/test/integration/test/authoring/parity/generator-on-update-time/schema.prisma b/test/integration/test/authoring/parity/generator-on-update-time/schema.prisma index 8e5eba67ee9c..46bd5d96fd38 100644 --- a/test/integration/test/authoring/parity/generator-on-update-time/schema.prisma +++ b/test/integration/test/authoring/parity/generator-on-update-time/schema.prisma @@ -2,6 +2,6 @@ model Event { id Int @id - seenAt Time(3) @onUpdate(now()) + seenAt TimeTemporal(3) @onUpdate(now()) @@map("event") } diff --git a/test/integration/test/authoring/parity/sql-expressions/schema.prisma b/test/integration/test/authoring/parity/sql-expressions/schema.prisma index 39965a39094d..f2e75f3a904a 100644 --- a/test/integration/test/authoring/parity/sql-expressions/schema.prisma +++ b/test/integration/test/authoring/parity/sql-expressions/schema.prisma @@ -7,7 +7,7 @@ namespace public { title String email String body String - archivedAt DateTime? + archivedAt TimestamptzTemporal? slug String @default(sql` lower( 'post-' || md5(random()::text) diff --git a/test/integration/test/authoring/side-by-side/postgres/contract.prisma b/test/integration/test/authoring/side-by-side/postgres/contract.prisma index 1a95722b5382..45efed1084c2 100644 --- a/test/integration/test/authoring/side-by-side/postgres/contract.prisma +++ b/test/integration/test/authoring/side-by-side/postgres/contract.prisma @@ -14,7 +14,7 @@ model Post { id Int @id @default(autoincrement()) authorId Int title String - publishedAt DateTime? + publishedAt TimestamptzTemporal? author User @relation(fields: [authorId], references: [id]) @@map("posts") diff --git a/test/integration/test/date-time-defaults/_fixture-before-canonical-form/contract-with-tags.prisma b/test/integration/test/date-time-defaults/_fixture-before-canonical-form/contract-with-tags.prisma index a188c9678979..9d2c9a0186d9 100644 --- a/test/integration/test/date-time-defaults/_fixture-before-canonical-form/contract-with-tags.prisma +++ b/test/integration/test/date-time-defaults/_fixture-before-canonical-form/contract-with-tags.prisma @@ -2,14 +2,14 @@ model Event { id Int @id - a DateTime @default(timestamptz`2024-01-01T00:00:00Z`) - b DateTime @default(timestamptz`2024-01-01T00:00:00.000Z`) - c DateTime @default(timestamptz`2024-01-01T01:00:00+01:00`) + a TimestamptzTemporal @default(timestamptz`2024-01-01T00:00:00Z`) + b TimestamptzTemporal @default(timestamptz`2024-01-01T00:00:00.000Z`) + c TimestamptzTemporal @default(timestamptz`2024-01-01T01:00:00+01:00`) localAt TimestampTemporal(3) @default(timestamp`2024-01-01 12:34:56.500`) - day Date @default(date`2024-01-01`) - clock Time @default(time`12:34`) + day DateTemporal @default(date`2024-01-01`) + clock TimeTemporal @default(time`12:34`) zoned Timetz @default(timetz`12:34:56+02`) - history DateTime[] @default([timestamptz`2024-01-01T00:00:00.000Z`, timestamptz`2024-06-30 12:34:56+00`]) + history TimestamptzTemporal[] @default([timestamptz`2024-01-01T00:00:00.000Z`, timestamptz`2024-06-30 12:34:56+00`]) @@map("event") } diff --git a/test/integration/test/date-time-defaults/_fixture-before-canonical-form/contract.prisma b/test/integration/test/date-time-defaults/_fixture-before-canonical-form/contract.prisma index 400a07fa0e3e..b67412775120 100644 --- a/test/integration/test/date-time-defaults/_fixture-before-canonical-form/contract.prisma +++ b/test/integration/test/date-time-defaults/_fixture-before-canonical-form/contract.prisma @@ -2,14 +2,14 @@ model Event { id Int @id - a DateTime @default("2024-01-01T00:00:00Z") - b DateTime @default("2024-01-01T00:00:00.000Z") - c DateTime @default("2024-01-01T01:00:00+01:00") + a TimestamptzTemporal @default("2024-01-01T00:00:00Z") + b TimestamptzTemporal @default("2024-01-01T00:00:00.000Z") + c TimestamptzTemporal @default("2024-01-01T01:00:00+01:00") localAt TimestampTemporal(3) @default("2024-01-01 12:34:56.500") - day Date @default("2024-01-01") - clock Time @default("12:34") + day DateTemporal @default("2024-01-01") + clock TimeTemporal @default("12:34") zoned Timetz @default("12:34:56+02") - history DateTime[] @default(["2024-01-01T00:00:00.000Z", "2024-06-30 12:34:56+00"]) + history TimestamptzTemporal[] @default(["2024-01-01T00:00:00.000Z", "2024-06-30 12:34:56+00"]) @@map("event") } diff --git a/test/integration/test/ports/engines/queries/aggregation/count/_fixture/contract.prisma b/test/integration/test/ports/engines/queries/aggregation/count/_fixture/contract.prisma index edad9cc86a50..bf7d41561094 100644 --- a/test/integration/test/ports/engines/queries/aggregation/count/_fixture/contract.prisma +++ b/test/integration/test/ports/engines/queries/aggregation/count/_fixture/contract.prisma @@ -8,6 +8,6 @@ model TestModel { float Float? bytes Bytes? bool Boolean? - dt DateTime? + dt TimestamptzTemporal? @@map("testModel") } diff --git a/test/integration/test/ports/engines/queries/aggregation/many_count_relation/_fixture/compound/contract.prisma b/test/integration/test/ports/engines/queries/aggregation/many_count_relation/_fixture/compound/contract.prisma index eccba00f25f4..c239bafef02b 100644 --- a/test/integration/test/ports/engines/queries/aggregation/many_count_relation/_fixture/compound/contract.prisma +++ b/test/integration/test/ports/engines/queries/aggregation/many_count_relation/_fixture/compound/contract.prisma @@ -29,7 +29,7 @@ model Vote { userId Int objectiveId Int followerId Int - createdAt DateTime @default(now()) + createdAt TimestamptzTemporal @default(now()) user User @relation(fields: [userId], references: [id]) userObjective UserToObjective @relation(fields: [objectiveId, followerId], references: [userId, objectiveId], onDelete: NoAction, onUpdate: NoAction) diff --git a/test/integration/test/ports/engines/queries/data_types/bigint/_fixture/contract.prisma b/test/integration/test/ports/engines/queries/data_types/bigint/_fixture/contract.prisma index 9ab9e5e5b23a..94972ebb0ebf 100644 --- a/test/integration/test/ports/engines/queries/data_types/bigint/_fixture/contract.prisma +++ b/test/integration/test/ports/engines/queries/data_types/bigint/_fixture/contract.prisma @@ -8,6 +8,6 @@ model TestModel { float Float? bytes Bytes? bool Boolean? - dt DateTime? + dt TimestamptzTemporal? @@map("testModel") } diff --git a/test/integration/test/ports/engines/queries/data_types/bool/_fixture/contract.prisma b/test/integration/test/ports/engines/queries/data_types/bool/_fixture/contract.prisma index 9ab9e5e5b23a..94972ebb0ebf 100644 --- a/test/integration/test/ports/engines/queries/data_types/bool/_fixture/contract.prisma +++ b/test/integration/test/ports/engines/queries/data_types/bool/_fixture/contract.prisma @@ -8,6 +8,6 @@ model TestModel { float Float? bytes Bytes? bool Boolean? - dt DateTime? + dt TimestamptzTemporal? @@map("testModel") } diff --git a/test/integration/test/ports/engines/queries/data_types/bytes/_fixture/scalars/contract.prisma b/test/integration/test/ports/engines/queries/data_types/bytes/_fixture/scalars/contract.prisma index 9ab9e5e5b23a..94972ebb0ebf 100644 --- a/test/integration/test/ports/engines/queries/data_types/bytes/_fixture/scalars/contract.prisma +++ b/test/integration/test/ports/engines/queries/data_types/bytes/_fixture/scalars/contract.prisma @@ -8,6 +8,6 @@ model TestModel { float Float? bytes Bytes? bool Boolean? - dt DateTime? + dt TimestamptzTemporal? @@map("testModel") } diff --git a/test/integration/test/ports/engines/queries/data_types/datetime/_fixture/contract.prisma b/test/integration/test/ports/engines/queries/data_types/datetime/_fixture/contract.prisma index 9ab9e5e5b23a..94972ebb0ebf 100644 --- a/test/integration/test/ports/engines/queries/data_types/datetime/_fixture/contract.prisma +++ b/test/integration/test/ports/engines/queries/data_types/datetime/_fixture/contract.prisma @@ -8,6 +8,6 @@ model TestModel { float Float? bytes Bytes? bool Boolean? - dt DateTime? + dt TimestamptzTemporal? @@map("testModel") } diff --git a/test/integration/test/ports/engines/queries/data_types/float/_fixture/contract.prisma b/test/integration/test/ports/engines/queries/data_types/float/_fixture/contract.prisma index edad9cc86a50..bf7d41561094 100644 --- a/test/integration/test/ports/engines/queries/data_types/float/_fixture/contract.prisma +++ b/test/integration/test/ports/engines/queries/data_types/float/_fixture/contract.prisma @@ -8,6 +8,6 @@ model TestModel { float Float? bytes Bytes? bool Boolean? - dt DateTime? + dt TimestamptzTemporal? @@map("testModel") } diff --git a/test/integration/test/ports/engines/queries/data_types/int/_fixture/contract.prisma b/test/integration/test/ports/engines/queries/data_types/int/_fixture/contract.prisma index edad9cc86a50..bf7d41561094 100644 --- a/test/integration/test/ports/engines/queries/data_types/int/_fixture/contract.prisma +++ b/test/integration/test/ports/engines/queries/data_types/int/_fixture/contract.prisma @@ -8,6 +8,6 @@ model TestModel { float Float? bytes Bytes? bool Boolean? - dt DateTime? + dt TimestamptzTemporal? @@map("testModel") } diff --git a/test/integration/test/ports/engines/queries/data_types/string/_fixture/contract.prisma b/test/integration/test/ports/engines/queries/data_types/string/_fixture/contract.prisma index edad9cc86a50..bf7d41561094 100644 --- a/test/integration/test/ports/engines/queries/data_types/string/_fixture/contract.prisma +++ b/test/integration/test/ports/engines/queries/data_types/string/_fixture/contract.prisma @@ -8,6 +8,6 @@ model TestModel { float Float? bytes Bytes? bool Boolean? - dt DateTime? + dt TimestamptzTemporal? @@map("testModel") } diff --git a/test/integration/test/ports/engines/queries/data_types/through_relation/_fixture/common/contract.prisma b/test/integration/test/ports/engines/queries/data_types/through_relation/_fixture/common/contract.prisma index 9667510bb971..21d5bdfadd21 100644 --- a/test/integration/test/ports/engines/queries/data_types/through_relation/_fixture/common/contract.prisma +++ b/test/integration/test/ports/engines/queries/data_types/through_relation/_fixture/common/contract.prisma @@ -15,6 +15,6 @@ model Child { float Float bytes Bytes bool Boolean - dt DateTime + dt TimestamptzTemporal @@map("child") } diff --git a/test/integration/test/ports/engines/queries/data_types/through_relation/_fixture/lists/contract.prisma b/test/integration/test/ports/engines/queries/data_types/through_relation/_fixture/lists/contract.prisma index 6798c6fef79d..eef6d42b96b8 100644 --- a/test/integration/test/ports/engines/queries/data_types/through_relation/_fixture/lists/contract.prisma +++ b/test/integration/test/ports/engines/queries/data_types/through_relation/_fixture/lists/contract.prisma @@ -15,7 +15,7 @@ model Child { float Float[] bytes Bytes[] bool Boolean[] - dt DateTime[] + dt TimestamptzTemporal[] empty Int[] unset Int[] @@map("child") diff --git a/test/integration/test/ports/engines/queries/distinct/_fixture/contract.prisma b/test/integration/test/ports/engines/queries/distinct/_fixture/contract.prisma index 6256f224ab4f..f16eb5f9dcde 100644 --- a/test/integration/test/ports/engines/queries/distinct/_fixture/contract.prisma +++ b/test/integration/test/ports/engines/queries/distinct/_fixture/contract.prisma @@ -5,7 +5,7 @@ model User { first_name String last_name String email String @unique - birthday DateTime? + birthday TimestamptzTemporal? posts Post[] @@map("user") } diff --git a/test/integration/test/ports/engines/queries/filters/bigint_filter/_fixture/contract.prisma b/test/integration/test/ports/engines/queries/filters/bigint_filter/_fixture/contract.prisma index edad9cc86a50..bf7d41561094 100644 --- a/test/integration/test/ports/engines/queries/filters/bigint_filter/_fixture/contract.prisma +++ b/test/integration/test/ports/engines/queries/filters/bigint_filter/_fixture/contract.prisma @@ -8,6 +8,6 @@ model TestModel { float Float? bytes Bytes? bool Boolean? - dt DateTime? + dt TimestamptzTemporal? @@map("testModel") } diff --git a/test/integration/test/ports/engines/queries/filters/bytes_filter/_fixture/contract.prisma b/test/integration/test/ports/engines/queries/filters/bytes_filter/_fixture/contract.prisma index edad9cc86a50..bf7d41561094 100644 --- a/test/integration/test/ports/engines/queries/filters/bytes_filter/_fixture/contract.prisma +++ b/test/integration/test/ports/engines/queries/filters/bytes_filter/_fixture/contract.prisma @@ -8,6 +8,6 @@ model TestModel { float Float? bytes Bytes? bool Boolean? - dt DateTime? + dt TimestamptzTemporal? @@map("testModel") } diff --git a/test/integration/test/ports/engines/queries/filters/field_reference/_fixture/list/contract.prisma b/test/integration/test/ports/engines/queries/filters/field_reference/_fixture/list/contract.prisma index 2fd9e1eb13f5..a70f0afe84a3 100644 --- a/test/integration/test/ports/engines/queries/filters/field_reference/_fixture/list/contract.prisma +++ b/test/integration/test/ports/engines/queries/filters/field_reference/_fixture/list/contract.prisma @@ -27,8 +27,8 @@ model TestModel { bool_list Boolean[] bool_list2 Boolean[] - dt DateTime? - dt_list DateTime[] - dt_list2 DateTime[] + dt TimestamptzTemporal? + dt_list TimestamptzTemporal[] + dt_list2 TimestamptzTemporal[] @@map("testModel") } diff --git a/test/integration/test/ports/engines/queries/filters/field_reference/_fixture/mixed/contract.prisma b/test/integration/test/ports/engines/queries/filters/field_reference/_fixture/mixed/contract.prisma index 9cb562e15cc7..db95b9e4d5ff 100644 --- a/test/integration/test/ports/engines/queries/filters/field_reference/_fixture/mixed/contract.prisma +++ b/test/integration/test/ports/engines/queries/filters/field_reference/_fixture/mixed/contract.prisma @@ -14,7 +14,7 @@ model TestModel { bytes2 Bytes[] bool Boolean? bool2 Boolean[] - dt DateTime? - dt2 DateTime[] + dt TimestamptzTemporal? + dt2 TimestamptzTemporal[] @@map("testModel") } diff --git a/test/integration/test/ports/engines/queries/filters/field_reference/bigint_filter/_fixture/contract.prisma b/test/integration/test/ports/engines/queries/filters/field_reference/bigint_filter/_fixture/contract.prisma index 1f51080b5f31..3946f3a2ab05 100644 --- a/test/integration/test/ports/engines/queries/filters/field_reference/bigint_filter/_fixture/contract.prisma +++ b/test/integration/test/ports/engines/queries/filters/field_reference/bigint_filter/_fixture/contract.prisma @@ -14,7 +14,7 @@ model TestModel { bytes2 Bytes? bool Boolean? bool2 Boolean? - dt DateTime? - dt2 DateTime? + dt TimestamptzTemporal? + dt2 TimestamptzTemporal? @@map("testModel") } diff --git a/test/integration/test/ports/engines/queries/filters/field_reference/bytes_filter/_fixture/contract.prisma b/test/integration/test/ports/engines/queries/filters/field_reference/bytes_filter/_fixture/contract.prisma index 1f51080b5f31..3946f3a2ab05 100644 --- a/test/integration/test/ports/engines/queries/filters/field_reference/bytes_filter/_fixture/contract.prisma +++ b/test/integration/test/ports/engines/queries/filters/field_reference/bytes_filter/_fixture/contract.prisma @@ -14,7 +14,7 @@ model TestModel { bytes2 Bytes? bool Boolean? bool2 Boolean? - dt DateTime? - dt2 DateTime? + dt TimestamptzTemporal? + dt2 TimestamptzTemporal? @@map("testModel") } diff --git a/test/integration/test/ports/engines/queries/filters/field_reference/datetime_filter/_fixture/contract.prisma b/test/integration/test/ports/engines/queries/filters/field_reference/datetime_filter/_fixture/contract.prisma index 1f51080b5f31..3946f3a2ab05 100644 --- a/test/integration/test/ports/engines/queries/filters/field_reference/datetime_filter/_fixture/contract.prisma +++ b/test/integration/test/ports/engines/queries/filters/field_reference/datetime_filter/_fixture/contract.prisma @@ -14,7 +14,7 @@ model TestModel { bytes2 Bytes? bool Boolean? bool2 Boolean? - dt DateTime? - dt2 DateTime? + dt TimestamptzTemporal? + dt2 TimestamptzTemporal? @@map("testModel") } diff --git a/test/integration/test/ports/engines/queries/filters/field_reference/failure/_fixture/common/contract.prisma b/test/integration/test/ports/engines/queries/filters/field_reference/failure/_fixture/common/contract.prisma index 1f51080b5f31..3946f3a2ab05 100644 --- a/test/integration/test/ports/engines/queries/filters/field_reference/failure/_fixture/common/contract.prisma +++ b/test/integration/test/ports/engines/queries/filters/field_reference/failure/_fixture/common/contract.prisma @@ -14,7 +14,7 @@ model TestModel { bytes2 Bytes? bool Boolean? bool2 Boolean? - dt DateTime? - dt2 DateTime? + dt TimestamptzTemporal? + dt2 TimestamptzTemporal? @@map("testModel") } diff --git a/test/integration/test/ports/engines/queries/filters/field_reference/float_filter/_fixture/contract.prisma b/test/integration/test/ports/engines/queries/filters/field_reference/float_filter/_fixture/contract.prisma index 1f51080b5f31..3946f3a2ab05 100644 --- a/test/integration/test/ports/engines/queries/filters/field_reference/float_filter/_fixture/contract.prisma +++ b/test/integration/test/ports/engines/queries/filters/field_reference/float_filter/_fixture/contract.prisma @@ -14,7 +14,7 @@ model TestModel { bytes2 Bytes? bool Boolean? bool2 Boolean? - dt DateTime? - dt2 DateTime? + dt TimestamptzTemporal? + dt2 TimestamptzTemporal? @@map("testModel") } diff --git a/test/integration/test/ports/engines/queries/filters/field_reference/having_filter/_fixture/contract.prisma b/test/integration/test/ports/engines/queries/filters/field_reference/having_filter/_fixture/contract.prisma index 1f51080b5f31..3946f3a2ab05 100644 --- a/test/integration/test/ports/engines/queries/filters/field_reference/having_filter/_fixture/contract.prisma +++ b/test/integration/test/ports/engines/queries/filters/field_reference/having_filter/_fixture/contract.prisma @@ -14,7 +14,7 @@ model TestModel { bytes2 Bytes? bool Boolean? bool2 Boolean? - dt DateTime? - dt2 DateTime? + dt TimestamptzTemporal? + dt2 TimestamptzTemporal? @@map("testModel") } diff --git a/test/integration/test/ports/engines/queries/filters/field_reference/int_filter/_fixture/contract.prisma b/test/integration/test/ports/engines/queries/filters/field_reference/int_filter/_fixture/contract.prisma index 1f51080b5f31..3946f3a2ab05 100644 --- a/test/integration/test/ports/engines/queries/filters/field_reference/int_filter/_fixture/contract.prisma +++ b/test/integration/test/ports/engines/queries/filters/field_reference/int_filter/_fixture/contract.prisma @@ -14,7 +14,7 @@ model TestModel { bytes2 Bytes? bool Boolean? bool2 Boolean? - dt DateTime? - dt2 DateTime? + dt TimestamptzTemporal? + dt2 TimestamptzTemporal? @@map("testModel") } diff --git a/test/integration/test/ports/engines/queries/filters/field_reference/string_filter/_fixture/contract.prisma b/test/integration/test/ports/engines/queries/filters/field_reference/string_filter/_fixture/contract.prisma index 1f51080b5f31..3946f3a2ab05 100644 --- a/test/integration/test/ports/engines/queries/filters/field_reference/string_filter/_fixture/contract.prisma +++ b/test/integration/test/ports/engines/queries/filters/field_reference/string_filter/_fixture/contract.prisma @@ -14,7 +14,7 @@ model TestModel { bytes2 Bytes? bool Boolean? bool2 Boolean? - dt DateTime? - dt2 DateTime? + dt TimestamptzTemporal? + dt2 TimestamptzTemporal? @@map("testModel") } diff --git a/test/integration/test/ports/engines/queries/filters/list_filters/_fixture/base/contract.prisma b/test/integration/test/ports/engines/queries/filters/list_filters/_fixture/base/contract.prisma index d64a52cc7109..e2c2e20ca273 100644 --- a/test/integration/test/ports/engines/queries/filters/list_filters/_fixture/base/contract.prisma +++ b/test/integration/test/ports/engines/queries/filters/list_filters/_fixture/base/contract.prisma @@ -8,6 +8,6 @@ model TestModel { float Float[] bytes Bytes[] bool Boolean[] - dt DateTime[] + dt TimestamptzTemporal[] @@map("testModel") } diff --git a/test/integration/test/ports/engines/queries/filters/many_relation/_fixture/25103/contract.prisma b/test/integration/test/ports/engines/queries/filters/many_relation/_fixture/25103/contract.prisma index 221c67d010b4..4f48d0874734 100644 --- a/test/integration/test/ports/engines/queries/filters/many_relation/_fixture/25103/contract.prisma +++ b/test/integration/test/ports/engines/queries/filters/many_relation/_fixture/25103/contract.prisma @@ -2,5 +2,5 @@ model Contact { id String @id identities Identity[] @@map("contact") } model Identity { id String @id contactId String contact Contact @relation(fields: [contactId], references: [id]) subscriptions Subscription[] @@map("identity") } -model Subscription { id String @id identityId String audienceId String optedOutAt DateTime? audience Audience @relation(fields: [audienceId], references: [id]) identity Identity @relation(fields: [identityId], references: [id]) @@map("subscription") } -model Audience { id String @id deletedAt DateTime? subscriptions Subscription[] @@map("audience") } +model Subscription { id String @id identityId String audienceId String optedOutAt TimestamptzTemporal? audience Audience @relation(fields: [audienceId], references: [id]) identity Identity @relation(fields: [identityId], references: [id]) @@map("subscription") } +model Audience { id String @id deletedAt TimestamptzTemporal? subscriptions Subscription[] @@map("audience") } diff --git a/test/integration/test/ports/prisma/functional/blog-update/_fixture/contract.prisma b/test/integration/test/ports/prisma/functional/blog-update/_fixture/contract.prisma index 00fe44e0ca38..05af4dacb507 100644 --- a/test/integration/test/ports/prisma/functional/blog-update/_fixture/contract.prisma +++ b/test/integration/test/ports/prisma/functional/blog-update/_fixture/contract.prisma @@ -42,8 +42,8 @@ model User { name String? posts Post[] profile Profile? - wakesUpAt DateTime? @default(now()) - lastLoginAt DateTime? @default(now()) + wakesUpAt TimestamptzTemporal? @default(now()) + lastLoginAt TimestamptzTemporal? @default(now()) @@map("user") } @@ -53,14 +53,14 @@ model Profile { notrequired String? user User @relation(fields: [userId], references: [id]) userId String @unique - goesToBedAt DateTime? @default(now()) - goesToOfficeAt DateTime? @default(now()) + goesToBedAt TimestamptzTemporal? @default(now()) + goesToOfficeAt TimestamptzTemporal? @default(now()) @@map("profile") } model Post { id String @id @onCreate(cuid(2)) - createdAt DateTime @default(now()) + createdAt TimestamptzTemporal @default(now()) updatedAt temporal.updatedAt() published Boolean title String @@ -68,7 +68,7 @@ model Post { optional String? authorId String? @map("author") author User? @relation(fields: [authorId], references: [id]) - lastReviewedAt DateTime? @default(now()) - lastPublishedAt DateTime? @default(now()) + lastReviewedAt TimestamptzTemporal? @default(now()) + lastPublishedAt TimestamptzTemporal? @default(now()) @@map("post") } diff --git a/test/integration/test/ports/prisma/functional/create-default-date/_fixture/contract.prisma b/test/integration/test/ports/prisma/functional/create-default-date/_fixture/contract.prisma index 56c2998ad5de..e21af13cfdff 100644 --- a/test/integration/test/ports/prisma/functional/create-default-date/_fixture/contract.prisma +++ b/test/integration/test/ports/prisma/functional/create-default-date/_fixture/contract.prisma @@ -10,6 +10,6 @@ model Visit { id Int @id @default(autoincrement()) - visitTime DateTime @default(now()) + visitTime TimestamptzTemporal @default(now()) @@map("visit") } diff --git a/test/integration/test/ports/prisma/functional/extended-where/_fixture/contract.prisma b/test/integration/test/ports/prisma/functional/extended-where/_fixture/contract.prisma index 44d36578e37f..77122a88539f 100644 --- a/test/integration/test/ports/prisma/functional/extended-where/_fixture/contract.prisma +++ b/test/integration/test/ports/prisma/functional/extended-where/_fixture/contract.prisma @@ -46,7 +46,7 @@ model Profile { userId String @unique email String @unique alias String @unique - createdAt DateTime @default(now()) + createdAt TimestamptzTemporal @default(now()) @@map("profile") } diff --git a/test/integration/test/ports/prisma/functional/interactive-transactions/_fixture/contract.prisma b/test/integration/test/ports/prisma/functional/interactive-transactions/_fixture/contract.prisma index 9faa570c1230..a3eacad353b9 100644 --- a/test/integration/test/ports/prisma/functional/interactive-transactions/_fixture/contract.prisma +++ b/test/integration/test/ports/prisma/functional/interactive-transactions/_fixture/contract.prisma @@ -32,7 +32,7 @@ model User { model Post { id String @id - createdAt DateTime @default(now()) + createdAt TimestamptzTemporal @default(now()) updatedAt temporal.updatedAt() published Boolean title String diff --git a/test/integration/test/ports/prisma/functional/issues-12572/_fixture/contract.prisma b/test/integration/test/ports/prisma/functional/issues-12572/_fixture/contract.prisma index dfad8c5dbeea..b7fec4862579 100644 --- a/test/integration/test/ports/prisma/functional/issues-12572/_fixture/contract.prisma +++ b/test/integration/test/ports/prisma/functional/issues-12572/_fixture/contract.prisma @@ -14,7 +14,7 @@ model User { id String @id @onCreate(cuid(2)) - createdAt DateTime @default(now()) + createdAt TimestamptzTemporal @default(now()) updatedAt temporal.updatedAt() @@map("user") } diff --git a/test/integration/test/ports/prisma/functional/issues-14954-date-batch/_fixture/contract.prisma b/test/integration/test/ports/prisma/functional/issues-14954-date-batch/_fixture/contract.prisma index c166d924de56..14210346bf65 100644 --- a/test/integration/test/ports/prisma/functional/issues-14954-date-batch/_fixture/contract.prisma +++ b/test/integration/test/ports/prisma/functional/issues-14954-date-batch/_fixture/contract.prisma @@ -11,6 +11,6 @@ // idForProvider(postgresql) is `String @id @default(cuid(2))`. model Resource { id String @id @onCreate(cuid(2)) - date DateTime @unique + date TimestamptzTemporal @unique @@map("resource") } diff --git a/test/integration/test/ports/prisma/functional/issues-18970-invalid-date/_fixture/contract.prisma b/test/integration/test/ports/prisma/functional/issues-18970-invalid-date/_fixture/contract.prisma index ccd9d3264a45..21c841a0f00d 100644 --- a/test/integration/test/ports/prisma/functional/issues-18970-invalid-date/_fixture/contract.prisma +++ b/test/integration/test/ports/prisma/functional/issues-18970-invalid-date/_fixture/contract.prisma @@ -10,6 +10,6 @@ model User { id String @id @onCreate(cuid(2)) - date DateTime? + date TimestamptzTemporal? @@map("user") } diff --git a/test/integration/test/ports/prisma/functional/issues-23902/_fixture/contract.prisma b/test/integration/test/ports/prisma/functional/issues-23902/_fixture/contract.prisma index 3c74638f20d0..18d16c56ab45 100644 --- a/test/integration/test/ports/prisma/functional/issues-23902/_fixture/contract.prisma +++ b/test/integration/test/ports/prisma/functional/issues-23902/_fixture/contract.prisma @@ -36,7 +36,7 @@ model User { model Post { id String @id @onCreate(cuid(2)) - createdAt DateTime @default(now()) + createdAt TimestamptzTemporal @default(now()) updatedAt temporal.updatedAt() title String content String? diff --git a/test/integration/test/ports/prisma/functional/issues-28192-pg-historical-dates/_fixture/contract.prisma b/test/integration/test/ports/prisma/functional/issues-28192-pg-historical-dates/_fixture/contract.prisma index 05c765d1e5d8..c2e77f6020a9 100644 --- a/test/integration/test/ports/prisma/functional/issues-28192-pg-historical-dates/_fixture/contract.prisma +++ b/test/integration/test/ports/prisma/functional/issues-28192-pg-historical-dates/_fixture/contract.prisma @@ -18,7 +18,7 @@ // types in Prisma 8 PSL. model TestData { id String @id @onCreate(cuid(2)) - date Date + date DateTemporal timestamp TimestampTemporal timestamptz TimestamptzTemporal @@map("testData") diff --git a/test/integration/test/ports/prisma/functional/issues-29309-datetime-cursor/_fixture/contract.prisma b/test/integration/test/ports/prisma/functional/issues-29309-datetime-cursor/_fixture/contract.prisma index bbbf88ef1d90..d689c695dc3f 100644 --- a/test/integration/test/ports/prisma/functional/issues-29309-datetime-cursor/_fixture/contract.prisma +++ b/test/integration/test/ports/prisma/functional/issues-29309-datetime-cursor/_fixture/contract.prisma @@ -17,7 +17,7 @@ model Event { appId Int - createdAt Date + createdAt DateTemporal value Int @@id([appId, createdAt]) diff --git a/test/integration/test/ports/prisma/functional/issues-29331-query-plan-cache-bloat/_fixture/contract.prisma b/test/integration/test/ports/prisma/functional/issues-29331-query-plan-cache-bloat/_fixture/contract.prisma index 3f6087fc54d7..12c1297486fd 100644 --- a/test/integration/test/ports/prisma/functional/issues-29331-query-plan-cache-bloat/_fixture/contract.prisma +++ b/test/integration/test/ports/prisma/functional/issues-29331-query-plan-cache-bloat/_fixture/contract.prisma @@ -22,13 +22,13 @@ model ContactAnalytics { id String @id @onCreate(cuid(2)) contactId String contact Contact @relation(fields: [contactId], references: [id], onDelete: Cascade) - date1 DateTime? - date2 DateTime? - date3 DateTime? - date4 DateTime? - date5 DateTime? - date6 DateTime? - date7 DateTime? + date1 TimestamptzTemporal? + date2 TimestamptzTemporal? + date3 TimestamptzTemporal? + date4 TimestamptzTemporal? + date5 TimestamptzTemporal? + date6 TimestamptzTemporal? + date7 TimestamptzTemporal? val1 Int? val2 Int? val3 Int? diff --git a/test/integration/test/ports/prisma/functional/legacy-aggregations/_fixture/contract.prisma b/test/integration/test/ports/prisma/functional/legacy-aggregations/_fixture/contract.prisma index fc7cf8151a1c..ba76414a5187 100644 --- a/test/integration/test/ports/prisma/functional/legacy-aggregations/_fixture/contract.prisma +++ b/test/integration/test/ports/prisma/functional/legacy-aggregations/_fixture/contract.prisma @@ -33,7 +33,7 @@ model User { model Post { id String @id @onCreate(cuid(2)) - createdAt DateTime @default(now()) + createdAt TimestamptzTemporal @default(now()) updatedAt temporal.updatedAt() published Boolean title String diff --git a/test/integration/test/ports/prisma/functional/methods-count/_fixture/contract.prisma b/test/integration/test/ports/prisma/functional/methods-count/_fixture/contract.prisma index 8831c16e1fd5..3f474ad648e6 100644 --- a/test/integration/test/ports/prisma/functional/methods-count/_fixture/contract.prisma +++ b/test/integration/test/ports/prisma/functional/methods-count/_fixture/contract.prisma @@ -33,7 +33,7 @@ model User { model Post { id String @id @onCreate(cuid(2)) - createdAt DateTime @default(now()) + createdAt TimestamptzTemporal @default(now()) updatedAt temporal.updatedAt() published Boolean title String diff --git a/test/integration/test/ports/prisma/functional/multiple-types/_fixture/contract.prisma b/test/integration/test/ports/prisma/functional/multiple-types/_fixture/contract.prisma index 3409574f954b..4f793701717d 100644 --- a/test/integration/test/ports/prisma/functional/multiple-types/_fixture/contract.prisma +++ b/test/integration/test/ports/prisma/functional/multiple-types/_fixture/contract.prisma @@ -22,7 +22,7 @@ model TestModel { float Float? bytes Bytes? bool Boolean? - dt DateTime? + dt TimestamptzTemporal? dec Decimal? @@map("testModel") } diff --git a/test/integration/test/sql-orm-client/fixtures/soft-delete/contract.prisma b/test/integration/test/sql-orm-client/fixtures/soft-delete/contract.prisma index 5b879ee6875b..176996849795 100644 --- a/test/integration/test/sql-orm-client/fixtures/soft-delete/contract.prisma +++ b/test/integration/test/sql-orm-client/fixtures/soft-delete/contract.prisma @@ -24,8 +24,8 @@ model Post { title String views Int userId Int @map("user_id") - createdAt DateTime @default(now()) @map("created_at") - deletedAt DateTime? @map("deleted_at") + createdAt TimestamptzTemporal @default(now()) @map("created_at") + deletedAt TimestamptzTemporal? @map("deleted_at") user User @relation(fields: [userId], references: [id]) comments Comment[] @@ -37,7 +37,7 @@ model Comment { id Int @id @default(autoincrement()) body String postId Int @map("post_id") - deletedAt DateTime? @map("deleted_at") + deletedAt TimestamptzTemporal? @map("deleted_at") post Post @relation(fields: [postId], references: [id]) diff --git a/test/integration/test/temporal-defaults/_fixture-attributes/contract.prisma b/test/integration/test/temporal-defaults/_fixture-attributes/contract.prisma index 7ee8ec4e8baa..872abfba5dc2 100644 --- a/test/integration/test/temporal-defaults/_fixture-attributes/contract.prisma +++ b/test/integration/test/temporal-defaults/_fixture-attributes/contract.prisma @@ -3,11 +3,11 @@ model Clock { id Int @id label String - date Date @onCreate(now()) @onUpdate(now()) + date DateTemporal @onCreate(now()) @onUpdate(now()) dateString DateString @onCreate(now()) @onUpdate(now()) - time0 Time(0) @onCreate(now()) @onUpdate(now()) - time3 Time(3) @onCreate(now()) @onUpdate(now()) - time6 Time(6) @onCreate(now()) @onUpdate(now()) + time0 TimeTemporal(0) @onCreate(now()) @onUpdate(now()) + time3 TimeTemporal(3) @onCreate(now()) @onUpdate(now()) + time6 TimeTemporal(6) @onCreate(now()) @onUpdate(now()) timeString0 TimeString(0) @onCreate(now()) @onUpdate(now()) timeString3 TimeString(3) @onCreate(now()) @onUpdate(now()) timeString6 TimeString(6) @onCreate(now()) @onUpdate(now()) From 139dac430ef13426b295aa2ebf1f8a199faad0c5 Mon Sep 17 00:00:00 2001 From: willbot Date: Thu, 22 Oct 2026 20:41:22 +0200 Subject: [PATCH 038/118] The Prisma 7 reader resolves a type its binding maps to a type choice Co-Authored-By: Claude Opus 5.5 Signed-off-by: willbot Signed-off-by: Will Madden --- .../src/exports/authoring.ts | 1 + .../src/shared/framework-authoring.ts | 26 +++++++++++ .../contract-prisma7/src/interpreter.ts | 25 ++++++++--- .../contract-prisma7/test/support.ts | 8 +++- .../test/type-choice-binding.test.ts | 43 +++++++++++++++++++ 5 files changed, 96 insertions(+), 7 deletions(-) create mode 100644 packages/2-sql/2-authoring/contract-prisma7/test/type-choice-binding.test.ts diff --git a/packages/1-framework/1-core/framework-components/src/exports/authoring.ts b/packages/1-framework/1-core/framework-components/src/exports/authoring.ts index e30dfa176dd4..c76f6813fc2c 100644 --- a/packages/1-framework/1-core/framework-components/src/exports/authoring.ts +++ b/packages/1-framework/1-core/framework-components/src/exports/authoring.ts @@ -100,6 +100,7 @@ export { classifyEnumMemberType, collectScalarTypeConstructors, flushAuthoringWarnings, + getAuthoringTypeChoice, getAuthoringTypeConstructor, instantiateAuthoringEntityType, instantiateAuthoringFieldPreset, diff --git a/packages/1-framework/1-core/framework-components/src/shared/framework-authoring.ts b/packages/1-framework/1-core/framework-components/src/shared/framework-authoring.ts index 80503ee90b2d..13652e0b41de 100644 --- a/packages/1-framework/1-core/framework-components/src/shared/framework-authoring.ts +++ b/packages/1-framework/1-core/framework-components/src/shared/framework-authoring.ts @@ -799,6 +799,32 @@ export function getAuthoringTypeConstructor( : undefined; } +/** The type choice at `path` in the contributions' type namespace, with a lookup of the type constructors beside it that its rows name, or `undefined` when none is there. */ +export function getAuthoringTypeChoice( + contributions: { readonly type?: AuthoringTypeNamespace } | undefined, + path: readonly string[], +): + | { + readonly descriptor: AuthoringTypeChoiceDescriptor; + readonly constructorNamed: (name: string) => AuthoringTypeConstructorDescriptor | undefined; + } + | undefined { + let level: AuthoringTypeNamespace | undefined = contributions?.type; + for (const segment of path.slice(0, -1)) { + const member: AuthoringTypeNamespaceMember | undefined = + level !== undefined && Object.hasOwn(level, segment) ? level[segment] : undefined; + level = member !== undefined && !('kind' in member) ? member : undefined; + } + const name = path.at(-1); + const member = + level === undefined || name === undefined || !Object.hasOwn(level, name) + ? undefined + : level[name]; + return member !== undefined && level !== undefined && isAuthoringTypeChoiceDescriptor(member) + ? { descriptor: member, constructorNamed: typeConstructorLookup(level) } + : undefined; +} + export function isAuthoringFieldPresetDescriptor( value: AuthoringFieldPresetDescriptor | AuthoringFieldNamespace, ): value is AuthoringFieldPresetDescriptor { diff --git a/packages/2-sql/2-authoring/contract-prisma7/src/interpreter.ts b/packages/2-sql/2-authoring/contract-prisma7/src/interpreter.ts index 01331ffe694c..a06ee6e5ef65 100644 --- a/packages/2-sql/2-authoring/contract-prisma7/src/interpreter.ts +++ b/packages/2-sql/2-authoring/contract-prisma7/src/interpreter.ts @@ -9,9 +9,11 @@ import type { DataTypeSupport, } from '@internal/framework-components/authoring'; import { + getAuthoringTypeChoice, getAuthoringTypeConstructor, instantiateAuthoringEntityType, instantiateAuthoringTypeConstructorFromValues, + resolveAuthoringTypeChoice, } from '@internal/framework-components/authoring'; import type { CodecLookupWithDescriptors } from '@internal/framework-components/codec'; import { @@ -1293,10 +1295,23 @@ function readField(args: ReadFieldArgs): void { } const constructorPath = fieldType.kind === 'written' ? fieldType.call.path : fieldType.path; - const typeConstructor = getAuthoringTypeConstructor( - input.authoringContributions, - constructorPath, - ); + const typeChoice = + fieldType.kind === 'values' + ? getAuthoringTypeChoice(input.authoringContributions, constructorPath) + : undefined; + const chosen = + typeChoice === undefined || fieldType.kind !== 'values' + ? undefined + : resolveAuthoringTypeChoice({ + helperPath: constructorPath.join('.'), + descriptor: typeChoice.descriptor, + args: fieldType.values, + constructorNamed: typeChoice.constructorNamed, + }); + const typeConstructor = + chosen?.descriptor ?? + getAuthoringTypeConstructor(input.authoringContributions, constructorPath); + const typeValues = chosen?.args ?? (fieldType.kind === 'values' ? fieldType.values : []); if (typeConstructor === undefined) { diagnostics.push( prisma7Diagnostic( @@ -1326,7 +1341,7 @@ function readField(args: ReadFieldArgs): void { } else { const instantiated = instantiateAuthoringTypeConstructorFromValues( typeConstructor, - fieldType.values, + typeValues, input.codecLookup.descriptorFor(typeConstructor.output.codecId)?.paramsSchema, ); if (!instantiated.ok && unsupportedArgument !== undefined) { diff --git a/packages/2-sql/2-authoring/contract-prisma7/test/support.ts b/packages/2-sql/2-authoring/contract-prisma7/test/support.ts index b59c8499b89c..23e4528d0ed6 100644 --- a/packages/2-sql/2-authoring/contract-prisma7/test/support.ts +++ b/packages/2-sql/2-authoring/contract-prisma7/test/support.ts @@ -14,6 +14,7 @@ import { PostgresContractSerializer } from '@internal/target-postgres/runtime'; import { dirname, join } from 'pathe'; import { interpretPrisma7Documents } from '../src/interpreter'; import { prisma7Contract } from '../src/provider'; +import type { Prisma7TargetBinding } from '../src/target-binding'; export const fixturesDir = join(dirname(fileURLToPath(import.meta.url)), 'fixtures'); @@ -110,7 +111,10 @@ export function fixtureSchemaText(caseName: string): string { } /** Interprets Prisma 7 schema text with the Postgres composition. */ -export function interpretSchemaTextResult(text: string) { +export function interpretSchemaTextResult( + text: string, + binding: Prisma7TargetBinding = prisma7PostgresBinding, +) { const context = postgresSourceContext(['schema.prisma']); const { document, sources } = parse(text, 'schema.prisma', { grammar: 'prisma-7' }); return interpretPrisma7Documents({ @@ -124,7 +128,7 @@ export function interpretSchemaTextResult(text: string) { ], seedDiagnostics: [], defaultNamespaceId: context.defaultNamespaceId, - binding: prisma7PostgresBinding, + binding, controlMutationDefaults: context.controlMutationDefaults, generatorFunctions: context.generatorFunctions, authoringContributions: context.authoringContributions, diff --git a/packages/2-sql/2-authoring/contract-prisma7/test/type-choice-binding.test.ts b/packages/2-sql/2-authoring/contract-prisma7/test/type-choice-binding.test.ts new file mode 100644 index 000000000000..4e2e2af15cfd --- /dev/null +++ b/packages/2-sql/2-authoring/contract-prisma7/test/type-choice-binding.test.ts @@ -0,0 +1,43 @@ +import type { Contract } from '@internal/contract/types'; +import type { SqlStorage } from '@internal/sql-contract/types'; +import { prisma7PostgresBinding } from '@internal/target-postgres/prisma7-binding'; +import { describe, expect, it } from 'vitest'; +import { interpretSchemaTextResult, serializedTable } from './support'; + +const SCHEMA = `datasource db { + provider = "postgresql" +} + +model Event { + id Int @id + at DateTime +} +`; + +const bindingWithDateTime = (args: readonly unknown[]) => ({ + ...prisma7PostgresBinding, + typeMap: { + ...prisma7PostgresBinding.typeMap, + scalars: { + ...prisma7PostgresBinding.typeMap.scalars, + DateTime: { constructorName: 'DateTime', args }, + }, + }, +}); + +describe('a Prisma 7 type the binding maps to a type choice', () => { + it('becomes the explicit type the values choose', () => { + const result = interpretSchemaTextResult( + SCHEMA, + bindingWithDateTime(['jsDate', 'timestamp', 3]), + ); + if (!result.ok) throw new Error(JSON.stringify(result.failure.diagnostics)); + expect(serializedTable(result.value as Contract, 'Event').columns['at']).toEqual({ + codecId: 'pg/timestamp-date@1', + dataType: 'pg/timestamp', + many: false, + nullable: false, + typeParams: { precision: 3 }, + }); + }); +}); From bcf34d46c62ec437d41843e88498ffb2af2f13ac Mon Sep 17 00:00:00 2001 From: willbot Date: Thu, 22 Oct 2026 20:41:26 +0200 Subject: [PATCH 039/118] A misplaced option argument names the values the option takes Time(3) now reads 3 as the value option; the message says so and lists string and temporal instead of naming a descriptor kind. Co-Authored-By: Claude Opus 5.5 Signed-off-by: willbot Signed-off-by: Will Madden --- .../2-authoring/psl-parser/src/authoring-arguments.ts | 5 ++++- .../psl-parser/test/authoring-arguments.test.ts | 10 ++++++++++ 2 files changed, 14 insertions(+), 1 deletion(-) diff --git a/packages/1-framework/2-authoring/psl-parser/src/authoring-arguments.ts b/packages/1-framework/2-authoring/psl-parser/src/authoring-arguments.ts index 2e5a74e70608..f541144e3ebb 100644 --- a/packages/1-framework/2-authoring/psl-parser/src/authoring-arguments.ts +++ b/packages/1-framework/2-authoring/psl-parser/src/authoring-arguments.ts @@ -376,7 +376,10 @@ function readPslHelperArgument( span: argument.span, entityLabel: input.entityLabel, helperLabel: input.helperLabel, - message: `cannot parse ${argumentLabel} for descriptor kind "${descriptor.kind}".`, + message: + descriptor.kind === 'option' && descriptor.name !== undefined + ? `cannot parse ${argumentLabel}: ${argumentLabel} is ${argument.value.trim()}, but argument "${descriptor.name}" takes one of: ${descriptor.values.join(', ')}.` + : `cannot parse ${argumentLabel} for descriptor kind "${descriptor.kind}".`, }); return INVALID_AUTHORING_ARGUMENT; }; diff --git a/packages/1-framework/2-authoring/psl-parser/test/authoring-arguments.test.ts b/packages/1-framework/2-authoring/psl-parser/test/authoring-arguments.test.ts index 0435d3e93065..b683320385b0 100644 --- a/packages/1-framework/2-authoring/psl-parser/test/authoring-arguments.test.ts +++ b/packages/1-framework/2-authoring/psl-parser/test/authoring-arguments.test.ts @@ -124,6 +124,16 @@ describe('mapPslHelperArgs argument kinds', () => { expect(diagnostics[0]?.message).toMatch(/cannot parse argument #1/); }); + it('names the values a named option takes when a number is written in its place', () => { + const { diagnostics } = callMap( + [positional('3')], + [{ kind: 'option', name: 'value', values: ['string', 'temporal'] }], + ); + expect(diagnostics[0]?.message).toMatch( + /argument #1 is 3, but argument "value" takes one of: string, temporal\.$/, + ); + }); + it('rejects an object for an option-kind argument', () => { const { result, diagnostics } = callMap( [positional('{a: 1}')], From 54ee6f5fbd2f1b0fefcb04139266949a23da65d5 Mon Sep 17 00:00:00 2001 From: willbot Date: Thu, 22 Oct 2026 20:41:30 +0200 Subject: [PATCH 040/118] Postgres tests write the explicit Temporal names, and test the convenience types Tests that meant the Temporal types write TimestamptzTemporal, DateTemporal and TimeTemporal. The scalar view, contract print and the type.* helpers are tested with DateTime, Date, Time and Duration. Co-Authored-By: Claude Opus 5.5 Signed-off-by: willbot Signed-off-by: Will Madden --- .../postgres/test/native-type-parity.test.ts | 18 +++--- .../temporal-field-types.test-d.ts | 62 ++++++++++++++++++- .../postgres/test/scalar-type-parity.test.ts | 10 +-- ...lts-on-existing-tables.integration.test.ts | 4 +- ...lane-commands-without-temporal.e2e.test.ts | 8 +-- .../data-type-defaults.e2e.test.ts | 2 +- ...te-time-default-canonical-form.e2e.test.ts | 8 +-- .../defaults-read-by-codec.e2e.test.ts | 10 +-- .../sql-default-stored-form.e2e.test.ts | 12 ++-- ...ane-libraries-without-temporal.e2e.test.ts | 2 +- ...psl-date-time-defaults.integration.test.ts | 34 +++++----- .../date-time-types-print.integration.test.ts | 14 +++-- ...rated-values-roundtrip.integration.test.ts | 4 +- .../psl-list-roundtrip.integration.test.ts | 2 +- .../default-stored-form.integration.test.ts | 2 +- 15 files changed, 127 insertions(+), 65 deletions(-) diff --git a/packages/3-extensions/postgres/test/native-type-parity.test.ts b/packages/3-extensions/postgres/test/native-type-parity.test.ts index 0969fdc1084b..964d99fec320 100644 --- a/packages/3-extensions/postgres/test/native-type-parity.test.ts +++ b/packages/3-extensions/postgres/test/native-type-parity.test.ts @@ -188,15 +188,15 @@ const parityCases: readonly ParityCase[] = [ expected: { dataType: 'pg/timestamptz', codecId: 'pg/timestamptz-temporal@1', typeParams: {} }, }, { - title: 'Time(3)', - bare: 'Time(3)', - alias: 'Time(3)', + title: 'TimeTemporal(3)', + bare: 'TimeTemporal(3)', + alias: 'TimeTemporal(3)', expected: { dataType: 'pg/time', codecId: 'pg/time-temporal@1', typeParams: { precision: 3 } }, }, { - title: 'Time — bare', - bare: 'Time', - alias: 'Time', + title: 'TimeTemporal — bare', + bare: 'TimeTemporal', + alias: 'TimeTemporal', expected: { dataType: 'pg/time', codecId: 'pg/time-temporal@1', typeParams: {} }, }, // The representation-explicit spellings, which must carry precision exactly as their unsuffixed @@ -294,9 +294,9 @@ const parityCases: readonly ParityCase[] = [ expected: { dataType: 'pg/float4', codecId: 'pg/float4@1', typeParams: {} }, }, { - title: 'Date — bare', - bare: 'Date', - alias: 'Date', + title: 'DateTemporal — bare', + bare: 'DateTemporal', + alias: 'DateTemporal', expected: { dataType: 'pg/date', codecId: 'pg/date-temporal@1', typeParams: {} }, }, ]; diff --git a/packages/3-extensions/postgres/test/no-temporal-global/temporal-field-types.test-d.ts b/packages/3-extensions/postgres/test/no-temporal-global/temporal-field-types.test-d.ts index 0f7e47614d34..948de2ad566f 100644 --- a/packages/3-extensions/postgres/test/no-temporal-global/temporal-field-types.test-d.ts +++ b/packages/3-extensions/postgres/test/no-temporal-global/temporal-field-types.test-d.ts @@ -13,7 +13,6 @@ const contract = defineContract({}, ({ type, field, model }) => ({ local: field.column(type.TimestampTemporal()), day: field.column(type.DateTemporal()), opens: field.column(type.TimeTemporal(0)), - legacyDay: field.column(type.Date()), utcAt: field.column(type.TimestampInstant(6)), utcDate: field.column(type.TimestampJsDate(3)), span: field.column(type.IntervalString(3)), @@ -30,7 +29,6 @@ test('a Temporal field is typed precisely in a project without the global Tempor expectTypeOf().toEqualTypeOf(); expectTypeOf().toEqualTypeOf(); expectTypeOf().toEqualTypeOf(); - expectTypeOf().toEqualTypeOf(); expectTypeOf().not.toBeAny(); expectTypeOf().toEqualTypeOf(); }); @@ -53,3 +51,63 @@ test('a timestamp read as a Date reads a Date, and an interval read as a string expectTypeOf().toEqualTypeOf(); expectTypeOf().toEqualTypeOf(); }); + +const convenience = defineContract({}, ({ type, field, model }) => ({ + models: { + Order: model('Order', { + fields: { + id: field.id.uuidv4String(), + paidAt: field.column(type.DateTime()), + settledAt: field.column(type.DateTime('temporal')), + textAt: field.column(type.DateTime('string', undefined, 6)), + legacyAt: field.column(type.DateTime(undefined, 'timestamp')), + utcAt: field.column(type.DateTime('temporal', 'timestamp')), + shipBy: field.column(type.Date()), + shipOn: field.column(type.Date('temporal')), + opensAt: field.column(type.Time(undefined, undefined, 0)), + opensAtT: field.column(type.Time('temporal')), + localAt: field.column(type.Time(undefined, 'timetz')), + period: field.column(type.Duration()), + parts: field.column(type.Duration('parts')), + textAtExplicit: field.column(type.TimestamptzString(6)), + opensAtExplicit: field.column(type.TimeString(0)), + localAtExplicit: field.column(type.Timetz()), + partsExplicit: field.column(type.IntervalParts()), + }, + }), + }, +})); + +type OrderRow = DefaultModelRow; + +test('a convenience type is typed as the value of the explicit type it chooses', () => { + expectTypeOf().toEqualTypeOf(); + expectTypeOf().toEqualTypeOf(); + expectTypeOf().toEqualTypeOf(); + expectTypeOf().toEqualTypeOf(); + expectTypeOf().toEqualTypeOf(); + expectTypeOf().toEqualTypeOf(); + expectTypeOf().toEqualTypeOf(); + expectTypeOf().toEqualTypeOf(); + expectTypeOf().toEqualTypeOf(); + expectTypeOf().toEqualTypeOf(); + expectTypeOf().toEqualTypeOf(); + expectTypeOf().not.toBeAny(); + expectTypeOf().toEqualTypeOf(); +}); + +test('a convenience type helper refuses a combination with no explicit type', () => { + defineContract({}, ({ type, field, model }) => ({ + models: { + Order: model('Order', { + fields: { + id: field.id.uuidv4String(), + // @ts-expect-error Temporal.PlainTime cannot hold a timetz offset + at: field.column(type.Time('temporal', 'timetz')), + // @ts-expect-error a calendar date is never a JavaScript Date + day: field.column(type.Date('jsDate')), + }, + }), + }, + })); +}); diff --git a/packages/3-extensions/postgres/test/scalar-type-parity.test.ts b/packages/3-extensions/postgres/test/scalar-type-parity.test.ts index c61dc8382f4c..1b682fdfb588 100644 --- a/packages/3-extensions/postgres/test/scalar-type-parity.test.ts +++ b/packages/3-extensions/postgres/test/scalar-type-parity.test.ts @@ -28,7 +28,7 @@ const REPRESENTATIVE_SCHEMA = `model sample { unbounded UnboundedInt ratio Float price Decimal - createdAt DateTime + createdAt TimestamptzTemporal payload Json document Jsonb raw Bytes @@ -68,7 +68,6 @@ describe('postgres scalar types derived from the unified namespace', () => { Float: { codecId: 'pg/float8@1' }, Inet: { codecId: 'pg/inet@1' }, Decimal: { codecId: 'pg/numeric@1' }, - DateTime: { codecId: 'pg/timestamptz-temporal@1' }, Json: { codecId: 'pg/json@1' }, Jsonb: { codecId: 'pg/jsonb@1' }, Bytes: { codecId: 'pg/bytea@1' }, @@ -77,13 +76,11 @@ describe('postgres scalar types derived from the unified namespace', () => { Numeric: { codecId: 'pg/numeric@1' }, TimestampTemporal: { codecId: 'pg/timestamp-temporal@1' }, TimestamptzTemporal: { codecId: 'pg/timestamptz-temporal@1' }, - Time: { codecId: 'pg/time-temporal@1' }, TimeTemporal: { codecId: 'pg/time-temporal@1' }, Timetz: { codecId: 'pg/timetz@1' }, Uuid: { codecId: 'pg/uuid@1' }, SmallInt: { codecId: 'pg/int2@1' }, Real: { codecId: 'pg/float4@1' }, - Date: { codecId: 'pg/date-temporal@1' }, DateTemporal: { codecId: 'pg/date-temporal@1' }, DateString: { codecId: 'pg/date-string@1' }, TimestampString: { codecId: 'pg/timestamp-string@1' }, @@ -96,6 +93,10 @@ describe('postgres scalar types derived from the unified namespace', () => { IntervalString: { codecId: 'pg/interval-string@1' }, TimestampJsDate: { codecId: 'pg/timestamp-date@1' }, TimestampInstant: { codecId: 'pg/timestamp-instant@1' }, + DateTime: { codecId: 'pg/timestamptz-date@1', typeParams: { precision: 3 } }, + Date: { codecId: 'pg/date-string@1' }, + Time: { codecId: 'pg/time-string@1' }, + Duration: { codecId: 'pg/interval-string@1' }, }); }); @@ -112,6 +113,7 @@ describe('postgres scalar types derived from the unified namespace', () => { 'DateTemporal', 'DateTime', 'Decimal', + 'Duration', 'Float', 'Inet', 'Int', diff --git a/test/integration/test/authoring/defaults-on-existing-tables.integration.test.ts b/test/integration/test/authoring/defaults-on-existing-tables.integration.test.ts index 18500a960d2a..d8f94f5d9a0d 100644 --- a/test/integration/test/authoring/defaults-on-existing-tables.integration.test.ts +++ b/test/integration/test/authoring/defaults-on-existing-tables.integration.test.ts @@ -163,7 +163,7 @@ const POSTGRES_BEFORE = ` model Box { id Int @id tags String[] @default(["x"]) - at DateTime @default(timestamptz\`2020-01-01T00:00:00Z\`) + at TimestamptzTemporal @default(timestamptz\`2020-01-01T00:00:00Z\`) hundreds Numeric(5, -2) @default(100) code Char(5) @default("z") token Uuid @default("11111111-1111-1111-1111-111111111111") @@ -174,7 +174,7 @@ const POSTGRES_AFTER = ` model Box { id Int @id tags String[] @default(["a", "b"]) - at DateTime @default(timestamptz\`2024-01-01T01:00:00+01:00\`) + at TimestamptzTemporal @default(timestamptz\`2024-01-01T01:00:00+01:00\`) hundreds Numeric(5, -2) @default(12300) code Char(5) @default("ab") token Uuid @default("A0EEBC99-9C0B-4EF8-BB6D-6BB9BD380A11") diff --git a/test/integration/test/cli-journeys/control-plane-commands-without-temporal.e2e.test.ts b/test/integration/test/cli-journeys/control-plane-commands-without-temporal.e2e.test.ts index 492da66eb7d3..5cb11df307de 100644 --- a/test/integration/test/cli-journeys/control-plane-commands-without-temporal.e2e.test.ts +++ b/test/integration/test/cli-journeys/control-plane-commands-without-temporal.e2e.test.ts @@ -43,7 +43,7 @@ const EMIT_SCHEMA = `// use prisma-8 model Event { id Int @id - createdAt DateTime @default(timestamptz\`2024-01-01T00:00:00Z\`) + createdAt TimestamptzTemporal @default(timestamptz\`2024-01-01T00:00:00Z\`) } `; @@ -51,9 +51,9 @@ const ONE_INSTANT_SCHEMA = `// use prisma-8 model Event { id Int @id - a DateTime @default(timestamptz\`2024-01-01T00:00:00Z\`) - b DateTime @default(timestamptz\`2024-01-01T00:00:00.000Z\`) - c DateTime @default(timestamptz\`2024-01-01T01:00:00+01:00\`) + a TimestamptzTemporal @default(timestamptz\`2024-01-01T00:00:00Z\`) + b TimestamptzTemporal @default(timestamptz\`2024-01-01T00:00:00.000Z\`) + c TimestamptzTemporal @default(timestamptz\`2024-01-01T01:00:00+01:00\`) } `; diff --git a/test/integration/test/cli-journeys/data-type-defaults.e2e.test.ts b/test/integration/test/cli-journeys/data-type-defaults.e2e.test.ts index fe6b2e297ddd..3860f65b86af 100644 --- a/test/integration/test/cli-journeys/data-type-defaults.e2e.test.ts +++ b/test/integration/test/cli-journeys/data-type-defaults.e2e.test.ts @@ -39,7 +39,7 @@ model Account { meta Jsonb @default(json\`{ "plan": "free", "seats": 1 }\`) scores Int[] @default([1, 2]) docs Jsonb[] @default([json\`{}\`, json\`[]\`]) - expires DateTime @default(sql\`(now() + '3 days'::interval)\`) + expires TimestamptzTemporal @default(sql\`(now() + '3 days'::interval)\`) } `; diff --git a/test/integration/test/cli-journeys/date-time-default-canonical-form.e2e.test.ts b/test/integration/test/cli-journeys/date-time-default-canonical-form.e2e.test.ts index 1b602e624422..c8b056b91a34 100644 --- a/test/integration/test/cli-journeys/date-time-default-canonical-form.e2e.test.ts +++ b/test/integration/test/cli-journeys/date-time-default-canonical-form.e2e.test.ts @@ -178,8 +178,8 @@ withTempDir(({ createTempDir }) => { model Created { id Int @id - instant DateTime @default(timestamptz\`0044-03-15 00:00:00+00 BC\`) - day Date @default(date\`0044-03-15 BC\`) + instant TimestamptzTemporal @default(timestamptz\`0044-03-15 00:00:00+00 BC\`) + day DateTemporal @default(date\`0044-03-15 BC\`) local TimestampTemporal @default(timestamp\`-000043-03-15T12:00:00\`) @@map("created") @@ -187,8 +187,8 @@ model Created { `; const altered = (defaults: boolean) => `model Altered { id Int @id - instant DateTime ${defaults ? '@default(timestamptz`-000043-03-15T00:00:00Z`)' : ''} - day Date ${defaults ? '@default(date`-000043-03-15`)' : ''} + instant TimestamptzTemporal ${defaults ? '@default(timestamptz`-000043-03-15T00:00:00Z`)' : ''} + day DateTemporal ${defaults ? '@default(date`-000043-03-15`)' : ''} local TimestampTemporal ${defaults ? '@default(timestamp`0044-03-15 12:00:00 BC`)' : ''} @@map("altered") diff --git a/test/integration/test/cli-journeys/defaults-read-by-codec.e2e.test.ts b/test/integration/test/cli-journeys/defaults-read-by-codec.e2e.test.ts index 2d7bfb98127e..1aeaf4758d1a 100644 --- a/test/integration/test/cli-journeys/defaults-read-by-codec.e2e.test.ts +++ b/test/integration/test/cli-journeys/defaults-read-by-codec.e2e.test.ts @@ -37,7 +37,7 @@ model Sample { float8 Float @default(1.5) decimal Decimal @default(1.50) money Numeric(10, 2) @default(3.10) - instant DateTime @default(${tagged('timestamptz', '2024-01-01T00:00:00Z')}) + instant TimestamptzTemporal @default(${tagged('timestamptz', '2024-01-01T00:00:00Z')}) doc Json @default(${tagged('json', '{"a": 1}')}) docb Jsonb @default(${tagged('json', '{"b": [1, 2]}')}) bytes Bytes @default(${tagged('bytea', '\\x6869')}) @@ -46,13 +46,13 @@ model Sample { initials Char(3) @default("ab") local TimestampTemporal(3) @default(${tagged('timestamp', '2024-01-01T12:34:56.5')}) precise TimestamptzTemporal(6) @default(${tagged('timestamptz', '2024-01-01T00:00:00.123456Z')}) - clock Time @default(${tagged('time', '12:34:56')}) + clock TimeTemporal @default(${tagged('time', '12:34:56')}) zoned Timetz @default(${tagged('timetz', '12:34:56+02:00')}) token Uuid @default("a0eebc99-9c0b-4ef8-bb6d-6bb9bd380a11") host Inet @default("10.0.0.1/32") small SmallInt @default(7) ratio Real @default(2.5) - day Date @default(${tagged('date', '2024-01-01')}) + day DateTemporal @default(${tagged('date', '2024-01-01')}) dayText DateString @default(${tagged('date', '2024-01-01')}) localText TimestampString @default(${tagged('timestamp', '2024-01-01T00:00:00')}) jsDate TimestamptzJsDate @default(${tagged('timestamptz', '2024-01-01T00:00:00.5Z')}) @@ -72,8 +72,8 @@ model Sample { model Edge { id Int @id - dayBc Date @default(${tagged('date', '0044-03-15 BC')}) - dayFar Date @default(${tagged('date', '+012026-01-02')}) + dayBc DateTemporal @default(${tagged('date', '0044-03-15 BC')}) + dayFar DateTemporal @default(${tagged('date', '+012026-01-02')}) localBc TimestampTemporal(3) @default(${tagged('timestamp', '0044-03-15 12:00:00 BC')}) instantFar TimestamptzTemporal(6) @default(${tagged('timestamptz', '+012026-01-02T03:04:05Z')}) forever TimestamptzString @default(${tagged('timestamptz', 'infinity')}) diff --git a/test/integration/test/cli-journeys/sql-default-stored-form.e2e.test.ts b/test/integration/test/cli-journeys/sql-default-stored-form.e2e.test.ts index b6376fa3bbbe..5abae4545cca 100644 --- a/test/integration/test/cli-journeys/sql-default-stored-form.e2e.test.ts +++ b/test/integration/test/cli-journeys/sql-default-stored-form.e2e.test.ts @@ -71,7 +71,7 @@ function writeContract(ctx: JourneyContext, text: string): void { const cases = [ { name: 'an interval', - type: 'DateTime', + type: 'TimestamptzTemporal', written: "sql`now() - interval '1 day'`", stored: "(now() - '1 day'::interval)", literal: "sql`(now() - '1 day'::interval)`", @@ -158,7 +158,7 @@ withTempDir(({ createTempDir }) => { describe('two pending migrations, the first of which writes the default', () => { const db = useDevDatabase(); - const withTag = (literal: string) => `${schema('DateTime', literal)} + const withTag = (literal: string) => `${schema('TimestamptzTemporal', literal)} model Tag { id Int @id } @@ -172,7 +172,7 @@ model Tag { createTempDir, contractMode: 'psl', }); - writeContract(ctx, schema('DateTime', "sql`now() - interval '1 day'`")); + writeContract(ctx, schema('TimestamptzTemporal', "sql`now() - interval '1 day'`")); expect((await runContractEmit(ctx)).exitCode).toBe(0); expect((await planMigrationAndSelfEmit(ctx, ['--name', 'first'])).exitCode).toBe(0); const firstHash = storageHash(ctx); @@ -230,7 +230,7 @@ model Tag { createTempDir, contractMode: 'psl', }); - writeContract(ctx, schema('DateTime', "sql`now() - interval '1 day'`")); + writeContract(ctx, schema('TimestamptzTemporal', "sql`now() - interval '1 day'`")); expect((await runContractEmit(ctx)).exitCode).toBe(0); const failed = await runDbInit(ctx); const shown = shownOutput(failed); @@ -254,7 +254,7 @@ model Tag { createTempDir, contractMode: 'psl', }); - writeContract(ctx, schema('DateTime', "sql`now() - interval '1 day'`")); + writeContract(ctx, schema('TimestamptzTemporal', "sql`now() - interval '1 day'`")); expect((await runContractEmit(ctx)).exitCode).toBe(0); const failed = await runDbUpdate(ctx); const shown = shownOutput(failed); @@ -279,7 +279,7 @@ model Tag { createTempDir, contractMode: 'psl', }); - writeContract(ctx, schema('DateTime', "sql`(now() + '1 day'::interval)`")); + writeContract(ctx, schema('TimestamptzTemporal', "sql`(now() + '1 day'::interval)`")); expect((await runContractEmit(ctx)).exitCode).toBe(0); expect((await planMigrationAndSelfEmit(ctx, ['--name', 'initial'])).exitCode).toBe(0); expect((await runMigrate(ctx)).exitCode).toBe(0); diff --git a/test/integration/test/control-plane-libraries-without-temporal.e2e.test.ts b/test/integration/test/control-plane-libraries-without-temporal.e2e.test.ts index ddeb3f4b3668..b83f1a85afb2 100644 --- a/test/integration/test/control-plane-libraries-without-temporal.e2e.test.ts +++ b/test/integration/test/control-plane-libraries-without-temporal.e2e.test.ts @@ -30,7 +30,7 @@ const SCHEMA = `// use prisma-8 model Event { id Int @id - createdAt DateTime @default(timestamptz\`2024-01-01T00:00:00Z\`) + createdAt TimestamptzTemporal @default(timestamptz\`2024-01-01T00:00:00Z\`) @@map("event") } diff --git a/test/integration/test/date-time-defaults/psl-date-time-defaults.integration.test.ts b/test/integration/test/date-time-defaults/psl-date-time-defaults.integration.test.ts index e3647df39d7d..8af9ed90e38c 100644 --- a/test/integration/test/date-time-defaults/psl-date-time-defaults.integration.test.ts +++ b/test/integration/test/date-time-defaults/psl-date-time-defaults.integration.test.ts @@ -18,10 +18,10 @@ const literal = (value: unknown) => ({ kind: 'literal', value }); /** The tag that writes a value of each Postgres column type these tests use. */ const TAG_OF: Readonly> = { - DateTime: 'timestamptz', + TimestamptzTemporal: 'timestamptz', TimestampTemporal: 'timestamp', - Date: 'date', - Time: 'time', + DateTemporal: 'date', + TimeTemporal: 'time', Timetz: 'timetz', }; @@ -31,9 +31,9 @@ describe('a PSL date or time default is stored in its data type canonical form', await postgresDefaults( `model Event { id Int @id - a DateTime @default(timestamptz\`2024-01-01T00:00:00Z\`) - b DateTime @default(timestamptz\`2024-01-01T00:00:00.000Z\`) - c DateTime @default(timestamptz\`2024-01-01T01:00:00+01:00\`) + a TimestamptzTemporal @default(timestamptz\`2024-01-01T00:00:00Z\`) + b TimestamptzTemporal @default(timestamptz\`2024-01-01T00:00:00.000Z\`) + c TimestamptzTemporal @default(timestamptz\`2024-01-01T01:00:00+01:00\`) }`, ['a', 'b', 'c'], ), @@ -53,9 +53,9 @@ describe('a PSL date or time default is stored in its data type canonical form', jsDate TimestamptzJsDate @default(timestamptz\`2024-01-01 00:00:00.500+00\`) local TimestampTemporal @default(timestamp\`2024-01-01 12:34:56.500\`) localText TimestampString @default(timestamp\`2024-01-01 12:34:56\`) - day Date @default(date\`0044-03-15 BC\`) + day DateTemporal @default(date\`0044-03-15 BC\`) dayText DateString @default(date\`12026-01-02\`) - clock Time @default(time\`12:34\`) + clock TimeTemporal @default(time\`12:34\`) zoned Timetz @default(timetz\`12:34:56+02\`) history TimestampTemporal(3)[] @default([timestamp\`2024-01-01 00:00:00\`, timestamp\`2024-06-30 12:34:56.789000\`]) }`, @@ -92,17 +92,17 @@ describe('a PSL date or time default is stored in its data type canonical form', 'pg/timestamp holds no UTC offset, but "2024-01-01T00:00:00Z" has one. Leave it out, as in "2024-01-01T12:34:56".', ], [ - 'DateTime', + 'TimestamptzTemporal', '2024-01-01 00:00:00', 'pg/timestamptz needs a UTC offset, but "2024-01-01 00:00:00" has none. Add Z for UTC or an offset such as +02:00, as in "2024-01-01T12:34:56Z".', ], [ - 'Date', + 'DateTemporal', '2024-02-30', '"2024-02-30" is not a date that exists. Write a real date, as in "2024-01-01".', ], [ - 'Time', + 'TimeTemporal', '25:00:00', '"25:00:00" is not a time of day that exists: hours run from 00 to 23, and minutes and seconds from 00 to 59. Write one, as in "12:34:56".', ], @@ -112,7 +112,7 @@ describe('a PSL date or time default is stored in its data type canonical form', 'pg/timetz needs a UTC offset, but "12:34:56" has none. Add Z for UTC or an offset such as +02:00, as in "12:34:56+02:00".', ], [ - 'DateTime', + 'TimestamptzTemporal', '2024-01-01T00:00:00.1234567Z', '"2024-01-01T00:00:00.1234567Z" has 7 digits after the decimal point, but pg/timestamptz holds microseconds, so at most 6. Round it, as in "2024-01-01T12:34:56.123456Z".', ], @@ -157,10 +157,10 @@ describe('a PSL date or time default is stored in its data type canonical form', describe('a quoted string on a Postgres date, time, interval or bytes column', () => { it.each([ - ['DateTime', '2024-01-01T00:00:00Z', 'timestamptz'], + ['TimestamptzTemporal', '2024-01-01T00:00:00Z', 'timestamptz'], ['TimestampTemporal', '2024-01-01 12:34:56', 'timestamp'], - ['Date', '2024-01-01', 'date'], - ['Time', '12:34:56', 'time'], + ['DateTemporal', '2024-01-01', 'date'], + ['TimeTemporal', '12:34:56', 'time'], ['Timetz', '12:34:56+02:00', 'timetz'], ['IntervalParts', 'P1D', 'interval'], ['Bytes', '\\x68656c6c6f', 'bytea'], @@ -192,9 +192,9 @@ describe('a quoted string on a Postgres date, time, interval or bytes column', ( describe('infinity on a column whose codec holds a Temporal value or a Date', () => { it.each([ - ['DateTime', 'timestamptz', 'pg/timestamptz-temporal@1', 'pg/timestamptz-string@1'], + ['TimestamptzTemporal', 'timestamptz', 'pg/timestamptz-temporal@1', 'pg/timestamptz-string@1'], ['TimestamptzJsDate', 'timestamptz', 'pg/timestamptz-date@1', 'pg/timestamptz-string@1'], - ['Date', 'date', 'pg/date-temporal@1', 'pg/date-string@1'], + ['DateTemporal', 'date', 'pg/date-temporal@1', 'pg/date-string@1'], ])( 'is refused on a %s column, naming the text codec that holds it', async (type, tag, codecId, textCodecId) => { diff --git a/test/integration/test/psl-print/date-time-types-print.integration.test.ts b/test/integration/test/psl-print/date-time-types-print.integration.test.ts index 15c65f9b93a1..5c591e943f23 100644 --- a/test/integration/test/psl-print/date-time-types-print.integration.test.ts +++ b/test/integration/test/psl-print/date-time-types-print.integration.test.ts @@ -18,12 +18,13 @@ model Event { span IntervalString(3) day Date clock Time - clock3 Time(3) + clock3 Time(precision: 3) zonedText TimestamptzString(3) localText TimestampString(3) dayText DateString clockText TimeString(3) offset Timetz + lasts Duration } `; @@ -39,12 +40,12 @@ function fieldTypes(printed: string): Record { } describe('contract print spells each date and time column', () => { - it('as the type contract infer writes, or else as the explicit type that reads the column back', async () => { + it('as the type contract infer writes, or else as the explicit type that reads the column back, a convenience type included', async () => { const contract = await readPsl(SCHEMA); expect(fieldTypes(printContract(contract).text)).toEqual({ id: 'Int', - instant: 'TimestamptzTemporal', + instant: 'TimestamptzJsDate(3)', zoned: 'TimestamptzTemporal', zoned3: 'TimestamptzTemporal(3)', local: 'TimestampTemporal', @@ -55,14 +56,15 @@ describe('contract print spells each date and time column', () => { clockT: 'TimeTemporal(0)', period: 'IntervalParts', span: 'IntervalString(3)', - day: 'DateTemporal', - clock: 'TimeTemporal', - clock3: 'TimeTemporal(3)', + day: 'DateString', + clock: 'TimeString', + clock3: 'TimeString(3)', zonedText: 'TimestamptzString(3)', localText: 'TimestampString(3)', dayText: 'DateString', clockText: 'TimeString(3)', offset: 'Timetz', + lasts: 'IntervalString', }); expect(await printAndReadBack(contract)).toEqual(contract); }); diff --git a/test/integration/test/psl-print/generated-values-roundtrip.integration.test.ts b/test/integration/test/psl-print/generated-values-roundtrip.integration.test.ts index c6e32f32c0ef..fab4edf3fad5 100644 --- a/test/integration/test/psl-print/generated-values-roundtrip.integration.test.ts +++ b/test/integration/test/psl-print/generated-values-roundtrip.integration.test.ts @@ -130,8 +130,8 @@ describe('every generated value the stack reads prints as PSL that reads back', model Event { id String @id @onCreate(uuid(7)) code VarChar(32) @onCreate(nanoid()) - seenAt Time(3) @onUpdate(now()) - createdAt DateTime @default(now()) @onCreate(now()) + seenAt TimeTemporal(3) @onUpdate(now()) + createdAt TimestamptzTemporal @default(now()) @onCreate(now()) } `); const fieldLines = printContract(authored) diff --git a/test/integration/test/scalar-lists/psl-list-roundtrip.integration.test.ts b/test/integration/test/scalar-lists/psl-list-roundtrip.integration.test.ts index b1ddc9bf2d63..c5e0c4266f02 100644 --- a/test/integration/test/scalar-lists/psl-list-roundtrip.integration.test.ts +++ b/test/integration/test/scalar-lists/psl-list-roundtrip.integration.test.ts @@ -178,7 +178,7 @@ describe('PSL scalar-list end-to-end', { concurrent: false }, () => { model Reading { id Int @id - dates DateTime[] + dates TimestamptzTemporal[] payloads Bytes[] amounts Amount[] }`); diff --git a/test/integration/test/sql-expression-defaults/default-stored-form.integration.test.ts b/test/integration/test/sql-expression-defaults/default-stored-form.integration.test.ts index 3a6dd1e2c7d7..deba80697b4a 100644 --- a/test/integration/test/sql-expression-defaults/default-stored-form.integration.test.ts +++ b/test/integration/test/sql-expression-defaults/default-stored-form.integration.test.ts @@ -29,7 +29,7 @@ const EXPLANATION = `The database reads this default back as sql\`${STORED}\`; t async function contractWithDefault(expression: string): Promise> { const authored = await authorSqlContractFromPsl(`model Event { id Int @id - at DateTime @default(sql\`${expression}\`) + at TimestamptzTemporal @default(sql\`${expression}\`) }`); expect(authored.diagnostics).toEqual([]); if (authored.contract === undefined) throw new Error('authoring produced no contract'); From 48e82df372fc481b08059936800fcfc0fdfb4e2b Mon Sep 17 00:00:00 2001 From: willbot Date: Thu, 22 Oct 2026 20:42:38 +0200 Subject: [PATCH 041/118] Journey: the convenience date and time types work with no global Temporal Co-Authored-By: Claude Opus 5.5 Signed-off-by: willbot Signed-off-by: Will Madden --- ...te-time-types-without-temporal.e2e.test.ts | 161 ++++++++++++++++++ 1 file changed, 161 insertions(+) create mode 100644 test/integration/test/cli-journeys/convenience-date-time-types-without-temporal.e2e.test.ts diff --git a/test/integration/test/cli-journeys/convenience-date-time-types-without-temporal.e2e.test.ts b/test/integration/test/cli-journeys/convenience-date-time-types-without-temporal.e2e.test.ts new file mode 100644 index 000000000000..40566e677378 --- /dev/null +++ b/test/integration/test/cli-journeys/convenience-date-time-types-without-temporal.e2e.test.ts @@ -0,0 +1,161 @@ +/** + * Journey: a schema written with the convenience date and time types `DateTime`, `Date`, `Time` and `Duration`, at their defaults and with the choices that need no `Temporal`, emits, creates its table, and is written and read by an application that runs with no global `Temporal`. Each field reads the kind of value its convenience type chooses: a JavaScript `Date` for `DateTime`, a string for `Date`, `Time` and `Duration`, and the `{ months, days, micros }` object for `Duration(value: parts)`. The client generates `updatedAt` with `now()` in that process. + */ +import { writeFileSync } from 'node:fs'; +import { join } from 'pathe'; +import stripAnsi from 'strip-ansi'; +import { describe, expect, it } from 'vitest'; +import { withTempDir } from '../utils/cli-test-helpers'; +import { emittedColumns } from '../utils/emitted-columns'; +import { + type EngineCommandResult, + runContractEmit, + runDbInit, + setupJourney, + timeouts, + useDevDatabase, +} from '../utils/journey-test-helpers'; +import { + childOutput, + NO_GLOBAL_TEMPORAL, + runNodeWithoutTemporal, +} from '../utils/node-without-temporal'; + +const SCHEMA = `// use prisma-8 + +model Order { + id Int @id + paidAt DateTime + legacyAt DateTime(column: timestamp) + textAt DateTime(value: string, precision: 6) + shipBy Date + opensAt Time(precision: 0) + localAt Time(column: timetz) + period Duration + parts Duration(value: parts) + settledAt DateTime(value: temporal)? + updatedAt DateTime @onCreate(now()) @onUpdate(now()) + + @@map("order") +} +`; + +const APP_SCRIPT = ` +import { readFileSync } from 'node:fs'; + +const { default: postgres } = await import('@prisma/orm-postgres/runtime'); +const { contractFromJson } = await import('@prisma/orm-postgres/static'); +const db = postgres({ + contract: contractFromJson(JSON.parse(readFileSync('contract.json', 'utf8'))), + url: process.env.JOURNEY_DB_URL, +}); +const described = (row) => + Object.fromEntries( + Object.entries(row).map(([name, value]) => [ + name, + value instanceof Date + ? { kind: 'Date', value: value.toISOString() } + : value !== null && typeof value === 'object' + ? { + kind: 'object', + value: Object.fromEntries(Object.entries(value).map(([k, v]) => [k, String(v)])), + } + : { kind: value === null ? 'null' : typeof value, value }, + ]), + ); +try { + const orders = db.orm.public.Order; + await orders.create({ + id: 1, + paidAt: new Date('2024-02-29T10:00:00.123Z'), + legacyAt: new Date('2024-02-29T10:00:00.5Z'), + textAt: '2024-02-29 10:00:00.123456+00', + shipBy: '2024-02-29', + opensAt: '09:30:00', + localAt: '09:30:00+02', + period: 'P1M', + parts: { months: 1, days: 2, micros: 3n }, + settledAt: null, + }); + const read = await orders.where({ id: 1 }).first(); + process.stdout.write(JSON.stringify(described(read))); +} finally { + await db.close(); +} +`; + +const COLUMN_CODECS = { + paidAt: 'pg/timestamptz-date@1', + legacyAt: 'pg/timestamp-date@1', + textAt: 'pg/timestamptz-string@1', + shipBy: 'pg/date-string@1', + opensAt: 'pg/time-string@1', + localAt: 'pg/timetz@1', + period: 'pg/interval-string@1', + parts: 'pg/interval@1', + settledAt: 'pg/timestamptz-temporal@1', + updatedAt: 'pg/timestamptz-date@1', +}; + +function output(run: EngineCommandResult): string { + return `${stripAnsi(run.stderr)}\n${stripAnsi(run.stdout)}`; +} + +withTempDir(({ createTempDir }) => { + describe('Journey: the convenience date and time types in an application with no Temporal', () => { + const db = useDevDatabase(); + + it( + 'emits each field as the explicit type it chooses, and reads the value that type gives', + async () => { + const ctx = setupJourney({ + connectionString: db.connectionString, + createTempDir, + contractMode: 'psl', + }); + writeFileSync(join(ctx.testDir, 'contract.prisma'), SCHEMA, 'utf-8'); + const emit = await runContractEmit(ctx); + expect(emit.exitCode, `contract emit\n${output(emit)}`).toBe(0); + const columns = emittedColumns(ctx.testDir); + expect( + Object.fromEntries( + Object.keys(COLUMN_CODECS).map((name) => [name, columns[name]?.codecId]), + ), + ).toEqual(COLUMN_CODECS); + const init = await runDbInit(ctx); + expect(init.exitCode, `db init\n${output(init)}`).toBe(0); + + const script = join(ctx.testDir, 'app.mjs'); + writeFileSync(script, APP_SCRIPT, 'utf-8'); + const before = Date.now(); + const run = await runNodeWithoutTemporal([script], { + cwd: ctx.testDir, + env: { JOURNEY_DB_URL: db.connectionString }, + }); + const after = Date.now(); + expect(run, childOutput(run)).toMatchObject({ exitCode: 0, ...NO_GLOBAL_TEMPORAL }); + + const read = JSON.parse(run.stdout); + expect(read).toEqual({ + id: { kind: 'number', value: 1 }, + paidAt: { kind: 'Date', value: '2024-02-29T10:00:00.123Z' }, + legacyAt: { kind: 'Date', value: '2024-02-29T10:00:00.500Z' }, + textAt: { kind: 'string', value: read.textAt.value }, + shipBy: { kind: 'string', value: '2024-02-29' }, + opensAt: { kind: 'string', value: '09:30:00' }, + localAt: { kind: 'string', value: read.localAt.value }, + period: { kind: 'string', value: 'P1M' }, + parts: { kind: 'object', value: { months: '1', days: '2', micros: '3' } }, + settledAt: { kind: 'null', value: null }, + updatedAt: { kind: 'Date', value: read.updatedAt.value }, + }); + expect(read.textAt.value).toMatch(/^2024-02-29[ T]10:00:00\.123456/); + expect(read.localAt.value).toMatch(/^09:30:00\+02/); + const updatedAt = new Date(read.updatedAt.value).getTime(); + expect(updatedAt).toBeGreaterThanOrEqual(before - 1_000); + expect(updatedAt).toBeLessThanOrEqual(after + 1_000); + }, + timeouts.spinUpPpgDev * 2, + ); + }); +}); From c3606eaab17ec528426c4ef9fc5dca37102d071c Mon Sep 17 00:00:00 2001 From: willbot Date: Thu, 22 Oct 2026 20:44:14 +0200 Subject: [PATCH 042/118] Upgrade instruction: DateTime, Date and Time are convenience types on PostgreSQL Co-Authored-By: Claude Opus 5.5 Signed-off-by: willbot Signed-off-by: Will Madden --- .../detection.test.ts | 70 +++++++++++++++++++ .../app/instructions.md | 61 ++++++++++++++++ 2 files changed, 131 insertions(+) create mode 100644 test/integration/test/upgrade-instructions/convenience-date-time-types/detection.test.ts create mode 100644 upgrade-instructions/pending/convenience-date-time-types/app/instructions.md diff --git a/test/integration/test/upgrade-instructions/convenience-date-time-types/detection.test.ts b/test/integration/test/upgrade-instructions/convenience-date-time-types/detection.test.ts new file mode 100644 index 000000000000..bb0c743b3852 --- /dev/null +++ b/test/integration/test/upgrade-instructions/convenience-date-time-types/detection.test.ts @@ -0,0 +1,70 @@ +import { readFileSync } from 'node:fs'; +import { fileURLToPath } from 'node:url'; +import { dirname, join } from 'pathe'; +import { describe, expect, it } from 'vitest'; + +const here = dirname(fileURLToPath(import.meta.url)); +const repoRoot = join(here, '../../../../..'); + +function patternsOf(id: string): readonly RegExp[] { + const guide = readFileSync( + join(repoRoot, 'upgrade-instructions/pending/convenience-date-time-types/app/instructions.md'), + 'utf-8', + ); + const block = guide.split(/\n {2}- id: /).find((entry) => entry.startsWith(`${id}\n`)) ?? ''; + return [...block.matchAll(/^ {8}- '(.*)'$/gm)].map( + ([, pattern]) => new RegExp((pattern ?? '').replaceAll("''", "'")), + ); +} + +const matches = (id: string, content: string) => + patternsOf(id).some((pattern) => pattern.test(content)); + +describe('the convenience date and time type detection', () => { + it('matches a Prisma 8 field or alias typed DateTime, Date or Time, and nothing else', () => { + const psl = (body: string) => + matches('psl-date-time-types-are-convenience-types', `// use prisma-8\n\n${body}\n`); + expect({ + dateTime: psl('model A {\n at DateTime\n}'), + date: psl('model A {\n day Date?\n}'), + time: psl('model A {\n opens Time(3)\n}'), + list: psl('model A {\n days Date[]\n}'), + alias: psl('types {\n Stamp = DateTime @onCreate(now())\n}'), + explicit: psl('model A {\n at TimestamptzTemporal\n day DateTemporal\n}'), + stringType: psl('model A {\n day DateString\n}'), + comment: psl('// a DateTime field\nmodel A {\n id Int @id\n}'), + modelName: psl('model Date {\n id Int @id\n}'), + prisma7: psl( + 'datasource db {\n provider = "postgresql"\n}\nmodel A {\n at DateTime @db.Timestamptz(3)\n}', + ), + }).toEqual({ + dateTime: true, + date: true, + time: true, + list: true, + alias: true, + explicit: false, + stringType: false, + comment: false, + modelName: false, + prisma7: false, + }); + }); + + it('matches the TypeScript helpers of the three names only', () => { + const ts = (content: string) => matches('ts-date-time-helpers-are-convenience-types', content); + expect({ + dateTime: ts('field.column(type.DateTime())'), + date: ts('field.column(type.Date())'), + time: ts('field.column(type.Time(3))'), + explicit: ts('field.column(type.TimeTemporal(3))'), + jsDate: ts('const at = new Date()'), + }).toEqual({ + dateTime: true, + date: true, + time: true, + explicit: false, + jsDate: false, + }); + }); +}); diff --git a/upgrade-instructions/pending/convenience-date-time-types/app/instructions.md b/upgrade-instructions/pending/convenience-date-time-types/app/instructions.md new file mode 100644 index 000000000000..a31216f53b61 --- /dev/null +++ b/upgrade-instructions/pending/convenience-date-time-types/app/instructions.md @@ -0,0 +1,61 @@ +--- +changes: + - id: psl-date-time-types-are-convenience-types + summary: | + In a Prisma 8 schema on PostgreSQL, `DateTime`, `Date` and `Time` are now convenience types that choose an explicit type from their arguments, and their defaults read different values: `DateTime` is a `timestamptz(3)` column read as a JavaScript `Date`, and `Date` and `Time` are read as strings. To keep what a field reads today, and its contract, rename `DateTime` to `TimestamptzTemporal`, `Date` to `DateTemporal` and `Time` to `TimeTemporal`, keeping the arguments: `Time(3)` becomes `TimeTemporal(3)`. A Prisma 7 schema, which has a `datasource` block, and SQLite and MongoDB schemas are not affected. + detection: + glob: "**/*.prisma" + matches: + - '(? Date: Thu, 22 Oct 2026 20:47:21 +0200 Subject: [PATCH 043/118] Test that type.* and PSL give the same column for each convenience type Co-Authored-By: Claude Opus 5.5 Signed-off-by: willbot Signed-off-by: Will Madden --- .../parity/ts-psl-parity.real-packs.test.ts | 53 ++++++++++++++++++- ...arked-types-round-trip.integration.test.ts | 3 +- 2 files changed, 53 insertions(+), 3 deletions(-) diff --git a/test/integration/test/authoring/parity/ts-psl-parity.real-packs.test.ts b/test/integration/test/authoring/parity/ts-psl-parity.real-packs.test.ts index 88deffca187a..1509144e62ce 100644 --- a/test/integration/test/authoring/parity/ts-psl-parity.real-packs.test.ts +++ b/test/integration/test/authoring/parity/ts-psl-parity.real-packs.test.ts @@ -3,7 +3,10 @@ import sqliteAdapter from '@internal/adapter-sqlite/control'; import pgvectorControl from '@internal/extension-pgvector/control'; import pgvectorPack from '@internal/extension-pgvector/pack'; import sqlFamilyControl from '@internal/family-sql/control'; -import type { AuthoringTypeConstructorDescriptor } from '@internal/framework-components/authoring'; +import { + type AuthoringTypeNamespace, + isAuthoringTypeConstructorDescriptor, +} from '@internal/framework-components/authoring'; import { createControlStack } from '@internal/framework-components/control'; import { defineContract, @@ -70,9 +73,10 @@ const SAMPLE_ARGUMENTS: Readonly> = { length: 10, precisi /** Every PSL type constructor of a namespace, called with no arguments and, when it takes any, with a sample value for each. */ function constructorCalls( - constructors: Readonly>, + constructors: AuthoringTypeNamespace, ): readonly { readonly column: string; readonly name: string; readonly args: readonly number[] }[] { return Object.entries(constructors).flatMap(([name, descriptor]) => { + if (!isAuthoringTypeConstructorDescriptor(descriptor)) return []; const declared = descriptor.args ?? []; const sampled = declared.map((arg) => { const value = arg.name === undefined ? undefined : SAMPLE_ARGUMENTS[arg.name]; @@ -349,6 +353,51 @@ ${calls.map((call) => ` ${call.column} ${pslFieldType(call)}`).join('\n')} expect(interpreted.value).toEqual(tsContract); }); + it('give the same column for each convenience date and time type and the same arguments', () => { + const tsContract = defineContract( + { extensions: { pgvector: pgvectorPack } }, + ({ type, field, model }) => ({ + models: { + Sample: model('Sample', { + fields: { + id: field.column(int4Column).id(), + paid: field.column(type.DateTime()), + settled: field.column(type.DateTime('temporal')), + legacy: field.column(type.DateTime('jsDate', 'timestamp', 6)), + text: field.column(type.DateTime('string', 'timestamp')), + ship: field.column(type.Date()), + shipT: field.column(type.Date('temporal')), + opens: field.column(type.Time(undefined, undefined, 0)), + local: field.column(type.Time('string', 'timetz')), + period: field.column(type.Duration(undefined, undefined, 3)), + parts: field.column(type.Duration('parts')), + }, + }).sql({ table: 'Sample' }), + }, + }), + ); + + const interpreted = interpretWithRealPacks(`model Sample { + id Int @id + paid DateTime + settled DateTime(value: temporal) + legacy DateTime(jsDate, timestamp, 6) + text DateTime(value: string, column: timestamp) + ship Date + shipT Date(temporal) + opens Time(precision: 0) + local Time(column: timetz) + period Duration(precision: 3) + parts Duration(value: parts) +} +`); + + expect(interpreted.ok).toBe(true); + if (!interpreted.ok) return; + + expect(interpreted.value).toEqual(tsContract); + }); + it('give the same column for every SQLite constructor and the same arguments', () => { const calls = constructorCalls(sqliteScalarTypeConstructors); const tsContract = defineSqliteContract({}, ({ type, field, model }) => ({ diff --git a/test/integration/test/psl-infer/marked-types-round-trip.integration.test.ts b/test/integration/test/psl-infer/marked-types-round-trip.integration.test.ts index 1df6f154073a..e0fb1de09ecc 100644 --- a/test/integration/test/psl-infer/marked-types-round-trip.integration.test.ts +++ b/test/integration/test/psl-infer/marked-types-round-trip.integration.test.ts @@ -10,6 +10,7 @@ import sql, { INIT_ADDITIVE_POLICY } from '@internal/family-sql/control'; import { type AuthoringTypeNamespace, isAuthoringTypeAliasDescriptor, + isAuthoringTypeChoiceDescriptor, isAuthoringTypeConstructorDescriptor, } from '@internal/framework-components/authoring'; import { dataTypeParams } from '@internal/framework-components/codec'; @@ -38,7 +39,7 @@ const familyInstance = sql.create(controlStack); /** The data types whose constructor the stack marks `inferred`, found by walking every constructor. */ function dataTypesWithMarkedConstructor(namespace: AuthoringTypeNamespace): readonly string[] { return Object.values(namespace).flatMap((value) => { - if (isAuthoringTypeAliasDescriptor(value)) return []; + if (isAuthoringTypeAliasDescriptor(value) || isAuthoringTypeChoiceDescriptor(value)) return []; if (!isAuthoringTypeConstructorDescriptor(value)) return dataTypesWithMarkedConstructor(value); if (value.inferred !== true) return []; const dataType = controlStack.codecLookup.descriptorFor(value.output.codecId)?.dataType; From 0117077619cc89386fe898d668b0c91ae4fc6cae Mon Sep 17 00:00:00 2001 From: willbot Date: Thu, 22 Oct 2026 20:50:00 +0200 Subject: [PATCH 044/118] Hover shows a type choice's choices and the explicit type each becomes Option values show as PSL writes them, bare. Completion inserts a type choice's bare name, which writes its default choice. Co-Authored-By: Claude Opus 5.5 Signed-off-by: willbot Signed-off-by: Will Madden --- .../3-tooling/language-server/src/hover.ts | 40 ++++++++++++-- .../test/completion-provider.test.ts | 53 ++++++++++++++++++ .../language-server/test/hover.test.ts | 55 +++++++++++++++++++ 3 files changed, 144 insertions(+), 4 deletions(-) diff --git a/packages/1-framework/3-tooling/language-server/src/hover.ts b/packages/1-framework/3-tooling/language-server/src/hover.ts index 2c9eea9075e6..cff29c140886 100644 --- a/packages/1-framework/3-tooling/language-server/src/hover.ts +++ b/packages/1-framework/3-tooling/language-server/src/hover.ts @@ -1,8 +1,12 @@ import { type AuthoringArgumentDescriptor, type AuthoringPslBlockDescriptorNamespace, + type AuthoringTypeChoiceDescriptor, + type AuthoringTypeChoiceOption, + type AuthoringTypeChoiceRow, type DataTypeSupport, describeAdmittedForms, + isAuthoringTypeChoiceOption, } from '@internal/framework-components/authoring'; import { dataTypeId } from '@internal/framework-components/codec'; import type { @@ -144,7 +148,31 @@ function renderContributedTypeContent( const fence = ['```prisma', renderContributedTypeLabel(symbol, dataTypes), '```'].join('\n'); const documentation = symbol.descriptor.kind === 'fieldPreset' ? undefined : symbol.descriptor.documentation; - return withDocumentation(fence, documentation); + const content = withDocumentation(fence, documentation); + return symbol.descriptor.kind === 'typeChoice' + ? `${content}\n\n${renderTypeChoiceTable(symbol.descriptor)}` + : content; +} + +/** Each combination of a type choice's options, as written, and the explicit type it becomes. */ +function renderTypeChoiceTable(descriptor: AuthoringTypeChoiceDescriptor): string { + const options = descriptor.args.filter(isAuthoringTypeChoiceOption); + const cell = (value: string | undefined, option: AuthoringTypeChoiceOption) => + value === option.default ? `${value} (default)` : (value ?? ''); + const becomes = (row: AuthoringTypeChoiceRow) => { + const defaults = Object.entries(row.defaults ?? {}).map( + ([name, value]) => `${name}: ${JSON.stringify(value)}`, + ); + return `\`${row.type}${defaults.length === 0 ? '' : `(${defaults.join(', ')})`}\``; + }; + return [ + `| ${[...options.map((option) => option.name), 'Becomes'].join(' | ')} |`, + `| ${[...options, undefined].map(() => '---').join(' | ')} |`, + ...descriptor.choices.map( + (row) => + `| ${[...options.map((option) => cell(row.when[option.name], option)), becomes(row)].join(' | ')} |`, + ), + ].join('\n'); } function renderParameterContent(symbol: ParameterSymbol): string { @@ -180,16 +208,20 @@ function renderContributedTypeLabel( if (args.length === 0) return path; const labels = args.map((arg, index) => { const key = arg.name ?? `arg${index + 1}`; - return `${key}${arg.optional === true ? '?' : ''}: ${argumentTypeLabel(arg, dataTypes)}`; + return `${key}${arg.optional === true ? '?' : ''}: ${argumentTypeLabel(arg, dataTypes, descriptor.kind === 'typeChoice')}`; }); return `${path}(${labels.join(', ')})`; } -function argumentTypeLabel(arg: AuthoringArgumentDescriptor, dataTypes: DataTypeSupport): string { +function argumentTypeLabel( + arg: AuthoringArgumentDescriptor, + dataTypes: DataTypeSupport, + optionsWrittenBare: boolean, +): string { if (arg.kind === 'value') return describeAdmittedForms(dataTypes, dataTypeId(arg.dataType)); if (arg.kind === 'number') return 'number'; if (arg.kind === 'object') return 'object'; - return arg.values.map((value) => `'${value}'`).join(' | '); + return arg.values.map((value) => (optionsWrittenBare ? value : `'${value}'`)).join(' | '); } function blockKeywordDocumentationAt( diff --git a/packages/1-framework/3-tooling/language-server/test/completion-provider.test.ts b/packages/1-framework/3-tooling/language-server/test/completion-provider.test.ts index c2b509ec152d..37063d1e36d7 100644 --- a/packages/1-framework/3-tooling/language-server/test/completion-provider.test.ts +++ b/packages/1-framework/3-tooling/language-server/test/completion-provider.test.ts @@ -696,6 +696,59 @@ namespace other { model Hidden { id Int } }`, ]); }); + it('completes a type choice as its bare name, which writes its default choice', () => { + const { items } = completeWithSource({ + markedSource: 'model Owner { value | }', + pslBlockDescriptors: {}, + scalarTypes: [], + clientSupportsSnippets: true, + authoringContributions: assembleAuthoringContributions([ + { + id: 'choices', + authoring: { + type: { + Moment: { + kind: 'typeChoice', + documentation: 'A moment in time.', + args: [ + { + kind: 'option', + name: 'value', + values: ['date', 'text'], + default: 'date', + optional: true, + }, + ], + choices: [ + { when: { value: 'date' }, type: 'Stamp' }, + { when: { value: 'text' }, type: 'Stamp' }, + ], + }, + Stamp: { kind: 'typeConstructor', output: { codecId: 'fixture/value' } }, + }, + }, + }, + ]), + }); + expect( + items + .filter((item) => item.label === 'Moment') + .map((item) => ({ + kind: item.kind, + detail: item.detail, + newText: item.textEdit?.newText, + format: item.insertTextFormat, + })), + ).toEqual([ + { + kind: CompletionItemKind.Class, + detail: 'A moment in time.', + newText: 'Moment', + format: undefined, + }, + ]); + }); + it('does not complete a global namespace when its qualifier is shadowed locally', () => { const { items } = complete(`namespace remote { model Item { id Int } } namespace app { diff --git a/packages/1-framework/3-tooling/language-server/test/hover.test.ts b/packages/1-framework/3-tooling/language-server/test/hover.test.ts index 6a4234d2dd97..41db28daaa51 100644 --- a/packages/1-framework/3-tooling/language-server/test/hover.test.ts +++ b/packages/1-framework/3-tooling/language-server/test/hover.test.ts @@ -151,6 +151,41 @@ const authoringContributions = assembleAuthoringContributions([ documentation: '', output: { codecId: 'fixture/flag' }, }, + Moment: { + kind: 'typeChoice', + documentation: 'A moment in time.', + args: [ + { + kind: 'option', + name: 'value', + values: ['date', 'text'], + default: 'date', + optional: true, + }, + { + kind: 'option', + name: 'column', + values: ['wide', 'narrow'], + default: 'wide', + optional: true, + }, + { kind: 'value', name: 'precision', dataType: 'fixture/int', optional: true }, + ], + choices: [ + { + when: { value: 'date', column: 'wide' }, + type: 'Stamp', + defaults: { precision: 3 }, + }, + { when: { value: 'text', column: 'wide' }, type: 'Text' }, + { when: { value: 'date', column: 'narrow' }, type: 'Flag' }, + ], + }, + Stamp: { + kind: 'typeConstructor', + args: [{ name: 'precision', kind: 'value', dataType: 'fixture/int', optional: true }], + output: { codecId: 'fixture/stamp' }, + }, CreatedAt: { kind: 'typeAlias', documentation: 'Set to now when a row is created.', @@ -566,6 +601,26 @@ describe('providePslHover', () => { }); }); + it('shows a type choice with its choices as written and the explicit type each becomes', () => { + const result = hover('model Product {\n at pg.Mo|ment(value: text)\n}'); + expect(result?.contents).toEqual({ + kind: 'markdown', + value: [ + '```prisma', + 'pg.Moment(value?: date | text, column?: wide | narrow, precision?: a number)', + '```', + '', + 'A moment in time.', + '', + '| value | column | Becomes |', + '| --- | --- | --- |', + '| date (default) | wide (default) | `Stamp(precision: 3)` |', + '| text | wide (default) | `Text` |', + '| date (default) | narrow | `Flag` |', + ].join('\n'), + }); + }); + it('shows a block keyword with documentation', () => { const result = hover('polic|y ReadOwn {\n}'); expect(result).toEqual({ From 494234b0386cd3a97e073ebb15d15cf82a83ebff Mon Sep 17 00:00:00 2001 From: willbot Date: Thu, 22 Oct 2026 20:52:21 +0200 Subject: [PATCH 045/118] ADR 279 is accepted; ADR 254, 257 and 275 note what it changes ADR 279's How it works describes the type choice as built: its arguments, its table, how a call resolves and what assembly refuses. Co-Authored-By: Claude Opus 5.5 Signed-off-by: willbot Signed-off-by: Will Madden --- docs/architecture docs/ADR-INDEX.md | 2 +- .../adrs/ADR 254 - Data types and casts.md | 2 +- ...named after the target on every surface.md | 2 ++ ...type alias is expanded into each column.md | 1 + ...e an explicit type from their arguments.md | 28 +++++++++++++++---- 5 files changed, 28 insertions(+), 7 deletions(-) diff --git a/docs/architecture docs/ADR-INDEX.md b/docs/architecture docs/ADR-INDEX.md index 3428a89ded74..9772283432d3 100644 --- a/docs/architecture docs/ADR-INDEX.md +++ b/docs/architecture docs/ADR-INDEX.md @@ -47,7 +47,7 @@ This document provides a comprehensive index of all Architectural Decision Recor | 272 | Packages contribute collection methods computed from a model | **Accepted.** A package reads each model of the contract (target, name, fields, relations, storage table) and contributes collection methods under names it chooses; every SQL ORM collection offers them under the reserved member `using`, such as `db.orm.public.Post.using.post_search(q)` and `db.orm.public.Post.using.embedding_nearest(v)`. The type half registers in `CollectionMethodRegistry` by declaration merging through the package's own entry points, never through the emitted `contract.d.ts`; the run-time half goes on a target or extension descriptor. Two contributions that give a model the same name are a compile error and an `ORM.ARGUMENT_INVALID` at `orm()` | [ADR 272 - Packages contribute collection methods computed from a model.md](adrs/ADR%20272%20-%20Packages%20contribute%20collection%20methods%20computed%20from%20a%20model.md) | | 273 | A column of a type no data type claims is a raw column | A column whose database type no data type claims has data type `sql/raw`, whose parameter `sql` is the type exactly as the catalog prints it, and codec `sql/raw@1`, which passes values through typed `unknown`; migrations write the text into DDL and `db verify` compares it exactly; a Prisma 8 field declares one with ``Raw(sql`...`)``, and its default is a `sql` expression; the Prisma 7 reader reads `Unsupported("...")` as a raw column with no field | [ADR 273 - A column of a type no data type claims is a raw column.md](adrs/ADR%20273%20-%20A%20column%20of%20a%20type%20no%20data%20type%20claims%20is%20a%20raw%20column.md) | | 275 | Client generators are field attributes, and a type alias is expanded into each column | **Proposed.** `@onCreate(fn())` and `@onUpdate(fn())` set the client's generators, resolved from the column's codec through a generator function registry the stack assembles, with params from the column's type parameters; `@default` means a database default only; a `types {}` alias may carry `@default`, `@onCreate` and `@onUpdate`, is expanded into each column, and is never stored, so `storage.types` keeps only types a contract space registers; field presets are removed from SQL and Mongo, PSL and the TypeScript builder, and targets contribute aliases such as `temporal.UpdatedAt` | [ADR 275 - Client generators are field attributes, and a type alias is expanded into each column.md](adrs/ADR%20275%20-%20Client%20generators%20are%20field%20attributes%2C%20and%20a%20type%20alias%20is%20expanded%20into%20each%20column.md) | -| 279 | Convenience date and time types choose an explicit type from their arguments | **Proposed.** PostgreSQL date and time types have two layers: the explicit, database-named types (`TimestamptzJsDate`, `TimestamptzTemporal`, `DateString`, …), each fixing one codec, and the convenience types `DateTime`, `Date`, `Time` and `Duration`, named for the application concept, whose `value:` and `column:` arguments choose one explicit type, with defaults that need no `Temporal`; a type constructor may choose its codec from its arguments; the contract stores only the explicit type | [ADR 279 - Convenience date and time types choose an explicit type from their arguments.md](adrs/ADR%20279%20-%20Convenience%20date%20and%20time%20types%20choose%20an%20explicit%20type%20from%20their%20arguments.md) | +| 279 | Convenience date and time types choose an explicit type from their arguments | **Accepted.** PostgreSQL date and time types have two layers: the explicit, database-named types (`TimestamptzJsDate`, `TimestamptzTemporal`, `DateString`, …), each fixing one codec, and the convenience types `DateTime`, `Date`, `Time` and `Duration`, named for the application concept, whose `value:` and `column:` arguments choose one explicit type, with defaults that need no `Temporal`; a convenience type is a type choice, a declaration whose arguments name the type constructor it becomes; the contract stores only the explicit type | [ADR 279 - Convenience date and time types choose an explicit type from their arguments.md](adrs/ADR%20279%20-%20Convenience%20date%20and%20time%20types%20choose%20an%20explicit%20type%20from%20their%20arguments.md) | ## Query System diff --git a/docs/architecture docs/adrs/ADR 254 - Data types and casts.md b/docs/architecture docs/adrs/ADR 254 - Data types and casts.md index 111131156d52..229f8a01e296 100644 --- a/docs/architecture docs/adrs/ADR 254 - Data types and casts.md +++ b/docs/architecture docs/adrs/ADR 254 - Data types and casts.md @@ -1,6 +1,6 @@ # ADR 254 — Data types and casts -Status: **Accepted.** Implemented for the Postgres and SQLite targets and their extensions; the Mongo parts listed under "Not decided here" remain open. Amended 2026-10-20: a type-constructor argument that is a value names a data type and is admitted by the cast rule (see "Columns and type constructors"). +Status: **Accepted.** Implemented for the Postgres and SQLite targets and their extensions; the Mongo parts listed under "Not decided here" remain open. Amended 2026-10-20: a type-constructor argument that is a value names a data type and is admitted by the cast rule (see "Columns and type constructors"). Amended by [ADR 279](ADR%20279%20-%20Convenience%20date%20and%20time%20types%20choose%20an%20explicit%20type%20from%20their%20arguments.md) on 2026-10-22: a type constructor still names one codec, and a second kind of type declaration, a type choice, resolves to a type constructor from its arguments, so a column always stores the codec and parameters of a type constructor. ## At a glance diff --git a/docs/architecture docs/adrs/ADR 257 - Scalar types are named after the target on every surface.md b/docs/architecture docs/adrs/ADR 257 - Scalar types are named after the target on every surface.md index 51645b8535d1..30db310a3364 100644 --- a/docs/architecture docs/adrs/ADR 257 - Scalar types are named after the target on every surface.md +++ b/docs/architecture docs/adrs/ADR 257 - Scalar types are named after the target on every surface.md @@ -2,6 +2,8 @@ Status: **Accepted** +**Amended by [ADR 279](ADR%20279%20-%20Convenience%20date%20and%20time%20types%20choose%20an%20explicit%20type%20from%20their%20arguments.md) (2026-10-22):** where a stored type has several codecs, every one of them carries a suffix naming its kind of value, not only the second (`TimestamptzTemporal`, `TimestamptzJsDate`, `TimestamptzString`). The unsuffixed name of the concept, such as `DateTime`, `Date`, `Time` and `Duration` on PostgreSQL, belongs to a convenience type, a type choice that becomes one of the suffixed types. + ## Decision A scalar type's name says what the database stores, in the database's own vocabulary. Each type has one token, written exactly as its codec id writes it (`objectId` keeps its inner capital), and the three surfaces a user writes derive from it: the codec id is `/@`, the PSL name is the token with its first letter uppercased, and the TypeScript builder helper is `field.()`. Where a codec id already carries a name, the codec id is the source of the token, and no codec id is renamed to fit a surface name. diff --git a/docs/architecture docs/adrs/ADR 275 - Client generators are field attributes, and a type alias is expanded into each column.md b/docs/architecture docs/adrs/ADR 275 - Client generators are field attributes, and a type alias is expanded into each column.md index 121327f5b86a..ddc29e4c8e29 100644 --- a/docs/architecture docs/adrs/ADR 275 - Client generators are field attributes, and a type alias is expanded into each column.md +++ b/docs/architecture docs/adrs/ADR 275 - Client generators are field attributes, and a type alias is expanded into each column.md @@ -5,6 +5,7 @@ **Builds on:** [ADR 158 — Execution mutation defaults](ADR%20158%20-%20Execution%20mutation%20defaults.md), [ADR 241 — Scalar types use the authoring type-constructor channel](ADR%20241%20-%20Scalar%20types%20use%20the%20authoring%20type-constructor%20channel.md), [ADR 269 — PSL mixins](ADR%20269%20-%20PSL%20mixins.md) **Supersedes:** the field-preset half of [ADR 170 — Pack-provided type constructors and field presets](ADR%20170%20-%20Pack-provided%20type%20constructors%20and%20field%20presets.md); [ADR 246 — Option arguments and select templates for authoring helpers](ADR%20246%20-%20Option%20arguments%20and%20select%20templates%20for%20authoring%20helpers.md), except its two omission rules **Amends:** [ADR 104](ADR%20104%20-%20PSL%20extension%20namespacing%20%26%20syntax.md), [ADR 158](ADR%20158%20-%20Execution%20mutation%20defaults.md), [ADR 169 — Declared applicability for mutation default generators](ADR%20169%20-%20Declared%20applicability%20for%20mutation%20default%20generators.md), [ADR 208 — Higher-order codecs for parameterized types](ADR%20208%20-%20Higher-order%20codecs%20for%20parameterized%20types.md), [ADR 212 — AST-bound codec resolution](ADR%20212%20-%20AST-bound%20codec%20resolution.md), [ADR 231](ADR%20231%20-%20Declarative%20attribute%20specifications.md), [ADR 241](ADR%20241%20-%20Scalar%20types%20use%20the%20authoring%20type-constructor%20channel.md), [ADR 249](ADR%20249%20-%20Central%20attribute-spec%20registry.md), [ADR 254](ADR%20254%20-%20Data%20types%20and%20casts.md) +**Amended by [ADR 279](ADR%20279%20-%20Convenience%20date%20and%20time%20types%20choose%20an%20explicit%20type%20from%20their%20arguments.md) (2026-10-22):** the contributed `temporal.*` aliases are to be built on the convenience type `DateTime`, and their `String` and `JsDate` variants are not needed, because `now()` picks its generator from the field's final codec and a user writes `DateTime(value: string) @onCreate(now())` for another kind of value. Until that change is made, the PostgreSQL aliases remain on `TimestamptzTemporal`, with the variants. **Amended 2026-10-21:** the TypeScript `types` key is renamed `databaseTypes` and registers database types only; a contributed alias is used as `field.column(type.temporal.UpdatedAt())`; a contributed alias carries no database default; Mongo's contributed aliases are on `Date`; the Mongo TypeScript builder gets no contributed aliases yet. Each change is marked "Amended 2026-10-21" below. --- diff --git a/docs/architecture docs/adrs/ADR 279 - Convenience date and time types choose an explicit type from their arguments.md b/docs/architecture docs/adrs/ADR 279 - Convenience date and time types choose an explicit type from their arguments.md index 673e94a7ceef..c9256247a8dc 100644 --- a/docs/architecture docs/adrs/ADR 279 - Convenience date and time types choose an explicit type from their arguments.md +++ b/docs/architecture docs/adrs/ADR 279 - Convenience date and time types choose an explicit type from their arguments.md @@ -1,6 +1,6 @@ # ADR 279 — Convenience date and time types choose an explicit type from their arguments -**Status:** Proposed +**Status:** Accepted **Builds on:** [ADR 241 — Scalar types use the authoring type-constructor channel](ADR%20241%20-%20Scalar%20types%20use%20the%20authoring%20type-constructor%20channel.md) **Amends:** [ADR 254 — Data types and casts](ADR%20254%20-%20Data%20types%20and%20casts.md), [ADR 257 — Scalar types are named after the target on every surface](ADR%20257%20-%20Scalar%20types%20are%20named%20after%20the%20target%20on%20every%20surface.md), [ADR 275 — Client generators are field attributes, and a type alias is expanded into each column](ADR%20275%20-%20Client%20generators%20are%20field%20attributes%2C%20and%20a%20type%20alias%20is%20expanded%20into%20each%20column.md) @@ -90,7 +90,25 @@ TimestamptzTemporal: { }, ``` -A convenience type is a different kind of declaration in the same namespace, a type choice. It names no codec. It declares its option arguments and a table whose rows each name an explicit type constructor: +A convenience type is a different kind of declaration in the same namespace, a type choice. It names no codec. It declares its option arguments, each with its values and a default, the value arguments it passes on by name, and a table whose rows each name an explicit type constructor of the same namespace: + +```ts +DateTime: { + kind: 'typeChoice', + args: [ + { kind: 'option', name: 'value', values: ['jsDate', 'temporal', 'string'], default: 'jsDate', optional: true }, + { kind: 'option', name: 'column', values: ['timestamptz', 'timestamp'], default: 'timestamptz', optional: true }, + { kind: 'value', name: 'precision', dataType: 'pg/int4', optional: true }, + ], + choices: [ + { when: { value: 'jsDate', column: 'timestamptz' }, type: 'TimestamptzJsDate', defaults: { precision: 3 } }, + { when: { value: 'temporal', column: 'timestamptz' }, type: 'TimestamptzTemporal' }, + // …one row for each combination below + ], +}, +``` + +As a table: | `value` | `column` | Explicit type | Parameter defaults | | --- | --- | --- | --- | @@ -101,11 +119,11 @@ A convenience type is a different kind of declaration in the same namespace, a t | `temporal` | `timestamp` | `TimestampInstant` | | | `string` | `timestamp` | `TimestampString` | | -That is `DateTime`'s table; the other three are declared the same way. Resolving a call looks up the row for its option values, then instantiates the named explicit constructor with the passed-on arguments, so the result is exactly what writing the explicit type gives. A combination with no row, such as `Date(value: jsDate)`, is refused at the argument that makes it invalid, and the message lists the values that argument accepts with the others as written. Assembly refuses a table that names a constructor no component registers. +That is `DateTime`'s table; the other three are declared the same way. Resolving a call fills each option left out with its default, looks up the row for the option values, then instantiates the named explicit constructor with the passed-on arguments, a row default standing in for one left out, so the result is exactly what writing the explicit type gives. A combination with no row, such as `Time(value: temporal, column: timetz)`, is refused at the argument that makes it invalid, and the message lists the values that argument accepts with the others as written; a value an option does not list, such as `Date(value: jsDate)`, is refused the same way. A passed-on argument the chosen constructor does not take is refused at that argument. Arguments are written by name or in declaration order, so `Time(3)` reads `3` as the `value` option and is refused. Every argument is optional, so a type choice is also written bare, as its default row. Assembly refuses a table that names a constructor no component registers beside it, a combination or default an option does not list, defaults with no row, a row default for an argument the choice does not pass on, and a passed-on argument whose data type differs from the chosen constructor's. The field-preset option arguments and select templates (ADR 246) are not used: a select resolves a template value and has no defaults and no refusal, while a type choice resolves to a named constructor, which is what "becomes one explicit type" means. -Every place that reads a field's codec reads the codec its call resolves to, including through a `types {}` alias: the PSL column resolution, the `@default` value check and the `@onCreate`/`@onUpdate` generator lookup, the Prisma 7 reader and the TypeScript builder, whose `type.*` helpers (ADR 275) include the type choices and resolve their application type at compile time from literal arguments. +Every place that reads a field's codec reads the codec its call resolves to, including through a `types {}` alias: the PSL column resolution, the `@default` value check and the `@onCreate`/`@onUpdate` generator lookup, the Prisma 7 reader and the TypeScript builder, whose `type.*` helpers (ADR 275) include the type choices and resolve their application type at compile time from literal arguments. The `type.*` helper of a type choice takes the same arguments in the same order, and its type is one overload per row, so a call is typed as the explicit type it chooses and a combination with no row does not compile. The editor's hover shows each argument's values as PSL writes them and the explicit type each combination becomes. ### What the contract and the tools see @@ -138,7 +156,7 @@ The `String` and `JsDate` variants are not needed. A contributed alias takes no - ADR 257 says the second codec of a stored type adds a suffix. Where a stored type has several codecs, every one now has a suffix, and the unsuffixed name of the concept belongs to a convenience type. - ADR 275's `String` and `JsDate` variants of the contributed `temporal.*` aliases are not built. - The planned rename of the PostgreSQL and SQLite types, which applies ADR 257 to those targets (for example `Int` to `Int4`), keeps `DateTime` as the convenience type instead of removing it, and adds the renames in item 6. -- The editor's completion offers each convenience type's `value` and `column` choices, and its hover text gives the explicit type each choice becomes. +- The editor's hover text gives each convenience type's `value` and `column` choices and the explicit type each combination becomes, and completion inserts the bare name, which writes the defaults. ## Not decided here From 235a744ca451b9f4a1499c9d4d6d9e80daebc251 Mon Sep 17 00:00:00 2001 From: willbot Date: Thu, 22 Oct 2026 20:52:22 +0200 Subject: [PATCH 046/118] Docs describe the two layers of PostgreSQL date and time types The scalar types reference lists the convenience types and the explicit type each combination becomes; the codec authoring guide, error reference, READMEs and skill references follow. Co-Authored-By: Claude Opus 5.5 Signed-off-by: willbot Signed-off-by: Will Madden --- docs/Serverless Deployment Guide.md | 2 +- docs/reference/codec-authoring-guide.md | 4 ++- docs/reference/error-reference.md | 2 ++ docs/reference/scalar-types.md | 30 +++++++++++++++---- gotchas.md | 2 +- .../1-core/framework-components/README.md | 2 +- .../2-sql/2-authoring/contract-psl/README.md | 4 +-- .../3-targets/3-targets/postgres/README.md | 2 +- skills/prisma-8/references/contract.md | 4 ++- skills/prisma-8/references/debug.md | 2 +- .../prisma-8/references/queries-postgres.md | 2 +- 11 files changed, 41 insertions(+), 15 deletions(-) diff --git a/docs/Serverless Deployment Guide.md b/docs/Serverless Deployment Guide.md index 6085b16bd461..babd8406fdc7 100644 --- a/docs/Serverless Deployment Guide.md +++ b/docs/Serverless Deployment Guide.md @@ -110,7 +110,7 @@ This goes in `.env`, not `.dev.vars`. `.dev.vars` is for runtime worker secrets; A serverless client at module scope; a connection from `connect` in each request; SQL, ORM and transactions on that connection; cursor streaming. The full file is `examples/prisma-8-cloudflare-worker/src/worker.ts`. -The samples use extensionless relative imports and `./contract.d`, which need `moduleResolution: "bundler"` in `tsconfig.json`; Worker projects built with wrangler use that setting, and so does the example. On a runtime without a built-in `Temporal`, which today includes Cloudflare Workers at the example's compatibility date and Node.js 24, reading a `DateTime` column needs a Temporal polyfill: add `temporal-polyfill` and import `temporal-polyfill/full/global` before the first query, as the example does at the top of `worker.ts` and `seed.ts`. +The samples use extensionless relative imports and `./contract.d`, which need `moduleResolution: "bundler"` in `tsconfig.json`; Worker projects built with wrangler use that setting, and so does the example. On a runtime without a built-in `Temporal`, which today includes Cloudflare Workers at the example's compatibility date and Node.js 24, reading a column whose type reads `Temporal` values, such as `TimestamptzTemporal` or `DateTime(value: temporal)`, needs a Temporal polyfill: add `temporal-polyfill` and import `temporal-polyfill/full/global` before the first query, as the example does at the top of `worker.ts` and `seed.ts`. #### Module scope diff --git a/docs/reference/codec-authoring-guide.md b/docs/reference/codec-authoring-guide.md index 4ba3be54154a..9b5d8cf8550f 100644 --- a/docs/reference/codec-authoring-guide.md +++ b/docs/reference/codec-authoring-guide.md @@ -588,7 +588,9 @@ A codec's `paramsSchema` is its data type's `params`, referenced and never resta A codec with parameters of its own sets `paramsSchema` to `dataType.params.and(ownKeys)`, or to its own keys alone when the data type declares no `params`, as `arktype/json@1` does. `postgresCodec(...)` and `sqliteCodec(...)` take the adapted codec's params schema from the data type they are given. -A type constructor maps its arguments onto the parameters and names only the codec; the data type follows from the codec. Every argument of a type constructor is a value argument, `{ kind: 'value', dataType }`, and assembly refuses any other kind with `CONTRACT.PACK_CONTRIBUTION_INVALID`; field presets keep their own argument kinds until they are removed. A value argument is a value of the named data type, read and cast as an attribute argument is (ADR 254), so `Vector(1536)` reads `1536` as a `pg/int2` and casts it to `pg/int4`, and `Vector("1536")` is refused. On Postgres every length, precision, scale and SRID is a `pg/int4`, the type of a typmod. A value argument carries no bounds, because the parameter's schema checks the value it gives. Assembly refuses a value argument whose data type no component registers (`CONTRACT.DATA_TYPE_UNREGISTERED`) or that no contract source can write (`CONTRACT.DATA_TYPE_NOT_WRITABLE`). In the TypeScript builder, the pack that registers a data type declares the TypeScript value an argument of it takes as `__dataTypeArguments` in its type, such as `{ 'pg/int4': { argument: number; parameter: number } }`: `argument` is what an author passes, and `parameter` is the type parameter the data type's reader gives for it, and `contract print` writes each argument as a literal of its data type. `inferred: true` marks the constructor written for a column of the data type when no schema names one: `contract infer` and the Prisma 7 reader write it. `contract print` writes it when it produces the column's codec, and otherwise the first constructor in the stack that does, so a `timestamptz` column with the `Temporal` codec prints `TimestamptzTemporal`, which the PostgreSQL target lists before its second name `DateTime`. It is called with the fewest arguments that give the column's parameters in normal form, so a `numeric(10,0)` column is written `Numeric(10)`. The Prisma 7 reader builds a contract rather than printing PSL, so it passes every parameter the Prisma 7 column gives. At most one constructor per data type carries the mark. A data type with no marked constructor, such as `pg/tsquery`, is still recognised, but `contract infer` cannot write a column of it and fails with `CONTRACT.INFER_COLUMN_TYPE_UNCLAIMED`. +A type constructor maps its arguments onto the parameters and names only the codec; the data type follows from the codec. Every argument of a type constructor is a value argument, `{ kind: 'value', dataType }`, and assembly refuses any other kind with `CONTRACT.PACK_CONTRIBUTION_INVALID`; field presets keep their own argument kinds until they are removed. A value argument is a value of the named data type, read and cast as an attribute argument is (ADR 254), so `Vector(1536)` reads `1536` as a `pg/int2` and casts it to `pg/int4`, and `Vector("1536")` is refused. On Postgres every length, precision, scale and SRID is a `pg/int4`, the type of a typmod. A value argument carries no bounds, because the parameter's schema checks the value it gives. Assembly refuses a value argument whose data type no component registers (`CONTRACT.DATA_TYPE_UNREGISTERED`) or that no contract source can write (`CONTRACT.DATA_TYPE_NOT_WRITABLE`). In the TypeScript builder, the pack that registers a data type declares the TypeScript value an argument of it takes as `__dataTypeArguments` in its type, such as `{ 'pg/int4': { argument: number; parameter: number } }`: `argument` is what an author passes, and `parameter` is the type parameter the data type's reader gives for it, and `contract print` writes each argument as a literal of its data type. `inferred: true` marks the constructor written for a column of the data type when no schema names one: `contract infer` and the Prisma 7 reader write it. `contract print` writes it when it produces the column's codec, and otherwise the first constructor in the stack that does, so a `timestamptz` column with the `Temporal` codec prints `TimestamptzTemporal`. It is called with the fewest arguments that give the column's parameters in normal form, so a `numeric(10,0)` column is written `Numeric(10)`. The Prisma 7 reader builds a contract rather than printing PSL, so it passes every parameter the Prisma 7 column gives. At most one constructor per data type carries the mark. A data type with no marked constructor, such as `pg/tsquery`, is still recognised, but `contract infer` cannot write a column of it and fails with `CONTRACT.INFER_COLUMN_TYPE_UNCLAIMED`. + +A type choice (`kind: 'typeChoice'`) is a second kind of type declaration, for a type named after what a field means rather than a column type ([ADR 279](../architecture%20docs/adrs/ADR%20279%20-%20Convenience%20date%20and%20time%20types%20choose%20an%20explicit%20type%20from%20their%20arguments.md)). It names no codec. It declares option arguments, `{ kind: 'option', name, values, default, optional: true }`, written as bare names, and value arguments it passes on by name, such as `precision`; and `choices`, a table whose rows map one value of each option to the name of a type constructor at the same level of the namespace, with `defaults` for passed-on arguments the call leaves out. A call resolves to its row's constructor, called with the passed-on arguments, so the contract records only that constructor's codec and parameters. A combination with no row, and a passed-on argument the chosen constructor does not take, are refused at the argument. Every argument is optional, so a type choice is also written bare, as its default row. Assembly refuses a table that names no registered constructor, a value or default an option does not list, defaults with no row, and a passed-on argument of another data type than its constructor's. A type choice is never marked `inferred`, and `contract print` and `contract infer` write the constructor. In the TypeScript builder its `type.*` helper takes the same arguments in the same order and is typed as one overload per row. ```ts import type { AuthoringTypeNamespace } from '@internal/framework-components/authoring'; diff --git a/docs/reference/error-reference.md b/docs/reference/error-reference.md index f97eab009bb3..c83a1861d4fe 100644 --- a/docs/reference/error-reference.md +++ b/docs/reference/error-reference.md @@ -543,6 +543,8 @@ Namespaces are declared (contract-level list or a model-level `namespace`) on a A composed pack's contribution is malformed or collides with another contribution; this is the extension-author-facing bucket. Covers: entity types colliding with reserved helper keys, duplicate entity kinds or index-type registrations, a registered entity kind with no `lowerEntityHandles` lowering, an invalid `indexTypes` shape, entries-slot collisions between a model attribute and a block entry kind, a model attribute that lowers to a malformed index, bad authoring-helper paths, a codec registered with an entity-ref arg but no `columnFromEntity` hook, and print-time contribution mismatches (a block keyword with no PSL block descriptor, or a descriptor whose discriminator disagrees with the block's kind). Raised during contract authoring/lowering and PSL printing. +A contributed type choice (`kind: 'typeChoice'`, such as PostgreSQL's `DateTime`) is refused when the stack is assembled, by the control stack and by the TypeScript builders, when its table cannot be resolved: `Type choice "" contributed by "" chooses "", which is not a type constructor any component registers beside it.`, a row whose combination or an option default the option does not list, a combination of defaults with no row (`… has no row for its defaults value: , so it cannot be written bare.`), a parameter default for an argument it does not pass on, or a passed-on argument of another data type than the chosen constructor takes. Payload: `packId`, `contribution`. + A contributed type alias (`kind: 'typeAlias'`, such as `temporal.CreatedAt`) is refused when the stack is assembled, by the control stack and by the TypeScript builders, so PSL and TypeScript refuse the same packs. Its type params may not reference an argument, because a type alias takes none: `Invalid authoring type alias "" contributed by descriptor "". output.type.typeParams. references an argument, but a type alias takes none. Write the value itself.` Each generator call must resolve for the alias's codec: `Type alias "" calls now() on create, which does not apply to its codec "".`, `…, which its codec "" refuses: `, or `…, but no component declares the generator function "".` Payload: `packId`, `contribution`, `reason`, `keyword`, `paramName`, `codecId`. The Mongo `defineContract` builder raises it for a model field, a value object field or an enum whose codec represents a data type that is not a MongoDB data type (`reason: 'data-type-not-mongo'`): the pack declared the data type without `mongoDataType`, so its BSON type is unknown and the contract cannot describe the field. The Mongo PSL interpreter reports the same case at the field as `PSL_UNSUPPORTED_FIELD_TYPE`. Payload (meta): `modelName`, `valueObjectName` or `enumName`, `fieldName` for a field, `codecId`, `dataType`, `reason`. diff --git a/docs/reference/scalar-types.md b/docs/reference/scalar-types.md index 0bee42f30f4f..66298c60a45e 100644 --- a/docs/reference/scalar-types.md +++ b/docs/reference/scalar-types.md @@ -66,6 +66,26 @@ Two limitations to know about: These are the current names. The Postgres and SQLite rename project will change several of them to the target's own type names and update this table. +The date and time types have two layers ([ADR 279](../architecture%20docs/adrs/ADR%20279%20-%20Convenience%20date%20and%20time%20types%20choose%20an%20explicit%20type%20from%20their%20arguments.md)). Each explicit type in the table below names one column type and one kind of value. The convenience types `DateTime`, `Date`, `Time` and `Duration` are named for what a field means, and each becomes one explicit type, chosen by its optional `value` and `column` arguments; `precision` is passed on to the explicit type. The contract stores only the explicit type, so `DateTime(value: temporal)` and `TimestamptzTemporal` emit the same contract. + +| Convenience type | `value` | `column` | Becomes | +| --- | --- | --- | --- | +| `DateTime(value?, column?, precision?)` | `jsDate` (default) | `timestamptz` (default) | `TimestamptzJsDate`, precision 3 unless written | +| | `temporal` | `timestamptz` | `TimestamptzTemporal` | +| | `string` | `timestamptz` | `TimestamptzString` | +| | `jsDate` | `timestamp` | `TimestampJsDate`, precision 3 unless written | +| | `temporal` | `timestamp` | `TimestampInstant` | +| | `string` | `timestamp` | `TimestampString` | +| `Date(value?, column?)` | `string` (default) | `date` (default) | `DateString` | +| | `temporal` | `date` | `DateTemporal` | +| `Time(value?, column?, precision?)` | `string` (default) | `time` (default) | `TimeString` | +| | `temporal` | `time` | `TimeTemporal` | +| | `string` | `timetz` | `Timetz` | +| `Duration(value?, column?, precision?)` | `string` (default) | `interval` (default) | `IntervalString` | +| | `parts` | `interval` | `IntervalParts` | + +The arguments are written as bare names, by name or in this order: `DateTime(value: string, precision: 6)`, `DateTime(temporal, timestamp)`. A combination the table does not list, such as `Time(value: temporal, column: timetz)` or `Date(value: jsDate)`, is refused at the argument that makes it invalid, naming the values it accepts; so is a `precision` the chosen type does not take. No default needs a global `Temporal`. In a TypeScript contract the helpers take the same arguments in the same order, `type.DateTime('string', undefined, 6)`, and are typed as the explicit type they choose. `contract print` and `contract infer` write the explicit type. + | PSL name | TS helper | Codec id | Storage type | Application type | | --- | --- | --- | --- | --- | | `String` | `field.text()` | `pg/text@1` | `text` | `string` | @@ -80,16 +100,16 @@ These are the current names. The Postgres and SQLite rename project will change | `Real` | — | `pg/float4@1` | `float4` | `number` | | `Float` | `field.float()` | `pg/float8@1` | `float8` | `number` | | `Decimal`, `Numeric(p?, s?)` | `field.decimal()` | `pg/numeric@1` | `numeric` | `string` (decimal text) | -| `DateTime`, `TimestamptzTemporal(p?)` | `field.dateTime()` | `pg/timestamptz-temporal@1` | `timestamptz` | `Temporal.Instant` | +| `TimestamptzTemporal(p?)` | `field.dateTime()` | `pg/timestamptz-temporal@1` | `timestamptz` | `Temporal.Instant` | | `TimestamptzJsDate(p?)` | — | `pg/timestamptz-date@1` | `timestamptz` | `Date` | | `TimestamptzString(p?)` | — | `pg/timestamptz-string@1` | `timestamptz` | `string` | | `TimestampTemporal(p?)` | — | `pg/timestamp-temporal@1` | `timestamp` | `Temporal.PlainDateTime` | | `TimestampString(p?)` | — | `pg/timestamp-string@1` | `timestamp` | `string` | | `TimestampJsDate(p?)` | — | `pg/timestamp-date@1` | `timestamp` | `Date`, the stored date and time read as UTC | | `TimestampInstant(p?)` | — | `pg/timestamp-instant@1` | `timestamp` | `Temporal.Instant`, the stored date and time read as UTC | -| `Date`, `DateTemporal` | — | `pg/date-temporal@1` | `date` | `Temporal.PlainDate` | +| `DateTemporal` | — | `pg/date-temporal@1` | `date` | `Temporal.PlainDate` | | `DateString` | — | `pg/date-string@1` | `date` | `string` | -| `Time(p?)`, `TimeTemporal(p?)` | — | `pg/time-temporal@1` | `time` | `Temporal.PlainTime` | +| `TimeTemporal(p?)` | — | `pg/time-temporal@1` | `time` | `Temporal.PlainTime` | | `TimeString(p?)` | — | `pg/time-string@1` | `time` | `string` | | `Timetz(p?)` | — | `pg/timetz@1` | `timetz` | `string` | | `Uuid` | `field.uuidNative()` | `pg/uuid@1` | `uuid` | `string` | @@ -107,7 +127,7 @@ These are the current names. The Postgres and SQLite rename project will change A literal default of a date, time, interval or bytes type is written with the tag of its type, as in `@default(timestamptz`2024-01-01T00:00:00Z`)` or `@default(bytea`\x0102`)`; a quoted string is refused with a message that shows the tag. It is stored in the one form its data type stores, whichever codec the column uses and however the default was written, and a default with more fraction digits or more scale than its column holds is refused rather than rounded; [ADR 254](../architecture%20docs/adrs/ADR%20254%20-%20Data%20types%20and%20casts.md#date-and-time-types) states each form. -The tools that write a schema for you use the text date and time types. In a PSL schema, `prisma orm init` writes `createdAt TimestamptzString @default(now())` and `updatedAt temporal.updatedAtString()`. In a TypeScript contract, it writes `field.temporal.createdAtString()` and `field.temporal.updatedAtString()`. All of these are `TimestamptzString` columns. `prisma contract infer` and `prisma7Schema(...)` write `TimestampString(p)`, `TimestamptzString(p)`, `DateString` and `TimeString(p)`. Five types read and write `Temporal` values: `TimestamptzTemporal` (also written `DateTime`), `TimestampTemporal`, `TimestampInstant`, `DateTemporal` (also written `Date`) and `TimeTemporal` (also written `Time`). Their TypeScript types come from `temporal-spec`, so a project needs no global `Temporal` declarations to type them. An application that uses them needs a global `Temporal`: on a runtime without one, such as Node.js 24, install it, for example with `import 'temporal-polyfill/full/global'`. +The tools that write a schema for you use the text date and time types. In a PSL schema, `prisma orm init` writes `createdAt TimestamptzString @default(now())` and `updatedAt temporal.updatedAtString()`. In a TypeScript contract, it writes `field.temporal.createdAtString()` and `field.temporal.updatedAtString()`. All of these are `TimestamptzString` columns. `prisma contract infer` and `prisma7Schema(...)` write `TimestampString(p)`, `TimestamptzString(p)`, `DateString` and `TimeString(p)`. Five types read and write `Temporal` values: `TimestamptzTemporal`, `TimestampTemporal`, `TimestampInstant`, `DateTemporal` and `TimeTemporal`, which the convenience types choose with `value: temporal`. Their TypeScript types come from `temporal-spec`, so a project needs no global `Temporal` declarations to type them. An application that uses them needs a global `Temporal`: on a runtime without one, such as Node.js 24, install it, for example with `import 'temporal-polyfill/full/global'`. ## SQLite @@ -139,7 +159,7 @@ The PSL name on each target for a concept, and the Prisma 6/7 name a migrating s | double | `Float` | `Float` | `Float` | `Double` | | decimal | `Decimal` | `Decimal`, `Numeric(p?, s?)` | `Decimal` (stored as text) | `Decimal128` | | boolean | `Boolean` | `Boolean` | — | `Bool` | -| timestamp | `DateTime` | `DateTime`, `TimestamptzTemporal(p?)` | `DateTime` | `Date` | +| timestamp | `DateTime` | `DateTime` (a `TimestamptzJsDate(3)`) | `DateTime` | `Date` | | text | `String` | `String` | `String` | `String` | | bytes | `Bytes` | `Bytes` | `Bytes` | `Binary` | | JSON | `Json` | `Json`, `Jsonb` | `Json` | `Json` | diff --git a/gotchas.md b/gotchas.md index de99e7b1a20e..20226a8631b2 100644 --- a/gotchas.md +++ b/gotchas.md @@ -132,7 +132,7 @@ The same command with `--from 20260707T1005_init` (a migration directory name) s **Workaround.** `import 'temporal-polyfill/full/global'` before the client is created (the example did so in `src/db.ts` before the fix), or author the column with the `*String` presets to receive PostgreSQL's text. -**Resolved.** `prisma7Schema(...)` and `contract infer` now write `TimestampString(3)`, `TimestamptzString(p)`, `DateString` and `TimeString(p)` for these columns, so they read as PostgreSQL's text, `@updatedAt` writes UTC text, and the application needs no `Temporal`. A column written by hand as `DateTime`, `TimestampTemporal(p)`, `Date` or `Time(p)` still reads as a `Temporal` value and still needs one. +**Resolved.** `prisma7Schema(...)` and `contract infer` now write `TimestampString(3)`, `TimestamptzString(p)`, `DateString` and `TimeString(p)` for these columns, so they read as PostgreSQL's text, `@updatedAt` writes UTC text, and the application needs no `Temporal`. A column written by hand as `TimestamptzTemporal(p)`, `TimestampTemporal(p)`, `DateTemporal` or `TimeTemporal(p)`, or as a convenience type with `value: temporal`, still reads as a `Temporal` value and still needs one; the convenience types `DateTime`, `Date` and `Time` need none at their defaults. **Reproduction.** 1. `cd examples/prisma7-adoption && pnpm db:start`, then `pnpm v7:migrate && pnpm emit && pnpm sign && pnpm seed`. diff --git a/packages/1-framework/1-core/framework-components/README.md b/packages/1-framework/1-core/framework-components/README.md index cdd2e6089854..686147980faa 100644 --- a/packages/1-framework/1-core/framework-components/README.md +++ b/packages/1-framework/1-core/framework-components/README.md @@ -7,7 +7,7 @@ Framework component types, authoring logic, control stack assembly, and emission ## What this package provides - **Component types** (`./components`): Base descriptor and instance interfaces for framework components (family, target, adapter, driver, extension), pack refs, and type renderer system -- **Authoring types** (`./authoring`): Declarative authoring contribution types, template resolution, and validation for type constructors and field presets. `findAuthoringTypeConstructorCall(namespace, output)` is the inverse of `instantiateAuthoringTypeConstructor`: the first call to a constructor in a namespace that produces a given codec, native type and type parameters. `assembleDataTypeSupport(components, dataTypeOfCodec)` builds the data types and authoring entries every contract source reads values with, refusing duplicates and value arguments whose data type is unregistered or unwritten; the control stack and the TypeScript builder both call it +- **Authoring types** (`./authoring`): Declarative authoring contribution types, template resolution, and validation for type constructors and field presets. A type choice (`kind: 'typeChoice'`) chooses a type constructor of its namespace from its option arguments: `resolveAuthoringTypeChoice` resolves a call, `instantiateAuthoringTypeCall` instantiates a constructor or choice by name, and `assertTypeChoicesValid` refuses an unresolvable table at assembly (ADR 279). `findAuthoringTypeConstructorCall(namespace, output)` is the inverse of `instantiateAuthoringTypeConstructor`: the first call to a constructor in a namespace that produces a given codec, native type and type parameters. `assembleDataTypeSupport(components, dataTypeOfCodec)` builds the data types and authoring entries every contract source reads values with, refusing duplicates and value arguments whose data type is unregistered or unwritten; the control stack and the TypeScript builder both call it - **Codec base interface** (`./codec`): The cross-family `Codec` base type that SQL `Codec` and Mongo `MongoCodec` extend - **Control stack** (`./control`): Assembly functions that combine component descriptors into a unified `ControlStack` with derived state (codec imports, renderers, authoring contributions) - **Emission SPI** (`./emission`): Types for the emission pipeline — `TargetFamilyHook`, `GenerateContractTypesOptions`, `TypeRenderEntry`, `TypeRenderer`, `ParameterizedCodecDescriptor`, and related types diff --git a/packages/2-sql/2-authoring/contract-psl/README.md b/packages/2-sql/2-authoring/contract-psl/README.md index 79fc885802d2..1d4a8b7b4d1c 100644 --- a/packages/2-sql/2-authoring/contract-psl/README.md +++ b/packages/2-sql/2-authoring/contract-psl/README.md @@ -18,7 +18,7 @@ This keeps core/CLI source-agnostic while giving PSL-first SQL users a one-line - Interpret SQL timestamp semantics: `DateTime @default(now())` as a storage default, and `temporal.createdAt()` / `temporal.updatedAt()` as client-side execution mutation defaults - Lower shared constructor expressions in both `types {}` blocks and inline field positions (for example `ShortName = sql.String(length: 35)` and `embedding pgvector.Vector(length: 1536)?`) - Lower supported default functions through composed registry inputs -- Resolve Postgres native storage types from bare names and constructor calls in type position (`Char`, `VarChar`, `Numeric`, `Uuid`, `Inet`, `SmallInt`, `Real`, `TimestampTemporal`, `TimestamptzTemporal`, `Date`, `DateTemporal`, `Time`, `TimeTemporal`, `Timetz`, `IntervalParts`, `Bit`, `VarBit`, `Json`, `Jsonb`, `BigIntNumber`, `UnboundedInt`) +- Resolve Postgres native storage types from bare names and constructor calls in type position (`Char`, `VarChar`, `Numeric`, `Uuid`, `Inet`, `SmallInt`, `Real`, `TimestampTemporal`, `TimestamptzTemporal`, `DateTemporal`, `TimeTemporal`, `Timetz`, `IntervalParts`, `Bit`, `VarBit`, `Json`, `Jsonb`, `BigIntNumber`, `UnboundedInt`), and resolve a type choice, such as the convenience types `DateTime`, `Date`, `Time` and `Duration`, to the constructor its arguments choose, in a field and in a `types {}` alias; `@default` and `@onCreate`/`@onUpdate` are read against the chosen constructor's codec - Map PSL relation action tokens to SQL contract referential actions and emit diagnostics for unsupported values - Emit deterministic relation metadata in `models..relations` - Enforce extension composition for namespaced constructor expressions and emit strict diagnostics for unsupported namespaced attributes @@ -91,7 +91,7 @@ Field-preset calls in type position: Generated values: - `@default(...)` sets a database default only: a literal, a tagged literal, `now()` or `autoincrement()`. A function the client generates in `@default`, such as `@default(uuid())`, is refused with `PSL_GENERATOR_IN_DEFAULT`, whose message names the `@onCreate` form. -- `@onCreate(fn())` and `@onUpdate(fn())` set the generator the client runs on create and on every update. `fn` is one of the generator functions the stack binds to the field's codec: `now()` on every date and time type, and `uuid()`, `uuid(7)`, `cuid(2)`, `ulid()`, `nanoid()`, `nanoid(n)` and `ksuid()` on every text type (`uuid` also on `Uuid`). The spec of each attribute offers only the functions bound to the field's codec, and the call resolves through `generatorFunctions` with the column's codec and type params: `now()` on a `Time(3)` column takes `precision: 3`. `nanoid()` writes 21 characters, or the size the call gives, on every column. A generator is refused on a column shorter than what it writes (`uuid` 36, `ksuid` 27, `ulid` 26, `cuid` 24), with the column's length read in its type's normal form, so a bare `Char` holds 1; SQLite does not limit the length. A function that does not apply is `PSL_GENERATOR_NOT_APPLICABLE`, an unknown one `PSL_UNKNOWN_GENERATOR_FUNCTION`, either attribute on a list field `PSL_LIST_EXECUTION_DEFAULT_UNSUPPORTED`, and the same attribute twice on one field `PSL_DUPLICATE_ATTRIBUTE`. +- `@onCreate(fn())` and `@onUpdate(fn())` set the generator the client runs on create and on every update. `fn` is one of the generator functions the stack binds to the field's codec: `now()` on every date and time type, and `uuid()`, `uuid(7)`, `cuid(2)`, `ulid()`, `nanoid()`, `nanoid(n)` and `ksuid()` on every text type (`uuid` also on `Uuid`). The spec of each attribute offers only the functions bound to the field's codec, and the call resolves through `generatorFunctions` with the column's codec and type params: `now()` on a `TimeTemporal(3)` column takes `precision: 3`. `nanoid()` writes 21 characters, or the size the call gives, on every column. A generator is refused on a column shorter than what it writes (`uuid` 36, `ksuid` 27, `ulid` 26, `cuid` 24), with the column's length read in its type's normal form, so a bare `Char` holds 1; SQLite does not limit the length. A function that does not apply is `PSL_GENERATOR_NOT_APPLICABLE`, an unknown one `PSL_UNKNOWN_GENERATOR_FUNCTION`, either attribute on a list field `PSL_LIST_EXECUTION_DEFAULT_UNSUPPORTED`, and the same attribute twice on one field `PSL_DUPLICATE_ATTRIBUTE`. - `@default(now()) @onCreate(now())` on one column keeps both: the client writes its time on create and the database default fills other inserts. `@onUpdate(...)` alone is allowed. - A generator works on an optional field, such as `token String? @onCreate(uuid())`. On create and update, a value left out is generated and an explicit `null` is stored. diff --git a/packages/3-targets/3-targets/postgres/README.md b/packages/3-targets/3-targets/postgres/README.md index 1c13ae89e6d9..3ce5c6d901bc 100644 --- a/packages/3-targets/3-targets/postgres/README.md +++ b/packages/3-targets/3-targets/postgres/README.md @@ -26,7 +26,7 @@ Provides the Postgres target descriptor (`SqlControlTargetDescriptor`) for CLI c - **Generated Defaults Policy**: Treats client-generated defaults as non-DB defaults when emitting DDL - **Database Dependency Consumption**: The planner extracts database dependencies from the configured framework components (passed as `frameworkComponents`), verifies each dependency against the live schema, and only emits install operations when required. The runner reuses the same metadata for post-apply verification, so there are no hardcoded extension mappings—database dependencies stay component-owned. - **Storage Type Planning**: The planner dispatches storage type hooks for `storage.types` and emits type operations before table creation when supported by the policy -- **Contract-to-PSL Printing**: Implements the `buildPslContract` descriptor hook, which `prisma contract print` uses. It takes a Postgres contract the contract serializer accepted, from any source, and returns the Prisma 8 PSL document that reads back as the same contract. It does not check the contract's structure again; a broken structure is an internal error. It writes models, value objects, named types, domain and native enums, keys with their names, indexes, checks, relations, polymorphism, control policies, row-level security (`@@rls`, policies, and roles in `namespace unbound`) and defaults. A column is written as a type constructor the configured stack contributes that reads back with its codec, native type and type parameters: the constructor marked `inferred` for the column's data type, which is the one `contract infer` writes, when it does, or else the first constructor in the stack that does, such as `TimestamptzTemporal` or `TimestamptzTemporal(3)` for a `Temporal`-backed `timestamptz` column, because the target lists each explicit type before a second name for it such as `DateTime`, or an extension's `pgvector.Vector(3)`. A value-object member has no column, so its type goes through the stack the same way: the stack's codec names the native type for the member's type parameters, and the member is written as the type constructor for that native type, called with those parameters (`Numeric(10, 2)`); a member typed by a domain enum is written by the enum's name. The contract does not record which named type a member used, so a member written `code Short`, with `types { Short = VarChar(10) }`, is printed as the type it stands for, `code VarChar(10)`; a model field keeps `Short`, because its column names the named type. A literal default is written by the data type of the column's codec. The family passes the stack's type constructors, codecs and data types to the hook for this. Anything it cannot write so that it reads back the same is refused by name with `CONTRACT.PRINT_UNSUPPORTED` (the full list is in `docs/reference/error-reference.md`, one function per case in `src/core/psl-print/refusals.ts`); nothing is dropped. It lives in `src/core/psl-print/`, the counterpart of the `inferPslContract` hook in `src/core/psl-infer/`, which writes PSL from a live database. Both build with the shared builders in `src/core/psl-build/`: literals, index and check attributes, and the native enum block. Both find the constructor marked `inferred` for a data type through the SQL family's `@internal/family-sql/psl-build`. Model and field names come from the contract; `@@map` and `@map` appear only where the name the PSL source would derive differs. A unique constraint prints as `@@unique` and a unique index as `@@index(..., unique: true)`; every index prints explicitly, and a relation writes `index: false` when nothing backs its foreign key, no `index` argument when a relation that says nothing would be backed by the same object (`defaultForeignKeyIndex`), and otherwise `index: ""` naming the index, or the key whose first columns are the foreign key's. Execution generators join back onto their columns, through whatever the configured stack contributes, extensions included. A column that is not a list and whose PSL type stands for its data type prints as the call of the field preset that writes its codec with its generators, built from the preset's print form, when the preset accepts that call and it reads back with the column's codec, type params and generators. A column typed by a named type, a value object or an enum, and a column whose call fails those checks, such as one without a value for a required argument, does not. A column with only a create generator then prints it as `@default((...))`, the call of the default function that declares `printArguments` for it; any other column is refused. +- **Contract-to-PSL Printing**: Implements the `buildPslContract` descriptor hook, which `prisma contract print` uses. It takes a Postgres contract the contract serializer accepted, from any source, and returns the Prisma 8 PSL document that reads back as the same contract. It does not check the contract's structure again; a broken structure is an internal error. It writes models, value objects, named types, domain and native enums, keys with their names, indexes, checks, relations, polymorphism, control policies, row-level security (`@@rls`, policies, and roles in `namespace unbound`) and defaults. A column is written as a type constructor the configured stack contributes that reads back with its codec, native type and type parameters: the constructor marked `inferred` for the column's data type, which is the one `contract infer` writes, when it does, or else the first constructor in the stack that does, such as `TimestamptzTemporal` or `TimestamptzTemporal(3)` for a `Temporal`-backed `timestamptz` column, or an extension's `pgvector.Vector(3)`. A convenience type such as `DateTime` is a type choice, not a constructor, so it is never written. A value-object member has no column, so its type goes through the stack the same way: the stack's codec names the native type for the member's type parameters, and the member is written as the type constructor for that native type, called with those parameters (`Numeric(10, 2)`); a member typed by a domain enum is written by the enum's name. The contract does not record which named type a member used, so a member written `code Short`, with `types { Short = VarChar(10) }`, is printed as the type it stands for, `code VarChar(10)`; a model field keeps `Short`, because its column names the named type. A literal default is written by the data type of the column's codec. The family passes the stack's type constructors, codecs and data types to the hook for this. Anything it cannot write so that it reads back the same is refused by name with `CONTRACT.PRINT_UNSUPPORTED` (the full list is in `docs/reference/error-reference.md`, one function per case in `src/core/psl-print/refusals.ts`); nothing is dropped. It lives in `src/core/psl-print/`, the counterpart of the `inferPslContract` hook in `src/core/psl-infer/`, which writes PSL from a live database. Both build with the shared builders in `src/core/psl-build/`: literals, index and check attributes, and the native enum block. Both find the constructor marked `inferred` for a data type through the SQL family's `@internal/family-sql/psl-build`. Model and field names come from the contract; `@@map` and `@map` appear only where the name the PSL source would derive differs. A unique constraint prints as `@@unique` and a unique index as `@@index(..., unique: true)`; every index prints explicitly, and a relation writes `index: false` when nothing backs its foreign key, no `index` argument when a relation that says nothing would be backed by the same object (`defaultForeignKeyIndex`), and otherwise `index: ""` naming the index, or the key whose first columns are the foreign key's. Execution generators join back onto their columns, through whatever the configured stack contributes, extensions included. A column that is not a list and whose PSL type stands for its data type prints as the call of the field preset that writes its codec with its generators, built from the preset's print form, when the preset accepts that call and it reads back with the column's codec, type params and generators. A column typed by a named type, a value object or an enum, and a column whose call fails those checks, such as one without a value for a required argument, does not. A column with only a create generator then prints it as `@default((...))`, the call of the default function that declares `printArguments` for it; any other column is refused. - **Database-to-PSL inference**: Implements the `inferPslContract` descriptor hook, which `prisma contract infer` uses. Introspection records each column's element type as the database reports it (`format_type`, its kind, schema and name). Infer resolves that reported type through the data types of the configured stack, the target's and the extension packs', and writes the constructor marked `inferred` for the data type, with the fewest arguments that give the same parameters (`numeric(10,0)` is written `Numeric(10)`); a list column adds `[]`, and a native enum column is written `pg.enum()`. When a column's type is claimed by no data type, or by one with no marked constructor (such as `tsquery`), infer writes nothing and fails with `CONTRACT.INFER_COLUMN_TYPE_UNCLAIMED`, listing every such column as `.
.: `. No configured pack has a type for such a column. When the type comes from a database extension that has a Prisma 8 pack, such as `pgvector` or `postgis`, adding the pack to `extensions` in `prisma.config.ts` lets infer write it, for the forms of the type the pack supports. Otherwise, as for domains, `"char"`, `hstore` and `tsquery`, the column's type must change, or the column must go. The rule itself (resolving a reported type through the data types, the fewest-arguments call, the unclaimed error) lives in the SQL family's `@internal/family-sql/psl-build`; the target adds the walk over its schema tree and the native enum handling in `src/core/psl-infer/`. - **`Temporal` for the target's own code**: The `Temporal`-backed codecs (`pg/date-temporal@1`, `pg/time-temporal@1`, `pg/timestamp-temporal@1`, `pg/timestamp-instant@1` and `pg/timestamptz-temporal@1`), when they decode, and the `instantNow`, `plainDateTimeNow`, `plainDateNow` and `plainTimeNow` generators get `Temporal` from `requireTemporal()` (`src/core/require-temporal.ts`): the runtime's global `Temporal` when it has one, else the fallback `Temporal`, else it throws `RUNTIME.TEMPORAL_UNAVAILABLE`. The text codecs (`pg/date-string@1`, `pg/time-string@1`, `pg/timestamp-string@1`, `pg/timestamptz-string@1` and `pg/timetz@1`), `pg/timestamptz-date@1`, `pg/timestamp-date@1`, `pg/interval@1`, `pg/interval-string@1`, the `timestampNow` generator and the string generators (`timestampStringNow`, `timestamptzStringNow`, `dateStringNow`, `timeStringNow` and `timetzNow`) do not use `Temporal`, so an application that uses only them reads and writes its date and time columns without one. The target's control entry (`@internal/target-postgres/control`) sets the fallback `Temporal`, from `temporal-polyfill`, when it is loaded. Nothing sets `globalThis.Temporal`. No other entry of this package imports `temporal-polyfill`. `temporal-polyfill` is a required peer dependency of this package and of the published `@prisma/orm-target-postgres` and `@prisma/orm-postgres`, so a project that already installs it for its own code has one copy. npm, pnpm and bun install it automatically; a project that uses Yarn adds `temporal-polyfill` to its own dependencies. In the published package, `@prisma/orm-target-postgres/target` is the aggregate of every entry of this package, the control entry included, so it loads the control entry too; an application imports the entry it needs, for example `@prisma/orm-target-postgres/target/runtime`. The fallback is held once per process, and every `Temporal`-backed codec in the process uses it. An application process that loads no control-plane code and has no global `Temporal` gets `RUNTIME.TEMPORAL_UNAVAILABLE` when one of those codecs decodes or one of those generators runs. An application that loads control-plane code in its own process, such as server code under `vite dev` with the Prisma Vite plugin or a script that calls the control client, decodes dates without its own `Temporal`, and then fails in production. An application that uses the Temporal codecs must load its own `Temporal`. Every signature that names a `Temporal` type takes it from `temporal-spec`, a package of type declarations with no runtime code (`import type { Temporal } from 'temporal-spec'`), never from a global declaration, so a Temporal field is typed precisely in a project that has no global `Temporal` declarations. `tsconfig.no-temporal-global.json` type-checks `src` without them. - **Runtime List Framing**: Parses inbound Postgres array text for contract-declared list columns before applying the scalar element codec. Builtin arrays and enum arrays therefore share one target-owned decode path; fixed-scale `numeric(30,10)[]` reads database-normalized text such as `"1.5000000000"`, matching scalar numeric decoding rather than the previous driver numeric-array float spelling `"1.5"`. diff --git a/skills/prisma-8/references/contract.md b/skills/prisma-8/references/contract.md index c8202045d4c7..84cb92a81848 100644 --- a/skills/prisma-8/references/contract.md +++ b/skills/prisma-8/references/contract.md @@ -117,7 +117,9 @@ model Post { Then run `pnpm prisma contract emit` (or rely on the Vite plugin — see `references/build.md`). Specify cascade behaviour explicitly with `onDelete` / `onUpdate`; the default is `Restrict`. -**Temporal columns.** On PostgreSQL, `DateTemporal`, `TimestampTemporal(p)`, `TimestampInstant(p)`, `TimestamptzTemporal(p)` and `TimeTemporal(p)` (and `Date`, `DateTime` and `Time`, which today mean the same as `DateTemporal`, `TimestamptzTemporal` and `TimeTemporal`) read and write `Temporal` values (`Temporal.PlainDate`, `PlainDateTime`, `Instant` for both instant types, `PlainTime`), never JavaScript `Date`. They need a global `Temporal` at query time: Node.js 26.8.2 and later ship `globalThis.Temporal`; 26.8.1 and earlier — including every 22 and 24 release — do not, and the first read or write of such a column throws `RUNTIME.TEMPORAL_UNAVAILABLE`. On those runtimes either `import 'temporal-polyfill/full/global'` before the first query (add `temporal-polyfill` as a dependency) or author the column as `DateString` / `TimestampString(p)` / `TimestamptzString(p)` / `TimeString(p)`, which carry text and need no `Temporal`, or as `TimestamptzJsDate(p)` / `TimestampJsDate(p)`, which read a JavaScript `Date`. `IntervalParts(p)` reads `{ months, days, micros }` and `IntervalString(p)` an ISO 8601 duration such as `P1Y2M`. `prisma contract emit`, `prisma db init` and the other commands need no global `Temporal`: the Postgres target loads `temporal-polyfill` as a fallback `Temporal` for the control plane, and sets no global. `temporal-polyfill` is a required peer dependency of `@prisma/orm-postgres`. npm, pnpm and bun install it automatically. With Yarn, add `temporal-polyfill` (`^1.0.4`) to the project's dependencies, or the commands fail because Node.js cannot find the package. That fallback is held once per process. An application that loads control-plane code in its own process, such as server code under `vite dev` with the Prisma Vite plugin or a script that calls the control client, decodes dates without its own `Temporal`, and then fails in production. An application that uses the Temporal codecs must load its own `Temporal`. A TypeScript contract file that constructs a `Temporal` value must load an implementation too. +**Date and time types.** On PostgreSQL, write a field with a convenience type named for what it means: `DateTime` (a moment in time, a `timestamptz(3)` read as a JavaScript `Date`), `Date` (a calendar date, read as a string such as `"2024-02-29"`), `Time` (a time of day, read as a string) or `Duration` (an ISO 8601 duration string such as `"P1M"`). None needs `Temporal`. Optional arguments choose another explicit type: `value` (`jsDate`, `temporal` or `string` for `DateTime`; `string` or `temporal` for `Date` and `Time`; `string` or `parts` for `Duration`), `column` (`timestamptz` or `timestamp` for `DateTime`; `time` or, with `value: string`, `timetz` for `Time`), and `precision`: `DateTime(value: temporal)`, `DateTime(column: timestamp)`, `Time(precision: 0)`. The contract stores only the explicit type, and `contract print` writes it; the full table is [Scalar types](https://github.com/prisma/orm/blob/main/docs/reference/scalar-types.md). `Time(3)` is refused, because the first argument is `value`; write `Time(precision: 3)`. + +**Temporal columns.** On PostgreSQL, `DateTemporal`, `TimestampTemporal(p)`, `TimestampInstant(p)`, `TimestamptzTemporal(p)` and `TimeTemporal(p)` (and a convenience type with `value: temporal`) read and write `Temporal` values (`Temporal.PlainDate`, `PlainDateTime`, `Instant` for both instant types, `PlainTime`), never JavaScript `Date`. They need a global `Temporal` at query time: Node.js 26.8.2 and later ship `globalThis.Temporal`; 26.8.1 and earlier — including every 22 and 24 release — do not, and the first read or write of such a column throws `RUNTIME.TEMPORAL_UNAVAILABLE`. On those runtimes either `import 'temporal-polyfill/full/global'` before the first query (add `temporal-polyfill` as a dependency) or author the column as `DateString` / `TimestampString(p)` / `TimestamptzString(p)` / `TimeString(p)`, which carry text and need no `Temporal`, or as `TimestamptzJsDate(p)` / `TimestampJsDate(p)`, which read a JavaScript `Date`. `IntervalParts(p)` reads `{ months, days, micros }` and `IntervalString(p)` an ISO 8601 duration such as `P1Y2M`. `prisma contract emit`, `prisma db init` and the other commands need no global `Temporal`: the Postgres target loads `temporal-polyfill` as a fallback `Temporal` for the control plane, and sets no global. `temporal-polyfill` is a required peer dependency of `@prisma/orm-postgres`. npm, pnpm and bun install it automatically. With Yarn, add `temporal-polyfill` (`^1.0.4`) to the project's dependencies, or the commands fail because Node.js cannot find the package. That fallback is held once per process. An application that loads control-plane code in its own process, such as server code under `vite dev` with the Prisma Vite plugin or a script that calls the control client, decodes dates without its own `Temporal`, and then fails in production. An application that uses the Temporal codecs must load its own `Temporal`. A TypeScript contract file that constructs a `Temporal` value must load an implementation too. `@@index` also accepts `expression:` (instead of a fields list), `where:` (partial-index predicate), `unique:`, `type:`/`options:` (target-registered access method), and `name:` xor `map:`. Raw SQL in `expression:` and `where:` is a `sql` literal; a quoted string is refused with `PSL_VALUE_TYPE_INCOMPATIBLE`, whose message gives the rewrite: diff --git a/skills/prisma-8/references/debug.md b/skills/prisma-8/references/debug.md index 1924419b2ef5..599ead4fc595 100644 --- a/skills/prisma-8/references/debug.md +++ b/skills/prisma-8/references/debug.md @@ -110,7 +110,7 @@ The single source of truth: read the envelope, find the row by `code`, follow th | `BUDGET.ROWS_EXCEEDED` / `BUDGET.TIME_EXCEEDED` | Runtime, when the `budgets` middleware is active | Tune `budgets({ maxRows, maxLatencyMs, ... })` or rewrite the query. See `references/runtime.md`. | | `LINT.SELECT_STAR` / `LINT.NO_LIMIT` / `LINT.DELETE_WITHOUT_WHERE` / `LINT.UPDATE_WITHOUT_WHERE` / `LINT.READ_ONLY_MUTATION` | Runtime, when the `lints` middleware is active | Fix the query (add a `WHERE` / `LIMIT` / explicit columns), or relax the lint config. See `references/runtime.md`. | | `PLAN.HASH_MISMATCH` | Runtime, executing a precompiled plan | The contract the plan was built against does not match the runtime contract. Re-emit, rebuild, redeploy. | -| `RUNTIME.TEMPORAL_UNAVAILABLE` | Runtime, first read or write touching a Temporal-backed column (`DateTemporal`, `TimestampTemporal(p)`, `TimestampInstant(p)`, `TimestamptzTemporal(p)`, `TimeTemporal(p)`, and `Date`, `DateTime` and `Time`) or a `temporal.updatedAt()` clock | No global `Temporal`. Node.js 26.8.2 and later ship `globalThis.Temporal`; 26.8.1 and earlier — including every 22 and 24 — do not. Either `import 'temporal-polyfill/full/global'` before the first query, or author the column as `DateString` / `TimestampString(p)` / `TimestamptzString(p)` / `TimeString(p)` to read PostgreSQL's own text. | +| `RUNTIME.TEMPORAL_UNAVAILABLE` | Runtime, first read or write touching a Temporal-backed column (`DateTemporal`, `TimestampTemporal(p)`, `TimestampInstant(p)`, `TimestamptzTemporal(p)`, `TimeTemporal(p)`, and `DateTime`, `Date` or `Time` with `value: temporal`) or a `temporal.updatedAt()` clock | No global `Temporal`. Node.js 26.8.2 and later ship `globalThis.Temporal`; 26.8.1 and earlier — including every 22 and 24 — do not. Either `import 'temporal-polyfill/full/global'` before the first query, or write the field as a convenience type at its default (`DateTime`, `Date`, `Time`), which reads a JavaScript `Date` or a string. | | `ORM.RELATION_MUTATION_UNSUPPORTED` | ORM nested `create` / `connect` on an N:M relation whose junction has required payload columns | Write the junction table directly or use the SQL builder (`meta.junction`). | | `RUNTIME.ABORTED` (`details.phase` = `encode\|decode\|stream\|beforeEncode\|onRow`) | Runtime, when an `AbortSignal` fires mid-execute | Cancellation, not a bug; surface to the caller. | | `SqlQueryError` (no dotted code) | Raw-SQL paths surfacing a driver error | Inspect `sqlState` + `constraint` + `table` + `column`. Postgres `23505` = unique violation, `23503` = foreign-key violation, etc. Fix the data or the schema. | diff --git a/skills/prisma-8/references/queries-postgres.md b/skills/prisma-8/references/queries-postgres.md index 8cc31baad00c..5de9423ce5dd 100644 --- a/skills/prisma-8/references/queries-postgres.md +++ b/skills/prisma-8/references/queries-postgres.md @@ -430,7 +430,7 @@ await db.orm.public.User.with(createdSince(since)).all(); await db.orm.public.Post.with(createdSince(since)).deleteAll(); ``` -Pick the builder whose codec matches the field's codec in `contract.d.ts`: a PSL `DateTime` is `field.temporal.timestamptz()` (`pg/timestamptz-temporal@1`), a `String` is `field.text()` (`pg/text@1`), a `Uuid` is `field.uuidNative()` (`pg/uuid@1`). `field.column(columnType)` is the explicit form. A package that offers a fragment and does not import the facade writes `{ codecId: 'pg/text@1', nullable: false }`, with a codec of the contract. For a list field such as `String[]`, add `.many()` to the builder or `many: { elementNullable: false }` to the object; for a `String?[]`, whose elements may be null, add `.many({ elementsNullable: true })` or `many: { elementNullable: true }`. Without it the declaration does not match a list field, and with it the element nullability must match too. A list of value objects such as `Address[]` is stored as one `jsonb` value and is still a list: `field.column(jsonbColumn).many()`. The body may call `where`, `orderBy`, `limit` and `offset`. The result keeps the collection's class and records the filter, so `update` and `delete` are allowed after a fragment that filters. `updateAll`, `deleteAll` and their `AndCount` forms change every matching row and throw `ORM.ARGUMENT_INVALID` when the chain has a `limit`, `offset`, `cursor`, `distinct` or `distinctOn`, so do not put a limit in a fragment that will be followed by one of them. `update` and `delete` change the one row `first()` returns, so the order, offset, cursor, `distinct` and `distinctOn` choose it, and after `limit(0)` they change nothing and return `null`; `update` with a relation callback throws on an order, a limit, an offset, a cursor, `distinct` or `distinctOn`, because it finds its row by the filter alone. A model without the field, or with the field under another codec or nullability, is a compile error, and a run-time `ORM.FIELD_UNKNOWN`. A custom collection class carries no namespace in its type, so at compile time it matches only a field that every model of that name has, with the same codec and nullability, and its real namespace is checked at run time. The codec, the nullability, whether the field is a list and the nullability of its elements are compared; the column type's parameters are not, so `field.uuidString()` (`char(36)`) also matches a `char(10)` field. +Pick the builder whose codec matches the field's codec in `contract.d.ts`: a PSL `TimestamptzTemporal` is `field.temporal.timestamptz()` (`pg/timestamptz-temporal@1`), a `String` is `field.text()` (`pg/text@1`), a `Uuid` is `field.uuidNative()` (`pg/uuid@1`). `field.column(columnType)` is the explicit form. A package that offers a fragment and does not import the facade writes `{ codecId: 'pg/text@1', nullable: false }`, with a codec of the contract. For a list field such as `String[]`, add `.many()` to the builder or `many: { elementNullable: false }` to the object; for a `String?[]`, whose elements may be null, add `.many({ elementsNullable: true })` or `many: { elementNullable: true }`. Without it the declaration does not match a list field, and with it the element nullability must match too. A list of value objects such as `Address[]` is stored as one `jsonb` value and is still a list: `field.column(jsonbColumn).many()`. The body may call `where`, `orderBy`, `limit` and `offset`. The result keeps the collection's class and records the filter, so `update` and `delete` are allowed after a fragment that filters. `updateAll`, `deleteAll` and their `AndCount` forms change every matching row and throw `ORM.ARGUMENT_INVALID` when the chain has a `limit`, `offset`, `cursor`, `distinct` or `distinctOn`, so do not put a limit in a fragment that will be followed by one of them. `update` and `delete` change the one row `first()` returns, so the order, offset, cursor, `distinct` and `distinctOn` choose it, and after `limit(0)` they change nothing and return `null`; `update` with a relation callback throws on an order, a limit, an offset, a cursor, `distinct` or `distinctOn`, because it finds its row by the filter alone. A model without the field, or with the field under another codec or nullability, is a compile error, and a run-time `ORM.FIELD_UNKNOWN`. A custom collection class carries no namespace in its type, so at compile time it matches only a field that every model of that name has, with the same codec and nullability, and its real namespace is checked at run time. The codec, the nullability, whether the field is a list and the nullability of its elements are compared; the column type's parameters are not, so `field.uuidString()` (`char(36)`) also matches a `char(10)` field. For a filter used inside a larger `where`, a plain function of the row works too. Type the field with `CodecField`, the type of any field with that codec: From 8b2235a96085f247bc278ed2ee47affcef31ceb4 Mon Sep 17 00:00:00 2001 From: willbot Date: Thu, 22 Oct 2026 20:55:01 +0200 Subject: [PATCH 047/118] Say an option argument takes bare names when a value is written in its place Co-Authored-By: Claude Opus 5.5 Signed-off-by: willbot Signed-off-by: Will Madden --- .../2-authoring/psl-parser/src/authoring-arguments.ts | 2 +- .../2-authoring/psl-parser/test/authoring-arguments.test.ts | 2 +- .../test/interpreter.defaults.temporal-codecs.test.ts | 3 ++- 3 files changed, 4 insertions(+), 3 deletions(-) diff --git a/packages/1-framework/2-authoring/psl-parser/src/authoring-arguments.ts b/packages/1-framework/2-authoring/psl-parser/src/authoring-arguments.ts index f541144e3ebb..33cbdd891a9c 100644 --- a/packages/1-framework/2-authoring/psl-parser/src/authoring-arguments.ts +++ b/packages/1-framework/2-authoring/psl-parser/src/authoring-arguments.ts @@ -378,7 +378,7 @@ function readPslHelperArgument( helperLabel: input.helperLabel, message: descriptor.kind === 'option' && descriptor.name !== undefined - ? `cannot parse ${argumentLabel}: ${argumentLabel} is ${argument.value.trim()}, but argument "${descriptor.name}" takes one of: ${descriptor.values.join(', ')}.` + ? `cannot parse ${argumentLabel}: ${argumentLabel} is ${argument.value.trim()}, but argument "${descriptor.name}" takes one of these names, written without quotes: ${descriptor.values.join(', ')}.` : `cannot parse ${argumentLabel} for descriptor kind "${descriptor.kind}".`, }); return INVALID_AUTHORING_ARGUMENT; diff --git a/packages/1-framework/2-authoring/psl-parser/test/authoring-arguments.test.ts b/packages/1-framework/2-authoring/psl-parser/test/authoring-arguments.test.ts index b683320385b0..e4a69dfd6134 100644 --- a/packages/1-framework/2-authoring/psl-parser/test/authoring-arguments.test.ts +++ b/packages/1-framework/2-authoring/psl-parser/test/authoring-arguments.test.ts @@ -130,7 +130,7 @@ describe('mapPslHelperArgs argument kinds', () => { [{ kind: 'option', name: 'value', values: ['string', 'temporal'] }], ); expect(diagnostics[0]?.message).toMatch( - /argument #1 is 3, but argument "value" takes one of: string, temporal\.$/, + /argument #1 is 3, but argument "value" takes one of these names, written without quotes: string, temporal\.$/, ); }); diff --git a/packages/2-sql/2-authoring/contract-psl/test/interpreter.defaults.temporal-codecs.test.ts b/packages/2-sql/2-authoring/contract-psl/test/interpreter.defaults.temporal-codecs.test.ts index 01006da8be10..1edbf0841b0b 100644 --- a/packages/2-sql/2-authoring/contract-psl/test/interpreter.defaults.temporal-codecs.test.ts +++ b/packages/2-sql/2-authoring/contract-psl/test/interpreter.defaults.temporal-codecs.test.ts @@ -179,7 +179,8 @@ stamped ${field} { name: 'a quoted option value (one spelling only)', field: 'temporal.timestamp(onCreate: "now")', - message: /cannot parse named argument "onCreate" for descriptor kind "option"/, + message: + /cannot parse named argument "onCreate": named argument "onCreate" is "now", but argument "onCreate" takes one of these names, written without quotes: now\./, }, { name: 'a duplicate named argument', From 75dcd251cabaa2f7bb67170b1df9a65ece912009 Mon Sep 17 00:00:00 2001 From: willbot Date: Thu, 22 Oct 2026 21:00:33 +0200 Subject: [PATCH 048/118] Keep SQL vocabulary out of the language server's hover helper name Co-Authored-By: Claude Opus 5.5 Signed-off-by: willbot Signed-off-by: Will Madden --- packages/1-framework/3-tooling/language-server/src/hover.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/1-framework/3-tooling/language-server/src/hover.ts b/packages/1-framework/3-tooling/language-server/src/hover.ts index cff29c140886..74c8c4855f79 100644 --- a/packages/1-framework/3-tooling/language-server/src/hover.ts +++ b/packages/1-framework/3-tooling/language-server/src/hover.ts @@ -150,12 +150,12 @@ function renderContributedTypeContent( symbol.descriptor.kind === 'fieldPreset' ? undefined : symbol.descriptor.documentation; const content = withDocumentation(fence, documentation); return symbol.descriptor.kind === 'typeChoice' - ? `${content}\n\n${renderTypeChoiceTable(symbol.descriptor)}` + ? `${content}\n\n${renderTypeChoices(symbol.descriptor)}` : content; } /** Each combination of a type choice's options, as written, and the explicit type it becomes. */ -function renderTypeChoiceTable(descriptor: AuthoringTypeChoiceDescriptor): string { +function renderTypeChoices(descriptor: AuthoringTypeChoiceDescriptor): string { const options = descriptor.args.filter(isAuthoringTypeChoiceOption); const cell = (value: string | undefined, option: AuthoringTypeChoiceOption) => value === option.default ? `${value} (default)` : (value ?? ''); From ac2581a2bd2a83d0631d26bfa6bd812cba55f1ba Mon Sep 17 00:00:00 2001 From: willbot Date: Thu, 22 Oct 2026 21:01:48 +0200 Subject: [PATCH 049/118] TML-3709: Slice 5 rebuilds the temporal.* aliases on DateTime Co-Authored-By: Claude Opus 5.5 Signed-off-by: willbot Signed-off-by: Will Madden --- projects/datetime-convenience-types/plan.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/projects/datetime-convenience-types/plan.md b/projects/datetime-convenience-types/plan.md index 43882661c3da..e1be20c0414c 100644 --- a/projects/datetime-convenience-types/plan.md +++ b/projects/datetime-convenience-types/plan.md @@ -36,7 +36,7 @@ A type constructor's `codecId` may be a `select` over `option` arguments; every ### Slice 5 — The tools write convenience types -`contract infer` and `contract print` write convenience types, with arguments only where they differ from the defaults, and the explicit type only where no convenience type fits. The Prisma 7 reader maps `DateTime` to `DateTime(column: timestamp)` and `@db.*` date and time columns to the matching convenience form. `prisma orm init` writes `DateTime`. Upgrade instructions: re-emit and `db sign` for adopted projects. Update slice 2's upgrade instruction (`upgrade-instructions/pending/prisma7-now-default-on-create/`), which names the type the reader picks today (`TimestampString(3)`). +`contract infer` and `contract print` write convenience types, with arguments only where they differ from the defaults, and the explicit type only where no convenience type fits. The Prisma 7 reader maps `DateTime` to `DateTime(column: timestamp)` and `@db.*` date and time columns to the matching convenience form. `prisma orm init` writes `DateTime`. The contributed `temporal.CreatedAt` and `temporal.UpdatedAt` aliases are rebuilt on `DateTime` (`DateTime @onCreate(now())`, and `@onUpdate(now())` for the second), and their `String` and `JsDate` variants are removed, per ADR 279; contracts that use them change codec, so the upgrade instruction says to re-emit and `db sign`. Upgrade instructions: re-emit and `db sign` for adopted projects. Update slice 2's upgrade instruction (`upgrade-instructions/pending/prisma7-now-default-on-create/`), which names the type the reader picks today (`TimestampString(3)`). ## Dependencies From b5ede4ac0d9c390968670978c7169054c0ebcb28 Mon Sep 17 00:00:00 2001 From: willbot Date: Thu, 22 Oct 2026 21:01:57 +0200 Subject: [PATCH 050/118] Say how the date and time upgrade relates to the convenience types The instructions said DateTime, Date and Time keep their meaning in this release. The convenience date and time types change them, likely in the same release, so the instructions now say this change leaves them as they are, that the convenience types give them new meanings, and that the explicit names keep what a field reads today. Co-Authored-By: Claude Opus 5.5 Signed-off-by: willbot Signed-off-by: Will Madden --- .../pending/explicit-date-time-types/app/instructions.md | 2 +- .../pending/explicit-date-time-types/extension/instructions.md | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/upgrade-instructions/pending/explicit-date-time-types/app/instructions.md b/upgrade-instructions/pending/explicit-date-time-types/app/instructions.md index 667530d7b986..60d4b1d58904 100644 --- a/upgrade-instructions/pending/explicit-date-time-types/app/instructions.md +++ b/upgrade-instructions/pending/explicit-date-time-types/app/instructions.md @@ -51,7 +51,7 @@ On PostgreSQL, a column type that several codecs read now names the kind of valu | `Timestamp(p?)` | `TimestampTemporal(p?)` | `Temporal.PlainDateTime` | | `Interval(p?)` | `IntervalParts(p?)` | `{ months, days, micros }` | -`DateTime`, `Date` and `Time` keep their meaning in this release; `DateTemporal` and `TimeTemporal` are new names for `Date` and `Time`. +This change leaves `DateTime`, `Date` and `Time` as they are, and adds `DateTemporal` and `TimeTemporal` as the explicit names of what `Date` and `Time` read today. A separate change, the convenience date and time types, gives `DateTime`, `Date` and `Time` new meanings and has its own instructions. To keep what a field reads today, write the explicit name: `TimestamptzTemporal` for `DateTime`, `DateTemporal` for `Date` and `TimeTemporal` for `Time`. Three explicit types are new, and need no change to existing schemas: `TimestampJsDate(p?)`, a `timestamp` column read as a JavaScript `Date`; `TimestampInstant(p?)`, a `timestamp` column read as a `Temporal.Instant`; and `IntervalString(p?)`, an `interval` column read as an ISO 8601 duration such as `P1Y2M`. Both timestamp types read and write the stored date and time as UTC. diff --git a/upgrade-instructions/pending/explicit-date-time-types/extension/instructions.md b/upgrade-instructions/pending/explicit-date-time-types/extension/instructions.md index fb91569d0259..37e0b0d5d796 100644 --- a/upgrade-instructions/pending/explicit-date-time-types/extension/instructions.md +++ b/upgrade-instructions/pending/explicit-date-time-types/extension/instructions.md @@ -51,7 +51,7 @@ On PostgreSQL, a column type that several codecs read now names the kind of valu | `Timestamp(p?)` | `TimestampTemporal(p?)` | `Temporal.PlainDateTime` | | `Interval(p?)` | `IntervalParts(p?)` | `{ months, days, micros }` | -`DateTime`, `Date` and `Time` keep their meaning in this release; `DateTemporal` and `TimeTemporal` are new names for `Date` and `Time`. +This change leaves `DateTime`, `Date` and `Time` as they are, and adds `DateTemporal` and `TimeTemporal` as the explicit names of what `Date` and `Time` read today. A separate change, the convenience date and time types, gives `DateTime`, `Date` and `Time` new meanings and has its own instructions. To keep what a field reads today, write the explicit name: `TimestamptzTemporal` for `DateTime`, `DateTemporal` for `Date` and `TimeTemporal` for `Time`. Three explicit types are new, and need no change to existing schemas: `TimestampJsDate(p?)`, a `timestamp` column read as a JavaScript `Date`; `TimestampInstant(p?)`, a `timestamp` column read as a `Temporal.Instant`; and `IntervalString(p?)`, an `interval` column read as an ISO 8601 duration such as `P1Y2M`. Both timestamp types read and write the stored date and time as UTC. From a479f37fdc5a561e657e64c6caff8d7ec8dbb9b8 Mon Sep 17 00:00:00 2001 From: willbot Date: Thu, 22 Oct 2026 21:22:14 +0200 Subject: [PATCH 051/118] Regenerate the on-update-only fixture for the new codecs' aggregate rows The fixture arrived from main and lists every codec's aggregates. Co-Authored-By: Claude Opus 5.5 Signed-off-by: willbot Signed-off-by: Will Madden --- .../on-update-only/generated/contract.d.ts | 32 +++++++++++++++++++ .../on-update-only/generated/contract.d.ts | 32 +++++++++++++++++++ 2 files changed, 64 insertions(+) diff --git a/packages/3-extensions/sql-orm-client/test/fixtures/on-update-only/generated/contract.d.ts b/packages/3-extensions/sql-orm-client/test/fixtures/on-update-only/generated/contract.d.ts index 78960b7aa687..37b0bd85e647 100644 --- a/packages/3-extensions/sql-orm-client/test/fixtures/on-update-only/generated/contract.d.ts +++ b/packages/3-extensions/sql-orm-client/test/fixtures/on-update-only/generated/contract.d.ts @@ -43,6 +43,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; @@ -92,6 +96,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; @@ -101,6 +109,14 @@ export type AggregateTypes = { readonly output: 'pg/time-temporal@1'; readonly nullable: true; }; + readonly 'pg/timestamp-date@1': { + readonly output: 'pg/timestamp-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-instant@1': { + readonly output: 'pg/timestamp-instant@1'; + readonly nullable: true; + }; readonly 'pg/timestamp-string@1': { readonly output: 'pg/timestamp-string@1'; readonly nullable: true; @@ -152,6 +168,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; @@ -161,6 +181,14 @@ export type AggregateTypes = { readonly output: 'pg/time-temporal@1'; readonly nullable: true; }; + readonly 'pg/timestamp-date@1': { + readonly output: 'pg/timestamp-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-instant@1': { + readonly output: 'pg/timestamp-instant@1'; + readonly nullable: true; + }; readonly 'pg/timestamp-string@1': { readonly output: 'pg/timestamp-string@1'; readonly nullable: true; @@ -204,6 +232,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; diff --git a/test/integration/test/sql-orm-client/fixtures/on-update-only/generated/contract.d.ts b/test/integration/test/sql-orm-client/fixtures/on-update-only/generated/contract.d.ts index 78960b7aa687..37b0bd85e647 100644 --- a/test/integration/test/sql-orm-client/fixtures/on-update-only/generated/contract.d.ts +++ b/test/integration/test/sql-orm-client/fixtures/on-update-only/generated/contract.d.ts @@ -43,6 +43,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; @@ -92,6 +96,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; @@ -101,6 +109,14 @@ export type AggregateTypes = { readonly output: 'pg/time-temporal@1'; readonly nullable: true; }; + readonly 'pg/timestamp-date@1': { + readonly output: 'pg/timestamp-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-instant@1': { + readonly output: 'pg/timestamp-instant@1'; + readonly nullable: true; + }; readonly 'pg/timestamp-string@1': { readonly output: 'pg/timestamp-string@1'; readonly nullable: true; @@ -152,6 +168,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; @@ -161,6 +181,14 @@ export type AggregateTypes = { readonly output: 'pg/time-temporal@1'; readonly nullable: true; }; + readonly 'pg/timestamp-date@1': { + readonly output: 'pg/timestamp-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-instant@1': { + readonly output: 'pg/timestamp-instant@1'; + readonly nullable: true; + }; readonly 'pg/timestamp-string@1': { readonly output: 'pg/timestamp-string@1'; readonly nullable: true; @@ -204,6 +232,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; From 151761f08a044934dd2ecf66d49c8978888a9959 Mon Sep 17 00:00:00 2001 From: willbot Date: Thu, 22 Oct 2026 21:32:25 +0200 Subject: [PATCH 052/118] A convenience type's parameter error names its own argument DateTime(precision: 7) said argument 0 of DateTime, the position of precision in TimestamptzJsDate. PSL and the type.* helpers now name the argument as the convenience type declares it, and PSL reports it at that argument. Co-Authored-By: Claude Opus 5.5 Signed-off-by: willbot Signed-off-by: Will Madden --- .../src/exports/authoring.ts | 1 + .../src/shared/framework-authoring.ts | 25 ++++++++ .../contract-psl/src/psl-column-resolution.ts | 58 +++++++------------ .../test/interpreter.type-choices.test.ts | 13 ++++- .../src/authoring-helper-runtime.ts | 6 +- .../test/convenience-type-helpers.test.ts | 19 ++++++ 6 files changed, 79 insertions(+), 43 deletions(-) create mode 100644 packages/3-targets/postgres/9-facade/test/convenience-type-helpers.test.ts diff --git a/packages/1-framework/1-core/framework-components/src/exports/authoring.ts b/packages/1-framework/1-core/framework-components/src/exports/authoring.ts index a20b74d1669f..d8c9f245b7ec 100644 --- a/packages/1-framework/1-core/framework-components/src/exports/authoring.ts +++ b/packages/1-framework/1-core/framework-components/src/exports/authoring.ts @@ -135,6 +135,7 @@ export { resolveTypeAliasGenerators, typeConstructorLookup, validateAuthoringHelperArguments, + validateAuthoringTypeChoiceParams, validateAuthoringTypeParams, } from '../shared/framework-authoring'; export type { AuthoringOption } from '../shared/option-descriptor'; diff --git a/packages/1-framework/1-core/framework-components/src/shared/framework-authoring.ts b/packages/1-framework/1-core/framework-components/src/shared/framework-authoring.ts index 7de54919ccb8..8b07c83cabdb 100644 --- a/packages/1-framework/1-core/framework-components/src/shared/framework-authoring.ts +++ b/packages/1-framework/1-core/framework-components/src/shared/framework-authoring.ts @@ -1898,6 +1898,31 @@ export function validateAuthoringTypeParams( ); } +/** + * {@link validateAuthoringTypeParams} for a call of the type choice `choice` that chose `chosen`: a failure names the choice's own argument the failing parameter came from, by its name and its index in the choice's arguments. + */ +export function validateAuthoringTypeChoiceParams( + helperPath: string, + choice: AuthoringTypeChoiceDescriptor, + chosen: AuthoringTypeConstructorDescriptor, + typeParams: Record | undefined, + paramsSchema: StandardSchemaV1 | undefined, +): void { + const issue = authoringTypeParamsIssue(chosen.output, typeParams, paramsSchema); + if (issue === undefined) return; + const name = + issue.argumentIndex === undefined ? undefined : chosen.args?.[issue.argumentIndex]?.name; + const argumentIndex = + name === undefined ? -1 : choice.args.findIndex((argument) => argument.name === name); + throw runtimeError( + 'CONTRACT.ARGUMENT_INVALID', + argumentIndex < 0 + ? `The type parameters of ${helperPath} are invalid: ${issue.message}` + : `Argument "${name}" of ${helperPath} is invalid: ${issue.message}`, + { helperPath, ...(argumentIndex < 0 ? {} : { argumentIndex }) }, + ); +} + function resolveAuthoringStorageTypeTemplate( template: AuthoringStorageTypeTemplate, args: readonly unknown[], diff --git a/packages/2-sql-family/2-authoring/contract-psl/src/psl-column-resolution.ts b/packages/2-sql-family/2-authoring/contract-psl/src/psl-column-resolution.ts index bedad382ab5e..7034ac4e9c29 100644 --- a/packages/2-sql-family/2-authoring/contract-psl/src/psl-column-resolution.ts +++ b/packages/2-sql-family/2-authoring/contract-psl/src/psl-column-resolution.ts @@ -21,10 +21,10 @@ import { instantiateAuthoringTypeAlias, instantiateAuthoringTypeConstructor, isAuthoringEntityTypeDescriptor, - isAuthoringTypeChoiceOption, readWrittenValue, resolveAuthoringTypeChoice, validateAuthoringHelperArguments, + validateAuthoringTypeChoiceParams, validateAuthoringTypeParams, type WrittenScalar, type WrittenValue, @@ -142,8 +142,6 @@ export function checkPslTypeParams(input: { readonly codecLookup: CodecLookupWithDescriptors; readonly diagnostics: PslDiagnosticCollector; readonly source: DiagnosticSource; - /** The index in `args` of the argument at `index` of the constructor whose parameters are checked, when the call is written with other arguments than the constructor's. */ - readonly callArgumentIndex?: (index: number) => number | undefined; }): boolean { const helperPath = input.call.path.join('.'); try { @@ -159,26 +157,12 @@ export function checkPslTypeParams(input: { input.diagnostics.push({ code: 'PSL_INVALID_ATTRIBUTE_ARGUMENT', message: `${input.subject} ${message}`, - ...input.source.at( - argumentSpan( - input.call, - input.args, - callIndex(argumentIndexOf(error), input.callArgumentIndex), - ), - ), + ...input.source.at(argumentSpan(input.call, input.args, argumentIndexOf(error))), }); return false; } } -function callIndex( - index: number | undefined, - callArgumentIndex: ((index: number) => number | undefined) | undefined, -): number | undefined { - if (index === undefined || callArgumentIndex === undefined) return index; - return callArgumentIndex(index); -} - function argumentIndexOf(error: unknown): number | undefined { if (typeof error !== 'object' || error === null || !('details' in error)) return undefined; const { details } = error; @@ -316,26 +300,24 @@ export function instantiatePslTypeChoice(input: { } const output = instantiateAuthoringTypeConstructor(chosen.descriptor, chosen.args); - const passedOn = (chosen.descriptor.args ?? []).map((parameter) => - descriptor.args.findIndex( - (argument) => !isAuthoringTypeChoiceOption(argument) && argument.name === parameter.name, - ), - ); - const passed = checkPslTypeParams({ - call, - subject, - args: descriptor.args, - template: chosen.descriptor.output, - typeParams: output.typeParams, - codecLookup: input.codecLookup, - diagnostics: input.diagnostics, - source: input.source, - callArgumentIndex: (index) => { - const choiceIndex = passedOn[index]; - return choiceIndex === undefined || choiceIndex < 0 ? undefined : choiceIndex; - }, - }); - return passed ? output : undefined; + try { + validateAuthoringTypeChoiceParams( + helperPath, + descriptor, + chosen.descriptor, + output.typeParams, + input.codecLookup.descriptorFor(output.codecId)?.paramsSchema, + ); + } catch (error) { + if (isInternalError(error)) throw error; + input.diagnostics.push({ + code: 'PSL_INVALID_ATTRIBUTE_ARGUMENT', + message: `${subject}: ${error instanceof Error ? error.message : String(error)}`, + ...input.source.at(argumentSpan(call, descriptor.args, argumentIndexOf(error))), + }); + return undefined; + } + return output; } /** diff --git a/packages/2-sql-family/2-authoring/contract-psl/test/interpreter.type-choices.test.ts b/packages/2-sql-family/2-authoring/contract-psl/test/interpreter.type-choices.test.ts index b5846c07d44c..9cb39bc7b033 100644 --- a/packages/2-sql-family/2-authoring/contract-psl/test/interpreter.type-choices.test.ts +++ b/packages/2-sql-family/2-authoring/contract-psl/test/interpreter.type-choices.test.ts @@ -192,9 +192,16 @@ describe('a type choice in a field type', () => { ]); }); - it('refuses a precision the chosen type’s codec refuses, at the precision argument', () => { - expect(diagnostics(model('at Moment(precision: 7)'))).toMatchObject([ - { code: 'PSL_INVALID_ATTRIBUTE_ARGUMENT' }, + it('refuses a precision the chosen type’s codec refuses, naming the argument of the choice', () => { + const line = 'at Moment(value: temporal, column: local, precision: 7)'; + const start = ' '.length + line.indexOf('precision'); + expect(diagnostics(model(line))).toEqual([ + { + code: 'PSL_INVALID_ATTRIBUTE_ARGUMENT', + message: + 'Field "T.at" type "Moment": Argument "precision" of Moment is invalid: precision must be at most 6 (was 7)', + columns: [start + 1, start + 1 + 'precision: 7'.length], + }, ]); }); }); diff --git a/packages/2-sql-family/2-authoring/contract-ts/src/authoring-helper-runtime.ts b/packages/2-sql-family/2-authoring/contract-ts/src/authoring-helper-runtime.ts index 5eaa46cd3e13..c87e89152cb6 100644 --- a/packages/2-sql-family/2-authoring/contract-ts/src/authoring-helper-runtime.ts +++ b/packages/2-sql-family/2-authoring/contract-ts/src/authoring-helper-runtime.ts @@ -18,6 +18,7 @@ import { resolveAuthoringTypeChoice, typeConstructorLookup, validateAuthoringHelperArguments, + validateAuthoringTypeChoiceParams, validateAuthoringTypeParams, } from '@internal/framework-components/authoring'; import { @@ -153,9 +154,10 @@ export function createTypeHelpersFromNamespace( constructorNamed, }); const output = instantiateAuthoringTypeConstructor(chosen.descriptor, chosen.args); - validateAuthoringTypeParams( + validateAuthoringTypeChoiceParams( helperPath, - chosen.descriptor.output, + value, + chosen.descriptor, output.typeParams, lookups.codecLookup.descriptorFor(output.codecId)?.paramsSchema, ); diff --git a/packages/3-targets/postgres/9-facade/test/convenience-type-helpers.test.ts b/packages/3-targets/postgres/9-facade/test/convenience-type-helpers.test.ts new file mode 100644 index 000000000000..fd7effedfd15 --- /dev/null +++ b/packages/3-targets/postgres/9-facade/test/convenience-type-helpers.test.ts @@ -0,0 +1,19 @@ +import { describe, expect, it } from 'vitest'; +import { defineContract } from '../src/exports/contract-builder'; + +describe('a convenience type helper', () => { + it('refuses a precision its chosen type refuses, naming the argument of the convenience type', () => { + expect(() => + defineContract({}, ({ type, field, model }) => ({ + models: { + Order: model('Order', { + fields: { + id: field.id.uuidv4String(), + at: field.column(type.DateTime(undefined, undefined, 7)), + }, + }), + }, + })), + ).toThrow('Argument "precision" of DateTime is invalid: precision must be at most 6 (was 7)'); + }); +}); From fcb2fe4255e7bad70f57ee87629014b056212ba6 Mon Sep 17 00:00:00 2001 From: willbot Date: Thu, 22 Oct 2026 21:33:29 +0200 Subject: [PATCH 053/118] Type-test every PostgreSQL convenience row against its explicit helper Co-Authored-By: Claude Opus 5.5 Signed-off-by: willbot Signed-off-by: Will Madden --- .../temporal-field-types.test-d.ts | 43 +++++++++++++++++++ 1 file changed, 43 insertions(+) diff --git a/packages/3-targets/postgres/9-facade/test/no-temporal-global/temporal-field-types.test-d.ts b/packages/3-targets/postgres/9-facade/test/no-temporal-global/temporal-field-types.test-d.ts index 948de2ad566f..c59dd2a5c2d3 100644 --- a/packages/3-targets/postgres/9-facade/test/no-temporal-global/temporal-field-types.test-d.ts +++ b/packages/3-targets/postgres/9-facade/test/no-temporal-global/temporal-field-types.test-d.ts @@ -111,3 +111,46 @@ test('a convenience type helper refuses a combination with no explicit type', () }, })); }); + +test('each convenience type call has the type of the explicit type helper it chooses', () => { + defineContract({}, ({ type }) => { + expectTypeOf(type.DateTime()).toEqualTypeOf(type.TimestamptzJsDate(3)); + expectTypeOf(type.DateTime(undefined, undefined, 6)).toEqualTypeOf(type.TimestamptzJsDate(6)); + expectTypeOf(type.DateTime('jsDate', 'timestamptz')).toEqualTypeOf(type.TimestamptzJsDate(3)); + expectTypeOf(type.DateTime('temporal')).toEqualTypeOf(type.TimestamptzTemporal()); + expectTypeOf(type.DateTime('temporal', undefined, 3)).toEqualTypeOf( + type.TimestamptzTemporal(3), + ); + expectTypeOf(type.DateTime('string')).toEqualTypeOf(type.TimestamptzString()); + expectTypeOf(type.DateTime('string', 'timestamptz', 6)).toEqualTypeOf( + type.TimestamptzString(6), + ); + expectTypeOf(type.DateTime(undefined, 'timestamp')).toEqualTypeOf(type.TimestampJsDate(3)); + expectTypeOf(type.DateTime('jsDate', 'timestamp', 0)).toEqualTypeOf(type.TimestampJsDate(0)); + expectTypeOf(type.DateTime('temporal', 'timestamp')).toEqualTypeOf(type.TimestampInstant()); + expectTypeOf(type.DateTime('temporal', 'timestamp', 6)).toEqualTypeOf(type.TimestampInstant(6)); + expectTypeOf(type.DateTime('string', 'timestamp')).toEqualTypeOf(type.TimestampString()); + expectTypeOf(type.DateTime('string', 'timestamp', 3)).toEqualTypeOf(type.TimestampString(3)); + expectTypeOf(type.Date()).toEqualTypeOf(type.DateString()); + expectTypeOf(type.Date('string', 'date')).toEqualTypeOf(type.DateString()); + expectTypeOf(type.Date('temporal')).toEqualTypeOf(type.DateTemporal()); + expectTypeOf(type.Time()).toEqualTypeOf(type.TimeString()); + expectTypeOf(type.Time(undefined, undefined, 0)).toEqualTypeOf(type.TimeString(0)); + expectTypeOf(type.Time('temporal')).toEqualTypeOf(type.TimeTemporal()); + expectTypeOf(type.Time('temporal', 'time', 3)).toEqualTypeOf(type.TimeTemporal(3)); + expectTypeOf(type.Time('string', 'timetz')).toEqualTypeOf(type.Timetz()); + expectTypeOf(type.Time(undefined, 'timetz', 2)).toEqualTypeOf(type.Timetz(2)); + expectTypeOf(type.Duration()).toEqualTypeOf(type.IntervalString()); + expectTypeOf(type.Duration(undefined, undefined, 3)).toEqualTypeOf(type.IntervalString(3)); + expectTypeOf(type.Duration('parts')).toEqualTypeOf(type.IntervalParts()); + expectTypeOf(type.Duration('parts', 'interval', 6)).toEqualTypeOf(type.IntervalParts(6)); + const widened: number = 4; + expectTypeOf(type.DateTime('string', undefined, widened)).toEqualTypeOf( + type.TimestamptzString(widened), + ); + expectTypeOf(type.DateTime(undefined, undefined, widened)).not.toEqualTypeOf( + type.TimestamptzString(widened), + ); + return { models: {} }; + }); +}); From 332e05e57d567347a31ee80de3d624da64f047d4 Mon Sep 17 00:00:00 2001 From: willbot Date: Thu, 22 Oct 2026 21:33:54 +0200 Subject: [PATCH 054/118] ADR 279's explicit type example matches the code, and its status says what is built Co-Authored-By: Claude Opus 5.5 Signed-off-by: willbot Signed-off-by: Will Madden --- ...me types choose an explicit type from their arguments.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/docs/architecture docs/adrs/ADR 279 - Convenience date and time types choose an explicit type from their arguments.md b/docs/architecture docs/adrs/ADR 279 - Convenience date and time types choose an explicit type from their arguments.md index c9256247a8dc..c1acb0bb5871 100644 --- a/docs/architecture docs/adrs/ADR 279 - Convenience date and time types choose an explicit type from their arguments.md +++ b/docs/architecture docs/adrs/ADR 279 - Convenience date and time types choose an explicit type from their arguments.md @@ -1,6 +1,6 @@ # ADR 279 — Convenience date and time types choose an explicit type from their arguments -**Status:** Accepted +**Status:** Accepted. Implemented: the type choice, the four PostgreSQL convenience types, and every reader of a field's codec. Not yet implemented: items 8 and 10, what the Prisma 7 reader, `contract infer`, `contract print` and `prisma orm init` write, and the `temporal.*` aliases built on `DateTime` ("Created-at and updated-at"); until then those write and use the explicit types. **Builds on:** [ADR 241 — Scalar types use the authoring type-constructor channel](ADR%20241%20-%20Scalar%20types%20use%20the%20authoring%20type-constructor%20channel.md) **Amends:** [ADR 254 — Data types and casts](ADR%20254%20-%20Data%20types%20and%20casts.md), [ADR 257 — Scalar types are named after the target on every surface](ADR%20257%20-%20Scalar%20types%20are%20named%20after%20the%20target%20on%20every%20surface.md), [ADR 275 — Client generators are field attributes, and a type alias is expanded into each column](ADR%20275%20-%20Client%20generators%20are%20field%20attributes%2C%20and%20a%20type%20alias%20is%20expanded%20into%20each%20column.md) @@ -82,7 +82,7 @@ An explicit type is a type constructor: it names one codec, and maps its argumen ```ts TimestamptzTemporal: { kind: 'typeConstructor', - args: [{ kind: 'number', name: 'precision', integer: true, optional: true }], + args: [{ kind: 'value', dataType: 'pg/int4', name: 'precision', optional: true }], output: { codecId: 'pg/timestamptz-temporal@1', typeParams: { precision: { kind: 'arg', index: 0 } }, @@ -139,7 +139,7 @@ The PSL interpreter resolves a convenience type to its explicit type's codec and ### Created-at and updated-at -ADR 275 has the PostgreSQL target ship ready-made type aliases under `temporal`, such as `temporal.UpdatedAt`, built on `Timestamptz`, with `String` and `JsDate` variants for the other kinds of value. With this decision they are built on `DateTime` instead, for example `temporal.UpdatedAt = DateTime @onCreate(now()) @onUpdate(now())`, and use its defaults. `now()` picks its generator from the field's final codec, so it writes a `Date`, a `Temporal.Instant` or a string to match. +ADR 275 has the PostgreSQL target ship ready-made type aliases under `temporal`, such as `temporal.UpdatedAt`, built on `TimestamptzTemporal` (formerly `Timestamptz`), with `String` and `JsDate` variants for the other kinds of value. With this decision they are built on `DateTime` instead, for example `temporal.UpdatedAt = DateTime @onCreate(now()) @onUpdate(now())`, and use its defaults. `now()` picks its generator from the field's final codec, so it writes a `Date`, a `Temporal.Instant` or a string to match. The `String` and `JsDate` variants are not needed. A contributed alias takes no arguments (ADR 275), so a user who wants another kind of value writes the attributes on the convenience type, `DateTime(value: string) @onCreate(now()) @onUpdate(now())`, or defines their own alias with that line. From 929cb7e25b186ce0350f4cdc1500deeb925cc63f Mon Sep 17 00:00:00 2001 From: willbot Date: Thu, 22 Oct 2026 21:34:35 +0200 Subject: [PATCH 055/118] The upgrade instruction says a SQLite or MongoDB match needs no change Co-Authored-By: Claude Opus 5.5 Signed-off-by: willbot Signed-off-by: Will Madden --- .../convenience-date-time-types/detection.test.ts | 8 +++++++- .../convenience-date-time-types/app/instructions.md | 4 ++++ 2 files changed, 11 insertions(+), 1 deletion(-) diff --git a/test/integration/test/upgrade-instructions/convenience-date-time-types/detection.test.ts b/test/integration/test/upgrade-instructions/convenience-date-time-types/detection.test.ts index bb0c743b3852..6e2826d6cc60 100644 --- a/test/integration/test/upgrade-instructions/convenience-date-time-types/detection.test.ts +++ b/test/integration/test/upgrade-instructions/convenience-date-time-types/detection.test.ts @@ -21,7 +21,7 @@ const matches = (id: string, content: string) => patternsOf(id).some((pattern) => pattern.test(content)); describe('the convenience date and time type detection', () => { - it('matches a Prisma 8 field or alias typed DateTime, Date or Time, and nothing else', () => { + it('matches a Prisma 8 field or alias typed DateTime, Date or Time on any target, and nothing else', () => { const psl = (body: string) => matches('psl-date-time-types-are-convenience-types', `// use prisma-8\n\n${body}\n`); expect({ @@ -34,6 +34,8 @@ describe('the convenience date and time type detection', () => { stringType: psl('model A {\n day DateString\n}'), comment: psl('// a DateTime field\nmodel A {\n id Int @id\n}'), modelName: psl('model Date {\n id Int @id\n}'), + sqliteDateTime: psl('model A {\n at DateTime @default(datetime`2024-01-01T00:00:00Z`)\n}'), + mongoDate: psl('model A {\n id ObjectId @id @map("_id")\n at Date\n}'), prisma7: psl( 'datasource db {\n provider = "postgresql"\n}\nmodel A {\n at DateTime @db.Timestamptz(3)\n}', ), @@ -47,6 +49,8 @@ describe('the convenience date and time type detection', () => { stringType: false, comment: false, modelName: false, + sqliteDateTime: true, + mongoDate: true, prisma7: false, }); }); @@ -58,12 +62,14 @@ describe('the convenience date and time type detection', () => { date: ts('field.column(type.Date())'), time: ts('field.column(type.Time(3))'), explicit: ts('field.column(type.TimeTemporal(3))'), + sqliteDateTime: ts('field.column(type.DateTime())'), jsDate: ts('const at = new Date()'), }).toEqual({ dateTime: true, date: true, time: true, explicit: false, + sqliteDateTime: true, jsDate: false, }); }); diff --git a/upgrade-instructions/pending/convenience-date-time-types/app/instructions.md b/upgrade-instructions/pending/convenience-date-time-types/app/instructions.md index a31216f53b61..4ff8f82cb9ed 100644 --- a/upgrade-instructions/pending/convenience-date-time-types/app/instructions.md +++ b/upgrade-instructions/pending/convenience-date-time-types/app/instructions.md @@ -48,12 +48,16 @@ model Event { } ``` +The detection cannot tell which target a schema is for, so it also matches SQLite's `DateTime` and MongoDB's `Date`. A match in a project whose `prisma.config.ts` uses the SQLite or MongoDB target needs no change: on those targets the names keep their meaning. + A type alias written with one of the names, such as `types { Stamp = DateTime @onCreate(now()) }`, is renamed the same way. Rename only field types and alias types in Prisma 8 schemas whose config uses the PostgreSQL target, including the `contract.prisma` copies in migration folders; leave a model or field named `Date`, comments, and Prisma 7 schemas as they are. Re-emit the contract with `prisma contract emit`: it is byte for byte the same as before. To move a field to the new defaults instead, keep the name and update the application code that reads the field, then re-emit, plan and apply a migration: a `DateTime` column becomes `timestamptz(3)`, so the migration changes its precision. ## `ts-date-time-helpers-are-convenience-types` +Rename only in contracts on PostgreSQL; `type.DateTime(...)` in a SQLite contract keeps its meaning. + ```ts field.column(type.TimestamptzTemporal()); // was type.DateTime() field.column(type.DateTemporal()); // was type.Date() From a061fbac12ab827d6cffef145ece9e17082cd528 Mon Sep 17 00:00:00 2001 From: willbot Date: Thu, 22 Oct 2026 21:36:23 +0200 Subject: [PATCH 056/118] A number written where an option goes is told to name its argument Time(3) now says argument #1 is 3, lists the names value takes, and says to write precision: 3, without repeating the argument's label. Co-Authored-By: Claude Opus 5.5 Signed-off-by: willbot Signed-off-by: Will Madden --- .../psl-parser/src/authoring-arguments.ts | 26 ++++++++++++++++++- .../test/authoring-arguments.test.ts | 20 +++++++++++--- ...terpreter.defaults.temporal-codecs.test.ts | 2 +- .../convenience-types.integration.test.ts | 6 +++++ 4 files changed, 49 insertions(+), 5 deletions(-) diff --git a/packages/1-framework/2-authoring/psl-parser/src/authoring-arguments.ts b/packages/1-framework/2-authoring/psl-parser/src/authoring-arguments.ts index 33cbdd891a9c..5699d83b1dde 100644 --- a/packages/1-framework/2-authoring/psl-parser/src/authoring-arguments.ts +++ b/packages/1-framework/2-authoring/psl-parser/src/authoring-arguments.ts @@ -1,5 +1,6 @@ import type { AuthoringArgumentDescriptor, + AuthoringOption, AuthoringValueArgument, DataTypeSupport, } from '@internal/framework-components/authoring'; @@ -332,6 +333,23 @@ function parsePslAuthoringArgumentText( } } +/** Why a written value is not one of the names an option takes, and, for a number, which value argument it was probably meant for. */ +function misplacedOptionMessage( + descriptors: readonly AuthoringArgumentDescriptor[], + option: AuthoringOption, + optionName: string, + written: string, + argumentLabel: string, +): string { + const refused = `${argumentLabel} is ${written}, but argument "${optionName}" takes one of these names, written without quotes: ${option.values.join(', ')}.`; + const valueArgument = descriptors.find( + (descriptor) => descriptor.kind === 'value' && descriptor.name !== undefined, + ); + return /^[+-]?\d/.test(written) && valueArgument?.name !== undefined + ? `${refused} Write ${valueArgument.name} by name, as in ${valueArgument.name}: ${written}.` + : refused; +} + function pushInvalidPslHelperArgument(input: { readonly diagnostics: PslDiagnosticCollector; readonly source: DiagnosticSource; @@ -378,7 +396,13 @@ function readPslHelperArgument( helperLabel: input.helperLabel, message: descriptor.kind === 'option' && descriptor.name !== undefined - ? `cannot parse ${argumentLabel}: ${argumentLabel} is ${argument.value.trim()}, but argument "${descriptor.name}" takes one of these names, written without quotes: ${descriptor.values.join(', ')}.` + ? misplacedOptionMessage( + input.descriptors, + descriptor, + descriptor.name, + argument.value.trim(), + argumentLabel, + ) : `cannot parse ${argumentLabel} for descriptor kind "${descriptor.kind}".`, }); return INVALID_AUTHORING_ARGUMENT; diff --git a/packages/1-framework/2-authoring/psl-parser/test/authoring-arguments.test.ts b/packages/1-framework/2-authoring/psl-parser/test/authoring-arguments.test.ts index e4a69dfd6134..45794658ae60 100644 --- a/packages/1-framework/2-authoring/psl-parser/test/authoring-arguments.test.ts +++ b/packages/1-framework/2-authoring/psl-parser/test/authoring-arguments.test.ts @@ -124,13 +124,27 @@ describe('mapPslHelperArgs argument kinds', () => { expect(diagnostics[0]?.message).toMatch(/cannot parse argument #1/); }); - it('names the values a named option takes when a number is written in its place', () => { + it('names the values a named option takes when a name is quoted in its place', () => { const { diagnostics } = callMap( - [positional('3')], + [positional('"string"')], [{ kind: 'option', name: 'value', values: ['string', 'temporal'] }], ); expect(diagnostics[0]?.message).toMatch( - /argument #1 is 3, but argument "value" takes one of these names, written without quotes: string, temporal\.$/, + / argument #1 is "string", but argument "value" takes one of these names, written without quotes: string, temporal\.$/, + ); + expect(diagnostics[0]?.message).not.toMatch(/cannot parse/); + }); + + it('says to write a number by name when it is written where an option is expected', () => { + const { diagnostics } = callMap( + [positional('3')], + [ + { kind: 'option', name: 'value', values: ['string', 'temporal'] }, + { kind: 'value', name: 'precision', dataType: 'test/int' }, + ], + ); + expect(diagnostics[0]?.message).toMatch( + / argument #1 is 3, but argument "value" takes one of these names, written without quotes: string, temporal\. Write precision by name, as in precision: 3\.$/, ); }); diff --git a/packages/2-sql-family/2-authoring/contract-psl/test/interpreter.defaults.temporal-codecs.test.ts b/packages/2-sql-family/2-authoring/contract-psl/test/interpreter.defaults.temporal-codecs.test.ts index 1edbf0841b0b..f1d7d820e77d 100644 --- a/packages/2-sql-family/2-authoring/contract-psl/test/interpreter.defaults.temporal-codecs.test.ts +++ b/packages/2-sql-family/2-authoring/contract-psl/test/interpreter.defaults.temporal-codecs.test.ts @@ -180,7 +180,7 @@ stamped ${field} name: 'a quoted option value (one spelling only)', field: 'temporal.timestamp(onCreate: "now")', message: - /cannot parse named argument "onCreate": named argument "onCreate" is "now", but argument "onCreate" takes one of these names, written without quotes: now\./, + /preset "temporal\.timestamp" named argument "onCreate" is "now", but argument "onCreate" takes one of these names, written without quotes: now\./, }, { name: 'a duplicate named argument', diff --git a/test/integration/test/convenience-date-time-types/convenience-types.integration.test.ts b/test/integration/test/convenience-date-time-types/convenience-types.integration.test.ts index 87c8e23cb0bc..3d39d50d9435 100644 --- a/test/integration/test/convenience-date-time-types/convenience-types.integration.test.ts +++ b/test/integration/test/convenience-date-time-types/convenience-types.integration.test.ts @@ -112,6 +112,12 @@ model Order { await expect(readPsl(model(`at ${written}`))).rejects.toThrow(message); }); + it('refuses a precision written in the place of the value option, saying to write it by name', async () => { + await expect(readPsl(model('at Time(3)'))).rejects.toThrow( + 'Field \\"Order.at\\" type \\"Time\\" argument #1 is 3, but argument \\"value\\" takes one of these names, written without quotes: string, temporal. Write precision by name, as in precision: 3.', + ); + }); + it('refuses a precision the type it becomes does not take', async () => { await expect(readPsl(model('at Date(precision: 3)'))).rejects.toThrow( 'received unknown named argument \\"precision\\"', From 4ad38032d81915c3e8eca6f2a6e30e2bae68f21c Mon Sep 17 00:00:00 2001 From: willbot Date: Thu, 22 Oct 2026 21:37:03 +0200 Subject: [PATCH 057/118] Journey asserts the exact text the string codecs read back Co-Authored-By: Claude Opus 5.5 Signed-off-by: willbot Signed-off-by: Will Madden --- ...convenience-date-time-types-without-temporal.e2e.test.ts | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/test/integration/test/cli-journeys/convenience-date-time-types-without-temporal.e2e.test.ts b/test/integration/test/cli-journeys/convenience-date-time-types-without-temporal.e2e.test.ts index 40566e677378..cccdd133e74e 100644 --- a/test/integration/test/cli-journeys/convenience-date-time-types-without-temporal.e2e.test.ts +++ b/test/integration/test/cli-journeys/convenience-date-time-types-without-temporal.e2e.test.ts @@ -140,17 +140,15 @@ withTempDir(({ createTempDir }) => { id: { kind: 'number', value: 1 }, paidAt: { kind: 'Date', value: '2024-02-29T10:00:00.123Z' }, legacyAt: { kind: 'Date', value: '2024-02-29T10:00:00.500Z' }, - textAt: { kind: 'string', value: read.textAt.value }, + textAt: { kind: 'string', value: '2024-02-29 10:00:00.123456+00' }, shipBy: { kind: 'string', value: '2024-02-29' }, opensAt: { kind: 'string', value: '09:30:00' }, - localAt: { kind: 'string', value: read.localAt.value }, + localAt: { kind: 'string', value: '09:30:00+02' }, period: { kind: 'string', value: 'P1M' }, parts: { kind: 'object', value: { months: '1', days: '2', micros: '3' } }, settledAt: { kind: 'null', value: null }, updatedAt: { kind: 'Date', value: read.updatedAt.value }, }); - expect(read.textAt.value).toMatch(/^2024-02-29[ T]10:00:00\.123456/); - expect(read.localAt.value).toMatch(/^09:30:00\+02/); const updatedAt = new Date(read.updatedAt.value).getTime(); expect(updatedAt).toBeGreaterThanOrEqual(before - 1_000); expect(updatedAt).toBeLessThanOrEqual(after + 1_000); From bad02734a52daeb181486bf897a5b8f56e9ed682 Mon Sep 17 00:00:00 2001 From: willbot Date: Thu, 22 Oct 2026 21:44:29 +0200 Subject: [PATCH 058/118] TML-3709: Note the journey values slices 1 and 4 must reconcile Co-Authored-By: Claude Opus 5.5 Signed-off-by: willbot Signed-off-by: Will Madden --- projects/datetime-convenience-types/plan.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/projects/datetime-convenience-types/plan.md b/projects/datetime-convenience-types/plan.md index e1be20c0414c..d8855e22b561 100644 --- a/projects/datetime-convenience-types/plan.md +++ b/projects/datetime-convenience-types/plan.md @@ -38,6 +38,10 @@ A type constructor's `codecId` may be a `select` over `option` arguments; every `contract infer` and `contract print` write convenience types, with arguments only where they differ from the defaults, and the explicit type only where no convenience type fits. The Prisma 7 reader maps `DateTime` to `DateTime(column: timestamp)` and `@db.*` date and time columns to the matching convenience form. `prisma orm init` writes `DateTime`. The contributed `temporal.CreatedAt` and `temporal.UpdatedAt` aliases are rebuilt on `DateTime` (`DateTime @onCreate(now())`, and `@onUpdate(now())` for the second), and their `String` and `JsDate` variants are removed, per ADR 279; contracts that use them change codec, so the upgrade instruction says to re-emit and `db sign`. Upgrade instructions: re-emit and `db sign` for adopted projects. Update slice 2's upgrade instruction (`upgrade-instructions/pending/prisma7-now-default-on-create/`), which names the type the reader picks today (`TimestampString(3)`). +## Cross-slice notes + +- Slices 1 and 4 both touch what string date and time values look like. The convenience-types journey (`convenience-date-time-types-without-temporal.e2e.test.ts`) asserts today's display text for `textAt` (`2024-02-29 10:00:00.123456+00`) and `localAt` (`09:30:00+02`). Whichever of the two slices merges second changes them to `2024-02-29T10:00:00.123456Z` and `09:30:00+02:00`. + ## Dependencies - **Field-preset removal project, slice 2.** Delivers the `type.*` helpers that expose slice 4's types in TypeScript, and the contributed aliases built on `DateTime`. Not blocking; coordinate so its `temporal.CreatedAt` and `temporal.UpdatedAt` land on `DateTime` once slice 4 merges. From 3a70f19c4111f2f73608949bc0c294325b10dbaa Mon Sep 17 00:00:00 2001 From: willbot Date: Thu, 22 Oct 2026 21:51:13 +0200 Subject: [PATCH 059/118] Test both messages of validateAuthoringTypeChoiceParams Co-Authored-By: Claude Opus 5.5 Signed-off-by: willbot Signed-off-by: Will Madden --- .../test/authoring-type-choice.test.ts | 51 +++++++++++++++++++ 1 file changed, 51 insertions(+) diff --git a/packages/1-framework/1-core/framework-components/test/authoring-type-choice.test.ts b/packages/1-framework/1-core/framework-components/test/authoring-type-choice.test.ts index 1f4cc98ca064..297b025f6c8b 100644 --- a/packages/1-framework/1-core/framework-components/test/authoring-type-choice.test.ts +++ b/packages/1-framework/1-core/framework-components/test/authoring-type-choice.test.ts @@ -1,10 +1,13 @@ +import { type } from 'arktype'; import { describe, expect, it } from 'vitest'; import { resolveAuthoringTypeChoice } from '../src/shared/authoring-type-choice'; import { + type AuthoringTypeConstructorDescriptor, type AuthoringTypeNamespace, collectScalarTypeConstructors, instantiateAuthoringTypeCall, typeConstructorLookup, + validateAuthoringTypeChoiceParams, } from '../src/shared/framework-authoring'; const precision = { @@ -128,3 +131,51 @@ describe('a type choice in the type namespace', () => { }); }); }); + +describe('validateAuthoringTypeChoiceParams', () => { + const paramsSchema = type({ 'precision?': 'number <= 6' }); + const check = ( + chosen: AuthoringTypeConstructorDescriptor, + typeParams: Record, + ) => { + try { + validateAuthoringTypeChoiceParams( + 'Moment', + namespace.Moment, + chosen, + typeParams, + paramsSchema, + ); + } catch (error) { + const { message, details } = error as { message: string; details: unknown }; + return { message, details }; + } + return undefined; + }; + + it('passes parameters the codec accepts', () => { + expect(check(namespace.WideDate, { precision: 6 })).toBeUndefined(); + }); + + it('names the argument of the choice a refused parameter came from', () => { + expect(check(namespace.WideDate, { precision: 7 })).toEqual({ + message: expect.stringMatching( + /^Argument "precision" of Moment is invalid: precision must be/, + ), + details: { helperPath: 'Moment', argumentIndex: 2 }, + }); + }); + + it('names the type parameters when a refused parameter came from no argument', () => { + const fixed: AuthoringTypeConstructorDescriptor = { + kind: 'typeConstructor', + output: { codecId: 'test/fixed@1', typeParams: { precision: 9 } }, + }; + expect(check(fixed, { precision: 9 })).toEqual({ + message: expect.stringMatching( + /^The type parameters of Moment are invalid: precision must be/, + ), + details: { helperPath: 'Moment' }, + }); + }); +}); From ce1720641175cffce78c9ee8b7de7bbe6bb2fceb Mon Sep 17 00:00:00 2001 From: willbot Date: Thu, 22 Oct 2026 21:51:14 +0200 Subject: [PATCH 060/118] Name MySQL's DateTime where the upgrade detection and ADR 279 list other targets Co-Authored-By: Claude Opus 5.5 Signed-off-by: willbot Signed-off-by: Will Madden --- ...time types choose an explicit type from their arguments.md | 2 +- .../convenience-date-time-types/detection.test.ts | 2 ++ .../pending/convenience-date-time-types/app/instructions.md | 4 ++-- 3 files changed, 5 insertions(+), 3 deletions(-) diff --git a/docs/architecture docs/adrs/ADR 279 - Convenience date and time types choose an explicit type from their arguments.md b/docs/architecture docs/adrs/ADR 279 - Convenience date and time types choose an explicit type from their arguments.md index c1acb0bb5871..5426dd1a508a 100644 --- a/docs/architecture docs/adrs/ADR 279 - Convenience date and time types choose an explicit type from their arguments.md +++ b/docs/architecture docs/adrs/ADR 279 - Convenience date and time types choose an explicit type from their arguments.md @@ -160,7 +160,7 @@ The `String` and `JsDate` variants are not needed. A contributed alias takes no ## Not decided here -- SQLite and MongoDB. Each declares its own convenience types over its own explicit types, under item 11, and each already uses one of the names: SQLite's explicit `DateTime` is its `sqlite/datetime@1` type, and MongoDB's explicit `Date` is `mongo/date@1`, a moment in time, with `DateTime` as a deprecated alias of it. +- SQLite, MySQL and MongoDB. Each declares its own convenience types over its own explicit types, under item 11, and each already uses one of the names: SQLite's explicit `DateTime` is its `sqlite/datetime@1` type, MySQL's explicit `DateTime` is a `datetime(3)` column read as a JavaScript `Date`, and MongoDB's explicit `Date` is `mongo/date@1`, a moment in time, with `DateTime` as a deprecated alias of it. - Whether other scalar types get convenience types, for example an `Int` whose arguments choose between the explicit `Int2`, `Int4` and `Int8`. ## Alternatives considered diff --git a/test/integration/test/upgrade-instructions/convenience-date-time-types/detection.test.ts b/test/integration/test/upgrade-instructions/convenience-date-time-types/detection.test.ts index 6e2826d6cc60..ec9259248563 100644 --- a/test/integration/test/upgrade-instructions/convenience-date-time-types/detection.test.ts +++ b/test/integration/test/upgrade-instructions/convenience-date-time-types/detection.test.ts @@ -35,6 +35,7 @@ describe('the convenience date and time type detection', () => { comment: psl('// a DateTime field\nmodel A {\n id Int @id\n}'), modelName: psl('model Date {\n id Int @id\n}'), sqliteDateTime: psl('model A {\n at DateTime @default(datetime`2024-01-01T00:00:00Z`)\n}'), + mysqlDateTime: psl('model A {\n id Int @id\n at DateTime @onCreate(now())\n}'), mongoDate: psl('model A {\n id ObjectId @id @map("_id")\n at Date\n}'), prisma7: psl( 'datasource db {\n provider = "postgresql"\n}\nmodel A {\n at DateTime @db.Timestamptz(3)\n}', @@ -50,6 +51,7 @@ describe('the convenience date and time type detection', () => { comment: false, modelName: false, sqliteDateTime: true, + mysqlDateTime: true, mongoDate: true, prisma7: false, }); diff --git a/upgrade-instructions/pending/convenience-date-time-types/app/instructions.md b/upgrade-instructions/pending/convenience-date-time-types/app/instructions.md index 4ff8f82cb9ed..b62dba2fd139 100644 --- a/upgrade-instructions/pending/convenience-date-time-types/app/instructions.md +++ b/upgrade-instructions/pending/convenience-date-time-types/app/instructions.md @@ -48,7 +48,7 @@ model Event { } ``` -The detection cannot tell which target a schema is for, so it also matches SQLite's `DateTime` and MongoDB's `Date`. A match in a project whose `prisma.config.ts` uses the SQLite or MongoDB target needs no change: on those targets the names keep their meaning. +The detection cannot tell which target a schema is for, so it also matches SQLite's and MySQL's `DateTime` and MongoDB's `Date`. A match in a project whose `prisma.config.ts` uses the SQLite, MySQL or MongoDB target needs no change: on those targets the names keep their meaning. A type alias written with one of the names, such as `types { Stamp = DateTime @onCreate(now()) }`, is renamed the same way. Rename only field types and alias types in Prisma 8 schemas whose config uses the PostgreSQL target, including the `contract.prisma` copies in migration folders; leave a model or field named `Date`, comments, and Prisma 7 schemas as they are. Re-emit the contract with `prisma contract emit`: it is byte for byte the same as before. @@ -56,7 +56,7 @@ To move a field to the new defaults instead, keep the name and update the applic ## `ts-date-time-helpers-are-convenience-types` -Rename only in contracts on PostgreSQL; `type.DateTime(...)` in a SQLite contract keeps its meaning. +Rename only in contracts on PostgreSQL; `type.DateTime(...)` in a SQLite or MySQL contract keeps its meaning. ```ts field.column(type.TimestamptzTemporal()); // was type.DateTime() From c26d61976e934a35303cd78ea567fcdf7ee4037e Mon Sep 17 00:00:00 2001 From: willbot Date: Thu, 22 Oct 2026 22:06:09 +0200 Subject: [PATCH 061/118] Assembly refuses two rows for one combination and a default the chosen type does not take The lookup of the constructor a row names is one function, used by assembly, getAuthoringTypeChoice and the PSL scope. Co-Authored-By: Claude Opus 5.5 Signed-off-by: willbot Signed-off-by: Will Madden --- ...e an explicit type from their arguments.md | 2 +- docs/reference/error-reference.md | 2 +- .../src/exports/authoring.ts | 2 +- .../src/shared/authoring-type-choice.ts | 27 ++++++++++++++-- .../src/shared/framework-authoring.ts | 13 +------- .../test/authoring-type-choice.test.ts | 6 ++-- .../test/type-choice-assembly.test.ts | 31 +++++++++++++++++++ 7 files changed, 64 insertions(+), 19 deletions(-) diff --git a/docs/architecture docs/adrs/ADR 279 - Convenience date and time types choose an explicit type from their arguments.md b/docs/architecture docs/adrs/ADR 279 - Convenience date and time types choose an explicit type from their arguments.md index 5426dd1a508a..69b51affff32 100644 --- a/docs/architecture docs/adrs/ADR 279 - Convenience date and time types choose an explicit type from their arguments.md +++ b/docs/architecture docs/adrs/ADR 279 - Convenience date and time types choose an explicit type from their arguments.md @@ -119,7 +119,7 @@ As a table: | `temporal` | `timestamp` | `TimestampInstant` | | | `string` | `timestamp` | `TimestampString` | | -That is `DateTime`'s table; the other three are declared the same way. Resolving a call fills each option left out with its default, looks up the row for the option values, then instantiates the named explicit constructor with the passed-on arguments, a row default standing in for one left out, so the result is exactly what writing the explicit type gives. A combination with no row, such as `Time(value: temporal, column: timetz)`, is refused at the argument that makes it invalid, and the message lists the values that argument accepts with the others as written; a value an option does not list, such as `Date(value: jsDate)`, is refused the same way. A passed-on argument the chosen constructor does not take is refused at that argument. Arguments are written by name or in declaration order, so `Time(3)` reads `3` as the `value` option and is refused. Every argument is optional, so a type choice is also written bare, as its default row. Assembly refuses a table that names a constructor no component registers beside it, a combination or default an option does not list, defaults with no row, a row default for an argument the choice does not pass on, and a passed-on argument whose data type differs from the chosen constructor's. +That is `DateTime`'s table; the other three are declared the same way. Resolving a call fills each option left out with its default, looks up the row for the option values, then instantiates the named explicit constructor with the passed-on arguments, a row default standing in for one left out, so the result is exactly what writing the explicit type gives. A combination with no row, such as `Time(value: temporal, column: timetz)`, is refused at the argument that makes it invalid, and the message lists the values that argument accepts with the others as written; a value an option does not list, such as `Date(value: jsDate)`, is refused the same way. A passed-on argument the chosen constructor does not take is refused at that argument. Arguments are written by name or in declaration order, so `Time(3)` reads `3` as the `value` option and is refused. Every argument is optional, so a type choice is also written bare, as its default row. Assembly refuses a table that names a constructor no component registers beside it, a combination or default an option does not list, two rows for one combination, defaults with no row, a row default for an argument the choice does not pass on or the chosen constructor does not take, and a passed-on argument whose data type differs from the chosen constructor's. The field-preset option arguments and select templates (ADR 246) are not used: a select resolves a template value and has no defaults and no refusal, while a type choice resolves to a named constructor, which is what "becomes one explicit type" means. diff --git a/docs/reference/error-reference.md b/docs/reference/error-reference.md index 273abaa1eed4..04fd59fdccd8 100644 --- a/docs/reference/error-reference.md +++ b/docs/reference/error-reference.md @@ -543,7 +543,7 @@ Namespaces are declared (contract-level list or a model-level `namespace`) on a A composed pack's contribution is malformed or collides with another contribution; this is the extension-author-facing bucket. Covers: entity types colliding with reserved helper keys, duplicate entity kinds or index-type registrations, a registered entity kind with no `lowerEntityHandles` lowering, an invalid `indexTypes` shape, entries-slot collisions between a model attribute and a block entry kind, a model attribute that lowers to a malformed index, bad authoring-helper paths, a codec registered with an entity-ref arg but no `columnFromEntity` hook, and print-time contribution mismatches (a block keyword with no PSL block descriptor, or a descriptor whose discriminator disagrees with the block's kind). Raised during contract authoring/lowering and PSL printing. -A contributed type choice (`kind: 'typeChoice'`, such as PostgreSQL's `DateTime`) is refused when the stack is assembled, by the control stack and by the TypeScript builders, when its table cannot be resolved: `Type choice "" contributed by "" chooses "", which is not a type constructor any component registers beside it.`, a row whose combination or an option default the option does not list, a combination of defaults with no row (`… has no row for its defaults value: , so it cannot be written bare.`), a parameter default for an argument it does not pass on, or a passed-on argument of another data type than the chosen constructor takes. Payload: `packId`, `contribution`. +A contributed type choice (`kind: 'typeChoice'`, such as PostgreSQL's `DateTime`) is refused when the stack is assembled, by the control stack and by the TypeScript builders, when its table cannot be resolved: `Type choice "" contributed by "" chooses "", which is not a type constructor any component registers beside it.`, a row whose combination or an option default the option does not list, two rows for one combination (`… has two rows for value: ; each combination chooses one type.`), a combination of defaults with no row (`… has no row for its defaults value: , so it cannot be written bare.`), a parameter default for an argument it does not pass on or that the chosen constructor does not take (`… gives a default for "" to "", which takes no argument of that name.`), or a passed-on argument of another data type than the chosen constructor takes. Payload: `packId`, `contribution`. A contributed type alias (`kind: 'typeAlias'`, such as `temporal.CreatedAt`) is refused when the stack is assembled, by the control stack and by the TypeScript builders, so PSL and TypeScript refuse the same packs. Its type params may not reference an argument, because a type alias takes none: `Invalid authoring type alias "" contributed by descriptor "". output.type.typeParams. references an argument, but a type alias takes none. Write the value itself.` Each generator call must resolve for the alias's codec: `Type alias "" calls now() on create, which does not apply to its codec "".`, `…, which its codec "" refuses: `, or `…, but no component declares the generator function "".` Payload: `packId`, `contribution`, `reason`, `keyword`, `paramName`, `codecId`. diff --git a/packages/1-framework/1-core/framework-components/src/exports/authoring.ts b/packages/1-framework/1-core/framework-components/src/exports/authoring.ts index d8c9f245b7ec..a215d8e5ac10 100644 --- a/packages/1-framework/1-core/framework-components/src/exports/authoring.ts +++ b/packages/1-framework/1-core/framework-components/src/exports/authoring.ts @@ -11,6 +11,7 @@ export { isAuthoringTypeChoiceOption, resolveAuthoringTypeChoice, typeChoiceCombinationText, + typeConstructorLookup, } from '../shared/authoring-type-choice'; export type { AuthoringTypeConstructorCall, @@ -133,7 +134,6 @@ export { resolveAuthoringTemplateValue, resolveEnumCodecId, resolveTypeAliasGenerators, - typeConstructorLookup, validateAuthoringHelperArguments, validateAuthoringTypeChoiceParams, validateAuthoringTypeParams, diff --git a/packages/1-framework/1-core/framework-components/src/shared/authoring-type-choice.ts b/packages/1-framework/1-core/framework-components/src/shared/authoring-type-choice.ts index f0478544c40b..74b74c745fab 100644 --- a/packages/1-framework/1-core/framework-components/src/shared/authoring-type-choice.ts +++ b/packages/1-framework/1-core/framework-components/src/shared/authoring-type-choice.ts @@ -151,6 +151,16 @@ export function resolveAuthoringTypeChoice(input: { type TypeNamespaceLevel = { readonly [name: string]: unknown }; +/** Looks a type constructor up by name in one level of a type namespace, as a type choice's rows name them. */ +export function typeConstructorLookup( + namespace: TypeNamespaceLevel, +): (name: string) => AuthoringTypeConstructorDescriptor | undefined { + return (name) => { + const member = Object.hasOwn(namespace, name) ? namespace[name] : undefined; + return isTypeConstructor(member) ? member : undefined; + }; +} + function isTypeChoice(value: unknown): value is AuthoringTypeChoiceDescriptor { return ( typeof value === 'object' && value !== null && 'kind' in value && value.kind === 'typeChoice' @@ -185,6 +195,7 @@ function assertTypeChoiceValid( const invalid = (detail: string): never => { throw invalidError(detail); }; + const constructorNamed = typeConstructorLookup(level); const options = descriptor.args.filter(isAuthoringTypeChoiceOption); const passed = descriptor.args.filter( (argument): argument is AuthoringTypeChoicePassedArgument => @@ -204,8 +215,15 @@ function assertTypeChoiceValid( `has a row for ${typeChoiceCombinationText(row)}, which does not give every option one of its values.`, ); } - const chosenType = Object.hasOwn(level, row.type) ? level[row.type] : undefined; - if (!isTypeConstructor(chosenType)) { + const names = options.map((option) => option.name); + const chosen = new Map(Object.entries(row.when)); + if (descriptor.choices.filter((other) => rowMatches(other, chosen, names)).length > 1) { + invalid( + `has two rows for ${typeChoiceCombinationText(row)}; each combination chooses one type.`, + ); + } + const chosenType = constructorNamed(row.type); + if (chosenType === undefined) { throw invalidError( `chooses "${row.type}", which is not a type constructor any component registers beside it.`, ); @@ -214,6 +232,11 @@ function assertTypeChoiceValid( if (!passed.some((argument) => argument.name === name)) { invalid(`gives a default for "${name}", which is not an argument it passes on.`); } + if (!chosenType.args?.some((parameter) => parameter.name === name)) { + invalid( + `gives a default for "${name}" to "${row.type}", which takes no argument of that name.`, + ); + } } for (const argument of passed) { const parameter = chosenType.args?.find((candidate) => candidate.name === argument.name); diff --git a/packages/1-framework/1-core/framework-components/src/shared/framework-authoring.ts b/packages/1-framework/1-core/framework-components/src/shared/framework-authoring.ts index 8b07c83cabdb..00a3223696d7 100644 --- a/packages/1-framework/1-core/framework-components/src/shared/framework-authoring.ts +++ b/packages/1-framework/1-core/framework-components/src/shared/framework-authoring.ts @@ -18,6 +18,7 @@ import type { Type } from 'arktype'; import { type AuthoringTypeChoiceDescriptor, resolveAuthoringTypeChoice, + typeConstructorLookup, } from './authoring-type-choice'; import type { CodecLookupWithDescriptors } from './codec-types'; import type { DataTypeId } from './data-type'; @@ -757,18 +758,6 @@ export function isAuthoringTypeDescriptor( ); } -/** Looks a type constructor up by name in one level of a type namespace, as a type choice's rows name them. */ -export function typeConstructorLookup( - namespace: AuthoringTypeNamespace, -): (name: string) => AuthoringTypeConstructorDescriptor | undefined { - return (name) => { - const member = Object.hasOwn(namespace, name) ? namespace[name] : undefined; - return member !== undefined && isAuthoringTypeConstructorDescriptor(member) - ? member - : undefined; - }; -} - /** The type constructor at `path` in the contributions' type namespace, or `undefined` when none is there. */ export function getAuthoringTypeConstructor( contributions: { readonly type?: AuthoringTypeNamespace } | undefined, diff --git a/packages/1-framework/1-core/framework-components/test/authoring-type-choice.test.ts b/packages/1-framework/1-core/framework-components/test/authoring-type-choice.test.ts index 297b025f6c8b..591fcb5700bb 100644 --- a/packages/1-framework/1-core/framework-components/test/authoring-type-choice.test.ts +++ b/packages/1-framework/1-core/framework-components/test/authoring-type-choice.test.ts @@ -1,12 +1,14 @@ import { type } from 'arktype'; import { describe, expect, it } from 'vitest'; -import { resolveAuthoringTypeChoice } from '../src/shared/authoring-type-choice'; +import { + resolveAuthoringTypeChoice, + typeConstructorLookup, +} from '../src/shared/authoring-type-choice'; import { type AuthoringTypeConstructorDescriptor, type AuthoringTypeNamespace, collectScalarTypeConstructors, instantiateAuthoringTypeCall, - typeConstructorLookup, validateAuthoringTypeChoiceParams, } from '../src/shared/framework-authoring'; diff --git a/packages/1-framework/1-core/framework-components/test/type-choice-assembly.test.ts b/packages/1-framework/1-core/framework-components/test/type-choice-assembly.test.ts index b81c814a4544..ef0ecd070aee 100644 --- a/packages/1-framework/1-core/framework-components/test/type-choice-assembly.test.ts +++ b/packages/1-framework/1-core/framework-components/test/type-choice-assembly.test.ts @@ -120,6 +120,37 @@ describe('assertTypeChoicesValid', () => { ); }); + it('refuses two rows for one combination', () => { + expect( + check( + choice({ + choices: [ + { when: { value: 'date' }, type: 'WideDate' }, + { when: { value: 'text' }, type: 'WideText' }, + { when: { value: 'date' }, type: 'WideText' }, + ], + }), + ), + ).toThrow( + 'Type choice "Moment" contributed by "x-pack" has two rows for value: date; each combination chooses one type.', + ); + }); + + it('refuses a parameter default the chosen constructor does not take', () => { + expect( + check( + choice({ + choices: [ + { when: { value: 'date' }, type: 'WideDate' }, + { when: { value: 'text' }, type: 'WideText', defaults: { precision: 3 } }, + ], + }), + ), + ).toThrow( + 'Type choice "Moment" contributed by "x-pack" gives a default for "precision" to "WideText", which takes no argument of that name.', + ); + }); + it('refuses a passed-on argument of another data type than the chosen constructor takes', () => { expect(check(choice({ args: [valueOption, { ...precision, dataType: 'test/text' }] }))).toThrow( 'Type choice "Moment" contributed by "x-pack" passes "precision" as a value of data type "test/text" to "WideDate", which takes "test/int".', From e78c7afa72f0a696f96596be25d63319c800b84d Mon Sep 17 00:00:00 2001 From: willbot Date: Thu, 22 Oct 2026 22:06:32 +0200 Subject: [PATCH 062/118] A type choice call is read one way, for the column, its attributes and MongoDB The column, @default admission and the generator lookup all read a type choice call with the PSL argument reader, so an unknown or duplicate argument chooses no codec anywhere. The MongoDB reader resolves a type choice as the SQL reader does, refusing at the argument; the shared resolution moves into the PSL parser. A type choice's symbol is its own variant with its constructors required, and the spec context's typeConstructor is renamed contributedType. The 'write it by name' hint for a number written where an option goes names an argument only when exactly one value argument reads that number. Co-Authored-By: Claude Opus 5.5 Signed-off-by: willbot Signed-off-by: Will Madden --- .../src/attribute-spec/generator-attribute.ts | 4 +- .../src/attribute-spec/spec-context.ts | 6 +- .../psl-parser/src/authoring-arguments.ts | 226 +++++++++--------- .../2-authoring/psl-parser/src/binder.ts | 4 +- .../psl-parser/src/contributed-type-scope.ts | 122 +++++----- .../psl-parser/src/exports/index.ts | 7 +- .../psl-parser/src/exports/interpret.ts | 5 + .../psl-parser/src/type-aliases.ts | 22 +- .../psl-parser/src/type-choice-calls.ts | 107 +++++++++ .../test/authoring-arguments.test.ts | 13 - .../test/authoring-arguments.value.test.ts | 29 +++ .../test/binder-contributed-types.test.ts | 14 +- .../psl-parser/test/binder.mixin.test.ts | 2 +- .../psl-parser/test/type-aliases.test.ts | 4 +- .../src/attribute-spec-resolution.ts | 4 +- .../test/attribute-spec-consumability.test.ts | 2 +- .../contract-psl/src/interpreter.ts | 53 ++-- .../contract-psl/src/type-aliases.ts | 37 +-- .../test/interpreter.type-choices.test.ts | 98 ++++++++ .../test/mongo-attribute-specs.test.ts | 2 +- .../contract-psl/src/psl-column-resolution.ts | 125 ++-------- .../src/psl-named-type-resolution.ts | 3 +- .../contract-psl/src/sql-attribute-specs.ts | 6 +- .../test/sql-expression-places.test.ts | 2 +- .../attribute-specs.lsp-consumability.test.ts | 2 +- 25 files changed, 527 insertions(+), 372 deletions(-) create mode 100644 packages/1-framework/2-authoring/psl-parser/src/type-choice-calls.ts create mode 100644 packages/2-mongo-family/2-authoring/contract-psl/test/interpreter.type-choices.test.ts diff --git a/packages/1-framework/2-authoring/psl-parser/src/attribute-spec/generator-attribute.ts b/packages/1-framework/2-authoring/psl-parser/src/attribute-spec/generator-attribute.ts index ba187f48f7d5..c0d45b466975 100644 --- a/packages/1-framework/2-authoring/psl-parser/src/attribute-spec/generator-attribute.ts +++ b/packages/1-framework/2-authoring/psl-parser/src/attribute-spec/generator-attribute.ts @@ -69,7 +69,9 @@ export function generatorFunctionCall( */ function generatorCallArm(ctx: TypeAttributeSpecContext) { const codecId = - ctx.typeConstructor === undefined ? undefined : contributedTypeCodecId(ctx.typeConstructor); + ctx.contributedType === undefined + ? undefined + : contributedTypeCodecId(ctx.contributedType, ctx.dataTypes); const functions = codecId === undefined ? [] : ctx.generatorFunctions.functionsFor(codecId); const subject = typeAttributeSubjectLabel(ctx.subject); const target = codecId === undefined ? "the field's type" : `codec "${codecId}"`; diff --git a/packages/1-framework/2-authoring/psl-parser/src/attribute-spec/spec-context.ts b/packages/1-framework/2-authoring/psl-parser/src/attribute-spec/spec-context.ts index ee1f6009e517..c556a19c1244 100644 --- a/packages/1-framework/2-authoring/psl-parser/src/attribute-spec/spec-context.ts +++ b/packages/1-framework/2-authoring/psl-parser/src/attribute-spec/spec-context.ts @@ -32,7 +32,7 @@ export interface FieldAttributeSpecContext extends AttributeSpecContext { readonly field: FieldSymbol; readonly typeResolution: Resolution | undefined; /** The contributed type the field's type resolves to, through a named type when it is declared with one, with the call it is written with. */ - readonly typeConstructor: ContributedTypeUse | undefined; + readonly contributedType: ContributedTypeUse | undefined; } /** The declaration an attribute that reads its type is written on: a field, or a type alias whose fields take the attribute. */ @@ -55,7 +55,7 @@ export interface TypeAttributeSpecContext extends Omit descriptor.kind === 'value' && descriptor.name !== undefined, + if (!/^[+-]?\d/.test(written)) return refused; + const takers = descriptors.flatMap((descriptor) => + descriptor.kind === 'value' && + descriptor.name !== undefined && + readWrittenValueAs(dataTypes, dataTypeId(descriptor.dataType), { + kind: 'number', + text: written, + }).ok + ? [descriptor.name] + : [], ); - return /^[+-]?\d/.test(written) && valueArgument?.name !== undefined - ? `${refused} Write ${valueArgument.name} by name, as in ${valueArgument.name}: ${written}.` + const [taker] = takers; + return takers.length === 1 && taker !== undefined + ? `${refused} Write ${taker} by name, as in ${taker}: ${written}.` : refused; } -function pushInvalidPslHelperArgument(input: { - readonly diagnostics: PslDiagnosticCollector; - readonly source: DiagnosticSource; - readonly span: PslSpan; - readonly entityLabel: string; - readonly helperLabel: string; - readonly message: string; -}): undefined { - input.diagnostics.push({ - code: 'PSL_INVALID_ATTRIBUTE_ARGUMENT', - message: `${input.entityLabel} ${input.helperLabel} ${input.message}`, - ...input.source.at(input.span), - }); - return undefined; +/** Where a reader of a call's arguments reports what it refuses: at the call, at an argument, or at a value whose data type refuses it. */ +interface ArgumentRefusals { + call(message: string): void; + argument(span: PslSpan, message: string): void; + value(node: SyntaxNode, failure: { readonly code: string; readonly message: string }): void; } -interface MapPslHelperArgsInput { +interface ReadPslHelperArgsInput { readonly args: readonly ResolvedAttributeArg[]; readonly descriptors: readonly AuthoringArgumentDescriptor[]; + /** The stack's data types, which a `value` argument is read as. ADR 254. */ + readonly dataTypes: DataTypeSupport; +} + +interface MapPslHelperArgsInput extends ReadPslHelperArgsInput { readonly helperLabel: string; readonly span: PslSpan; readonly diagnostics: PslDiagnosticCollector; readonly source: DiagnosticSource; readonly entityLabel: string; - /** The stack's data types, which a `value` argument is read as. ADR 254. */ - readonly dataTypes: DataTypeSupport; } +const SILENT_REFUSALS: ArgumentRefusals = { + call: () => {}, + argument: () => {}, + value: () => {}, +}; + /** - * The value of one argument as `descriptor` reads it: a `value` argument as a value of its data type, read from its expression as attribute arguments are, and any other kind from its text. A refusal is pushed as a diagnostic at the argument. + * The value of one argument as `descriptor` reads it: a `value` argument as a value of its data type, read from its expression as attribute arguments are, and any other kind from its text. A refusal is reported at the argument. */ function readPslHelperArgument( - input: MapPslHelperArgsInput, + input: ReadPslHelperArgsInput, + refusals: ArgumentRefusals, descriptor: AuthoringArgumentDescriptor, argument: ResolvedAttributeArg, argumentLabel: string, ): unknown | typeof INVALID_AUTHORING_ARGUMENT { - const invalid = (): typeof INVALID_AUTHORING_ARGUMENT => { - pushInvalidPslHelperArgument({ - diagnostics: input.diagnostics, - source: input.source, - span: argument.span, - entityLabel: input.entityLabel, - helperLabel: input.helperLabel, - message: - descriptor.kind === 'option' && descriptor.name !== undefined - ? misplacedOptionMessage( - input.descriptors, - descriptor, - descriptor.name, - argument.value.trim(), - argumentLabel, - ) - : `cannot parse ${argumentLabel} for descriptor kind "${descriptor.kind}".`, - }); + const refuse = (message: string): typeof INVALID_AUTHORING_ARGUMENT => { + refusals.argument(argument.span, message); return INVALID_AUTHORING_ARGUMENT; }; if (descriptor.kind !== 'value') { const value = parsePslAuthoringArgumentText(descriptor, argument.value); - return value === INVALID_AUTHORING_ARGUMENT ? invalid() : value; + if (value !== INVALID_AUTHORING_ARGUMENT) return value; + return refuse( + descriptor.kind === 'option' && descriptor.name !== undefined + ? misplacedOptionMessage( + input.descriptors, + input.dataTypes, + descriptor, + descriptor.name, + argument.value.trim(), + argumentLabel, + ) + : `cannot parse ${argumentLabel} for descriptor kind "${descriptor.kind}".`, + ); } const { expression } = argument; if (expression === undefined && argument.node !== undefined) { - pushInvalidPslHelperArgument({ - diagnostics: input.diagnostics, - source: input.source, - span: argument.span, - entityLabel: input.entityLabel, - helperLabel: input.helperLabel, - message: `${argumentLabel} has no value.`, - }); - return INVALID_AUTHORING_ARGUMENT; + return refuse(`${argumentLabel} has no value.`); } if (expression === undefined) { throw new InternalError( - `${input.entityLabel} ${input.helperLabel} received ${argumentLabel} with no expression. A value argument is read from the expression a schema writes; a reader that builds a call must pass the arguments it parsed, or instantiate the constructor with values.`, + `A call received ${argumentLabel} with no expression. A value argument is read from the expression a schema writes; a reader that builds a call must pass the arguments it parsed, or instantiate the constructor with values.`, ); } const read = readExpressionAs(input.dataTypes, dataTypeId(descriptor.dataType), expression); if (read.ok) return read.value.value; - input.diagnostics.push({ - code: read.failure.code, - message: read.failure.message, - ...input.source.at(nodePslSpan(expression.syntax, input.source.sources)), - }); + refusals.value(expression.syntax, read.failure); return INVALID_AUTHORING_ARGUMENT; } -/** - * The arguments of a type constructor or field preset call, positional and named, each read as its descriptor says. `undefined`, with a diagnostic pushed, when one cannot be read or does not match a descriptor. - */ -export function mapPslHelperArgs(input: MapPslHelperArgsInput): readonly unknown[] | undefined { +function readPslHelperArgsReporting( + input: ReadPslHelperArgsInput, + refusals: ArgumentRefusals, +): readonly unknown[] | undefined { const mappedArgs: unknown[] = input.descriptors.map(() => undefined); const positionalArgs = input.args.filter((arg) => arg.kind === 'positional'); const namedArgs = input.args.filter((arg) => arg.kind === 'named'); if (positionalArgs.length > input.descriptors.length) { - return pushInvalidPslHelperArgument({ - diagnostics: input.diagnostics, - source: input.source, - span: input.span, - entityLabel: input.entityLabel, - helperLabel: input.helperLabel, - message: `accepts at most ${input.descriptors.length} argument(s), received ${positionalArgs.length}.`, - }); + refusals.call( + `accepts at most ${input.descriptors.length} argument(s), received ${positionalArgs.length}.`, + ); + return undefined; } for (const [index, argument] of positionalArgs.entries()) { const descriptor = input.descriptors[index]; if (!descriptor) { - return pushInvalidPslHelperArgument({ - diagnostics: input.diagnostics, - source: input.source, - span: argument.span, - entityLabel: input.entityLabel, - helperLabel: input.helperLabel, - message: `does not define positional argument #${index + 1}.`, - }); + refusals.argument(argument.span, `does not define positional argument #${index + 1}.`); + return undefined; } - const value = readPslHelperArgument(input, descriptor, argument, `argument #${index + 1}`); + const value = readPslHelperArgument( + input, + refusals, + descriptor, + argument, + `argument #${index + 1}`, + ); if (value === INVALID_AUTHORING_ARGUMENT) return undefined; mappedArgs[index] = value; } @@ -482,42 +475,20 @@ export function mapPslHelperArgs(input: MapPslHelperArgsInput): readonly unknown const descriptorIndex = input.descriptors.findIndex( (descriptor) => descriptor.name === argument.name, ); - if (descriptorIndex < 0) { - return pushInvalidPslHelperArgument({ - diagnostics: input.diagnostics, - source: input.source, - span: argument.span, - entityLabel: input.entityLabel, - helperLabel: input.helperLabel, - message: `received unknown named argument "${argument.name}".`, - }); + const descriptor = input.descriptors[descriptorIndex]; + if (descriptorIndex < 0 || descriptor === undefined) { + refusals.argument(argument.span, `received unknown named argument "${argument.name}".`); + return undefined; } if (mappedArgs[descriptorIndex] !== undefined) { - return pushInvalidPslHelperArgument({ - diagnostics: input.diagnostics, - source: input.source, - span: argument.span, - entityLabel: input.entityLabel, - helperLabel: input.helperLabel, - message: `received duplicate value for argument "${argument.name}".`, - }); - } - - const descriptor = input.descriptors[descriptorIndex]; - if (!descriptor) { - return pushInvalidPslHelperArgument({ - diagnostics: input.diagnostics, - source: input.source, - span: argument.span, - entityLabel: input.entityLabel, - helperLabel: input.helperLabel, - message: `does not define named argument "${argument.name}".`, - }); + refusals.argument(argument.span, `received duplicate value for argument "${argument.name}".`); + return undefined; } const value = readPslHelperArgument( input, + refusals, descriptor, argument, `named argument "${argument.name}"`, @@ -528,3 +499,32 @@ export function mapPslHelperArgs(input: MapPslHelperArgsInput): readonly unknown return mappedArgs; } + +/** + * The arguments of a type constructor or field preset call, positional and named, each read as its descriptor says. `undefined`, with a diagnostic pushed, when one cannot be read or does not match a descriptor. + */ +export function mapPslHelperArgs(input: MapPslHelperArgsInput): readonly unknown[] | undefined { + const refused = (span: PslSpan, message: string) => + input.diagnostics.push({ + code: 'PSL_INVALID_ATTRIBUTE_ARGUMENT', + message: `${input.entityLabel} ${input.helperLabel} ${message}`, + ...input.source.at(span), + }); + return readPslHelperArgsReporting(input, { + call: (message) => refused(input.span, message), + argument: refused, + value: (node, failure) => + input.diagnostics.push({ + code: failure.code, + message: failure.message, + ...input.source.at(nodePslSpan(node, input.source.sources)), + }), + }); +} + +/** + * The arguments of a call read as {@link mapPslHelperArgs} reads them, without reporting what it refuses: `undefined` when the call does not read, which the reader that resolves the column reports. + */ +export function readPslHelperArgs(input: ReadPslHelperArgsInput): readonly unknown[] | undefined { + return readPslHelperArgsReporting(input, SILENT_REFUSALS); +} diff --git a/packages/1-framework/2-authoring/psl-parser/src/binder.ts b/packages/1-framework/2-authoring/psl-parser/src/binder.ts index 65d94e54215a..1079c960615c 100644 --- a/packages/1-framework/2-authoring/psl-parser/src/binder.ts +++ b/packages/1-framework/2-authoring/psl-parser/src/binder.ts @@ -513,12 +513,12 @@ function bind(options: BindingInputs): BinderResult { if (specContext === undefined) return undefined; const node = typeReferenceNode(field); const typeResolution = node === undefined ? undefined : references.get(node); - const typeConstructor = contributedTypeOfResolution( + const contributedType = contributedTypeOfResolution( typeResolution, (base) => references.get(base), field.typeConstructor, ); - return factory({ ...specContext, field, typeResolution, typeConstructor }); + return factory({ ...specContext, field, typeResolution, contributedType }); }, { ...context, field }, ); diff --git a/packages/1-framework/2-authoring/psl-parser/src/contributed-type-scope.ts b/packages/1-framework/2-authoring/psl-parser/src/contributed-type-scope.ts index f45c30767e60..f5860ce3244f 100644 --- a/packages/1-framework/2-authoring/psl-parser/src/contributed-type-scope.ts +++ b/packages/1-framework/2-authoring/psl-parser/src/contributed-type-scope.ts @@ -3,11 +3,13 @@ import { type AuthoringTypeAliasDescriptor, type AuthoringTypeChoiceDescriptor, type AuthoringTypeConstructorDescriptor, - isAuthoringTypeChoiceOption, + type DataTypeSupport, resolveAuthoringTypeChoice, + typeConstructorLookup, } from '@internal/framework-components/authoring'; import { isInternalError } from '@internal/utils/internal-error'; import { notOk, ok, type Result } from '@internal/utils/result'; +import { readPslHelperArgs } from './authoring-arguments'; import type { ResolvedTypeConstructorCall } from './resolve'; export type ContributedTypeDescriptor = @@ -20,13 +22,28 @@ export type ContributedTypeNamespace = { readonly [name: string]: ContributedTypeDescriptor | ContributedTypeNamespace; }; -export interface ContributedTypeSymbol { +interface ContributedTypeSymbolBase { readonly kind: 'contributedType'; readonly name: string; readonly path: readonly string[]; - readonly descriptor: ContributedTypeDescriptor; - /** For a type choice, the type constructors its rows name, by name, from the namespace that holds it. */ - readonly choiceTypes?: ReadonlyMap; +} + +/** A contributed type choice, with the type constructors its rows name, by name, from the namespace that holds it. */ +export interface ContributedTypeChoiceSymbol extends ContributedTypeSymbolBase { + readonly descriptor: AuthoringTypeChoiceDescriptor; + readonly choiceTypes: ReadonlyMap; +} + +export type ContributedTypeSymbol = + | (ContributedTypeSymbolBase & { + readonly descriptor: Exclude; + }) + | ContributedTypeChoiceSymbol; + +export function isContributedTypeChoice( + symbol: ContributedTypeSymbol, +): symbol is ContributedTypeChoiceSymbol { + return symbol.descriptor.kind === 'typeChoice'; } /** A contributed type as a field or type alias uses it: the type, and the call it is written with, `undefined` when it is written bare. */ @@ -61,34 +78,26 @@ export function isContributedTypeDescriptor( ); } -/** The bare-name values a call writes for the options of a type choice, by the choice's argument positions; every other argument is left out. */ -function writtenOptions( - descriptor: AuthoringTypeChoiceDescriptor, - call: ResolvedTypeConstructorCall | undefined, -): readonly unknown[] { - const positional = call?.args.filter((arg) => arg.kind === 'positional') ?? []; - return descriptor.args.map((argument, index) => { - if (!isAuthoringTypeChoiceOption(argument)) return undefined; - const written = - call?.args.find((arg) => arg.kind === 'named' && arg.name === argument.name) ?? - positional[index]; - return written?.value.trim(); - }); -} - -/** The type constructor a use of the type choice `descriptor` chooses from its options, or why it chooses none. */ +/** The type constructor a call of the type choice `symbol` chooses, reading the call as the column does, or why it chooses none. */ export function chosenTypeConstructor( - use: ContributedTypeUse, - descriptor: AuthoringTypeChoiceDescriptor, + symbol: ContributedTypeChoiceSymbol, + call: ResolvedTypeConstructorCall | undefined, + dataTypes: DataTypeSupport, ): Result { - const { choiceTypes, path } = use.symbol; + const helperPath = symbol.path.join('.'); + const args = readPslHelperArgs({ + args: call?.args ?? [], + descriptors: symbol.descriptor.args, + dataTypes, + }); + if (args === undefined) return notOk(`The arguments of ${helperPath} do not read.`); try { return ok( resolveAuthoringTypeChoice({ - helperPath: path.join('.'), - descriptor, - args: writtenOptions(descriptor, use.call), - constructorNamed: (name) => choiceTypes?.get(name), + helperPath, + descriptor: symbol.descriptor, + args, + constructorNamed: (name) => symbol.choiceTypes.get(name), }).descriptor, ); } catch (error) { @@ -98,18 +107,19 @@ export function chosenTypeConstructor( } /** The codec a column typed by `use` is stored with; `undefined` for a type choice whose call chooses no type, which the column's resolution reports. */ -export function contributedTypeCodecId(use: ContributedTypeUse): string | undefined { - const { descriptor } = use.symbol; - switch (descriptor.kind) { - case 'typeAlias': - return descriptor.output.type.codecId; - case 'typeChoice': { - const chosen = chosenTypeConstructor(use, descriptor); - return chosen.ok ? chosen.value.output.codecId : undefined; - } - default: - return descriptor.output.codecId; +export function contributedTypeCodecId( + use: ContributedTypeUse, + dataTypes: DataTypeSupport, +): string | undefined { + const { symbol } = use; + if (isContributedTypeChoice(symbol)) { + const chosen = chosenTypeConstructor(symbol, use.call, dataTypes); + return chosen.ok ? chosen.value.output.codecId : undefined; } + const { descriptor } = symbol; + return descriptor.kind === 'typeAlias' + ? descriptor.output.type.codecId + : descriptor.output.codecId; } export function mergeContributedTypes( @@ -154,35 +164,25 @@ function collect( members.set( name, isContributedTypeDescriptor(value) - ? { - kind: 'contributedType', - name, - path, - descriptor: value, - ...(value.kind === 'typeChoice' - ? { choiceTypes: choiceTypesOf(value, namespace) } - : {}), - } + ? contributedTypeSymbol(name, path, value, namespace) : { kind: 'contributedNamespace', name, path, members: collect(value, path) }, ); } return members; } -function choiceTypesOf( - descriptor: AuthoringTypeChoiceDescriptor, +function contributedTypeSymbol( + name: string, + path: readonly string[], + descriptor: ContributedTypeDescriptor, namespace: ContributedTypeNamespace, -): ReadonlyMap { - const types = new Map(); +): ContributedTypeSymbol { + if (descriptor.kind !== 'typeChoice') return { kind: 'contributedType', name, path, descriptor }; + const constructorNamed = typeConstructorLookup(namespace); + const choiceTypes = new Map(); for (const { type } of descriptor.choices) { - const member = Object.hasOwn(namespace, type) ? namespace[type] : undefined; - if ( - member !== undefined && - isContributedTypeDescriptor(member) && - member.kind === 'typeConstructor' - ) { - types.set(type, member); - } + const chosen = constructorNamed(type); + if (chosen !== undefined) choiceTypes.set(type, chosen); } - return types; + return { kind: 'contributedType', name, path, descriptor, choiceTypes }; } diff --git a/packages/1-framework/2-authoring/psl-parser/src/exports/index.ts b/packages/1-framework/2-authoring/psl-parser/src/exports/index.ts index d7367a5035a4..51008e5002aa 100644 --- a/packages/1-framework/2-authoring/psl-parser/src/exports/index.ts +++ b/packages/1-framework/2-authoring/psl-parser/src/exports/index.ts @@ -172,13 +172,18 @@ export type { export type { ContributedMember, ContributedNamespaceSymbol, + ContributedTypeChoiceSymbol, ContributedTypeDescriptor, ContributedTypeNamespace, ContributedTypeScope, ContributedTypeSymbol, ContributedTypeUse, } from '../contributed-type-scope'; -export { chosenTypeConstructor, contributedTypeCodecId } from '../contributed-type-scope'; +export { + chosenTypeConstructor, + contributedTypeCodecId, + isContributedTypeChoice, +} from '../contributed-type-scope'; export type { DiagnosticSource, PslDiagnostic, PslDiagnosticCollector } from '../diagnostic'; export { createPslDiagnosticCollector, diff --git a/packages/1-framework/2-authoring/psl-parser/src/exports/interpret.ts b/packages/1-framework/2-authoring/psl-parser/src/exports/interpret.ts index 55962d95f543..3f57bae3d279 100644 --- a/packages/1-framework/2-authoring/psl-parser/src/exports/interpret.ts +++ b/packages/1-framework/2-authoring/psl-parser/src/exports/interpret.ts @@ -9,5 +9,10 @@ export { fkRelationPairKey, requiredOneToOneBackrelationDiagnostic, } from '../relation-backrelations'; +export { + argumentIndexOf, + argumentSpan, + instantiatePslTypeChoice, +} from '../type-choice-calls'; export { isBareTypeConstructor, reportTypeConstructorNotCalled } from '../type-constructor-calls'; export { claimedBlockKeywords, unsupportedBlockDiagnostic } from '../unclaimed-blocks'; diff --git a/packages/1-framework/2-authoring/psl-parser/src/type-aliases.ts b/packages/1-framework/2-authoring/psl-parser/src/type-aliases.ts index b166b387e3a0..14ac2ab6f487 100644 --- a/packages/1-framework/2-authoring/psl-parser/src/type-aliases.ts +++ b/packages/1-framework/2-authoring/psl-parser/src/type-aliases.ts @@ -1,6 +1,5 @@ import { type AuthoringTypeAliasDescriptor, - type AuthoringTypeChoiceDescriptor, type AuthoringTypeConstructorDescriptor, type AuthoringTypeNamespace, type DataTypeSupport, @@ -11,7 +10,10 @@ import type { GeneratorFunctionRegistry } from '@internal/framework-components/c import type { ControlMutationDefaultRegistry } from '@internal/framework-components/control'; import type { TypeAttributeSpecContext } from './attribute-spec/spec-context'; import { type Binder, contributedTypeOf, type Resolution, typeReferenceNode } from './binder'; -import type { ContributedTypeSymbol } from './contributed-type-scope'; +import { + type ContributedTypeChoiceSymbol, + isContributedTypeChoice, +} from './contributed-type-scope'; import { diagnosticSource, type PslDiagnosticCollector } from './diagnostic'; import type { PslSources } from './source-file'; import type { @@ -44,8 +46,7 @@ export type TypeAliasBase = } | { readonly kind: 'typeChoice'; - readonly descriptor: AuthoringTypeChoiceDescriptor; - readonly symbol: ContributedTypeSymbol; + readonly symbol: ContributedTypeChoiceSymbol; /** `undefined` when the type choice is written bare. */ readonly call: ResolvedTypeConstructorCall | undefined; } @@ -193,15 +194,10 @@ function baseOf( return { kind: 'block', symbol: resolution.symbol }; } if (resolution?.kind !== 'contributedType') return undefined; - const { descriptor } = resolution.symbol; - if (descriptor.kind === 'typeChoice') { - return { - kind: 'typeChoice', - descriptor, - symbol: resolution.symbol, - call: declaration.typeConstructor, - }; + if (isContributedTypeChoice(resolution.symbol)) { + return { kind: 'typeChoice', symbol: resolution.symbol, call: declaration.typeConstructor }; } + const { descriptor } = resolution.symbol; if (descriptor.kind !== 'typeConstructor') return undefined; if (declaration.isConstructor) { return { kind: 'typeConstructor', descriptor, call: declaration.typeConstructor }; @@ -360,7 +356,7 @@ export function typeAliasSpecContext( list: false, typeName: alias.baseName, typeResolution: alias.baseResolution, - typeConstructor: contributedTypeOf( + contributedType: contributedTypeOf( alias.baseResolution, input.binder, alias.symbol.typeConstructor, diff --git a/packages/1-framework/2-authoring/psl-parser/src/type-choice-calls.ts b/packages/1-framework/2-authoring/psl-parser/src/type-choice-calls.ts new file mode 100644 index 000000000000..dfc48ed0a5e7 --- /dev/null +++ b/packages/1-framework/2-authoring/psl-parser/src/type-choice-calls.ts @@ -0,0 +1,107 @@ +import { + type AuthoringArgumentDescriptor, + type DataTypeSupport, + instantiateAuthoringTypeConstructor, + resolveAuthoringTypeChoice, + type ScalarTypeConstructorOutput, + validateAuthoringTypeChoiceParams, +} from '@internal/framework-components/authoring'; +import type { CodecLookupWithDescriptors } from '@internal/framework-components/codec'; +import type { PslSpan } from '@internal/framework-components/psl-ast'; +import { isInternalError } from '@internal/utils/internal-error'; +import { mapPslHelperArgs } from './authoring-arguments'; +import type { ContributedTypeChoiceSymbol } from './contributed-type-scope'; +import type { DiagnosticSource, PslDiagnosticCollector } from './diagnostic'; +import type { ResolvedTypeConstructorCall } from './resolve'; + +/** The span of the argument at `index` of `descriptors` in `call`, named or positional, or the call's when it is not written. */ +export function argumentSpan( + call: ResolvedTypeConstructorCall, + descriptors: readonly AuthoringArgumentDescriptor[] | undefined, + index: number | undefined, +): PslSpan { + if (index === undefined) return call.span; + const name = descriptors?.[index]?.name; + const named = call.args.find((arg) => arg.kind === 'named' && arg.name === name); + const positional = call.args.filter((arg) => arg.kind === 'positional')[index]; + return (named ?? positional)?.span ?? call.span; +} + +/** The index of the argument an authoring refusal names, when it names one. */ +export function argumentIndexOf(error: unknown): number | undefined { + if (typeof error !== 'object' || error === null || !('details' in error)) return undefined; + const { details } = error; + if (typeof details !== 'object' || details === null || !('argumentIndex' in details)) { + return undefined; + } + return typeof details.argumentIndex === 'number' ? details.argumentIndex : undefined; +} + +/** + * The column a field or type alias typed by the type choice `symbol` gets: its call's arguments, read as the choice declares them, choose a type constructor, which is instantiated with the arguments the choice passes on. A refusal is reported at the argument that makes it. `span` is where a bare use is written. + */ +export function instantiatePslTypeChoice(input: { + readonly symbol: ContributedTypeChoiceSymbol; + readonly call: ResolvedTypeConstructorCall | undefined; + readonly span: PslSpan; + readonly codecLookup: Pick; + readonly diagnostics: PslDiagnosticCollector; + readonly source: DiagnosticSource; + readonly entityLabel: string; + readonly dataTypes: DataTypeSupport; +}): ScalarTypeConstructorOutput | undefined { + const { symbol } = input; + const { descriptor } = symbol; + const helperPath = symbol.path.join('.'); + const call = input.call ?? { path: symbol.path, args: [], span: input.span }; + const subject = `${input.entityLabel} type "${helperPath}"`; + const args = mapPslHelperArgs({ + args: call.args, + descriptors: descriptor.args, + helperLabel: `type "${helperPath}"`, + span: call.span, + diagnostics: input.diagnostics, + source: input.source, + entityLabel: input.entityLabel, + dataTypes: input.dataTypes, + }); + if (!args) return undefined; + + let chosen: ReturnType; + try { + chosen = resolveAuthoringTypeChoice({ + helperPath, + descriptor, + args, + constructorNamed: (name) => symbol.choiceTypes.get(name), + }); + } catch (error) { + if (isInternalError(error)) throw error; + input.diagnostics.push({ + code: 'PSL_INVALID_ATTRIBUTE_ARGUMENT', + message: `${subject}: ${error instanceof Error ? error.message : String(error)}`, + ...input.source.at(argumentSpan(call, descriptor.args, argumentIndexOf(error))), + }); + return undefined; + } + + const output = instantiateAuthoringTypeConstructor(chosen.descriptor, chosen.args); + try { + validateAuthoringTypeChoiceParams( + helperPath, + descriptor, + chosen.descriptor, + output.typeParams, + input.codecLookup.descriptorFor(output.codecId)?.paramsSchema, + ); + } catch (error) { + if (isInternalError(error)) throw error; + input.diagnostics.push({ + code: 'PSL_INVALID_ATTRIBUTE_ARGUMENT', + message: `${subject}: ${error instanceof Error ? error.message : String(error)}`, + ...input.source.at(argumentSpan(call, descriptor.args, argumentIndexOf(error))), + }); + return undefined; + } + return output; +} diff --git a/packages/1-framework/2-authoring/psl-parser/test/authoring-arguments.test.ts b/packages/1-framework/2-authoring/psl-parser/test/authoring-arguments.test.ts index 45794658ae60..6e39fac4ef7a 100644 --- a/packages/1-framework/2-authoring/psl-parser/test/authoring-arguments.test.ts +++ b/packages/1-framework/2-authoring/psl-parser/test/authoring-arguments.test.ts @@ -135,19 +135,6 @@ describe('mapPslHelperArgs argument kinds', () => { expect(diagnostics[0]?.message).not.toMatch(/cannot parse/); }); - it('says to write a number by name when it is written where an option is expected', () => { - const { diagnostics } = callMap( - [positional('3')], - [ - { kind: 'option', name: 'value', values: ['string', 'temporal'] }, - { kind: 'value', name: 'precision', dataType: 'test/int' }, - ], - ); - expect(diagnostics[0]?.message).toMatch( - / argument #1 is 3, but argument "value" takes one of these names, written without quotes: string, temporal\. Write precision by name, as in precision: 3\.$/, - ); - }); - it('rejects an object for an option-kind argument', () => { const { result, diagnostics } = callMap( [positional('{a: 1}')], diff --git a/packages/1-framework/2-authoring/psl-parser/test/authoring-arguments.value.test.ts b/packages/1-framework/2-authoring/psl-parser/test/authoring-arguments.value.test.ts index a976a15ffed1..249db8773530 100644 --- a/packages/1-framework/2-authoring/psl-parser/test/authoring-arguments.value.test.ts +++ b/packages/1-framework/2-authoring/psl-parser/test/authoring-arguments.value.test.ts @@ -198,3 +198,32 @@ describe('mapPslHelperArgs on a value argument with no expression', () => { ); }); }); + +describe('a number written where an option is expected', () => { + const option = { + kind: 'option', + name: 'value', + values: ['string', 'temporal'], + } as const; + const message = (refusal: { diagnostics: ContractSourceDiagnostic[] }) => + refusal.diagnostics[0]?.message; + + it('says to write the one value argument that takes a number by name', () => { + expect(message(callWith('3', [option, { ...length, name: 'precision' }]))).toBe( + 'Field "Model.field" constructor "Sized" argument #1 is 3, but argument "value" takes one of these names, written without quotes: string, temporal. Write precision by name, as in precision: 3.', + ); + }); + + it('says only which names the option takes when no value argument takes a number', () => { + const label = { kind: 'value', dataType: 'pg/text', name: 'label', optional: true } as const; + expect(message(callWith('3', [option, label]))).toBe( + 'Field "Model.field" constructor "Sized" argument #1 is 3, but argument "value" takes one of these names, written without quotes: string, temporal.', + ); + }); + + it('says only which names the option takes when two value arguments take a number', () => { + expect(message(callWith('3', [option, length, { ...length, name: 'scale' }]))).toBe( + 'Field "Model.field" constructor "Sized" argument #1 is 3, but argument "value" takes one of these names, written without quotes: string, temporal.', + ); + }); +}); diff --git a/packages/1-framework/2-authoring/psl-parser/test/binder-contributed-types.test.ts b/packages/1-framework/2-authoring/psl-parser/test/binder-contributed-types.test.ts index f7b895309e19..93358fe39e8b 100644 --- a/packages/1-framework/2-authoring/psl-parser/test/binder-contributed-types.test.ts +++ b/packages/1-framework/2-authoring/psl-parser/test/binder-contributed-types.test.ts @@ -235,13 +235,17 @@ describe('createBinder — contributed types', () => { ' positional Moment(text)', ' aliased Stamp', ' refused Moment(value: text, column: narrow)', + ' unknownNamed Moment(bogus: narrow)', + ' twice Moment(text, value: date)', '}', ].join('\n'), ); const codecOf = (fieldName: string) => { const field = symbolTable.defaultNamespace.models['Post']?.fields[fieldName]; const use = contributedTypeOf(resolve(fieldName), binder, field?.typeConstructor); - return use === undefined ? 'no contributed type' : contributedTypeCodecId(use); + return use === undefined + ? 'no contributed type' + : contributedTypeCodecId(use, EMPTY_DATA_TYPES); }; it('is the codec of the constructor the call chooses, bare, named or positional', () => { @@ -260,8 +264,12 @@ describe('createBinder — contributed types', () => { expect(codecOf('aliased')).toBe('fixture/wide-text@1'); }); - it('is unknown for a combination the choice refuses', () => { - expect(codecOf('refused')).toBeUndefined(); + it('is unknown for a call the column refuses, as the column reads the call', () => { + expect({ + refused: codecOf('refused'), + unknownNamed: codecOf('unknownNamed'), + twice: codecOf('twice'), + }).toEqual({ refused: undefined, unknownNamed: undefined, twice: undefined }); }); }); }); diff --git a/packages/1-framework/2-authoring/psl-parser/test/binder.mixin.test.ts b/packages/1-framework/2-authoring/psl-parser/test/binder.mixin.test.ts index 0bf1a1655285..7b438ff4428f 100644 --- a/packages/1-framework/2-authoring/psl-parser/test/binder.mixin.test.ts +++ b/packages/1-framework/2-authoring/psl-parser/test/binder.mixin.test.ts @@ -602,7 +602,7 @@ describe('createBinder — binding a mixin body once', () => { ); expect(diagnostics).toEqual([]); - expect(fieldContexts.map((ctx) => ctx.typeConstructor?.symbol.name)).toEqual([ + expect(fieldContexts.map((ctx) => ctx.contributedType?.symbol.name)).toEqual([ 'Int', 'Int', undefined, diff --git a/packages/1-framework/2-authoring/psl-parser/test/type-aliases.test.ts b/packages/1-framework/2-authoring/psl-parser/test/type-aliases.test.ts index 8a77cd91981b..90d501908d7d 100644 --- a/packages/1-framework/2-authoring/psl-parser/test/type-aliases.test.ts +++ b/packages/1-framework/2-authoring/psl-parser/test/type-aliases.test.ts @@ -363,9 +363,9 @@ model Post { list: context?.list, typeName: context?.typeName, codecId: - context?.typeConstructor === undefined + context?.contributedType === undefined ? undefined - : contributedTypeCodecId(context.typeConstructor), + : contributedTypeCodecId(context.contributedType, EMPTY_DATA_TYPES), }).toEqual({ alias: 'Slug', untyped: undefined, diff --git a/packages/1-framework/3-tooling/language-server/src/attribute-spec-resolution.ts b/packages/1-framework/3-tooling/language-server/src/attribute-spec-resolution.ts index f6e82a5a90d9..d21bfbed86f6 100644 --- a/packages/1-framework/3-tooling/language-server/src/attribute-spec-resolution.ts +++ b/packages/1-framework/3-tooling/language-server/src/attribute-spec-resolution.ts @@ -193,13 +193,13 @@ export function attributeSpecResolver( }; const node = typeReferenceNode(field); const typeResolution = node === undefined ? undefined : source.binder.symbolForNode(node); - const typeConstructor = contributedTypeOf( + const contributedType = contributedTypeOf( typeResolution, source.binder, field.typeConstructor, ); return (name) => - specs.field[name]?.({ ...specContext, field, typeResolution, typeConstructor }); + specs.field[name]?.({ ...specContext, field, typeResolution, contributedType }); } } } diff --git a/packages/1-framework/3-tooling/language-server/test/attribute-spec-consumability.test.ts b/packages/1-framework/3-tooling/language-server/test/attribute-spec-consumability.test.ts index 39a619c92c74..c87c1c60b553 100644 --- a/packages/1-framework/3-tooling/language-server/test/attribute-spec-consumability.test.ts +++ b/packages/1-framework/3-tooling/language-server/test/attribute-spec-consumability.test.ts @@ -191,7 +191,7 @@ describe('assembled attribute specs are consumable from a resolved project', () model, field, typeResolution: undefined, - typeConstructor: undefined, + contributedType: undefined, defaultFunctionRegistry: interpretation.context.controlMutationDefaults.defaultFunctionRegistry, generatorFunctions: interpretation.context.generatorFunctions, diff --git a/packages/2-mongo-family/2-authoring/contract-psl/src/interpreter.ts b/packages/2-mongo-family/2-authoring/contract-psl/src/interpreter.ts index f7304ecb4bca..cd292ed3b581 100644 --- a/packages/2-mongo-family/2-authoring/contract-psl/src/interpreter.ts +++ b/packages/2-mongo-family/2-authoring/contract-psl/src/interpreter.ts @@ -80,6 +80,7 @@ import { type DiagnosticSource, diagnosticSource, interpretExtensionBlocks, + isContributedTypeChoice, mapPslDiagnostics, nodePslSpan, type PslDiagnostic, @@ -307,7 +308,7 @@ function resolvePhysicalNames(input: { ...specContext, field, typeResolution: fieldTypeResolution(field, binder), - typeConstructor: contributedTypeOf( + contributedType: contributedTypeOf( fieldTypeResolution(field, binder), binder, field.typeConstructor, @@ -1140,7 +1141,7 @@ function collectIndexes( ...specContext, field, typeResolution: fieldTypeResolution(field, binder), - typeConstructor: contributedTypeOf( + contributedType: contributedTypeOf( fieldTypeResolution(field, binder), binder, field.typeConstructor, @@ -1511,10 +1512,31 @@ function resolveNonRelationField( if (resolution?.kind !== 'contributedType') { return undefined; } - const { descriptor, path } = resolution.symbol; const call = field.typeConstructor; const entityLabel = `Field "${ownerName}.${field.name}"`; const source = diagnosticSource(presetContext.sources, field.node.syntax); + if (isContributedTypeChoice(resolution.symbol)) { + const type = scalarTypeOfChoice({ + symbol: resolution.symbol, + call, + span: field.span, + codecLookup: presetContext.typeLookups.codecLookup, + diagnostics: presetContext.diagnostics, + source, + entityLabel, + dataTypes: presetContext.dataTypes, + }); + return type === undefined + ? undefined + : { + field: { + type, + nullable: field.optional, + many: field.list ? { elementNullable: field.elementOptional } : false, + }, + }; + } + const { descriptor, path } = resolution.symbol; if (descriptor.kind === 'typeAlias') { if (call !== undefined) { presetContext.diagnostics.push({ @@ -1553,24 +1575,6 @@ function resolveNonRelationField( ...ifDefined('executionDefaults', preset.executionDefaults), }; } - if (descriptor.kind === 'typeChoice') { - const type = scalarTypeOfChoice({ symbol: resolution.symbol, call }, descriptor); - if (!type.ok) { - presetContext.diagnostics.push({ - code: 'PSL_INVALID_ATTRIBUTE_ARGUMENT', - message: `${entityLabel} type "${path.join('.')}": ${type.failure}`, - ...source.at(call?.span ?? field.span), - }); - return undefined; - } - return { - field: { - type: type.value, - nullable: field.optional, - many: field.list ? { elementNullable: field.elementOptional } : false, - }, - }; - } if (call === undefined && !isBareTypeConstructor(descriptor)) { reportTypeConstructorNotCalled({ entityLabel, @@ -1849,6 +1853,7 @@ export function interpretPslDocumentToMongoContract( defaultFunctionRegistry: input.defaultFunctionRegistry, generatorFunctions, dataTypes: input.dataTypes, + codecLookup: input.codecLookup, authoringTypes: input.authoringContributions?.type, }); @@ -1892,7 +1897,7 @@ export function interpretPslDocumentToMongoContract( ...specContext, field, typeResolution: fieldTypeResolution(field, binder), - typeConstructor: contributedTypeOf( + contributedType: contributedTypeOf( fieldTypeResolution(field, binder), binder, field.typeConstructor, @@ -2011,7 +2016,7 @@ export function interpretPslDocumentToMongoContract( ...specContext, field, typeResolution: resolution, - typeConstructor: contributedTypeOf(resolution, binder, field.typeConstructor), + contributedType: contributedTypeOf(resolution, binder, field.typeConstructor), }, generatorFunctions, sources, @@ -2043,7 +2048,7 @@ export function interpretPslDocumentToMongoContract( ...specContext, field, typeResolution: fieldTypeResolution(field, binder), - typeConstructor: contributedTypeOf( + contributedType: contributedTypeOf( fieldTypeResolution(field, binder), binder, field.typeConstructor, diff --git a/packages/2-mongo-family/2-authoring/contract-psl/src/type-aliases.ts b/packages/2-mongo-family/2-authoring/contract-psl/src/type-aliases.ts index 1ea0db5e8671..0b631343673a 100644 --- a/packages/2-mongo-family/2-authoring/contract-psl/src/type-aliases.ts +++ b/packages/2-mongo-family/2-authoring/contract-psl/src/type-aliases.ts @@ -5,7 +5,6 @@ import type { ValueSetRef, } from '@internal/contract/types'; import { - type AuthoringTypeChoiceDescriptor, type AuthoringTypeConstructorDescriptor, type AuthoringTypeNamespace, type DataTypeSupport, @@ -13,13 +12,12 @@ import { instantiateAuthoringTypeConstructor, resolveTypeAliasGenerators, } from '@internal/framework-components/authoring'; +import type { CodecLookupWithDescriptors } from '@internal/framework-components/codec'; import type { GeneratorFunctionRegistry } from '@internal/framework-components/components'; import type { ControlMutationDefaultRegistry } from '@internal/framework-components/control'; import { type Binder, type ContributedTypeAlias, - type ContributedTypeUse, - chosenTypeConstructor, contributedTypeAliasOf, diagnosticSource, interpretAttribute, @@ -36,20 +34,21 @@ import { typeAttributeSubjectLabel, typeGeneratorAttributeSpec, } from '@internal/psl-parser'; +import { instantiatePslTypeChoice } from '@internal/psl-parser/interpret'; import type { FieldAttributeAst, PslSources } from '@internal/psl-parser/syntax'; import { ifDefined } from '@internal/utils/defined'; -import { ok, type Result } from '@internal/utils/result'; import { unloweredAttributeHint } from './mongo-attribute-specs'; type ScalarFieldType = Extract; -/** The field type a use of a type choice gives: the type of the constructor its call chooses, or why it chooses none. */ +/** The field type a use of a type choice gives: the column of the constructor its call chooses, read and refused as a SQL column's is. `undefined`, with a diagnostic pushed, when the call is refused. */ export function scalarTypeOfChoice( - use: ContributedTypeUse, - descriptor: AuthoringTypeChoiceDescriptor, -): Result { - const chosen = chosenTypeConstructor(use, descriptor); - return chosen.ok ? ok(scalarTypeOfConstructor(chosen.value, use.call)) : chosen; + input: Parameters[0], +): ScalarFieldType | undefined { + const output = instantiatePslTypeChoice(input); + return output === undefined + ? undefined + : { kind: 'scalar', codecId: output.codecId, ...ifDefined('typeParams', output.typeParams) }; } /** The field type a type constructor gives, keeping a bare constructor's fixed type params. */ @@ -88,6 +87,7 @@ interface ResolveMongoTypeAliasesInput { readonly defaultFunctionRegistry: ControlMutationDefaultRegistry; readonly generatorFunctions: GeneratorFunctionRegistry; readonly dataTypes: DataTypeSupport; + readonly codecLookup: CodecLookupWithDescriptors; readonly authoringTypes: AuthoringTypeNamespace | undefined; } @@ -159,14 +159,17 @@ function aliasType( return { type: scalarTypeOfConstructor(base.descriptor, base.call), valueSet: undefined }; } if (base.kind === 'typeChoice') { - const type = scalarTypeOfChoice(base, base.descriptor); - if (type.ok) return { type: type.value, valueSet: undefined }; - input.diagnostics.push({ - code: 'PSL_INVALID_ATTRIBUTE_ARGUMENT', - message: `Type alias "${symbol.name}" type "${base.symbol.path.join('.')}": ${type.failure}`, - ...diagnosticSource(input.sources, symbol.node.syntax).at(base.call?.span ?? symbol.span), + const type = scalarTypeOfChoice({ + symbol: base.symbol, + call: base.call, + span: symbol.span, + codecLookup: input.codecLookup, + diagnostics: input.diagnostics, + source: diagnosticSource(input.sources, symbol.node.syntax), + entityLabel: `Type alias "${symbol.name}"`, + dataTypes: input.dataTypes, }); - return undefined; + return type === undefined ? undefined : { type, valueSet: undefined }; } if (base.symbol.keyword !== 'enum') { input.diagnostics.push({ diff --git a/packages/2-mongo-family/2-authoring/contract-psl/test/interpreter.type-choices.test.ts b/packages/2-mongo-family/2-authoring/contract-psl/test/interpreter.type-choices.test.ts new file mode 100644 index 000000000000..678480e65be4 --- /dev/null +++ b/packages/2-mongo-family/2-authoring/contract-psl/test/interpreter.type-choices.test.ts @@ -0,0 +1,98 @@ +import type { + AuthoringContributions, + AuthoringTypeNamespace, +} from '@internal/framework-components/authoring'; +import { describe, expect, it } from 'vitest'; +import { interpretMongoContract } from './interpreter-test-helpers'; + +const label = { kind: 'value', dataType: 'mongo/string', name: 'label', optional: true } as const; + +const type = { + Stamp: { kind: 'typeConstructor', output: { codecId: 'mongo/date@1' } }, + Text: { + kind: 'typeConstructor', + args: [label], + output: { codecId: 'mongo/string@1', typeParams: { label: { kind: 'arg', index: 0 } } }, + }, + Moment: { + kind: 'typeChoice', + args: [ + { kind: 'option', name: 'value', values: ['date', 'text'], default: 'date', optional: true }, + label, + ], + choices: [ + { when: { value: 'date' }, type: 'Stamp' }, + { when: { value: 'text' }, type: 'Text' }, + ], + }, +} as const satisfies AuthoringTypeNamespace; + +const authoringContributions: AuthoringContributions = { type }; + +function interpret(schema: string) { + return interpretMongoContract(schema, { + scalarTypeCodecIds: new Map([['ObjectId', 'mongo/objectId@1']]), + defaultFunctionRegistry: new Map(), + authoringContributions, + }); +} + +const model = (...fields: readonly string[]) => + ['model Doc {', ' id ObjectId @id @map("_id")', ...fields.map((f) => ` ${f}`), '}', ''].join( + '\n', + ); + +function fieldTypes(schema: string) { + const result = interpret(schema); + if (!result.ok) throw new Error(JSON.stringify(result.failure.diagnostics)); + const fields = result.value.domain.namespaces['__unbound__']?.models['Doc']?.fields ?? {}; + return Object.fromEntries(Object.entries(fields).map(([name, field]) => [name, field.type])); +} + +function diagnostics(schema: string) { + const result = interpret(schema); + if (result.ok) throw new Error('expected the schema to be refused'); + return result.failure.diagnostics.map(({ code, message }) => ({ code, message })); +} + +describe('a MongoDB field typed by a type choice', () => { + it('gets the type of the constructor its call chooses, bare, named and positional', () => { + expect( + fieldTypes(model('bare Moment', 'named Moment(value: text)', 'positional Moment(text, "x")')), + ).toEqual({ + id: { kind: 'scalar', codecId: 'mongo/objectId@1' }, + bare: { kind: 'scalar', codecId: 'mongo/date@1' }, + named: { kind: 'scalar', codecId: 'mongo/string@1' }, + positional: { kind: 'scalar', codecId: 'mongo/string@1', typeParams: { label: 'x' } }, + }); + }); + + it('gets the type its alias’s call chooses', () => { + expect(fieldTypes(`types {\n Note = Moment(value: text)\n}\n${model('note Note')}`)).toEqual({ + id: { kind: 'scalar', codecId: 'mongo/objectId@1' }, + note: { kind: 'scalar', codecId: 'mongo/string@1' }, + }); + }); + + it.each([ + [ + 'an unknown named argument', + 'Moment(bogus: date)', + 'Field "Doc.at" type "Moment" received unknown named argument "bogus".', + ], + [ + 'a value the option does not list', + 'Moment(value: json)', + 'Field "Doc.at" type "Moment": Argument "value" of Moment is json; write one of: date, text.', + ], + [ + 'an argument the chosen type does not take', + 'Moment(label: "x")', + 'Field "Doc.at" type "Moment": Argument "label" of Moment does not apply to Stamp, the type that value: date chooses.', + ], + ])('refuses %s at the argument', (_case, written, message) => { + expect(diagnostics(model(`at ${written}`))).toEqual([ + { code: 'PSL_INVALID_ATTRIBUTE_ARGUMENT', message }, + ]); + }); +}); diff --git a/packages/2-mongo-family/2-authoring/contract-psl/test/mongo-attribute-specs.test.ts b/packages/2-mongo-family/2-authoring/contract-psl/test/mongo-attribute-specs.test.ts index 3e5927376894..33df34025ca7 100644 --- a/packages/2-mongo-family/2-authoring/contract-psl/test/mongo-attribute-specs.test.ts +++ b/packages/2-mongo-family/2-authoring/contract-psl/test/mongo-attribute-specs.test.ts @@ -116,7 +116,7 @@ function contexts(): { model: AttributeSpecContext; field: FieldAttributeSpecCon }; return { model: modelContext, - field: { ...modelContext, field, typeResolution: undefined, typeConstructor: undefined }, + field: { ...modelContext, field, typeResolution: undefined, contributedType: undefined }, }; } diff --git a/packages/2-sql-family/2-authoring/contract-psl/src/psl-column-resolution.ts b/packages/2-sql-family/2-authoring/contract-psl/src/psl-column-resolution.ts index 7034ac4e9c29..e3415af80b04 100644 --- a/packages/2-sql-family/2-authoring/contract-psl/src/psl-column-resolution.ts +++ b/packages/2-sql-family/2-authoring/contract-psl/src/psl-column-resolution.ts @@ -12,7 +12,6 @@ import type { AuthoringEntityTypeNamespace, AuthoringFieldPresetDescriptor, AuthoringStorageTypeTemplate, - AuthoringTypeChoiceDescriptor, AuthoringTypeConstructorDescriptor, ScalarTypeConstructorOutput, } from '@internal/framework-components/authoring'; @@ -22,9 +21,7 @@ import { instantiateAuthoringTypeConstructor, isAuthoringEntityTypeDescriptor, readWrittenValue, - resolveAuthoringTypeChoice, validateAuthoringHelperArguments, - validateAuthoringTypeChoiceParams, validateAuthoringTypeParams, type WrittenScalar, type WrittenValue, @@ -43,7 +40,6 @@ import type { ContributedPslDiagnosticCode } from '@internal/framework-component import type { Binder, BlockSymbol, - ContributedTypeSymbol, FieldSymbol, ModelSymbol, NamedTypeSymbol, @@ -58,6 +54,7 @@ import type { import { type DiagnosticSource, diagnosticSource, + isContributedTypeChoice, nodePslSpan, type PslDiagnosticCollector, typeAttributeSpecContextOfField, @@ -65,7 +62,10 @@ import { typeGeneratorAttributeSpec, } from '@internal/psl-parser'; import { + argumentIndexOf, + argumentSpan, instantiatePslFieldPreset, + instantiatePslTypeChoice, isBareTypeConstructor, mapPslHelperArgs, reportPresetNotCalled, @@ -117,18 +117,6 @@ export type ColumnDescriptor = { readonly valueSet?: ValueSetRef; }; -function argumentSpan( - call: ResolvedTypeConstructorCall, - descriptors: readonly AuthoringArgumentDescriptor[] | undefined, - index: number | undefined, -): PslSpan { - if (index === undefined) return call.span; - const name = descriptors?.[index]?.name; - const named = call.args.find((arg) => arg.kind === 'named' && arg.name === name); - const positional = call.args.filter((arg) => arg.kind === 'positional')[index]; - return (named ?? positional)?.span ?? call.span; -} - /** * Checks the type parameters a constructor or preset produced against its codec's parameter schema, * reporting a failure at the argument the failing parameter came from. Returns whether they passed. @@ -163,15 +151,6 @@ export function checkPslTypeParams(input: { } } -function argumentIndexOf(error: unknown): number | undefined { - if (typeof error !== 'object' || error === null || !('details' in error)) return undefined; - const { details } = error; - if (typeof details !== 'object' || details === null || !('argumentIndex' in details)) { - return undefined; - } - return typeof details.argumentIndex === 'number' ? details.argumentIndex : undefined; -} - /** * Walks `authoringContributions.entityTypes` segment-by-segment and returns * the entity type descriptor at the resolved path, or `undefined` if no @@ -251,75 +230,6 @@ export function instantiatePslTypeConstructor(input: { return passed ? output : undefined; } -/** - * The column a field or type alias typed by the type choice `symbol` gets: its call's arguments, read as the choice declares them, choose a type constructor, which is instantiated with the arguments the choice passes on. A refusal is reported at the argument that makes it. `span` is where a bare use is written. - */ -export function instantiatePslTypeChoice(input: { - readonly symbol: ContributedTypeSymbol; - readonly descriptor: AuthoringTypeChoiceDescriptor; - readonly call: ResolvedTypeConstructorCall | undefined; - readonly span: PslSpan; - readonly codecLookup: CodecLookupWithDescriptors; - readonly diagnostics: PslDiagnosticCollector; - readonly source: DiagnosticSource; - readonly entityLabel: string; - readonly dataTypes: DataTypeSupport; -}): ScalarTypeConstructorOutput | undefined { - const { descriptor, symbol } = input; - const helperPath = symbol.path.join('.'); - const call = input.call ?? { path: symbol.path, args: [], span: input.span }; - const subject = `${input.entityLabel} type "${helperPath}"`; - const args = mapPslHelperArgs({ - args: call.args, - descriptors: descriptor.args, - helperLabel: `type "${helperPath}"`, - span: call.span, - diagnostics: input.diagnostics, - source: input.source, - entityLabel: input.entityLabel, - dataTypes: input.dataTypes, - }); - if (!args) return undefined; - - let chosen: ReturnType; - try { - chosen = resolveAuthoringTypeChoice({ - helperPath, - descriptor, - args, - constructorNamed: (name) => symbol.choiceTypes?.get(name), - }); - } catch (error) { - if (isInternalError(error)) throw error; - input.diagnostics.push({ - code: 'PSL_INVALID_ATTRIBUTE_ARGUMENT', - message: `${subject}: ${error instanceof Error ? error.message : String(error)}`, - ...input.source.at(argumentSpan(call, descriptor.args, argumentIndexOf(error))), - }); - return undefined; - } - - const output = instantiateAuthoringTypeConstructor(chosen.descriptor, chosen.args); - try { - validateAuthoringTypeChoiceParams( - helperPath, - descriptor, - chosen.descriptor, - output.typeParams, - input.codecLookup.descriptorFor(output.codecId)?.paramsSchema, - ); - } catch (error) { - if (isInternalError(error)) throw error; - input.diagnostics.push({ - code: 'PSL_INVALID_ATTRIBUTE_ARGUMENT', - message: `${subject}: ${error instanceof Error ? error.message : String(error)}`, - ...input.source.at(argumentSpan(call, descriptor.args, argumentIndexOf(error))), - }); - return undefined; - } - return output; -} - /** * Result of a codec descriptor's `columnFromEntity` authoring hook — the * per-column params derived from the entity a type constructor's @@ -631,6 +541,19 @@ export function resolveFieldTypeDescriptor( case 'mixin': return NOT_RESOLVED; case 'contributedType': { + if (isContributedTypeChoice(resolution.symbol)) { + const instantiated = instantiatePslTypeChoice({ + symbol: resolution.symbol, + call, + span: field.span, + codecLookup: input.codecLookup, + diagnostics, + source, + entityLabel, + dataTypes: input.dataTypes, + }); + return instantiated === undefined ? NOT_RESOLVED : { ok: true, descriptor: instantiated }; + } const { descriptor, path } = resolution.symbol; const written = path.join('.'); if (descriptor.kind === 'typeAlias') { @@ -644,20 +567,6 @@ export function resolveFieldTypeDescriptor( } return { ok: true, descriptor: instantiateAuthoringTypeAlias(descriptor).type }; } - if (descriptor.kind === 'typeChoice') { - const instantiated = instantiatePslTypeChoice({ - symbol: resolution.symbol, - descriptor, - call, - span: field.span, - codecLookup: input.codecLookup, - diagnostics, - source, - entityLabel, - dataTypes: input.dataTypes, - }); - return instantiated === undefined ? NOT_RESOLVED : { ok: true, descriptor: instantiated }; - } if (descriptor.kind === 'fieldPreset') { if (call === undefined) { reportPresetNotCalled({ diff --git a/packages/2-sql-family/2-authoring/contract-psl/src/psl-named-type-resolution.ts b/packages/2-sql-family/2-authoring/contract-psl/src/psl-named-type-resolution.ts index bd620fe92e12..4132ac685ed2 100644 --- a/packages/2-sql-family/2-authoring/contract-psl/src/psl-named-type-resolution.ts +++ b/packages/2-sql-family/2-authoring/contract-psl/src/psl-named-type-resolution.ts @@ -26,6 +26,7 @@ import { resolveTypeAliases, typeAliasSpecContext, } from '@internal/psl-parser'; +import { instantiatePslTypeChoice } from '@internal/psl-parser/interpret'; import type { FieldAttributeAst, PslSources } from '@internal/psl-parser/syntax'; import { sqlCodecColumnLength } from '@internal/sql-contract/data-type'; import type { @@ -35,7 +36,6 @@ import type { import { formatDbAttributeMigrationMessage } from './psl-attribute-parsing'; import { type ColumnDescriptor, - instantiatePslTypeChoice, instantiatePslTypeConstructor, lowerEnumDefault, lowerTypeDefault, @@ -95,7 +95,6 @@ function aliasDescriptor( if (base.kind === 'typeChoice') { return instantiatePslTypeChoice({ symbol: base.symbol, - descriptor: base.descriptor, call: base.call, span: symbol.span, codecLookup: input.codecLookup, diff --git a/packages/2-sql-family/2-authoring/contract-psl/src/sql-attribute-specs.ts b/packages/2-sql-family/2-authoring/contract-psl/src/sql-attribute-specs.ts index 718e720d3fa5..93ac7cf271e5 100644 --- a/packages/2-sql-family/2-authoring/contract-psl/src/sql-attribute-specs.ts +++ b/packages/2-sql-family/2-authoring/contract-psl/src/sql-attribute-specs.ts @@ -299,7 +299,9 @@ export function defaultFunctionCall( /** The data type of the column a field declares, when its type, or the named type it is declared with, is a constructor or preset the stack knows. */ function receivingDataType(ctx: TypeAttributeSpecContext): string | undefined { const codecId = - ctx.typeConstructor === undefined ? undefined : contributedTypeCodecId(ctx.typeConstructor); + ctx.contributedType === undefined + ? undefined + : contributedTypeCodecId(ctx.contributedType, ctx.dataTypes); return codecId === undefined ? undefined : ctx.dataTypes.dataTypeOfCodec(codecId); } @@ -875,7 +877,7 @@ export function fieldSpecContext(input: { model: input.model, field: input.field, typeResolution, - typeConstructor: contributedTypeOf(typeResolution, input.binder, input.field.typeConstructor), + contributedType: contributedTypeOf(typeResolution, input.binder, input.field.typeConstructor), defaultFunctionRegistry: input.defaultFunctionRegistry, generatorFunctions: input.generatorFunctions, dataTypes: input.dataTypes, diff --git a/packages/3-targets/postgres/1-target/test/sql-expression-places.test.ts b/packages/3-targets/postgres/1-target/test/sql-expression-places.test.ts index afac442e47e2..57b2656b606a 100644 --- a/packages/3-targets/postgres/1-target/test/sql-expression-places.test.ts +++ b/packages/3-targets/postgres/1-target/test/sql-expression-places.test.ts @@ -52,7 +52,7 @@ const fieldContext = { ...modelContext, field, typeResolution: undefined, - typeConstructor: undefined, + contributedType: undefined, }; const blockContext = blockSpecContext({ symbols: symbolTable, dataTypes: postgresDataTypeSupport }); diff --git a/test/integration/test/authoring/attribute-specs.lsp-consumability.test.ts b/test/integration/test/authoring/attribute-specs.lsp-consumability.test.ts index dd6d926c1dcc..9c660e72e13d 100644 --- a/test/integration/test/authoring/attribute-specs.lsp-consumability.test.ts +++ b/test/integration/test/authoring/attribute-specs.lsp-consumability.test.ts @@ -207,7 +207,7 @@ describe('mongo attribute specs are consumable from a resolved language-server p model, field, typeResolution: undefined, - typeConstructor: undefined, + contributedType: undefined, defaultFunctionRegistry: interpretation.context.controlMutationDefaults.defaultFunctionRegistry, generatorFunctions: interpretation.context.generatorFunctions, From 3e2f4967761257d7278ae55c7d31b920640fe532 Mon Sep 17 00:00:00 2001 From: willbot Date: Thu, 22 Oct 2026 22:07:11 +0200 Subject: [PATCH 063/118] The Prisma 7 reader names the field and binding when a type choice refuses its values Co-Authored-By: Claude Opus 5.5 Signed-off-by: willbot Signed-off-by: Will Madden --- .../contract-prisma7/src/interpreter.ts | 31 ++++++++++++++----- .../test/type-choice-binding.test.ts | 8 +++++ 2 files changed, 32 insertions(+), 7 deletions(-) diff --git a/packages/2-sql-family/2-authoring/contract-prisma7/src/interpreter.ts b/packages/2-sql-family/2-authoring/contract-prisma7/src/interpreter.ts index 6cd2ac0725ae..5c288113051d 100644 --- a/packages/2-sql-family/2-authoring/contract-prisma7/src/interpreter.ts +++ b/packages/2-sql-family/2-authoring/contract-prisma7/src/interpreter.ts @@ -80,7 +80,7 @@ import { } from '@internal/sql-contract-ts/contract-builder'; import { blindCast } from '@internal/utils/casts'; import { ifDefined } from '@internal/utils/defined'; -import { InternalError } from '@internal/utils/internal-error'; +import { InternalError, isInternalError } from '@internal/utils/internal-error'; import { notOk, ok, type Result } from '@internal/utils/result'; import { basename } from 'pathe'; import { @@ -1009,6 +1009,28 @@ function lowerNativeEnums( /** The SQL family's type constructor for a column of a type no data type claims (ADR 273). */ const RAW_TYPE_CONSTRUCTOR: readonly string[] = ['Raw']; +/** The constructor the values a binding maps a Prisma 7 type to choose from the type choice it names; a refusal is the binding's mistake. */ +function chooseBindingType( + label: string, + path: readonly string[], + typeChoice: NonNullable>, + values: readonly unknown[], +): ReturnType { + try { + return resolveAuthoringTypeChoice({ + helperPath: path.join('.'), + descriptor: typeChoice.descriptor, + args: values, + constructorNamed: typeChoice.constructorNamed, + }); + } catch (error) { + if (isInternalError(error) || !(error instanceof Error)) throw error; + throw new InternalError( + `The Prisma 7 target binding maps ${label} to type choice "${path.join('.')}" with values it refuses: ${error.message}`, + ); + } +} + /** Prisma 7's `Unsupported("...")`, which this source reads as ``Raw(sql`...`)``. */ function isUnsupportedType(call: ResolvedTypeConstructorCall | undefined): boolean { return call?.path.length === 1 && call.path[0] === 'Unsupported'; @@ -1306,12 +1328,7 @@ function readField(args: ReadFieldArgs): void { const chosen = typeChoice === undefined || fieldType.kind !== 'values' ? undefined - : resolveAuthoringTypeChoice({ - helperPath: constructorPath.join('.'), - descriptor: typeChoice.descriptor, - args: fieldType.values, - constructorNamed: typeChoice.constructorNamed, - }); + : chooseBindingType(label, constructorPath, typeChoice, fieldType.values); const typeConstructor = chosen?.descriptor ?? getAuthoringTypeConstructor(input.authoringContributions, constructorPath); diff --git a/packages/2-sql-family/2-authoring/contract-prisma7/test/type-choice-binding.test.ts b/packages/2-sql-family/2-authoring/contract-prisma7/test/type-choice-binding.test.ts index 4e2e2af15cfd..e2ade2594cef 100644 --- a/packages/2-sql-family/2-authoring/contract-prisma7/test/type-choice-binding.test.ts +++ b/packages/2-sql-family/2-authoring/contract-prisma7/test/type-choice-binding.test.ts @@ -40,4 +40,12 @@ describe('a Prisma 7 type the binding maps to a type choice', () => { typeParams: { precision: 3 }, }); }); + + it('names the field and the binding when the values choose no type', () => { + expect(() => + interpretSchemaTextResult(SCHEMA, bindingWithDateTime(['jsDate', 'timetz'])), + ).toThrow( + 'The Prisma 7 target binding maps Field "Event.at" to type choice "DateTime" with values it refuses: Argument "column" of DateTime is timetz; write one of: timestamptz, timestamp.', + ); + }); }); From d8ab9189aeb0b634f49daf2db6b7cf79383a318a Mon Sep 17 00:00:00 2001 From: willbot Date: Thu, 22 Oct 2026 22:07:33 +0200 Subject: [PATCH 064/118] Glossary: explicit type, convenience type and type choice Co-Authored-By: Claude Opus 5.5 Signed-off-by: willbot Signed-off-by: Will Madden --- docs/glossary.md | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/docs/glossary.md b/docs/glossary.md index d569c81c4cee..733df434055e 100644 --- a/docs/glossary.md +++ b/docs/glossary.md @@ -213,6 +213,14 @@ A specific feature that a database may or may not support (e.g., `RETURNING` cla Handles the translation between JavaScript values and database values. When you read a timestamp from the database, a codec converts it to a JavaScript `Date`; when you write it back, the codec converts it the other way. Extensions provide codecs for specialized types like vectors or geometries. +### Convenience type + +A PSL type named for what a field means in the application, such as `DateTime`, `Date`, `Time` or `Duration`, that becomes exactly one explicit type. Optional arguments choose which, with defaults. The contract stores only the explicit type. See ADR 279. + +### Explicit type + +A PSL type that fixes one codec, so it fixes both the column type and the value the application reads. It is named after the database, with a suffix for the kind of value when a column type has several codecs: `TimestamptzTemporal`, `TimestamptzJsDate`, `DateString`. See ADR 257 and ADR 279. + ### Marker A small record stored in the database that tracks which contract the database is currently migrated to. Before running queries or migrations, Prisma 8 checks that the marker matches the contract the application is carrying. This catches situations where the database and application have drifted out of sync — for example, if a migration was run but the application wasn't redeployed. @@ -227,6 +235,10 @@ Where a database object's name comes from. An object is **wire-named** when Pris Naming mode is independent of [control policy](architecture%20docs/adrs/ADR%20224%20-%20Control%20Policy%20—%20framework-locked%20vocabulary%20and%20family-owned%20dispatch.md), which answers a different question: whether Prisma 8 may write to the object at all. +### Type choice + +The declaration a pack contributes for a convenience type: its option arguments with their values and defaults, the arguments it passes on, and a table from each allowed combination of option values to the name of an explicit type constructor. See ADR 279. + ### Wire Name The name Prisma 8 derives for an object it names itself: your prefix, an underscore, and eight hex characters of a hash over the object's content — `user_email_idx_46df9cad`. Because the suffix is content-addressed, an unchanged definition always produces the same name, and a name match is a content match. Renaming the prefix while leaving the definition alone keeps the suffix, which is how a rename is recognized as a rename rather than a drop and a create. See [ADR 234](architecture%20docs/adrs/ADR%20234%20-%20Content-addressed%20wire%20names%20for%20Postgres-normalized%20objects.md). From 9b01e9c239e5694515727a839f26933badb13c87 Mon Sep 17 00:00:00 2001 From: willbot Date: Thu, 22 Oct 2026 22:31:33 +0200 Subject: [PATCH 065/118] The upgrade instruction gives the verified steps for moving to the new defaults Date and Time change only the codec: migration plan refuses and names migration new --from , which writes a migration with no operations, and db update applies none. DateTime lowers a timestamptz column to timestamptz(3), which both commands alter only with --delete .. A journey pins each step. Co-Authored-By: Claude Opus 5.5 Signed-off-by: willbot Signed-off-by: Will Madden --- ...nvenience-date-time-types-move.e2e.test.ts | 149 ++++++++++++++++++ .../app/instructions.md | 7 +- 2 files changed, 155 insertions(+), 1 deletion(-) create mode 100644 test/integration/test/cli-journeys/convenience-date-time-types-move.e2e.test.ts diff --git a/test/integration/test/cli-journeys/convenience-date-time-types-move.e2e.test.ts b/test/integration/test/cli-journeys/convenience-date-time-types-move.e2e.test.ts new file mode 100644 index 000000000000..52618f2ce900 --- /dev/null +++ b/test/integration/test/cli-journeys/convenience-date-time-types-move.e2e.test.ts @@ -0,0 +1,149 @@ +/** + * Journey: a schema that wrote the Temporal types under their explicit names moves its fields to the convenience types' defaults, as the upgrade instruction describes. `Date` and `Time` keep the `date` and `time(0)` columns and change only the codec: `migration plan` refuses to plan nothing and names `migration new --from `, which writes a migration with no operations, and `db update` applies no operations and moves the marker. `DateTime` makes the `timestamptz` column `timestamptz(3)`, which can round stored values, so both `migration plan` and `db update` ask for `--delete Event.at` before they alter it. + */ +import { readFileSync, writeFileSync } from 'node:fs'; +import { join } from 'pathe'; +import stripAnsi from 'strip-ansi'; +import { describe, expect, it } from 'vitest'; +import { withTempDir } from '../utils/cli-test-helpers'; +import { + type JourneyContext, + runContractEmit, + runDbInit, + runDbUpdate, + runDbVerify, + runMigrate, + runMigrationNew, + runMigrationPlan, + setupJourney, + timeouts, + useDevDatabase, +} from '../utils/journey-test-helpers'; + +const schema = (day: string, opens: string, at: string) => `// use prisma-8 + +model Event { + id Int @id + day ${day} + opens ${opens} + at ${at} + + @@map("event") +} +`; + +const EXPLICIT = schema('DateTemporal', 'TimeTemporal(0)', 'TimestamptzTemporal'); +const CODEC_ONLY = schema('Date', 'Time(precision: 0)', 'TimestamptzTemporal'); +const PRECISION = schema('Date', 'Time(precision: 0)', 'DateTime'); + +function output(result: { readonly stdout: string; readonly stderr: string }): string { + return `${stripAnsi(result.stderr)}\n${stripAnsi(result.stdout)}`; +} + +async function emit(ctx: JourneyContext, text: string): Promise { + writeFileSync(join(ctx.testDir, 'contract.prisma'), text, 'utf-8'); + const emitted = await runContractEmit(ctx); + expect(emitted.exitCode, output(emitted)).toBe(0); + const contract: { readonly storage: { readonly storageHash: string } } = JSON.parse( + readFileSync(join(ctx.testDir, 'contract.json'), 'utf-8'), + ); + return contract.storage.storageHash; +} + +async function expectVerified(ctx: JourneyContext): Promise { + const verify = await runDbVerify(ctx); + expect(verify.exitCode, output(verify)).toBe(0); +} + +withTempDir(({ createTempDir }) => { + describe('Journey: moving explicit Temporal fields to the convenience types', () => { + describe('with migrations', () => { + const db = useDevDatabase(); + + it( + 'writes a migration with no operations for a codec-only change, and a lossy-change consent for the precision', + async () => { + const ctx = setupJourney({ + connectionString: db.connectionString, + createTempDir, + contractMode: 'psl', + }); + const explicit = await emit(ctx, EXPLICIT); + const first = await runMigrationPlan(ctx, ['--name', 'initial']); + expect(first.exitCode, output(first)).toBe(0); + const applied = await runMigrate(ctx); + expect(applied.exitCode, output(applied)).toBe(0); + + const codecOnly = await emit(ctx, CODEC_ONLY); + const refused = await runMigrationPlan(ctx, ['--from', explicit, '--name', 'codecs']); + expect(refused.exitCode).toBe(2); + expect(output(refused)).toContain( + 'The contract changed, but migration plan found nothing to change in the database.', + ); + expect(output(refused)).toContain(`prisma migration new --from ${explicit}`); + const empty = await runMigrationNew(ctx, ['--from', explicit, '--name', 'codecs']); + expect(empty.exitCode, output(empty)).toBe(0); + const appliedEmpty = await runMigrate(ctx); + expect(appliedEmpty.exitCode, output(appliedEmpty)).toBe(0); + expect(output(appliedEmpty)).toContain('Applied 1 migration(s) (0 operation(s))'); + await expectVerified(ctx); + + await emit(ctx, PRECISION); + const unconsented = await runMigrationPlan(ctx, ['--from', codecOnly, '--name', 'at']); + expect(unconsented.exitCode).toBe(2); + expect(output(unconsented)).toContain('--delete Event.at'); + const precision = await runMigrationPlan(ctx, [ + '--from', + codecOnly, + '--name', + 'precision', + '--delete', + 'Event.at', + ]); + expect(precision.exitCode, output(precision)).toBe(0); + expect(output(precision)).toContain( + 'ALTER TABLE "public"."event" ALTER COLUMN "at" TYPE timestamptz(3)', + ); + const appliedPrecision = await runMigrate(ctx); + expect(appliedPrecision.exitCode, output(appliedPrecision)).toBe(0); + await expectVerified(ctx); + }, + timeouts.spinUpPpgDev * 3, + ); + }); + + describe('with db update', () => { + const db = useDevDatabase(); + + it( + 'applies no operations for a codec-only change, and alters the precision with --delete', + async () => { + const ctx = setupJourney({ + connectionString: db.connectionString, + createTempDir, + contractMode: 'psl', + }); + await emit(ctx, EXPLICIT); + const init = await runDbInit(ctx); + expect(init.exitCode, output(init)).toBe(0); + + await emit(ctx, CODEC_ONLY); + const codecOnly = await runDbUpdate(ctx); + expect(codecOnly.exitCode, output(codecOnly)).toBe(0); + expect(output(codecOnly)).toContain('(no operations)'); + await expectVerified(ctx); + + await emit(ctx, PRECISION); + const unconsented = await runDbUpdate(ctx); + expect(unconsented.exitCode).toBe(2); + expect(output(unconsented)).toContain('Run the command again with --delete Event.at'); + const precision = await runDbUpdate(ctx, ['--delete', 'Event.at']); + expect(precision.exitCode, output(precision)).toBe(0); + expect(output(precision)).toContain('Alter type of "event"."at" to timestamptz(3)'); + await expectVerified(ctx); + }, + timeouts.spinUpPpgDev * 3, + ); + }); + }); +}); diff --git a/upgrade-instructions/pending/convenience-date-time-types/app/instructions.md b/upgrade-instructions/pending/convenience-date-time-types/app/instructions.md index b62dba2fd139..88fc7912faf9 100644 --- a/upgrade-instructions/pending/convenience-date-time-types/app/instructions.md +++ b/upgrade-instructions/pending/convenience-date-time-types/app/instructions.md @@ -52,7 +52,12 @@ The detection cannot tell which target a schema is for, so it also matches SQLit A type alias written with one of the names, such as `types { Stamp = DateTime @onCreate(now()) }`, is renamed the same way. Rename only field types and alias types in Prisma 8 schemas whose config uses the PostgreSQL target, including the `contract.prisma` copies in migration folders; leave a model or field named `Date`, comments, and Prisma 7 schemas as they are. Re-emit the contract with `prisma contract emit`: it is byte for byte the same as before. -To move a field to the new defaults instead, keep the name and update the application code that reads the field, then re-emit, plan and apply a migration: a `DateTime` column becomes `timestamptz(3)`, so the migration changes its precision. +To move a field to the new defaults instead, keep the name, update the application code that reads the field, and run `prisma contract emit`. What the database needs then depends on the type: + +- **`Date` and `Time`** keep their `date` and `time(p)` columns, when a precision is written by name (`Time(precision: 0)` for `Time(0)`), and change only the codec, so the database does not change, but the contract hash does. + - With migrations: `prisma migration plan` refuses, because it finds nothing to change, and names the command to run instead. Run `prisma migration new --from ` with the hash it gives, which writes a migration with no operations, then `prisma db migrate`. + - With `prisma db update`: run it. It applies no operations and records the new contract on the database. +- **`DateTime`** makes the column `timestamptz(3)`, and a schema that wrote `DateTime` before had a `timestamptz` column, which holds microseconds. Lowering the precision rounds stored values to milliseconds, so both commands ask before they change it. Run `prisma migration plan --delete .` and then `prisma db migrate`, or `prisma db update --delete .`, which accepts the rounding. A JavaScript `Date` holds milliseconds only, so to keep microseconds, keep the `Temporal.Instant` field: write `DateTime(value: temporal)`, which is exactly `TimestamptzTemporal`. ## `ts-date-time-helpers-are-convenience-types` From b4d163f52271aad5883c4c603b2ce69362e04546 Mon Sep 17 00:00:00 2001 From: willbot Date: Fri, 23 Oct 2026 07:37:40 +0200 Subject: [PATCH 066/118] TML-3709: Slice 1: the target declares DateStyle=ISO and the driver ensures it on every connection Co-Authored-By: Claude Opus 5.5 Signed-off-by: willbot Signed-off-by: Will Madden --- .../datetime-convenience-types/slices/01-iso-strings/spec.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/projects/datetime-convenience-types/slices/01-iso-strings/spec.md b/projects/datetime-convenience-types/slices/01-iso-strings/spec.md index b8f86cc9c5c3..1abc7d6f62e4 100644 --- a/projects/datetime-convenience-types/slices/01-iso-strings/spec.md +++ b/projects/datetime-convenience-types/slices/01-iso-strings/spec.md @@ -18,7 +18,7 @@ Every string codec's application value is the stored form ADR 254 defines for it ## Chosen design 1. **The codecs read with the stored-form readers.** `fromWire` turns PostgreSQL's ISO-DateStyle text into the stored form with the existing readers in `packages/3-targets/3-targets/postgres/src/core/data-types.ts` (`pgDateStoredText`, `pgTimeStoredText`, `pgTimetzStoredText`, `pgTimestampStoredText`, `pgTimestamptzStoredText`), which already convert any time zone offset to UTC for `timestamptz`, ` BC` years and long years to ISO, and keep `infinity`. `pg/timestamptz-date@1` already reads wire text this way. `toWire` keeps converting the stored ISO year into PostgreSQL's year text (`postgresYearText`). `fromDataTypeValue` is the stored value; `toDataTypeValue` requires the stored form. -2. **`DateStyle=ISO` is a connection requirement.** Prisma sets it on every connection it opens, as a connection startup option (`options=-c DateStyle=ISO`), which survives the Supabase role scope's `RESET ALL`. A connection the user supplies (`pgPool`, `pgClient`) is not changed. A value that is not in ISO DateStyle is refused when read, with an error that names the setting and the fix. No `TimeZone` setting is needed. +2. **`DateStyle=ISO` is a connection requirement the target declares and the driver ensures** (operator ruling, 2026-10-23). The PostgreSQL target states, as data on its descriptor, the session settings its codecs need (`DateStyle=ISO`). The PostgreSQL driver ensures every connection it hands to the runtime satisfies them, whatever its binding: a URL, a `pgPool` or a `pgClient` the user supplies, and the Supabase runtime's connections after its `RESET ALL`. How the driver ensures it (a startup option, a setting applied when a connection is first acquired or after a reset, or both) is the driver's own implementation detail; the codecs never depend on how. The codecs still refuse non-ISO text with `RUNTIME.DATE_STYLE_NOT_ISO`, which now means the driver could not ensure the requirement. No `TimeZone` setting is needed: the `timestamptz` codecs convert any offset to UTC and `timetz` keeps its offset. 3. **The `now()` generators for these codecs write the stored form** (`timestampStringNow`, `timestamptzStringNow`, `dateStringNow`, `timeStringNow`, `timetzNow` in `date-time-now-generators.ts`), so a generated value reads back as written. 4. **Codec ids, the contract and stored defaults do not change.** It is a bug fix; nothing keeps the old format before RC. 5. **Values PostgreSQL holds beyond the stored form's range** (time `24:00:00`, dates after `+275760-09-13`, seven-digit years) are read as ISO-shaped text, not refused: the string types exist to carry values the other codecs cannot. The implementer documents the exact text for each. From 3101f0446fdbb0e680c87e67c7a27659228ffa1c Mon Sep 17 00:00:00 2001 From: willbot Date: Fri, 23 Oct 2026 07:39:25 +0200 Subject: [PATCH 067/118] TML-3709: Slice 5 spec: infer takes the default path where it can Co-Authored-By: Claude Opus 5.5 Signed-off-by: willbot Signed-off-by: Will Madden --- .../05-tools-write-convenience-types/spec.md | 76 +++++++++++++++++++ 1 file changed, 76 insertions(+) create mode 100644 projects/datetime-convenience-types/slices/05-tools-write-convenience-types/spec.md diff --git a/projects/datetime-convenience-types/slices/05-tools-write-convenience-types/spec.md b/projects/datetime-convenience-types/slices/05-tools-write-convenience-types/spec.md new file mode 100644 index 000000000000..70bed8dd6269 --- /dev/null +++ b/projects/datetime-convenience-types/slices/05-tools-write-convenience-types/spec.md @@ -0,0 +1,76 @@ +# Slice 5 — `contract infer`, `contract print`, the Prisma 7 reader and `orm init` write the convenience types + +**Linear:** [TML-3713](https://linear.app/prisma-company/issue/TML-3713) · **Parent:** [project spec](../../spec.md), decision 10 · **Design record:** ADR 279, items 8 and 10 · **Builds on:** slice 4 (the type choices) and slice 2 (the Prisma 7 `now()` mapping) + +## At a glance + +A database the user adopts with `contract infer`: + +```sql +CREATE TABLE event ( + at timestamptz, -- no declared precision + paid_at timestamptz(3), + legacy timestamp(3), + day date, + opens time(0), + local_at timetz, + span interval +); +``` + +```prisma +model Event { + at DateTime(precision: 6) // TimestamptzJsDate: a JavaScript Date + paidAt DateTime // TimestamptzJsDate(3) + legacy DateTime(column: timestamp) // TimestampJsDate(3) + day Date // DateString + opens Time(precision: 0) // TimeString(0) + localAt Time(column: timetz) // Timetz + span Duration // IntervalString +} +``` + +`contract print` writes the same forms for the same codecs; a `timestamp` column read as `Temporal.PlainDateTime` (`TimestampTemporal`), which no convenience type reaches, prints as its explicit type. + +A Prisma 7 schema read by `prisma7Schema(...)`: `DateTime` becomes `TimestampJsDate(3)` (prints `DateTime(column: timestamp)`), `@db.Timestamptz(p)` becomes `TimestamptzJsDate(p)`, `@db.Date` `DateString`, `@db.Time(p)` `TimeString(p)`, `@db.Timetz(p)` `Timetz(p)`. + +`prisma orm init` writes `createdAt DateTime @default(now()) @onCreate(now())` and `updatedAt temporal.UpdatedAt`. + +## Chosen design + +1. **Infer always takes the default path where it can** (operator ruling, 2026-10-23: "infer should always choose the default path, where possible, since that's the simplest to see in your contract, as a user, and ought to represent the well-trodden path"). For each date and time column, infer picks the codec of the convenience type's default value for that column type, and writes the convenience call with only the arguments that differ from its defaults. The explicit type is written only where no convenience call reproduces the column. + - Consequence the docs state: a `Date` holds milliseconds, so a `timestamptz` or `timestamp` column with more than 3 digits of precision reads with the extra digits dropped, and `infinity` is refused on read. The upgrade and infer docs say to write `DateTime(value: temporal)` or `DateTime(value: string)` to keep them. +2. **An omitted precision equals precision 6** on `pg/timestamptz`, `pg/timestamp`, `pg/time`, `pg/timetz` and `pg/interval` when columns are compared (the data type's normal form, ADR 254), because PostgreSQL stores microseconds either way. That lets infer write `DateTime(precision: 6)` for a column with no declared precision without `db verify` or the planner seeing a difference. The contract keeps parameters as written; stored contracts and hashes do not change. If this normal-form change would change any existing contract, hash or plan in the repository's fixtures, stop and report. +3. **The `inferred` marks move to the default explicit types:** `pg/timestamptz` → `TimestamptzJsDate`, `pg/timestamp` → `TimestampJsDate`, `pg/interval` → `IntervalString`; `DateString`, `TimeString`, `Timetz` keep theirs. Both infer and the Prisma 7 reader follow the marks, which gives the Prisma 7 table above. The guard test that forbids Temporal codecs in these maps keeps passing and gains `pg/timestamp-instant@1`. +4. **Print and infer write convenience calls through one framework helper** beside `resolveAuthoringTypeChoice`: given an explicit constructor call, find the type choice row (at the same namespace level) that names that constructor, build named arguments from the row (options from its `when`, passed-on arguments by name), drop arguments equal to the defaults, re-resolve to prove it reproduces the same codec and parameters, and otherwise return nothing so the caller writes the explicit call. Arguments are always written by name (positional `DateTime(6)` would land in `value`). +5. **The Prisma 7 reader's generators follow the codec**: `@updatedAt` and slice 2's `@onCreate(now())` resolve `timestampNow` for the `Date` codecs. +6. **`temporal.CreatedAt` and `temporal.UpdatedAt`** are rebuilt on `TimestamptzJsDate(3)` (exactly what bare `DateTime` resolves to, pinned by a test comparing them), with `@onCreate(now())`, and `@onUpdate(now())` for the second; the `String` and `JsDate` variants are removed. A contributed alias stays a literal type, so `type.temporal.CreatedAt()` keeps a precise codec type. +7. **`orm init`** writes the convenience forms in both its PSL and TypeScript templates. +8. **Upgrade instructions:** adopted projects re-emit (the reader and infer pick new codecs; the storage hash changes) and run `prisma db sign`; code that read strings from those columns now reads `Date`. Projects using the removed alias variants rewrite them. Slice 2's upgrade instruction (`prisma7-now-default-on-create`), which names `TimestampString(3)`, is updated. + +## Scope + +**In:** the framework helper, print and infer (`psl-print/column-types.ts`, `psl-build/column-types.ts`, `infer-model-blocks.ts`, `infer-default-codec.ts`, `inferred-type-constructors.ts`), the data types' normal form, the marks, the Prisma 7 reader, the aliases, `orm init` templates, `examples/prisma7-adoption` (contract, snapshot, `src/main.ts`, README, tests), every fixture and test pinning today's output, docs (`scalar-types.md`, `gotchas.md`, the contract-prisma7 and contract-ts READMEs, skills), ADR 279 items 8 and 10 marked built, ADR 275's alias line. + +**Out:** SQLite, MySQL and MongoDB (TML-3714); regenerating the Supabase pack contract (TML-3417). + +## Pre-investigated edge cases + +| Case | Disposition | +| --- | --- | +| `timestamptz` with no declared precision | `DateTime(precision: 6)`, by decision 2 | +| `timestamp` column read as `Temporal.PlainDateTime` | Explicit `TimestampTemporal` | +| An inferred `infinity` default on a `timestamptz` column | The `Date` codec cannot read it; infer prints it as a raw `sql` default and says so (the existing fallback), pinned by `infer-roundtrip-fidelity.prisma7-defaults.e2e.test.ts` | +| `time` with no precision | `Time` (string default; omitted precision equals 6 by decision 2) | +| `examples/prisma7-adoption` reading `updatedAt.replace(' ', 'T')` | Code updated to read a `Date` | +| Two type choices naming one constructor | The helper takes the first in namespace order; no PostgreSQL constructor is named twice | + +## Slice-specific done conditions + +- `contract infer` on the at-a-glance table writes exactly the at-a-glance schema, which emits, verifies clean against the same database with `db verify`, and plans no migration; a Prisma 7 adoption journey reads `Date` values for its `DateTime` columns. + +## Dispatch plan + +1. **The helper, the normal form and the marks** — outcome: print and infer write convenience calls for every reachable codec, the normal form treats omitted precision as 6, marks moved; unit and round-trip print/infer tests. Gates: framework-components, family-sql, target-postgres package tests; the psl-print and psl-infer integration tests; typecheck; lint; `pnpm lint:deps`; `pnpm fixtures:check`. +2. **The Prisma 7 reader, the aliases, `orm init` and the example** — builds on 1. Outcome: the at-a-glance Prisma 7 table, the aliases, the init templates, the adoption example and journeys. Gates: contract-prisma7 and CLI package tests; the Prisma 7 and infer journeys; `examples/prisma7-adoption` tests; `pnpm fixtures:check`; `pnpm check:upgrade-coverage --mode pr`. +3. **Docs and upgrade instructions** — builds on 2. Gates: `pnpm lint:docs`; `pnpm check:upgrade-coverage --mode pr`. From db8e4d6c36cff9cc331316d7bb6d81c786ad5796 Mon Sep 17 00:00:00 2001 From: willbot Date: Fri, 23 Oct 2026 07:45:08 +0200 Subject: [PATCH 068/118] ADR 279 rewritten for a reader without context: grounding example first, a five-point decision, a narrative that builds the explicit layer before the type choice, and no transient state Co-Authored-By: Claude Opus 5.5 Signed-off-by: willbot Signed-off-by: Will Madden --- ...type alias is expanded into each column.md | 2 +- ...e an explicit type from their arguments.md | 261 +++++++++++------- 2 files changed, 161 insertions(+), 102 deletions(-) diff --git a/docs/architecture docs/adrs/ADR 275 - Client generators are field attributes, and a type alias is expanded into each column.md b/docs/architecture docs/adrs/ADR 275 - Client generators are field attributes, and a type alias is expanded into each column.md index ddc29e4c8e29..4ad2527a0eb3 100644 --- a/docs/architecture docs/adrs/ADR 275 - Client generators are field attributes, and a type alias is expanded into each column.md +++ b/docs/architecture docs/adrs/ADR 275 - Client generators are field attributes, and a type alias is expanded into each column.md @@ -5,7 +5,7 @@ **Builds on:** [ADR 158 — Execution mutation defaults](ADR%20158%20-%20Execution%20mutation%20defaults.md), [ADR 241 — Scalar types use the authoring type-constructor channel](ADR%20241%20-%20Scalar%20types%20use%20the%20authoring%20type-constructor%20channel.md), [ADR 269 — PSL mixins](ADR%20269%20-%20PSL%20mixins.md) **Supersedes:** the field-preset half of [ADR 170 — Pack-provided type constructors and field presets](ADR%20170%20-%20Pack-provided%20type%20constructors%20and%20field%20presets.md); [ADR 246 — Option arguments and select templates for authoring helpers](ADR%20246%20-%20Option%20arguments%20and%20select%20templates%20for%20authoring%20helpers.md), except its two omission rules **Amends:** [ADR 104](ADR%20104%20-%20PSL%20extension%20namespacing%20%26%20syntax.md), [ADR 158](ADR%20158%20-%20Execution%20mutation%20defaults.md), [ADR 169 — Declared applicability for mutation default generators](ADR%20169%20-%20Declared%20applicability%20for%20mutation%20default%20generators.md), [ADR 208 — Higher-order codecs for parameterized types](ADR%20208%20-%20Higher-order%20codecs%20for%20parameterized%20types.md), [ADR 212 — AST-bound codec resolution](ADR%20212%20-%20AST-bound%20codec%20resolution.md), [ADR 231](ADR%20231%20-%20Declarative%20attribute%20specifications.md), [ADR 241](ADR%20241%20-%20Scalar%20types%20use%20the%20authoring%20type-constructor%20channel.md), [ADR 249](ADR%20249%20-%20Central%20attribute-spec%20registry.md), [ADR 254](ADR%20254%20-%20Data%20types%20and%20casts.md) -**Amended by [ADR 279](ADR%20279%20-%20Convenience%20date%20and%20time%20types%20choose%20an%20explicit%20type%20from%20their%20arguments.md) (2026-10-22):** the contributed `temporal.*` aliases are to be built on the convenience type `DateTime`, and their `String` and `JsDate` variants are not needed, because `now()` picks its generator from the field's final codec and a user writes `DateTime(value: string) @onCreate(now())` for another kind of value. Until that change is made, the PostgreSQL aliases remain on `TimestamptzTemporal`, with the variants. +**Amended by [ADR 279](ADR%20279%20-%20Convenience%20date%20and%20time%20types%20choose%20an%20explicit%20type%20from%20their%20arguments.md) (2026-10-22):** the contributed `temporal.*` aliases are built on the convenience type `DateTime`, and their `String` and `JsDate` variants are not needed, because `now()` picks its generator from the field's final codec and a user writes `DateTime(value: string) @onCreate(now())` for another kind of value. **Amended 2026-10-21:** the TypeScript `types` key is renamed `databaseTypes` and registers database types only; a contributed alias is used as `field.column(type.temporal.UpdatedAt())`; a contributed alias carries no database default; Mongo's contributed aliases are on `Date`; the Mongo TypeScript builder gets no contributed aliases yet. Each change is marked "Amended 2026-10-21" below. --- diff --git a/docs/architecture docs/adrs/ADR 279 - Convenience date and time types choose an explicit type from their arguments.md b/docs/architecture docs/adrs/ADR 279 - Convenience date and time types choose an explicit type from their arguments.md index 69b51affff32..d43a7b837f60 100644 --- a/docs/architecture docs/adrs/ADR 279 - Convenience date and time types choose an explicit type from their arguments.md +++ b/docs/architecture docs/adrs/ADR 279 - Convenience date and time types choose an explicit type from their arguments.md @@ -1,88 +1,61 @@ # ADR 279 — Convenience date and time types choose an explicit type from their arguments -**Status:** Accepted. Implemented: the type choice, the four PostgreSQL convenience types, and every reader of a field's codec. Not yet implemented: items 8 and 10, what the Prisma 7 reader, `contract infer`, `contract print` and `prisma orm init` write, and the `temporal.*` aliases built on `DateTime` ("Created-at and updated-at"); until then those write and use the explicit types. -**Builds on:** [ADR 241 — Scalar types use the authoring type-constructor channel](ADR%20241%20-%20Scalar%20types%20use%20the%20authoring%20type-constructor%20channel.md) -**Amends:** [ADR 254 — Data types and casts](ADR%20254%20-%20Data%20types%20and%20casts.md), [ADR 257 — Scalar types are named after the target on every surface](ADR%20257%20-%20Scalar%20types%20are%20named%20after%20the%20target%20on%20every%20surface.md), [ADR 275 — Client generators are field attributes, and a type alias is expanded into each column](ADR%20275%20-%20Client%20generators%20are%20field%20attributes%2C%20and%20a%20type%20alias%20is%20expanded%20into%20each%20column.md) +**Status:** Accepted +**Builds on:** [ADR 241 — Scalar types use the authoring type-constructor channel](ADR%20241%20-%20Scalar%20types%20use%20the%20authoring%20type-constructor%20channel.md), [ADR 254 — Data types and casts](ADR%20254%20-%20Data%20types%20and%20casts.md), [ADR 257 — Scalar types are named after the target on every surface](ADR%20257%20-%20Scalar%20types%20are%20named%20after%20the%20target%20on%20every%20surface.md) +**Amends:** [ADR 275 — Client generators are field attributes, and a type alias is expanded into each column](ADR%20275%20-%20Client%20generators%20are%20field%20attributes%2C%20and%20a%20type%20alias%20is%20expanded%20into%20each%20column.md) ## At a glance -On PostgreSQL today, each date and time type name fixes one column type and one kind of value that the application reads: `Timestamptz` is a `timestamptz` column read as a `Temporal.Instant`, `TimestamptzString` is the same column read as a string, and so on. This decision calls those the **explicit types**, and keeps them. - -It adds four **convenience types**, named for what a field means in the application: `DateTime` for a moment in time, `Date` for a calendar date, `Time` for a time of day, and `Duration` for a length of time. Each convenience type becomes one explicit type. Two optional arguments say which: `value`, what the application reads, and `column`, the PostgreSQL column type. - ```prisma model Order { - id Int @id - paidAt DateTime - heldAt DateTime(value: temporal, column: timestamptz, precision: 3) - shipBy Date(value: string) - opensAt Time(value: temporal, precision: 0) + id Int @id + paidAt DateTime // timestamptz(3), read as a JavaScript Date + shipBy Date // date, read as "2024-02-29" + opensAt Time // time, read as "09:30:00" + period Duration // interval, read as "P1M" + settledAt DateTime(value: temporal) // timestamptz, read as a Temporal.Instant + legacyAt DateTime(column: timestamp) // timestamp(3), read as a Date stored as UTC + exactAt TimestamptzTemporal(6) // the explicit type, written directly } ``` -| Field | Becomes the explicit type | Column | The application reads | -| --- | --- | --- | --- | -| `paidAt` | `TimestamptzJsDate(3)` | `timestamptz(3)` | a JavaScript `Date` | -| `heldAt` | `TimestamptzTemporal(3)` | `timestamptz(3)` | `Temporal.Instant` | -| `shipBy` | `DateString` | `date` | a string, such as `"2024-02-29"` | -| `opensAt` | `TimeTemporal(0)` | `time(0)` | `Temporal.PlainTime` | - -The contract stores what the explicit type stores: the codec, its data type and its parameters. `heldAt` is stored exactly as if the user had written `heldAt TimestamptzTemporal(3)`, and nothing in the contract records that a convenience type was written. +`DateTime`, `Date`, `Time` and `Duration` are **convenience types**. Each is named for what a field means in the application, and each becomes exactly one **explicit type**, the database-named type that fixes the column type and the value the application reads. Written bare, a convenience type takes defaults that work on any JavaScript runtime with no setup. Its optional arguments choose any other explicit type. The contract records only the explicit type: `settledAt` is stored exactly as `settledAt TimestamptzTemporal` would be. ## Decision -1. **Date and time types have two layers.** Each explicit type uses one codec, so it fixes one column type and one application value, and is named after the database as ADR 257 says. Each convenience type is named for what a field means in the application, and becomes exactly one explicit type. -2. **A convenience type's arguments choose its explicit type.** `value` says what the application reads: a JavaScript `Date`, a `Temporal` object or a string. `column` says the PostgreSQL column type. Each has a default that the convenience type declares. Any other argument, such as `precision`, is passed on to the chosen explicit type. -3. **A convenience type is a type choice: a type declaration whose arguments name the explicit type it becomes.** It declares its option arguments, each with a fixed set of values written as bare names and a default, the arguments it passes on (such as `precision`), and a table from each allowed combination of option values to the name of an explicit type constructor, with any parameter defaults for that combination. An unlisted combination is refused. -4. **The contract stores only the explicit type.** The contract format, the codecs, the data types, the storage hash and the execution hash are the same whether a schema writes the convenience type or the explicit type it becomes. -5. **The PostgreSQL target provides four convenience types:** - - | Convenience type | `value` choices | `column` choices | Defaults | - | --- | --- | --- | --- | - | `DateTime` | `jsDate` (a JavaScript `Date`), `temporal` (`Temporal.Instant`), `string` | `timestamptz`, `timestamp` | `jsDate`, `timestamptz`; precision 3 with `jsDate`, PostgreSQL's default otherwise | - | `Date` | `string`, `temporal` (`Temporal.PlainDate`) | `date` | `string` | - | `Time` | `string`, `temporal` (`Temporal.PlainTime`) | `time`; `timetz` is allowed only with `value: string` | `string`, `time` | - | `Duration` | `string` (an ISO 8601 duration such as `P1Y2M`), `parts` (the object `{ months, days, micros }`) | `interval` | `string` | - - The value names repeat the suffixes of the explicit types, so `DateTime(value: jsDate)` and `TimestamptzJsDate` read as the same thing. No default needs a global `Temporal`, so every default works on a runtime without one. A JavaScript `Date` is the default only for a moment in time; a calendar date, a time of day and a length of time default to plain ISO 8601 strings, because JavaScript has no built-in value for them and a `Date` there stores the wrong day. - -6. **Every explicit type for a column that has several codecs carries a suffix naming its kind of value.** The explicit types that read `Temporal` values gain the suffix `Temporal`, next to the existing `String` and `JsDate` suffixes: - - | Column | `Temporal` value | Explicit name today | Explicit name after | - | --- | --- | --- | --- | - | `timestamptz` | `Temporal.Instant` | `Timestamptz`, and `DateTime` as a second name | `TimestamptzTemporal` | - | `timestamp` | `Temporal.PlainDateTime` | `Timestamp` | `TimestampTemporal` | - | `date` | `Temporal.PlainDate` | `Date` | `DateTemporal` | - | `time` | `Temporal.PlainTime` | `Time` | `TimeTemporal` | +1. **Date and time types have two layers.** The explicit types are named after the database, as ADR 257 requires. Each one uses a single codec, so it fixes both the column type and the value the application reads. The convenience types are named after the application concept, and each one becomes exactly one explicit type. +2. **A convenience type is a type choice.** It is a declaration with: + - option arguments, each with a fixed set of values and a default; + - arguments it passes on to the chosen type, such as `precision`; + - a table from each allowed combination of option values to the name of an explicit type. - This frees `DateTime`, `Date` and `Time` for the convenience types. Without the suffix, a name such as `Timestamptz` would look like a neutral default when it is one specific kind of value. For the same reason, `Interval`, which reads the object `{ months, days, micros }`, becomes `IntervalParts`, next to the new `IntervalString`. + A combination the table does not list is refused. +3. **The contract stores only the explicit type.** A schema written with convenience types and the same schema written with the explicit types they become give the same contract and the same hashes. +4. **The defaults need no setup.** On PostgreSQL: + - `DateTime` defaults to a `timestamptz(3)` column read as a JavaScript `Date`. + - `Date`, `Time` and `Duration` default to ISO 8601 strings. -7. **Three codecs are added:** a `timestamp` column read as a JavaScript `Date`, and a `timestamp` column read as a `Temporal.Instant`, both reading and writing the stored value as UTC; and an `interval` column read as an ISO 8601 duration string. Each gets an explicit type: `TimestampJsDate(p)` for the `Date` codec, `TimestampInstant(p)` for the `Temporal.Instant` codec, and `IntervalString(p)`. `IntervalString` reads interval text in every `IntervalStyle` PostgreSQL prints (`postgres`, `postgres_verbose`, `sql_standard`, `iso_8601`) and returns the ISO 8601 duration ADR 254 stores, so it needs no `IntervalStyle` setting. `DateTime(column: timestamp)` with `value: jsDate` becomes `TimestampJsDate`, with `value: temporal` becomes `TimestampInstant`, and `Duration` with `value: string` becomes `IntervalString`. `TimestampInstant` names its `Temporal` type rather than taking the `Temporal` suffix, because the `timestamp` column has two `Temporal` codecs and `TimestampTemporal` is the one that reads `Temporal.PlainDateTime`. -8. **The Prisma 7 reader maps Prisma 7's `DateTime` to `TimestampJsDate(3)`,** which `contract print` writes as `DateTime(column: timestamp)`: a `timestamp(3)` column read as a JavaScript `Date`, instead of a string. The column and its stored values do not change; only the application value goes from a string back to the `Date` that Prisma 7 gave, so an upgraded application keeps the values its code was written for. `prisma orm init`, which creates new columns, writes plain `DateTime`, a `timestamptz(3)` column read as a `Date`. -9. **A string value is ISO 8601, the same on every server.** Every string codec of a date or time type returns the form its data type stores (see ADR 254, "Date and time types") and reads that form back unchanged. PostgreSQL's display text, such as `2024-01-01 00:00:00+00` or a year written with BC, never reaches the application. The `timestamptz` codec converts any session time zone offset to UTC itself; the `timetz` codec keeps the offset the value was stored with, because that offset is part of a `timetz` value. Prisma requires `DateStyle=ISO` on its database connections: it sets it on the connections it opens, and refuses a value it reads in another style with an error naming the setting. The codec ids do not change; the old text was never a format an application could rely on. -10. **`contract infer` and `contract print` write convenience types,** with arguments only where they differ from the defaults. A column no convenience type covers, a `timestamp` read as `Temporal.PlainDateTime`, is written as its explicit type. `prisma orm init` writes `DateTime`. -11. **A convenience name means the same thing on every target.** This decision covers PostgreSQL. Another target adds convenience types under the same names, and renames any explicit type that already uses one of them for something else. + No default needs a global `Temporal`. +5. **The tools write the convenience types.** `contract infer`, `contract print`, the Prisma 7 schema reader and `prisma orm init` write each date or time column as the convenience call that becomes its explicit type. They write only the arguments that differ from the defaults. An explicit type is written only for a column that no convenience call reaches. ## Why -Prisma 8 gives users explicit, predictable controls first, then makes common cases easy by building convenience APIs on top of them. For example, the `postgres({ contract, url })` function builds the runtime, adapter and driver that a user could otherwise create and connect by hand. The date and time types have the explicit controls but nothing built on top of them. +In Prisma 7, every date or time field is `DateTime`, and the application always reads a JavaScript `Date`. A `@db.*` attribute changes the column type and nothing else. That is easy to learn. But a `Date` is a moment in time, so it is the wrong value for a calendar date or a time of day. A birthday created at local midnight on a machine east of UTC is stored as the day before. -In Prisma 7, every date or time field is `DateTime`, and the application always reads a JavaScript `Date`. A `@db.*` attribute changes the column type. A user who never learns those attributes still gets a working field. That was simple, but Prisma made the choice for the user, and for some fields it was wrong. A `Date` is a moment in time, not a calendar date: a birthday created at local midnight on a machine east of UTC is stored as the day before. +Prisma 8's explicit types make each choice visible instead. A `date` column can be read as a `Temporal.PlainDate` (`DateTemporal`) or as a string (`DateString`), and each is correct for what it is. The cost is that every name fixes two things at once: the column type and the kind of value. So a column type with three kinds of value has three names, and across PostgreSQL's date and time column types there are more than a dozen. Nothing says which one to start with. To choose, a user must already know the difference between `timestamp` and `timestamptz`, what each Temporal type means, and what each kind of value costs. A user writing a model thinks "this field is when the order was paid", not "this is a `timestamptz` column read as a `Temporal.Instant`". -Prisma 8's explicit types fix that by making every choice visible. But each name fixes both the column type and the kind of value, so PostgreSQL has eleven date and time type names (`Timestamptz`, `TimestamptzJsDate`, `TimestamptzString`, `Timestamp`, `TimestampString`, `Date`, `DateString`, `Time`, `TimeString`, `Timetz`, and `DateTime` as a second name for `Timestamptz`). Prisma 8 does not say which one a new user should pick. To choose, a user must already know the difference between `timestamp` and `timestamptz`, what each `Temporal` type means, and how each kind of value behaves. A user writing a schema thinks "this field is when the order was paid", not "this is a `timestamptz` column read as a `Temporal.Instant`". - -The convenience types give that user one name per concept, with defaults that work. Every explicit choice stays available, as an argument or as the explicit type itself. +Prisma 8's approach is to give users explicit, predictable controls first, then make common cases easy by building convenience APIs on top. The client facades are an example. `postgres({ contract, url })` builds the runtime, adapter and driver that a user could otherwise create and connect by hand. The convenience types do the same for dates and times. There is one name per concept, with a default that works, and every explicit choice stays available as an argument or as the explicit type itself. ## How it works -### A type choice names its explicit types +### The explicit layer -An explicit type is a type constructor: it names one codec, and maps its arguments onto that codec's parameters. +An explicit type is a type constructor. It names one codec and maps its arguments onto that codec's parameters: ```ts TimestamptzTemporal: { kind: 'typeConstructor', - args: [{ kind: 'value', dataType: 'pg/int4', name: 'precision', optional: true }], + args: [{ kind: 'value', dataType: PG_SIZE_TYPE, name: 'precision', optional: true }], output: { codecId: 'pg/timestamptz-temporal@1', typeParams: { precision: { kind: 'arg', index: 0 } }, @@ -90,7 +63,22 @@ TimestamptzTemporal: { }, ``` -A convenience type is a different kind of declaration in the same namespace, a type choice. It names no codec. It declares its option arguments, each with its values and a default, the value arguments it passes on by name, and a table whose rows each name an explicit type constructor of the same namespace: +When a column type has more than one codec, every explicit type for it carries a suffix naming its kind of value. No name looks like the neutral default: + +| Column | `Date` | `Temporal` | String | Other | +| --- | --- | --- | --- | --- | +| `timestamptz` | `TimestamptzJsDate` | `TimestamptzTemporal` (`Temporal.Instant`) | `TimestamptzString` | | +| `timestamp` | `TimestampJsDate` | `TimestampTemporal` (`Temporal.PlainDateTime`), `TimestampInstant` (`Temporal.Instant`) | `TimestampString` | | +| `date` | | `DateTemporal` (`Temporal.PlainDate`) | `DateString` | | +| `time` | | `TimeTemporal` (`Temporal.PlainTime`) | `TimeString` | | +| `timetz` | | | `Timetz` | | +| `interval` | | | `IntervalString` (an ISO 8601 duration) | `IntervalParts` (`{ months, days, micros }`) | + +`TimestampJsDate` and `TimestampInstant` read and write a `timestamp` column as UTC. This lets a `timestamp` column, the column Prisma 7's `DateTime` creates, hold the same moment-in-time value as a `timestamptz` column. `TimestampInstant` names its Temporal type instead of taking the `Temporal` suffix, because the `timestamp` column has two Temporal codecs. + +### A type choice names its explicit types + +A convenience type is a second kind of declaration in the same namespace, a type choice. It names no codec. It declares its arguments and a table whose rows each name an explicit type: ```ts DateTime: { @@ -98,85 +86,156 @@ DateTime: { args: [ { kind: 'option', name: 'value', values: ['jsDate', 'temporal', 'string'], default: 'jsDate', optional: true }, { kind: 'option', name: 'column', values: ['timestamptz', 'timestamp'], default: 'timestamptz', optional: true }, - { kind: 'value', name: 'precision', dataType: 'pg/int4', optional: true }, + { kind: 'value', dataType: PG_SIZE_TYPE, name: 'precision', optional: true }, ], choices: [ { when: { value: 'jsDate', column: 'timestamptz' }, type: 'TimestamptzJsDate', defaults: { precision: 3 } }, { when: { value: 'temporal', column: 'timestamptz' }, type: 'TimestamptzTemporal' }, - // …one row for each combination below + { when: { value: 'string', column: 'timestamptz' }, type: 'TimestamptzString' }, + { when: { value: 'jsDate', column: 'timestamp' }, type: 'TimestampJsDate', defaults: { precision: 3 } }, + { when: { value: 'temporal', column: 'timestamp' }, type: 'TimestampInstant' }, + { when: { value: 'string', column: 'timestamp' }, type: 'TimestampString' }, ], }, ``` -As a table: +The value names repeat the explicit types' suffixes, so `DateTime(value: jsDate)` and `TimestamptzJsDate` read as the same thing. The PostgreSQL target declares four type choices: -| `value` | `column` | Explicit type | Parameter defaults | +| Convenience type | `value` | `column` | Default | | --- | --- | --- | --- | -| `jsDate` (default) | `timestamptz` (default) | `TimestamptzJsDate` | precision 3 | -| `temporal` | `timestamptz` | `TimestamptzTemporal` | | -| `string` | `timestamptz` | `TimestamptzString` | | -| `jsDate` | `timestamp` | `TimestampJsDate` | precision 3 | -| `temporal` | `timestamp` | `TimestampInstant` | | -| `string` | `timestamp` | `TimestampString` | | +| `DateTime` | `jsDate`, `temporal`, `string` | `timestamptz`, `timestamp` | `jsDate`, `timestamptz`, precision 3 | +| `Date` | `string`, `temporal` | `date` | `string` | +| `Time` | `string`, `temporal` | `time`, `timetz` (with `string` only) | `string`, `time` | +| `Duration` | `string`, `parts` | `interval` | `string` | + +Resolving a call takes four steps: +1. Fill in the default for every option the call leaves out. +2. Look up the table row for the resulting combination of values. +3. Instantiate the explicit type that row names, passing on the other arguments. A row default stands in for an argument the call leaves out. +4. The result is exactly what writing that explicit type would give. -That is `DateTime`'s table; the other three are declared the same way. Resolving a call fills each option left out with its default, looks up the row for the option values, then instantiates the named explicit constructor with the passed-on arguments, a row default standing in for one left out, so the result is exactly what writing the explicit type gives. A combination with no row, such as `Time(value: temporal, column: timetz)`, is refused at the argument that makes it invalid, and the message lists the values that argument accepts with the others as written; a value an option does not list, such as `Date(value: jsDate)`, is refused the same way. A passed-on argument the chosen constructor does not take is refused at that argument. Arguments are written by name or in declaration order, so `Time(3)` reads `3` as the `value` option and is refused. Every argument is optional, so a type choice is also written bare, as its default row. Assembly refuses a table that names a constructor no component registers beside it, a combination or default an option does not list, two rows for one combination, defaults with no row, a row default for an argument the choice does not pass on or the chosen constructor does not take, and a passed-on argument whose data type differs from the chosen constructor's. +Every argument is optional, so a bare `DateTime` is its default row. Arguments are written by name or in declaration order. Because `value` comes first, `Time(3)` reads `3` as the `value` option, and the refusal says to write `Time(precision: 3)`. -The field-preset option arguments and select templates (ADR 246) are not used: a select resolves a template value and has no defaults and no refusal, while a type choice resolves to a named constructor, which is what "becomes one explicit type" means. +A call is refused at the argument that makes it invalid, and the message lists what that argument accepts. That covers: +- a value an option does not list, such as `Date(value: jsDate)`; +- a combination with no row, such as `Time(value: temporal, column: timetz)`; +- a passed-on argument the chosen type does not take, such as `Date(precision: 3)`. -Every place that reads a field's codec reads the codec its call resolves to, including through a `types {}` alias: the PSL column resolution, the `@default` value check and the `@onCreate`/`@onUpdate` generator lookup, the Prisma 7 reader and the TypeScript builder, whose `type.*` helpers (ADR 275) include the type choices and resolve their application type at compile time from literal arguments. The `type.*` helper of a type choice takes the same arguments in the same order, and its type is one overload per row, so a call is typed as the explicit type it chooses and a combination with no row does not compile. The editor's hover shows each argument's values as PSL writes them and the explicit type each combination becomes. +Assembly refuses a type choice that: +- names a constructor not registered beside it; +- has two rows for one combination; +- lists a value or default its option does not declare; +- gives a row default for an argument the chosen constructor does not take; +- passes on an argument whose data type differs from the chosen constructor's. -### What the contract and the tools see +### Every reader uses the type the call chooses -The PSL interpreter resolves a convenience type to its explicit type's codec and parameters while it builds the contract, so the contract only ever holds the explicit type. The migration planner, `db verify` and the runtime read the contract and are unchanged. +Every place that needs a field's codec resolves the field's call first, including through a `types {}` alias, so it sees the explicit type's codec: +- the PSL interpreter, which builds the column; +- the `@default` value check; +- the `@onCreate`/`@onUpdate` generator lookup; +- the Prisma 7 reader; +- the MongoDB reader; +- the language server. -`contract infer` and `contract print` write a schema from a database or a contract, where nothing records which layer the user wrote. They write the convenience type: the one whose choices reach the column's codec, with the `value:`, `column:` and `precision:` arguments that differ from its defaults. Each data type marks at most one type constructor `inferred` (ADR 254); a convenience type is the inferred constructor of every data type it reaches, and the explicit type is written only for a codec no convenience type reaches. The printer runs the text it writes back through the interpreter, as it does today, to prove it gives the same codec and parameters. +`DateTime @onCreate(now())` therefore gets the generator for `TimestamptzJsDate`, and `DateTime(value: temporal) @onCreate(now())` gets the one for `TimestamptzTemporal`. + +In a TypeScript contract, `type.DateTime(...)`, `type.Date(...)`, `type.Time(...)` and `type.Duration(...)` take the same arguments in the same order. Each has one overload per row, so a call is typed as the explicit type it chooses, and a combination with no row does not compile. + +### The values the defaults read + +The defaults are chosen so that the value is right for the concept and needs nothing installed: +- **`DateTime` reads a JavaScript `Date`.** That is the right value for a moment in time, it works everywhere, and it is what Prisma 7 gave. A `Date` holds milliseconds, so the default precision is 3. A user who needs microseconds writes `DateTime(value: temporal)` or `DateTime(value: string)`. +- **`Date`, `Time` and `Duration` read plain ISO 8601 strings.** JavaScript has no built-in calendar date, time of day or duration. A `Date` would bring back the wrong-day bug, and a class or branded string owned by Prisma would add friction without catching anything the codec doesn't. + +A string is the ISO 8601 form its data type stores (ADR 254, "Date and time types"). It reads the same on every server, and a value written in that form reads back unchanged: +- The `timestamptz` codec converts any session time zone offset to UTC. +- The `timetz` codec keeps the offset stored with the value. +- `IntervalString` reads every `IntervalStyle` PostgreSQL prints. + +Reading dates and times as ISO text needs `DateStyle=ISO`. The PostgreSQL target declares that requirement, and the PostgreSQL driver ensures every connection it hands to the runtime meets it. How the driver does that is its own concern. A codec that still reads text in another style refuses it, with an error that names the setting. + +The Temporal codecs take their TypeScript types from `temporal-spec`, a package of declarations with no runtime code. A Temporal field is then typed precisely in a project that has no global `Temporal` declarations. No polyfill reaches an application that does not choose a Temporal value. + +### What the tools write + +`contract infer` reads a database, and `contract print` reads a contract. Neither has a record of which layer the user wrote. Both take the well-trodden path wherever it reaches the column: they write the convenience type's default, with only the arguments that differ from its defaults. The table shows what they write; the last row is the explicit fallback. + +| Column | Written as | Explicit type it becomes | +| --- | --- | --- | +| `timestamptz(3)` | `DateTime` | `TimestamptzJsDate(3)` | +| `timestamptz` (no declared precision) | `DateTime(precision: 6)` | `TimestamptzJsDate(6)` | +| `timestamp(3)` | `DateTime(column: timestamp)` | `TimestampJsDate(3)` | +| `date` | `Date` | `DateString` | +| `time(0)` | `Time(precision: 0)` | `TimeString(0)` | +| `timetz` | `Time(column: timetz)` | `Timetz` | +| `interval` | `Duration` | `IntervalString` | +| a `timestamp` read as `Temporal.PlainDateTime` | `TimestampTemporal` | `TimestampTemporal` | + +**A missing precision.** PostgreSQL stores microseconds whether a precision of 6 is declared or omitted. So the date and time data types treat an omitted precision as 6 when comparing columns (their normal form, ADR 254). A column with no declared precision is written as `DateTime(precision: 6)`, and `db verify` and the migration planner see no difference. + +**Type marks and arguments.** Each data type marks one explicit type `inferred`: the type its convenience default becomes. To write the convenience call, the tools find the row that names that explicit type, build its arguments from the row, and drop those equal to the defaults. They then check that the call resolves back to the same codec and parameters. Arguments are always written by name. + +**The Prisma 7 schema reader** follows the same marks: + +| Prisma 7 field | Explicit type | +| --- | --- | +| `DateTime` | `TimestampJsDate(3)` (a `timestamp(3)` column, read as a `Date`, as Prisma 7 read it) | +| `@db.Timestamptz(p)` | `TimestamptzJsDate(p)` | +| `@db.Date` | `DateString` | +| `@db.Time(p)` | `TimeString(p)` | +| `@db.Timetz(p)` | `Timetz(p)` | + +**`prisma orm init`** writes `DateTime` for the timestamps of a new project. ### Combinations left out -- A JavaScript `Date` for a calendar date or a time of day. A `Date` is a moment in time, so a calendar date stored through one depends on the time zone of the machine that made it. That is the Prisma 7 bug this layer must not bring back. -- `Temporal.PlainTime` on a `timetz` column, because it drops the stored UTC offset. -- `Temporal.PlainDateTime`, a date and a time with no time zone. It is rarely what an application means: a field that holds "9am wherever the user is". It stays available as the explicit type `TimestampTemporal`. `DateTime(value: temporal, column: timestamp)` reads a `Temporal.Instant`, treating the stored value as UTC. +- **A JavaScript `Date` for a calendar date or a time of day.** A calendar date stored through a moment in time depends on the time zone of the machine that made it. +- **`Temporal.PlainTime` on a `timetz` column.** It would drop the stored UTC offset. +- **`Temporal.PlainDateTime`.** This is a date and a time with no time zone, which is rarely what an application means ("9am wherever the user is"). It stays available as the explicit type `TimestampTemporal`. `DateTime(value: temporal, column: timestamp)` reads a `Temporal.Instant` instead. ### Created-at and updated-at -ADR 275 has the PostgreSQL target ship ready-made type aliases under `temporal`, such as `temporal.UpdatedAt`, built on `TimestamptzTemporal` (formerly `Timestamptz`), with `String` and `JsDate` variants for the other kinds of value. With this decision they are built on `DateTime` instead, for example `temporal.UpdatedAt = DateTime @onCreate(now()) @onUpdate(now())`, and use its defaults. `now()` picks its generator from the field's final codec, so it writes a `Date`, a `Temporal.Instant` or a string to match. +The PostgreSQL target ships two ready-made type aliases (ADR 275): +- `temporal.CreatedAt` is `DateTime @onCreate(now())`. +- `temporal.UpdatedAt` is `DateTime @onCreate(now()) @onUpdate(now())`. -The `String` and `JsDate` variants are not needed. A contributed alias takes no arguments (ADR 275), so a user who wants another kind of value writes the attributes on the convenience type, `DateTime(value: string) @onCreate(now()) @onUpdate(now())`, or defines their own alias with that line. +Both are the default `TimestamptzJsDate(3)`. A contributed alias takes no arguments, so a user who wants another kind of value writes the attributes on the convenience call, for example `DateTime(value: string) @onCreate(now()) @onUpdate(now())`, or declares their own alias with that line. `now()` picks its generator from the field's final codec, so it always writes the value that codec expects. ## Consequences -- A user can write `DateTime`, `Date` or `Time` and get a working field without knowing PostgreSQL's column types or the `Temporal` API. A user who wants a specific column type or kind of value says so with an argument, or writes the explicit type. -- **Breaking for schemas that write `DateTime`, `Date`, `Time`, `Timestamptz`, `Timestamp` or `Interval`.** `DateTime`, `Date` and `Time` change meaning from one explicit type to a convenience type, whose defaults may choose a different kind of value. `Timestamptz`, `Timestamp` and `Interval` become errors. ADR 257's rule keeps a renamed name for one release line as a deprecated alias; that rule is not applied before Prisma 8 reaches its release candidate, so every name changes at once and the upgrade instructions give the rewrite. A schema that wants today's meaning writes the explicit names (`TimestamptzTemporal`, `TimestampTemporal`, `DateTemporal`, `TimeTemporal`, `IntervalParts`), and its contract does not change. -- **Breaking for `contract print`,** which today writes `DateTime` for a `timestamptz` column read as a `Temporal.Instant`. -- **Upgraded Prisma 7 schemas read `Date` values instead of strings.** Their contracts change codec, so each database needs `prisma db sign` after the upgrade. A schema written by an earlier `contract infer` or `prisma orm init` keeps its explicit string types and its contract. -- **String values change form.** An application that reads a string date or time type gets ISO 8601 text instead of PostgreSQL's display text, for example `2024-01-01T00:00:00Z` instead of `2024-01-01 00:00:00+00`. The codec ids and the contract do not change. -- **Temporal fields are typed from `temporal-spec`,** a package of TypeScript declarations with no runtime code, instead of an undeclared global `Temporal`. A Temporal field is then never typed `any` in a project without the polyfill's global declarations, and no polyfill code reaches an application that does not opt into Temporal values. A missing global `Temporal` at runtime is still reported when a Temporal codec or generator first runs. -- **`Interval` reads the same object as before, under the name `IntervalParts`;** `Duration` reads an ISO string by default. -- ADR 254 says a type constructor names one codec and maps its arguments onto parameters. That stays true; a type choice is a second kind of type declaration that resolves to a type constructor. -- ADR 257 says the second codec of a stored type adds a suffix. Where a stored type has several codecs, every one now has a suffix, and the unsuffixed name of the concept belongs to a convenience type. -- ADR 275's `String` and `JsDate` variants of the contributed `temporal.*` aliases are not built. -- The planned rename of the PostgreSQL and SQLite types, which applies ADR 257 to those targets (for example `Int` to `Int4`), keeps `DateTime` as the convenience type instead of removing it, and adds the renames in item 6. -- The editor's hover text gives each convenience type's `value` and `column` choices and the explicit type each combination becomes, and completion inserts the bare name, which writes the defaults. +- A user can write `DateTime`, `Date`, `Time` or `Duration` without knowing PostgreSQL's column types or the Temporal API, and get a working field. A user who needs a specific column type or kind of value says so with an argument, or writes the explicit type. +- The contract, the migration planner, `db verify` and the runtime see only explicit types, and nothing below the PSL and TypeScript surfaces changes. +- A schema the tools write reads like one a user would write. +- A type constructor still names one codec, as ADR 254 says. A type choice is a second kind of type declaration that resolves to a type constructor. +- ADR 257's suffix rule applies to every codec of a column type that has several. The unsuffixed name of a concept belongs to a convenience type. +- ADR 275's contributed `temporal.*` aliases are built on `DateTime`, and need no variants for other kinds of value. +- The editor's hover lists each convenience type's choices and the explicit type each combination becomes. ## Not decided here -- SQLite, MySQL and MongoDB. Each declares its own convenience types over its own explicit types, under item 11, and each already uses one of the names: SQLite's explicit `DateTime` is its `sqlite/datetime@1` type, MySQL's explicit `DateTime` is a `datetime(3)` column read as a JavaScript `Date`, and MongoDB's explicit `Date` is `mongo/date@1`, a moment in time, with `DateTime` as a deprecated alias of it. -- Whether other scalar types get convenience types, for example an `Int` whose arguments choose between the explicit `Int2`, `Int4` and `Int8`. +- **SQLite, MySQL and MongoDB.** A convenience name must mean the same thing on every target. Each of these targets declares its own type choices over its own explicit types, and each already uses one of the names for an explicit type: + - SQLite's `DateTime` is `sqlite/datetime@1`. + - MySQL's `DateTime` is a `datetime(3)` column read as a `Date`. + - MongoDB's `Date` is `mongo/date@1`, a moment in time. MongoDB's explicit `Date` therefore needs another name before a convenience `Date` can mean a calendar date there. +- **Whether other scalar types get convenience types,** for example an `Int` whose arguments choose between `Int2`, `Int4` and `Int8`. ## Alternatives considered -**Name every type after the application's meaning, with no explicit types.** Every field would be written `DateTime`, `Date` or `Time` and refined by arguments, and the database-named types would go. This reverses ADR 257, and leaves arguments as the only way to state a column type exactly. Two layers keep the database-named types as the precise form and add the short form on top. +**Name every type after the application's meaning, with no explicit types.** Every field would be written `DateTime`, `Date` or `Time` and refined by arguments, and the database-named types would go. That reverses ADR 257 and leaves arguments as the only way to state a column type exactly. Two layers keep the database-named types as the precise form and put the short form on top. + +**One codec for `DateTime`, with the column type as a parameter.** A codec converts values of exactly one data type, and a column stores the data type its codec represents (ADR 254). A codec whose data type depends on a parameter breaks both rules. Choosing among existing codecs gives the same schema without changing codecs. -**One codec for `DateTime`, with the column type as a parameter.** A codec converts values of exactly one data type, and a column stores the data type its codec represents (ADR 254). A codec whose data type depends on a parameter breaks both rules. Choosing among existing codecs in the type constructor gives the same schema syntax without changing codecs. +**A type constructor whose codec is a select template over option arguments** (ADR 246). A select picks a value by case, but it has no default case and no way to refuse a combination. A bare `DateTime` would resolve to no codec, and `Time(value: temporal, column: timetz)` could not be refused. A type choice that names explicit types has defaults and refusals by construction. -**A type constructor whose codec is chosen by a select template over option arguments** (the field-preset machinery of ADR 246). A select has no default case and no way to refuse a combination, so bare `DateTime` would resolve to no codec and `Time(value: temporal, column: timetz)` could not be refused; and the machinery is being removed with the presets. A type choice that names explicit constructors has defaults and refusals by construction. +**Convenience types as contributed type aliases with arguments.** A type alias attaches attributes such as `@onCreate` to one fixed type (ADR 275). Choosing which type a field has is a different job. -**Convenience types as contributed type aliases with arguments.** A type alias attaches attributes such as `@onCreate` to one fixed type (ADR 275). Choosing which type is what a type constructor does, and a type constructor already takes arguments. +**Keep only the explicit types and document which to choose.** It adds nothing to learn, but the schema offers no default, so every user must understand every choice before writing their first field. -**Keep only the explicit types and document which to choose.** This adds nothing to learn, but the schema offers no default, so every user must understand every choice before writing a first field. +**Leave the Temporal-valued explicit types unsuffixed.** That changes fewer names, but `Timestamptz` would look like the neutral choice when it reads one particular kind of value. The explicit types would also follow two naming patterns. -**Keep `Timestamptz` and `Timestamp` unsuffixed and rename only `Date` and `Time`.** It changes fewer names, but `Timestamptz` would remain an unmarked name for one particular kind of value while its neighbours `Date` and `Time` become convenience types, and the explicit types would follow two naming patterns. +**Name the convenience types after the Temporal API: `Instant`, `PlainDate`, `PlainTime`.** The names would say exactly what the application reads, but they are a library's vocabulary. A user modelling an application thinks "a birthday" or "when the order was paid". -**Name the convenience types after the `Temporal` API: `Instant`, `PlainDate`, `PlainTime`.** The names would say exactly what value the application reads, but they are a library's vocabulary. A user modelling an application thinks "a birthday" or "when the order was paid", and the mapping from `Date` to a calendar date is obvious without it. +**A Prisma-owned value for calendar dates, times and durations, or a branded string type.** A class is one more date API to learn and convert from. It does not survive a trip through JSON, and becomes redundant once runtimes ship Temporal. A branded string forces every write, which almost always starts from a plain string, through a helper or a cast. It catches nothing the codec doesn't already refuse when the value is written. -**A Prisma-owned value for calendar dates, times of day and durations, or a branded string type.** A class would be one more date API to learn and convert from, would not survive a trip through JSON, and would be redundant once runtimes ship `Temporal`. A branded string (`string & { __brand }`) makes every write that starts from a plain string, which is almost every write, go through a helper or a cast, to catch a mistake the codec already reports when the value is written. Plain ISO 8601 strings, validated by the codec, have neither cost. +**Infer the lossless string types for columns a `Date` cannot hold exactly.** The string types never lose a digit, but the inferred schema would then differ from what a user writing the same model would write. Infer takes the default instead, and the docs say how to keep microseconds. From a82aacffd18b7e9afd5d6d220db6500eed6deca4 Mon Sep 17 00:00:00 2001 From: willbot Date: Fri, 23 Oct 2026 07:45:12 +0200 Subject: [PATCH 069/118] TML-3709: ADR 279 rewritten for a reader without context Co-Authored-By: Claude Opus 5.5 Signed-off-by: willbot Signed-off-by: Will Madden --- ...e an explicit type from their arguments.md | 273 +++++++++++------- 1 file changed, 175 insertions(+), 98 deletions(-) diff --git a/docs/architecture docs/adrs/ADR 279 - Convenience date and time types choose an explicit type from their arguments.md b/docs/architecture docs/adrs/ADR 279 - Convenience date and time types choose an explicit type from their arguments.md index 673e94a7ceef..d43a7b837f60 100644 --- a/docs/architecture docs/adrs/ADR 279 - Convenience date and time types choose an explicit type from their arguments.md +++ b/docs/architecture docs/adrs/ADR 279 - Convenience date and time types choose an explicit type from their arguments.md @@ -1,88 +1,61 @@ # ADR 279 — Convenience date and time types choose an explicit type from their arguments -**Status:** Proposed -**Builds on:** [ADR 241 — Scalar types use the authoring type-constructor channel](ADR%20241%20-%20Scalar%20types%20use%20the%20authoring%20type-constructor%20channel.md) -**Amends:** [ADR 254 — Data types and casts](ADR%20254%20-%20Data%20types%20and%20casts.md), [ADR 257 — Scalar types are named after the target on every surface](ADR%20257%20-%20Scalar%20types%20are%20named%20after%20the%20target%20on%20every%20surface.md), [ADR 275 — Client generators are field attributes, and a type alias is expanded into each column](ADR%20275%20-%20Client%20generators%20are%20field%20attributes%2C%20and%20a%20type%20alias%20is%20expanded%20into%20each%20column.md) +**Status:** Accepted +**Builds on:** [ADR 241 — Scalar types use the authoring type-constructor channel](ADR%20241%20-%20Scalar%20types%20use%20the%20authoring%20type-constructor%20channel.md), [ADR 254 — Data types and casts](ADR%20254%20-%20Data%20types%20and%20casts.md), [ADR 257 — Scalar types are named after the target on every surface](ADR%20257%20-%20Scalar%20types%20are%20named%20after%20the%20target%20on%20every%20surface.md) +**Amends:** [ADR 275 — Client generators are field attributes, and a type alias is expanded into each column](ADR%20275%20-%20Client%20generators%20are%20field%20attributes%2C%20and%20a%20type%20alias%20is%20expanded%20into%20each%20column.md) ## At a glance -On PostgreSQL today, each date and time type name fixes one column type and one kind of value that the application reads: `Timestamptz` is a `timestamptz` column read as a `Temporal.Instant`, `TimestamptzString` is the same column read as a string, and so on. This decision calls those the **explicit types**, and keeps them. - -It adds four **convenience types**, named for what a field means in the application: `DateTime` for a moment in time, `Date` for a calendar date, `Time` for a time of day, and `Duration` for a length of time. Each convenience type becomes one explicit type. Two optional arguments say which: `value`, what the application reads, and `column`, the PostgreSQL column type. - ```prisma model Order { - id Int @id - paidAt DateTime - heldAt DateTime(value: temporal, column: timestamptz, precision: 3) - shipBy Date(value: string) - opensAt Time(value: temporal, precision: 0) + id Int @id + paidAt DateTime // timestamptz(3), read as a JavaScript Date + shipBy Date // date, read as "2024-02-29" + opensAt Time // time, read as "09:30:00" + period Duration // interval, read as "P1M" + settledAt DateTime(value: temporal) // timestamptz, read as a Temporal.Instant + legacyAt DateTime(column: timestamp) // timestamp(3), read as a Date stored as UTC + exactAt TimestamptzTemporal(6) // the explicit type, written directly } ``` -| Field | Becomes the explicit type | Column | The application reads | -| --- | --- | --- | --- | -| `paidAt` | `TimestamptzJsDate(3)` | `timestamptz(3)` | a JavaScript `Date` | -| `heldAt` | `TimestamptzTemporal(3)` | `timestamptz(3)` | `Temporal.Instant` | -| `shipBy` | `DateString` | `date` | a string, such as `"2024-02-29"` | -| `opensAt` | `TimeTemporal(0)` | `time(0)` | `Temporal.PlainTime` | - -The contract stores what the explicit type stores: the codec, its data type and its parameters. `heldAt` is stored exactly as if the user had written `heldAt TimestamptzTemporal(3)`, and nothing in the contract records that a convenience type was written. +`DateTime`, `Date`, `Time` and `Duration` are **convenience types**. Each is named for what a field means in the application, and each becomes exactly one **explicit type**, the database-named type that fixes the column type and the value the application reads. Written bare, a convenience type takes defaults that work on any JavaScript runtime with no setup. Its optional arguments choose any other explicit type. The contract records only the explicit type: `settledAt` is stored exactly as `settledAt TimestamptzTemporal` would be. ## Decision -1. **Date and time types have two layers.** Each explicit type uses one codec, so it fixes one column type and one application value, and is named after the database as ADR 257 says. Each convenience type is named for what a field means in the application, and becomes exactly one explicit type. -2. **A convenience type's arguments choose its explicit type.** `value` says what the application reads: a JavaScript `Date`, a `Temporal` object or a string. `column` says the PostgreSQL column type. Each has a default that the convenience type declares. Any other argument, such as `precision`, is passed on to the chosen explicit type. -3. **A convenience type is a type choice: a type declaration whose arguments name the explicit type it becomes.** It declares its option arguments, each with a fixed set of values written as bare names and a default, the arguments it passes on (such as `precision`), and a table from each allowed combination of option values to the name of an explicit type constructor, with any parameter defaults for that combination. An unlisted combination is refused. -4. **The contract stores only the explicit type.** The contract format, the codecs, the data types, the storage hash and the execution hash are the same whether a schema writes the convenience type or the explicit type it becomes. -5. **The PostgreSQL target provides four convenience types:** - - | Convenience type | `value` choices | `column` choices | Defaults | - | --- | --- | --- | --- | - | `DateTime` | `jsDate` (a JavaScript `Date`), `temporal` (`Temporal.Instant`), `string` | `timestamptz`, `timestamp` | `jsDate`, `timestamptz`; precision 3 with `jsDate`, PostgreSQL's default otherwise | - | `Date` | `string`, `temporal` (`Temporal.PlainDate`) | `date` | `string` | - | `Time` | `string`, `temporal` (`Temporal.PlainTime`) | `time`; `timetz` is allowed only with `value: string` | `string`, `time` | - | `Duration` | `string` (an ISO 8601 duration such as `P1Y2M`), `parts` (the object `{ months, days, micros }`) | `interval` | `string` | +1. **Date and time types have two layers.** The explicit types are named after the database, as ADR 257 requires. Each one uses a single codec, so it fixes both the column type and the value the application reads. The convenience types are named after the application concept, and each one becomes exactly one explicit type. +2. **A convenience type is a type choice.** It is a declaration with: + - option arguments, each with a fixed set of values and a default; + - arguments it passes on to the chosen type, such as `precision`; + - a table from each allowed combination of option values to the name of an explicit type. - The value names repeat the suffixes of the explicit types, so `DateTime(value: jsDate)` and `TimestamptzJsDate` read as the same thing. No default needs a global `Temporal`, so every default works on a runtime without one. A JavaScript `Date` is the default only for a moment in time; a calendar date, a time of day and a length of time default to plain ISO 8601 strings, because JavaScript has no built-in value for them and a `Date` there stores the wrong day. + A combination the table does not list is refused. +3. **The contract stores only the explicit type.** A schema written with convenience types and the same schema written with the explicit types they become give the same contract and the same hashes. +4. **The defaults need no setup.** On PostgreSQL: + - `DateTime` defaults to a `timestamptz(3)` column read as a JavaScript `Date`. + - `Date`, `Time` and `Duration` default to ISO 8601 strings. -6. **Every explicit type for a column that has several codecs carries a suffix naming its kind of value.** The explicit types that read `Temporal` values gain the suffix `Temporal`, next to the existing `String` and `JsDate` suffixes: - - | Column | `Temporal` value | Explicit name today | Explicit name after | - | --- | --- | --- | --- | - | `timestamptz` | `Temporal.Instant` | `Timestamptz`, and `DateTime` as a second name | `TimestamptzTemporal` | - | `timestamp` | `Temporal.PlainDateTime` | `Timestamp` | `TimestampTemporal` | - | `date` | `Temporal.PlainDate` | `Date` | `DateTemporal` | - | `time` | `Temporal.PlainTime` | `Time` | `TimeTemporal` | - - This frees `DateTime`, `Date` and `Time` for the convenience types. Without the suffix, a name such as `Timestamptz` would look like a neutral default when it is one specific kind of value. For the same reason, `Interval`, which reads the object `{ months, days, micros }`, becomes `IntervalParts`, next to the new `IntervalString`. - -7. **Three codecs are added:** a `timestamp` column read as a JavaScript `Date`, and a `timestamp` column read as a `Temporal.Instant`, both reading and writing the stored value as UTC; and an `interval` column read as an ISO 8601 duration string. Each gets an explicit type: `TimestampJsDate(p)` for the `Date` codec, `TimestampInstant(p)` for the `Temporal.Instant` codec, and `IntervalString(p)`. `IntervalString` reads interval text in every `IntervalStyle` PostgreSQL prints (`postgres`, `postgres_verbose`, `sql_standard`, `iso_8601`) and returns the ISO 8601 duration ADR 254 stores, so it needs no `IntervalStyle` setting. `DateTime(column: timestamp)` with `value: jsDate` becomes `TimestampJsDate`, with `value: temporal` becomes `TimestampInstant`, and `Duration` with `value: string` becomes `IntervalString`. `TimestampInstant` names its `Temporal` type rather than taking the `Temporal` suffix, because the `timestamp` column has two `Temporal` codecs and `TimestampTemporal` is the one that reads `Temporal.PlainDateTime`. -8. **The Prisma 7 reader maps Prisma 7's `DateTime` to `TimestampJsDate(3)`,** which `contract print` writes as `DateTime(column: timestamp)`: a `timestamp(3)` column read as a JavaScript `Date`, instead of a string. The column and its stored values do not change; only the application value goes from a string back to the `Date` that Prisma 7 gave, so an upgraded application keeps the values its code was written for. `prisma orm init`, which creates new columns, writes plain `DateTime`, a `timestamptz(3)` column read as a `Date`. -9. **A string value is ISO 8601, the same on every server.** Every string codec of a date or time type returns the form its data type stores (see ADR 254, "Date and time types") and reads that form back unchanged. PostgreSQL's display text, such as `2024-01-01 00:00:00+00` or a year written with BC, never reaches the application. The `timestamptz` codec converts any session time zone offset to UTC itself; the `timetz` codec keeps the offset the value was stored with, because that offset is part of a `timetz` value. Prisma requires `DateStyle=ISO` on its database connections: it sets it on the connections it opens, and refuses a value it reads in another style with an error naming the setting. The codec ids do not change; the old text was never a format an application could rely on. -10. **`contract infer` and `contract print` write convenience types,** with arguments only where they differ from the defaults. A column no convenience type covers, a `timestamp` read as `Temporal.PlainDateTime`, is written as its explicit type. `prisma orm init` writes `DateTime`. -11. **A convenience name means the same thing on every target.** This decision covers PostgreSQL. Another target adds convenience types under the same names, and renames any explicit type that already uses one of them for something else. + No default needs a global `Temporal`. +5. **The tools write the convenience types.** `contract infer`, `contract print`, the Prisma 7 schema reader and `prisma orm init` write each date or time column as the convenience call that becomes its explicit type. They write only the arguments that differ from the defaults. An explicit type is written only for a column that no convenience call reaches. ## Why -Prisma 8 gives users explicit, predictable controls first, then makes common cases easy by building convenience APIs on top of them. For example, the `postgres({ contract, url })` function builds the runtime, adapter and driver that a user could otherwise create and connect by hand. The date and time types have the explicit controls but nothing built on top of them. - -In Prisma 7, every date or time field is `DateTime`, and the application always reads a JavaScript `Date`. A `@db.*` attribute changes the column type. A user who never learns those attributes still gets a working field. That was simple, but Prisma made the choice for the user, and for some fields it was wrong. A `Date` is a moment in time, not a calendar date: a birthday created at local midnight on a machine east of UTC is stored as the day before. +In Prisma 7, every date or time field is `DateTime`, and the application always reads a JavaScript `Date`. A `@db.*` attribute changes the column type and nothing else. That is easy to learn. But a `Date` is a moment in time, so it is the wrong value for a calendar date or a time of day. A birthday created at local midnight on a machine east of UTC is stored as the day before. -Prisma 8's explicit types fix that by making every choice visible. But each name fixes both the column type and the kind of value, so PostgreSQL has eleven date and time type names (`Timestamptz`, `TimestamptzJsDate`, `TimestamptzString`, `Timestamp`, `TimestampString`, `Date`, `DateString`, `Time`, `TimeString`, `Timetz`, and `DateTime` as a second name for `Timestamptz`). Prisma 8 does not say which one a new user should pick. To choose, a user must already know the difference between `timestamp` and `timestamptz`, what each `Temporal` type means, and how each kind of value behaves. A user writing a schema thinks "this field is when the order was paid", not "this is a `timestamptz` column read as a `Temporal.Instant`". +Prisma 8's explicit types make each choice visible instead. A `date` column can be read as a `Temporal.PlainDate` (`DateTemporal`) or as a string (`DateString`), and each is correct for what it is. The cost is that every name fixes two things at once: the column type and the kind of value. So a column type with three kinds of value has three names, and across PostgreSQL's date and time column types there are more than a dozen. Nothing says which one to start with. To choose, a user must already know the difference between `timestamp` and `timestamptz`, what each Temporal type means, and what each kind of value costs. A user writing a model thinks "this field is when the order was paid", not "this is a `timestamptz` column read as a `Temporal.Instant`". -The convenience types give that user one name per concept, with defaults that work. Every explicit choice stays available, as an argument or as the explicit type itself. +Prisma 8's approach is to give users explicit, predictable controls first, then make common cases easy by building convenience APIs on top. The client facades are an example. `postgres({ contract, url })` builds the runtime, adapter and driver that a user could otherwise create and connect by hand. The convenience types do the same for dates and times. There is one name per concept, with a default that works, and every explicit choice stays available as an argument or as the explicit type itself. ## How it works -### A type choice names its explicit types +### The explicit layer -An explicit type is a type constructor: it names one codec, and maps its arguments onto that codec's parameters. +An explicit type is a type constructor. It names one codec and maps its arguments onto that codec's parameters: ```ts TimestamptzTemporal: { kind: 'typeConstructor', - args: [{ kind: 'number', name: 'precision', integer: true, optional: true }], + args: [{ kind: 'value', dataType: PG_SIZE_TYPE, name: 'precision', optional: true }], output: { codecId: 'pg/timestamptz-temporal@1', typeParams: { precision: { kind: 'arg', index: 0 } }, @@ -90,75 +63,179 @@ TimestamptzTemporal: { }, ``` -A convenience type is a different kind of declaration in the same namespace, a type choice. It names no codec. It declares its option arguments and a table whose rows each name an explicit type constructor: +When a column type has more than one codec, every explicit type for it carries a suffix naming its kind of value. No name looks like the neutral default: + +| Column | `Date` | `Temporal` | String | Other | +| --- | --- | --- | --- | --- | +| `timestamptz` | `TimestamptzJsDate` | `TimestamptzTemporal` (`Temporal.Instant`) | `TimestamptzString` | | +| `timestamp` | `TimestampJsDate` | `TimestampTemporal` (`Temporal.PlainDateTime`), `TimestampInstant` (`Temporal.Instant`) | `TimestampString` | | +| `date` | | `DateTemporal` (`Temporal.PlainDate`) | `DateString` | | +| `time` | | `TimeTemporal` (`Temporal.PlainTime`) | `TimeString` | | +| `timetz` | | | `Timetz` | | +| `interval` | | | `IntervalString` (an ISO 8601 duration) | `IntervalParts` (`{ months, days, micros }`) | + +`TimestampJsDate` and `TimestampInstant` read and write a `timestamp` column as UTC. This lets a `timestamp` column, the column Prisma 7's `DateTime` creates, hold the same moment-in-time value as a `timestamptz` column. `TimestampInstant` names its Temporal type instead of taking the `Temporal` suffix, because the `timestamp` column has two Temporal codecs. + +### A type choice names its explicit types + +A convenience type is a second kind of declaration in the same namespace, a type choice. It names no codec. It declares its arguments and a table whose rows each name an explicit type: + +```ts +DateTime: { + kind: 'typeChoice', + args: [ + { kind: 'option', name: 'value', values: ['jsDate', 'temporal', 'string'], default: 'jsDate', optional: true }, + { kind: 'option', name: 'column', values: ['timestamptz', 'timestamp'], default: 'timestamptz', optional: true }, + { kind: 'value', dataType: PG_SIZE_TYPE, name: 'precision', optional: true }, + ], + choices: [ + { when: { value: 'jsDate', column: 'timestamptz' }, type: 'TimestamptzJsDate', defaults: { precision: 3 } }, + { when: { value: 'temporal', column: 'timestamptz' }, type: 'TimestamptzTemporal' }, + { when: { value: 'string', column: 'timestamptz' }, type: 'TimestamptzString' }, + { when: { value: 'jsDate', column: 'timestamp' }, type: 'TimestampJsDate', defaults: { precision: 3 } }, + { when: { value: 'temporal', column: 'timestamp' }, type: 'TimestampInstant' }, + { when: { value: 'string', column: 'timestamp' }, type: 'TimestampString' }, + ], +}, +``` + +The value names repeat the explicit types' suffixes, so `DateTime(value: jsDate)` and `TimestamptzJsDate` read as the same thing. The PostgreSQL target declares four type choices: -| `value` | `column` | Explicit type | Parameter defaults | +| Convenience type | `value` | `column` | Default | | --- | --- | --- | --- | -| `jsDate` (default) | `timestamptz` (default) | `TimestamptzJsDate` | precision 3 | -| `temporal` | `timestamptz` | `TimestamptzTemporal` | | -| `string` | `timestamptz` | `TimestamptzString` | | -| `jsDate` | `timestamp` | `TimestampJsDate` | precision 3 | -| `temporal` | `timestamp` | `TimestampInstant` | | -| `string` | `timestamp` | `TimestampString` | | +| `DateTime` | `jsDate`, `temporal`, `string` | `timestamptz`, `timestamp` | `jsDate`, `timestamptz`, precision 3 | +| `Date` | `string`, `temporal` | `date` | `string` | +| `Time` | `string`, `temporal` | `time`, `timetz` (with `string` only) | `string`, `time` | +| `Duration` | `string`, `parts` | `interval` | `string` | + +Resolving a call takes four steps: +1. Fill in the default for every option the call leaves out. +2. Look up the table row for the resulting combination of values. +3. Instantiate the explicit type that row names, passing on the other arguments. A row default stands in for an argument the call leaves out. +4. The result is exactly what writing that explicit type would give. + +Every argument is optional, so a bare `DateTime` is its default row. Arguments are written by name or in declaration order. Because `value` comes first, `Time(3)` reads `3` as the `value` option, and the refusal says to write `Time(precision: 3)`. + +A call is refused at the argument that makes it invalid, and the message lists what that argument accepts. That covers: +- a value an option does not list, such as `Date(value: jsDate)`; +- a combination with no row, such as `Time(value: temporal, column: timetz)`; +- a passed-on argument the chosen type does not take, such as `Date(precision: 3)`. + +Assembly refuses a type choice that: +- names a constructor not registered beside it; +- has two rows for one combination; +- lists a value or default its option does not declare; +- gives a row default for an argument the chosen constructor does not take; +- passes on an argument whose data type differs from the chosen constructor's. + +### Every reader uses the type the call chooses + +Every place that needs a field's codec resolves the field's call first, including through a `types {}` alias, so it sees the explicit type's codec: +- the PSL interpreter, which builds the column; +- the `@default` value check; +- the `@onCreate`/`@onUpdate` generator lookup; +- the Prisma 7 reader; +- the MongoDB reader; +- the language server. + +`DateTime @onCreate(now())` therefore gets the generator for `TimestamptzJsDate`, and `DateTime(value: temporal) @onCreate(now())` gets the one for `TimestamptzTemporal`. -That is `DateTime`'s table; the other three are declared the same way. Resolving a call looks up the row for its option values, then instantiates the named explicit constructor with the passed-on arguments, so the result is exactly what writing the explicit type gives. A combination with no row, such as `Date(value: jsDate)`, is refused at the argument that makes it invalid, and the message lists the values that argument accepts with the others as written. Assembly refuses a table that names a constructor no component registers. +In a TypeScript contract, `type.DateTime(...)`, `type.Date(...)`, `type.Time(...)` and `type.Duration(...)` take the same arguments in the same order. Each has one overload per row, so a call is typed as the explicit type it chooses, and a combination with no row does not compile. -The field-preset option arguments and select templates (ADR 246) are not used: a select resolves a template value and has no defaults and no refusal, while a type choice resolves to a named constructor, which is what "becomes one explicit type" means. +### The values the defaults read -Every place that reads a field's codec reads the codec its call resolves to, including through a `types {}` alias: the PSL column resolution, the `@default` value check and the `@onCreate`/`@onUpdate` generator lookup, the Prisma 7 reader and the TypeScript builder, whose `type.*` helpers (ADR 275) include the type choices and resolve their application type at compile time from literal arguments. +The defaults are chosen so that the value is right for the concept and needs nothing installed: +- **`DateTime` reads a JavaScript `Date`.** That is the right value for a moment in time, it works everywhere, and it is what Prisma 7 gave. A `Date` holds milliseconds, so the default precision is 3. A user who needs microseconds writes `DateTime(value: temporal)` or `DateTime(value: string)`. +- **`Date`, `Time` and `Duration` read plain ISO 8601 strings.** JavaScript has no built-in calendar date, time of day or duration. A `Date` would bring back the wrong-day bug, and a class or branded string owned by Prisma would add friction without catching anything the codec doesn't. -### What the contract and the tools see +A string is the ISO 8601 form its data type stores (ADR 254, "Date and time types"). It reads the same on every server, and a value written in that form reads back unchanged: +- The `timestamptz` codec converts any session time zone offset to UTC. +- The `timetz` codec keeps the offset stored with the value. +- `IntervalString` reads every `IntervalStyle` PostgreSQL prints. -The PSL interpreter resolves a convenience type to its explicit type's codec and parameters while it builds the contract, so the contract only ever holds the explicit type. The migration planner, `db verify` and the runtime read the contract and are unchanged. +Reading dates and times as ISO text needs `DateStyle=ISO`. The PostgreSQL target declares that requirement, and the PostgreSQL driver ensures every connection it hands to the runtime meets it. How the driver does that is its own concern. A codec that still reads text in another style refuses it, with an error that names the setting. -`contract infer` and `contract print` write a schema from a database or a contract, where nothing records which layer the user wrote. They write the convenience type: the one whose choices reach the column's codec, with the `value:`, `column:` and `precision:` arguments that differ from its defaults. Each data type marks at most one type constructor `inferred` (ADR 254); a convenience type is the inferred constructor of every data type it reaches, and the explicit type is written only for a codec no convenience type reaches. The printer runs the text it writes back through the interpreter, as it does today, to prove it gives the same codec and parameters. +The Temporal codecs take their TypeScript types from `temporal-spec`, a package of declarations with no runtime code. A Temporal field is then typed precisely in a project that has no global `Temporal` declarations. No polyfill reaches an application that does not choose a Temporal value. + +### What the tools write + +`contract infer` reads a database, and `contract print` reads a contract. Neither has a record of which layer the user wrote. Both take the well-trodden path wherever it reaches the column: they write the convenience type's default, with only the arguments that differ from its defaults. The table shows what they write; the last row is the explicit fallback. + +| Column | Written as | Explicit type it becomes | +| --- | --- | --- | +| `timestamptz(3)` | `DateTime` | `TimestamptzJsDate(3)` | +| `timestamptz` (no declared precision) | `DateTime(precision: 6)` | `TimestamptzJsDate(6)` | +| `timestamp(3)` | `DateTime(column: timestamp)` | `TimestampJsDate(3)` | +| `date` | `Date` | `DateString` | +| `time(0)` | `Time(precision: 0)` | `TimeString(0)` | +| `timetz` | `Time(column: timetz)` | `Timetz` | +| `interval` | `Duration` | `IntervalString` | +| a `timestamp` read as `Temporal.PlainDateTime` | `TimestampTemporal` | `TimestampTemporal` | + +**A missing precision.** PostgreSQL stores microseconds whether a precision of 6 is declared or omitted. So the date and time data types treat an omitted precision as 6 when comparing columns (their normal form, ADR 254). A column with no declared precision is written as `DateTime(precision: 6)`, and `db verify` and the migration planner see no difference. + +**Type marks and arguments.** Each data type marks one explicit type `inferred`: the type its convenience default becomes. To write the convenience call, the tools find the row that names that explicit type, build its arguments from the row, and drop those equal to the defaults. They then check that the call resolves back to the same codec and parameters. Arguments are always written by name. + +**The Prisma 7 schema reader** follows the same marks: + +| Prisma 7 field | Explicit type | +| --- | --- | +| `DateTime` | `TimestampJsDate(3)` (a `timestamp(3)` column, read as a `Date`, as Prisma 7 read it) | +| `@db.Timestamptz(p)` | `TimestamptzJsDate(p)` | +| `@db.Date` | `DateString` | +| `@db.Time(p)` | `TimeString(p)` | +| `@db.Timetz(p)` | `Timetz(p)` | + +**`prisma orm init`** writes `DateTime` for the timestamps of a new project. ### Combinations left out -- A JavaScript `Date` for a calendar date or a time of day. A `Date` is a moment in time, so a calendar date stored through one depends on the time zone of the machine that made it. That is the Prisma 7 bug this layer must not bring back. -- `Temporal.PlainTime` on a `timetz` column, because it drops the stored UTC offset. -- `Temporal.PlainDateTime`, a date and a time with no time zone. It is rarely what an application means: a field that holds "9am wherever the user is". It stays available as the explicit type `TimestampTemporal`. `DateTime(value: temporal, column: timestamp)` reads a `Temporal.Instant`, treating the stored value as UTC. +- **A JavaScript `Date` for a calendar date or a time of day.** A calendar date stored through a moment in time depends on the time zone of the machine that made it. +- **`Temporal.PlainTime` on a `timetz` column.** It would drop the stored UTC offset. +- **`Temporal.PlainDateTime`.** This is a date and a time with no time zone, which is rarely what an application means ("9am wherever the user is"). It stays available as the explicit type `TimestampTemporal`. `DateTime(value: temporal, column: timestamp)` reads a `Temporal.Instant` instead. ### Created-at and updated-at -ADR 275 has the PostgreSQL target ship ready-made type aliases under `temporal`, such as `temporal.UpdatedAt`, built on `Timestamptz`, with `String` and `JsDate` variants for the other kinds of value. With this decision they are built on `DateTime` instead, for example `temporal.UpdatedAt = DateTime @onCreate(now()) @onUpdate(now())`, and use its defaults. `now()` picks its generator from the field's final codec, so it writes a `Date`, a `Temporal.Instant` or a string to match. +The PostgreSQL target ships two ready-made type aliases (ADR 275): +- `temporal.CreatedAt` is `DateTime @onCreate(now())`. +- `temporal.UpdatedAt` is `DateTime @onCreate(now()) @onUpdate(now())`. -The `String` and `JsDate` variants are not needed. A contributed alias takes no arguments (ADR 275), so a user who wants another kind of value writes the attributes on the convenience type, `DateTime(value: string) @onCreate(now()) @onUpdate(now())`, or defines their own alias with that line. +Both are the default `TimestamptzJsDate(3)`. A contributed alias takes no arguments, so a user who wants another kind of value writes the attributes on the convenience call, for example `DateTime(value: string) @onCreate(now()) @onUpdate(now())`, or declares their own alias with that line. `now()` picks its generator from the field's final codec, so it always writes the value that codec expects. ## Consequences -- A user can write `DateTime`, `Date` or `Time` and get a working field without knowing PostgreSQL's column types or the `Temporal` API. A user who wants a specific column type or kind of value says so with an argument, or writes the explicit type. -- **Breaking for schemas that write `DateTime`, `Date`, `Time`, `Timestamptz`, `Timestamp` or `Interval`.** `DateTime`, `Date` and `Time` change meaning from one explicit type to a convenience type, whose defaults may choose a different kind of value. `Timestamptz`, `Timestamp` and `Interval` become errors. ADR 257's rule keeps a renamed name for one release line as a deprecated alias; that rule is not applied before Prisma 8 reaches its release candidate, so every name changes at once and the upgrade instructions give the rewrite. A schema that wants today's meaning writes the explicit names (`TimestamptzTemporal`, `TimestampTemporal`, `DateTemporal`, `TimeTemporal`, `IntervalParts`), and its contract does not change. -- **Breaking for `contract print`,** which today writes `DateTime` for a `timestamptz` column read as a `Temporal.Instant`. -- **Upgraded Prisma 7 schemas read `Date` values instead of strings.** Their contracts change codec, so each database needs `prisma db sign` after the upgrade. A schema written by an earlier `contract infer` or `prisma orm init` keeps its explicit string types and its contract. -- **String values change form.** An application that reads a string date or time type gets ISO 8601 text instead of PostgreSQL's display text, for example `2024-01-01T00:00:00Z` instead of `2024-01-01 00:00:00+00`. The codec ids and the contract do not change. -- **Temporal fields are typed from `temporal-spec`,** a package of TypeScript declarations with no runtime code, instead of an undeclared global `Temporal`. A Temporal field is then never typed `any` in a project without the polyfill's global declarations, and no polyfill code reaches an application that does not opt into Temporal values. A missing global `Temporal` at runtime is still reported when a Temporal codec or generator first runs. -- **`Interval` reads the same object as before, under the name `IntervalParts`;** `Duration` reads an ISO string by default. -- ADR 254 says a type constructor names one codec and maps its arguments onto parameters. That stays true; a type choice is a second kind of type declaration that resolves to a type constructor. -- ADR 257 says the second codec of a stored type adds a suffix. Where a stored type has several codecs, every one now has a suffix, and the unsuffixed name of the concept belongs to a convenience type. -- ADR 275's `String` and `JsDate` variants of the contributed `temporal.*` aliases are not built. -- The planned rename of the PostgreSQL and SQLite types, which applies ADR 257 to those targets (for example `Int` to `Int4`), keeps `DateTime` as the convenience type instead of removing it, and adds the renames in item 6. -- The editor's completion offers each convenience type's `value` and `column` choices, and its hover text gives the explicit type each choice becomes. +- A user can write `DateTime`, `Date`, `Time` or `Duration` without knowing PostgreSQL's column types or the Temporal API, and get a working field. A user who needs a specific column type or kind of value says so with an argument, or writes the explicit type. +- The contract, the migration planner, `db verify` and the runtime see only explicit types, and nothing below the PSL and TypeScript surfaces changes. +- A schema the tools write reads like one a user would write. +- A type constructor still names one codec, as ADR 254 says. A type choice is a second kind of type declaration that resolves to a type constructor. +- ADR 257's suffix rule applies to every codec of a column type that has several. The unsuffixed name of a concept belongs to a convenience type. +- ADR 275's contributed `temporal.*` aliases are built on `DateTime`, and need no variants for other kinds of value. +- The editor's hover lists each convenience type's choices and the explicit type each combination becomes. ## Not decided here -- SQLite and MongoDB. Each declares its own convenience types over its own explicit types, under item 11, and each already uses one of the names: SQLite's explicit `DateTime` is its `sqlite/datetime@1` type, and MongoDB's explicit `Date` is `mongo/date@1`, a moment in time, with `DateTime` as a deprecated alias of it. -- Whether other scalar types get convenience types, for example an `Int` whose arguments choose between the explicit `Int2`, `Int4` and `Int8`. +- **SQLite, MySQL and MongoDB.** A convenience name must mean the same thing on every target. Each of these targets declares its own type choices over its own explicit types, and each already uses one of the names for an explicit type: + - SQLite's `DateTime` is `sqlite/datetime@1`. + - MySQL's `DateTime` is a `datetime(3)` column read as a `Date`. + - MongoDB's `Date` is `mongo/date@1`, a moment in time. MongoDB's explicit `Date` therefore needs another name before a convenience `Date` can mean a calendar date there. +- **Whether other scalar types get convenience types,** for example an `Int` whose arguments choose between `Int2`, `Int4` and `Int8`. ## Alternatives considered -**Name every type after the application's meaning, with no explicit types.** Every field would be written `DateTime`, `Date` or `Time` and refined by arguments, and the database-named types would go. This reverses ADR 257, and leaves arguments as the only way to state a column type exactly. Two layers keep the database-named types as the precise form and add the short form on top. +**Name every type after the application's meaning, with no explicit types.** Every field would be written `DateTime`, `Date` or `Time` and refined by arguments, and the database-named types would go. That reverses ADR 257 and leaves arguments as the only way to state a column type exactly. Two layers keep the database-named types as the precise form and put the short form on top. + +**One codec for `DateTime`, with the column type as a parameter.** A codec converts values of exactly one data type, and a column stores the data type its codec represents (ADR 254). A codec whose data type depends on a parameter breaks both rules. Choosing among existing codecs gives the same schema without changing codecs. -**One codec for `DateTime`, with the column type as a parameter.** A codec converts values of exactly one data type, and a column stores the data type its codec represents (ADR 254). A codec whose data type depends on a parameter breaks both rules. Choosing among existing codecs in the type constructor gives the same schema syntax without changing codecs. +**A type constructor whose codec is a select template over option arguments** (ADR 246). A select picks a value by case, but it has no default case and no way to refuse a combination. A bare `DateTime` would resolve to no codec, and `Time(value: temporal, column: timetz)` could not be refused. A type choice that names explicit types has defaults and refusals by construction. -**A type constructor whose codec is chosen by a select template over option arguments** (the field-preset machinery of ADR 246). A select has no default case and no way to refuse a combination, so bare `DateTime` would resolve to no codec and `Time(value: temporal, column: timetz)` could not be refused; and the machinery is being removed with the presets. A type choice that names explicit constructors has defaults and refusals by construction. +**Convenience types as contributed type aliases with arguments.** A type alias attaches attributes such as `@onCreate` to one fixed type (ADR 275). Choosing which type a field has is a different job. -**Convenience types as contributed type aliases with arguments.** A type alias attaches attributes such as `@onCreate` to one fixed type (ADR 275). Choosing which type is what a type constructor does, and a type constructor already takes arguments. +**Keep only the explicit types and document which to choose.** It adds nothing to learn, but the schema offers no default, so every user must understand every choice before writing their first field. -**Keep only the explicit types and document which to choose.** This adds nothing to learn, but the schema offers no default, so every user must understand every choice before writing a first field. +**Leave the Temporal-valued explicit types unsuffixed.** That changes fewer names, but `Timestamptz` would look like the neutral choice when it reads one particular kind of value. The explicit types would also follow two naming patterns. -**Keep `Timestamptz` and `Timestamp` unsuffixed and rename only `Date` and `Time`.** It changes fewer names, but `Timestamptz` would remain an unmarked name for one particular kind of value while its neighbours `Date` and `Time` become convenience types, and the explicit types would follow two naming patterns. +**Name the convenience types after the Temporal API: `Instant`, `PlainDate`, `PlainTime`.** The names would say exactly what the application reads, but they are a library's vocabulary. A user modelling an application thinks "a birthday" or "when the order was paid". -**Name the convenience types after the `Temporal` API: `Instant`, `PlainDate`, `PlainTime`.** The names would say exactly what value the application reads, but they are a library's vocabulary. A user modelling an application thinks "a birthday" or "when the order was paid", and the mapping from `Date` to a calendar date is obvious without it. +**A Prisma-owned value for calendar dates, times and durations, or a branded string type.** A class is one more date API to learn and convert from. It does not survive a trip through JSON, and becomes redundant once runtimes ship Temporal. A branded string forces every write, which almost always starts from a plain string, through a helper or a cast. It catches nothing the codec doesn't already refuse when the value is written. -**A Prisma-owned value for calendar dates, times of day and durations, or a branded string type.** A class would be one more date API to learn and convert from, would not survive a trip through JSON, and would be redundant once runtimes ship `Temporal`. A branded string (`string & { __brand }`) makes every write that starts from a plain string, which is almost every write, go through a helper or a cast, to catch a mistake the codec already reports when the value is written. Plain ISO 8601 strings, validated by the codec, have neither cost. +**Infer the lossless string types for columns a `Date` cannot hold exactly.** The string types never lose a digit, but the inferred schema would then differ from what a user writing the same model would write. Infer takes the default instead, and the docs say how to keep microseconds. From 55d7024ab482408642f0f083a97abe5404dfca65 Mon Sep 17 00:00:00 2001 From: willbot Date: Fri, 23 Oct 2026 07:47:54 +0200 Subject: [PATCH 070/118] ADR 279: fixes from a cold read Defines codec and type choice where first used, explains the temporal alias namespace, shows what the Prisma 7 reader's types print as, moves the left-out combinations next to the table they explain, and drops the MongoDB reader from the list of PSL readers. Co-Authored-By: Claude Opus 5.5 Signed-off-by: willbot Signed-off-by: Will Madden --- ...e an explicit type from their arguments.md | 120 +++++++++--------- 1 file changed, 62 insertions(+), 58 deletions(-) diff --git a/docs/architecture docs/adrs/ADR 279 - Convenience date and time types choose an explicit type from their arguments.md b/docs/architecture docs/adrs/ADR 279 - Convenience date and time types choose an explicit type from their arguments.md index d43a7b837f60..86f9cb81a0cd 100644 --- a/docs/architecture docs/adrs/ADR 279 - Convenience date and time types choose an explicit type from their arguments.md +++ b/docs/architecture docs/adrs/ADR 279 - Convenience date and time types choose an explicit type from their arguments.md @@ -19,30 +19,30 @@ model Order { } ``` -`DateTime`, `Date`, `Time` and `Duration` are **convenience types**. Each is named for what a field means in the application, and each becomes exactly one **explicit type**, the database-named type that fixes the column type and the value the application reads. Written bare, a convenience type takes defaults that work on any JavaScript runtime with no setup. Its optional arguments choose any other explicit type. The contract records only the explicit type: `settledAt` is stored exactly as `settledAt TimestamptzTemporal` would be. +`DateTime`, `Date`, `Time` and `Duration` are **convenience types**. Each is named for what a field means in the application, and each becomes exactly one **explicit type**. An explicit type, such as `TimestamptzJsDate`, is named after the database and sets both the column type and the JavaScript value the application reads. Written bare, a convenience type takes defaults that work on any JavaScript runtime with no setup. Its optional arguments choose any other explicit type. The contract records only the explicit type: `settledAt` is stored exactly as `settledAt TimestamptzTemporal` would be. ## Decision -1. **Date and time types have two layers.** The explicit types are named after the database, as ADR 257 requires. Each one uses a single codec, so it fixes both the column type and the value the application reads. The convenience types are named after the application concept, and each one becomes exactly one explicit type. -2. **A convenience type is a type choice.** It is a declaration with: +1. **Date and time types have two layers.** The explicit types are named after the database, as ADR 257 requires. Each one uses a single codec, the code that converts between one column type and one kind of JavaScript value, so it fixes both. The convenience types are named after the application concept, and each one becomes exactly one explicit type. +2. **A convenience type is declared as a type choice:** a declaration that picks an explicit type from its arguments. It has: - option arguments, each with a fixed set of values and a default; - - arguments it passes on to the chosen type, such as `precision`; + - arguments it hands through to the chosen explicit type, such as `precision`; - a table from each allowed combination of option values to the name of an explicit type. A combination the table does not list is refused. -3. **The contract stores only the explicit type.** A schema written with convenience types and the same schema written with the explicit types they become give the same contract and the same hashes. +3. **The contract stores only the explicit type.** A schema written with convenience types and the same schema written with the explicit types they become give the same contract. 4. **The defaults need no setup.** On PostgreSQL: - `DateTime` defaults to a `timestamptz(3)` column read as a JavaScript `Date`. - `Date`, `Time` and `Duration` default to ISO 8601 strings. No default needs a global `Temporal`. -5. **The tools write the convenience types.** `contract infer`, `contract print`, the Prisma 7 schema reader and `prisma orm init` write each date or time column as the convenience call that becomes its explicit type. They write only the arguments that differ from the defaults. An explicit type is written only for a column that no convenience call reaches. +5. **Tools that generate a schema write convenience types.** `contract infer`, `contract print`, the Prisma 7 schema reader and `prisma orm init` write each date or time column as the convenience call that becomes its explicit type, with only the arguments that differ from the defaults. They write the explicit type only when no convenience call becomes it. ## Why -In Prisma 7, every date or time field is `DateTime`, and the application always reads a JavaScript `Date`. A `@db.*` attribute changes the column type and nothing else. That is easy to learn. But a `Date` is a moment in time, so it is the wrong value for a calendar date or a time of day. A birthday created at local midnight on a machine east of UTC is stored as the day before. +In Prisma 7, every date or time field is `DateTime`, and the application always reads a JavaScript `Date`. A `@db.*` attribute changes the column type and nothing else. That is easy to learn. But a `Date` is a moment in time, so it is the wrong value for a calendar date or a time of day. A birthday created at local midnight on a machine east of UTC is stored as the day before, because that midnight is still the previous day in UTC. -Prisma 8's explicit types make each choice visible instead. A `date` column can be read as a `Temporal.PlainDate` (`DateTemporal`) or as a string (`DateString`), and each is correct for what it is. The cost is that every name fixes two things at once: the column type and the kind of value. So a column type with three kinds of value has three names, and across PostgreSQL's date and time column types there are more than a dozen. Nothing says which one to start with. To choose, a user must already know the difference between `timestamp` and `timestamptz`, what each Temporal type means, and what each kind of value costs. A user writing a model thinks "this field is when the order was paid", not "this is a `timestamptz` column read as a `Temporal.Instant`". +Prisma 8 replaces the single type with explicit types, each naming both the column type and the value read. A `date` column can be read as a `Temporal.PlainDate` (`DateTemporal`) or as a string (`DateString`), and each is correct for what it is. The cost is that every name fixes two things at once: the column type and the kind of value. So a column type with three kinds of value has three names, and across PostgreSQL's date and time column types there are more than a dozen. Nothing says which one to start with. To choose, a user must already know the difference between `timestamp` and `timestamptz`, what each Temporal type means, and what each kind of value costs. A user writing a model thinks "this field is when the order was paid", not "this is a `timestamptz` column read as a `Temporal.Instant`". Prisma 8's approach is to give users explicit, predictable controls first, then make common cases easy by building convenience APIs on top. The client facades are an example. `postgres({ contract, url })` builds the runtime, adapter and driver that a user could otherwise create and connect by hand. The convenience types do the same for dates and times. There is one name per concept, with a default that works, and every explicit choice stays available as an argument or as the explicit type itself. @@ -50,7 +50,7 @@ Prisma 8's approach is to give users explicit, predictable controls first, then ### The explicit layer -An explicit type is a type constructor. It names one codec and maps its arguments onto that codec's parameters: +An explicit type is a type constructor. It names one codec and fills that codec's parameters (`typeParams`) from its arguments; here the first argument becomes `precision`: ```ts TimestamptzTemporal: { @@ -63,7 +63,7 @@ TimestamptzTemporal: { }, ``` -When a column type has more than one codec, every explicit type for it carries a suffix naming its kind of value. No name looks like the neutral default: +When a column type has more than one codec, every explicit type for it carries a suffix naming its kind of value. So no explicit type takes a bare name like `Timestamptz`; the bare concept names belong to the convenience types. `timetz` has one codec, so its explicit type has no suffix. | Column | `Date` | `Temporal` | String | Other | | --- | --- | --- | --- | --- | @@ -74,11 +74,13 @@ When a column type has more than one codec, every explicit type for it carries a | `timetz` | | | `Timetz` | | | `interval` | | | `IntervalString` (an ISO 8601 duration) | `IntervalParts` (`{ months, days, micros }`) | -`TimestampJsDate` and `TimestampInstant` read and write a `timestamp` column as UTC. This lets a `timestamp` column, the column Prisma 7's `DateTime` creates, hold the same moment-in-time value as a `timestamptz` column. `TimestampInstant` names its Temporal type instead of taking the `Temporal` suffix, because the `timestamp` column has two Temporal codecs. +`TimestampJsDate` and `TimestampInstant` read and write a `timestamp` column as UTC, so a `timestamp` column can hold the same moment in time as a `timestamptz` column. That matters because `timestamp` is the column Prisma 7's `DateTime` creates. + +`TimestampInstant` names its Temporal type instead of taking the `Temporal` suffix, because the `timestamp` column has two Temporal codecs. ### A type choice names its explicit types -A convenience type is a second kind of declaration in the same namespace, a type choice. It names no codec. It declares its arguments and a table whose rows each name an explicit type: +A convenience type is a second kind of declaration, a type choice, in the same set of type names as the explicit types. It names no codec. It declares its arguments and a table whose rows each name an explicit type: ```ts DateTime: { @@ -99,7 +101,16 @@ DateTime: { }, ``` -The value names repeat the explicit types' suffixes, so `DateTime(value: jsDate)` and `TimestamptzJsDate` read as the same thing. The PostgreSQL target declares four type choices: +The value names repeat the explicit types' suffixes, so `DateTime(value: jsDate)` and `TimestamptzJsDate` read as the same thing. The one exception is `DateTime(value: temporal, column: timestamp)`, which becomes `TimestampInstant`; "Combinations left out" says why. + +Resolving a call takes three steps: +1. Fill in the default for every option the call leaves out. +2. Look up the table row for the resulting combination of values. +3. Instantiate the explicit type that row names, handing through the other arguments. If the row gives a default for an argument the call leaves out, such as `precision: 3` for `TimestamptzJsDate`, that default applies. + +The result is exactly what writing that explicit type would give. Every argument is optional, so a bare `DateTime` is its default row. Arguments are written by name or in declaration order. Because `value` comes first, `Time(3)` reads `3` as the `value` option, and the refusal says to write `Time(precision: 3)`. + +The PostgreSQL target declares four type choices: | Convenience type | `value` | `column` | Default | | --- | --- | --- | --- | @@ -108,34 +119,31 @@ The value names repeat the explicit types' suffixes, so `DateTime(value: jsDate) | `Time` | `string`, `temporal` | `time`, `timetz` (with `string` only) | `string`, `time` | | `Duration` | `string`, `parts` | `interval` | `string` | -Resolving a call takes four steps: -1. Fill in the default for every option the call leaves out. -2. Look up the table row for the resulting combination of values. -3. Instantiate the explicit type that row names, passing on the other arguments. A row default stands in for an argument the call leaves out. -4. The result is exactly what writing that explicit type would give. - -Every argument is optional, so a bare `DateTime` is its default row. Arguments are written by name or in declaration order. Because `value` comes first, `Time(3)` reads `3` as the `value` option, and the refusal says to write `Time(precision: 3)`. - A call is refused at the argument that makes it invalid, and the message lists what that argument accepts. That covers: - a value an option does not list, such as `Date(value: jsDate)`; - a combination with no row, such as `Time(value: temporal, column: timetz)`; -- a passed-on argument the chosen type does not take, such as `Date(precision: 3)`. +- an argument the chosen explicit type does not take, such as `Date(precision: 3)`. -Assembly refuses a type choice that: -- names a constructor not registered beside it; +When a target's type declarations are loaded, a type choice is refused if it: +- names an explicit type the same target does not declare; - has two rows for one combination; - lists a value or default its option does not declare; - gives a row default for an argument the chosen constructor does not take; -- passes on an argument whose data type differs from the chosen constructor's. +- hands through an argument whose data type differs from the chosen constructor's. + +### Combinations left out + +- **A JavaScript `Date` for a calendar date or a time of day.** A calendar date stored through a moment in time depends on the time zone of the machine that made it. +- **`Temporal.PlainTime` on a `timetz` column.** It would drop the stored UTC offset. +- **`Temporal.PlainDateTime`.** This is a date and a time with no time zone, which is rarely what an application means ("9am wherever the user is"). It stays available as the explicit type `TimestampTemporal`. `DateTime(value: temporal, column: timestamp)` reads a `Temporal.Instant` instead. -### Every reader uses the type the call chooses +### The whole toolchain sees the chosen explicit type -Every place that needs a field's codec resolves the field's call first, including through a `types {}` alias, so it sees the explicit type's codec: +Every place that needs a field's codec resolves the field's call first, including a call inside a named alias declared in a `types {}` block, so it sees the explicit type's codec: - the PSL interpreter, which builds the column; - the `@default` value check; - the `@onCreate`/`@onUpdate` generator lookup; -- the Prisma 7 reader; -- the MongoDB reader; +- the Prisma 7 schema reader; - the language server. `DateTime @onCreate(now())` therefore gets the generator for `TimestamptzJsDate`, and `DateTime(value: temporal) @onCreate(now())` gets the one for `TimestamptzTemporal`. @@ -146,20 +154,20 @@ In a TypeScript contract, `type.DateTime(...)`, `type.Date(...)`, `type.Time(... The defaults are chosen so that the value is right for the concept and needs nothing installed: - **`DateTime` reads a JavaScript `Date`.** That is the right value for a moment in time, it works everywhere, and it is what Prisma 7 gave. A `Date` holds milliseconds, so the default precision is 3. A user who needs microseconds writes `DateTime(value: temporal)` or `DateTime(value: string)`. -- **`Date`, `Time` and `Duration` read plain ISO 8601 strings.** JavaScript has no built-in calendar date, time of day or duration. A `Date` would bring back the wrong-day bug, and a class or branded string owned by Prisma would add friction without catching anything the codec doesn't. +- **`Date`, `Time` and `Duration` read plain ISO 8601 strings.** JavaScript has no built-in calendar date, time of day or duration. A `Date` would bring back the wrong-day bug. A Prisma-owned class or branded string is rejected under Alternatives. -A string is the ISO 8601 form its data type stores (ADR 254, "Date and time types"). It reads the same on every server, and a value written in that form reads back unchanged: -- The `timestamptz` codec converts any session time zone offset to UTC. +A string is the ISO 8601 text of the stored value (ADR 254, "Date and time types"). It reads the same on every server, and a value written in that form reads back unchanged: +- The `timestamptz` string codec converts any session time zone offset to UTC. - The `timetz` codec keeps the offset stored with the value. -- `IntervalString` reads every `IntervalStyle` PostgreSQL prints. +- The `interval` string codec reads every `IntervalStyle` PostgreSQL prints. -Reading dates and times as ISO text needs `DateStyle=ISO`. The PostgreSQL target declares that requirement, and the PostgreSQL driver ensures every connection it hands to the runtime meets it. How the driver does that is its own concern. A codec that still reads text in another style refuses it, with an error that names the setting. +Reading dates and times as ISO text needs `DateStyle=ISO`. The PostgreSQL target declares that requirement, and the PostgreSQL driver ensures every connection it hands to the runtime meets it. This record does not decide how the driver does it. If a codec receives text in another style, it fails with an error that names `DateStyle`. The Temporal codecs take their TypeScript types from `temporal-spec`, a package of declarations with no runtime code. A Temporal field is then typed precisely in a project that has no global `Temporal` declarations. No polyfill reaches an application that does not choose a Temporal value. ### What the tools write -`contract infer` reads a database, and `contract print` reads a contract. Neither has a record of which layer the user wrote. Both take the well-trodden path wherever it reaches the column: they write the convenience type's default, with only the arguments that differ from its defaults. The table shows what they write; the last row is the explicit fallback. +`contract infer` reads a database, and `contract print` reads a contract. Neither has a record of which layer the user wrote. Both write the convenience type whenever one becomes the column's explicit type, with only the arguments that differ from its defaults. For each column, they pick the explicit type the convenience default gives, so the schema they write is the one a user would write. The table shows what they write; the last row is the explicit fallback. | Column | Written as | Explicit type it becomes | | --- | --- | --- | @@ -172,44 +180,40 @@ The Temporal codecs take their TypeScript types from `temporal-spec`, a package | `interval` | `Duration` | `IntervalString` | | a `timestamp` read as `Temporal.PlainDateTime` | `TimestampTemporal` | `TimestampTemporal` | -**A missing precision.** PostgreSQL stores microseconds whether a precision of 6 is declared or omitted. So the date and time data types treat an omitted precision as 6 when comparing columns (their normal form, ADR 254). A column with no declared precision is written as `DateTime(precision: 6)`, and `db verify` and the migration planner see no difference. - -**Type marks and arguments.** Each data type marks one explicit type `inferred`: the type its convenience default becomes. To write the convenience call, the tools find the row that names that explicit type, build its arguments from the row, and drop those equal to the defaults. They then check that the call resolves back to the same codec and parameters. Arguments are always written by name. +**A missing precision.** PostgreSQL stores microseconds whether a precision of 6 is declared or omitted. So when Prisma compares a column with the contract, an omitted precision on a date or time column counts as 6 (ADR 254 calls this the data type's normal form). A column with no declared precision is written as `DateTime(precision: 6)`, and `db verify` and the migration planner see no difference. -**The Prisma 7 schema reader** follows the same marks: +A `Date` holds milliseconds, so a column with more digits loses them when read through `DateTime`. The infer documentation says to write `DateTime(value: temporal)` or `DateTime(value: string)` to keep them. -| Prisma 7 field | Explicit type | -| --- | --- | -| `DateTime` | `TimestampJsDate(3)` (a `timestamp(3)` column, read as a `Date`, as Prisma 7 read it) | -| `@db.Timestamptz(p)` | `TimestamptzJsDate(p)` | -| `@db.Date` | `DateString` | -| `@db.Time(p)` | `TimeString(p)` | -| `@db.Timetz(p)` | `Timetz(p)` | +**The Prisma 7 schema reader** (`prisma7Schema`, which turns a Prisma 7 `schema.prisma` into a Prisma 8 contract) picks the same explicit types for the same columns: -**`prisma orm init`** writes `DateTime` for the timestamps of a new project. +| Prisma 7 field | Explicit type | Printed as | +| --- | --- | --- | +| `DateTime` | `TimestampJsDate(3)`: a `timestamp(3)` column read as a `Date`, as Prisma 7 read it | `DateTime(column: timestamp)` | +| `@db.Timestamptz(p)` | `TimestamptzJsDate(p)` | `DateTime(precision: p)` | +| `@db.Date` | `DateString` | `Date` | +| `@db.Time(p)` | `TimeString(p)` | `Time(precision: p)` | +| `@db.Timetz(p)` | `Timetz(p)` | `Time(column: timetz, precision: p)` | -### Combinations left out +**`prisma orm init`** writes the new project's starter model with `createdAt DateTime @default(now()) @onCreate(now())` and `updatedAt temporal.UpdatedAt`. -- **A JavaScript `Date` for a calendar date or a time of day.** A calendar date stored through a moment in time depends on the time zone of the machine that made it. -- **`Temporal.PlainTime` on a `timetz` column.** It would drop the stored UTC offset. -- **`Temporal.PlainDateTime`.** This is a date and a time with no time zone, which is rarely what an application means ("9am wherever the user is"). It stays available as the explicit type `TimestampTemporal`. `DateTime(value: temporal, column: timestamp)` reads a `Temporal.Instant` instead. +**How the tools find the call.** For each column type, one explicit type is flagged `inferred`: the one the convenience default gives. `contract infer` and the Prisma 7 schema reader pick that type. To write the convenience call for an explicit type, the tools find the type-choice row that names it, build the call's arguments from the row, and drop those equal to the defaults. They then check that the call resolves back to the same codec and parameters, and otherwise write the explicit type. Arguments are always written by name. ### Created-at and updated-at -The PostgreSQL target ships two ready-made type aliases (ADR 275): +The PostgreSQL target ships two ready-made type aliases (ADR 275), in a namespace named `temporal` that groups its time aliases; the name does not mean they read Temporal values: - `temporal.CreatedAt` is `DateTime @onCreate(now())`. - `temporal.UpdatedAt` is `DateTime @onCreate(now()) @onUpdate(now())`. -Both are the default `TimestamptzJsDate(3)`. A contributed alias takes no arguments, so a user who wants another kind of value writes the attributes on the convenience call, for example `DateTime(value: string) @onCreate(now()) @onUpdate(now())`, or declares their own alias with that line. `now()` picks its generator from the field's final codec, so it always writes the value that codec expects. +Both are the default `TimestamptzJsDate(3)`. A type alias the target ships takes no arguments, so a user who wants another kind of value writes the attributes on the convenience call, for example `DateTime(value: string) @onCreate(now()) @onUpdate(now())`, or declares their own alias with that line. `now()` picks its generator from the field's final codec, so it always writes the value that codec expects. ## Consequences - A user can write `DateTime`, `Date`, `Time` or `Duration` without knowing PostgreSQL's column types or the Temporal API, and get a working field. A user who needs a specific column type or kind of value says so with an argument, or writes the explicit type. - The contract, the migration planner, `db verify` and the runtime see only explicit types, and nothing below the PSL and TypeScript surfaces changes. - A schema the tools write reads like one a user would write. -- A type constructor still names one codec, as ADR 254 says. A type choice is a second kind of type declaration that resolves to a type constructor. -- ADR 257's suffix rule applies to every codec of a column type that has several. The unsuffixed name of a concept belongs to a convenience type. -- ADR 275's contributed `temporal.*` aliases are built on `DateTime`, and need no variants for other kinds of value. +- A type constructor names one codec, as ADR 254 says. A type choice is a second kind of type declaration that resolves to a type constructor. +- When a column type has several codecs, every explicit type for it names its kind of value in a suffix (ADR 257). The unsuffixed name of a concept belongs to a convenience type. +- The target's `temporal.*` aliases (ADR 275) are built on `DateTime`, and need no variants for other kinds of value. - The editor's hover lists each convenience type's choices and the explicit type each combination becomes. ## Not decided here @@ -236,6 +240,6 @@ Both are the default `TimestamptzJsDate(3)`. A contributed alias takes no argume **Name the convenience types after the Temporal API: `Instant`, `PlainDate`, `PlainTime`.** The names would say exactly what the application reads, but they are a library's vocabulary. A user modelling an application thinks "a birthday" or "when the order was paid". -**A Prisma-owned value for calendar dates, times and durations, or a branded string type.** A class is one more date API to learn and convert from. It does not survive a trip through JSON, and becomes redundant once runtimes ship Temporal. A branded string forces every write, which almost always starts from a plain string, through a helper or a cast. It catches nothing the codec doesn't already refuse when the value is written. +**A Prisma-owned value for calendar dates, times and durations, or a branded string type.** A class is one more date API to learn and convert from. It does not survive a trip through JSON, and it duplicates what Temporal provides. A branded string forces every write, which almost always starts from a plain string, through a helper or a cast. It catches nothing the codec doesn't already refuse when the value is written. -**Infer the lossless string types for columns a `Date` cannot hold exactly.** The string types never lose a digit, but the inferred schema would then differ from what a user writing the same model would write. Infer takes the default instead, and the docs say how to keep microseconds. +**Infer the lossless string types for columns a `Date` cannot hold exactly.** The string types never lose a digit, but the inferred schema would then differ from what a user writing the same model would write. Infer takes the default instead. From 49fce625085dae76502606d6178f17ed222b1324 Mon Sep 17 00:00:00 2001 From: willbot Date: Fri, 23 Oct 2026 07:47:57 +0200 Subject: [PATCH 071/118] TML-3709: ADR 279 fixes from a cold read; slice 5 does not edit the ADR Co-Authored-By: Claude Opus 5.5 Signed-off-by: willbot Signed-off-by: Will Madden --- ...e an explicit type from their arguments.md | 120 +++++++++--------- .../05-tools-write-convenience-types/spec.md | 2 +- 2 files changed, 63 insertions(+), 59 deletions(-) diff --git a/docs/architecture docs/adrs/ADR 279 - Convenience date and time types choose an explicit type from their arguments.md b/docs/architecture docs/adrs/ADR 279 - Convenience date and time types choose an explicit type from their arguments.md index d43a7b837f60..86f9cb81a0cd 100644 --- a/docs/architecture docs/adrs/ADR 279 - Convenience date and time types choose an explicit type from their arguments.md +++ b/docs/architecture docs/adrs/ADR 279 - Convenience date and time types choose an explicit type from their arguments.md @@ -19,30 +19,30 @@ model Order { } ``` -`DateTime`, `Date`, `Time` and `Duration` are **convenience types**. Each is named for what a field means in the application, and each becomes exactly one **explicit type**, the database-named type that fixes the column type and the value the application reads. Written bare, a convenience type takes defaults that work on any JavaScript runtime with no setup. Its optional arguments choose any other explicit type. The contract records only the explicit type: `settledAt` is stored exactly as `settledAt TimestamptzTemporal` would be. +`DateTime`, `Date`, `Time` and `Duration` are **convenience types**. Each is named for what a field means in the application, and each becomes exactly one **explicit type**. An explicit type, such as `TimestamptzJsDate`, is named after the database and sets both the column type and the JavaScript value the application reads. Written bare, a convenience type takes defaults that work on any JavaScript runtime with no setup. Its optional arguments choose any other explicit type. The contract records only the explicit type: `settledAt` is stored exactly as `settledAt TimestamptzTemporal` would be. ## Decision -1. **Date and time types have two layers.** The explicit types are named after the database, as ADR 257 requires. Each one uses a single codec, so it fixes both the column type and the value the application reads. The convenience types are named after the application concept, and each one becomes exactly one explicit type. -2. **A convenience type is a type choice.** It is a declaration with: +1. **Date and time types have two layers.** The explicit types are named after the database, as ADR 257 requires. Each one uses a single codec, the code that converts between one column type and one kind of JavaScript value, so it fixes both. The convenience types are named after the application concept, and each one becomes exactly one explicit type. +2. **A convenience type is declared as a type choice:** a declaration that picks an explicit type from its arguments. It has: - option arguments, each with a fixed set of values and a default; - - arguments it passes on to the chosen type, such as `precision`; + - arguments it hands through to the chosen explicit type, such as `precision`; - a table from each allowed combination of option values to the name of an explicit type. A combination the table does not list is refused. -3. **The contract stores only the explicit type.** A schema written with convenience types and the same schema written with the explicit types they become give the same contract and the same hashes. +3. **The contract stores only the explicit type.** A schema written with convenience types and the same schema written with the explicit types they become give the same contract. 4. **The defaults need no setup.** On PostgreSQL: - `DateTime` defaults to a `timestamptz(3)` column read as a JavaScript `Date`. - `Date`, `Time` and `Duration` default to ISO 8601 strings. No default needs a global `Temporal`. -5. **The tools write the convenience types.** `contract infer`, `contract print`, the Prisma 7 schema reader and `prisma orm init` write each date or time column as the convenience call that becomes its explicit type. They write only the arguments that differ from the defaults. An explicit type is written only for a column that no convenience call reaches. +5. **Tools that generate a schema write convenience types.** `contract infer`, `contract print`, the Prisma 7 schema reader and `prisma orm init` write each date or time column as the convenience call that becomes its explicit type, with only the arguments that differ from the defaults. They write the explicit type only when no convenience call becomes it. ## Why -In Prisma 7, every date or time field is `DateTime`, and the application always reads a JavaScript `Date`. A `@db.*` attribute changes the column type and nothing else. That is easy to learn. But a `Date` is a moment in time, so it is the wrong value for a calendar date or a time of day. A birthday created at local midnight on a machine east of UTC is stored as the day before. +In Prisma 7, every date or time field is `DateTime`, and the application always reads a JavaScript `Date`. A `@db.*` attribute changes the column type and nothing else. That is easy to learn. But a `Date` is a moment in time, so it is the wrong value for a calendar date or a time of day. A birthday created at local midnight on a machine east of UTC is stored as the day before, because that midnight is still the previous day in UTC. -Prisma 8's explicit types make each choice visible instead. A `date` column can be read as a `Temporal.PlainDate` (`DateTemporal`) or as a string (`DateString`), and each is correct for what it is. The cost is that every name fixes two things at once: the column type and the kind of value. So a column type with three kinds of value has three names, and across PostgreSQL's date and time column types there are more than a dozen. Nothing says which one to start with. To choose, a user must already know the difference between `timestamp` and `timestamptz`, what each Temporal type means, and what each kind of value costs. A user writing a model thinks "this field is when the order was paid", not "this is a `timestamptz` column read as a `Temporal.Instant`". +Prisma 8 replaces the single type with explicit types, each naming both the column type and the value read. A `date` column can be read as a `Temporal.PlainDate` (`DateTemporal`) or as a string (`DateString`), and each is correct for what it is. The cost is that every name fixes two things at once: the column type and the kind of value. So a column type with three kinds of value has three names, and across PostgreSQL's date and time column types there are more than a dozen. Nothing says which one to start with. To choose, a user must already know the difference between `timestamp` and `timestamptz`, what each Temporal type means, and what each kind of value costs. A user writing a model thinks "this field is when the order was paid", not "this is a `timestamptz` column read as a `Temporal.Instant`". Prisma 8's approach is to give users explicit, predictable controls first, then make common cases easy by building convenience APIs on top. The client facades are an example. `postgres({ contract, url })` builds the runtime, adapter and driver that a user could otherwise create and connect by hand. The convenience types do the same for dates and times. There is one name per concept, with a default that works, and every explicit choice stays available as an argument or as the explicit type itself. @@ -50,7 +50,7 @@ Prisma 8's approach is to give users explicit, predictable controls first, then ### The explicit layer -An explicit type is a type constructor. It names one codec and maps its arguments onto that codec's parameters: +An explicit type is a type constructor. It names one codec and fills that codec's parameters (`typeParams`) from its arguments; here the first argument becomes `precision`: ```ts TimestamptzTemporal: { @@ -63,7 +63,7 @@ TimestamptzTemporal: { }, ``` -When a column type has more than one codec, every explicit type for it carries a suffix naming its kind of value. No name looks like the neutral default: +When a column type has more than one codec, every explicit type for it carries a suffix naming its kind of value. So no explicit type takes a bare name like `Timestamptz`; the bare concept names belong to the convenience types. `timetz` has one codec, so its explicit type has no suffix. | Column | `Date` | `Temporal` | String | Other | | --- | --- | --- | --- | --- | @@ -74,11 +74,13 @@ When a column type has more than one codec, every explicit type for it carries a | `timetz` | | | `Timetz` | | | `interval` | | | `IntervalString` (an ISO 8601 duration) | `IntervalParts` (`{ months, days, micros }`) | -`TimestampJsDate` and `TimestampInstant` read and write a `timestamp` column as UTC. This lets a `timestamp` column, the column Prisma 7's `DateTime` creates, hold the same moment-in-time value as a `timestamptz` column. `TimestampInstant` names its Temporal type instead of taking the `Temporal` suffix, because the `timestamp` column has two Temporal codecs. +`TimestampJsDate` and `TimestampInstant` read and write a `timestamp` column as UTC, so a `timestamp` column can hold the same moment in time as a `timestamptz` column. That matters because `timestamp` is the column Prisma 7's `DateTime` creates. + +`TimestampInstant` names its Temporal type instead of taking the `Temporal` suffix, because the `timestamp` column has two Temporal codecs. ### A type choice names its explicit types -A convenience type is a second kind of declaration in the same namespace, a type choice. It names no codec. It declares its arguments and a table whose rows each name an explicit type: +A convenience type is a second kind of declaration, a type choice, in the same set of type names as the explicit types. It names no codec. It declares its arguments and a table whose rows each name an explicit type: ```ts DateTime: { @@ -99,7 +101,16 @@ DateTime: { }, ``` -The value names repeat the explicit types' suffixes, so `DateTime(value: jsDate)` and `TimestamptzJsDate` read as the same thing. The PostgreSQL target declares four type choices: +The value names repeat the explicit types' suffixes, so `DateTime(value: jsDate)` and `TimestamptzJsDate` read as the same thing. The one exception is `DateTime(value: temporal, column: timestamp)`, which becomes `TimestampInstant`; "Combinations left out" says why. + +Resolving a call takes three steps: +1. Fill in the default for every option the call leaves out. +2. Look up the table row for the resulting combination of values. +3. Instantiate the explicit type that row names, handing through the other arguments. If the row gives a default for an argument the call leaves out, such as `precision: 3` for `TimestamptzJsDate`, that default applies. + +The result is exactly what writing that explicit type would give. Every argument is optional, so a bare `DateTime` is its default row. Arguments are written by name or in declaration order. Because `value` comes first, `Time(3)` reads `3` as the `value` option, and the refusal says to write `Time(precision: 3)`. + +The PostgreSQL target declares four type choices: | Convenience type | `value` | `column` | Default | | --- | --- | --- | --- | @@ -108,34 +119,31 @@ The value names repeat the explicit types' suffixes, so `DateTime(value: jsDate) | `Time` | `string`, `temporal` | `time`, `timetz` (with `string` only) | `string`, `time` | | `Duration` | `string`, `parts` | `interval` | `string` | -Resolving a call takes four steps: -1. Fill in the default for every option the call leaves out. -2. Look up the table row for the resulting combination of values. -3. Instantiate the explicit type that row names, passing on the other arguments. A row default stands in for an argument the call leaves out. -4. The result is exactly what writing that explicit type would give. - -Every argument is optional, so a bare `DateTime` is its default row. Arguments are written by name or in declaration order. Because `value` comes first, `Time(3)` reads `3` as the `value` option, and the refusal says to write `Time(precision: 3)`. - A call is refused at the argument that makes it invalid, and the message lists what that argument accepts. That covers: - a value an option does not list, such as `Date(value: jsDate)`; - a combination with no row, such as `Time(value: temporal, column: timetz)`; -- a passed-on argument the chosen type does not take, such as `Date(precision: 3)`. +- an argument the chosen explicit type does not take, such as `Date(precision: 3)`. -Assembly refuses a type choice that: -- names a constructor not registered beside it; +When a target's type declarations are loaded, a type choice is refused if it: +- names an explicit type the same target does not declare; - has two rows for one combination; - lists a value or default its option does not declare; - gives a row default for an argument the chosen constructor does not take; -- passes on an argument whose data type differs from the chosen constructor's. +- hands through an argument whose data type differs from the chosen constructor's. + +### Combinations left out + +- **A JavaScript `Date` for a calendar date or a time of day.** A calendar date stored through a moment in time depends on the time zone of the machine that made it. +- **`Temporal.PlainTime` on a `timetz` column.** It would drop the stored UTC offset. +- **`Temporal.PlainDateTime`.** This is a date and a time with no time zone, which is rarely what an application means ("9am wherever the user is"). It stays available as the explicit type `TimestampTemporal`. `DateTime(value: temporal, column: timestamp)` reads a `Temporal.Instant` instead. -### Every reader uses the type the call chooses +### The whole toolchain sees the chosen explicit type -Every place that needs a field's codec resolves the field's call first, including through a `types {}` alias, so it sees the explicit type's codec: +Every place that needs a field's codec resolves the field's call first, including a call inside a named alias declared in a `types {}` block, so it sees the explicit type's codec: - the PSL interpreter, which builds the column; - the `@default` value check; - the `@onCreate`/`@onUpdate` generator lookup; -- the Prisma 7 reader; -- the MongoDB reader; +- the Prisma 7 schema reader; - the language server. `DateTime @onCreate(now())` therefore gets the generator for `TimestamptzJsDate`, and `DateTime(value: temporal) @onCreate(now())` gets the one for `TimestamptzTemporal`. @@ -146,20 +154,20 @@ In a TypeScript contract, `type.DateTime(...)`, `type.Date(...)`, `type.Time(... The defaults are chosen so that the value is right for the concept and needs nothing installed: - **`DateTime` reads a JavaScript `Date`.** That is the right value for a moment in time, it works everywhere, and it is what Prisma 7 gave. A `Date` holds milliseconds, so the default precision is 3. A user who needs microseconds writes `DateTime(value: temporal)` or `DateTime(value: string)`. -- **`Date`, `Time` and `Duration` read plain ISO 8601 strings.** JavaScript has no built-in calendar date, time of day or duration. A `Date` would bring back the wrong-day bug, and a class or branded string owned by Prisma would add friction without catching anything the codec doesn't. +- **`Date`, `Time` and `Duration` read plain ISO 8601 strings.** JavaScript has no built-in calendar date, time of day or duration. A `Date` would bring back the wrong-day bug. A Prisma-owned class or branded string is rejected under Alternatives. -A string is the ISO 8601 form its data type stores (ADR 254, "Date and time types"). It reads the same on every server, and a value written in that form reads back unchanged: -- The `timestamptz` codec converts any session time zone offset to UTC. +A string is the ISO 8601 text of the stored value (ADR 254, "Date and time types"). It reads the same on every server, and a value written in that form reads back unchanged: +- The `timestamptz` string codec converts any session time zone offset to UTC. - The `timetz` codec keeps the offset stored with the value. -- `IntervalString` reads every `IntervalStyle` PostgreSQL prints. +- The `interval` string codec reads every `IntervalStyle` PostgreSQL prints. -Reading dates and times as ISO text needs `DateStyle=ISO`. The PostgreSQL target declares that requirement, and the PostgreSQL driver ensures every connection it hands to the runtime meets it. How the driver does that is its own concern. A codec that still reads text in another style refuses it, with an error that names the setting. +Reading dates and times as ISO text needs `DateStyle=ISO`. The PostgreSQL target declares that requirement, and the PostgreSQL driver ensures every connection it hands to the runtime meets it. This record does not decide how the driver does it. If a codec receives text in another style, it fails with an error that names `DateStyle`. The Temporal codecs take their TypeScript types from `temporal-spec`, a package of declarations with no runtime code. A Temporal field is then typed precisely in a project that has no global `Temporal` declarations. No polyfill reaches an application that does not choose a Temporal value. ### What the tools write -`contract infer` reads a database, and `contract print` reads a contract. Neither has a record of which layer the user wrote. Both take the well-trodden path wherever it reaches the column: they write the convenience type's default, with only the arguments that differ from its defaults. The table shows what they write; the last row is the explicit fallback. +`contract infer` reads a database, and `contract print` reads a contract. Neither has a record of which layer the user wrote. Both write the convenience type whenever one becomes the column's explicit type, with only the arguments that differ from its defaults. For each column, they pick the explicit type the convenience default gives, so the schema they write is the one a user would write. The table shows what they write; the last row is the explicit fallback. | Column | Written as | Explicit type it becomes | | --- | --- | --- | @@ -172,44 +180,40 @@ The Temporal codecs take their TypeScript types from `temporal-spec`, a package | `interval` | `Duration` | `IntervalString` | | a `timestamp` read as `Temporal.PlainDateTime` | `TimestampTemporal` | `TimestampTemporal` | -**A missing precision.** PostgreSQL stores microseconds whether a precision of 6 is declared or omitted. So the date and time data types treat an omitted precision as 6 when comparing columns (their normal form, ADR 254). A column with no declared precision is written as `DateTime(precision: 6)`, and `db verify` and the migration planner see no difference. - -**Type marks and arguments.** Each data type marks one explicit type `inferred`: the type its convenience default becomes. To write the convenience call, the tools find the row that names that explicit type, build its arguments from the row, and drop those equal to the defaults. They then check that the call resolves back to the same codec and parameters. Arguments are always written by name. +**A missing precision.** PostgreSQL stores microseconds whether a precision of 6 is declared or omitted. So when Prisma compares a column with the contract, an omitted precision on a date or time column counts as 6 (ADR 254 calls this the data type's normal form). A column with no declared precision is written as `DateTime(precision: 6)`, and `db verify` and the migration planner see no difference. -**The Prisma 7 schema reader** follows the same marks: +A `Date` holds milliseconds, so a column with more digits loses them when read through `DateTime`. The infer documentation says to write `DateTime(value: temporal)` or `DateTime(value: string)` to keep them. -| Prisma 7 field | Explicit type | -| --- | --- | -| `DateTime` | `TimestampJsDate(3)` (a `timestamp(3)` column, read as a `Date`, as Prisma 7 read it) | -| `@db.Timestamptz(p)` | `TimestamptzJsDate(p)` | -| `@db.Date` | `DateString` | -| `@db.Time(p)` | `TimeString(p)` | -| `@db.Timetz(p)` | `Timetz(p)` | +**The Prisma 7 schema reader** (`prisma7Schema`, which turns a Prisma 7 `schema.prisma` into a Prisma 8 contract) picks the same explicit types for the same columns: -**`prisma orm init`** writes `DateTime` for the timestamps of a new project. +| Prisma 7 field | Explicit type | Printed as | +| --- | --- | --- | +| `DateTime` | `TimestampJsDate(3)`: a `timestamp(3)` column read as a `Date`, as Prisma 7 read it | `DateTime(column: timestamp)` | +| `@db.Timestamptz(p)` | `TimestamptzJsDate(p)` | `DateTime(precision: p)` | +| `@db.Date` | `DateString` | `Date` | +| `@db.Time(p)` | `TimeString(p)` | `Time(precision: p)` | +| `@db.Timetz(p)` | `Timetz(p)` | `Time(column: timetz, precision: p)` | -### Combinations left out +**`prisma orm init`** writes the new project's starter model with `createdAt DateTime @default(now()) @onCreate(now())` and `updatedAt temporal.UpdatedAt`. -- **A JavaScript `Date` for a calendar date or a time of day.** A calendar date stored through a moment in time depends on the time zone of the machine that made it. -- **`Temporal.PlainTime` on a `timetz` column.** It would drop the stored UTC offset. -- **`Temporal.PlainDateTime`.** This is a date and a time with no time zone, which is rarely what an application means ("9am wherever the user is"). It stays available as the explicit type `TimestampTemporal`. `DateTime(value: temporal, column: timestamp)` reads a `Temporal.Instant` instead. +**How the tools find the call.** For each column type, one explicit type is flagged `inferred`: the one the convenience default gives. `contract infer` and the Prisma 7 schema reader pick that type. To write the convenience call for an explicit type, the tools find the type-choice row that names it, build the call's arguments from the row, and drop those equal to the defaults. They then check that the call resolves back to the same codec and parameters, and otherwise write the explicit type. Arguments are always written by name. ### Created-at and updated-at -The PostgreSQL target ships two ready-made type aliases (ADR 275): +The PostgreSQL target ships two ready-made type aliases (ADR 275), in a namespace named `temporal` that groups its time aliases; the name does not mean they read Temporal values: - `temporal.CreatedAt` is `DateTime @onCreate(now())`. - `temporal.UpdatedAt` is `DateTime @onCreate(now()) @onUpdate(now())`. -Both are the default `TimestamptzJsDate(3)`. A contributed alias takes no arguments, so a user who wants another kind of value writes the attributes on the convenience call, for example `DateTime(value: string) @onCreate(now()) @onUpdate(now())`, or declares their own alias with that line. `now()` picks its generator from the field's final codec, so it always writes the value that codec expects. +Both are the default `TimestamptzJsDate(3)`. A type alias the target ships takes no arguments, so a user who wants another kind of value writes the attributes on the convenience call, for example `DateTime(value: string) @onCreate(now()) @onUpdate(now())`, or declares their own alias with that line. `now()` picks its generator from the field's final codec, so it always writes the value that codec expects. ## Consequences - A user can write `DateTime`, `Date`, `Time` or `Duration` without knowing PostgreSQL's column types or the Temporal API, and get a working field. A user who needs a specific column type or kind of value says so with an argument, or writes the explicit type. - The contract, the migration planner, `db verify` and the runtime see only explicit types, and nothing below the PSL and TypeScript surfaces changes. - A schema the tools write reads like one a user would write. -- A type constructor still names one codec, as ADR 254 says. A type choice is a second kind of type declaration that resolves to a type constructor. -- ADR 257's suffix rule applies to every codec of a column type that has several. The unsuffixed name of a concept belongs to a convenience type. -- ADR 275's contributed `temporal.*` aliases are built on `DateTime`, and need no variants for other kinds of value. +- A type constructor names one codec, as ADR 254 says. A type choice is a second kind of type declaration that resolves to a type constructor. +- When a column type has several codecs, every explicit type for it names its kind of value in a suffix (ADR 257). The unsuffixed name of a concept belongs to a convenience type. +- The target's `temporal.*` aliases (ADR 275) are built on `DateTime`, and need no variants for other kinds of value. - The editor's hover lists each convenience type's choices and the explicit type each combination becomes. ## Not decided here @@ -236,6 +240,6 @@ Both are the default `TimestamptzJsDate(3)`. A contributed alias takes no argume **Name the convenience types after the Temporal API: `Instant`, `PlainDate`, `PlainTime`.** The names would say exactly what the application reads, but they are a library's vocabulary. A user modelling an application thinks "a birthday" or "when the order was paid". -**A Prisma-owned value for calendar dates, times and durations, or a branded string type.** A class is one more date API to learn and convert from. It does not survive a trip through JSON, and becomes redundant once runtimes ship Temporal. A branded string forces every write, which almost always starts from a plain string, through a helper or a cast. It catches nothing the codec doesn't already refuse when the value is written. +**A Prisma-owned value for calendar dates, times and durations, or a branded string type.** A class is one more date API to learn and convert from. It does not survive a trip through JSON, and it duplicates what Temporal provides. A branded string forces every write, which almost always starts from a plain string, through a helper or a cast. It catches nothing the codec doesn't already refuse when the value is written. -**Infer the lossless string types for columns a `Date` cannot hold exactly.** The string types never lose a digit, but the inferred schema would then differ from what a user writing the same model would write. Infer takes the default instead, and the docs say how to keep microseconds. +**Infer the lossless string types for columns a `Date` cannot hold exactly.** The string types never lose a digit, but the inferred schema would then differ from what a user writing the same model would write. Infer takes the default instead. diff --git a/projects/datetime-convenience-types/slices/05-tools-write-convenience-types/spec.md b/projects/datetime-convenience-types/slices/05-tools-write-convenience-types/spec.md index 70bed8dd6269..598010be4caa 100644 --- a/projects/datetime-convenience-types/slices/05-tools-write-convenience-types/spec.md +++ b/projects/datetime-convenience-types/slices/05-tools-write-convenience-types/spec.md @@ -50,7 +50,7 @@ A Prisma 7 schema read by `prisma7Schema(...)`: `DateTime` becomes `TimestampJsD ## Scope -**In:** the framework helper, print and infer (`psl-print/column-types.ts`, `psl-build/column-types.ts`, `infer-model-blocks.ts`, `infer-default-codec.ts`, `inferred-type-constructors.ts`), the data types' normal form, the marks, the Prisma 7 reader, the aliases, `orm init` templates, `examples/prisma7-adoption` (contract, snapshot, `src/main.ts`, README, tests), every fixture and test pinning today's output, docs (`scalar-types.md`, `gotchas.md`, the contract-prisma7 and contract-ts READMEs, skills), ADR 279 items 8 and 10 marked built, ADR 275's alias line. +**In:** the framework helper, print and infer (`psl-print/column-types.ts`, `psl-build/column-types.ts`, `infer-model-blocks.ts`, `infer-default-codec.ts`, `inferred-type-constructors.ts`), the data types' normal form, the marks, the Prisma 7 reader, the aliases, `orm init` templates, `examples/prisma7-adoption` (contract, snapshot, `src/main.ts`, README, tests), every fixture and test pinning today's output, docs (`scalar-types.md`, `gotchas.md`, the contract-prisma7 and contract-ts READMEs, skills), ADR 275's alias line. ADR 279 already describes this design; it is not edited unless the built behaviour differs from it. **Out:** SQLite, MySQL and MongoDB (TML-3714); regenerating the Supabase pack contract (TML-3417). From 6e7c26b497258746a6a9f5a5a36109369f802560 Mon Sep 17 00:00:00 2001 From: willbot Date: Fri, 23 Oct 2026 11:53:16 +0200 Subject: [PATCH 072/118] TML-3709: ADR 279 says a timestamptz written with another offset reads back in UTC Co-Authored-By: Claude Opus 5.5 Signed-off-by: willbot Signed-off-by: Will Madden --- ...d time types choose an explicit type from their arguments.md | 2 +- projects/datetime-convenience-types/spec.md | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/architecture docs/adrs/ADR 279 - Convenience date and time types choose an explicit type from their arguments.md b/docs/architecture docs/adrs/ADR 279 - Convenience date and time types choose an explicit type from their arguments.md index 86f9cb81a0cd..d4f5e1484b6f 100644 --- a/docs/architecture docs/adrs/ADR 279 - Convenience date and time types choose an explicit type from their arguments.md +++ b/docs/architecture docs/adrs/ADR 279 - Convenience date and time types choose an explicit type from their arguments.md @@ -156,7 +156,7 @@ The defaults are chosen so that the value is right for the concept and needs not - **`DateTime` reads a JavaScript `Date`.** That is the right value for a moment in time, it works everywhere, and it is what Prisma 7 gave. A `Date` holds milliseconds, so the default precision is 3. A user who needs microseconds writes `DateTime(value: temporal)` or `DateTime(value: string)`. - **`Date`, `Time` and `Duration` read plain ISO 8601 strings.** JavaScript has no built-in calendar date, time of day or duration. A `Date` would bring back the wrong-day bug. A Prisma-owned class or branded string is rejected under Alternatives. -A string is the ISO 8601 text of the stored value (ADR 254, "Date and time types"). It reads the same on every server, and a value written in that form reads back unchanged: +A string is the ISO 8601 text of the stored value (ADR 254, "Date and time types"). It reads the same on every server, and a value written in that form reads back unchanged; a `timestamptz` written with another offset reads back as the same instant in UTC: - The `timestamptz` string codec converts any session time zone offset to UTC. - The `timetz` codec keeps the offset stored with the value. - The `interval` string codec reads every `IntervalStyle` PostgreSQL prints. diff --git a/projects/datetime-convenience-types/spec.md b/projects/datetime-convenience-types/spec.md index ca60b5540d86..604938dfc553 100644 --- a/projects/datetime-convenience-types/spec.md +++ b/projects/datetime-convenience-types/spec.md @@ -1,6 +1,6 @@ # Project spec — Convenience date and time types -**Linear:** [Convenience date and time types](https://linear.app/prisma-company/project/convenience-date-and-time-types-6a969ca1c76e) ([TML-3709](https://linear.app/prisma-company/issue/TML-3709)) · **Branch:** `tml-3709-plan-datetime-convenience-types` · **Design record:** [ADR 279 — Convenience date and time types choose an explicit type from their arguments](../../docs/architecture%20docs/adrs/ADR%20279%20-%20Convenience%20date%20and%20time%20types%20choose%20an%20explicit%20type%20from%20their%20arguments.md) (Proposed) +**Linear:** [Convenience date and time types](https://linear.app/prisma-company/project/convenience-date-and-time-types-6a969ca1c76e) ([TML-3709](https://linear.app/prisma-company/issue/TML-3709)) · **Branch:** `tml-3709-plan-datetime-convenience-types` · **Design record:** [ADR 279 — Convenience date and time types choose an explicit type from their arguments](../../docs/architecture%20docs/adrs/ADR%20279%20-%20Convenience%20date%20and%20time%20types%20choose%20an%20explicit%20type%20from%20their%20arguments.md) (Accepted) ## Purpose From f4bcf933351817e55db1b5439c3946bfde4e3afc Mon Sep 17 00:00:00 2001 From: willbot Date: Fri, 23 Oct 2026 11:53:21 +0200 Subject: [PATCH 073/118] ADR 279 says a timestamptz written with another offset reads back in UTC Co-Authored-By: Claude Opus 5.5 Signed-off-by: willbot Signed-off-by: Will Madden --- ...d time types choose an explicit type from their arguments.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/architecture docs/adrs/ADR 279 - Convenience date and time types choose an explicit type from their arguments.md b/docs/architecture docs/adrs/ADR 279 - Convenience date and time types choose an explicit type from their arguments.md index 86f9cb81a0cd..d4f5e1484b6f 100644 --- a/docs/architecture docs/adrs/ADR 279 - Convenience date and time types choose an explicit type from their arguments.md +++ b/docs/architecture docs/adrs/ADR 279 - Convenience date and time types choose an explicit type from their arguments.md @@ -156,7 +156,7 @@ The defaults are chosen so that the value is right for the concept and needs not - **`DateTime` reads a JavaScript `Date`.** That is the right value for a moment in time, it works everywhere, and it is what Prisma 7 gave. A `Date` holds milliseconds, so the default precision is 3. A user who needs microseconds writes `DateTime(value: temporal)` or `DateTime(value: string)`. - **`Date`, `Time` and `Duration` read plain ISO 8601 strings.** JavaScript has no built-in calendar date, time of day or duration. A `Date` would bring back the wrong-day bug. A Prisma-owned class or branded string is rejected under Alternatives. -A string is the ISO 8601 text of the stored value (ADR 254, "Date and time types"). It reads the same on every server, and a value written in that form reads back unchanged: +A string is the ISO 8601 text of the stored value (ADR 254, "Date and time types"). It reads the same on every server, and a value written in that form reads back unchanged; a `timestamptz` written with another offset reads back as the same instant in UTC: - The `timestamptz` string codec converts any session time zone offset to UTC. - The `timetz` codec keeps the offset stored with the value. - The `interval` string codec reads every `IntervalStyle` PostgreSQL prints. From 3c6b36cf34204d2449049afc2c0a7133530e975a Mon Sep 17 00:00:00 2001 From: willbot Date: Fri, 23 Oct 2026 12:12:14 +0200 Subject: [PATCH 074/118] TML-3709: ADR 279 describes convenience types as a general mechanism, with as: and storage: options The record is retitled "Convenience types choose an explicit type from their arguments". It states the mechanism for any target and scalar, and applies it to PostgreSQL's date and time types as the first use. The options are renamed value: to as: and column: to storage:, and the TypeScript helpers take an object. Co-Authored-By: Claude Opus 5.5 Signed-off-by: willbot Signed-off-by: Will Madden --- docs/architecture docs/ADR-INDEX.md | 2 +- ...e an explicit type from their arguments.md | 245 ------------------ ...e an explicit type from their arguments.md | 216 +++++++++++++++ projects/datetime-convenience-types/README.md | 4 +- .../design-notes.md | 4 +- projects/datetime-convenience-types/plan.md | 2 +- .../slices/04-convenience-types/spec.md | 18 +- .../05-tools-write-convenience-types/spec.md | 10 +- projects/datetime-convenience-types/spec.md | 12 +- projects/target-named-scalars-sql/README.md | 2 +- 10 files changed, 243 insertions(+), 272 deletions(-) delete mode 100644 docs/architecture docs/adrs/ADR 279 - Convenience date and time types choose an explicit type from their arguments.md create mode 100644 docs/architecture docs/adrs/ADR 279 - Convenience types choose an explicit type from their arguments.md diff --git a/docs/architecture docs/ADR-INDEX.md b/docs/architecture docs/ADR-INDEX.md index 7c6b75cf10b2..980e16713b1e 100644 --- a/docs/architecture docs/ADR-INDEX.md +++ b/docs/architecture docs/ADR-INDEX.md @@ -79,7 +79,7 @@ Open this list before you propose a new feature or surface, or judge a feature r | 272 | Packages contribute collection methods computed from a model | **Accepted.** A package reads each model of the contract (target, name, fields, relations, storage table) and contributes collection methods under names it chooses; every SQL ORM collection offers them under the reserved member `using`, such as `db.orm.public.Post.using.post_search(q)` and `db.orm.public.Post.using.embedding_nearest(v)`. The type half registers in `CollectionMethodRegistry` by declaration merging through the package's own entry points, never through the emitted `contract.d.ts`; the run-time half goes on a target or extension descriptor. Two contributions that give a model the same name are a compile error and an `ORM.ARGUMENT_INVALID` at `orm()` | [ADR 272 - Packages contribute collection methods computed from a model.md](adrs/ADR%20272%20-%20Packages%20contribute%20collection%20methods%20computed%20from%20a%20model.md) | | 273 | A column of a type no data type claims is a raw column | A column whose database type no data type claims has data type `sql/raw`, whose parameter `sql` is the type exactly as the catalog prints it, and codec `sql/raw@1`, which passes values through typed `unknown`; migrations write the text into DDL and `db verify` compares it exactly; a Prisma 8 field declares one with ``Raw(sql`...`)``, and its default is a `sql` expression; the Prisma 7 reader reads `Unsupported("...")` as a raw column with no field | [ADR 273 - A column of a type no data type claims is a raw column.md](adrs/ADR%20273%20-%20A%20column%20of%20a%20type%20no%20data%20type%20claims%20is%20a%20raw%20column.md) | | 275 | Client generators are field attributes, and a type alias is expanded into each column | **Proposed.** `@onCreate(fn())` and `@onUpdate(fn())` set the client's generators, resolved from the column's codec through a generator function registry the stack assembles, with params from the column's type parameters; `@default` means a database default only; a `types {}` alias may carry `@default`, `@onCreate` and `@onUpdate`, is expanded into each column, and is never stored, so `storage.types` keeps only types a contract space registers; field presets are removed from SQL and Mongo, PSL and the TypeScript builder, and targets contribute aliases such as `temporal.UpdatedAt` | [ADR 275 - Client generators are field attributes, and a type alias is expanded into each column.md](adrs/ADR%20275%20-%20Client%20generators%20are%20field%20attributes%2C%20and%20a%20type%20alias%20is%20expanded%20into%20each%20column.md) | -| 279 | Convenience date and time types choose an explicit type from their arguments | **Proposed.** PostgreSQL date and time types have two layers: the explicit, database-named types (`TimestamptzJsDate`, `TimestamptzTemporal`, `DateString`, …), each fixing one codec, and the convenience types `DateTime`, `Date`, `Time` and `Duration`, named for the application concept, whose `value:` and `column:` arguments choose one explicit type, with defaults that need no `Temporal`; a type constructor may choose its codec from its arguments; the contract stores only the explicit type | [ADR 279 - Convenience date and time types choose an explicit type from their arguments.md](adrs/ADR%20279%20-%20Convenience%20date%20and%20time%20types%20choose%20an%20explicit%20type%20from%20their%20arguments.md) | +| 279 | Convenience types choose an explicit type from their arguments | A target can declare convenience types, named for what a field means, whose `as:` and `storage:` arguments pick one of its explicit, database-named types, with defaults that need no setup; the contract stores only the explicit type, and the tools write the convenience form. PostgreSQL's `DateTime`, `Date`, `Time` and `Duration` are the first | [ADR 279 - Convenience types choose an explicit type from their arguments.md](adrs/ADR%20279%20-%20Convenience%20types%20choose%20an%20explicit%20type%20from%20their%20arguments.md) | ## Query System diff --git a/docs/architecture docs/adrs/ADR 279 - Convenience date and time types choose an explicit type from their arguments.md b/docs/architecture docs/adrs/ADR 279 - Convenience date and time types choose an explicit type from their arguments.md deleted file mode 100644 index d4f5e1484b6f..000000000000 --- a/docs/architecture docs/adrs/ADR 279 - Convenience date and time types choose an explicit type from their arguments.md +++ /dev/null @@ -1,245 +0,0 @@ -# ADR 279 — Convenience date and time types choose an explicit type from their arguments - -**Status:** Accepted -**Builds on:** [ADR 241 — Scalar types use the authoring type-constructor channel](ADR%20241%20-%20Scalar%20types%20use%20the%20authoring%20type-constructor%20channel.md), [ADR 254 — Data types and casts](ADR%20254%20-%20Data%20types%20and%20casts.md), [ADR 257 — Scalar types are named after the target on every surface](ADR%20257%20-%20Scalar%20types%20are%20named%20after%20the%20target%20on%20every%20surface.md) -**Amends:** [ADR 275 — Client generators are field attributes, and a type alias is expanded into each column](ADR%20275%20-%20Client%20generators%20are%20field%20attributes%2C%20and%20a%20type%20alias%20is%20expanded%20into%20each%20column.md) - -## At a glance - -```prisma -model Order { - id Int @id - paidAt DateTime // timestamptz(3), read as a JavaScript Date - shipBy Date // date, read as "2024-02-29" - opensAt Time // time, read as "09:30:00" - period Duration // interval, read as "P1M" - settledAt DateTime(value: temporal) // timestamptz, read as a Temporal.Instant - legacyAt DateTime(column: timestamp) // timestamp(3), read as a Date stored as UTC - exactAt TimestamptzTemporal(6) // the explicit type, written directly -} -``` - -`DateTime`, `Date`, `Time` and `Duration` are **convenience types**. Each is named for what a field means in the application, and each becomes exactly one **explicit type**. An explicit type, such as `TimestamptzJsDate`, is named after the database and sets both the column type and the JavaScript value the application reads. Written bare, a convenience type takes defaults that work on any JavaScript runtime with no setup. Its optional arguments choose any other explicit type. The contract records only the explicit type: `settledAt` is stored exactly as `settledAt TimestamptzTemporal` would be. - -## Decision - -1. **Date and time types have two layers.** The explicit types are named after the database, as ADR 257 requires. Each one uses a single codec, the code that converts between one column type and one kind of JavaScript value, so it fixes both. The convenience types are named after the application concept, and each one becomes exactly one explicit type. -2. **A convenience type is declared as a type choice:** a declaration that picks an explicit type from its arguments. It has: - - option arguments, each with a fixed set of values and a default; - - arguments it hands through to the chosen explicit type, such as `precision`; - - a table from each allowed combination of option values to the name of an explicit type. - - A combination the table does not list is refused. -3. **The contract stores only the explicit type.** A schema written with convenience types and the same schema written with the explicit types they become give the same contract. -4. **The defaults need no setup.** On PostgreSQL: - - `DateTime` defaults to a `timestamptz(3)` column read as a JavaScript `Date`. - - `Date`, `Time` and `Duration` default to ISO 8601 strings. - - No default needs a global `Temporal`. -5. **Tools that generate a schema write convenience types.** `contract infer`, `contract print`, the Prisma 7 schema reader and `prisma orm init` write each date or time column as the convenience call that becomes its explicit type, with only the arguments that differ from the defaults. They write the explicit type only when no convenience call becomes it. - -## Why - -In Prisma 7, every date or time field is `DateTime`, and the application always reads a JavaScript `Date`. A `@db.*` attribute changes the column type and nothing else. That is easy to learn. But a `Date` is a moment in time, so it is the wrong value for a calendar date or a time of day. A birthday created at local midnight on a machine east of UTC is stored as the day before, because that midnight is still the previous day in UTC. - -Prisma 8 replaces the single type with explicit types, each naming both the column type and the value read. A `date` column can be read as a `Temporal.PlainDate` (`DateTemporal`) or as a string (`DateString`), and each is correct for what it is. The cost is that every name fixes two things at once: the column type and the kind of value. So a column type with three kinds of value has three names, and across PostgreSQL's date and time column types there are more than a dozen. Nothing says which one to start with. To choose, a user must already know the difference between `timestamp` and `timestamptz`, what each Temporal type means, and what each kind of value costs. A user writing a model thinks "this field is when the order was paid", not "this is a `timestamptz` column read as a `Temporal.Instant`". - -Prisma 8's approach is to give users explicit, predictable controls first, then make common cases easy by building convenience APIs on top. The client facades are an example. `postgres({ contract, url })` builds the runtime, adapter and driver that a user could otherwise create and connect by hand. The convenience types do the same for dates and times. There is one name per concept, with a default that works, and every explicit choice stays available as an argument or as the explicit type itself. - -## How it works - -### The explicit layer - -An explicit type is a type constructor. It names one codec and fills that codec's parameters (`typeParams`) from its arguments; here the first argument becomes `precision`: - -```ts -TimestamptzTemporal: { - kind: 'typeConstructor', - args: [{ kind: 'value', dataType: PG_SIZE_TYPE, name: 'precision', optional: true }], - output: { - codecId: 'pg/timestamptz-temporal@1', - typeParams: { precision: { kind: 'arg', index: 0 } }, - }, -}, -``` - -When a column type has more than one codec, every explicit type for it carries a suffix naming its kind of value. So no explicit type takes a bare name like `Timestamptz`; the bare concept names belong to the convenience types. `timetz` has one codec, so its explicit type has no suffix. - -| Column | `Date` | `Temporal` | String | Other | -| --- | --- | --- | --- | --- | -| `timestamptz` | `TimestamptzJsDate` | `TimestamptzTemporal` (`Temporal.Instant`) | `TimestamptzString` | | -| `timestamp` | `TimestampJsDate` | `TimestampTemporal` (`Temporal.PlainDateTime`), `TimestampInstant` (`Temporal.Instant`) | `TimestampString` | | -| `date` | | `DateTemporal` (`Temporal.PlainDate`) | `DateString` | | -| `time` | | `TimeTemporal` (`Temporal.PlainTime`) | `TimeString` | | -| `timetz` | | | `Timetz` | | -| `interval` | | | `IntervalString` (an ISO 8601 duration) | `IntervalParts` (`{ months, days, micros }`) | - -`TimestampJsDate` and `TimestampInstant` read and write a `timestamp` column as UTC, so a `timestamp` column can hold the same moment in time as a `timestamptz` column. That matters because `timestamp` is the column Prisma 7's `DateTime` creates. - -`TimestampInstant` names its Temporal type instead of taking the `Temporal` suffix, because the `timestamp` column has two Temporal codecs. - -### A type choice names its explicit types - -A convenience type is a second kind of declaration, a type choice, in the same set of type names as the explicit types. It names no codec. It declares its arguments and a table whose rows each name an explicit type: - -```ts -DateTime: { - kind: 'typeChoice', - args: [ - { kind: 'option', name: 'value', values: ['jsDate', 'temporal', 'string'], default: 'jsDate', optional: true }, - { kind: 'option', name: 'column', values: ['timestamptz', 'timestamp'], default: 'timestamptz', optional: true }, - { kind: 'value', dataType: PG_SIZE_TYPE, name: 'precision', optional: true }, - ], - choices: [ - { when: { value: 'jsDate', column: 'timestamptz' }, type: 'TimestamptzJsDate', defaults: { precision: 3 } }, - { when: { value: 'temporal', column: 'timestamptz' }, type: 'TimestamptzTemporal' }, - { when: { value: 'string', column: 'timestamptz' }, type: 'TimestamptzString' }, - { when: { value: 'jsDate', column: 'timestamp' }, type: 'TimestampJsDate', defaults: { precision: 3 } }, - { when: { value: 'temporal', column: 'timestamp' }, type: 'TimestampInstant' }, - { when: { value: 'string', column: 'timestamp' }, type: 'TimestampString' }, - ], -}, -``` - -The value names repeat the explicit types' suffixes, so `DateTime(value: jsDate)` and `TimestamptzJsDate` read as the same thing. The one exception is `DateTime(value: temporal, column: timestamp)`, which becomes `TimestampInstant`; "Combinations left out" says why. - -Resolving a call takes three steps: -1. Fill in the default for every option the call leaves out. -2. Look up the table row for the resulting combination of values. -3. Instantiate the explicit type that row names, handing through the other arguments. If the row gives a default for an argument the call leaves out, such as `precision: 3` for `TimestamptzJsDate`, that default applies. - -The result is exactly what writing that explicit type would give. Every argument is optional, so a bare `DateTime` is its default row. Arguments are written by name or in declaration order. Because `value` comes first, `Time(3)` reads `3` as the `value` option, and the refusal says to write `Time(precision: 3)`. - -The PostgreSQL target declares four type choices: - -| Convenience type | `value` | `column` | Default | -| --- | --- | --- | --- | -| `DateTime` | `jsDate`, `temporal`, `string` | `timestamptz`, `timestamp` | `jsDate`, `timestamptz`, precision 3 | -| `Date` | `string`, `temporal` | `date` | `string` | -| `Time` | `string`, `temporal` | `time`, `timetz` (with `string` only) | `string`, `time` | -| `Duration` | `string`, `parts` | `interval` | `string` | - -A call is refused at the argument that makes it invalid, and the message lists what that argument accepts. That covers: -- a value an option does not list, such as `Date(value: jsDate)`; -- a combination with no row, such as `Time(value: temporal, column: timetz)`; -- an argument the chosen explicit type does not take, such as `Date(precision: 3)`. - -When a target's type declarations are loaded, a type choice is refused if it: -- names an explicit type the same target does not declare; -- has two rows for one combination; -- lists a value or default its option does not declare; -- gives a row default for an argument the chosen constructor does not take; -- hands through an argument whose data type differs from the chosen constructor's. - -### Combinations left out - -- **A JavaScript `Date` for a calendar date or a time of day.** A calendar date stored through a moment in time depends on the time zone of the machine that made it. -- **`Temporal.PlainTime` on a `timetz` column.** It would drop the stored UTC offset. -- **`Temporal.PlainDateTime`.** This is a date and a time with no time zone, which is rarely what an application means ("9am wherever the user is"). It stays available as the explicit type `TimestampTemporal`. `DateTime(value: temporal, column: timestamp)` reads a `Temporal.Instant` instead. - -### The whole toolchain sees the chosen explicit type - -Every place that needs a field's codec resolves the field's call first, including a call inside a named alias declared in a `types {}` block, so it sees the explicit type's codec: -- the PSL interpreter, which builds the column; -- the `@default` value check; -- the `@onCreate`/`@onUpdate` generator lookup; -- the Prisma 7 schema reader; -- the language server. - -`DateTime @onCreate(now())` therefore gets the generator for `TimestamptzJsDate`, and `DateTime(value: temporal) @onCreate(now())` gets the one for `TimestamptzTemporal`. - -In a TypeScript contract, `type.DateTime(...)`, `type.Date(...)`, `type.Time(...)` and `type.Duration(...)` take the same arguments in the same order. Each has one overload per row, so a call is typed as the explicit type it chooses, and a combination with no row does not compile. - -### The values the defaults read - -The defaults are chosen so that the value is right for the concept and needs nothing installed: -- **`DateTime` reads a JavaScript `Date`.** That is the right value for a moment in time, it works everywhere, and it is what Prisma 7 gave. A `Date` holds milliseconds, so the default precision is 3. A user who needs microseconds writes `DateTime(value: temporal)` or `DateTime(value: string)`. -- **`Date`, `Time` and `Duration` read plain ISO 8601 strings.** JavaScript has no built-in calendar date, time of day or duration. A `Date` would bring back the wrong-day bug. A Prisma-owned class or branded string is rejected under Alternatives. - -A string is the ISO 8601 text of the stored value (ADR 254, "Date and time types"). It reads the same on every server, and a value written in that form reads back unchanged; a `timestamptz` written with another offset reads back as the same instant in UTC: -- The `timestamptz` string codec converts any session time zone offset to UTC. -- The `timetz` codec keeps the offset stored with the value. -- The `interval` string codec reads every `IntervalStyle` PostgreSQL prints. - -Reading dates and times as ISO text needs `DateStyle=ISO`. The PostgreSQL target declares that requirement, and the PostgreSQL driver ensures every connection it hands to the runtime meets it. This record does not decide how the driver does it. If a codec receives text in another style, it fails with an error that names `DateStyle`. - -The Temporal codecs take their TypeScript types from `temporal-spec`, a package of declarations with no runtime code. A Temporal field is then typed precisely in a project that has no global `Temporal` declarations. No polyfill reaches an application that does not choose a Temporal value. - -### What the tools write - -`contract infer` reads a database, and `contract print` reads a contract. Neither has a record of which layer the user wrote. Both write the convenience type whenever one becomes the column's explicit type, with only the arguments that differ from its defaults. For each column, they pick the explicit type the convenience default gives, so the schema they write is the one a user would write. The table shows what they write; the last row is the explicit fallback. - -| Column | Written as | Explicit type it becomes | -| --- | --- | --- | -| `timestamptz(3)` | `DateTime` | `TimestamptzJsDate(3)` | -| `timestamptz` (no declared precision) | `DateTime(precision: 6)` | `TimestamptzJsDate(6)` | -| `timestamp(3)` | `DateTime(column: timestamp)` | `TimestampJsDate(3)` | -| `date` | `Date` | `DateString` | -| `time(0)` | `Time(precision: 0)` | `TimeString(0)` | -| `timetz` | `Time(column: timetz)` | `Timetz` | -| `interval` | `Duration` | `IntervalString` | -| a `timestamp` read as `Temporal.PlainDateTime` | `TimestampTemporal` | `TimestampTemporal` | - -**A missing precision.** PostgreSQL stores microseconds whether a precision of 6 is declared or omitted. So when Prisma compares a column with the contract, an omitted precision on a date or time column counts as 6 (ADR 254 calls this the data type's normal form). A column with no declared precision is written as `DateTime(precision: 6)`, and `db verify` and the migration planner see no difference. - -A `Date` holds milliseconds, so a column with more digits loses them when read through `DateTime`. The infer documentation says to write `DateTime(value: temporal)` or `DateTime(value: string)` to keep them. - -**The Prisma 7 schema reader** (`prisma7Schema`, which turns a Prisma 7 `schema.prisma` into a Prisma 8 contract) picks the same explicit types for the same columns: - -| Prisma 7 field | Explicit type | Printed as | -| --- | --- | --- | -| `DateTime` | `TimestampJsDate(3)`: a `timestamp(3)` column read as a `Date`, as Prisma 7 read it | `DateTime(column: timestamp)` | -| `@db.Timestamptz(p)` | `TimestamptzJsDate(p)` | `DateTime(precision: p)` | -| `@db.Date` | `DateString` | `Date` | -| `@db.Time(p)` | `TimeString(p)` | `Time(precision: p)` | -| `@db.Timetz(p)` | `Timetz(p)` | `Time(column: timetz, precision: p)` | - -**`prisma orm init`** writes the new project's starter model with `createdAt DateTime @default(now()) @onCreate(now())` and `updatedAt temporal.UpdatedAt`. - -**How the tools find the call.** For each column type, one explicit type is flagged `inferred`: the one the convenience default gives. `contract infer` and the Prisma 7 schema reader pick that type. To write the convenience call for an explicit type, the tools find the type-choice row that names it, build the call's arguments from the row, and drop those equal to the defaults. They then check that the call resolves back to the same codec and parameters, and otherwise write the explicit type. Arguments are always written by name. - -### Created-at and updated-at - -The PostgreSQL target ships two ready-made type aliases (ADR 275), in a namespace named `temporal` that groups its time aliases; the name does not mean they read Temporal values: -- `temporal.CreatedAt` is `DateTime @onCreate(now())`. -- `temporal.UpdatedAt` is `DateTime @onCreate(now()) @onUpdate(now())`. - -Both are the default `TimestamptzJsDate(3)`. A type alias the target ships takes no arguments, so a user who wants another kind of value writes the attributes on the convenience call, for example `DateTime(value: string) @onCreate(now()) @onUpdate(now())`, or declares their own alias with that line. `now()` picks its generator from the field's final codec, so it always writes the value that codec expects. - -## Consequences - -- A user can write `DateTime`, `Date`, `Time` or `Duration` without knowing PostgreSQL's column types or the Temporal API, and get a working field. A user who needs a specific column type or kind of value says so with an argument, or writes the explicit type. -- The contract, the migration planner, `db verify` and the runtime see only explicit types, and nothing below the PSL and TypeScript surfaces changes. -- A schema the tools write reads like one a user would write. -- A type constructor names one codec, as ADR 254 says. A type choice is a second kind of type declaration that resolves to a type constructor. -- When a column type has several codecs, every explicit type for it names its kind of value in a suffix (ADR 257). The unsuffixed name of a concept belongs to a convenience type. -- The target's `temporal.*` aliases (ADR 275) are built on `DateTime`, and need no variants for other kinds of value. -- The editor's hover lists each convenience type's choices and the explicit type each combination becomes. - -## Not decided here - -- **SQLite, MySQL and MongoDB.** A convenience name must mean the same thing on every target. Each of these targets declares its own type choices over its own explicit types, and each already uses one of the names for an explicit type: - - SQLite's `DateTime` is `sqlite/datetime@1`. - - MySQL's `DateTime` is a `datetime(3)` column read as a `Date`. - - MongoDB's `Date` is `mongo/date@1`, a moment in time. MongoDB's explicit `Date` therefore needs another name before a convenience `Date` can mean a calendar date there. -- **Whether other scalar types get convenience types,** for example an `Int` whose arguments choose between `Int2`, `Int4` and `Int8`. - -## Alternatives considered - -**Name every type after the application's meaning, with no explicit types.** Every field would be written `DateTime`, `Date` or `Time` and refined by arguments, and the database-named types would go. That reverses ADR 257 and leaves arguments as the only way to state a column type exactly. Two layers keep the database-named types as the precise form and put the short form on top. - -**One codec for `DateTime`, with the column type as a parameter.** A codec converts values of exactly one data type, and a column stores the data type its codec represents (ADR 254). A codec whose data type depends on a parameter breaks both rules. Choosing among existing codecs gives the same schema without changing codecs. - -**A type constructor whose codec is a select template over option arguments** (ADR 246). A select picks a value by case, but it has no default case and no way to refuse a combination. A bare `DateTime` would resolve to no codec, and `Time(value: temporal, column: timetz)` could not be refused. A type choice that names explicit types has defaults and refusals by construction. - -**Convenience types as contributed type aliases with arguments.** A type alias attaches attributes such as `@onCreate` to one fixed type (ADR 275). Choosing which type a field has is a different job. - -**Keep only the explicit types and document which to choose.** It adds nothing to learn, but the schema offers no default, so every user must understand every choice before writing their first field. - -**Leave the Temporal-valued explicit types unsuffixed.** That changes fewer names, but `Timestamptz` would look like the neutral choice when it reads one particular kind of value. The explicit types would also follow two naming patterns. - -**Name the convenience types after the Temporal API: `Instant`, `PlainDate`, `PlainTime`.** The names would say exactly what the application reads, but they are a library's vocabulary. A user modelling an application thinks "a birthday" or "when the order was paid". - -**A Prisma-owned value for calendar dates, times and durations, or a branded string type.** A class is one more date API to learn and convert from. It does not survive a trip through JSON, and it duplicates what Temporal provides. A branded string forces every write, which almost always starts from a plain string, through a helper or a cast. It catches nothing the codec doesn't already refuse when the value is written. - -**Infer the lossless string types for columns a `Date` cannot hold exactly.** The string types never lose a digit, but the inferred schema would then differ from what a user writing the same model would write. Infer takes the default instead. diff --git a/docs/architecture docs/adrs/ADR 279 - Convenience types choose an explicit type from their arguments.md b/docs/architecture docs/adrs/ADR 279 - Convenience types choose an explicit type from their arguments.md new file mode 100644 index 000000000000..0dc2859b2a2f --- /dev/null +++ b/docs/architecture docs/adrs/ADR 279 - Convenience types choose an explicit type from their arguments.md @@ -0,0 +1,216 @@ +# ADR 279 — Convenience types choose an explicit type from their arguments + +**Status:** Accepted +**Builds on:** [ADR 241 — Scalar types use the authoring type-constructor channel](ADR%20241%20-%20Scalar%20types%20use%20the%20authoring%20type-constructor%20channel.md), [ADR 254 — Data types and casts](ADR%20254%20-%20Data%20types%20and%20casts.md), [ADR 257 — Scalar types are named after the target on every surface](ADR%20257%20-%20Scalar%20types%20are%20named%20after%20the%20target%20on%20every%20surface.md) +**Amends:** [ADR 275 — Client generators are field attributes, and a type alias is expanded into each column](ADR%20275%20-%20Client%20generators%20are%20field%20attributes%2C%20and%20a%20type%20alias%20is%20expanded%20into%20each%20column.md) + +## At a glance + +```prisma +model Order { + id Int @id + paidAt DateTime // exactly TimestamptzJsDate(3) + settledAt DateTime(as: temporal) // exactly TimestamptzTemporal + legacyAt DateTime(storage: timestamp) // exactly TimestampJsDate(3) + shipBy Date // exactly DateString + exactAt TimestamptzTemporal(6) // an explicit type, written directly +} +``` + +`TimestamptzJsDate(3)` says everything about a field's type: the database stores it in a `timestamptz(3)` column, and the application reads it as a JavaScript `Date`. `DateTime` is a shorter way to write a type like that. With no arguments, `paidAt DateTime` means exactly `paidAt TimestamptzJsDate(3)`. Its argument `as:` picks a different value for the application to read, and `storage:` a different database type, and each combination means exactly one type of the first kind. The contract records only that type, so `contract.json` is the same whichever way the field was written. + +## Decision + +A target or an extension can declare two kinds of type for PSL and the TypeScript builder. + +- **An explicit type** fixes everything: it names one codec, the code that converts between one database type and one kind of application value. Its name says both, in the database's words: `TimestamptzJsDate` is a `timestamptz` read as a `Date`. +- **A convenience type** is named for what a field means in the application, such as `DateTime` or `Date`. It names no codec. Its arguments pick one of the same target's explicit types from a table, and every argument has a default, so the name alone is a working type. + +Five rules follow: + +1. **A convenience call means exactly the explicit type it picks.** The contract stores that explicit type, so the contract, the migration planner, `db verify` and the runtime never see a convenience type. +2. **Its defaults need no setup.** The type a convenience name gives with no arguments works on any JavaScript runtime with nothing installed. +3. **Every explicit choice stays available,** as an argument of the convenience type or by writing the explicit type itself. +4. **A combination the table does not list is refused,** at the argument that makes it invalid. +5. **Tools that write a schema write the convenience type** wherever one gives the column's explicit type, with only the arguments that differ from the defaults. + +The first convenience types are PostgreSQL's `DateTime`, `Date`, `Time` and `Duration`, described below. Convenience types for other scalars or other targets use the same mechanism and are decided on their own. + +## Why + +Prisma 7 gave each kind of field one type name, such as `DateTime`, and read every field of that type as the same kind of value. That was easy to learn, but for dates it was wrong: a `Date` is a moment in time, so it is the wrong value for a calendar date. A birthday created at local midnight on a machine east of UTC is stored as the day before, because that midnight is still the previous day in UTC. + +Prisma 8 names each type after what the database stores and what the application reads (ADR 257). That makes every choice visible and correct, but a database type that can be read as three kinds of value has three names, and nothing says which to start with. PostgreSQL's date and time types alone have more than a dozen. A user writing a model thinks "this field is when the order was paid", not "a `timestamptz` column read as a `Temporal.Instant`". + +Prisma 8's approach is to give explicit, predictable controls first and to build convenience on top of them. The client facades are an example: `postgres({ contract, url })` builds the runtime, adapter and driver a user could otherwise create and connect by hand. Convenience types do the same for field types: one name per concept, with a default that works, over explicit types that stay available. + +## How it works + +### Explicit types + +An explicit type is a type constructor. It names one codec and fills the codec's parameters from its arguments: + +```ts +TimestamptzTemporal: { + kind: 'typeConstructor', + args: [{ kind: 'value', dataType: PG_SIZE_TYPE, name: 'precision', optional: true }], + output: { + codecId: 'pg/timestamptz-temporal@1', + typeParams: { precision: { kind: 'arg', index: 0 } }, + }, +}, +``` + +Its name is the database type, plus a suffix naming the application value when the database type has more than one codec: `TimestamptzJsDate`, `TimestamptzTemporal` and `TimestamptzString` are three readings of `timestamptz`. Every such reading carries a suffix, so no explicit type takes a bare name that looks like the obvious choice. Bare concept names are left for convenience types. + +### Convenience types + +A convenience type is declared as a **type choice**. It lists its arguments and a table whose rows each name an explicit type of the same target: + +```ts +DateTime: { + kind: 'typeChoice', + args: [ + { kind: 'option', name: 'as', values: ['jsDate', 'temporal', 'string'], default: 'jsDate', optional: true }, + { kind: 'option', name: 'storage', values: ['timestamptz', 'timestamp'], default: 'timestamptz', optional: true }, + { kind: 'value', dataType: PG_SIZE_TYPE, name: 'precision', optional: true }, + ], + choices: [ + { when: { as: 'jsDate', storage: 'timestamptz' }, type: 'TimestamptzJsDate', defaults: { precision: 3 } }, + { when: { as: 'temporal', storage: 'timestamptz' }, type: 'TimestamptzTemporal' }, + { when: { as: 'string', storage: 'timestamptz' }, type: 'TimestamptzString' }, + { when: { as: 'jsDate', storage: 'timestamp' }, type: 'TimestampJsDate', defaults: { precision: 3 } }, + { when: { as: 'temporal', storage: 'timestamp' }, type: 'TimestampInstant' }, + { when: { as: 'string', storage: 'timestamp' }, type: 'TimestampString' }, + ], +}, +``` + +There are two kinds of argument: +- **Options** pick the row. Each has a fixed set of values, written as bare names, and a default. By convention `as:` picks the value the application reads and `storage:` the database type that stores it. +- **Other arguments**, such as `precision`, are handed to the explicit type the row names. + +A call is resolved in three steps: +1. Fill in the default of every option the call leaves out. A call with no arguments is the default row. +2. Find the row for that combination of option values. +3. Build the explicit type the row names with the other arguments. A row may give a default for an argument the call leaves out, as `precision: 3` above. + +The result is exactly what writing that explicit type would give. + +Arguments are written by name or in the order they are declared. A call is refused at the argument that makes it invalid, and the message lists what that argument accepts: +- a value an option does not list, such as `Date(as: jsDate)`; +- a combination with no row, such as `Time(as: temporal, storage: timetz)`; +- an argument the chosen explicit type does not take, such as `Date(precision: 3)`. + +A type choice is checked when the target's types are loaded. It is refused if it names an explicit type the target does not declare, has two rows for one combination, lists a value or default its option does not declare, gives a row default for an argument the chosen type does not take, or hands on an argument whose data type differs from the chosen type's. + +In a TypeScript contract, a convenience type's helper takes its arguments as one object, `type.DateTime({ as: 'string', precision: 6 })`. It has one overload per row, so a call is typed as the explicit type it picks, and a combination with no row does not compile. + +### Everything that needs a field's codec sees the explicit type + +Each place that needs a field's codec resolves the field's call first, including a call inside a type alias declared in a `types {}` block: the PSL interpreter, the `@default` value check, the `@onCreate`/`@onUpdate` generator lookup, the Prisma 7 schema reader and the language server. So `DateTime @onCreate(now())` gets the `now()` generator for `TimestamptzJsDate`, and `DateTime(as: temporal) @onCreate(now())` the one for `TimestamptzTemporal`. + +### What the tools write + +`contract infer` reads a database and `contract print` reads a contract, and neither knows which kind of type the user wrote. Both write a convenience call wherever one gives the column's explicit type, and the explicit type otherwise: +1. For each database type, one explicit type is marked `inferred`: the one its convenience default gives. Infer and the Prisma 7 schema reader pick that type for a column. +2. To write the call for an explicit type, the tools find the row that names it, build the arguments from the row, and drop those equal to the defaults. +3. They resolve the call again and keep it only if it gives the same codec and parameters. + +Arguments are always written by name. So the schema the tools write is the one a user would write. + +## The PostgreSQL date and time types + +PostgreSQL's date and time types are the first to get convenience types. + +### The explicit types + +| Database type | `Date` | Temporal | String | Other | +| --- | --- | --- | --- | --- | +| `timestamptz` | `TimestamptzJsDate` | `TimestamptzTemporal` (`Temporal.Instant`) | `TimestamptzString` | | +| `timestamp` | `TimestampJsDate` | `TimestampTemporal` (`Temporal.PlainDateTime`), `TimestampInstant` (`Temporal.Instant`) | `TimestampString` | | +| `date` | | `DateTemporal` (`Temporal.PlainDate`) | `DateString` | | +| `time` | | `TimeTemporal` (`Temporal.PlainTime`) | `TimeString` | | +| `timetz` | | | `Timetz` | | +| `interval` | | | `IntervalString` (an ISO 8601 duration) | `IntervalParts` (`{ months, days, micros }`) | + +`timetz` has one codec, so its explicit type has no suffix. `TimestampJsDate` and `TimestampInstant` read and write a `timestamp` column as UTC, so it holds the same moment in time a `timestamptz` would; `timestamp` is the column Prisma 7's `DateTime` creates. `TimestampInstant` names its Temporal type because `timestamp` has two Temporal codecs. + +### The convenience types + +| Convenience type | `as` | `storage` | With no arguments | +| --- | --- | --- | --- | +| `DateTime` | `jsDate`, `temporal`, `string` | `timestamptz`, `timestamp` | `TimestamptzJsDate(3)` | +| `Date` | `string`, `temporal` | `date` | `DateString` | +| `Time` | `string`, `temporal` | `time`, `timetz` (with `string` only) | `TimeString` | +| `Duration` | `string`, `parts` | `interval` | `IntervalString` | + +The `as` values repeat the explicit types' suffixes, so `DateTime(as: temporal)` and `TimestamptzTemporal` read as the same thing. + +The defaults are the right value for each concept, with nothing to install: +- **`DateTime` reads a JavaScript `Date`.** It is the right value for a moment in time, it works everywhere, and it is what Prisma 7 gave. A `Date` holds milliseconds, so the default precision is 3; `as: temporal` or `as: string` keeps microseconds. +- **`Date`, `Time` and `Duration` read ISO 8601 strings,** such as `"2024-02-29"`. JavaScript has no built-in calendar date, time of day or duration, and a `Date` would bring back the wrong-day bug. The text is the stored form ADR 254 defines, the same on every server. + +A Temporal value is opt-in. The Temporal codecs take their TypeScript types from `temporal-spec`, a package of declarations with no runtime code, so no polyfill reaches an application that does not choose one. + +Three combinations are left out: +- **A `Date` for a calendar date or a time of day,** which would depend on the time zone of the machine that made it. +- **`Temporal.PlainTime` on a `timetz` column,** which would drop the stored offset. +- **`Temporal.PlainDateTime`,** a date and time with no time zone, which is rarely what an application means. It stays available as the explicit type `TimestampTemporal`, and `DateTime(as: temporal, storage: timestamp)` reads a `Temporal.Instant` instead. + +### What the tools write for them + +| Database column | Written as | Explicit type | +| --- | --- | --- | +| `timestamptz(3)` | `DateTime` | `TimestamptzJsDate(3)` | +| `timestamptz`, no declared precision | `DateTime(precision: 6)` | `TimestamptzJsDate(6)` | +| `timestamp(3)` | `DateTime(storage: timestamp)` | `TimestampJsDate(3)` | +| `date` | `Date` | `DateString` | +| `time(0)` | `Time(precision: 0)` | `TimeString(0)` | +| `timetz` | `Time(storage: timetz)` | `Timetz` | +| `interval` | `Duration` | `IntervalString` | +| a `timestamp` read as `Temporal.PlainDateTime` | `TimestampTemporal` | `TimestampTemporal` | + +PostgreSQL stores microseconds whether a precision of 6 is declared or omitted, so comparing columns treats an omitted precision as 6 (ADR 254). That is why a column with no declared precision can be written as `DateTime(precision: 6)` and still verify clean. + +The Prisma 7 schema reader picks the same types: Prisma 7's `DateTime` becomes `TimestampJsDate(3)`, written `DateTime(storage: timestamp)`, so an adopted application reads `Date` values as it did; `@db.Timestamptz(p)`, `@db.Date`, `@db.Time(p)` and `@db.Timetz(p)` become `DateTime(precision: p)`, `Date`, `Time(precision: p)` and `Time(storage: timetz, precision: p)`. `prisma orm init` writes `createdAt DateTime @default(now()) @onCreate(now())` and `updatedAt temporal.UpdatedAt`. + +### Created-at and updated-at + +The PostgreSQL target ships two type aliases (ADR 275), in the namespace `temporal` that holds its time aliases: `temporal.CreatedAt` is `DateTime @onCreate(now())` and `temporal.UpdatedAt` is `DateTime @onCreate(now()) @onUpdate(now())`. A shipped alias takes no arguments, so a user who wants another value writes the attributes on the call, such as `DateTime(as: string) @onCreate(now()) @onUpdate(now())`. `now()` picks its generator from the field's codec, so it writes the value that codec expects. + +## Consequences + +- A user can write a field's type by what it means and get a working field, without knowing the database's types or which libraries a value needs. A user who needs a particular database type or value says so with an argument, or writes the explicit type. +- The contract and everything that reads it see only explicit types, so nothing below PSL and the TypeScript builder changes. +- A type constructor still names exactly one codec (ADR 254). A type choice is a second kind of type declaration, which resolves to a type constructor. +- Every explicit type of a database type with several codecs carries a suffix for its value (ADR 257); bare concept names belong to convenience types. +- The `temporal.*` aliases (ADR 275) are built on `DateTime` and need no variants for other values. +- The editor's hover lists a convenience type's options and the explicit type each combination gives. + +## Not decided here + +- **Other targets.** A convenience name must mean the same thing on every target. SQLite, MySQL and MongoDB each already use one of these names for an explicit type (SQLite's and MySQL's `DateTime`, MongoDB's `Date`, which is a moment in time), so each needs its own decision. +- **Other scalars,** for example an `Int` whose `storage:` picks `Int2`, `Int4` or `Int8`. + +## Alternatives considered + +**Only one kind of type, named after the application's meaning.** Every field would be written `DateTime`, `Date` and so on, refined by arguments, and the database-named types would go. That reverses ADR 257 and leaves arguments as the only way to state a type exactly. + +**Keep only the explicit types and document which to choose.** Nothing new to learn, but no default: every user must understand every choice before writing a first field. + +**One codec per concept, with the database type as a parameter.** A codec converts values of exactly one data type (ADR 254); a codec whose data type depends on a parameter breaks that. Picking among existing codecs gives the same schema. + +**A type constructor whose codec is a select over option arguments** (ADR 246). A select has no default case and no way to refuse a combination, so a bare `DateTime` would resolve to nothing. + +**Type aliases with arguments.** An alias attaches attributes such as `@onCreate` to one fixed type (ADR 275); picking which type a field has is a different job. + +**Options named `value:` and `column:`, or `jsType:` and `dbType:`.** "Value" already means a data type's stored value (ADR 254), and `column:` is a SQL word where a convenience name must mean the same on every target. `jsType:` and `dbType:` are exact but read as jargon. `as:` reads as a user would say it, "a `DateTime` read as Temporal", and `storage:` is the contract's own word for how a field maps to the database. + +**Leave the Temporal explicit types unsuffixed.** Fewer renames, but `Timestamptz` would look like the neutral choice while reading one particular kind of value, and the explicit types would follow two naming patterns. + +**Name the date and time convenience types after the Temporal API: `Instant`, `PlainDate`, `PlainTime`.** Exact, but a library's vocabulary; a user modelling an application thinks "a birthday" or "when the order was paid". + +**A Prisma-owned value for calendar dates, times and durations, or a branded string.** A class is one more date API, does not survive JSON, and duplicates Temporal. A branded string forces every write through a helper or a cast and catches nothing the codec doesn't. + +**Infer the string types for columns a `Date` cannot hold exactly.** Nothing is lost, but the schema would differ from what a user would write for the same model; infer takes the default. diff --git a/projects/datetime-convenience-types/README.md b/projects/datetime-convenience-types/README.md index c45f16f74c9f..643b12a0c75d 100644 --- a/projects/datetime-convenience-types/README.md +++ b/projects/datetime-convenience-types/README.md @@ -1,12 +1,12 @@ # Convenience date and time types -Project folder for [Convenience date and time types](https://linear.app/prisma-company/project/convenience-date-and-time-types-6a969ca1c76e). PSL gets `DateTime`, `Date`, `Time` and `Duration`, one type per concept in the application, each choosing one of PostgreSQL's explicit date and time types from its `value:` and `column:` arguments, with defaults that need no Temporal polyfill. +Project folder for [Convenience date and time types](https://linear.app/prisma-company/project/convenience-date-and-time-types-6a969ca1c76e). PSL gets `DateTime`, `Date`, `Time` and `Duration`, one type per concept in the application, each choosing one of PostgreSQL's explicit date and time types from its `as:` and `storage:` arguments, with defaults that need no Temporal polyfill. - [`spec.md`](./spec.md) — purpose, settled decisions, non-goals, cross-cutting requirements, project DoD. - [`design-notes.md`](./design-notes.md) — the design discussion of 2026-10-21 and 2026-10-22, with the alternatives rejected. - [`plan.md`](./plan.md) — slice plan. - `slices//` — per-slice spec, plan and reviews. -The decision record is [ADR 279](../../docs/architecture%20docs/adrs/ADR%20279%20-%20Convenience%20date%20and%20time%20types%20choose%20an%20explicit%20type%20from%20their%20arguments.md). +The decision record is [ADR 279](../../docs/architecture%20docs/adrs/ADR%20279%20-%20Convenience%20types%20choose%20an%20explicit%20type%20from%20their%20arguments.md). Everything in this folder is transient. At close-out the durable parts move to `docs/` and the ADR, and the folder is deleted. diff --git a/projects/datetime-convenience-types/design-notes.md b/projects/datetime-convenience-types/design-notes.md index f3ff610c0ec1..ddc9bc11c81d 100644 --- a/projects/datetime-convenience-types/design-notes.md +++ b/projects/datetime-convenience-types/design-notes.md @@ -31,13 +31,13 @@ Record of the design discussion between the operator and the orchestrator on 202 **Plain strings, not a Prisma value class or a branded string.** A class owned by Prisma was rejected: users would learn another date API, it would not survive JSON across a server-client boundary, and Temporal will make it redundant. A branded string (`string & { __brand }`) was rejected: almost every written value starts as a plain string from a form or a request, so every write would need a helper or a cast, to catch a mistake the codec already reports at runtime. -**`Duration` defaults to an ISO duration string.** Today's `{ months, days, micros: bigint }` breaks `JSON.stringify`, and the contract already stores interval defaults as ISO durations. The object stays available as `value: parts`. +**`Duration` defaults to an ISO duration string.** Today's `{ months, days, micros: bigint }` breaks `JSON.stringify`, and the contract already stores interval defaults as ISO durations. The object stays available as `as: parts`. **`@db.*` does not come back.** Storage options are arguments in the type position, which agrees with ADR 241. **`contract infer` and `contract print` write convenience types,** with arguments only where they differ from the defaults, so a schema the tools write reads like one a user would write. Only `Temporal.PlainDateTime`, which no convenience type covers, prints as its explicit type. -**The Prisma 7 reader gives back `Date`.** Prisma 7's `DateTime` is a `timestamp(3)` column read as a `Date`. Mapping it to `DateTime(column: timestamp)` keeps an upgraded app's code compiling. This reverses the choice made when inferred and Prisma 7 columns moved to string types, which rejected `Date` only because no `timestamp` `Date` codec existed. +**The Prisma 7 reader gives back `Date`.** Prisma 7's `DateTime` is a `timestamp(3)` column read as a `Date`. Mapping it to `DateTime(storage: timestamp)` keeps an upgraded app's code compiling. This reverses the choice made when inferred and Prisma 7 columns moved to string types, which rejected `Date` only because no `timestamp` `Date` codec existed. **TypeScript follows the `type.*` helpers.** The field-preset removal project gives every PSL type constructor a `type.*` helper. This project makes the builder's compile-time resolver evaluate a codec chosen by arguments, so `type.DateTime(...)` types precisely once the helpers land, instead of building a separate TypeScript surface now. diff --git a/projects/datetime-convenience-types/plan.md b/projects/datetime-convenience-types/plan.md index d8855e22b561..a880bc868de7 100644 --- a/projects/datetime-convenience-types/plan.md +++ b/projects/datetime-convenience-types/plan.md @@ -36,7 +36,7 @@ A type constructor's `codecId` may be a `select` over `option` arguments; every ### Slice 5 — The tools write convenience types -`contract infer` and `contract print` write convenience types, with arguments only where they differ from the defaults, and the explicit type only where no convenience type fits. The Prisma 7 reader maps `DateTime` to `DateTime(column: timestamp)` and `@db.*` date and time columns to the matching convenience form. `prisma orm init` writes `DateTime`. The contributed `temporal.CreatedAt` and `temporal.UpdatedAt` aliases are rebuilt on `DateTime` (`DateTime @onCreate(now())`, and `@onUpdate(now())` for the second), and their `String` and `JsDate` variants are removed, per ADR 279; contracts that use them change codec, so the upgrade instruction says to re-emit and `db sign`. Upgrade instructions: re-emit and `db sign` for adopted projects. Update slice 2's upgrade instruction (`upgrade-instructions/pending/prisma7-now-default-on-create/`), which names the type the reader picks today (`TimestampString(3)`). +`contract infer` and `contract print` write convenience types, with arguments only where they differ from the defaults, and the explicit type only where no convenience type fits. The Prisma 7 reader maps `DateTime` to `DateTime(storage: timestamp)` and `@db.*` date and time columns to the matching convenience form. `prisma orm init` writes `DateTime`. The contributed `temporal.CreatedAt` and `temporal.UpdatedAt` aliases are rebuilt on `DateTime` (`DateTime @onCreate(now())`, and `@onUpdate(now())` for the second), and their `String` and `JsDate` variants are removed, per ADR 279; contracts that use them change codec, so the upgrade instruction says to re-emit and `db sign`. Upgrade instructions: re-emit and `db sign` for adopted projects. Update slice 2's upgrade instruction (`upgrade-instructions/pending/prisma7-now-default-on-create/`), which names the type the reader picks today (`TimestampString(3)`). ## Cross-slice notes diff --git a/projects/datetime-convenience-types/slices/04-convenience-types/spec.md b/projects/datetime-convenience-types/slices/04-convenience-types/spec.md index 0d44697f7848..be848506e868 100644 --- a/projects/datetime-convenience-types/slices/04-convenience-types/spec.md +++ b/projects/datetime-convenience-types/slices/04-convenience-types/spec.md @@ -8,23 +8,23 @@ model Order { id Int @id paidAt DateTime // TimestamptzJsDate(3): timestamptz(3), a JavaScript Date - settledAt DateTime(value: temporal) // TimestamptzTemporal: timestamptz, Temporal.Instant - legacyAt DateTime(column: timestamp) // TimestampJsDate(3): timestamp(3), a Date stored as UTC - textAt DateTime(value: string, precision: 6) // TimestamptzString(6) + settledAt DateTime(as: temporal) // TimestamptzTemporal: timestamptz, Temporal.Instant + legacyAt DateTime(storage: timestamp) // TimestampJsDate(3): timestamp(3), a Date stored as UTC + textAt DateTime(as: string, precision: 6) // TimestamptzString(6) shipBy Date // DateString: date, "2024-02-29" opensAt Time(precision: 0) // TimeString(0): time(0), "09:30:00" - localAt Time(column: timetz) // Timetz: timetz, "09:30:00+02:00" + localAt Time(storage: timetz) // Timetz: timetz, "09:30:00+02:00" period Duration // IntervalString: interval, "P1M" - parts Duration(value: parts) // IntervalParts: interval, { months, days, micros } + parts Duration(as: parts) // IntervalParts: interval, { months, days, micros } updatedAt DateTime @onCreate(now()) @onUpdate(now()) // generator chosen for TimestamptzJsDate } ``` -`Time(value: temporal, column: timetz)` and `Date(value: jsDate)` are refused at the argument, naming the accepted choices. The contract stores only the explicit type each field becomes. +`Time(as: temporal, storage: timetz)` and `Date(as: jsDate)` are refused at the argument, naming the accepted choices. The contract stores only the explicit type each field becomes. ## Chosen design -1. **A convenience type is a new authoring descriptor kind, a type choice.** It declares its arguments (option arguments with a fixed set of bare-name values and a default each, plus value arguments passed through such as `precision`) and a table from each allowed combination of option values to the **name of an explicit type constructor** in the same namespace, with any parameter defaults for that combination (for example precision 3 when `value: jsDate`). Resolving a call looks up the combination, then instantiates the named explicit constructor with the passed-through arguments. A combination missing from the table is refused at the argument that makes it invalid, listing the values that would be accepted. +1. **A convenience type is a new authoring descriptor kind, a type choice.** It declares its arguments (option arguments with a fixed set of bare-name values and a default each, plus value arguments passed through such as `precision`) and a table from each allowed combination of option values to the **name of an explicit type constructor** in the same namespace, with any parameter defaults for that combination (for example precision 3 when `as: jsDate`). Resolving a call looks up the combination, then instantiates the named explicit constructor with the passed-through arguments. A combination missing from the table is refused at the argument that makes it invalid, listing the values that would be accepted. - Rationale: ADR 279 says each convenience type becomes exactly one explicit type; naming the explicit constructor makes that literal, supports defaults and refusals directly, and needs none of the field-preset `option`/`select` template machinery that the field-preset removal project deletes. ADR 279's "How it works" is updated to this shape (an orchestrator representation decision). 2. **Every reader resolves the field's call.** The PSL column resolution, the TypeScript builder's runtime and type-level resolution (`type.*` helpers), the contract-prisma7 values path, and every place that today reads a constructor's fixed `output.codecId` without the call's arguments: `@default` literal admission (`receivingDataType`) and the `@onCreate`/`@onUpdate` generator lookup (`generatorCallArm`) must use the codec the field's call resolves to, including through a `types {}` alias. The control stack checks every reachable explicit constructor at assembly. 3. **Bare names are scalars.** `DateTime`, `Date`, `Time` and `Duration` work bare and called; the scalar view treats them like any all-optional constructor. @@ -51,10 +51,10 @@ One PR: the descriptor kind and its resolution in every reader exist only to car | --- | --- | | Field typed by a `types {}` alias whose right-hand side is a convenience call | Resolves through the alias; `@default` and `@onCreate` use the resolved codec | | `DateTime[]`, `DateTime?` | Same resolution as any constructor | -| `@default(timestamptz\`…\`)` on `DateTime(column: timestamp)` | Refused by the existing cast rule (wrong tag), naming `timestamp` | +| `@default(timestamptz\`…\`)` on `DateTime(storage: timestamp)` | Refused by the existing cast rule (wrong tag), naming `timestamp` | | `@onCreate(now())` on each convenience default | Generator resolved for the explicit codec (`timestampNow` for `TimestamptzJsDate`, `dateStringNow` for `DateString`, …) | | Positional arguments, e.g. `DateTime(temporal)` | Accepted in declaration order, like any constructor | -| Precision on `Date` or `Duration` with `value: string` | Refused unless the explicit type takes it (`IntervalString(p)` does, `DateString` does not) | +| Precision on `Date` or `Duration` with `as: string` | Refused unless the explicit type takes it (`IntervalString(p)` does, `DateString` does not) | | A pack declaring a choice that names a constructor no component registers | Assembly error naming the pack and the name | ## Slice-specific done conditions diff --git a/projects/datetime-convenience-types/slices/05-tools-write-convenience-types/spec.md b/projects/datetime-convenience-types/slices/05-tools-write-convenience-types/spec.md index 598010be4caa..465889326062 100644 --- a/projects/datetime-convenience-types/slices/05-tools-write-convenience-types/spec.md +++ b/projects/datetime-convenience-types/slices/05-tools-write-convenience-types/spec.md @@ -22,27 +22,27 @@ CREATE TABLE event ( model Event { at DateTime(precision: 6) // TimestamptzJsDate: a JavaScript Date paidAt DateTime // TimestamptzJsDate(3) - legacy DateTime(column: timestamp) // TimestampJsDate(3) + legacy DateTime(storage: timestamp) // TimestampJsDate(3) day Date // DateString opens Time(precision: 0) // TimeString(0) - localAt Time(column: timetz) // Timetz + localAt Time(storage: timetz) // Timetz span Duration // IntervalString } ``` `contract print` writes the same forms for the same codecs; a `timestamp` column read as `Temporal.PlainDateTime` (`TimestampTemporal`), which no convenience type reaches, prints as its explicit type. -A Prisma 7 schema read by `prisma7Schema(...)`: `DateTime` becomes `TimestampJsDate(3)` (prints `DateTime(column: timestamp)`), `@db.Timestamptz(p)` becomes `TimestamptzJsDate(p)`, `@db.Date` `DateString`, `@db.Time(p)` `TimeString(p)`, `@db.Timetz(p)` `Timetz(p)`. +A Prisma 7 schema read by `prisma7Schema(...)`: `DateTime` becomes `TimestampJsDate(3)` (prints `DateTime(storage: timestamp)`), `@db.Timestamptz(p)` becomes `TimestamptzJsDate(p)`, `@db.Date` `DateString`, `@db.Time(p)` `TimeString(p)`, `@db.Timetz(p)` `Timetz(p)`. `prisma orm init` writes `createdAt DateTime @default(now()) @onCreate(now())` and `updatedAt temporal.UpdatedAt`. ## Chosen design 1. **Infer always takes the default path where it can** (operator ruling, 2026-10-23: "infer should always choose the default path, where possible, since that's the simplest to see in your contract, as a user, and ought to represent the well-trodden path"). For each date and time column, infer picks the codec of the convenience type's default value for that column type, and writes the convenience call with only the arguments that differ from its defaults. The explicit type is written only where no convenience call reproduces the column. - - Consequence the docs state: a `Date` holds milliseconds, so a `timestamptz` or `timestamp` column with more than 3 digits of precision reads with the extra digits dropped, and `infinity` is refused on read. The upgrade and infer docs say to write `DateTime(value: temporal)` or `DateTime(value: string)` to keep them. + - Consequence the docs state: a `Date` holds milliseconds, so a `timestamptz` or `timestamp` column with more than 3 digits of precision reads with the extra digits dropped, and `infinity` is refused on read. The upgrade and infer docs say to write `DateTime(as: temporal)` or `DateTime(as: string)` to keep them. 2. **An omitted precision equals precision 6** on `pg/timestamptz`, `pg/timestamp`, `pg/time`, `pg/timetz` and `pg/interval` when columns are compared (the data type's normal form, ADR 254), because PostgreSQL stores microseconds either way. That lets infer write `DateTime(precision: 6)` for a column with no declared precision without `db verify` or the planner seeing a difference. The contract keeps parameters as written; stored contracts and hashes do not change. If this normal-form change would change any existing contract, hash or plan in the repository's fixtures, stop and report. 3. **The `inferred` marks move to the default explicit types:** `pg/timestamptz` → `TimestamptzJsDate`, `pg/timestamp` → `TimestampJsDate`, `pg/interval` → `IntervalString`; `DateString`, `TimeString`, `Timetz` keep theirs. Both infer and the Prisma 7 reader follow the marks, which gives the Prisma 7 table above. The guard test that forbids Temporal codecs in these maps keeps passing and gains `pg/timestamp-instant@1`. -4. **Print and infer write convenience calls through one framework helper** beside `resolveAuthoringTypeChoice`: given an explicit constructor call, find the type choice row (at the same namespace level) that names that constructor, build named arguments from the row (options from its `when`, passed-on arguments by name), drop arguments equal to the defaults, re-resolve to prove it reproduces the same codec and parameters, and otherwise return nothing so the caller writes the explicit call. Arguments are always written by name (positional `DateTime(6)` would land in `value`). +4. **Print and infer write convenience calls through one framework helper** beside `resolveAuthoringTypeChoice`: given an explicit constructor call, find the type choice row (at the same namespace level) that names that constructor, build named arguments from the row (options from its `when`, passed-on arguments by name), drop arguments equal to the defaults, re-resolve to prove it reproduces the same codec and parameters, and otherwise return nothing so the caller writes the explicit call. Arguments are always written by name (positional `DateTime(6)` would land in `as`). 5. **The Prisma 7 reader's generators follow the codec**: `@updatedAt` and slice 2's `@onCreate(now())` resolve `timestampNow` for the `Date` codecs. 6. **`temporal.CreatedAt` and `temporal.UpdatedAt`** are rebuilt on `TimestamptzJsDate(3)` (exactly what bare `DateTime` resolves to, pinned by a test comparing them), with `@onCreate(now())`, and `@onUpdate(now())` for the second; the `String` and `JsDate` variants are removed. A contributed alias stays a literal type, so `type.temporal.CreatedAt()` keeps a precise codec type. 7. **`orm init`** writes the convenience forms in both its PSL and TypeScript templates. diff --git a/projects/datetime-convenience-types/spec.md b/projects/datetime-convenience-types/spec.md index 604938dfc553..c88123df03e5 100644 --- a/projects/datetime-convenience-types/spec.md +++ b/projects/datetime-convenience-types/spec.md @@ -1,6 +1,6 @@ # Project spec — Convenience date and time types -**Linear:** [Convenience date and time types](https://linear.app/prisma-company/project/convenience-date-and-time-types-6a969ca1c76e) ([TML-3709](https://linear.app/prisma-company/issue/TML-3709)) · **Branch:** `tml-3709-plan-datetime-convenience-types` · **Design record:** [ADR 279 — Convenience date and time types choose an explicit type from their arguments](../../docs/architecture%20docs/adrs/ADR%20279%20-%20Convenience%20date%20and%20time%20types%20choose%20an%20explicit%20type%20from%20their%20arguments.md) (Accepted) +**Linear:** [Convenience date and time types](https://linear.app/prisma-company/project/convenience-date-and-time-types-6a969ca1c76e) ([TML-3709](https://linear.app/prisma-company/issue/TML-3709)) · **Branch:** `tml-3709-plan-datetime-convenience-types` · **Design record:** [ADR 279 — Convenience types choose an explicit type from their arguments](../../docs/architecture%20docs/adrs/ADR%20279%20-%20Convenience%20types%20choose%20an%20explicit%20type%20from%20their%20arguments.md) (Accepted) ## Purpose @@ -17,13 +17,13 @@ model Order { shipBy Date // date, read as "2024-02-29" opensAt Time(precision: 0) // time(0), read as "09:30:00" period Duration // interval, read as "P1M" - settledAt DateTime(value: temporal) // timestamptz, read as a Temporal.Instant - legacyAt DateTime(column: timestamp) // timestamp(3), read as a JavaScript Date, stored as UTC + settledAt DateTime(as: temporal) // timestamptz, read as a Temporal.Instant + legacyAt DateTime(storage: timestamp) // timestamp(3), read as a JavaScript Date, stored as UTC exactAt TimestamptzTemporal(6) // an explicit type, written directly } ``` -`DateTime`, `Date`, `Time` and `Duration` are convenience types. Each becomes one explicit type, chosen by its optional `value:` and `column:` arguments. The contract stores only the explicit type, so `settledAt` is stored exactly as `settledAt TimestamptzTemporal` would be. +`DateTime`, `Date`, `Time` and `Duration` are convenience types. Each becomes one explicit type, chosen by its optional `as:` and `storage:` arguments. The contract stores only the explicit type, so `settledAt` is stored exactly as `settledAt TimestamptzTemporal` would be. ## Decisions settled in discussion (2026-10-21 and 2026-10-22) @@ -33,7 +33,7 @@ The reasoning and the alternatives rejected are in [`design-notes.md`](./design- 2. **A convenience type is a type choice: a declaration whose arguments name the explicit type constructor it becomes**, through a table with defaults and refusals. Not a codec parameterized by database type (a codec represents one data type, ADR 254), not a type alias (an alias attaches attributes to one fixed type), and not a select template over option arguments (no defaults or refusals; being removed with the presets). Settled 2026-10-22 when slice 4 was grounded. 3. **The four PostgreSQL convenience types and their choices:** - | Type | `value:` | `column:` | Default | + | Type | `as:` | `storage:` | Default | | --- | --- | --- | --- | | `DateTime` | `jsDate`, `temporal` (`Temporal.Instant`), `string` | `timestamptz`, `timestamp` | `jsDate`, `timestamptz`; precision 3 when the value is `jsDate`, PostgreSQL's default otherwise | | `Date` | `string`, `temporal` (`Temporal.PlainDate`) | `date` | `string` | @@ -47,7 +47,7 @@ The reasoning and the alternatives rejected are in [`design-notes.md`](./design- 7. **Four codecs are added:** a `timestamp` column read as a `Date` and as a `Temporal.Instant`, both treating the stored value as UTC; and an `interval` column read as an ISO 8601 duration string. (The `parts` codec already exists.) 8. **String values are ISO 8601, the same on every server.** Every string codec returns the form ADR 254 stores, not PostgreSQL's display text. The codec ids do not change: this is a bug fix, and nothing keeps the old format before RC. Prisma requires `DateStyle=ISO` on its connections; it sets it on the connections it opens, and a connection the user supplies with another `DateStyle` is refused with an error naming the setting. Time zones need no setting: the `timestamptz` codec converts any session offset to UTC, and the `timetz` codec keeps the offset stored with the value. 9. **Temporal fields are never typed `any`.** The Temporal codecs and generators take their types from `temporal-spec`, a types-only package, instead of an undeclared global. No polyfill code is added anywhere. A missing global `Temporal` at runtime is still reported on first use, as today; a startup check was considered and dropped, because Temporal values are now an explicit opt-in. -10. **`contract infer` and `contract print` write convenience types**, with arguments only where they differ from the defaults; a column no convenience type covers (`Temporal.PlainDateTime`) is written as its explicit type. **The Prisma 7 reader** maps Prisma 7's `DateTime` to `TimestampJsDate(3)`, printed `DateTime(column: timestamp)`, so an upgraded app reads `Date` values again, and maps Prisma 7's `@default(now())` to `@default(now()) @onCreate(now())`, as Prisma 7 behaved ([TML-3719](https://linear.app/prisma-company/issue/TML-3719)). +10. **`contract infer` and `contract print` write convenience types**, with arguments only where they differ from the defaults; a column no convenience type covers (`Temporal.PlainDateTime`) is written as its explicit type. **The Prisma 7 reader** maps Prisma 7's `DateTime` to `TimestampJsDate(3)`, printed `DateTime(storage: timestamp)`, so an upgraded app reads `Date` values again, and maps Prisma 7's `@default(now())` to `@default(now()) @onCreate(now())`, as Prisma 7 behaved ([TML-3719](https://linear.app/prisma-company/issue/TML-3719)). 11. **PostgreSQL only.** A convenience name means the same thing on every target; SQLite and MongoDB follow in [TML-3714](https://linear.app/prisma-company/issue/TML-3714). 12. **TypeScript:** this project ships the PSL types and makes the TypeScript builder's compile-time resolver evaluate a codec chosen by arguments. The `type.DateTime(...)` helper itself arrives with the `type.*` helpers of the field-preset removal project. diff --git a/projects/target-named-scalars-sql/README.md b/projects/target-named-scalars-sql/README.md index e3fa163aec87..bf2d09ee41d9 100644 --- a/projects/target-named-scalars-sql/README.md +++ b/projects/target-named-scalars-sql/README.md @@ -2,6 +2,6 @@ Shaped 2026-09-25, not started; spec input from the Mongo defaults, codecs and Prisma 6 source project (merged 2026-09-25 to 2026-09-27). See [`spec.md`](./spec.md) for the rename tables, the `Json` preset split, `Json` refusing non-JSON values on PostgreSQL and SQLite, the enum storage-type rule moving into the framework, the upgrade fragments and the documentation changes. The decision it applies is [ADR 257](../../docs/architecture%20docs/adrs/ADR%20257%20-%20Scalar%20types%20are%20named%20after%20the%20target%20on%20every%20surface.md). -> **Changed by the Convenience date and time types project (2026-10-22).** [ADR 279](../../docs/architecture%20docs/adrs/ADR%20279%20-%20Convenience%20date%20and%20time%20types%20choose%20an%20explicit%20type%20from%20their%20arguments.md) (Proposed) keeps `DateTime` as a convenience type and renames the PostgreSQL date and time types itself: `Timestamptz` → `TimestamptzTemporal`, `Timestamp` → `TimestampTemporal`, `Date` → `DateTemporal`, `Time` → `TimeTemporal`, `Interval` → `IntervalParts`. The `DateTime` and `Timestamptz` rows of this spec's Postgres table, and `field.dateTime()` → `field.timestamptz()`, are superseded; see `projects/datetime-convenience-types/`. +> **Changed by the Convenience date and time types project (2026-10-22).** [ADR 279](../../docs/architecture%20docs/adrs/ADR%20279%20-%20Convenience%20types%20choose%20an%20explicit%20type%20from%20their%20arguments.md) (Proposed) keeps `DateTime` as a convenience type and renames the PostgreSQL date and time types itself: `Timestamptz` → `TimestamptzTemporal`, `Timestamp` → `TimestampTemporal`, `Date` → `DateTemporal`, `Time` → `TimeTemporal`, `Interval` → `IntervalParts`. The `DateTime` and `Timestamptz` rows of this spec's Postgres table, and `field.dateTime()` → `field.timestamptz()`, are superseded; see `projects/datetime-convenience-types/`. > Everything under `projects/` is transient — migrate long-lived architecture and upgrade documentation to `docs/`, remove repo-wide references to this workspace, and delete it at project close-out per [`projects/README.md`](../README.md). From d0b12dbfda4f8d48f8160d778019155cb7fe929d Mon Sep 17 00:00:00 2001 From: willbot Date: Fri, 23 Oct 2026 12:17:05 +0200 Subject: [PATCH 075/118] TML-3709: The spec says the driver ensures DateStyle=ISO on supplied connections Co-Authored-By: Claude Opus 5.5 Signed-off-by: willbot Signed-off-by: Will Madden --- projects/datetime-convenience-types/spec.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/projects/datetime-convenience-types/spec.md b/projects/datetime-convenience-types/spec.md index c88123df03e5..127b70ed326d 100644 --- a/projects/datetime-convenience-types/spec.md +++ b/projects/datetime-convenience-types/spec.md @@ -45,7 +45,7 @@ The reasoning and the alternatives rejected are in [`design-notes.md`](./design- 5. **Plain strings, no brand and no Prisma value class.** A brand or class adds friction on every write and does not survive JSON. The codec validates the format on write. 6. **Every explicit type of a column with several codecs carries a suffix.** `Timestamptz`, `Timestamp`, `Date` and `Time` become `TimestamptzTemporal`, `TimestampTemporal`, `DateTemporal` and `TimeTemporal`. `Interval` becomes `IntervalParts`, next to the new `IntervalString`. 7. **Four codecs are added:** a `timestamp` column read as a `Date` and as a `Temporal.Instant`, both treating the stored value as UTC; and an `interval` column read as an ISO 8601 duration string. (The `parts` codec already exists.) -8. **String values are ISO 8601, the same on every server.** Every string codec returns the form ADR 254 stores, not PostgreSQL's display text. The codec ids do not change: this is a bug fix, and nothing keeps the old format before RC. Prisma requires `DateStyle=ISO` on its connections; it sets it on the connections it opens, and a connection the user supplies with another `DateStyle` is refused with an error naming the setting. Time zones need no setting: the `timestamptz` codec converts any session offset to UTC, and the `timetz` codec keeps the offset stored with the value. +8. **String values are ISO 8601, the same on every server.** Every string codec returns the form ADR 254 stores, not PostgreSQL's display text. The codec ids do not change: this is a bug fix, and nothing keeps the old format before RC. The PostgreSQL target requires `DateStyle=ISO`, and the driver ensures it on every connection it hands out: as a startup option on connections it opens, and with a statement on a pool or client the user supplies. A connection that still prints another style is refused with an error naming the setting. Time zones need no setting: the `timestamptz` codec converts any session offset to UTC, and the `timetz` codec keeps the offset stored with the value. 9. **Temporal fields are never typed `any`.** The Temporal codecs and generators take their types from `temporal-spec`, a types-only package, instead of an undeclared global. No polyfill code is added anywhere. A missing global `Temporal` at runtime is still reported on first use, as today; a startup check was considered and dropped, because Temporal values are now an explicit opt-in. 10. **`contract infer` and `contract print` write convenience types**, with arguments only where they differ from the defaults; a column no convenience type covers (`Temporal.PlainDateTime`) is written as its explicit type. **The Prisma 7 reader** maps Prisma 7's `DateTime` to `TimestampJsDate(3)`, printed `DateTime(storage: timestamp)`, so an upgraded app reads `Date` values again, and maps Prisma 7's `@default(now())` to `@default(now()) @onCreate(now())`, as Prisma 7 behaved ([TML-3719](https://linear.app/prisma-company/issue/TML-3719)). 11. **PostgreSQL only.** A convenience name means the same thing on every target; SQLite and MongoDB follow in [TML-3714](https://linear.app/prisma-company/issue/TML-3714). From 8dd9a2c586c76b7b972f5a4365d0f1ebe769a6c3 Mon Sep 17 00:00:00 2001 From: willbot Date: Fri, 23 Oct 2026 12:34:14 +0200 Subject: [PATCH 076/118] TML-3709: Handover brief for the convenience date and time types project Co-Authored-By: Claude Opus 5.5 Signed-off-by: willbot Signed-off-by: Will Madden --- .../datetime-convenience-types/handover.md | 81 +++++++++++++++++++ 1 file changed, 81 insertions(+) create mode 100644 projects/datetime-convenience-types/handover.md diff --git a/projects/datetime-convenience-types/handover.md b/projects/datetime-convenience-types/handover.md new file mode 100644 index 000000000000..e1e54044e1b8 --- /dev/null +++ b/projects/datetime-convenience-types/handover.md @@ -0,0 +1,81 @@ +# Handover — Convenience date and time types + +Written 2026-10-23 by the agent `thales-11`, before a rate limit. Read this whole file, then the transcript, before acting. + +**Transcript (full context, including every ruling of Will's in his own words):** `/Users/will/.claude/projects/-Users-will-Projects-prisma-orm--claude-worktrees-prisma8-datetime-usability-5a9ffb/a52854a5-d212-46e3-996d-71bf5a31c118.jsonl`. It is long; filter Will's own messages with `jq -r 'select(.type=="user" and (.message.content|type)=="string") | .message.content' `. + +Linear: project "Convenience date and time types" (TML-3709). Repository `prisma/orm`. Push only through the `bot` remote (`git@github-wmadden-electric:...`); commit with `git commit -s --trailer "Signed-off-by: Will Madden "` and end messages with `Co-Authored-By: Claude Opus 5.5 `. + +## What the project is + +PostgreSQL date and time fields in Prisma 8 had a dozen database-named "explicit" types (`TimestamptzJsDate`, `TimestamptzTemporal`, `DateString`, …), each naming the database type and the JavaScript value read, and no obvious one to start with. The project adds **convenience types**: a general mechanism where a target declares a type named for what a field means (`DateTime`, `Date`, `Time`, `Duration`), whose options pick exactly one explicit type from a table, with defaults that need no setup. The contract stores only the explicit type. The tools (`contract infer`, `contract print`, the Prisma 7 schema reader, `orm init`) write the convenience form. + +The design record is **ADR 279 — Convenience types choose an explicit type from their arguments** (`docs/architecture docs/adrs/`). It was just rewritten as a general mechanism with PostgreSQL dates as the first use. Read it first. The project spec, plan and per-PR specs are in this folder. + +```prisma +model Order { + paidAt DateTime // exactly TimestamptzJsDate(3) + settledAt DateTime(as: temporal) // exactly TimestamptzTemporal + legacyAt DateTime(storage: timestamp) // exactly TimestampJsDate(3) + shipBy Date // exactly DateString +} +``` + +## Settled decisions — do not reopen + +Will ruled each of these; quotes are in the transcript. + +- Two kinds of type: explicit types (one codec each, named after the database, suffix for the value when a database type has several codecs) and convenience types (a framework descriptor `kind: 'typeChoice'` with option arguments, handed-on arguments and a `choices` table). Not a parameterized codec, not an alias, not the preset select machinery. `@db.*` never comes back. +- Options are **`as:`** (value read: `jsDate`, `temporal`, `string`, `parts`) and **`storage:`** (database type). Renamed from `value:`/`column:` on 2026-10-23; values unchanged. TypeScript helpers take **one object**: `type.DateTime({ as: 'string', precision: 6 })`. Explicit constructors stay positional. +- Defaults: `DateTime` = `TimestamptzJsDate(3)`; `Date`, `Time`, `Duration` = ISO 8601 strings. No polyfill needed. Temporal TypeScript types come from `temporal-spec` (types only); the polyfill is never bundled. +- No backward compatibility before RC: string codecs keep their ids when they switch to ISO; renames are outright. +- `DateStyle=ISO`: the PostgreSQL target declares it as `sessionSettings`; the driver ensures it on every connection (startup option on connections it opens, `set_config` on supplied pools/clients before first use and after exclusive release). Recorded in ADR 159. +- Prisma 7 reader: `@default(now())` becomes `@default(now()) @onCreate(now())`. Prisma 7 `DateTime` becomes `TimestampJsDate(3)` (printed `DateTime(storage: timestamp)`). +- `contract infer` always takes the default path where possible. An omitted precision compares equal to 6 (data type normal form); planner postcheck catalog text uses the precision as written (`catalogParams`). +- The `Date` codecs (`pg/timestamptz-date@1`, `pg/timestamp-date@1`, MySQL datetime) refuse in `fromDataTypeValue` a stored value with digits below a millisecond (ADR 254's rule that a codec refuses what it cannot hold exactly). Rows (`fromWire`) keep dropping the digits. Infer keeps an exactness check (beside its JSON one) and prints such a default as raw SQL; the Prisma 7 binding writes refused defaults via `refusedLiteralDefault`. `db verify` cannot see a sub-millisecond default difference on a `Date` column — accepted and documented. +- Scope: PostgreSQL only. SQLite, MySQL, MongoDB convenience types are TML-3714; other scalars TML-3715. + +## The PR stack (all awaiting Will's review — open no new PRs until these are cleared) + +| PR | Branch | Base | State | +| --- | --- | --- | --- | +| #30834 design | `tml-3709-plan-datetime-convenience-types` | `main` | pushed; ADR 279 rewritten (general); description rewritten | +| #30851 ISO strings (TML-3418) | `tml-3418-iso-string-date-time-codecs` | design | pushed, last CI green before the latest DateStyle commit 5c16f01456; check CI | +| #30839 Prisma 7 now() (TML-3719) | `tml-3719-prisma7-now-default-generator` | design | pushed, green | +| #30844 explicit types (TML-3711) | `tml-3711-explicit-date-time-types` | design | pushed, green | +| #30847 convenience types (TML-3712) | `tml-3712-convenience-date-time-types` | #30844 | pushed PR branch still has `value:`/`column:`; rename in progress (below) | +| #30858 tools (TML-3713) | `tml-3713-tools-write-convenience-types` | #30847 (merges #30839 in) | pushed PR branch still has old names; rename and three fixes in progress | + +Worktrees live under the shaping worktree: `wip/slices/`. `wip/slices/final-review` is a local-only branch merging everything (never push it). + +## In flight when this was written + +An implementer was renaming `value:`→`as:`, `column:`→`storage:` and making the TypeScript type-choice helpers take an object, first on #30847's branch, then merging the design branch (new ADR 279 file name: take it as is, delete the old file, update every link to the old name/title), then merging into #30858's branch and updating infer/print/Prisma 7/init expectations, fixtures, docs and upgrade instructions. Then three fixes on #30858's branch: +1. `timestampNow` for the Date codecs rounds down to the column precision (Prisma 7 `@db.Timestamp(0) @updatedAt` generated `.700`, stored `:01`); PGlite test for precision 0–2. +2. `RUNTIME.TEMPORAL_UNAVAILABLE` fix text (Postgres target `errors.ts` ~line 91) and its error-reference entry: remove `temporal.createdAtString()`, list every Temporal codec incl. `pg/timestamp-instant@1`, point to the convenience defaults. +3. `upgrade-instructions/pending/tools-write-convenience-types/app/instructions.md` ~37–39: don't say Prisma 7 date/time strings are "unchanged" (the ISO PR changes them). + +Its work was checkpointed to backup branches **`bot/wip/tml-3712-as-storage`** and **`bot/wip/tml-3713-as-storage`** (see "Status of the backup branches" at the end). Resume from those: verify, finish, run gates, then fast-forward or push the PR branches. + +## Remaining work, in order + +1. Finish the rename and the three fixes above; run the gates (package tests with coverage/lint/typecheck for touched packages, the affected integration files and journeys, `pnpm fixtures:check`, `lint:deps`, `lint:ratchets`, `lint:docs`, `check:upgrade-coverage --mode pr --prev `). Push #30847 and #30858; watch CI. +2. Update the descriptions of #30834, #30847 and #30858 for `as:`/`storage:`, the object form and the general ADR (current bodies are in the gitignored `wip/pr-rewrite/*.new.md` of the shaping worktree; regenerate from the PRs if gone: `gh pr view --repo prisma/orm --json body`). `gh pr edit` fails with the bot token; use `gh api -X PATCH repos/prisma/orm/pulls/ -F body=@file`. +3. Run `/drive-code-review` (no walkthrough) on the final combined result again after the rename, as Will asked; the last one found 7 issues, all now fixed or queued (see transcript). +4. When Will approves a PR: turn on auto-merge (squash); stacked branches rebase with `--onto` after a squash merge; close the Linear ticket with a comment. Merge conflict to expect between #30851 and #30844 in `date-codecs.ts` `decodeDate`: keep `requireIsoDateStyle` inside the shared `decodeDate`; `date-style-every-codec.test.ts` fails otherwise. The convenience journey's `textAt`/`localAt` values become `2024-02-29T10:00:00.123456Z` and `09:30:00+02:00` when #30851 and #30847 meet. +5. Close-out after all merge: retro, docs migration, delete this `projects/` folder. + +## How Will wants you to work (from his CLAUDE.md and this session) + +- Name yourself first (`bash ~/.claude/scripts/agent-name.sh`), prefix the session title, put `Agent: ` in PRs. +- **Read the design docs before any design opinion** (ADRs in full including Alternatives, glossary, vision). Before instructing an implementer on a design change, research the code and records yourself; say when a proposal reverses a recorded decision. Will was furious when I relayed a rework ADR 254 had rejected. +- Plain, short English; no ADR numbers or internal labels without saying what they mean; no invented jargon; banned words in his CLAUDE.md. +- ADRs and PR descriptions: open with a grounding example, lead with the decision, build up step by step, end with alternatives; ADRs carry no tickets, slices or history. +- Write docs, ADRs and PR text yourself; subagents implement and review. Subagents always `model: "opus"`. +- Decide routine and representation calls yourself; ask Will only for real decisions, with a recommendation. +- Don't open new PRs while the project's PRs await review. No session-proposal cards; follow-ups go to Linear. +- Never kill processes by pattern. Never re-run CI without Will's go-ahead; report flakes. Stay inside the worktree. + +## Status of the backup branches + +(Filled in below if the implementer replied before the rate limit.) From 739fd31ecc116e749940628480d3c98ed17baea9 Mon Sep 17 00:00:00 2001 From: willbot Date: Fri, 23 Oct 2026 12:35:51 +0200 Subject: [PATCH 077/118] TML-3709: Handover records the state of the as/storage rename Co-Authored-By: Claude Opus 5.5 Signed-off-by: willbot Signed-off-by: Will Madden --- projects/datetime-convenience-types/handover.md | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/projects/datetime-convenience-types/handover.md b/projects/datetime-convenience-types/handover.md index e1e54044e1b8..67279707b25e 100644 --- a/projects/datetime-convenience-types/handover.md +++ b/projects/datetime-convenience-types/handover.md @@ -78,4 +78,6 @@ Its work was checkpointed to backup branches **`bot/wip/tml-3712-as-storage`** a ## Status of the backup branches -(Filled in below if the implementer replied before the rate limit.) +- **`bot/wip/tml-3712-as-storage` at `b6bb9ffad6`** (WIP commit, signed off; local `tml-3712-convenience-date-time-types` is the same). Done: the `as:`/`storage:` rename everywhere on that branch (declarations, tests in every package, the integration file, parity test, journey, `scalar-types.md`, `error-reference.md`, Serverless guide, `gotchas.md`, ADR 275 note, `skills/prisma-8` contract and debug references, pending upgrade instruction, READMEs), and the TypeScript object form (one overload per row; options required only when not the default; unknown key, unlisted combination, unaccepted argument and positional calls fail to compile, with negative type tests; runtime `typeChoiceArgumentsFromObject` in framework-components converts the object to the argument list). The no-row refusal now reads `Argument "storage" of Time cannot be timetz with as: temporal; write one of: time.` Package tests, lint, typecheck, coverage, integration, journeys, `lint:deps`, `lint:ratchets`, `lint:docs` and `check:upgrade-coverage` passed (a few timing tests in language-server and the facade timed out under a load average of 70–140; unrelated files). +- **Not done on it:** `pnpm fixtures:check` fails: its diff is in `skills/prisma-8/references/contract.md` (the "Date and time types" and "Temporal columns" paragraphs), so some generated copy of the skill reference disagrees with the edited source; investigate (`pnpm fixtures:check` output was in the gitignored `wip/gates/` of that worktree). Then amend the WIP commit into a proper one, merge the design branch (new ADR 279 file), fix links, push the PR branch. +- **`bot/wip/tml-3713-as-storage` does not exist:** the tools branch work (merge, rename in what the tools write, and the three fixes) was never started. `bot/tml-3713-tools-write-convenience-types` is unchanged. From 5149dfbb793508a8fa7a61ab723ac351678ea5e9 Mon Sep 17 00:00:00 2001 From: willbot Date: Fri, 23 Oct 2026 14:41:21 +0200 Subject: [PATCH 078/118] TML-3711: Every explicit PostgreSQL date and time type names the value it reads, and timestamp and interval get the missing codecs (#30844) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ## Linked issue Fixes TML-3711. Part of the Convenience date and time types project (TML-3709); stacked on the design PR, prisma/orm#30834. ## Summary ```prisma model Event { id Int @id at TimestamptzTemporal(3) // was Timestamptz(3): timestamptz(3), read as a Temporal.Instant local TimestampTemporal // was Timestamp: timestamp, read as a Temporal.PlainDateTime legacyAt TimestampJsDate(3) // new: timestamp(3), read as a JavaScript Date, stored as UTC utcAt TimestampInstant(6) // new: timestamp(6), read as a Temporal.Instant, stored as UTC day DateTemporal // new name: date, read as a Temporal.PlainDate opens TimeTemporal(0) // new name: time(0), read as a Temporal.PlainTime period IntervalParts // was Interval: interval, read as { months, days, micros } span IntervalString // new: interval, read as "P1Y2M" } ``` **The decision: every explicit PostgreSQL date and time type names the kind of value it reads, and `timestamp` and `interval` columns get the codecs they were missing.** These explicit types are the precise layer that the convenience types `DateTime`, `Date`, `Time` and `Duration` (prisma/orm#30847) each turn into. ### Every type names its kind of value The explicit types followed two naming patterns. The ones that read a string or a `Date` carried a suffix, such as `TimestamptzString` and `TimestamptzJsDate`. The ones that read Temporal values had none, `Timestamptz` and `Timestamp`, so the Temporal type looked like the neutral default for its column. Now every type for a column type with more than one codec carries a suffix: - `Timestamptz` becomes `TimestamptzTemporal`; - `Timestamp` becomes `TimestampTemporal`; - `Interval` becomes `IntervalParts`; - `DateTemporal` and `TimeTemporal` are added beside `Date` and `Time`, which the convenience types take over in prisma/orm#30847. The old names are removed outright; there are no deprecated aliases before the release candidate. `contract print` writes the new names. ### Three new codecs - **`pg/timestamp-date@1` (`TimestampJsDate`)** and **`pg/timestamp-instant@1` (`TimestampInstant`)** read and write a `timestamp` column as UTC, whatever the session's or the host's time zone. Prisma 7's `DateTime` is a `timestamp(3)` column read as a `Date`, so an adopted Prisma 7 schema needs the first. `TimestampInstant` names its Temporal type, because `TimestampTemporal` already reads a `Temporal.PlainDateTime`. - **`pg/interval-string@1` (`IntervalString`)** reads every `IntervalStyle` PostgreSQL prints and returns the ISO 8601 duration the contract stores. Each comes with its PSL type, TypeScript column helper, `now` generator, aggregates, enum rules and conformance cases, including sessions not in UTC and every `IntervalStyle`. ### Temporal fields are typed without a global The Temporal codecs named the global `Temporal` type, which exists only if a project loads a polyfill's global declarations. Without them, these fields were typed `any`, so passing a `Date` compiled and failed at runtime. The types now come from `temporal-spec`, a package of declarations with no runtime code, so no runtime bundle gains polyfill code. A type test checks the fields under a tsconfig with no global declarations. ### Smaller fixes in the same code - `IntervalParts(p)` with a precision was typed as a branded string, though the codec reads an object; it now has the codec's type. - The unused branded types `Timestamp`, `Timestamptz` and `Time` are removed, and 48 migration snapshot `contract.d.ts` files drop their imports. ## Testing performed - `pnpm build`, the repo-wide `pnpm typecheck`, `pnpm lint` for every touched package, `pnpm lint:deps`, `pnpm lint:ratchets`, `pnpm check:publish-deps`, `pnpm fixtures:check`, `pnpm check:upgrade-coverage --mode pr`. - Package tests: target-postgres, adapter-postgres, contract-psl, contract-ts, the postgres extension, supabase, language-server and the codec testkit, including its conformance, aggregate and new UTC files. - Integration: `test/psl-print` with its round-trip sweeps, `test/psl-infer`, `test/contract-parity`, `test/value-objects`, date and time defaults, and the upgrade-instruction detection test. - Codec probes: values built as the driver builds them with the host `TZ` set to UTC, Asia/Kolkata and America/Los_Angeles on a session not in UTC; stored values the codecs must refuse, written with raw SQL; and removing each fix to see its test fail. - target-postgres coverage is 95.8% of lines, above its threshold. ## Skill update `skills/prisma-8/references/contract.md` and `debug.md` use the new names. Upgrade instructions in `upgrade-instructions/pending/explicit-date-time-types/` (`app` and `extension`) give the rename table, the removed branded types, the Temporal types from `temporal-spec`, and the new aggregate rows, each with a detection pattern and a test. ## Checklist - [x] All commits are signed off. - [x] Scoped to one logical concern. - [x] Tests are updated. - [x] The PR title is in `TML-NNNN: ` form. - [x] The Skill update section is filled in. ## Notes for the reviewer - 245 `contract.d.ts` fixtures are regenerated; the changes are added aggregate rows for the new codecs. No `contract.json` changes, and the Supabase contract's storage hash is unchanged. The committed Supabase contract is not re-inferred here (TML-3417). - `@prisma/orm-target-postgres` and `@prisma/orm-postgres` gain a dependency on `temporal-spec`, because their declarations name it. - `TimestampJsDate` takes precision 0 to 6, like `TimestamptzJsDate`; a `Date` keeps milliseconds, and the docs point to `TimestampInstant` or `TimestampString` for more. - A database `@default(now())` on a `timestamp` column is filled by PostgreSQL in the session's time zone, as before; a test pins it, and the docs say to use `@onCreate(now())` for UTC. **Alternatives considered:** - **Rename only `Date` and `Time`, keeping `Timestamptz` and `Timestamp` unsuffixed.** The explicit types would still follow two naming patterns. - **Keep the old names as deprecated aliases.** There is no backward compatibility before the release candidate, and the SQL schema interpreter has no deprecation warnings. - **Name the timestamp-as-Instant type `TimestampTemporal`.** That name belongs to the codec that reads a `Temporal.PlainDateTime`. Agent: thales-11 🤖 Generated with [Claude Code](https://claude.com/claude-code) --------- Signed-off-by: willbot Signed-off-by: Will Madden Co-authored-by: Claude Opus 5.5 --- .../contract.d.ts | 4 - .../contract.d.ts | 4 - .../src/prisma/contract.d.ts | 32 ++++ docs/reference/codec-authoring-guide.md | 4 +- docs/reference/error-reference.md | 2 +- docs/reference/scalar-types.md | 19 ++- .../src/postgres/generated/contract.d.ts | 32 ++++ .../app/src/contract.d.ts | 32 ++++ .../contract.d.ts | 32 ++++ .../packages/audit/src/contract.d.ts | 32 ++++ .../contract.d.ts | 32 ++++ .../packages/feature-flags/src/contract.d.ts | 32 ++++ .../paradedb-demo/src/prisma/contract.d.ts | 32 ++++ .../src/prisma/contract.d.ts | 32 ++++ .../contract.d.ts | 4 - .../contract.d.ts | 4 - .../contract.d.ts | 4 - .../contract.d.ts | 4 - .../contract.d.ts | 4 - .../contract.d.ts | 4 - .../contract.d.ts | 4 - .../contract.d.ts | 4 - .../contract.d.ts | 4 - .../contract.d.ts | 4 - .../contract.d.ts | 4 - .../contract.d.ts | 4 - .../contract.d.ts | 4 - .../contract.d.ts | 4 - .../contract.d.ts | 4 - .../contract.d.ts | 4 - .../contract.d.ts | 4 - .../contract.d.ts | 4 - .../contract.d.ts | 4 - .../contract.d.ts | 4 - .../contract.d.ts | 4 - .../contract.d.ts | 4 - .../contract.d.ts | 4 - .../contract.d.ts | 4 - .../contract.d.ts | 4 - .../contract.d.ts | 4 - .../contract.d.ts | 4 - .../contract.d.ts | 4 - .../contract.d.ts | 4 - .../contract.d.ts | 4 - .../contract.d.ts | 4 - .../contract.d.ts | 4 - .../contract.d.ts | 4 - .../contract.d.ts | 4 - .../contract.d.ts | 4 - .../contract.d.ts | 4 - .../contract.d.ts | 4 - .../contract.d.ts | 4 - .../contract.d.ts | 4 - .../contract.d.ts | 4 - .../contract.d.ts | 4 - .../contract.d.ts | 4 - .../contract.d.ts | 4 - .../contract.d.ts | 4 - .../contract.d.ts | 32 ++++ .../contract.d.ts | 32 ++++ .../contract.d.ts | 4 - .../contract.d.ts | 32 ++++ .../contract.d.ts | 32 ++++ .../contract.d.ts | 4 - .../contract.d.ts | 4 - .../contract.d.ts | 32 ++++ examples/prisma-8-demo/package.json | 1 + .../prisma-8-demo/src/prisma/contract.d.ts | 32 ++++ .../test/fragments.types.test-d.ts | 1 + .../contract.d.ts | 32 ++++ .../contract.d.ts | 4 - .../src/prisma/contract.d.ts | 32 ++++ .../generated/prisma8/contract.d.ts | 32 ++++ .../src/prisma/contract.d.ts | 32 ++++ examples/supabase/src/contract.d.ts | 32 ++++ gotchas.md | 2 +- .../2-authoring/contract-psl/README.md | 4 +- .../2-authoring/contract-psl/test/fixtures.ts | 4 +- .../test/interpreter.alias-attributes.test.ts | 22 +-- .../interpreter.contributed-aliases.test.ts | 6 +- .../test/interpreter.diagnostics.test.ts | 16 +- .../test/interpreter.types.test.ts | 4 +- .../test/fixtures/generated/contract.d.ts | 32 ++++ .../contract.d.ts | 32 ++++ .../3-extensions/paradedb/src/contract.d.ts | 32 ++++ .../contract.d.ts | 32 ++++ .../3-extensions/pgvector/src/contract.d.ts | 32 ++++ .../contract.d.ts | 32 ++++ .../3-extensions/postgis/src/contract.d.ts | 32 ++++ .../generated/contract.d.ts | 32 ++++ .../generated/contract.d.ts | 32 ++++ .../test/fixtures/generated/contract.d.ts | 32 ++++ .../generated/contract.d.ts | 32 ++++ .../on-update-only/generated/contract.d.ts | 32 ++++ .../polymorphism/generated/contract.d.ts | 32 ++++ .../scope-namespace/generated/contract.d.ts | 32 ++++ .../soft-delete/generated/contract.d.ts | 32 ++++ .../supabase/scripts/generate-contract.ts | 2 +- .../supabase/src/contract/contract.d.ts | 32 ++++ .../supabase/src/contract/contract.prisma | 158 +++++++++--------- .../supabase/src/contract/handles.ts | 3 +- .../test/fixtures/example-app/contract.d.ts | 32 ++++ .../test/fixtures/no-policy/contract.d.ts | 32 ++++ .../fixtures/renamed-policy/contract.d.ts | 32 ++++ .../3-targets/postgres/1-target/README.md | 4 +- .../3-targets/postgres/1-target/package.json | 5 +- .../postgres/1-target/src/core/aggregates.ts | 16 ++ .../postgres/1-target/src/core/codec-ids.ts | 3 + .../1-target/src/core/codec-type-map.ts | 7 +- .../postgres/1-target/src/core/codecs.ts | 7 +- .../postgres/1-target/src/core/date-codecs.ts | 136 +++++++++++---- .../src/core/date-time-now-generators.ts | 1 + .../src/core/interval-string-codec.ts | 92 ++++++++++ .../1-target/src/core/now-generators.ts | 4 + .../src/core/psl-print/column-types.ts | 2 +- .../1-target/src/core/require-temporal.ts | 10 +- .../src/core/temporal-codec-helpers.ts | 48 +++++- .../1-target/src/core/temporal-codecs.ts | 60 +++++++ .../1-target/src/core/type-constructors.ts | 61 ++++++- .../1-target/src/exports/codec-types.ts | 11 -- .../postgres/1-target/src/exports/codecs.ts | 10 ++ .../test/codec-render-value-literal.test.ts | 3 + .../1-target/test/data-type-inventory.test.ts | 3 + .../postgres/1-target/test/data-types.test.ts | 5 +- .../test/explicit-type-codec-wiring.test.ts | 80 +++++++++ .../test/fixtures/namespaced-contract.d.ts | 4 - .../1-target/test/generator-functions.test.ts | 2 + .../test/interval-string-codec.test.ts | 72 ++++++++ .../1-target/test/now-generators.test.ts | 11 +- .../test/parameterized-output-types.test-d.ts | 7 +- .../test/psl-infer/infer-column-types.test.ts | 4 +- .../test/psl-print/generated-values.test.ts | 6 +- .../{src => test}/temporal-global.d.ts | 0 .../temporal-representation-taxonomy.test.ts | 23 ++- .../test/timestamp-utc-codecs.test.ts | 158 ++++++++++++++++++ .../timestamp-utc-codecs.time-zone.test.ts | 43 +++++ .../1-target/test/type-constructors.test.ts | 79 +++++++-- .../1-target/tsconfig.no-temporal-global.json | 4 + .../3-targets/postgres/2-adapter/README.md | 2 +- .../2-adapter/src/exports/column-types.ts | 41 ++++- .../test/date-time-column-types.test.ts | 30 ++++ .../bit-interval-columns.integration.test.ts | 4 +- .../aggregate-defaults.integration.test.ts | 4 + .../4-codec-testkit/test/aggregate-matrix.ts | 11 ++ .../test/codec-conformance/cases.ts | 70 ++++++++ ...stamp-utc-now-defaults.integration.test.ts | 77 +++++++++ ...stamp-utc-runtime-read.integration.test.ts | 78 +++++++++ .../3-targets/postgres/9-facade/package.json | 3 +- .../9-facade/src/exports/contract-builder.ts | 1 - .../contract-builder/type-helpers.test-d.ts | 2 +- .../test/fixtures/generated/contract.d.ts | 32 ++++ .../9-facade/test/native-type-parity.test.ts | 32 ++-- .../temporal-field-types.test-d.ts | 55 ++++++ .../9-facade/test/scalar-type-parity.test.ts | 22 ++- .../9-facade/tsconfig.no-temporal-global.json | 4 + .../contract-builder/type-helpers.test-d.ts | 2 +- .../@prisma/orm-postgres/package.json | 1 + .../@prisma/orm-target-postgres/package.json | 3 +- pnpm-lock.yaml | 18 ++ skills/prisma-8/references/contract.md | 4 +- skills/prisma-8/references/debug.md | 2 +- test/e2e/framework/test/fixtures/contract.ts | 4 +- .../test/fixtures/generated/contract.d.ts | 32 ++++ test/integration/package.json | 1 + ...lts-on-existing-tables.integration.test.ts | 2 +- .../default-date-time-types/schema.prisma | 2 +- ...lane-commands-without-temporal.e2e.test.ts | 6 +- ...te-time-default-canonical-form.e2e.test.ts | 4 +- .../defaults-read-by-codec.e2e.test.ts | 18 +- .../test/contract-builder.types.test-d.ts | 3 +- .../fixtures/postgres/generated/contract.d.ts | 32 ++++ .../rendered-output-types.test.ts | 7 +- .../contract-with-tags.prisma | 2 +- .../contract.prisma | 2 +- ...psl-date-time-defaults.integration.test.ts | 16 +- .../generated/contract.d.ts | 32 ++++ .../_fixture-postgres/generated/contract.d.ts | 32 ++++ .../enum-members.postgres.test.ts | 9 +- .../_fixture/generated/contract.d.ts | 32 ++++ .../contract-date-time-defaults.ts | 6 +- test/integration/test/fixtures/contract.d.ts | 32 ++++ .../fixtures/generated/contract.d.ts | 32 ++++ .../fixtures/postgres/contract.ts | 8 +- .../fixtures/postgres/generated/contract.d.ts | 32 ++++ .../_fixture/decimal/generated/contract.d.ts | 32 ++++ .../_fixture/numeric/generated/contract.d.ts | 32 ++++ .../count/_fixture/generated/contract.d.ts | 32 ++++ .../_fixture/main/generated/contract.d.ts | 32 ++++ .../regression-21789/generated/contract.d.ts | 32 ++++ .../_fixture/common/generated/contract.d.ts | 32 ++++ .../_fixture/decimal/generated/contract.d.ts | 32 ++++ .../_fixture/compound/generated/contract.d.ts | 32 ++++ .../_fixture/generated/contract.d.ts | 32 ++++ .../_fixture/nested/generated/contract.d.ts | 32 ++++ .../_fixture/self/generated/contract.d.ts | 32 ++++ .../_fixture/common/generated/contract.d.ts | 32 ++++ .../_fixture/decimal/generated/contract.d.ts | 32 ++++ .../_fixture/common/generated/contract.d.ts | 32 ++++ .../_fixture/decimal/generated/contract.d.ts | 32 ++++ .../_fixture/decimal/generated/contract.d.ts | 32 ++++ .../_fixture/numeric/generated/contract.d.ts | 32 ++++ .../_fixture/base/generated/contract.d.ts | 32 ++++ .../_fixture/nested/generated/contract.d.ts | 32 ++++ .../bigint/_fixture/generated/contract.d.ts | 32 ++++ .../bool/_fixture/generated/contract.d.ts | 32 ++++ .../relations/generated/contract.d.ts | 32 ++++ .../_fixture/scalars/generated/contract.d.ts | 32 ++++ .../datetime/_fixture/generated/contract.d.ts | 32 ++++ .../decimal/_fixture/generated/contract.d.ts | 32 ++++ .../_fixture/postgres/generated/contract.d.ts | 32 ++++ .../float/_fixture/generated/contract.d.ts | 32 ++++ .../int/_fixture/generated/contract.d.ts | 32 ++++ .../_fixture/scalar/generated/contract.d.ts | 32 ++++ .../_fixture/other/generated/contract.d.ts | 32 ++++ .../_fixture/string/generated/contract.d.ts | 32 ++++ .../string/_fixture/generated/contract.d.ts | 32 ++++ .../_fixture/common/generated/contract.d.ts | 32 ++++ .../_fixture/decimal/generated/contract.d.ts | 32 ++++ .../_fixture/enum/generated/contract.d.ts | 32 ++++ .../_fixture/json/generated/contract.d.ts | 32 ++++ .../_fixture/lists/generated/contract.d.ts | 32 ++++ .../distinct/_fixture/generated/contract.d.ts | 32 ++++ .../_fixture/generated/contract.d.ts | 32 ++++ .../_fixture/generated/contract.d.ts | 32 ++++ .../_fixture/generated/contract.d.ts | 32 ++++ .../decimal-list/generated/contract.d.ts | 32 ++++ .../_fixture/enum/generated/contract.d.ts | 32 ++++ .../_fixture/list/generated/contract.d.ts | 32 ++++ .../_fixture/mixed/generated/contract.d.ts | 32 ++++ .../_fixture/generated/contract.d.ts | 32 ++++ .../_fixture/generated/contract.d.ts | 32 ++++ .../_fixture/generated/contract.d.ts | 32 ++++ .../_fixture/generated/contract.d.ts | 32 ++++ .../_fixture/common/generated/contract.d.ts | 32 ++++ .../_fixture/default/generated/contract.d.ts | 32 ++++ .../_fixture/list/generated/contract.d.ts | 32 ++++ .../_fixture/generated/contract.d.ts | 32 ++++ .../_fixture/generated/contract.d.ts | 32 ++++ .../_fixture/generated/contract.d.ts | 32 ++++ .../_fixture/generated/contract.d.ts | 32 ++++ .../_fixture/list/generated/contract.d.ts | 32 ++++ .../_fixture/complex/generated/contract.d.ts | 32 ++++ .../one-to-many/generated/contract.d.ts | 32 ++++ .../one-to-one-list/generated/contract.d.ts | 32 ++++ .../one-to-one/generated/contract.d.ts | 32 ++++ .../_fixture/generated/contract.d.ts | 32 ++++ .../generated/contract.d.ts | 32 ++++ .../many-to-many/generated/contract.d.ts | 32 ++++ .../one-to-many/generated/contract.d.ts | 32 ++++ .../filters/_fixture/generated/contract.d.ts | 32 ++++ .../_fixture/optional/generated/contract.d.ts | 32 ++++ .../_fixture/base/generated/contract.d.ts | 32 ++++ .../_fixture/decimal/generated/contract.d.ts | 32 ++++ .../_fixture/json/generated/contract.d.ts | 32 ++++ .../_fixture/25103/generated/contract.d.ts | 32 ++++ .../l2-to-one/generated/contract.d.ts | 32 ++++ .../_fixture/generated/contract.d.ts | 32 ++++ .../_fixture/21356/generated/contract.d.ts | 32 ++++ .../_fixture/21366/generated/contract.d.ts | 32 ++++ .../_fixture/generated/contract.d.ts | 32 ++++ .../_fixture/generated/contract.d.ts | 32 ++++ .../_fixture/generated/contract.d.ts | 32 ++++ .../_fixture/generated/contract.d.ts | 32 ++++ .../_fixture/generated/contract.d.ts | 32 ++++ .../_fixture/generated/contract.d.ts | 32 ++++ .../_fixture/generated/contract.d.ts | 32 ++++ .../_fixture/generated/contract.d.ts | 32 ++++ .../_fixture/generated/contract.d.ts | 32 ++++ .../_fixture/generated/contract.d.ts | 32 ++++ .../_fixture/generated/contract.d.ts | 32 ++++ .../distinct/_fixture/generated/contract.d.ts | 32 ++++ .../_fixture/generated/contract.d.ts | 32 ++++ .../_fixture/generated/contract.d.ts | 32 ++++ .../_fixture/generated/contract.d.ts | 32 ++++ .../_fixture/generated/contract.d.ts | 32 ++++ .../_fixture/generated/contract.d.ts | 32 ++++ .../_fixture/generated/contract.d.ts | 32 ++++ .../_fixture/generated/contract.d.ts | 32 ++++ .../_fixture/generated/contract.d.ts | 32 ++++ .../_fixture/generated/contract.d.ts | 32 ++++ .../_fixture/generated/contract.d.ts | 32 ++++ .../_fixture/generated/contract.d.ts | 32 ++++ .../_fixture/generated/contract.d.ts | 32 ++++ .../_fixture/generated/contract.d.ts | 32 ++++ .../_fixture/generated/contract.d.ts | 32 ++++ .../_fixture/generated/contract.d.ts | 32 ++++ .../_fixture/generated/contract.d.ts | 32 ++++ .../_fixture/generated/contract.d.ts | 32 ++++ .../_fixture/generated/contract.d.ts | 32 ++++ .../_fixture/generated/contract.d.ts | 32 ++++ .../_fixture/generated/contract.d.ts | 32 ++++ .../_fixture/generated/contract.d.ts | 32 ++++ .../_fixture/generated/contract.d.ts | 32 ++++ .../_fixture/generated/contract.d.ts | 32 ++++ .../_fixture/generated/contract.d.ts | 32 ++++ .../_fixture/generated/contract.d.ts | 32 ++++ .../_fixture/generated/contract.d.ts | 32 ++++ .../_fixture/generated/contract.d.ts | 32 ++++ .../_fixture/generated/contract.d.ts | 32 ++++ .../_fixture/generated/contract.d.ts | 32 ++++ .../_fixture/generated/contract.d.ts | 32 ++++ .../_fixture/generated/contract.d.ts | 32 ++++ .../_fixture/generated/contract.d.ts | 32 ++++ .../_fixture/contract.prisma | 4 +- .../_fixture/generated/contract.d.ts | 32 ++++ .../_fixture/generated/contract.d.ts | 32 ++++ .../_fixture/generated/contract.d.ts | 32 ++++ .../_fixture/generated/contract.d.ts | 32 ++++ .../_fixture/generated/contract.d.ts | 32 ++++ .../_fixture/generated/contract.d.ts | 32 ++++ .../_fixture/generated/contract.d.ts | 32 ++++ .../_fixture/generated/contract.d.ts | 32 ++++ .../_fixture/generated/contract.d.ts | 32 ++++ .../_fixture/generated/contract.d.ts | 32 ++++ .../_fixture/generated/contract.d.ts | 32 ++++ .../_fixture/generated/contract.d.ts | 32 ++++ .../_fixture/generated/contract.d.ts | 32 ++++ .../_fixture/generated/contract.d.ts | 32 ++++ .../_fixture/generated/contract.d.ts | 32 ++++ .../_fixture/generated/contract.d.ts | 32 ++++ .../_fixture/generated/contract.d.ts | 32 ++++ .../_fixture/generated/contract.d.ts | 32 ++++ .../_fixture/generated/contract.d.ts | 32 ++++ .../_fixture/generated/contract.d.ts | 32 ++++ .../_fixture/generated/contract.d.ts | 32 ++++ .../_fixture/generated/contract.d.ts | 32 ++++ .../_fixture/generated/contract.d.ts | 32 ++++ .../_fixture/generated/contract.d.ts | 32 ++++ .../_fixture/generated/contract.d.ts | 32 ++++ .../_fixture/generated/contract.d.ts | 32 ++++ .../_fixture/generated/contract.d.ts | 32 ++++ .../_fixture/generated/contract.d.ts | 32 ++++ .../different-names/generated/contract.d.ts | 32 ++++ .../identical-names/generated/contract.d.ts | 32 ++++ .../_fixture/no-map/generated/contract.d.ts | 32 ++++ .../_fixture/generated/contract.d.ts | 32 ++++ .../_fixture/generated/contract.d.ts | 32 ++++ .../_fixture/generated/contract.d.ts | 32 ++++ .../_fixture/generated/contract.d.ts | 32 ++++ .../_fixture/generated/contract.d.ts | 32 ++++ .../_fixture/generated/contract.d.ts | 32 ++++ .../cascade-map/generated/contract.d.ts | 32 ++++ .../cascade-plain/generated/contract.d.ts | 32 ++++ .../noaction-map/generated/contract.d.ts | 32 ++++ .../noaction-plain/generated/contract.d.ts | 32 ++++ .../restrict-map/generated/contract.d.ts | 32 ++++ .../restrict-plain/generated/contract.d.ts | 32 ++++ .../setnull-map/generated/contract.d.ts | 32 ++++ .../setnull-plain/generated/contract.d.ts | 32 ++++ .../cascade-map/generated/contract.d.ts | 32 ++++ .../cascade-plain/generated/contract.d.ts | 32 ++++ .../noaction-map/generated/contract.d.ts | 32 ++++ .../noaction-plain/generated/contract.d.ts | 32 ++++ .../restrict-map/generated/contract.d.ts | 32 ++++ .../restrict-plain/generated/contract.d.ts | 32 ++++ .../setnull-map/generated/contract.d.ts | 32 ++++ .../setnull-plain/generated/contract.d.ts | 32 ++++ .../cascade-nomap/generated/contract.d.ts | 32 ++++ .../default-nomap/generated/contract.d.ts | 32 ++++ .../noaction-nomap/generated/contract.d.ts | 32 ++++ .../restrict-nomap/generated/contract.d.ts | 32 ++++ ...arked-types-round-trip.integration.test.ts | 4 +- .../date-time-types-print.integration.test.ts | 38 +++-- ...rated-values-roundtrip.integration.test.ts | 4 +- .../generated-no-pgvector/contract.d.ts | 32 ++++ .../fixtures/generated/contract.d.ts | 32 ++++ .../generated/contract.d.ts | 32 ++++ .../generated/contract.d.ts | 32 ++++ .../generated/contract.d.ts | 32 ++++ .../fixtures/generated/contract.d.ts | 32 ++++ .../generated/contract.d.ts | 32 ++++ .../generated/contract.d.ts | 32 ++++ .../fixtures/mn-psl/generated/contract.d.ts | 32 ++++ .../generated/contract.d.ts | 32 ++++ .../on-update-only/generated/contract.d.ts | 32 ++++ .../generated/contract.d.ts | 32 ++++ .../generated/contract.d.ts | 32 ++++ .../polymorphism/generated/contract.d.ts | 32 ++++ .../scalar-lists/generated/contract.d.ts | 32 ++++ .../scope-namespace/generated/contract.d.ts | 32 ++++ .../self-relations/generated/contract.d.ts | 32 ++++ .../soft-delete/generated/contract.d.ts | 32 ++++ .../include-codec-canonical-json.test.ts | 4 +- .../_fixture-aliases/generated/contract.d.ts | 32 ++++ .../_fixture-attributes/contract.prisma | 12 +- .../generated/contract.d.ts | 32 ++++ .../_fixture-now/generated/contract.d.ts | 32 ++++ .../generated/contract.d.ts | 32 ++++ .../_fixture/generated/contract.d.ts | 32 ++++ .../detection.test.ts | 148 ++++++++++++++++ .../fixtures/generated/sql-contract.d.ts | 4 - .../app/instructions.md | 107 ++++++++++++ .../extension/instructions.md | 107 ++++++++++++ 393 files changed, 9802 insertions(+), 519 deletions(-) create mode 100644 packages/3-targets/postgres/1-target/src/core/interval-string-codec.ts create mode 100644 packages/3-targets/postgres/1-target/test/explicit-type-codec-wiring.test.ts create mode 100644 packages/3-targets/postgres/1-target/test/interval-string-codec.test.ts rename packages/3-targets/postgres/1-target/{src => test}/temporal-global.d.ts (100%) create mode 100644 packages/3-targets/postgres/1-target/test/timestamp-utc-codecs.test.ts create mode 100644 packages/3-targets/postgres/1-target/test/timestamp-utc-codecs.time-zone.test.ts create mode 100644 packages/3-targets/postgres/1-target/tsconfig.no-temporal-global.json create mode 100644 packages/3-targets/postgres/2-adapter/test/date-time-column-types.test.ts create mode 100644 packages/3-targets/postgres/4-codec-testkit/test/timestamp-utc-now-defaults.integration.test.ts create mode 100644 packages/3-targets/postgres/4-codec-testkit/test/timestamp-utc-runtime-read.integration.test.ts create mode 100644 packages/3-targets/postgres/9-facade/test/no-temporal-global/temporal-field-types.test-d.ts create mode 100644 packages/3-targets/postgres/9-facade/tsconfig.no-temporal-global.json create mode 100644 test/integration/test/upgrade-instructions/explicit-date-time-types/detection.test.ts create mode 100644 upgrade-instructions/pending/explicit-date-time-types/app/instructions.md create mode 100644 upgrade-instructions/pending/explicit-date-time-types/extension/instructions.md diff --git a/apps/telemetry-backend/migrations/snapshots/269d0430c7c70fa502e75feb52d9219c0339daa041c6cc9fb4ecb5ad8d0373b6/contract.d.ts b/apps/telemetry-backend/migrations/snapshots/269d0430c7c70fa502e75feb52d9219c0339daa041c6cc9fb4ecb5ad8d0373b6/contract.d.ts index 30ca7a3ef8e6..5702ea4de0b9 100644 --- a/apps/telemetry-backend/migrations/snapshots/269d0430c7c70fa502e75feb52d9219c0339daa041c6cc9fb4ecb5ad8d0373b6/contract.d.ts +++ b/apps/telemetry-backend/migrations/snapshots/269d0430c7c70fa502e75feb52d9219c0339daa041c6cc9fb4ecb5ad8d0373b6/contract.d.ts @@ -6,12 +6,8 @@ import type { Bit, Char, CodecTypes as PgTypes, - Interval, JsonValue, Numeric, - Time, - Timestamp, - Timestamptz, Timetz, VarBit, Varchar, diff --git a/apps/telemetry-backend/migrations/snapshots/2c0677a6b6e5b60bb328a78d882483d8a00804e00c1e05fba259ff8304882558/contract.d.ts b/apps/telemetry-backend/migrations/snapshots/2c0677a6b6e5b60bb328a78d882483d8a00804e00c1e05fba259ff8304882558/contract.d.ts index 1f1826c5a46c..a7769384d106 100644 --- a/apps/telemetry-backend/migrations/snapshots/2c0677a6b6e5b60bb328a78d882483d8a00804e00c1e05fba259ff8304882558/contract.d.ts +++ b/apps/telemetry-backend/migrations/snapshots/2c0677a6b6e5b60bb328a78d882483d8a00804e00c1e05fba259ff8304882558/contract.d.ts @@ -8,11 +8,7 @@ import type { Varchar } from '@prisma/orm-postgres/target/codec-types'; import type { Numeric } from '@prisma/orm-postgres/target/codec-types'; import type { Bit } from '@prisma/orm-postgres/target/codec-types'; import type { VarBit } from '@prisma/orm-postgres/target/codec-types'; -import type { Timestamp } from '@prisma/orm-postgres/target/codec-types'; -import type { Timestamptz } from '@prisma/orm-postgres/target/codec-types'; -import type { Time } from '@prisma/orm-postgres/target/codec-types'; import type { Timetz } from '@prisma/orm-postgres/target/codec-types'; -import type { Interval } from '@prisma/orm-postgres/target/codec-types'; import type { QueryOperationTypes as PgAdapterQueryOps } from '@prisma/orm-postgres/adapter/operation-types'; import type { diff --git a/apps/telemetry-backend/src/prisma/contract.d.ts b/apps/telemetry-backend/src/prisma/contract.d.ts index 6b9db76303bb..4c48fb3b1095 100644 --- a/apps/telemetry-backend/src/prisma/contract.d.ts +++ b/apps/telemetry-backend/src/prisma/contract.d.ts @@ -42,6 +42,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; @@ -91,6 +95,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; @@ -100,6 +108,14 @@ export type AggregateTypes = { readonly output: 'pg/time-temporal@1'; readonly nullable: true; }; + readonly 'pg/timestamp-date@1': { + readonly output: 'pg/timestamp-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-instant@1': { + readonly output: 'pg/timestamp-instant@1'; + readonly nullable: true; + }; readonly 'pg/timestamp-string@1': { readonly output: 'pg/timestamp-string@1'; readonly nullable: true; @@ -151,6 +167,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; @@ -160,6 +180,14 @@ export type AggregateTypes = { readonly output: 'pg/time-temporal@1'; readonly nullable: true; }; + readonly 'pg/timestamp-date@1': { + readonly output: 'pg/timestamp-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-instant@1': { + readonly output: 'pg/timestamp-instant@1'; + readonly nullable: true; + }; readonly 'pg/timestamp-string@1': { readonly output: 'pg/timestamp-string@1'; readonly nullable: true; @@ -203,6 +231,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; diff --git a/docs/reference/codec-authoring-guide.md b/docs/reference/codec-authoring-guide.md index 337c919f75b4..65e4359cb398 100644 --- a/docs/reference/codec-authoring-guide.md +++ b/docs/reference/codec-authoring-guide.md @@ -56,7 +56,7 @@ An enum compares values with its members, so every enum authoring surface (`enum The consumer-facing [`BigInt`, `BigIntNumber`, and `UnboundedInt` representation choices](./integer-representation-types.md), are documented separately from this contributor guide. -The PostgreSQL temporal codecs pair a `Temporal`-valued codec with a raw-text one for each native type — `pg/timestamptz-temporal@1` reads a `Temporal.Instant`, `pg/timestamptz-string@1` reads PostgreSQL's own text, and the contract names which one a column uses. A text codec's `fromWire` keeps PostgreSQL's year text, ` BC` included, and its `toWire` converts the stored ISO year into it. +The PostgreSQL temporal codecs pair a `Temporal`-valued codec with a raw-text one for each native type — `pg/timestamptz-temporal@1` reads a `Temporal.Instant`, `pg/timestamptz-string@1` reads PostgreSQL's own text, and the contract names which one a column uses. A type may have more codecs: `timestamptz` and `timestamp` also have a `Date` codec, `timestamp` a second `Temporal` codec, `pg/timestamp-instant@1`, that reads the stored date and time as a UTC `Temporal.Instant`, and `interval` a codec that reads an ISO 8601 duration string. A codec of one type differs from its siblings only in the application value, so a new one is a codec, never a new data type. A text codec's `fromWire` keeps PostgreSQL's year text, ` BC` included, and its `toWire` converts the stored ISO year into it. ## Three case studies @@ -589,7 +589,7 @@ A codec's `paramsSchema` is its data type's `params`, referenced and never resta A codec with parameters of its own sets `paramsSchema` to `dataType.params.and(ownKeys)`, or to its own keys alone when the data type declares no `params`, as `arktype/json@1` does. `postgresCodec(...)` and `sqliteCodec(...)` take the adapted codec's params schema from the data type they are given. -A type constructor maps its arguments onto the parameters and names only the codec; the data type follows from the codec. Every argument of a type constructor is a value argument, `{ kind: 'value', dataType }`, and assembly refuses any other kind with `CONTRACT.PACK_CONTRIBUTION_INVALID`; field presets keep their own argument kinds until they are removed. A value argument is a value of the named data type, read and cast as an attribute argument is (ADR 254), so `Vector(1536)` reads `1536` as a `pg/int2` and casts it to `pg/int4`, and `Vector("1536")` is refused. On Postgres every length, precision, scale and SRID is a `pg/int4`, the type of a typmod. A value argument carries no bounds, because the parameter's schema checks the value it gives. Assembly refuses a value argument whose data type no component registers (`CONTRACT.DATA_TYPE_UNREGISTERED`) or that no contract source can write (`CONTRACT.DATA_TYPE_NOT_WRITABLE`). In the TypeScript builder, the pack that registers a data type declares the TypeScript value an argument of it takes as `__dataTypeArguments` in its type, such as `{ 'pg/int4': { argument: number; parameter: number } }`: `argument` is what an author passes, and `parameter` is the type parameter the data type's reader gives for it, and `contract print` writes each argument as a literal of its data type. `inferred: true` marks the constructor written for a column of the data type when no schema names one: `contract infer` and the Prisma 7 reader write it. `contract print` writes it when it produces the column's codec, and otherwise the first constructor in the stack that does, so a `timestamptz` column with the `Temporal` codec prints `DateTime`. It is called with the fewest arguments that give the column's parameters in normal form, so a `numeric(10,0)` column is written `Numeric(10)`. The Prisma 7 reader builds a contract rather than printing PSL, so it passes every parameter the Prisma 7 column gives. At most one constructor per data type carries the mark. A data type with no marked constructor, such as `pg/tsquery`, is still recognised, but `contract infer` cannot write a column of it and fails with `CONTRACT.INFER_COLUMN_TYPE_UNCLAIMED`. +A type constructor maps its arguments onto the parameters and names only the codec; the data type follows from the codec. Every argument of a type constructor is a value argument, `{ kind: 'value', dataType }`, and assembly refuses any other kind with `CONTRACT.PACK_CONTRIBUTION_INVALID`; field presets keep their own argument kinds until they are removed. A value argument is a value of the named data type, read and cast as an attribute argument is (ADR 254), so `Vector(1536)` reads `1536` as a `pg/int2` and casts it to `pg/int4`, and `Vector("1536")` is refused. On Postgres every length, precision, scale and SRID is a `pg/int4`, the type of a typmod. A value argument carries no bounds, because the parameter's schema checks the value it gives. Assembly refuses a value argument whose data type no component registers (`CONTRACT.DATA_TYPE_UNREGISTERED`) or that no contract source can write (`CONTRACT.DATA_TYPE_NOT_WRITABLE`). In the TypeScript builder, the pack that registers a data type declares the TypeScript value an argument of it takes as `__dataTypeArguments` in its type, such as `{ 'pg/int4': { argument: number; parameter: number } }`: `argument` is what an author passes, and `parameter` is the type parameter the data type's reader gives for it, and `contract print` writes each argument as a literal of its data type. `inferred: true` marks the constructor written for a column of the data type when no schema names one: `contract infer` and the Prisma 7 reader write it. `contract print` writes it when it produces the column's codec, and otherwise the first constructor in the stack that does, so a `timestamptz` column with the `Temporal` codec prints `TimestamptzTemporal`, which the PostgreSQL target lists before its second name `DateTime`. It is called with the fewest arguments that give the column's parameters in normal form, so a `numeric(10,0)` column is written `Numeric(10)`. The Prisma 7 reader builds a contract rather than printing PSL, so it passes every parameter the Prisma 7 column gives. At most one constructor per data type carries the mark. A data type with no marked constructor, such as `pg/tsquery`, is still recognised, but `contract infer` cannot write a column of it and fails with `CONTRACT.INFER_COLUMN_TYPE_UNCLAIMED`. ```ts import type { AuthoringTypeNamespace } from '@internal/framework-components/authoring'; diff --git a/docs/reference/error-reference.md b/docs/reference/error-reference.md index 4897b26344c4..3dc27f229994 100644 --- a/docs/reference/error-reference.md +++ b/docs/reference/error-reference.md @@ -990,7 +990,7 @@ A type alias in a `types {}` block has no base, or its base is not a type constr ### PSL_INVALID_ATTRIBUTE_ARGUMENT -An argument of an attribute or a type constructor is invalid. One case is a contributed type alias written with arguments, as in `temporal.CreatedAt(3)`: `temporal.CreatedAt is a type alias and takes no arguments.` Write it bare, `temporal.CreatedAt`, or write the type with the attributes, as in `Timestamptz(3) @onCreate(now())`. Reported at the call, in SQL and Mongo schemas. In a Mongo schema, a `types {}` alias written with arguments is `PSL_UNSUPPORTED_FIELD_TYPE`: `Field "." calls "", which is a type alias, not a type constructor. Remove the arguments.` +An argument of an attribute or a type constructor is invalid. One case is a contributed type alias written with arguments, as in `temporal.CreatedAt(3)`: `temporal.CreatedAt is a type alias and takes no arguments.` Write it bare, `temporal.CreatedAt`, or write the type with the attributes, as in `TimestamptzTemporal(3) @onCreate(now())`. Reported at the call, in SQL and Mongo schemas. In a Mongo schema, a `types {}` alias written with arguments is `PSL_UNSUPPORTED_FIELD_TYPE`: `Field "." calls "", which is a type alias, not a type constructor. Remove the arguments.` The same code reports a discriminator the discriminator rule refuses, in SQL and Mongo schemas alike. At the `@@discriminator` attribute: `Discriminator field "" on model "" must be required, but it is optional. Remove the "?"`, `... must hold one value, but it is a list. Remove the "[]"`, and `... must be text, such as String, but is ""` for a field whose data type is not one of the stack's text data types; a field is text by its data type, so a type alias of `String` is a discriminator. At the `@@base` value, for a discriminator typed by an enum: `Model "" declares @@base value "", which is not a member of enum "", the type of discriminator field ".". Write one of: "", ...`. A refused discriminator or value gets no second diagnostic about an orphaned `@@base` or `@@discriminator`, and neither does a `@@discriminator` or `@@base` attribute that does not parse, such as `@@discriminator(nope)` or `@@base(Asset, 1)`. diff --git a/docs/reference/scalar-types.md b/docs/reference/scalar-types.md index 6e021a903bd0..0bee42f30f4f 100644 --- a/docs/reference/scalar-types.md +++ b/docs/reference/scalar-types.md @@ -80,19 +80,22 @@ These are the current names. The Postgres and SQLite rename project will change | `Real` | — | `pg/float4@1` | `float4` | `number` | | `Float` | `field.float()` | `pg/float8@1` | `float8` | `number` | | `Decimal`, `Numeric(p?, s?)` | `field.decimal()` | `pg/numeric@1` | `numeric` | `string` (decimal text) | -| `DateTime`, `Timestamptz(p?)` | `field.dateTime()` | `pg/timestamptz-temporal@1` | `timestamptz` | `Temporal.Instant` | +| `DateTime`, `TimestamptzTemporal(p?)` | `field.dateTime()` | `pg/timestamptz-temporal@1` | `timestamptz` | `Temporal.Instant` | | `TimestamptzJsDate(p?)` | — | `pg/timestamptz-date@1` | `timestamptz` | `Date` | | `TimestamptzString(p?)` | — | `pg/timestamptz-string@1` | `timestamptz` | `string` | -| `Timestamp(p?)` | — | `pg/timestamp-temporal@1` | `timestamp` | `Temporal.PlainDateTime` | +| `TimestampTemporal(p?)` | — | `pg/timestamp-temporal@1` | `timestamp` | `Temporal.PlainDateTime` | | `TimestampString(p?)` | — | `pg/timestamp-string@1` | `timestamp` | `string` | -| `Date` | — | `pg/date-temporal@1` | `date` | `Temporal.PlainDate` | +| `TimestampJsDate(p?)` | — | `pg/timestamp-date@1` | `timestamp` | `Date`, the stored date and time read as UTC | +| `TimestampInstant(p?)` | — | `pg/timestamp-instant@1` | `timestamp` | `Temporal.Instant`, the stored date and time read as UTC | +| `Date`, `DateTemporal` | — | `pg/date-temporal@1` | `date` | `Temporal.PlainDate` | | `DateString` | — | `pg/date-string@1` | `date` | `string` | -| `Time(p?)` | — | `pg/time-temporal@1` | `time` | `Temporal.PlainTime` | +| `Time(p?)`, `TimeTemporal(p?)` | — | `pg/time-temporal@1` | `time` | `Temporal.PlainTime` | | `TimeString(p?)` | — | `pg/time-string@1` | `time` | `string` | | `Timetz(p?)` | — | `pg/timetz@1` | `timetz` | `string` | | `Uuid` | `field.uuidNative()` | `pg/uuid@1` | `uuid` | `string` | | `Inet` | — | `pg/inet@1` | `inet` | `string` | -| `Interval(p?)` | — | `pg/interval@1` | `interval` | `{ months: number; days: number; micros: bigint }` | +| `IntervalParts(p?)` | — | `pg/interval@1` | `interval` | `{ months: number; days: number; micros: bigint }` | +| `IntervalString(p?)` | — | `pg/interval-string@1` | `interval` | `string`, the ISO 8601 duration, such as `P1Y2M` | | `Bit(n?)` | — | `pg/bit@1` | `bit` | `string` (the bits as `0` and `1`) | | `VarBit(n?)` | — | `pg/varbit@1` | `bit varying` | `string` (the bits as `0` and `1`) | | `Json` | — | `pg/json@1` | `json` | `JsonValue` | @@ -100,9 +103,11 @@ These are the current names. The Postgres and SQLite rename project will change | `Bytes` | `field.bytes()` | `pg/bytea@1` | `bytea` | `Uint8Array` | | `pg.enum(Name)` | — | `pg/enum@1` | the native enum type | `string` | +`TimestampJsDate` and `TimestampInstant` read the date and time a `timestamp` column stores as UTC, and write a value's UTC date and time with no offset, so a value the application writes reads back as the same instant whatever the time zone of the server, the session or the application. A database default is different: PostgreSQL fills a `timestamp` column's `@default(now())` in the session's time zone, so on a server whose `TimeZone` is not UTC that value reads back shifted by the zone's offset. `@onCreate(now())` writes the client's time in UTC and reads back unshifted. A `Date` holds milliseconds, so on a `TimestampJsDate` column with a precision above 3 a read drops the extra digits, and an equality filter with a value read back misses a row stored with microseconds. Use `TimestampInstant` or `TimestampString` to keep them. They refuse `infinity`, `-infinity` and a value after `+275760-09-13T00:00:00`, which no `Date` or `Temporal.Instant` holds, and name `TimestampString`, which holds them. `IntervalString` reads every `IntervalStyle` and writes an ISO 8601 duration; it refuses any other text on write. + A literal default of a date, time, interval or bytes type is written with the tag of its type, as in `@default(timestamptz`2024-01-01T00:00:00Z`)` or `@default(bytea`\x0102`)`; a quoted string is refused with a message that shows the tag. It is stored in the one form its data type stores, whichever codec the column uses and however the default was written, and a default with more fraction digits or more scale than its column holds is refused rather than rounded; [ADR 254](../architecture%20docs/adrs/ADR%20254%20-%20Data%20types%20and%20casts.md#date-and-time-types) states each form. -The tools that write a schema for you use the text date and time types. In a PSL schema, `prisma orm init` writes `createdAt TimestamptzString @default(now())` and `updatedAt temporal.updatedAtString()`. In a TypeScript contract, it writes `field.temporal.createdAtString()` and `field.temporal.updatedAtString()`. All of these are `TimestamptzString` columns. `prisma contract infer` and `prisma7Schema(...)` write `TimestampString(p)`, `TimestamptzString(p)`, `DateString` and `TimeString(p)`. Four types read and write `Temporal` values: `Timestamptz` (also written `DateTime`), `Timestamp`, `Date` and `Time`. An application that uses them needs a global `Temporal`: on a runtime without one, such as Node.js 24, install it, for example with `import 'temporal-polyfill/full/global'`. +The tools that write a schema for you use the text date and time types. In a PSL schema, `prisma orm init` writes `createdAt TimestamptzString @default(now())` and `updatedAt temporal.updatedAtString()`. In a TypeScript contract, it writes `field.temporal.createdAtString()` and `field.temporal.updatedAtString()`. All of these are `TimestamptzString` columns. `prisma contract infer` and `prisma7Schema(...)` write `TimestampString(p)`, `TimestamptzString(p)`, `DateString` and `TimeString(p)`. Five types read and write `Temporal` values: `TimestamptzTemporal` (also written `DateTime`), `TimestampTemporal`, `TimestampInstant`, `DateTemporal` (also written `Date`) and `TimeTemporal` (also written `Time`). Their TypeScript types come from `temporal-spec`, so a project needs no global `Temporal` declarations to type them. An application that uses them needs a global `Temporal`: on a runtime without one, such as Node.js 24, install it, for example with `import 'temporal-polyfill/full/global'`. ## SQLite @@ -134,7 +139,7 @@ The PSL name on each target for a concept, and the Prisma 6/7 name a migrating s | double | `Float` | `Float` | `Float` | `Double` | | decimal | `Decimal` | `Decimal`, `Numeric(p?, s?)` | `Decimal` (stored as text) | `Decimal128` | | boolean | `Boolean` | `Boolean` | — | `Bool` | -| timestamp | `DateTime` | `DateTime`, `Timestamptz(p?)` | `DateTime` | `Date` | +| timestamp | `DateTime` | `DateTime`, `TimestamptzTemporal(p?)` | `DateTime` | `Date` | | text | `String` | `String` | `String` | `String` | | bytes | `Bytes` | `Bytes` | `Bytes` | `Binary` | | JSON | `Json` | `Json`, `Jsonb` | `Json` | `Json` | diff --git a/examples/bundle-size/src/postgres/generated/contract.d.ts b/examples/bundle-size/src/postgres/generated/contract.d.ts index 8fa3efb80080..2e97bc21082b 100644 --- a/examples/bundle-size/src/postgres/generated/contract.d.ts +++ b/examples/bundle-size/src/postgres/generated/contract.d.ts @@ -43,6 +43,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; @@ -92,6 +96,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; @@ -101,6 +109,14 @@ export type AggregateTypes = { readonly output: 'pg/time-temporal@1'; readonly nullable: true; }; + readonly 'pg/timestamp-date@1': { + readonly output: 'pg/timestamp-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-instant@1': { + readonly output: 'pg/timestamp-instant@1'; + readonly nullable: true; + }; readonly 'pg/timestamp-string@1': { readonly output: 'pg/timestamp-string@1'; readonly nullable: true; @@ -152,6 +168,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; @@ -161,6 +181,14 @@ export type AggregateTypes = { readonly output: 'pg/time-temporal@1'; readonly nullable: true; }; + readonly 'pg/timestamp-date@1': { + readonly output: 'pg/timestamp-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-instant@1': { + readonly output: 'pg/timestamp-instant@1'; + readonly nullable: true; + }; readonly 'pg/timestamp-string@1': { readonly output: 'pg/timestamp-string@1'; readonly nullable: true; @@ -204,6 +232,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; diff --git a/examples/multi-extension-monorepo/app/src/contract.d.ts b/examples/multi-extension-monorepo/app/src/contract.d.ts index f824b988935d..3b6e63ce9c6a 100644 --- a/examples/multi-extension-monorepo/app/src/contract.d.ts +++ b/examples/multi-extension-monorepo/app/src/contract.d.ts @@ -42,6 +42,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; @@ -91,6 +95,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; @@ -100,6 +108,14 @@ export type AggregateTypes = { readonly output: 'pg/time-temporal@1'; readonly nullable: true; }; + readonly 'pg/timestamp-date@1': { + readonly output: 'pg/timestamp-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-instant@1': { + readonly output: 'pg/timestamp-instant@1'; + readonly nullable: true; + }; readonly 'pg/timestamp-string@1': { readonly output: 'pg/timestamp-string@1'; readonly nullable: true; @@ -151,6 +167,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; @@ -160,6 +180,14 @@ export type AggregateTypes = { readonly output: 'pg/time-temporal@1'; readonly nullable: true; }; + readonly 'pg/timestamp-date@1': { + readonly output: 'pg/timestamp-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-instant@1': { + readonly output: 'pg/timestamp-instant@1'; + readonly nullable: true; + }; readonly 'pg/timestamp-string@1': { readonly output: 'pg/timestamp-string@1'; readonly nullable: true; @@ -203,6 +231,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; diff --git a/examples/multi-extension-monorepo/packages/audit/migrations/snapshots/d60ebdd877de6283fafbbe0dc9a4fa13c1197009a53ba95ed37ba04fa3640cb1/contract.d.ts b/examples/multi-extension-monorepo/packages/audit/migrations/snapshots/d60ebdd877de6283fafbbe0dc9a4fa13c1197009a53ba95ed37ba04fa3640cb1/contract.d.ts index 3488479cd7fd..796b7442a900 100644 --- a/examples/multi-extension-monorepo/packages/audit/migrations/snapshots/d60ebdd877de6283fafbbe0dc9a4fa13c1197009a53ba95ed37ba04fa3640cb1/contract.d.ts +++ b/examples/multi-extension-monorepo/packages/audit/migrations/snapshots/d60ebdd877de6283fafbbe0dc9a4fa13c1197009a53ba95ed37ba04fa3640cb1/contract.d.ts @@ -42,6 +42,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; @@ -91,6 +95,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; @@ -100,6 +108,14 @@ export type AggregateTypes = { readonly output: 'pg/time-temporal@1'; readonly nullable: true; }; + readonly 'pg/timestamp-date@1': { + readonly output: 'pg/timestamp-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-instant@1': { + readonly output: 'pg/timestamp-instant@1'; + readonly nullable: true; + }; readonly 'pg/timestamp-string@1': { readonly output: 'pg/timestamp-string@1'; readonly nullable: true; @@ -151,6 +167,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; @@ -160,6 +180,14 @@ export type AggregateTypes = { readonly output: 'pg/time-temporal@1'; readonly nullable: true; }; + readonly 'pg/timestamp-date@1': { + readonly output: 'pg/timestamp-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-instant@1': { + readonly output: 'pg/timestamp-instant@1'; + readonly nullable: true; + }; readonly 'pg/timestamp-string@1': { readonly output: 'pg/timestamp-string@1'; readonly nullable: true; @@ -203,6 +231,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; diff --git a/examples/multi-extension-monorepo/packages/audit/src/contract.d.ts b/examples/multi-extension-monorepo/packages/audit/src/contract.d.ts index 3488479cd7fd..796b7442a900 100644 --- a/examples/multi-extension-monorepo/packages/audit/src/contract.d.ts +++ b/examples/multi-extension-monorepo/packages/audit/src/contract.d.ts @@ -42,6 +42,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; @@ -91,6 +95,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; @@ -100,6 +108,14 @@ export type AggregateTypes = { readonly output: 'pg/time-temporal@1'; readonly nullable: true; }; + readonly 'pg/timestamp-date@1': { + readonly output: 'pg/timestamp-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-instant@1': { + readonly output: 'pg/timestamp-instant@1'; + readonly nullable: true; + }; readonly 'pg/timestamp-string@1': { readonly output: 'pg/timestamp-string@1'; readonly nullable: true; @@ -151,6 +167,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; @@ -160,6 +180,14 @@ export type AggregateTypes = { readonly output: 'pg/time-temporal@1'; readonly nullable: true; }; + readonly 'pg/timestamp-date@1': { + readonly output: 'pg/timestamp-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-instant@1': { + readonly output: 'pg/timestamp-instant@1'; + readonly nullable: true; + }; readonly 'pg/timestamp-string@1': { readonly output: 'pg/timestamp-string@1'; readonly nullable: true; @@ -203,6 +231,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; diff --git a/examples/multi-extension-monorepo/packages/feature-flags/migrations/snapshots/9ea058903cdc9636107c83c1a6bbb6c3f28d92d4515cff00c31c2b4e37bb5bd4/contract.d.ts b/examples/multi-extension-monorepo/packages/feature-flags/migrations/snapshots/9ea058903cdc9636107c83c1a6bbb6c3f28d92d4515cff00c31c2b4e37bb5bd4/contract.d.ts index 24f1c76a2e72..578d9aa80d33 100644 --- a/examples/multi-extension-monorepo/packages/feature-flags/migrations/snapshots/9ea058903cdc9636107c83c1a6bbb6c3f28d92d4515cff00c31c2b4e37bb5bd4/contract.d.ts +++ b/examples/multi-extension-monorepo/packages/feature-flags/migrations/snapshots/9ea058903cdc9636107c83c1a6bbb6c3f28d92d4515cff00c31c2b4e37bb5bd4/contract.d.ts @@ -42,6 +42,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; @@ -91,6 +95,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; @@ -100,6 +108,14 @@ export type AggregateTypes = { readonly output: 'pg/time-temporal@1'; readonly nullable: true; }; + readonly 'pg/timestamp-date@1': { + readonly output: 'pg/timestamp-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-instant@1': { + readonly output: 'pg/timestamp-instant@1'; + readonly nullable: true; + }; readonly 'pg/timestamp-string@1': { readonly output: 'pg/timestamp-string@1'; readonly nullable: true; @@ -151,6 +167,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; @@ -160,6 +180,14 @@ export type AggregateTypes = { readonly output: 'pg/time-temporal@1'; readonly nullable: true; }; + readonly 'pg/timestamp-date@1': { + readonly output: 'pg/timestamp-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-instant@1': { + readonly output: 'pg/timestamp-instant@1'; + readonly nullable: true; + }; readonly 'pg/timestamp-string@1': { readonly output: 'pg/timestamp-string@1'; readonly nullable: true; @@ -203,6 +231,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; diff --git a/examples/multi-extension-monorepo/packages/feature-flags/src/contract.d.ts b/examples/multi-extension-monorepo/packages/feature-flags/src/contract.d.ts index 24f1c76a2e72..578d9aa80d33 100644 --- a/examples/multi-extension-monorepo/packages/feature-flags/src/contract.d.ts +++ b/examples/multi-extension-monorepo/packages/feature-flags/src/contract.d.ts @@ -42,6 +42,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; @@ -91,6 +95,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; @@ -100,6 +108,14 @@ export type AggregateTypes = { readonly output: 'pg/time-temporal@1'; readonly nullable: true; }; + readonly 'pg/timestamp-date@1': { + readonly output: 'pg/timestamp-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-instant@1': { + readonly output: 'pg/timestamp-instant@1'; + readonly nullable: true; + }; readonly 'pg/timestamp-string@1': { readonly output: 'pg/timestamp-string@1'; readonly nullable: true; @@ -151,6 +167,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; @@ -160,6 +180,14 @@ export type AggregateTypes = { readonly output: 'pg/time-temporal@1'; readonly nullable: true; }; + readonly 'pg/timestamp-date@1': { + readonly output: 'pg/timestamp-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-instant@1': { + readonly output: 'pg/timestamp-instant@1'; + readonly nullable: true; + }; readonly 'pg/timestamp-string@1': { readonly output: 'pg/timestamp-string@1'; readonly nullable: true; @@ -203,6 +231,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; diff --git a/examples/paradedb-demo/src/prisma/contract.d.ts b/examples/paradedb-demo/src/prisma/contract.d.ts index 88ebb99c1466..55aef7a45f14 100644 --- a/examples/paradedb-demo/src/prisma/contract.d.ts +++ b/examples/paradedb-demo/src/prisma/contract.d.ts @@ -44,6 +44,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; @@ -93,6 +97,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; @@ -102,6 +110,14 @@ export type AggregateTypes = { readonly output: 'pg/time-temporal@1'; readonly nullable: true; }; + readonly 'pg/timestamp-date@1': { + readonly output: 'pg/timestamp-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-instant@1': { + readonly output: 'pg/timestamp-instant@1'; + readonly nullable: true; + }; readonly 'pg/timestamp-string@1': { readonly output: 'pg/timestamp-string@1'; readonly nullable: true; @@ -153,6 +169,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; @@ -162,6 +182,14 @@ export type AggregateTypes = { readonly output: 'pg/time-temporal@1'; readonly nullable: true; }; + readonly 'pg/timestamp-date@1': { + readonly output: 'pg/timestamp-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-instant@1': { + readonly output: 'pg/timestamp-instant@1'; + readonly nullable: true; + }; readonly 'pg/timestamp-string@1': { readonly output: 'pg/timestamp-string@1'; readonly nullable: true; @@ -205,6 +233,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; diff --git a/examples/prisma-8-cloudflare-worker/src/prisma/contract.d.ts b/examples/prisma-8-cloudflare-worker/src/prisma/contract.d.ts index 821a1af79612..6b0d7d4a3e13 100644 --- a/examples/prisma-8-cloudflare-worker/src/prisma/contract.d.ts +++ b/examples/prisma-8-cloudflare-worker/src/prisma/contract.d.ts @@ -44,6 +44,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; @@ -93,6 +97,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; @@ -102,6 +110,14 @@ export type AggregateTypes = { readonly output: 'pg/time-temporal@1'; readonly nullable: true; }; + readonly 'pg/timestamp-date@1': { + readonly output: 'pg/timestamp-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-instant@1': { + readonly output: 'pg/timestamp-instant@1'; + readonly nullable: true; + }; readonly 'pg/timestamp-string@1': { readonly output: 'pg/timestamp-string@1'; readonly nullable: true; @@ -153,6 +169,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; @@ -162,6 +182,14 @@ export type AggregateTypes = { readonly output: 'pg/time-temporal@1'; readonly nullable: true; }; + readonly 'pg/timestamp-date@1': { + readonly output: 'pg/timestamp-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-instant@1': { + readonly output: 'pg/timestamp-instant@1'; + readonly nullable: true; + }; readonly 'pg/timestamp-string@1': { readonly output: 'pg/timestamp-string@1'; readonly nullable: true; @@ -205,6 +233,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; diff --git a/examples/prisma-8-demo/fixtures/converging-branches/migrations/snapshots/6c26c85a74b9429d6bb929df0c3e5035352fdb4cc2ee0e711a7a215e490041b1/contract.d.ts b/examples/prisma-8-demo/fixtures/converging-branches/migrations/snapshots/6c26c85a74b9429d6bb929df0c3e5035352fdb4cc2ee0e711a7a215e490041b1/contract.d.ts index 15023119ed7a..ab185e4a2d53 100644 --- a/examples/prisma-8-demo/fixtures/converging-branches/migrations/snapshots/6c26c85a74b9429d6bb929df0c3e5035352fdb4cc2ee0e711a7a215e490041b1/contract.d.ts +++ b/examples/prisma-8-demo/fixtures/converging-branches/migrations/snapshots/6c26c85a74b9429d6bb929df0c3e5035352fdb4cc2ee0e711a7a215e490041b1/contract.d.ts @@ -6,12 +6,8 @@ import type { Bit, Char, CodecTypes as PgTypes, - Interval, JsonValue, Numeric, - Time, - Timestamp, - Timestamptz, Timetz, VarBit, Varchar, diff --git a/examples/prisma-8-demo/fixtures/converging-branches/migrations/snapshots/7b7a028bf17c118bdec714439df6456964c13d3d4a4dbd14869b888412a0bd01/contract.d.ts b/examples/prisma-8-demo/fixtures/converging-branches/migrations/snapshots/7b7a028bf17c118bdec714439df6456964c13d3d4a4dbd14869b888412a0bd01/contract.d.ts index 5fe6a104599a..eaac5bbcadb5 100644 --- a/examples/prisma-8-demo/fixtures/converging-branches/migrations/snapshots/7b7a028bf17c118bdec714439df6456964c13d3d4a4dbd14869b888412a0bd01/contract.d.ts +++ b/examples/prisma-8-demo/fixtures/converging-branches/migrations/snapshots/7b7a028bf17c118bdec714439df6456964c13d3d4a4dbd14869b888412a0bd01/contract.d.ts @@ -6,12 +6,8 @@ import type { Bit, Char, CodecTypes as PgTypes, - Interval, JsonValue, Numeric, - Time, - Timestamp, - Timestamptz, Timetz, VarBit, Varchar, diff --git a/examples/prisma-8-demo/fixtures/converging-branches/migrations/snapshots/df82ed1c905933192eca7c1d92ac6f1f8ada22d1d712865e5afd4400c554de2e/contract.d.ts b/examples/prisma-8-demo/fixtures/converging-branches/migrations/snapshots/df82ed1c905933192eca7c1d92ac6f1f8ada22d1d712865e5afd4400c554de2e/contract.d.ts index 468ba2b96c8d..abfa79686b19 100644 --- a/examples/prisma-8-demo/fixtures/converging-branches/migrations/snapshots/df82ed1c905933192eca7c1d92ac6f1f8ada22d1d712865e5afd4400c554de2e/contract.d.ts +++ b/examples/prisma-8-demo/fixtures/converging-branches/migrations/snapshots/df82ed1c905933192eca7c1d92ac6f1f8ada22d1d712865e5afd4400c554de2e/contract.d.ts @@ -6,12 +6,8 @@ import type { Bit, Char, CodecTypes as PgTypes, - Interval, JsonValue, Numeric, - Time, - Timestamp, - Timestamptz, Timetz, VarBit, Varchar, diff --git a/examples/prisma-8-demo/fixtures/converging-branches/migrations/snapshots/f6b0a58b2f0675a0d020673b1ac1713f6794c66773c1b33e2a2014c6a61f9a06/contract.d.ts b/examples/prisma-8-demo/fixtures/converging-branches/migrations/snapshots/f6b0a58b2f0675a0d020673b1ac1713f6794c66773c1b33e2a2014c6a61f9a06/contract.d.ts index 86d9111e492d..c847c1e829c8 100644 --- a/examples/prisma-8-demo/fixtures/converging-branches/migrations/snapshots/f6b0a58b2f0675a0d020673b1ac1713f6794c66773c1b33e2a2014c6a61f9a06/contract.d.ts +++ b/examples/prisma-8-demo/fixtures/converging-branches/migrations/snapshots/f6b0a58b2f0675a0d020673b1ac1713f6794c66773c1b33e2a2014c6a61f9a06/contract.d.ts @@ -6,12 +6,8 @@ import type { Bit, Char, CodecTypes as PgTypes, - Interval, JsonValue, Numeric, - Time, - Timestamp, - Timestamptz, Timetz, VarBit, Varchar, diff --git a/examples/prisma-8-demo/fixtures/converging-branches/migrations/snapshots/f984b41c72c9ae805561bd27891ef309f95bb5eda852892fc3e37b620f897b38/contract.d.ts b/examples/prisma-8-demo/fixtures/converging-branches/migrations/snapshots/f984b41c72c9ae805561bd27891ef309f95bb5eda852892fc3e37b620f897b38/contract.d.ts index 39fb6c3f61d6..2c64de55deb8 100644 --- a/examples/prisma-8-demo/fixtures/converging-branches/migrations/snapshots/f984b41c72c9ae805561bd27891ef309f95bb5eda852892fc3e37b620f897b38/contract.d.ts +++ b/examples/prisma-8-demo/fixtures/converging-branches/migrations/snapshots/f984b41c72c9ae805561bd27891ef309f95bb5eda852892fc3e37b620f897b38/contract.d.ts @@ -6,12 +6,8 @@ import type { Bit, Char, CodecTypes as PgTypes, - Interval, JsonValue, Numeric, - Time, - Timestamp, - Timestamptz, Timetz, VarBit, Varchar, diff --git a/examples/prisma-8-demo/fixtures/diamond/migrations/snapshots/6b4636f85cc2d7b2f8d4a70bd2c520246b4bbe786aa55d3b77592fae5c4d458f/contract.d.ts b/examples/prisma-8-demo/fixtures/diamond/migrations/snapshots/6b4636f85cc2d7b2f8d4a70bd2c520246b4bbe786aa55d3b77592fae5c4d458f/contract.d.ts index c61e89011f50..bc9abcd0a92f 100644 --- a/examples/prisma-8-demo/fixtures/diamond/migrations/snapshots/6b4636f85cc2d7b2f8d4a70bd2c520246b4bbe786aa55d3b77592fae5c4d458f/contract.d.ts +++ b/examples/prisma-8-demo/fixtures/diamond/migrations/snapshots/6b4636f85cc2d7b2f8d4a70bd2c520246b4bbe786aa55d3b77592fae5c4d458f/contract.d.ts @@ -6,12 +6,8 @@ import type { Bit, Char, CodecTypes as PgTypes, - Interval, JsonValue, Numeric, - Time, - Timestamp, - Timestamptz, Timetz, VarBit, Varchar, diff --git a/examples/prisma-8-demo/fixtures/diamond/migrations/snapshots/6c26c85a74b9429d6bb929df0c3e5035352fdb4cc2ee0e711a7a215e490041b1/contract.d.ts b/examples/prisma-8-demo/fixtures/diamond/migrations/snapshots/6c26c85a74b9429d6bb929df0c3e5035352fdb4cc2ee0e711a7a215e490041b1/contract.d.ts index 15023119ed7a..ab185e4a2d53 100644 --- a/examples/prisma-8-demo/fixtures/diamond/migrations/snapshots/6c26c85a74b9429d6bb929df0c3e5035352fdb4cc2ee0e711a7a215e490041b1/contract.d.ts +++ b/examples/prisma-8-demo/fixtures/diamond/migrations/snapshots/6c26c85a74b9429d6bb929df0c3e5035352fdb4cc2ee0e711a7a215e490041b1/contract.d.ts @@ -6,12 +6,8 @@ import type { Bit, Char, CodecTypes as PgTypes, - Interval, JsonValue, Numeric, - Time, - Timestamp, - Timestamptz, Timetz, VarBit, Varchar, diff --git a/examples/prisma-8-demo/fixtures/diamond/migrations/snapshots/df82ed1c905933192eca7c1d92ac6f1f8ada22d1d712865e5afd4400c554de2e/contract.d.ts b/examples/prisma-8-demo/fixtures/diamond/migrations/snapshots/df82ed1c905933192eca7c1d92ac6f1f8ada22d1d712865e5afd4400c554de2e/contract.d.ts index 468ba2b96c8d..abfa79686b19 100644 --- a/examples/prisma-8-demo/fixtures/diamond/migrations/snapshots/df82ed1c905933192eca7c1d92ac6f1f8ada22d1d712865e5afd4400c554de2e/contract.d.ts +++ b/examples/prisma-8-demo/fixtures/diamond/migrations/snapshots/df82ed1c905933192eca7c1d92ac6f1f8ada22d1d712865e5afd4400c554de2e/contract.d.ts @@ -6,12 +6,8 @@ import type { Bit, Char, CodecTypes as PgTypes, - Interval, JsonValue, Numeric, - Time, - Timestamp, - Timestamptz, Timetz, VarBit, Varchar, diff --git a/examples/prisma-8-demo/fixtures/diamond/migrations/snapshots/f984b41c72c9ae805561bd27891ef309f95bb5eda852892fc3e37b620f897b38/contract.d.ts b/examples/prisma-8-demo/fixtures/diamond/migrations/snapshots/f984b41c72c9ae805561bd27891ef309f95bb5eda852892fc3e37b620f897b38/contract.d.ts index 39fb6c3f61d6..2c64de55deb8 100644 --- a/examples/prisma-8-demo/fixtures/diamond/migrations/snapshots/f984b41c72c9ae805561bd27891ef309f95bb5eda852892fc3e37b620f897b38/contract.d.ts +++ b/examples/prisma-8-demo/fixtures/diamond/migrations/snapshots/f984b41c72c9ae805561bd27891ef309f95bb5eda852892fc3e37b620f897b38/contract.d.ts @@ -6,12 +6,8 @@ import type { Bit, Char, CodecTypes as PgTypes, - Interval, JsonValue, Numeric, - Time, - Timestamp, - Timestamptz, Timetz, VarBit, Varchar, diff --git a/examples/prisma-8-demo/fixtures/long-spine/migrations/snapshots/3539138011365388d750c8a37a053403de07308eb1d872451a33d9655c9f1dde/contract.d.ts b/examples/prisma-8-demo/fixtures/long-spine/migrations/snapshots/3539138011365388d750c8a37a053403de07308eb1d872451a33d9655c9f1dde/contract.d.ts index 2a8a9c82aced..7221cd7ca875 100644 --- a/examples/prisma-8-demo/fixtures/long-spine/migrations/snapshots/3539138011365388d750c8a37a053403de07308eb1d872451a33d9655c9f1dde/contract.d.ts +++ b/examples/prisma-8-demo/fixtures/long-spine/migrations/snapshots/3539138011365388d750c8a37a053403de07308eb1d872451a33d9655c9f1dde/contract.d.ts @@ -6,12 +6,8 @@ import type { Bit, Char, CodecTypes as PgTypes, - Interval, JsonValue, Numeric, - Time, - Timestamp, - Timestamptz, Timetz, VarBit, Varchar, diff --git a/examples/prisma-8-demo/fixtures/long-spine/migrations/snapshots/4ae263c38bb0abf30b3daf57d2b002997c15260b0e5528bd924969c25f3d00bb/contract.d.ts b/examples/prisma-8-demo/fixtures/long-spine/migrations/snapshots/4ae263c38bb0abf30b3daf57d2b002997c15260b0e5528bd924969c25f3d00bb/contract.d.ts index 810513406e1c..4273a713670d 100644 --- a/examples/prisma-8-demo/fixtures/long-spine/migrations/snapshots/4ae263c38bb0abf30b3daf57d2b002997c15260b0e5528bd924969c25f3d00bb/contract.d.ts +++ b/examples/prisma-8-demo/fixtures/long-spine/migrations/snapshots/4ae263c38bb0abf30b3daf57d2b002997c15260b0e5528bd924969c25f3d00bb/contract.d.ts @@ -6,12 +6,8 @@ import type { Bit, Char, CodecTypes as PgTypes, - Interval, JsonValue, Numeric, - Time, - Timestamp, - Timestamptz, Timetz, VarBit, Varchar, diff --git a/examples/prisma-8-demo/fixtures/long-spine/migrations/snapshots/6c26c85a74b9429d6bb929df0c3e5035352fdb4cc2ee0e711a7a215e490041b1/contract.d.ts b/examples/prisma-8-demo/fixtures/long-spine/migrations/snapshots/6c26c85a74b9429d6bb929df0c3e5035352fdb4cc2ee0e711a7a215e490041b1/contract.d.ts index 15023119ed7a..ab185e4a2d53 100644 --- a/examples/prisma-8-demo/fixtures/long-spine/migrations/snapshots/6c26c85a74b9429d6bb929df0c3e5035352fdb4cc2ee0e711a7a215e490041b1/contract.d.ts +++ b/examples/prisma-8-demo/fixtures/long-spine/migrations/snapshots/6c26c85a74b9429d6bb929df0c3e5035352fdb4cc2ee0e711a7a215e490041b1/contract.d.ts @@ -6,12 +6,8 @@ import type { Bit, Char, CodecTypes as PgTypes, - Interval, JsonValue, Numeric, - Time, - Timestamp, - Timestamptz, Timetz, VarBit, Varchar, diff --git a/examples/prisma-8-demo/fixtures/long-spine/migrations/snapshots/6dbf0d57fcfe6e0235a0dfa6cc07b1162c6f1f7656b281db92a5d7fb522b2e22/contract.d.ts b/examples/prisma-8-demo/fixtures/long-spine/migrations/snapshots/6dbf0d57fcfe6e0235a0dfa6cc07b1162c6f1f7656b281db92a5d7fb522b2e22/contract.d.ts index 639dffa2f228..29efeb739d50 100644 --- a/examples/prisma-8-demo/fixtures/long-spine/migrations/snapshots/6dbf0d57fcfe6e0235a0dfa6cc07b1162c6f1f7656b281db92a5d7fb522b2e22/contract.d.ts +++ b/examples/prisma-8-demo/fixtures/long-spine/migrations/snapshots/6dbf0d57fcfe6e0235a0dfa6cc07b1162c6f1f7656b281db92a5d7fb522b2e22/contract.d.ts @@ -6,12 +6,8 @@ import type { Bit, Char, CodecTypes as PgTypes, - Interval, JsonValue, Numeric, - Time, - Timestamp, - Timestamptz, Timetz, VarBit, Varchar, diff --git a/examples/prisma-8-demo/fixtures/long-spine/migrations/snapshots/b0a91f687675c5a96fb07e17b02fbb9ed5b873e2c0152b9985020f24f12a6109/contract.d.ts b/examples/prisma-8-demo/fixtures/long-spine/migrations/snapshots/b0a91f687675c5a96fb07e17b02fbb9ed5b873e2c0152b9985020f24f12a6109/contract.d.ts index d0aef3a7d109..d3e70ef6400d 100644 --- a/examples/prisma-8-demo/fixtures/long-spine/migrations/snapshots/b0a91f687675c5a96fb07e17b02fbb9ed5b873e2c0152b9985020f24f12a6109/contract.d.ts +++ b/examples/prisma-8-demo/fixtures/long-spine/migrations/snapshots/b0a91f687675c5a96fb07e17b02fbb9ed5b873e2c0152b9985020f24f12a6109/contract.d.ts @@ -6,12 +6,8 @@ import type { Bit, Char, CodecTypes as PgTypes, - Interval, JsonValue, Numeric, - Time, - Timestamp, - Timestamptz, Timetz, VarBit, Varchar, diff --git a/examples/prisma-8-demo/fixtures/long-spine/migrations/snapshots/c5aeaafc168786f8e7ac8bb9b14b6469610f67cd640c6f2d289922104545d7a0/contract.d.ts b/examples/prisma-8-demo/fixtures/long-spine/migrations/snapshots/c5aeaafc168786f8e7ac8bb9b14b6469610f67cd640c6f2d289922104545d7a0/contract.d.ts index 86ec73885572..f0e6a7969417 100644 --- a/examples/prisma-8-demo/fixtures/long-spine/migrations/snapshots/c5aeaafc168786f8e7ac8bb9b14b6469610f67cd640c6f2d289922104545d7a0/contract.d.ts +++ b/examples/prisma-8-demo/fixtures/long-spine/migrations/snapshots/c5aeaafc168786f8e7ac8bb9b14b6469610f67cd640c6f2d289922104545d7a0/contract.d.ts @@ -6,12 +6,8 @@ import type { Bit, Char, CodecTypes as PgTypes, - Interval, JsonValue, Numeric, - Time, - Timestamp, - Timestamptz, Timetz, VarBit, Varchar, diff --git a/examples/prisma-8-demo/fixtures/long-spine/migrations/snapshots/d5fdae79e33040f9cc33f8c56b9a65f1ce1f86d941198f9d6281abe88d762a55/contract.d.ts b/examples/prisma-8-demo/fixtures/long-spine/migrations/snapshots/d5fdae79e33040f9cc33f8c56b9a65f1ce1f86d941198f9d6281abe88d762a55/contract.d.ts index 52a7f1183e58..e20f335898ce 100644 --- a/examples/prisma-8-demo/fixtures/long-spine/migrations/snapshots/d5fdae79e33040f9cc33f8c56b9a65f1ce1f86d941198f9d6281abe88d762a55/contract.d.ts +++ b/examples/prisma-8-demo/fixtures/long-spine/migrations/snapshots/d5fdae79e33040f9cc33f8c56b9a65f1ce1f86d941198f9d6281abe88d762a55/contract.d.ts @@ -6,12 +6,8 @@ import type { Bit, Char, CodecTypes as PgTypes, - Interval, JsonValue, Numeric, - Time, - Timestamp, - Timestamptz, Timetz, VarBit, Varchar, diff --git a/examples/prisma-8-demo/fixtures/long-spine/migrations/snapshots/df82ed1c905933192eca7c1d92ac6f1f8ada22d1d712865e5afd4400c554de2e/contract.d.ts b/examples/prisma-8-demo/fixtures/long-spine/migrations/snapshots/df82ed1c905933192eca7c1d92ac6f1f8ada22d1d712865e5afd4400c554de2e/contract.d.ts index 468ba2b96c8d..abfa79686b19 100644 --- a/examples/prisma-8-demo/fixtures/long-spine/migrations/snapshots/df82ed1c905933192eca7c1d92ac6f1f8ada22d1d712865e5afd4400c554de2e/contract.d.ts +++ b/examples/prisma-8-demo/fixtures/long-spine/migrations/snapshots/df82ed1c905933192eca7c1d92ac6f1f8ada22d1d712865e5afd4400c554de2e/contract.d.ts @@ -6,12 +6,8 @@ import type { Bit, Char, CodecTypes as PgTypes, - Interval, JsonValue, Numeric, - Time, - Timestamp, - Timestamptz, Timetz, VarBit, Varchar, diff --git a/examples/prisma-8-demo/fixtures/long-spine/migrations/snapshots/f778f751881b602d067b69950a97081f5850d76e8edeee9c16cf14245114224e/contract.d.ts b/examples/prisma-8-demo/fixtures/long-spine/migrations/snapshots/f778f751881b602d067b69950a97081f5850d76e8edeee9c16cf14245114224e/contract.d.ts index 6e3557a8ee1d..1899c3b1d2e4 100644 --- a/examples/prisma-8-demo/fixtures/long-spine/migrations/snapshots/f778f751881b602d067b69950a97081f5850d76e8edeee9c16cf14245114224e/contract.d.ts +++ b/examples/prisma-8-demo/fixtures/long-spine/migrations/snapshots/f778f751881b602d067b69950a97081f5850d76e8edeee9c16cf14245114224e/contract.d.ts @@ -6,12 +6,8 @@ import type { Bit, Char, CodecTypes as PgTypes, - Interval, JsonValue, Numeric, - Time, - Timestamp, - Timestamptz, Timetz, VarBit, Varchar, diff --git a/examples/prisma-8-demo/fixtures/multi-branch/migrations/snapshots/10cdd020e0f2e31ec61d010c58b77fbed5fae1c1919e21839ba1d2bb82b6aef6/contract.d.ts b/examples/prisma-8-demo/fixtures/multi-branch/migrations/snapshots/10cdd020e0f2e31ec61d010c58b77fbed5fae1c1919e21839ba1d2bb82b6aef6/contract.d.ts index 72ecfe3fb4a0..d473d9ae02cd 100644 --- a/examples/prisma-8-demo/fixtures/multi-branch/migrations/snapshots/10cdd020e0f2e31ec61d010c58b77fbed5fae1c1919e21839ba1d2bb82b6aef6/contract.d.ts +++ b/examples/prisma-8-demo/fixtures/multi-branch/migrations/snapshots/10cdd020e0f2e31ec61d010c58b77fbed5fae1c1919e21839ba1d2bb82b6aef6/contract.d.ts @@ -6,12 +6,8 @@ import type { Bit, Char, CodecTypes as PgTypes, - Interval, JsonValue, Numeric, - Time, - Timestamp, - Timestamptz, Timetz, VarBit, Varchar, diff --git a/examples/prisma-8-demo/fixtures/multi-branch/migrations/snapshots/4ae263c38bb0abf30b3daf57d2b002997c15260b0e5528bd924969c25f3d00bb/contract.d.ts b/examples/prisma-8-demo/fixtures/multi-branch/migrations/snapshots/4ae263c38bb0abf30b3daf57d2b002997c15260b0e5528bd924969c25f3d00bb/contract.d.ts index 810513406e1c..4273a713670d 100644 --- a/examples/prisma-8-demo/fixtures/multi-branch/migrations/snapshots/4ae263c38bb0abf30b3daf57d2b002997c15260b0e5528bd924969c25f3d00bb/contract.d.ts +++ b/examples/prisma-8-demo/fixtures/multi-branch/migrations/snapshots/4ae263c38bb0abf30b3daf57d2b002997c15260b0e5528bd924969c25f3d00bb/contract.d.ts @@ -6,12 +6,8 @@ import type { Bit, Char, CodecTypes as PgTypes, - Interval, JsonValue, Numeric, - Time, - Timestamp, - Timestamptz, Timetz, VarBit, Varchar, diff --git a/examples/prisma-8-demo/fixtures/multi-branch/migrations/snapshots/6c26c85a74b9429d6bb929df0c3e5035352fdb4cc2ee0e711a7a215e490041b1/contract.d.ts b/examples/prisma-8-demo/fixtures/multi-branch/migrations/snapshots/6c26c85a74b9429d6bb929df0c3e5035352fdb4cc2ee0e711a7a215e490041b1/contract.d.ts index 15023119ed7a..ab185e4a2d53 100644 --- a/examples/prisma-8-demo/fixtures/multi-branch/migrations/snapshots/6c26c85a74b9429d6bb929df0c3e5035352fdb4cc2ee0e711a7a215e490041b1/contract.d.ts +++ b/examples/prisma-8-demo/fixtures/multi-branch/migrations/snapshots/6c26c85a74b9429d6bb929df0c3e5035352fdb4cc2ee0e711a7a215e490041b1/contract.d.ts @@ -6,12 +6,8 @@ import type { Bit, Char, CodecTypes as PgTypes, - Interval, JsonValue, Numeric, - Time, - Timestamp, - Timestamptz, Timetz, VarBit, Varchar, diff --git a/examples/prisma-8-demo/fixtures/multi-branch/migrations/snapshots/7b7a028bf17c118bdec714439df6456964c13d3d4a4dbd14869b888412a0bd01/contract.d.ts b/examples/prisma-8-demo/fixtures/multi-branch/migrations/snapshots/7b7a028bf17c118bdec714439df6456964c13d3d4a4dbd14869b888412a0bd01/contract.d.ts index 5fe6a104599a..eaac5bbcadb5 100644 --- a/examples/prisma-8-demo/fixtures/multi-branch/migrations/snapshots/7b7a028bf17c118bdec714439df6456964c13d3d4a4dbd14869b888412a0bd01/contract.d.ts +++ b/examples/prisma-8-demo/fixtures/multi-branch/migrations/snapshots/7b7a028bf17c118bdec714439df6456964c13d3d4a4dbd14869b888412a0bd01/contract.d.ts @@ -6,12 +6,8 @@ import type { Bit, Char, CodecTypes as PgTypes, - Interval, JsonValue, Numeric, - Time, - Timestamp, - Timestamptz, Timetz, VarBit, Varchar, diff --git a/examples/prisma-8-demo/fixtures/multi-branch/migrations/snapshots/df82ed1c905933192eca7c1d92ac6f1f8ada22d1d712865e5afd4400c554de2e/contract.d.ts b/examples/prisma-8-demo/fixtures/multi-branch/migrations/snapshots/df82ed1c905933192eca7c1d92ac6f1f8ada22d1d712865e5afd4400c554de2e/contract.d.ts index 468ba2b96c8d..abfa79686b19 100644 --- a/examples/prisma-8-demo/fixtures/multi-branch/migrations/snapshots/df82ed1c905933192eca7c1d92ac6f1f8ada22d1d712865e5afd4400c554de2e/contract.d.ts +++ b/examples/prisma-8-demo/fixtures/multi-branch/migrations/snapshots/df82ed1c905933192eca7c1d92ac6f1f8ada22d1d712865e5afd4400c554de2e/contract.d.ts @@ -6,12 +6,8 @@ import type { Bit, Char, CodecTypes as PgTypes, - Interval, JsonValue, Numeric, - Time, - Timestamp, - Timestamptz, Timetz, VarBit, Varchar, diff --git a/examples/prisma-8-demo/fixtures/multi-branch/migrations/snapshots/f984b41c72c9ae805561bd27891ef309f95bb5eda852892fc3e37b620f897b38/contract.d.ts b/examples/prisma-8-demo/fixtures/multi-branch/migrations/snapshots/f984b41c72c9ae805561bd27891ef309f95bb5eda852892fc3e37b620f897b38/contract.d.ts index 39fb6c3f61d6..2c64de55deb8 100644 --- a/examples/prisma-8-demo/fixtures/multi-branch/migrations/snapshots/f984b41c72c9ae805561bd27891ef309f95bb5eda852892fc3e37b620f897b38/contract.d.ts +++ b/examples/prisma-8-demo/fixtures/multi-branch/migrations/snapshots/f984b41c72c9ae805561bd27891ef309f95bb5eda852892fc3e37b620f897b38/contract.d.ts @@ -6,12 +6,8 @@ import type { Bit, Char, CodecTypes as PgTypes, - Interval, JsonValue, Numeric, - Time, - Timestamp, - Timestamptz, Timetz, VarBit, Varchar, diff --git a/examples/prisma-8-demo/fixtures/showcase/migrations/snapshots/0ad62eb8f8ec2138d5d3a1be39a3e6f4221b4652b3fad9ef35d2e2a35404714f/contract.d.ts b/examples/prisma-8-demo/fixtures/showcase/migrations/snapshots/0ad62eb8f8ec2138d5d3a1be39a3e6f4221b4652b3fad9ef35d2e2a35404714f/contract.d.ts index 8c0f986f5bde..eb1223b34f5e 100644 --- a/examples/prisma-8-demo/fixtures/showcase/migrations/snapshots/0ad62eb8f8ec2138d5d3a1be39a3e6f4221b4652b3fad9ef35d2e2a35404714f/contract.d.ts +++ b/examples/prisma-8-demo/fixtures/showcase/migrations/snapshots/0ad62eb8f8ec2138d5d3a1be39a3e6f4221b4652b3fad9ef35d2e2a35404714f/contract.d.ts @@ -6,12 +6,8 @@ import type { Bit, Char, CodecTypes as PgTypes, - Interval, JsonValue, Numeric, - Time, - Timestamp, - Timestamptz, Timetz, VarBit, Varchar, diff --git a/examples/prisma-8-demo/fixtures/showcase/migrations/snapshots/0d01a7e1c00acc052445624ecfc1542bbee85bdbd8d531c69b826b0a6698e14e/contract.d.ts b/examples/prisma-8-demo/fixtures/showcase/migrations/snapshots/0d01a7e1c00acc052445624ecfc1542bbee85bdbd8d531c69b826b0a6698e14e/contract.d.ts index 19f78df89635..70ff59f5eeae 100644 --- a/examples/prisma-8-demo/fixtures/showcase/migrations/snapshots/0d01a7e1c00acc052445624ecfc1542bbee85bdbd8d531c69b826b0a6698e14e/contract.d.ts +++ b/examples/prisma-8-demo/fixtures/showcase/migrations/snapshots/0d01a7e1c00acc052445624ecfc1542bbee85bdbd8d531c69b826b0a6698e14e/contract.d.ts @@ -6,12 +6,8 @@ import type { Bit, Char, CodecTypes as PgTypes, - Interval, JsonValue, Numeric, - Time, - Timestamp, - Timestamptz, Timetz, VarBit, Varchar, diff --git a/examples/prisma-8-demo/fixtures/showcase/migrations/snapshots/1fb95fa184d8fbead2f8147baf16641eaeabc51459616611882ee00d85addea3/contract.d.ts b/examples/prisma-8-demo/fixtures/showcase/migrations/snapshots/1fb95fa184d8fbead2f8147baf16641eaeabc51459616611882ee00d85addea3/contract.d.ts index e05beecf4cd3..39ff7691057d 100644 --- a/examples/prisma-8-demo/fixtures/showcase/migrations/snapshots/1fb95fa184d8fbead2f8147baf16641eaeabc51459616611882ee00d85addea3/contract.d.ts +++ b/examples/prisma-8-demo/fixtures/showcase/migrations/snapshots/1fb95fa184d8fbead2f8147baf16641eaeabc51459616611882ee00d85addea3/contract.d.ts @@ -6,12 +6,8 @@ import type { Bit, Char, CodecTypes as PgTypes, - Interval, JsonValue, Numeric, - Time, - Timestamp, - Timestamptz, Timetz, VarBit, Varchar, diff --git a/examples/prisma-8-demo/fixtures/showcase/migrations/snapshots/23371687751c218d354034a564d7709da7ac6af8dd54b37f1a2b9af4e5140073/contract.d.ts b/examples/prisma-8-demo/fixtures/showcase/migrations/snapshots/23371687751c218d354034a564d7709da7ac6af8dd54b37f1a2b9af4e5140073/contract.d.ts index f75c72e008a9..c399f5a0971f 100644 --- a/examples/prisma-8-demo/fixtures/showcase/migrations/snapshots/23371687751c218d354034a564d7709da7ac6af8dd54b37f1a2b9af4e5140073/contract.d.ts +++ b/examples/prisma-8-demo/fixtures/showcase/migrations/snapshots/23371687751c218d354034a564d7709da7ac6af8dd54b37f1a2b9af4e5140073/contract.d.ts @@ -6,12 +6,8 @@ import type { Bit, Char, CodecTypes as PgTypes, - Interval, JsonValue, Numeric, - Time, - Timestamp, - Timestamptz, Timetz, VarBit, Varchar, diff --git a/examples/prisma-8-demo/fixtures/showcase/migrations/snapshots/76f9a7585ee104f29508fd0ba31f78665ef252d0a42d9d78e1f0ad1ff4dfbe01/contract.d.ts b/examples/prisma-8-demo/fixtures/showcase/migrations/snapshots/76f9a7585ee104f29508fd0ba31f78665ef252d0a42d9d78e1f0ad1ff4dfbe01/contract.d.ts index 7d75c309c276..26c865aad5ab 100644 --- a/examples/prisma-8-demo/fixtures/showcase/migrations/snapshots/76f9a7585ee104f29508fd0ba31f78665ef252d0a42d9d78e1f0ad1ff4dfbe01/contract.d.ts +++ b/examples/prisma-8-demo/fixtures/showcase/migrations/snapshots/76f9a7585ee104f29508fd0ba31f78665ef252d0a42d9d78e1f0ad1ff4dfbe01/contract.d.ts @@ -6,12 +6,8 @@ import type { Bit, Char, CodecTypes as PgTypes, - Interval, JsonValue, Numeric, - Time, - Timestamp, - Timestamptz, Timetz, VarBit, Varchar, diff --git a/examples/prisma-8-demo/fixtures/showcase/migrations/snapshots/7999fa221ce9c00f04321344eb006a39bd4485ba5a913c8964ae6c1fb7a8049d/contract.d.ts b/examples/prisma-8-demo/fixtures/showcase/migrations/snapshots/7999fa221ce9c00f04321344eb006a39bd4485ba5a913c8964ae6c1fb7a8049d/contract.d.ts index 4fc48a4c4047..507c2b9b6254 100644 --- a/examples/prisma-8-demo/fixtures/showcase/migrations/snapshots/7999fa221ce9c00f04321344eb006a39bd4485ba5a913c8964ae6c1fb7a8049d/contract.d.ts +++ b/examples/prisma-8-demo/fixtures/showcase/migrations/snapshots/7999fa221ce9c00f04321344eb006a39bd4485ba5a913c8964ae6c1fb7a8049d/contract.d.ts @@ -6,12 +6,8 @@ import type { Bit, Char, CodecTypes as PgTypes, - Interval, JsonValue, Numeric, - Time, - Timestamp, - Timestamptz, Timetz, VarBit, Varchar, diff --git a/examples/prisma-8-demo/fixtures/showcase/migrations/snapshots/d82c665884d8cf0745edfcc24be8b374ccbe89dffb5e3e6ab10357cad4d5f03f/contract.d.ts b/examples/prisma-8-demo/fixtures/showcase/migrations/snapshots/d82c665884d8cf0745edfcc24be8b374ccbe89dffb5e3e6ab10357cad4d5f03f/contract.d.ts index 30764addb972..cfb0de1a5b4c 100644 --- a/examples/prisma-8-demo/fixtures/showcase/migrations/snapshots/d82c665884d8cf0745edfcc24be8b374ccbe89dffb5e3e6ab10357cad4d5f03f/contract.d.ts +++ b/examples/prisma-8-demo/fixtures/showcase/migrations/snapshots/d82c665884d8cf0745edfcc24be8b374ccbe89dffb5e3e6ab10357cad4d5f03f/contract.d.ts @@ -6,12 +6,8 @@ import type { Bit, Char, CodecTypes as PgTypes, - Interval, JsonValue, Numeric, - Time, - Timestamp, - Timestamptz, Timetz, VarBit, Varchar, diff --git a/examples/prisma-8-demo/fixtures/showcase/migrations/snapshots/f2501f92fe261b6e89bfcf335d7647ea5bdec8492e0587ed17dcbd63bfe81ef9/contract.d.ts b/examples/prisma-8-demo/fixtures/showcase/migrations/snapshots/f2501f92fe261b6e89bfcf335d7647ea5bdec8492e0587ed17dcbd63bfe81ef9/contract.d.ts index 45f46ad29094..89fd3af49cfd 100644 --- a/examples/prisma-8-demo/fixtures/showcase/migrations/snapshots/f2501f92fe261b6e89bfcf335d7647ea5bdec8492e0587ed17dcbd63bfe81ef9/contract.d.ts +++ b/examples/prisma-8-demo/fixtures/showcase/migrations/snapshots/f2501f92fe261b6e89bfcf335d7647ea5bdec8492e0587ed17dcbd63bfe81ef9/contract.d.ts @@ -6,12 +6,8 @@ import type { Bit, Char, CodecTypes as PgTypes, - Interval, JsonValue, Numeric, - Time, - Timestamp, - Timestamptz, Timetz, VarBit, Varchar, diff --git a/examples/prisma-8-demo/fixtures/showcase/migrations/snapshots/f894c02864e197d491f40111ceb9ec48d78de76f4462835c22a3c1f9d68f881f/contract.d.ts b/examples/prisma-8-demo/fixtures/showcase/migrations/snapshots/f894c02864e197d491f40111ceb9ec48d78de76f4462835c22a3c1f9d68f881f/contract.d.ts index b56c5c97159a..e40e51cea306 100644 --- a/examples/prisma-8-demo/fixtures/showcase/migrations/snapshots/f894c02864e197d491f40111ceb9ec48d78de76f4462835c22a3c1f9d68f881f/contract.d.ts +++ b/examples/prisma-8-demo/fixtures/showcase/migrations/snapshots/f894c02864e197d491f40111ceb9ec48d78de76f4462835c22a3c1f9d68f881f/contract.d.ts @@ -6,12 +6,8 @@ import type { Bit, Char, CodecTypes as PgTypes, - Interval, JsonValue, Numeric, - Time, - Timestamp, - Timestamptz, Timetz, VarBit, Varchar, diff --git a/examples/prisma-8-demo/fixtures/showcase/migrations/snapshots/fa509ad2ef15e0d1cd65b6b858d4729d9ac7c50ea28df3dab7cb790bc15d791d/contract.d.ts b/examples/prisma-8-demo/fixtures/showcase/migrations/snapshots/fa509ad2ef15e0d1cd65b6b858d4729d9ac7c50ea28df3dab7cb790bc15d791d/contract.d.ts index e450961c98ce..3b6411d0e500 100644 --- a/examples/prisma-8-demo/fixtures/showcase/migrations/snapshots/fa509ad2ef15e0d1cd65b6b858d4729d9ac7c50ea28df3dab7cb790bc15d791d/contract.d.ts +++ b/examples/prisma-8-demo/fixtures/showcase/migrations/snapshots/fa509ad2ef15e0d1cd65b6b858d4729d9ac7c50ea28df3dab7cb790bc15d791d/contract.d.ts @@ -6,12 +6,8 @@ import type { Bit, Char, CodecTypes as PgTypes, - Interval, JsonValue, Numeric, - Time, - Timestamp, - Timestamptz, Timetz, VarBit, Varchar, diff --git a/examples/prisma-8-demo/fixtures/skip-rollback/migrations/snapshots/3539138011365388d750c8a37a053403de07308eb1d872451a33d9655c9f1dde/contract.d.ts b/examples/prisma-8-demo/fixtures/skip-rollback/migrations/snapshots/3539138011365388d750c8a37a053403de07308eb1d872451a33d9655c9f1dde/contract.d.ts index 2a8a9c82aced..7221cd7ca875 100644 --- a/examples/prisma-8-demo/fixtures/skip-rollback/migrations/snapshots/3539138011365388d750c8a37a053403de07308eb1d872451a33d9655c9f1dde/contract.d.ts +++ b/examples/prisma-8-demo/fixtures/skip-rollback/migrations/snapshots/3539138011365388d750c8a37a053403de07308eb1d872451a33d9655c9f1dde/contract.d.ts @@ -6,12 +6,8 @@ import type { Bit, Char, CodecTypes as PgTypes, - Interval, JsonValue, Numeric, - Time, - Timestamp, - Timestamptz, Timetz, VarBit, Varchar, diff --git a/examples/prisma-8-demo/fixtures/skip-rollback/migrations/snapshots/4ae263c38bb0abf30b3daf57d2b002997c15260b0e5528bd924969c25f3d00bb/contract.d.ts b/examples/prisma-8-demo/fixtures/skip-rollback/migrations/snapshots/4ae263c38bb0abf30b3daf57d2b002997c15260b0e5528bd924969c25f3d00bb/contract.d.ts index 810513406e1c..4273a713670d 100644 --- a/examples/prisma-8-demo/fixtures/skip-rollback/migrations/snapshots/4ae263c38bb0abf30b3daf57d2b002997c15260b0e5528bd924969c25f3d00bb/contract.d.ts +++ b/examples/prisma-8-demo/fixtures/skip-rollback/migrations/snapshots/4ae263c38bb0abf30b3daf57d2b002997c15260b0e5528bd924969c25f3d00bb/contract.d.ts @@ -6,12 +6,8 @@ import type { Bit, Char, CodecTypes as PgTypes, - Interval, JsonValue, Numeric, - Time, - Timestamp, - Timestamptz, Timetz, VarBit, Varchar, diff --git a/examples/prisma-8-demo/fixtures/skip-rollback/migrations/snapshots/6c26c85a74b9429d6bb929df0c3e5035352fdb4cc2ee0e711a7a215e490041b1/contract.d.ts b/examples/prisma-8-demo/fixtures/skip-rollback/migrations/snapshots/6c26c85a74b9429d6bb929df0c3e5035352fdb4cc2ee0e711a7a215e490041b1/contract.d.ts index 15023119ed7a..ab185e4a2d53 100644 --- a/examples/prisma-8-demo/fixtures/skip-rollback/migrations/snapshots/6c26c85a74b9429d6bb929df0c3e5035352fdb4cc2ee0e711a7a215e490041b1/contract.d.ts +++ b/examples/prisma-8-demo/fixtures/skip-rollback/migrations/snapshots/6c26c85a74b9429d6bb929df0c3e5035352fdb4cc2ee0e711a7a215e490041b1/contract.d.ts @@ -6,12 +6,8 @@ import type { Bit, Char, CodecTypes as PgTypes, - Interval, JsonValue, Numeric, - Time, - Timestamp, - Timestamptz, Timetz, VarBit, Varchar, diff --git a/examples/prisma-8-demo/fixtures/skip-rollback/migrations/snapshots/df82ed1c905933192eca7c1d92ac6f1f8ada22d1d712865e5afd4400c554de2e/contract.d.ts b/examples/prisma-8-demo/fixtures/skip-rollback/migrations/snapshots/df82ed1c905933192eca7c1d92ac6f1f8ada22d1d712865e5afd4400c554de2e/contract.d.ts index 468ba2b96c8d..abfa79686b19 100644 --- a/examples/prisma-8-demo/fixtures/skip-rollback/migrations/snapshots/df82ed1c905933192eca7c1d92ac6f1f8ada22d1d712865e5afd4400c554de2e/contract.d.ts +++ b/examples/prisma-8-demo/fixtures/skip-rollback/migrations/snapshots/df82ed1c905933192eca7c1d92ac6f1f8ada22d1d712865e5afd4400c554de2e/contract.d.ts @@ -6,12 +6,8 @@ import type { Bit, Char, CodecTypes as PgTypes, - Interval, JsonValue, Numeric, - Time, - Timestamp, - Timestamptz, Timetz, VarBit, Varchar, diff --git a/examples/prisma-8-demo/fixtures/wide-fan/migrations/snapshots/6c26c85a74b9429d6bb929df0c3e5035352fdb4cc2ee0e711a7a215e490041b1/contract.d.ts b/examples/prisma-8-demo/fixtures/wide-fan/migrations/snapshots/6c26c85a74b9429d6bb929df0c3e5035352fdb4cc2ee0e711a7a215e490041b1/contract.d.ts index 15023119ed7a..ab185e4a2d53 100644 --- a/examples/prisma-8-demo/fixtures/wide-fan/migrations/snapshots/6c26c85a74b9429d6bb929df0c3e5035352fdb4cc2ee0e711a7a215e490041b1/contract.d.ts +++ b/examples/prisma-8-demo/fixtures/wide-fan/migrations/snapshots/6c26c85a74b9429d6bb929df0c3e5035352fdb4cc2ee0e711a7a215e490041b1/contract.d.ts @@ -6,12 +6,8 @@ import type { Bit, Char, CodecTypes as PgTypes, - Interval, JsonValue, Numeric, - Time, - Timestamp, - Timestamptz, Timetz, VarBit, Varchar, diff --git a/examples/prisma-8-demo/fixtures/wide-fan/migrations/snapshots/7161e6c8c74305ee59e584a6225cb1a60624664aef09069e2d6080ff9a91d5c0/contract.d.ts b/examples/prisma-8-demo/fixtures/wide-fan/migrations/snapshots/7161e6c8c74305ee59e584a6225cb1a60624664aef09069e2d6080ff9a91d5c0/contract.d.ts index c6835ed0be75..855c84ffbfab 100644 --- a/examples/prisma-8-demo/fixtures/wide-fan/migrations/snapshots/7161e6c8c74305ee59e584a6225cb1a60624664aef09069e2d6080ff9a91d5c0/contract.d.ts +++ b/examples/prisma-8-demo/fixtures/wide-fan/migrations/snapshots/7161e6c8c74305ee59e584a6225cb1a60624664aef09069e2d6080ff9a91d5c0/contract.d.ts @@ -6,12 +6,8 @@ import type { Bit, Char, CodecTypes as PgTypes, - Interval, JsonValue, Numeric, - Time, - Timestamp, - Timestamptz, Timetz, VarBit, Varchar, diff --git a/examples/prisma-8-demo/fixtures/wide-fan/migrations/snapshots/7b7a028bf17c118bdec714439df6456964c13d3d4a4dbd14869b888412a0bd01/contract.d.ts b/examples/prisma-8-demo/fixtures/wide-fan/migrations/snapshots/7b7a028bf17c118bdec714439df6456964c13d3d4a4dbd14869b888412a0bd01/contract.d.ts index 5fe6a104599a..eaac5bbcadb5 100644 --- a/examples/prisma-8-demo/fixtures/wide-fan/migrations/snapshots/7b7a028bf17c118bdec714439df6456964c13d3d4a4dbd14869b888412a0bd01/contract.d.ts +++ b/examples/prisma-8-demo/fixtures/wide-fan/migrations/snapshots/7b7a028bf17c118bdec714439df6456964c13d3d4a4dbd14869b888412a0bd01/contract.d.ts @@ -6,12 +6,8 @@ import type { Bit, Char, CodecTypes as PgTypes, - Interval, JsonValue, Numeric, - Time, - Timestamp, - Timestamptz, Timetz, VarBit, Varchar, diff --git a/examples/prisma-8-demo/fixtures/wide-fan/migrations/snapshots/d02f6207d52b6e854cac5d642d5514d5f2864ede49db9bb4ee78d45d8a95d406/contract.d.ts b/examples/prisma-8-demo/fixtures/wide-fan/migrations/snapshots/d02f6207d52b6e854cac5d642d5514d5f2864ede49db9bb4ee78d45d8a95d406/contract.d.ts index 02080882ca14..87f29bd46f5e 100644 --- a/examples/prisma-8-demo/fixtures/wide-fan/migrations/snapshots/d02f6207d52b6e854cac5d642d5514d5f2864ede49db9bb4ee78d45d8a95d406/contract.d.ts +++ b/examples/prisma-8-demo/fixtures/wide-fan/migrations/snapshots/d02f6207d52b6e854cac5d642d5514d5f2864ede49db9bb4ee78d45d8a95d406/contract.d.ts @@ -6,12 +6,8 @@ import type { Bit, Char, CodecTypes as PgTypes, - Interval, JsonValue, Numeric, - Time, - Timestamp, - Timestamptz, Timetz, VarBit, Varchar, diff --git a/examples/prisma-8-demo/fixtures/wide-fan/migrations/snapshots/df82ed1c905933192eca7c1d92ac6f1f8ada22d1d712865e5afd4400c554de2e/contract.d.ts b/examples/prisma-8-demo/fixtures/wide-fan/migrations/snapshots/df82ed1c905933192eca7c1d92ac6f1f8ada22d1d712865e5afd4400c554de2e/contract.d.ts index 468ba2b96c8d..abfa79686b19 100644 --- a/examples/prisma-8-demo/fixtures/wide-fan/migrations/snapshots/df82ed1c905933192eca7c1d92ac6f1f8ada22d1d712865e5afd4400c554de2e/contract.d.ts +++ b/examples/prisma-8-demo/fixtures/wide-fan/migrations/snapshots/df82ed1c905933192eca7c1d92ac6f1f8ada22d1d712865e5afd4400c554de2e/contract.d.ts @@ -6,12 +6,8 @@ import type { Bit, Char, CodecTypes as PgTypes, - Interval, JsonValue, Numeric, - Time, - Timestamp, - Timestamptz, Timetz, VarBit, Varchar, diff --git a/examples/prisma-8-demo/fixtures/wide-fan/migrations/snapshots/f984b41c72c9ae805561bd27891ef309f95bb5eda852892fc3e37b620f897b38/contract.d.ts b/examples/prisma-8-demo/fixtures/wide-fan/migrations/snapshots/f984b41c72c9ae805561bd27891ef309f95bb5eda852892fc3e37b620f897b38/contract.d.ts index 39fb6c3f61d6..2c64de55deb8 100644 --- a/examples/prisma-8-demo/fixtures/wide-fan/migrations/snapshots/f984b41c72c9ae805561bd27891ef309f95bb5eda852892fc3e37b620f897b38/contract.d.ts +++ b/examples/prisma-8-demo/fixtures/wide-fan/migrations/snapshots/f984b41c72c9ae805561bd27891ef309f95bb5eda852892fc3e37b620f897b38/contract.d.ts @@ -6,12 +6,8 @@ import type { Bit, Char, CodecTypes as PgTypes, - Interval, JsonValue, Numeric, - Time, - Timestamp, - Timestamptz, Timetz, VarBit, Varchar, diff --git a/examples/prisma-8-demo/migrations/snapshots/0ce16e4140d056a3abddbe18bd7ee5198d392370f0aeb23236216c51505944de/contract.d.ts b/examples/prisma-8-demo/migrations/snapshots/0ce16e4140d056a3abddbe18bd7ee5198d392370f0aeb23236216c51505944de/contract.d.ts index 909cb96ba6bb..96499166b9d7 100644 --- a/examples/prisma-8-demo/migrations/snapshots/0ce16e4140d056a3abddbe18bd7ee5198d392370f0aeb23236216c51505944de/contract.d.ts +++ b/examples/prisma-8-demo/migrations/snapshots/0ce16e4140d056a3abddbe18bd7ee5198d392370f0aeb23236216c51505944de/contract.d.ts @@ -50,6 +50,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; @@ -99,6 +103,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; @@ -108,6 +116,14 @@ export type AggregateTypes = { readonly output: 'pg/time-temporal@1'; readonly nullable: true; }; + readonly 'pg/timestamp-date@1': { + readonly output: 'pg/timestamp-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-instant@1': { + readonly output: 'pg/timestamp-instant@1'; + readonly nullable: true; + }; readonly 'pg/timestamp-string@1': { readonly output: 'pg/timestamp-string@1'; readonly nullable: true; @@ -159,6 +175,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; @@ -168,6 +188,14 @@ export type AggregateTypes = { readonly output: 'pg/time-temporal@1'; readonly nullable: true; }; + readonly 'pg/timestamp-date@1': { + readonly output: 'pg/timestamp-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-instant@1': { + readonly output: 'pg/timestamp-instant@1'; + readonly nullable: true; + }; readonly 'pg/timestamp-string@1': { readonly output: 'pg/timestamp-string@1'; readonly nullable: true; @@ -227,6 +255,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; diff --git a/examples/prisma-8-demo/migrations/snapshots/0ce1b6cf3e03ac1498f39eb8ef08d36d73ee2f0bc7fe368b97f1497ba050501a/contract.d.ts b/examples/prisma-8-demo/migrations/snapshots/0ce1b6cf3e03ac1498f39eb8ef08d36d73ee2f0bc7fe368b97f1497ba050501a/contract.d.ts index 6e6569558199..d180da08d5e9 100644 --- a/examples/prisma-8-demo/migrations/snapshots/0ce1b6cf3e03ac1498f39eb8ef08d36d73ee2f0bc7fe368b97f1497ba050501a/contract.d.ts +++ b/examples/prisma-8-demo/migrations/snapshots/0ce1b6cf3e03ac1498f39eb8ef08d36d73ee2f0bc7fe368b97f1497ba050501a/contract.d.ts @@ -50,6 +50,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; @@ -99,6 +103,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; @@ -108,6 +116,14 @@ export type AggregateTypes = { readonly output: 'pg/time-temporal@1'; readonly nullable: true; }; + readonly 'pg/timestamp-date@1': { + readonly output: 'pg/timestamp-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-instant@1': { + readonly output: 'pg/timestamp-instant@1'; + readonly nullable: true; + }; readonly 'pg/timestamp-string@1': { readonly output: 'pg/timestamp-string@1'; readonly nullable: true; @@ -159,6 +175,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; @@ -168,6 +188,14 @@ export type AggregateTypes = { readonly output: 'pg/time-temporal@1'; readonly nullable: true; }; + readonly 'pg/timestamp-date@1': { + readonly output: 'pg/timestamp-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-instant@1': { + readonly output: 'pg/timestamp-instant@1'; + readonly nullable: true; + }; readonly 'pg/timestamp-string@1': { readonly output: 'pg/timestamp-string@1'; readonly nullable: true; @@ -227,6 +255,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; diff --git a/examples/prisma-8-demo/migrations/snapshots/4a96b488a4ce92b434e5f7d6607b6435c0955f0d36b0018077045787764240e6/contract.d.ts b/examples/prisma-8-demo/migrations/snapshots/4a96b488a4ce92b434e5f7d6607b6435c0955f0d36b0018077045787764240e6/contract.d.ts index a9653b545f1b..bf7e7d5c048a 100644 --- a/examples/prisma-8-demo/migrations/snapshots/4a96b488a4ce92b434e5f7d6607b6435c0955f0d36b0018077045787764240e6/contract.d.ts +++ b/examples/prisma-8-demo/migrations/snapshots/4a96b488a4ce92b434e5f7d6607b6435c0955f0d36b0018077045787764240e6/contract.d.ts @@ -6,12 +6,8 @@ import type { Bit, Char, CodecTypes as PgTypes, - Interval, JsonValue, Numeric, - Time, - Timestamp, - Timestamptz, Timetz, VarBit, Varchar, diff --git a/examples/prisma-8-demo/migrations/snapshots/58888a20cba5529ede90056ecd1318183f0e5cf280c88cd58aeadd94bbf52c1b/contract.d.ts b/examples/prisma-8-demo/migrations/snapshots/58888a20cba5529ede90056ecd1318183f0e5cf280c88cd58aeadd94bbf52c1b/contract.d.ts index 64fd0c6ba064..d80ae3a33b2b 100644 --- a/examples/prisma-8-demo/migrations/snapshots/58888a20cba5529ede90056ecd1318183f0e5cf280c88cd58aeadd94bbf52c1b/contract.d.ts +++ b/examples/prisma-8-demo/migrations/snapshots/58888a20cba5529ede90056ecd1318183f0e5cf280c88cd58aeadd94bbf52c1b/contract.d.ts @@ -50,6 +50,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; @@ -99,6 +103,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; @@ -108,6 +116,14 @@ export type AggregateTypes = { readonly output: 'pg/time-temporal@1'; readonly nullable: true; }; + readonly 'pg/timestamp-date@1': { + readonly output: 'pg/timestamp-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-instant@1': { + readonly output: 'pg/timestamp-instant@1'; + readonly nullable: true; + }; readonly 'pg/timestamp-string@1': { readonly output: 'pg/timestamp-string@1'; readonly nullable: true; @@ -159,6 +175,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; @@ -168,6 +188,14 @@ export type AggregateTypes = { readonly output: 'pg/time-temporal@1'; readonly nullable: true; }; + readonly 'pg/timestamp-date@1': { + readonly output: 'pg/timestamp-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-instant@1': { + readonly output: 'pg/timestamp-instant@1'; + readonly nullable: true; + }; readonly 'pg/timestamp-string@1': { readonly output: 'pg/timestamp-string@1'; readonly nullable: true; @@ -227,6 +255,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; diff --git a/examples/prisma-8-demo/migrations/snapshots/6e6fbbc22d1d4904fbc8668644c5cabe694d946e550a2f0cdd07aaab308b953f/contract.d.ts b/examples/prisma-8-demo/migrations/snapshots/6e6fbbc22d1d4904fbc8668644c5cabe694d946e550a2f0cdd07aaab308b953f/contract.d.ts index fc656d75473b..111a2ad19b9b 100644 --- a/examples/prisma-8-demo/migrations/snapshots/6e6fbbc22d1d4904fbc8668644c5cabe694d946e550a2f0cdd07aaab308b953f/contract.d.ts +++ b/examples/prisma-8-demo/migrations/snapshots/6e6fbbc22d1d4904fbc8668644c5cabe694d946e550a2f0cdd07aaab308b953f/contract.d.ts @@ -50,6 +50,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; @@ -99,6 +103,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; @@ -108,6 +116,14 @@ export type AggregateTypes = { readonly output: 'pg/time-temporal@1'; readonly nullable: true; }; + readonly 'pg/timestamp-date@1': { + readonly output: 'pg/timestamp-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-instant@1': { + readonly output: 'pg/timestamp-instant@1'; + readonly nullable: true; + }; readonly 'pg/timestamp-string@1': { readonly output: 'pg/timestamp-string@1'; readonly nullable: true; @@ -159,6 +175,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; @@ -168,6 +188,14 @@ export type AggregateTypes = { readonly output: 'pg/time-temporal@1'; readonly nullable: true; }; + readonly 'pg/timestamp-date@1': { + readonly output: 'pg/timestamp-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-instant@1': { + readonly output: 'pg/timestamp-instant@1'; + readonly nullable: true; + }; readonly 'pg/timestamp-string@1': { readonly output: 'pg/timestamp-string@1'; readonly nullable: true; @@ -227,6 +255,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; diff --git a/examples/prisma-8-demo/migrations/snapshots/9fc3736e0efd980892a19f4d704ca30bcd0de243080e78ed1279b1f3a50f9a7e/contract.d.ts b/examples/prisma-8-demo/migrations/snapshots/9fc3736e0efd980892a19f4d704ca30bcd0de243080e78ed1279b1f3a50f9a7e/contract.d.ts index 987c5a7e3213..80013a282b41 100644 --- a/examples/prisma-8-demo/migrations/snapshots/9fc3736e0efd980892a19f4d704ca30bcd0de243080e78ed1279b1f3a50f9a7e/contract.d.ts +++ b/examples/prisma-8-demo/migrations/snapshots/9fc3736e0efd980892a19f4d704ca30bcd0de243080e78ed1279b1f3a50f9a7e/contract.d.ts @@ -11,14 +11,10 @@ import type { Bit, Char, CodecTypes as PgTypes, - Interval, JsonValue, Numeric, - Time, TimeString, - Timestamp, TimestampString, - Timestamptz, TimestamptzString, Timetz, VarBit, diff --git a/examples/prisma-8-demo/migrations/snapshots/a6e86cb536b51ac3b7542cb02dbf9f5004139e8dc5ee1b33a554c436d8cd2b2a/contract.d.ts b/examples/prisma-8-demo/migrations/snapshots/a6e86cb536b51ac3b7542cb02dbf9f5004139e8dc5ee1b33a554c436d8cd2b2a/contract.d.ts index 7d7a41f04eeb..b894bf47069b 100644 --- a/examples/prisma-8-demo/migrations/snapshots/a6e86cb536b51ac3b7542cb02dbf9f5004139e8dc5ee1b33a554c436d8cd2b2a/contract.d.ts +++ b/examples/prisma-8-demo/migrations/snapshots/a6e86cb536b51ac3b7542cb02dbf9f5004139e8dc5ee1b33a554c436d8cd2b2a/contract.d.ts @@ -11,14 +11,10 @@ import type { Bit, Char, CodecTypes as PgTypes, - Interval, JsonValue, Numeric, - Time, TimeString, - Timestamp, TimestampString, - Timestamptz, TimestamptzString, Timetz, VarBit, diff --git a/examples/prisma-8-demo/migrations/snapshots/d44ab6f7caddcc384332c669737008aec7ec8c9f5b013cb502a03c2eb04be9c0/contract.d.ts b/examples/prisma-8-demo/migrations/snapshots/d44ab6f7caddcc384332c669737008aec7ec8c9f5b013cb502a03c2eb04be9c0/contract.d.ts index bf0b5936eb55..6f22860d05c2 100644 --- a/examples/prisma-8-demo/migrations/snapshots/d44ab6f7caddcc384332c669737008aec7ec8c9f5b013cb502a03c2eb04be9c0/contract.d.ts +++ b/examples/prisma-8-demo/migrations/snapshots/d44ab6f7caddcc384332c669737008aec7ec8c9f5b013cb502a03c2eb04be9c0/contract.d.ts @@ -50,6 +50,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; @@ -99,6 +103,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; @@ -108,6 +116,14 @@ export type AggregateTypes = { readonly output: 'pg/time-temporal@1'; readonly nullable: true; }; + readonly 'pg/timestamp-date@1': { + readonly output: 'pg/timestamp-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-instant@1': { + readonly output: 'pg/timestamp-instant@1'; + readonly nullable: true; + }; readonly 'pg/timestamp-string@1': { readonly output: 'pg/timestamp-string@1'; readonly nullable: true; @@ -159,6 +175,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; @@ -168,6 +188,14 @@ export type AggregateTypes = { readonly output: 'pg/time-temporal@1'; readonly nullable: true; }; + readonly 'pg/timestamp-date@1': { + readonly output: 'pg/timestamp-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-instant@1': { + readonly output: 'pg/timestamp-instant@1'; + readonly nullable: true; + }; readonly 'pg/timestamp-string@1': { readonly output: 'pg/timestamp-string@1'; readonly nullable: true; @@ -227,6 +255,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; diff --git a/examples/prisma-8-demo/package.json b/examples/prisma-8-demo/package.json index c949fa1e92e5..1e64a8883fbe 100644 --- a/examples/prisma-8-demo/package.json +++ b/examples/prisma-8-demo/package.json @@ -45,6 +45,7 @@ "@vitejs/plugin-react-swc": "^4.3.2", "jsdom": "^29.0.2", "temporal-polyfill": "^1.0.4", + "temporal-spec": "1.0.1", "tsx": "catalog:", "typescript": "catalog:", "vite": "catalog:", diff --git a/examples/prisma-8-demo/src/prisma/contract.d.ts b/examples/prisma-8-demo/src/prisma/contract.d.ts index 6e6569558199..d180da08d5e9 100644 --- a/examples/prisma-8-demo/src/prisma/contract.d.ts +++ b/examples/prisma-8-demo/src/prisma/contract.d.ts @@ -50,6 +50,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; @@ -99,6 +103,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; @@ -108,6 +116,14 @@ export type AggregateTypes = { readonly output: 'pg/time-temporal@1'; readonly nullable: true; }; + readonly 'pg/timestamp-date@1': { + readonly output: 'pg/timestamp-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-instant@1': { + readonly output: 'pg/timestamp-instant@1'; + readonly nullable: true; + }; readonly 'pg/timestamp-string@1': { readonly output: 'pg/timestamp-string@1'; readonly nullable: true; @@ -159,6 +175,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; @@ -168,6 +188,14 @@ export type AggregateTypes = { readonly output: 'pg/time-temporal@1'; readonly nullable: true; }; + readonly 'pg/timestamp-date@1': { + readonly output: 'pg/timestamp-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-instant@1': { + readonly output: 'pg/timestamp-instant@1'; + readonly nullable: true; + }; readonly 'pg/timestamp-string@1': { readonly output: 'pg/timestamp-string@1'; readonly nullable: true; @@ -227,6 +255,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; diff --git a/examples/prisma-8-demo/test/fragments.types.test-d.ts b/examples/prisma-8-demo/test/fragments.types.test-d.ts index 33b482c978ff..9354ff1f9742 100644 --- a/examples/prisma-8-demo/test/fragments.types.test-d.ts +++ b/examples/prisma-8-demo/test/fragments.types.test-d.ts @@ -7,6 +7,7 @@ import { orderByField, } from '@prisma/orm-postgres/orm-client'; import { websearchToTsquery } from '@prisma/orm-postgres/target/full-text'; +import type { Temporal } from 'temporal-spec'; import { describe, expectTypeOf, test } from 'vitest'; import { createdBefore, createdSince, ownedBy, postSummary } from '../src/orm-client/fragments'; import type { ormClientGetFilteredPosts } from '../src/orm-client/get-filtered-posts'; diff --git a/examples/prisma-8-postgis-demo/migrations/snapshots/28b5e5f98ce0e148f03e345518c9c53cb7a04a82e8e58de74a87c1b4ae08cf65/contract.d.ts b/examples/prisma-8-postgis-demo/migrations/snapshots/28b5e5f98ce0e148f03e345518c9c53cb7a04a82e8e58de74a87c1b4ae08cf65/contract.d.ts index 742622ac8252..f7a5c544b529 100644 --- a/examples/prisma-8-postgis-demo/migrations/snapshots/28b5e5f98ce0e148f03e345518c9c53cb7a04a82e8e58de74a87c1b4ae08cf65/contract.d.ts +++ b/examples/prisma-8-postgis-demo/migrations/snapshots/28b5e5f98ce0e148f03e345518c9c53cb7a04a82e8e58de74a87c1b4ae08cf65/contract.d.ts @@ -46,6 +46,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; @@ -95,6 +99,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; @@ -104,6 +112,14 @@ export type AggregateTypes = { readonly output: 'pg/time-temporal@1'; readonly nullable: true; }; + readonly 'pg/timestamp-date@1': { + readonly output: 'pg/timestamp-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-instant@1': { + readonly output: 'pg/timestamp-instant@1'; + readonly nullable: true; + }; readonly 'pg/timestamp-string@1': { readonly output: 'pg/timestamp-string@1'; readonly nullable: true; @@ -155,6 +171,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; @@ -164,6 +184,14 @@ export type AggregateTypes = { readonly output: 'pg/time-temporal@1'; readonly nullable: true; }; + readonly 'pg/timestamp-date@1': { + readonly output: 'pg/timestamp-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-instant@1': { + readonly output: 'pg/timestamp-instant@1'; + readonly nullable: true; + }; readonly 'pg/timestamp-string@1': { readonly output: 'pg/timestamp-string@1'; readonly nullable: true; @@ -207,6 +235,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; diff --git a/examples/prisma-8-postgis-demo/migrations/snapshots/2db551a8a5619d0d97710f29130eaea0eccbdd5238f015ee9d18408d438e0f8a/contract.d.ts b/examples/prisma-8-postgis-demo/migrations/snapshots/2db551a8a5619d0d97710f29130eaea0eccbdd5238f015ee9d18408d438e0f8a/contract.d.ts index 61d64f29226d..c442ec1a3e59 100644 --- a/examples/prisma-8-postgis-demo/migrations/snapshots/2db551a8a5619d0d97710f29130eaea0eccbdd5238f015ee9d18408d438e0f8a/contract.d.ts +++ b/examples/prisma-8-postgis-demo/migrations/snapshots/2db551a8a5619d0d97710f29130eaea0eccbdd5238f015ee9d18408d438e0f8a/contract.d.ts @@ -6,12 +6,8 @@ import type { Bit, Char, CodecTypes as PgTypes, - Interval, JsonValue, Numeric, - Time, - Timestamp, - Timestamptz, Timetz, VarBit, Varchar, diff --git a/examples/prisma-8-postgis-demo/src/prisma/contract.d.ts b/examples/prisma-8-postgis-demo/src/prisma/contract.d.ts index 742622ac8252..f7a5c544b529 100644 --- a/examples/prisma-8-postgis-demo/src/prisma/contract.d.ts +++ b/examples/prisma-8-postgis-demo/src/prisma/contract.d.ts @@ -46,6 +46,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; @@ -95,6 +99,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; @@ -104,6 +112,14 @@ export type AggregateTypes = { readonly output: 'pg/time-temporal@1'; readonly nullable: true; }; + readonly 'pg/timestamp-date@1': { + readonly output: 'pg/timestamp-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-instant@1': { + readonly output: 'pg/timestamp-instant@1'; + readonly nullable: true; + }; readonly 'pg/timestamp-string@1': { readonly output: 'pg/timestamp-string@1'; readonly nullable: true; @@ -155,6 +171,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; @@ -164,6 +184,14 @@ export type AggregateTypes = { readonly output: 'pg/time-temporal@1'; readonly nullable: true; }; + readonly 'pg/timestamp-date@1': { + readonly output: 'pg/timestamp-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-instant@1': { + readonly output: 'pg/timestamp-instant@1'; + readonly nullable: true; + }; readonly 'pg/timestamp-string@1': { readonly output: 'pg/timestamp-string@1'; readonly nullable: true; @@ -207,6 +235,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; diff --git a/examples/prisma7-adoption/generated/prisma8/contract.d.ts b/examples/prisma7-adoption/generated/prisma8/contract.d.ts index 24b7812562d6..e21866441001 100644 --- a/examples/prisma7-adoption/generated/prisma8/contract.d.ts +++ b/examples/prisma7-adoption/generated/prisma8/contract.d.ts @@ -43,6 +43,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; @@ -92,6 +96,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; @@ -101,6 +109,14 @@ export type AggregateTypes = { readonly output: 'pg/time-temporal@1'; readonly nullable: true; }; + readonly 'pg/timestamp-date@1': { + readonly output: 'pg/timestamp-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-instant@1': { + readonly output: 'pg/timestamp-instant@1'; + readonly nullable: true; + }; readonly 'pg/timestamp-string@1': { readonly output: 'pg/timestamp-string@1'; readonly nullable: true; @@ -152,6 +168,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; @@ -161,6 +181,14 @@ export type AggregateTypes = { readonly output: 'pg/time-temporal@1'; readonly nullable: true; }; + readonly 'pg/timestamp-date@1': { + readonly output: 'pg/timestamp-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-instant@1': { + readonly output: 'pg/timestamp-instant@1'; + readonly nullable: true; + }; readonly 'pg/timestamp-string@1': { readonly output: 'pg/timestamp-string@1'; readonly nullable: true; @@ -204,6 +232,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; diff --git a/examples/react-router-demo/src/prisma/contract.d.ts b/examples/react-router-demo/src/prisma/contract.d.ts index 7126509cc497..3a4e6815649e 100644 --- a/examples/react-router-demo/src/prisma/contract.d.ts +++ b/examples/react-router-demo/src/prisma/contract.d.ts @@ -43,6 +43,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; @@ -92,6 +96,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; @@ -101,6 +109,14 @@ export type AggregateTypes = { readonly output: 'pg/time-temporal@1'; readonly nullable: true; }; + readonly 'pg/timestamp-date@1': { + readonly output: 'pg/timestamp-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-instant@1': { + readonly output: 'pg/timestamp-instant@1'; + readonly nullable: true; + }; readonly 'pg/timestamp-string@1': { readonly output: 'pg/timestamp-string@1'; readonly nullable: true; @@ -152,6 +168,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; @@ -161,6 +181,14 @@ export type AggregateTypes = { readonly output: 'pg/time-temporal@1'; readonly nullable: true; }; + readonly 'pg/timestamp-date@1': { + readonly output: 'pg/timestamp-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-instant@1': { + readonly output: 'pg/timestamp-instant@1'; + readonly nullable: true; + }; readonly 'pg/timestamp-string@1': { readonly output: 'pg/timestamp-string@1'; readonly nullable: true; @@ -204,6 +232,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; diff --git a/examples/supabase/src/contract.d.ts b/examples/supabase/src/contract.d.ts index 3173043eec06..390be2e522fe 100644 --- a/examples/supabase/src/contract.d.ts +++ b/examples/supabase/src/contract.d.ts @@ -42,6 +42,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; @@ -91,6 +95,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; @@ -100,6 +108,14 @@ export type AggregateTypes = { readonly output: 'pg/time-temporal@1'; readonly nullable: true; }; + readonly 'pg/timestamp-date@1': { + readonly output: 'pg/timestamp-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-instant@1': { + readonly output: 'pg/timestamp-instant@1'; + readonly nullable: true; + }; readonly 'pg/timestamp-string@1': { readonly output: 'pg/timestamp-string@1'; readonly nullable: true; @@ -151,6 +167,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; @@ -160,6 +180,14 @@ export type AggregateTypes = { readonly output: 'pg/time-temporal@1'; readonly nullable: true; }; + readonly 'pg/timestamp-date@1': { + readonly output: 'pg/timestamp-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-instant@1': { + readonly output: 'pg/timestamp-instant@1'; + readonly nullable: true; + }; readonly 'pg/timestamp-string@1': { readonly output: 'pg/timestamp-string@1'; readonly nullable: true; @@ -203,6 +231,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; diff --git a/gotchas.md b/gotchas.md index a1afb0d294af..3fdcb747ee0e 100644 --- a/gotchas.md +++ b/gotchas.md @@ -132,7 +132,7 @@ The same command with `--from 20260707T1005_init` (a migration directory name) s **Workaround.** `import 'temporal-polyfill/full/global'` before the client is created (the example did so in `src/db.ts` before the fix), or author the column with the `*String` presets to receive PostgreSQL's text. -**Resolved.** `prisma7Schema(...)` and `contract infer` now write `TimestampString(3)`, `TimestamptzString(p)`, `DateString` and `TimeString(p)` for these columns, so they read as PostgreSQL's text, `@updatedAt` writes UTC text, and the application needs no `Temporal`. A column written by hand as `DateTime`, `Timestamp(p)`, `Date` or `Time(p)` still reads as a `Temporal` value and still needs one. +**Resolved.** `prisma7Schema(...)` and `contract infer` now write `TimestampString(3)`, `TimestamptzString(p)`, `DateString` and `TimeString(p)` for these columns, so they read as PostgreSQL's text, `@updatedAt` writes UTC text, and the application needs no `Temporal`. A column written by hand as `DateTime`, `TimestampTemporal(p)`, `Date` or `Time(p)` still reads as a `Temporal` value and still needs one. **Reproduction.** 1. `cd examples/prisma7-adoption && pnpm db:start`, then `pnpm v7:migrate && pnpm emit && pnpm sign && pnpm seed`. diff --git a/packages/2-sql-family/2-authoring/contract-psl/README.md b/packages/2-sql-family/2-authoring/contract-psl/README.md index 36ef758cae79..d6e6e8de7441 100644 --- a/packages/2-sql-family/2-authoring/contract-psl/README.md +++ b/packages/2-sql-family/2-authoring/contract-psl/README.md @@ -18,7 +18,7 @@ This keeps core/CLI source-agnostic while giving PSL-first SQL users a one-line - Interpret SQL timestamp semantics: `DateTime @default(now())` as a storage default, and `temporal.createdAt()` / `temporal.updatedAt()` as client-side execution mutation defaults - Lower shared constructor expressions in both `types {}` blocks and inline field positions (for example `ShortName = sql.String(length: 35)` and `embedding pgvector.Vector(length: 1536)?`) - Lower supported default functions through composed registry inputs -- Resolve Postgres native storage types from bare names and constructor calls in type position (`Char`, `VarChar`, `Numeric`, `Uuid`, `Inet`, `SmallInt`, `Real`, `Timestamp`, `Timestamptz`, `Date`, `Time`, `Timetz`, `Interval`, `Bit`, `VarBit`, `Json`, `Jsonb`, `BigIntNumber`, `UnboundedInt`) +- Resolve Postgres native storage types from bare names and constructor calls in type position (`Char`, `VarChar`, `Numeric`, `Uuid`, `Inet`, `SmallInt`, `Real`, `TimestampTemporal`, `TimestamptzTemporal`, `Date`, `DateTemporal`, `Time`, `TimeTemporal`, `Timetz`, `IntervalParts`, `Bit`, `VarBit`, `Json`, `Jsonb`, `BigIntNumber`, `UnboundedInt`) - Map PSL relation action tokens to SQL contract referential actions and emit diagnostics for unsupported values - Emit deterministic relation metadata in `models..relations` - Enforce extension composition for namespaced constructor expressions and emit strict diagnostics for unsupported namespaced attributes @@ -115,7 +115,7 @@ model Post { - An alias's base is a type constructor, called or bare, or an enum. It may not name another alias or a contributed alias (`PSL_UNSUPPORTED_NAMED_TYPE_BASE`). - An alias may carry `@default`, `@onCreate` and `@onUpdate`. They are lowered once, at the declaration, so a diagnostic is reported once, on the alias. Every field typed by the alias takes them. `@id` and `@unique` on an alias are `PSL_UNSUPPORTED_NAMED_TYPE_ATTRIBUTE`, because they belong to a model's field, not to its type; write them on the field. A refused attribute is dropped and the alias is kept. - A field may add any attribute its alias does not set, including `@id` and `@unique`. A field that writes an attribute its alias already sets is `PSL_DUPLICATE_ATTRIBUTE`, naming the alias. A list field typed by an alias that generates a value is `PSL_LIST_EXECUTION_DEFAULT_UNSUPPORTED`; an optional field may use one. -- The targets contribute aliases with no arguments, written bare: `temporal.CreatedAt` (`now()` on create) and `temporal.UpdatedAt` (`now()` on create and on update). On Postgres they are `Timestamptz`, with `temporal.CreatedAtString` / `temporal.UpdatedAtString` on `TimestamptzString` and `temporal.CreatedAtJsDate` / `temporal.UpdatedAtJsDate` on `TimestamptzJsDate`; on SQLite they are `DateTime`. Each stores what the matching `temporal.createdAt()` or `temporal.updatedAt()` preset stores. `temporal.CreatedAt(3)` is `PSL_INVALID_ATTRIBUTE_ARGUMENT`; write a precision with the attributes, as in `Timestamptz(3) @onCreate(now())`. +- The targets contribute aliases with no arguments, written bare: `temporal.CreatedAt` (`now()` on create) and `temporal.UpdatedAt` (`now()` on create and on update). On Postgres they are `TimestamptzTemporal`, with `temporal.CreatedAtString` / `temporal.UpdatedAtString` on `TimestamptzString` and `temporal.CreatedAtJsDate` / `temporal.UpdatedAtJsDate` on `TimestamptzJsDate`; on SQLite they are `DateTime`. Each stores what the matching `temporal.createdAt()` or `temporal.updatedAt()` preset stores. `temporal.CreatedAt(3)` is `PSL_INVALID_ATTRIBUTE_ARGUMENT`; write a precision with the attributes, as in `TimestamptzTemporal(3) @onCreate(now())`. Integer representation authoring surface: diff --git a/packages/2-sql-family/2-authoring/contract-psl/test/fixtures.ts b/packages/2-sql-family/2-authoring/contract-psl/test/fixtures.ts index 59a5a3040b5f..71fbfe318d82 100644 --- a/packages/2-sql-family/2-authoring/contract-psl/test/fixtures.ts +++ b/packages/2-sql-family/2-authoring/contract-psl/test/fixtures.ts @@ -281,7 +281,7 @@ export const postgresScalarAuthoringTypes: AuthoringTypeNamespace = { }, }, }, - Timestamp: { + TimestampTemporal: { kind: 'typeConstructor', args: [{ kind: 'value', dataType: 'pg/int4', name: 'precision', optional: true }], output: { @@ -289,7 +289,7 @@ export const postgresScalarAuthoringTypes: AuthoringTypeNamespace = { typeParams: { precision: { kind: 'arg', index: 0 } }, }, }, - Timestamptz: { + TimestamptzTemporal: { kind: 'typeConstructor', args: [{ kind: 'value', dataType: 'pg/int4', name: 'precision', optional: true }], output: { diff --git a/packages/2-sql-family/2-authoring/contract-psl/test/interpreter.alias-attributes.test.ts b/packages/2-sql-family/2-authoring/contract-psl/test/interpreter.alias-attributes.test.ts index c25c6c2eb17a..31989ed8e872 100644 --- a/packages/2-sql-family/2-authoring/contract-psl/test/interpreter.alias-attributes.test.ts +++ b/packages/2-sql-family/2-authoring/contract-psl/test/interpreter.alias-attributes.test.ts @@ -50,7 +50,7 @@ function diagnosticsWith(schema: string, contributions: AuthoringContributions) describe('attributes on a types {} alias', () => { it('give each field typed by the alias what the same attributes give when written on the field', () => { const throughAlias = lowered(`types { - Stamp = Timestamp(3) @default(now()) @onCreate(now()) @onUpdate(now()) + Stamp = TimestampTemporal(3) @default(now()) @onCreate(now()) @onUpdate(now()) } model Event { @@ -60,8 +60,8 @@ model Event { }`); const onTheField = lowered(`model Event { id Int @id - createdAt Timestamp(3) @default(now()) @onCreate(now()) @onUpdate(now()) - updatedAt Timestamp(3) @default(now()) @onCreate(now()) @onUpdate(now()) + createdAt TimestampTemporal(3) @default(now()) @onCreate(now()) @onUpdate(now()) + updatedAt TimestampTemporal(3) @default(now()) @onCreate(now()) @onUpdate(now()) }`); expect(throughAlias).toEqual(onTheField); expect(onTheField.defaults).toHaveLength(2); @@ -70,7 +70,7 @@ model Event { it('let a field add an attribute its alias does not set', () => { const throughAlias = lowered(`types { Key = Uuid @onCreate(uuid()) - Stamp = Timestamp(3) @onUpdate(now()) + Stamp = TimestampTemporal(3) @onUpdate(now()) } model Event { @@ -79,7 +79,7 @@ model Event { }`); const onTheField = lowered(`model Event { id Uuid @id @onCreate(uuid()) - updatedAt Timestamp(3) @default(now()) @onUpdate(now()) + updatedAt TimestampTemporal(3) @default(now()) @onUpdate(now()) }`); expect(throughAlias).toEqual(onTheField); }); @@ -133,7 +133,7 @@ model Event { it('refuse a field that repeats an attribute its alias sets, naming the alias', () => { expect( diagnostics(`types { - Stamp = Timestamp(3) @default(now()) @onCreate(now()) @onUpdate(now()) + Stamp = TimestampTemporal(3) @default(now()) @onCreate(now()) @onUpdate(now()) } model Event { @@ -167,7 +167,7 @@ model Event { it('refuse an alias that repeats a generator attribute, naming the alias', () => { expect( diagnostics(`types { - Stamp = Timestamp(3) @onCreate(now()) @onCreate(now()) + Stamp = TimestampTemporal(3) @onCreate(now()) @onCreate(now()) } model Event { @@ -187,7 +187,7 @@ model Event { expect( diagnosticsWith( `types { - Stamp = Timestamp(3) @onCreate(now()) + Stamp = TimestampTemporal(3) @onCreate(now()) } type Audit { @@ -253,7 +253,7 @@ model Event { it('refuse a list field typed by an alias that generates a value', () => { expect( diagnostics(`types { - Stamp = Timestamp(3) @onCreate(now()) + Stamp = TimestampTemporal(3) @onCreate(now()) } model Event { @@ -292,7 +292,7 @@ model Event { it('accept an optional field typed by an alias that generates a value', () => { const throughAlias = lowered(`types { - Stamp = Timestamp(3) @onCreate(now()) + Stamp = TimestampTemporal(3) @onCreate(now()) } model Event { @@ -301,7 +301,7 @@ model Event { }`); const onTheField = lowered(`model Event { id Int @id - stamped Timestamp(3)? @onCreate(now()) + stamped TimestampTemporal(3)? @onCreate(now()) }`); expect(throughAlias).toEqual(onTheField); expect(onTheField.defaults).toHaveLength(1); diff --git a/packages/2-sql-family/2-authoring/contract-psl/test/interpreter.contributed-aliases.test.ts b/packages/2-sql-family/2-authoring/contract-psl/test/interpreter.contributed-aliases.test.ts index c707989e3615..9fa60af16e32 100644 --- a/packages/2-sql-family/2-authoring/contract-psl/test/interpreter.contributed-aliases.test.ts +++ b/packages/2-sql-family/2-authoring/contract-psl/test/interpreter.contributed-aliases.test.ts @@ -64,8 +64,8 @@ describe('a contributed type alias', () => { }`); const onTheField = lowered(`model Event { id Int @id - createdAt Timestamptz @onCreate(now()) - updatedAt Timestamptz @onCreate(now()) @onUpdate(now()) + createdAt TimestamptzTemporal @onCreate(now()) + updatedAt TimestamptzTemporal @onCreate(now()) @onUpdate(now()) }`); expect(throughAlias).toEqual(onTheField); expect(throughAlias.defaults).toEqual([ @@ -90,7 +90,7 @@ describe('a contributed type alias', () => { ).toEqual( lowered(`model Event { id Int @id - updatedAt Timestamptz? @onCreate(now()) @onUpdate(now()) + updatedAt TimestamptzTemporal? @onCreate(now()) @onUpdate(now()) }`), ); }); diff --git a/packages/2-sql-family/2-authoring/contract-psl/test/interpreter.diagnostics.test.ts b/packages/2-sql-family/2-authoring/contract-psl/test/interpreter.diagnostics.test.ts index 10dbd93201b2..043ed32756a5 100644 --- a/packages/2-sql-family/2-authoring/contract-psl/test/interpreter.diagnostics.test.ts +++ b/packages/2-sql-family/2-authoring/contract-psl/test/interpreter.diagnostics.test.ts @@ -418,7 +418,7 @@ model User { `types { BadChar = Char(0) BadReal = Real(1) - BadTimestamp = Timestamp(-1) + BadTimestamp = TimestampTemporal(-1) } model InvalidNativeTypes { @@ -451,7 +451,7 @@ model InvalidNativeTypes { expect.objectContaining({ code: 'PSL_INVALID_ATTRIBUTE_ARGUMENT', message: expect.stringContaining( - 'Type alias "BadTimestamp" constructor "Timestamp" Authoring helper argument at Timestamp[0] is invalid: precision must be non-negative (was -1)', + 'Type alias "BadTimestamp" constructor "TimestampTemporal" Authoring helper argument at TimestampTemporal[0] is invalid: precision must be non-negative (was -1)', ), }), ]), @@ -463,7 +463,7 @@ model InvalidNativeTypes { ['Char(10485761)', 'length must be at most 10485760 (was 10485761)'], ['Numeric(1001)', 'precision must be at most 1000 (was 1001)'], ['Numeric(10, 1001)', 'scale must be at most 1000 (was 1001)'], - ['Timestamptz(7)', 'precision must be at most 6 (was 7)'], + ['TimestamptzTemporal(7)', 'precision must be at most 6 (was 7)'], ['Time(7)', 'precision must be at most 6 (was 7)'], ['Timetz(7)', 'precision must be at most 6 (was 7)'], ])('refuses %s, outside its data type’s bounds', (call, message) => { @@ -479,8 +479,8 @@ model InvalidNativeTypes { 'Char(10485760)', 'Numeric(1000, 1000)', 'Numeric(1, 0)', - 'Timestamp(0)', - 'Timestamptz(6)', + 'TimestampTemporal(0)', + 'TimestamptzTemporal(6)', ])('accepts %s, at the edge of its data type’s bounds', (call) => { const result = interpretSqlContract(`model T {\n id Int @id\n value ${call}\n}\n`, { ...baseInput, @@ -502,7 +502,7 @@ model InvalidNativeTypes { it.each([ ['VarChar(3000000000)', '3000000000', 'Expected a number that pg/int4 can hold; got pg/int8'], - ['Timestamp(2.5)', '2.5', 'Expected a number that pg/int4 can hold; got pg/numeric'], + ['TimestampTemporal(2.5)', '2.5', 'Expected a number that pg/int4 can hold; got pg/numeric'], ['VarChar(255.0)', '255.0', 'Expected a number that pg/int4 can hold; got pg/numeric'], ['VarChar("255")', '"255"', 'Expected a number; write 255'], ['VarChar(length: true)', 'true', 'Expected a number'], @@ -549,8 +549,8 @@ model InvalidNativeTypes { }, ); - it('reports Timestamp(7) through the data type’s bounds, at the argument', () => { - const schema = 'model T {\n id Int @id\n value Timestamp(7)\n}\n'; + it('reports TimestampTemporal(7) through the data type’s bounds, at the argument', () => { + const schema = 'model T {\n id Int @id\n value TimestampTemporal(7)\n}\n'; const result = interpretSqlContract(schema, { ...baseInput, controlMutationDefaults: builtinControlMutationDefaults, diff --git a/packages/2-sql-family/2-authoring/contract-psl/test/interpreter.types.test.ts b/packages/2-sql-family/2-authoring/contract-psl/test/interpreter.types.test.ts index 5820d1952231..5d314c13dfca 100644 --- a/packages/2-sql-family/2-authoring/contract-psl/test/interpreter.types.test.ts +++ b/packages/2-sql-family/2-authoring/contract-psl/test/interpreter.types.test.ts @@ -203,8 +203,8 @@ model Event { `types { Code = Char(12) Score = Real - CreatedAt = Timestamp(3) - PublishedAt = Timestamptz(6) + CreatedAt = TimestampTemporal(3) + PublishedAt = TimestamptzTemporal(6) ReminderAt = Timetz(2) Ip = Inet } diff --git a/packages/2-sql-family/4-lanes/sql-builder/test/fixtures/generated/contract.d.ts b/packages/2-sql-family/4-lanes/sql-builder/test/fixtures/generated/contract.d.ts index 80b3d3eda96a..7a2058a0a1fe 100644 --- a/packages/2-sql-family/4-lanes/sql-builder/test/fixtures/generated/contract.d.ts +++ b/packages/2-sql-family/4-lanes/sql-builder/test/fixtures/generated/contract.d.ts @@ -43,6 +43,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; @@ -92,6 +96,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; @@ -101,6 +109,14 @@ export type AggregateTypes = { readonly output: 'pg/time-temporal@1'; readonly nullable: true; }; + readonly 'pg/timestamp-date@1': { + readonly output: 'pg/timestamp-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-instant@1': { + readonly output: 'pg/timestamp-instant@1'; + readonly nullable: true; + }; readonly 'pg/timestamp-string@1': { readonly output: 'pg/timestamp-string@1'; readonly nullable: true; @@ -152,6 +168,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; @@ -161,6 +181,14 @@ export type AggregateTypes = { readonly output: 'pg/time-temporal@1'; readonly nullable: true; }; + readonly 'pg/timestamp-date@1': { + readonly output: 'pg/timestamp-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-instant@1': { + readonly output: 'pg/timestamp-instant@1'; + readonly nullable: true; + }; readonly 'pg/timestamp-string@1': { readonly output: 'pg/timestamp-string@1'; readonly nullable: true; @@ -204,6 +232,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; diff --git a/packages/3-extensions/paradedb/migrations/snapshots/0c0734babd6eeb868fee1f281ca96963022475611560e9f170f465daa35f8599/contract.d.ts b/packages/3-extensions/paradedb/migrations/snapshots/0c0734babd6eeb868fee1f281ca96963022475611560e9f170f465daa35f8599/contract.d.ts index fc1798b6c3b0..3f411499b2fb 100644 --- a/packages/3-extensions/paradedb/migrations/snapshots/0c0734babd6eeb868fee1f281ca96963022475611560e9f170f465daa35f8599/contract.d.ts +++ b/packages/3-extensions/paradedb/migrations/snapshots/0c0734babd6eeb868fee1f281ca96963022475611560e9f170f465daa35f8599/contract.d.ts @@ -42,6 +42,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; @@ -91,6 +95,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; @@ -100,6 +108,14 @@ export type AggregateTypes = { readonly output: 'pg/time-temporal@1'; readonly nullable: true; }; + readonly 'pg/timestamp-date@1': { + readonly output: 'pg/timestamp-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-instant@1': { + readonly output: 'pg/timestamp-instant@1'; + readonly nullable: true; + }; readonly 'pg/timestamp-string@1': { readonly output: 'pg/timestamp-string@1'; readonly nullable: true; @@ -151,6 +167,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; @@ -160,6 +180,14 @@ export type AggregateTypes = { readonly output: 'pg/time-temporal@1'; readonly nullable: true; }; + readonly 'pg/timestamp-date@1': { + readonly output: 'pg/timestamp-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-instant@1': { + readonly output: 'pg/timestamp-instant@1'; + readonly nullable: true; + }; readonly 'pg/timestamp-string@1': { readonly output: 'pg/timestamp-string@1'; readonly nullable: true; @@ -203,6 +231,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; diff --git a/packages/3-extensions/paradedb/src/contract.d.ts b/packages/3-extensions/paradedb/src/contract.d.ts index fc1798b6c3b0..3f411499b2fb 100644 --- a/packages/3-extensions/paradedb/src/contract.d.ts +++ b/packages/3-extensions/paradedb/src/contract.d.ts @@ -42,6 +42,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; @@ -91,6 +95,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; @@ -100,6 +108,14 @@ export type AggregateTypes = { readonly output: 'pg/time-temporal@1'; readonly nullable: true; }; + readonly 'pg/timestamp-date@1': { + readonly output: 'pg/timestamp-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-instant@1': { + readonly output: 'pg/timestamp-instant@1'; + readonly nullable: true; + }; readonly 'pg/timestamp-string@1': { readonly output: 'pg/timestamp-string@1'; readonly nullable: true; @@ -151,6 +167,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; @@ -160,6 +180,14 @@ export type AggregateTypes = { readonly output: 'pg/time-temporal@1'; readonly nullable: true; }; + readonly 'pg/timestamp-date@1': { + readonly output: 'pg/timestamp-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-instant@1': { + readonly output: 'pg/timestamp-instant@1'; + readonly nullable: true; + }; readonly 'pg/timestamp-string@1': { readonly output: 'pg/timestamp-string@1'; readonly nullable: true; @@ -203,6 +231,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; diff --git a/packages/3-extensions/pgvector/migrations/snapshots/4a96b488a4ce92b434e5f7d6607b6435c0955f0d36b0018077045787764240e6/contract.d.ts b/packages/3-extensions/pgvector/migrations/snapshots/4a96b488a4ce92b434e5f7d6607b6435c0955f0d36b0018077045787764240e6/contract.d.ts index 64c2ccc4f849..9f816cdefb5f 100644 --- a/packages/3-extensions/pgvector/migrations/snapshots/4a96b488a4ce92b434e5f7d6607b6435c0955f0d36b0018077045787764240e6/contract.d.ts +++ b/packages/3-extensions/pgvector/migrations/snapshots/4a96b488a4ce92b434e5f7d6607b6435c0955f0d36b0018077045787764240e6/contract.d.ts @@ -42,6 +42,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; @@ -91,6 +95,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; @@ -100,6 +108,14 @@ export type AggregateTypes = { readonly output: 'pg/time-temporal@1'; readonly nullable: true; }; + readonly 'pg/timestamp-date@1': { + readonly output: 'pg/timestamp-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-instant@1': { + readonly output: 'pg/timestamp-instant@1'; + readonly nullable: true; + }; readonly 'pg/timestamp-string@1': { readonly output: 'pg/timestamp-string@1'; readonly nullable: true; @@ -151,6 +167,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; @@ -160,6 +180,14 @@ export type AggregateTypes = { readonly output: 'pg/time-temporal@1'; readonly nullable: true; }; + readonly 'pg/timestamp-date@1': { + readonly output: 'pg/timestamp-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-instant@1': { + readonly output: 'pg/timestamp-instant@1'; + readonly nullable: true; + }; readonly 'pg/timestamp-string@1': { readonly output: 'pg/timestamp-string@1'; readonly nullable: true; @@ -203,6 +231,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; diff --git a/packages/3-extensions/pgvector/src/contract.d.ts b/packages/3-extensions/pgvector/src/contract.d.ts index 64c2ccc4f849..9f816cdefb5f 100644 --- a/packages/3-extensions/pgvector/src/contract.d.ts +++ b/packages/3-extensions/pgvector/src/contract.d.ts @@ -42,6 +42,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; @@ -91,6 +95,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; @@ -100,6 +108,14 @@ export type AggregateTypes = { readonly output: 'pg/time-temporal@1'; readonly nullable: true; }; + readonly 'pg/timestamp-date@1': { + readonly output: 'pg/timestamp-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-instant@1': { + readonly output: 'pg/timestamp-instant@1'; + readonly nullable: true; + }; readonly 'pg/timestamp-string@1': { readonly output: 'pg/timestamp-string@1'; readonly nullable: true; @@ -151,6 +167,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; @@ -160,6 +180,14 @@ export type AggregateTypes = { readonly output: 'pg/time-temporal@1'; readonly nullable: true; }; + readonly 'pg/timestamp-date@1': { + readonly output: 'pg/timestamp-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-instant@1': { + readonly output: 'pg/timestamp-instant@1'; + readonly nullable: true; + }; readonly 'pg/timestamp-string@1': { readonly output: 'pg/timestamp-string@1'; readonly nullable: true; @@ -203,6 +231,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; diff --git a/packages/3-extensions/postgis/migrations/snapshots/2db551a8a5619d0d97710f29130eaea0eccbdd5238f015ee9d18408d438e0f8a/contract.d.ts b/packages/3-extensions/postgis/migrations/snapshots/2db551a8a5619d0d97710f29130eaea0eccbdd5238f015ee9d18408d438e0f8a/contract.d.ts index b56429d1fad7..0556e3ca8e35 100644 --- a/packages/3-extensions/postgis/migrations/snapshots/2db551a8a5619d0d97710f29130eaea0eccbdd5238f015ee9d18408d438e0f8a/contract.d.ts +++ b/packages/3-extensions/postgis/migrations/snapshots/2db551a8a5619d0d97710f29130eaea0eccbdd5238f015ee9d18408d438e0f8a/contract.d.ts @@ -42,6 +42,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; @@ -91,6 +95,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; @@ -100,6 +108,14 @@ export type AggregateTypes = { readonly output: 'pg/time-temporal@1'; readonly nullable: true; }; + readonly 'pg/timestamp-date@1': { + readonly output: 'pg/timestamp-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-instant@1': { + readonly output: 'pg/timestamp-instant@1'; + readonly nullable: true; + }; readonly 'pg/timestamp-string@1': { readonly output: 'pg/timestamp-string@1'; readonly nullable: true; @@ -151,6 +167,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; @@ -160,6 +180,14 @@ export type AggregateTypes = { readonly output: 'pg/time-temporal@1'; readonly nullable: true; }; + readonly 'pg/timestamp-date@1': { + readonly output: 'pg/timestamp-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-instant@1': { + readonly output: 'pg/timestamp-instant@1'; + readonly nullable: true; + }; readonly 'pg/timestamp-string@1': { readonly output: 'pg/timestamp-string@1'; readonly nullable: true; @@ -203,6 +231,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; diff --git a/packages/3-extensions/postgis/src/contract.d.ts b/packages/3-extensions/postgis/src/contract.d.ts index b56429d1fad7..0556e3ca8e35 100644 --- a/packages/3-extensions/postgis/src/contract.d.ts +++ b/packages/3-extensions/postgis/src/contract.d.ts @@ -42,6 +42,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; @@ -91,6 +95,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; @@ -100,6 +108,14 @@ export type AggregateTypes = { readonly output: 'pg/time-temporal@1'; readonly nullable: true; }; + readonly 'pg/timestamp-date@1': { + readonly output: 'pg/timestamp-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-instant@1': { + readonly output: 'pg/timestamp-instant@1'; + readonly nullable: true; + }; readonly 'pg/timestamp-string@1': { readonly output: 'pg/timestamp-string@1'; readonly nullable: true; @@ -151,6 +167,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; @@ -160,6 +180,14 @@ export type AggregateTypes = { readonly output: 'pg/time-temporal@1'; readonly nullable: true; }; + readonly 'pg/timestamp-date@1': { + readonly output: 'pg/timestamp-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-instant@1': { + readonly output: 'pg/timestamp-instant@1'; + readonly nullable: true; + }; readonly 'pg/timestamp-string@1': { readonly output: 'pg/timestamp-string@1'; readonly nullable: true; @@ -203,6 +231,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; diff --git a/packages/3-extensions/sql-orm-client/test/fixtures/collection-methods/generated/contract.d.ts b/packages/3-extensions/sql-orm-client/test/fixtures/collection-methods/generated/contract.d.ts index 98347ff79255..3a9d55796e0e 100644 --- a/packages/3-extensions/sql-orm-client/test/fixtures/collection-methods/generated/contract.d.ts +++ b/packages/3-extensions/sql-orm-client/test/fixtures/collection-methods/generated/contract.d.ts @@ -48,6 +48,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; @@ -97,6 +101,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; @@ -106,6 +114,14 @@ export type AggregateTypes = { readonly output: 'pg/time-temporal@1'; readonly nullable: true; }; + readonly 'pg/timestamp-date@1': { + readonly output: 'pg/timestamp-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-instant@1': { + readonly output: 'pg/timestamp-instant@1'; + readonly nullable: true; + }; readonly 'pg/timestamp-string@1': { readonly output: 'pg/timestamp-string@1'; readonly nullable: true; @@ -157,6 +173,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; @@ -166,6 +186,14 @@ export type AggregateTypes = { readonly output: 'pg/time-temporal@1'; readonly nullable: true; }; + readonly 'pg/timestamp-date@1': { + readonly output: 'pg/timestamp-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-instant@1': { + readonly output: 'pg/timestamp-instant@1'; + readonly nullable: true; + }; readonly 'pg/timestamp-string@1': { readonly output: 'pg/timestamp-string@1'; readonly nullable: true; @@ -209,6 +237,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; diff --git a/packages/3-extensions/sql-orm-client/test/fixtures/fragment-namespace/generated/contract.d.ts b/packages/3-extensions/sql-orm-client/test/fixtures/fragment-namespace/generated/contract.d.ts index 522738f53abe..4a0bc8f4834c 100644 --- a/packages/3-extensions/sql-orm-client/test/fixtures/fragment-namespace/generated/contract.d.ts +++ b/packages/3-extensions/sql-orm-client/test/fixtures/fragment-namespace/generated/contract.d.ts @@ -42,6 +42,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; @@ -91,6 +95,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; @@ -100,6 +108,14 @@ export type AggregateTypes = { readonly output: 'pg/time-temporal@1'; readonly nullable: true; }; + readonly 'pg/timestamp-date@1': { + readonly output: 'pg/timestamp-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-instant@1': { + readonly output: 'pg/timestamp-instant@1'; + readonly nullable: true; + }; readonly 'pg/timestamp-string@1': { readonly output: 'pg/timestamp-string@1'; readonly nullable: true; @@ -151,6 +167,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; @@ -160,6 +180,14 @@ export type AggregateTypes = { readonly output: 'pg/time-temporal@1'; readonly nullable: true; }; + readonly 'pg/timestamp-date@1': { + readonly output: 'pg/timestamp-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-instant@1': { + readonly output: 'pg/timestamp-instant@1'; + readonly nullable: true; + }; readonly 'pg/timestamp-string@1': { readonly output: 'pg/timestamp-string@1'; readonly nullable: true; @@ -203,6 +231,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; diff --git a/packages/3-extensions/sql-orm-client/test/fixtures/generated/contract.d.ts b/packages/3-extensions/sql-orm-client/test/fixtures/generated/contract.d.ts index fb429f0fef68..616e21b8f485 100644 --- a/packages/3-extensions/sql-orm-client/test/fixtures/generated/contract.d.ts +++ b/packages/3-extensions/sql-orm-client/test/fixtures/generated/contract.d.ts @@ -63,6 +63,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; @@ -112,6 +116,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; @@ -121,6 +129,14 @@ export type AggregateTypes = { readonly output: 'pg/time-temporal@1'; readonly nullable: true; }; + readonly 'pg/timestamp-date@1': { + readonly output: 'pg/timestamp-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-instant@1': { + readonly output: 'pg/timestamp-instant@1'; + readonly nullable: true; + }; readonly 'pg/timestamp-string@1': { readonly output: 'pg/timestamp-string@1'; readonly nullable: true; @@ -172,6 +188,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; @@ -181,6 +201,14 @@ export type AggregateTypes = { readonly output: 'pg/time-temporal@1'; readonly nullable: true; }; + readonly 'pg/timestamp-date@1': { + readonly output: 'pg/timestamp-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-instant@1': { + readonly output: 'pg/timestamp-instant@1'; + readonly nullable: true; + }; readonly 'pg/timestamp-string@1': { readonly output: 'pg/timestamp-string@1'; readonly nullable: true; @@ -224,6 +252,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; diff --git a/packages/3-extensions/sql-orm-client/test/fixtures/junction-namespaces/generated/contract.d.ts b/packages/3-extensions/sql-orm-client/test/fixtures/junction-namespaces/generated/contract.d.ts index 73af750f4244..cf321d22b5ac 100644 --- a/packages/3-extensions/sql-orm-client/test/fixtures/junction-namespaces/generated/contract.d.ts +++ b/packages/3-extensions/sql-orm-client/test/fixtures/junction-namespaces/generated/contract.d.ts @@ -43,6 +43,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; @@ -92,6 +96,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; @@ -101,6 +109,14 @@ export type AggregateTypes = { readonly output: 'pg/time-temporal@1'; readonly nullable: true; }; + readonly 'pg/timestamp-date@1': { + readonly output: 'pg/timestamp-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-instant@1': { + readonly output: 'pg/timestamp-instant@1'; + readonly nullable: true; + }; readonly 'pg/timestamp-string@1': { readonly output: 'pg/timestamp-string@1'; readonly nullable: true; @@ -152,6 +168,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; @@ -161,6 +181,14 @@ export type AggregateTypes = { readonly output: 'pg/time-temporal@1'; readonly nullable: true; }; + readonly 'pg/timestamp-date@1': { + readonly output: 'pg/timestamp-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-instant@1': { + readonly output: 'pg/timestamp-instant@1'; + readonly nullable: true; + }; readonly 'pg/timestamp-string@1': { readonly output: 'pg/timestamp-string@1'; readonly nullable: true; @@ -204,6 +232,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; diff --git a/packages/3-extensions/sql-orm-client/test/fixtures/on-update-only/generated/contract.d.ts b/packages/3-extensions/sql-orm-client/test/fixtures/on-update-only/generated/contract.d.ts index 78960b7aa687..37b0bd85e647 100644 --- a/packages/3-extensions/sql-orm-client/test/fixtures/on-update-only/generated/contract.d.ts +++ b/packages/3-extensions/sql-orm-client/test/fixtures/on-update-only/generated/contract.d.ts @@ -43,6 +43,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; @@ -92,6 +96,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; @@ -101,6 +109,14 @@ export type AggregateTypes = { readonly output: 'pg/time-temporal@1'; readonly nullable: true; }; + readonly 'pg/timestamp-date@1': { + readonly output: 'pg/timestamp-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-instant@1': { + readonly output: 'pg/timestamp-instant@1'; + readonly nullable: true; + }; readonly 'pg/timestamp-string@1': { readonly output: 'pg/timestamp-string@1'; readonly nullable: true; @@ -152,6 +168,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; @@ -161,6 +181,14 @@ export type AggregateTypes = { readonly output: 'pg/time-temporal@1'; readonly nullable: true; }; + readonly 'pg/timestamp-date@1': { + readonly output: 'pg/timestamp-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-instant@1': { + readonly output: 'pg/timestamp-instant@1'; + readonly nullable: true; + }; readonly 'pg/timestamp-string@1': { readonly output: 'pg/timestamp-string@1'; readonly nullable: true; @@ -204,6 +232,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; diff --git a/packages/3-extensions/sql-orm-client/test/fixtures/polymorphism/generated/contract.d.ts b/packages/3-extensions/sql-orm-client/test/fixtures/polymorphism/generated/contract.d.ts index 90af9e6edd9b..741846a4cec7 100644 --- a/packages/3-extensions/sql-orm-client/test/fixtures/polymorphism/generated/contract.d.ts +++ b/packages/3-extensions/sql-orm-client/test/fixtures/polymorphism/generated/contract.d.ts @@ -42,6 +42,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; @@ -91,6 +95,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; @@ -100,6 +108,14 @@ export type AggregateTypes = { readonly output: 'pg/time-temporal@1'; readonly nullable: true; }; + readonly 'pg/timestamp-date@1': { + readonly output: 'pg/timestamp-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-instant@1': { + readonly output: 'pg/timestamp-instant@1'; + readonly nullable: true; + }; readonly 'pg/timestamp-string@1': { readonly output: 'pg/timestamp-string@1'; readonly nullable: true; @@ -151,6 +167,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; @@ -160,6 +180,14 @@ export type AggregateTypes = { readonly output: 'pg/time-temporal@1'; readonly nullable: true; }; + readonly 'pg/timestamp-date@1': { + readonly output: 'pg/timestamp-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-instant@1': { + readonly output: 'pg/timestamp-instant@1'; + readonly nullable: true; + }; readonly 'pg/timestamp-string@1': { readonly output: 'pg/timestamp-string@1'; readonly nullable: true; @@ -203,6 +231,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; diff --git a/packages/3-extensions/sql-orm-client/test/fixtures/scope-namespace/generated/contract.d.ts b/packages/3-extensions/sql-orm-client/test/fixtures/scope-namespace/generated/contract.d.ts index ffa93ebd92ef..bc89d2cf7208 100644 --- a/packages/3-extensions/sql-orm-client/test/fixtures/scope-namespace/generated/contract.d.ts +++ b/packages/3-extensions/sql-orm-client/test/fixtures/scope-namespace/generated/contract.d.ts @@ -42,6 +42,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; @@ -91,6 +95,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; @@ -100,6 +108,14 @@ export type AggregateTypes = { readonly output: 'pg/time-temporal@1'; readonly nullable: true; }; + readonly 'pg/timestamp-date@1': { + readonly output: 'pg/timestamp-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-instant@1': { + readonly output: 'pg/timestamp-instant@1'; + readonly nullable: true; + }; readonly 'pg/timestamp-string@1': { readonly output: 'pg/timestamp-string@1'; readonly nullable: true; @@ -151,6 +167,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; @@ -160,6 +180,14 @@ export type AggregateTypes = { readonly output: 'pg/time-temporal@1'; readonly nullable: true; }; + readonly 'pg/timestamp-date@1': { + readonly output: 'pg/timestamp-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-instant@1': { + readonly output: 'pg/timestamp-instant@1'; + readonly nullable: true; + }; readonly 'pg/timestamp-string@1': { readonly output: 'pg/timestamp-string@1'; readonly nullable: true; @@ -203,6 +231,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; diff --git a/packages/3-extensions/sql-orm-client/test/fixtures/soft-delete/generated/contract.d.ts b/packages/3-extensions/sql-orm-client/test/fixtures/soft-delete/generated/contract.d.ts index 64b6249f01d3..62f3fe8bdef7 100644 --- a/packages/3-extensions/sql-orm-client/test/fixtures/soft-delete/generated/contract.d.ts +++ b/packages/3-extensions/sql-orm-client/test/fixtures/soft-delete/generated/contract.d.ts @@ -43,6 +43,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; @@ -92,6 +96,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; @@ -101,6 +109,14 @@ export type AggregateTypes = { readonly output: 'pg/time-temporal@1'; readonly nullable: true; }; + readonly 'pg/timestamp-date@1': { + readonly output: 'pg/timestamp-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-instant@1': { + readonly output: 'pg/timestamp-instant@1'; + readonly nullable: true; + }; readonly 'pg/timestamp-string@1': { readonly output: 'pg/timestamp-string@1'; readonly nullable: true; @@ -152,6 +168,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; @@ -161,6 +181,14 @@ export type AggregateTypes = { readonly output: 'pg/time-temporal@1'; readonly nullable: true; }; + readonly 'pg/timestamp-date@1': { + readonly output: 'pg/timestamp-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-instant@1': { + readonly output: 'pg/timestamp-instant@1'; + readonly nullable: true; + }; readonly 'pg/timestamp-string@1': { readonly output: 'pg/timestamp-string@1'; readonly nullable: true; @@ -204,6 +232,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; diff --git a/packages/3-extensions/supabase/scripts/generate-contract.ts b/packages/3-extensions/supabase/scripts/generate-contract.ts index 45283584499f..926f8c48d0e2 100644 --- a/packages/3-extensions/supabase/scripts/generate-contract.ts +++ b/packages/3-extensions/supabase/scripts/generate-contract.ts @@ -297,7 +297,7 @@ const NAMED_TYPE_ALIASES: Readonly> = { Inet: 'IpAddress', Json: 'Payload', SmallInt: 'EmailChangeConfirmStatus', - Timestamp: 'CreatedAt', + TimestampString: 'CreatedAt', Uuid: 'Id', 'VarChar(40)': 'Hash', 'VarChar(64)': 'IpAddress2', diff --git a/packages/3-extensions/supabase/src/contract/contract.d.ts b/packages/3-extensions/supabase/src/contract/contract.d.ts index f480e822713e..dbc9c7898e78 100644 --- a/packages/3-extensions/supabase/src/contract/contract.d.ts +++ b/packages/3-extensions/supabase/src/contract/contract.d.ts @@ -42,6 +42,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; @@ -91,6 +95,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; @@ -100,6 +108,14 @@ export type AggregateTypes = { readonly output: 'pg/time-temporal@1'; readonly nullable: true; }; + readonly 'pg/timestamp-date@1': { + readonly output: 'pg/timestamp-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-instant@1': { + readonly output: 'pg/timestamp-instant@1'; + readonly nullable: true; + }; readonly 'pg/timestamp-string@1': { readonly output: 'pg/timestamp-string@1'; readonly nullable: true; @@ -151,6 +167,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; @@ -160,6 +180,14 @@ export type AggregateTypes = { readonly output: 'pg/time-temporal@1'; readonly nullable: true; }; + readonly 'pg/timestamp-date@1': { + readonly output: 'pg/timestamp-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-instant@1': { + readonly output: 'pg/timestamp-instant@1'; + readonly nullable: true; + }; readonly 'pg/timestamp-string@1': { readonly output: 'pg/timestamp-string@1'; readonly nullable: true; @@ -203,6 +231,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; diff --git a/packages/3-extensions/supabase/src/contract/contract.prisma b/packages/3-extensions/supabase/src/contract/contract.prisma index 9935f0e5b630..3af4fa7b7341 100644 --- a/packages/3-extensions/supabase/src/contract/contract.prisma +++ b/packages/3-extensions/supabase/src/contract/contract.prisma @@ -2,7 +2,7 @@ // Contract inferred from the live database schema. Edit as needed, then run `prisma contract emit`. types { - CreatedAt = Timestamp + CreatedAt = TimestampTemporal EmailChangeConfirmStatus = SmallInt Hash = VarChar(40) Id = Uuid @@ -18,7 +18,7 @@ namespace auth { id Id @id(map: "audit_log_entries_pkey") instanceId Id? @map("instance_id") payload Payload? - createdAt Timestamptz? @map("created_at") + createdAt TimestamptzTemporal? @map("created_at") ipAddress IpAddress2 @default("") @map("ip_address") @@index([instanceId], map: "audit_logs_instance_id_idx") @@ -33,34 +33,34 @@ namespace auth { role Parent? email Parent? encryptedPassword Parent? @map("encrypted_password") - emailConfirmedAt Timestamptz? @map("email_confirmed_at") - invitedAt Timestamptz? @map("invited_at") + emailConfirmedAt TimestamptzTemporal? @map("email_confirmed_at") + invitedAt TimestamptzTemporal? @map("invited_at") confirmationToken Parent? @map("confirmation_token") - confirmationSentAt Timestamptz? @map("confirmation_sent_at") + confirmationSentAt TimestamptzTemporal? @map("confirmation_sent_at") recoveryToken Parent? @map("recovery_token") - recoverySentAt Timestamptz? @map("recovery_sent_at") + recoverySentAt TimestamptzTemporal? @map("recovery_sent_at") emailChangeTokenNew Parent? @map("email_change_token_new") emailChange Parent? @map("email_change") - emailChangeSentAt Timestamptz? @map("email_change_sent_at") - lastSignInAt Timestamptz? @map("last_sign_in_at") + emailChangeSentAt TimestamptzTemporal? @map("email_change_sent_at") + lastSignInAt TimestamptzTemporal? @map("last_sign_in_at") rawAppMetaData Jsonb? @map("raw_app_meta_data") rawUserMetaData Jsonb? @map("raw_user_meta_data") isSuperAdmin Boolean? @map("is_super_admin") - createdAt Timestamptz? @map("created_at") - updatedAt Timestamptz? @map("updated_at") + createdAt TimestamptzTemporal? @map("created_at") + updatedAt TimestamptzTemporal? @map("updated_at") phone String? @unique(map: "users_phone_key") - phoneConfirmedAt Timestamptz? @map("phone_confirmed_at") + phoneConfirmedAt TimestamptzTemporal? @map("phone_confirmed_at") phoneChange String? @default("") @map("phone_change") phoneChangeToken Parent? @default("") @map("phone_change_token") - phoneChangeSentAt Timestamptz? @map("phone_change_sent_at") - confirmedAt Timestamptz? @map("confirmed_at") + phoneChangeSentAt TimestamptzTemporal? @map("phone_change_sent_at") + confirmedAt TimestamptzTemporal? @map("confirmed_at") emailChangeTokenCurrent Parent? @default("") @map("email_change_token_current") emailChangeConfirmStatus EmailChangeConfirmStatus? @default(0) @map("email_change_confirm_status") - bannedUntil Timestamptz? @map("banned_until") + bannedUntil TimestamptzTemporal? @map("banned_until") reauthenticationToken Parent? @default("") @map("reauthentication_token") - reauthenticationSentAt Timestamptz? @map("reauthentication_sent_at") + reauthenticationSentAt TimestamptzTemporal? @map("reauthentication_sent_at") isSsoUser Boolean @default(false) @map("is_sso_user") - deletedAt Timestamptz? @map("deleted_at") + deletedAt TimestamptzTemporal? @map("deleted_at") isAnonymous Boolean @default(false) @map("is_anonymous") identities AuthIdentity[] mfaFactors MfaFactors[] @@ -91,9 +91,9 @@ namespace auth { userId Id @map("user_id") identityData Jsonb @map("identity_data") provider String - lastSignInAt Timestamptz? @map("last_sign_in_at") - createdAt Timestamptz? @map("created_at") - updatedAt Timestamptz? @map("updated_at") + lastSignInAt TimestamptzTemporal? @map("last_sign_in_at") + createdAt TimestamptzTemporal? @map("created_at") + updatedAt TimestamptzTemporal? @map("updated_at") email String? user AuthUser @relation(fields: [userId], references: [id], onDelete: Cascade, map: "identities_user_id_fkey") @@ -113,9 +113,9 @@ namespace auth { clientName String? @map("client_name") clientUri String? @map("client_uri") logoUri String? @map("logo_uri") - createdAt Timestamptz @default(now()) @map("created_at") - updatedAt Timestamptz @default(now()) @map("updated_at") - deletedAt Timestamptz? @map("deleted_at") + createdAt TimestamptzTemporal @default(now()) @map("created_at") + updatedAt TimestamptzTemporal @default(now()) @map("updated_at") + deletedAt TimestamptzTemporal? @map("deleted_at") clientType pg.enum(OauthClientType) @default("confidential") @map("client_type") tokenEndpointAuthMethod String @map("token_endpoint_auth_method") oauthAuthorizations OauthAuthorizations[] @@ -133,11 +133,11 @@ namespace auth { model AuthSession { id Id @id(map: "sessions_pkey") userId Id @map("user_id") - createdAt Timestamptz? @map("created_at") - updatedAt Timestamptz? @map("updated_at") + createdAt TimestamptzTemporal? @map("created_at") + updatedAt TimestamptzTemporal? @map("updated_at") factorId Id? @map("factor_id") aal pg.enum(AalLevel)? - notAfter Timestamptz? @map("not_after") + notAfter TimestamptzTemporal? @map("not_after") refreshedAt CreatedAt? @map("refreshed_at") userAgent String? @map("user_agent") ip IpAddress? @@ -178,13 +178,13 @@ namespace auth { discoveryUrl String? @map("discovery_url") skipNonceCheck Boolean @default(false) @map("skip_nonce_check") cachedDiscovery Jsonb? @map("cached_discovery") - discoveryCachedAt Timestamptz? @map("discovery_cached_at") + discoveryCachedAt TimestamptzTemporal? @map("discovery_cached_at") authorizationUrl String? @map("authorization_url") tokenUrl String? @map("token_url") userinfoUrl String? @map("userinfo_url") jwksUri String? @map("jwks_uri") - createdAt Timestamptz @default(now()) @map("created_at") - updatedAt Timestamptz @default(now()) @map("updated_at") + createdAt TimestamptzTemporal @default(now()) @map("created_at") + updatedAt TimestamptzTemporal @default(now()) @map("updated_at") @@index([createdAt], map: "custom_oauth_providers_created_at_idx") @@index([enabled], map: "custom_oauth_providers_enabled_idx") @@ -220,10 +220,10 @@ namespace auth { providerType String @map("provider_type") providerAccessToken String? @map("provider_access_token") providerRefreshToken String? @map("provider_refresh_token") - createdAt Timestamptz? @map("created_at") - updatedAt Timestamptz? @map("updated_at") + createdAt TimestamptzTemporal? @map("created_at") + updatedAt TimestamptzTemporal? @map("updated_at") authenticationMethod String @map("authentication_method") - authCodeIssuedAt Timestamptz? @map("auth_code_issued_at") + authCodeIssuedAt TimestamptzTemporal? @map("auth_code_issued_at") inviteToken String? @map("invite_token") referrer String? oauthClientStateId Id? @map("oauth_client_state_id") @@ -242,8 +242,8 @@ namespace auth { id Id @id(map: "instances_pkey") uuid Id? rawBaseConfig String? @map("raw_base_config") - createdAt Timestamptz? @map("created_at") - updatedAt Timestamptz? @map("updated_at") + createdAt TimestamptzTemporal? @map("created_at") + updatedAt TimestamptzTemporal? @map("updated_at") @@rls @@map("instances") @@ -252,8 +252,8 @@ namespace auth { model MfaAmrClaims { id Id @id(map: "amr_id_pk") sessionId Id @map("session_id") - createdAt Timestamptz @map("created_at") - updatedAt Timestamptz @map("updated_at") + createdAt TimestamptzTemporal @map("created_at") + updatedAt TimestamptzTemporal @map("updated_at") authenticationMethod String @map("authentication_method") session AuthSession @relation(fields: [sessionId], references: [id], onDelete: Cascade, map: "mfa_amr_claims_session_id_fkey", index: "mfa_amr_claims_session_id_authentication_method_pkey") @@ -268,11 +268,11 @@ namespace auth { friendlyName String? @map("friendly_name") factorType pg.enum(FactorType) @map("factor_type") status pg.enum(FactorStatus) - createdAt Timestamptz @map("created_at") - updatedAt Timestamptz @map("updated_at") + createdAt TimestamptzTemporal @map("created_at") + updatedAt TimestamptzTemporal @map("updated_at") secret String? phone String? - lastChallengedAt Timestamptz? @unique(map: "mfa_factors_last_challenged_at_key") @map("last_challenged_at") + lastChallengedAt TimestamptzTemporal? @unique(map: "mfa_factors_last_challenged_at_key") @map("last_challenged_at") webAuthnCredential Jsonb? @map("web_authn_credential") webAuthnAaguid Id? @map("web_authn_aaguid") lastWebauthnChallengeData Jsonb? @map("last_webauthn_challenge_data") @@ -290,8 +290,8 @@ namespace auth { model MfaChallenges { id Id @id(map: "mfa_challenges_pkey") factorId Id @map("factor_id") - createdAt Timestamptz @map("created_at") - verifiedAt Timestamptz? @map("verified_at") + createdAt TimestamptzTemporal @map("created_at") + verifiedAt TimestamptzTemporal? @map("verified_at") ipAddress IpAddress @map("ip_address") otpCode String? @map("otp_code") webAuthnSessionData Jsonb? @map("web_authn_session_data") @@ -316,9 +316,9 @@ namespace auth { responseType pg.enum(OauthResponseType) @default("code") @map("response_type") status pg.enum(OauthAuthorizationStatus) @default("pending") authorizationCode String? @unique(map: "oauth_authorizations_authorization_code_key") @map("authorization_code") - createdAt Timestamptz @default(now()) @map("created_at") - expiresAt Timestamptz @default(sql`(now() + '00:03:00'::interval)`) @map("expires_at") - approvedAt Timestamptz? @map("approved_at") + createdAt TimestamptzTemporal @default(now()) @map("created_at") + expiresAt TimestamptzTemporal @default(sql`(now() + '00:03:00'::interval)`) @map("expires_at") + approvedAt TimestamptzTemporal? @map("approved_at") nonce String? client OauthClients @relation(fields: [clientId], references: [id], onDelete: Cascade, map: "oauth_authorizations_client_id_fkey", index: false) user AuthUser? @relation(fields: [userId], references: [id], onDelete: Cascade, map: "oauth_authorizations_user_id_fkey", index: false) @@ -339,7 +339,7 @@ namespace auth { id Id @id(map: "oauth_client_states_pkey") providerType String @map("provider_type") codeVerifier String? @map("code_verifier") - createdAt Timestamptz @map("created_at") + createdAt TimestamptzTemporal @map("created_at") @@index([createdAt], map: "idx_oauth_client_states_created_at") @@map("oauth_client_states") @@ -350,8 +350,8 @@ namespace auth { userId Id @map("user_id") clientId Id @map("client_id") scopes String - grantedAt Timestamptz @default(now()) @map("granted_at") - revokedAt Timestamptz? @map("revoked_at") + grantedAt TimestamptzTemporal @default(now()) @map("granted_at") + revokedAt TimestamptzTemporal? @map("revoked_at") client OauthClients @relation(fields: [clientId], references: [id], onDelete: Cascade, map: "oauth_consents_client_id_fkey", index: false) user AuthUser @relation(fields: [userId], references: [id], onDelete: Cascade, map: "oauth_consents_user_id_fkey", index: "oauth_consents_user_client_unique") @@ -389,8 +389,8 @@ namespace auth { token Parent? @unique(map: "refresh_tokens_token_unique") userId Parent? @map("user_id") revoked Boolean? - createdAt Timestamptz? @map("created_at") - updatedAt Timestamptz? @map("updated_at") + createdAt TimestamptzTemporal? @map("created_at") + updatedAt TimestamptzTemporal? @map("updated_at") parent Parent? sessionId Id? @map("session_id") session AuthSession? @relation(fields: [sessionId], references: [id], onDelete: Cascade, map: "refresh_tokens_session_id_fkey", index: "refresh_tokens_session_id_revoked_idx") @@ -407,8 +407,8 @@ namespace auth { model SsoProviders { id Id @id(map: "sso_providers_pkey") resourceId String? @map("resource_id") - createdAt Timestamptz? @map("created_at") - updatedAt Timestamptz? @map("updated_at") + createdAt TimestamptzTemporal? @map("created_at") + updatedAt TimestamptzTemporal? @map("updated_at") disabled Boolean? samlProviders SamlProviders[] samlRelayStates SamlRelayStates[] @@ -428,8 +428,8 @@ namespace auth { metadataXml String @map("metadata_xml") metadataUrl String? @map("metadata_url") attributeMapping Jsonb? @map("attribute_mapping") - createdAt Timestamptz? @map("created_at") - updatedAt Timestamptz? @map("updated_at") + createdAt TimestamptzTemporal? @map("created_at") + updatedAt TimestamptzTemporal? @map("updated_at") nameIdFormat String? @map("name_id_format") ssoProvider SsoProviders @relation(fields: [ssoProviderId], references: [id], onDelete: Cascade, map: "saml_providers_sso_provider_id_fkey") @@ -447,8 +447,8 @@ namespace auth { requestId String @map("request_id") forEmail String? @map("for_email") redirectTo String? @map("redirect_to") - createdAt Timestamptz? @map("created_at") - updatedAt Timestamptz? @map("updated_at") + createdAt TimestamptzTemporal? @map("created_at") + updatedAt TimestamptzTemporal? @map("updated_at") flowStateId Id? @map("flow_state_id") flowState FlowState? @relation(fields: [flowStateId], references: [id], onDelete: Cascade, map: "saml_relay_states_flow_state_id_fkey", index: false) ssoProvider SsoProviders @relation(fields: [ssoProviderId], references: [id], onDelete: Cascade, map: "saml_relay_states_sso_provider_id_fkey") @@ -472,8 +472,8 @@ namespace auth { id Id @id(map: "sso_domains_pkey") ssoProviderId Id @map("sso_provider_id") domain String - createdAt Timestamptz? @map("created_at") - updatedAt Timestamptz? @map("updated_at") + createdAt TimestamptzTemporal? @map("created_at") + updatedAt TimestamptzTemporal? @map("updated_at") ssoProvider SsoProviders @relation(fields: [ssoProviderId], references: [id], onDelete: Cascade, map: "sso_domains_sso_provider_id_fkey") @@index(expression: sql`lower(domain)`, map: "sso_domains_domain_idx", unique: true) @@ -488,8 +488,8 @@ namespace auth { userId Id? @map("user_id") challengeType String @map("challenge_type") sessionData Jsonb @map("session_data") - createdAt Timestamptz @default(now()) @map("created_at") - expiresAt Timestamptz @map("expires_at") + createdAt TimestamptzTemporal @default(now()) @map("created_at") + expiresAt TimestamptzTemporal @map("expires_at") user AuthUser? @relation(fields: [userId], references: [id], onDelete: Cascade, map: "webauthn_challenges_user_id_fkey") @@index([expiresAt], map: "webauthn_challenges_expires_at_idx") @@ -510,9 +510,9 @@ namespace auth { backupEligible Boolean @default(false) @map("backup_eligible") backedUp Boolean @default(false) @map("backed_up") friendlyName String @default("") @map("friendly_name") - createdAt Timestamptz @default(now()) @map("created_at") - updatedAt Timestamptz @default(now()) @map("updated_at") - lastUsedAt Timestamptz? @map("last_used_at") + createdAt TimestamptzTemporal @default(now()) @map("created_at") + updatedAt TimestamptzTemporal @default(now()) @map("updated_at") + lastUsedAt TimestamptzTemporal? @map("last_used_at") user AuthUser @relation(fields: [userId], references: [id], onDelete: Cascade, map: "webauthn_credentials_user_id_fkey") @@index([credentialId], map: "webauthn_credentials_credential_id_key", unique: true) @@ -588,9 +588,9 @@ namespace storage { name String _type pg.enum(Buckettype) @default("ANALYTICS") @map("type") format String @default("ICEBERG") - createdAt Timestamptz @default(now()) @map("created_at") - updatedAt Timestamptz @default(now()) @map("updated_at") - deletedAt Timestamptz? @map("deleted_at") + createdAt TimestamptzTemporal @default(now()) @map("created_at") + updatedAt TimestamptzTemporal @default(now()) @map("updated_at") + deletedAt TimestamptzTemporal? @map("deleted_at") icebergNamespaces IcebergNamespaces[] icebergTables IcebergTables[] @@ -602,8 +602,8 @@ namespace storage { model BucketsVectors { id String @id(map: "buckets_vectors_pkey") _type pg.enum(Buckettype) @default("VECTOR") @map("type") - createdAt Timestamptz @default(now()) @map("created_at") - updatedAt Timestamptz @default(now()) @map("updated_at") + createdAt TimestamptzTemporal @default(now()) @map("created_at") + updatedAt TimestamptzTemporal @default(now()) @map("updated_at") vectorIndexes VectorIndexes[] @@rls @@ -614,8 +614,8 @@ namespace storage { id Id @id(map: "iceberg_namespaces_pkey") @default(sql`gen_random_uuid()`) bucketName String @map("bucket_name") name String - createdAt Timestamptz @default(now()) @map("created_at") - updatedAt Timestamptz @default(now()) @map("updated_at") + createdAt TimestamptzTemporal @default(now()) @map("created_at") + updatedAt TimestamptzTemporal @default(now()) @map("updated_at") metadata Jsonb @default(json`{}`) catalogId Id @map("catalog_id") icebergTables IcebergTables[] @@ -632,8 +632,8 @@ namespace storage { bucketName String @map("bucket_name") name String location String - createdAt Timestamptz @default(now()) @map("created_at") - updatedAt Timestamptz @default(now()) @map("updated_at") + createdAt TimestamptzTemporal @default(now()) @map("created_at") + updatedAt TimestamptzTemporal @default(now()) @map("updated_at") remoteTableId String? @map("remote_table_id") shardKey String? @map("shard_key") shardId String? @map("shard_id") @@ -661,8 +661,8 @@ namespace storage { id String @id(map: "buckets_pkey") name String owner Id? - createdAt Timestamptz? @default(now()) @map("created_at") - updatedAt Timestamptz? @default(now()) @map("updated_at") + createdAt TimestamptzTemporal? @default(now()) @map("created_at") + updatedAt TimestamptzTemporal? @default(now()) @map("updated_at") public Boolean? @default(false) avifAutodetection Boolean? @default(false) @map("avif_autodetection") fileSizeLimit BigInt? @map("file_size_limit") @@ -686,7 +686,7 @@ namespace storage { key String version String ownerId String? @map("owner_id") - createdAt Timestamptz @default(now()) @map("created_at") + createdAt TimestamptzTemporal @default(now()) @map("created_at") userMetadata Jsonb? @map("user_metadata") metadata Jsonb? s3MultipartUploadsParts S3MultipartUploadsParts[] @@ -707,7 +707,7 @@ namespace storage { etag String ownerId String? @map("owner_id") version String - createdAt Timestamptz @default(now()) @map("created_at") + createdAt TimestamptzTemporal @default(now()) @map("created_at") bucket StorageBucket @relation(fields: [bucketId], references: [id], map: "s3_multipart_uploads_parts_bucket_id_fkey", index: false) upload S3MultipartUploads @relation(fields: [uploadId], references: [id], onDelete: Cascade, map: "s3_multipart_uploads_parts_upload_id_fkey", index: false) @@ -720,9 +720,9 @@ namespace storage { bucketId String? @map("bucket_id") name String? owner Id? - createdAt Timestamptz? @default(now()) @map("created_at") - updatedAt Timestamptz? @default(now()) @map("updated_at") - lastAccessedAt Timestamptz? @default(now()) @map("last_accessed_at") + createdAt TimestamptzTemporal? @default(now()) @map("created_at") + updatedAt TimestamptzTemporal? @default(now()) @map("updated_at") + lastAccessedAt TimestamptzTemporal? @default(now()) @map("last_accessed_at") metadata Jsonb? pathTokens String?[]? @map("path_tokens") version String? @@ -746,8 +746,8 @@ namespace storage { dimension Int distanceMetric String @map("distance_metric") metadataConfiguration Jsonb? @map("metadata_configuration") - createdAt Timestamptz @default(now()) @map("created_at") - updatedAt Timestamptz @default(now()) @map("updated_at") + createdAt TimestamptzTemporal @default(now()) @map("created_at") + updatedAt TimestamptzTemporal @default(now()) @map("updated_at") bucket BucketsVectors @relation(fields: [bucketId], references: [id], map: "vector_indexes_bucket_id_fkey", index: false) @@index([name, bucketId], map: "vector_indexes_name_bucket_id_idx", unique: true) diff --git a/packages/3-extensions/supabase/src/contract/handles.ts b/packages/3-extensions/supabase/src/contract/handles.ts index 94e55f46e742..f967a2d9c9ac 100644 --- a/packages/3-extensions/supabase/src/contract/handles.ts +++ b/packages/3-extensions/supabase/src/contract/handles.ts @@ -14,8 +14,7 @@ import { extensionModel, field } from '@internal/sql-contract-ts/contract-builde const pgText = { codecId: 'pg/text@1' } as const; // Supabase's `auth` tables are read by application code, so these carry the Temporal -// representation — the same choice a user authoring the column themselves would get from a bare -// `Timestamptz`. +// representation, the codec of the explicit type `TimestamptzTemporal`. const pgTimestamptz = { codecId: 'pg/timestamptz-temporal@1', } as const; diff --git a/packages/3-extensions/supabase/test/fixtures/example-app/contract.d.ts b/packages/3-extensions/supabase/test/fixtures/example-app/contract.d.ts index 4967bea29906..91322a2e45c0 100644 --- a/packages/3-extensions/supabase/test/fixtures/example-app/contract.d.ts +++ b/packages/3-extensions/supabase/test/fixtures/example-app/contract.d.ts @@ -42,6 +42,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; @@ -91,6 +95,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; @@ -100,6 +108,14 @@ export type AggregateTypes = { readonly output: 'pg/time-temporal@1'; readonly nullable: true; }; + readonly 'pg/timestamp-date@1': { + readonly output: 'pg/timestamp-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-instant@1': { + readonly output: 'pg/timestamp-instant@1'; + readonly nullable: true; + }; readonly 'pg/timestamp-string@1': { readonly output: 'pg/timestamp-string@1'; readonly nullable: true; @@ -151,6 +167,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; @@ -160,6 +180,14 @@ export type AggregateTypes = { readonly output: 'pg/time-temporal@1'; readonly nullable: true; }; + readonly 'pg/timestamp-date@1': { + readonly output: 'pg/timestamp-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-instant@1': { + readonly output: 'pg/timestamp-instant@1'; + readonly nullable: true; + }; readonly 'pg/timestamp-string@1': { readonly output: 'pg/timestamp-string@1'; readonly nullable: true; @@ -203,6 +231,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; diff --git a/packages/3-extensions/supabase/test/fixtures/no-policy/contract.d.ts b/packages/3-extensions/supabase/test/fixtures/no-policy/contract.d.ts index af6ef82cb978..92e31d79f031 100644 --- a/packages/3-extensions/supabase/test/fixtures/no-policy/contract.d.ts +++ b/packages/3-extensions/supabase/test/fixtures/no-policy/contract.d.ts @@ -42,6 +42,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; @@ -91,6 +95,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; @@ -100,6 +108,14 @@ export type AggregateTypes = { readonly output: 'pg/time-temporal@1'; readonly nullable: true; }; + readonly 'pg/timestamp-date@1': { + readonly output: 'pg/timestamp-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-instant@1': { + readonly output: 'pg/timestamp-instant@1'; + readonly nullable: true; + }; readonly 'pg/timestamp-string@1': { readonly output: 'pg/timestamp-string@1'; readonly nullable: true; @@ -151,6 +167,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; @@ -160,6 +180,14 @@ export type AggregateTypes = { readonly output: 'pg/time-temporal@1'; readonly nullable: true; }; + readonly 'pg/timestamp-date@1': { + readonly output: 'pg/timestamp-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-instant@1': { + readonly output: 'pg/timestamp-instant@1'; + readonly nullable: true; + }; readonly 'pg/timestamp-string@1': { readonly output: 'pg/timestamp-string@1'; readonly nullable: true; @@ -203,6 +231,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; diff --git a/packages/3-extensions/supabase/test/fixtures/renamed-policy/contract.d.ts b/packages/3-extensions/supabase/test/fixtures/renamed-policy/contract.d.ts index 09c83cd26212..92aae61c4c73 100644 --- a/packages/3-extensions/supabase/test/fixtures/renamed-policy/contract.d.ts +++ b/packages/3-extensions/supabase/test/fixtures/renamed-policy/contract.d.ts @@ -42,6 +42,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; @@ -91,6 +95,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; @@ -100,6 +108,14 @@ export type AggregateTypes = { readonly output: 'pg/time-temporal@1'; readonly nullable: true; }; + readonly 'pg/timestamp-date@1': { + readonly output: 'pg/timestamp-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-instant@1': { + readonly output: 'pg/timestamp-instant@1'; + readonly nullable: true; + }; readonly 'pg/timestamp-string@1': { readonly output: 'pg/timestamp-string@1'; readonly nullable: true; @@ -151,6 +167,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; @@ -160,6 +180,14 @@ export type AggregateTypes = { readonly output: 'pg/time-temporal@1'; readonly nullable: true; }; + readonly 'pg/timestamp-date@1': { + readonly output: 'pg/timestamp-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-instant@1': { + readonly output: 'pg/timestamp-instant@1'; + readonly nullable: true; + }; readonly 'pg/timestamp-string@1': { readonly output: 'pg/timestamp-string@1'; readonly nullable: true; @@ -203,6 +231,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; diff --git a/packages/3-targets/postgres/1-target/README.md b/packages/3-targets/postgres/1-target/README.md index f347383d31a7..1c13ae89e6d9 100644 --- a/packages/3-targets/postgres/1-target/README.md +++ b/packages/3-targets/postgres/1-target/README.md @@ -26,9 +26,9 @@ Provides the Postgres target descriptor (`SqlControlTargetDescriptor`) for CLI c - **Generated Defaults Policy**: Treats client-generated defaults as non-DB defaults when emitting DDL - **Database Dependency Consumption**: The planner extracts database dependencies from the configured framework components (passed as `frameworkComponents`), verifies each dependency against the live schema, and only emits install operations when required. The runner reuses the same metadata for post-apply verification, so there are no hardcoded extension mappings—database dependencies stay component-owned. - **Storage Type Planning**: The planner dispatches storage type hooks for `storage.types` and emits type operations before table creation when supported by the policy -- **Contract-to-PSL Printing**: Implements the `buildPslContract` descriptor hook, which `prisma contract print` uses. It takes a Postgres contract the contract serializer accepted, from any source, and returns the Prisma 8 PSL document that reads back as the same contract. It does not check the contract's structure again; a broken structure is an internal error. It writes models, value objects, named types, domain and native enums, keys with their names, indexes, checks, relations, polymorphism, control policies, row-level security (`@@rls`, policies, and roles in `namespace unbound`) and defaults. A column is written as a type constructor the configured stack contributes that reads back with its codec, native type and type parameters: the constructor marked `inferred` for the column's data type, which is the one `contract infer` writes, when it does, or else the first constructor in the stack that does, such as `DateTime` or `Timestamptz(3)` for a `Temporal`-backed `timestamptz` column, or an extension's `pgvector.Vector(3)`. A value-object member has no column, so its type goes through the stack the same way: the stack's codec names the native type for the member's type parameters, and the member is written as the type constructor for that native type, called with those parameters (`Numeric(10, 2)`); a member typed by a domain enum is written by the enum's name. The contract does not record which named type a member used, so a member written `code Short`, with `types { Short = VarChar(10) }`, is printed as the type it stands for, `code VarChar(10)`; a model field keeps `Short`, because its column names the named type. A literal default is written by the data type of the column's codec. The family passes the stack's type constructors, codecs and data types to the hook for this. Anything it cannot write so that it reads back the same is refused by name with `CONTRACT.PRINT_UNSUPPORTED` (the full list is in `docs/reference/error-reference.md`, one function per case in `src/core/psl-print/refusals.ts`); nothing is dropped. It lives in `src/core/psl-print/`, the counterpart of the `inferPslContract` hook in `src/core/psl-infer/`, which writes PSL from a live database. Both build with the shared builders in `src/core/psl-build/`: literals, index and check attributes, and the native enum block. Both find the constructor marked `inferred` for a data type through the SQL family's `@internal/family-sql/psl-build`. Model and field names come from the contract; `@@map` and `@map` appear only where the name the PSL source would derive differs. A unique constraint prints as `@@unique` and a unique index as `@@index(..., unique: true)`; every index prints explicitly, and a relation writes `index: false` when nothing backs its foreign key, no `index` argument when a relation that says nothing would be backed by the same object (`defaultForeignKeyIndex`), and otherwise `index: ""` naming the index, or the key whose first columns are the foreign key's. Execution generators join back onto their columns, through whatever the configured stack contributes, extensions included. A column that is not a list and whose PSL type stands for its data type prints as the call of the field preset that writes its codec with its generators, built from the preset's print form, when the preset accepts that call and it reads back with the column's codec, type params and generators. A column typed by a named type, a value object or an enum, and a column whose call fails those checks, such as one without a value for a required argument, does not. A column with only a create generator then prints it as `@default((...))`, the call of the default function that declares `printArguments` for it; any other column is refused. +- **Contract-to-PSL Printing**: Implements the `buildPslContract` descriptor hook, which `prisma contract print` uses. It takes a Postgres contract the contract serializer accepted, from any source, and returns the Prisma 8 PSL document that reads back as the same contract. It does not check the contract's structure again; a broken structure is an internal error. It writes models, value objects, named types, domain and native enums, keys with their names, indexes, checks, relations, polymorphism, control policies, row-level security (`@@rls`, policies, and roles in `namespace unbound`) and defaults. A column is written as a type constructor the configured stack contributes that reads back with its codec, native type and type parameters: the constructor marked `inferred` for the column's data type, which is the one `contract infer` writes, when it does, or else the first constructor in the stack that does, such as `TimestamptzTemporal` or `TimestamptzTemporal(3)` for a `Temporal`-backed `timestamptz` column, because the target lists each explicit type before a second name for it such as `DateTime`, or an extension's `pgvector.Vector(3)`. A value-object member has no column, so its type goes through the stack the same way: the stack's codec names the native type for the member's type parameters, and the member is written as the type constructor for that native type, called with those parameters (`Numeric(10, 2)`); a member typed by a domain enum is written by the enum's name. The contract does not record which named type a member used, so a member written `code Short`, with `types { Short = VarChar(10) }`, is printed as the type it stands for, `code VarChar(10)`; a model field keeps `Short`, because its column names the named type. A literal default is written by the data type of the column's codec. The family passes the stack's type constructors, codecs and data types to the hook for this. Anything it cannot write so that it reads back the same is refused by name with `CONTRACT.PRINT_UNSUPPORTED` (the full list is in `docs/reference/error-reference.md`, one function per case in `src/core/psl-print/refusals.ts`); nothing is dropped. It lives in `src/core/psl-print/`, the counterpart of the `inferPslContract` hook in `src/core/psl-infer/`, which writes PSL from a live database. Both build with the shared builders in `src/core/psl-build/`: literals, index and check attributes, and the native enum block. Both find the constructor marked `inferred` for a data type through the SQL family's `@internal/family-sql/psl-build`. Model and field names come from the contract; `@@map` and `@map` appear only where the name the PSL source would derive differs. A unique constraint prints as `@@unique` and a unique index as `@@index(..., unique: true)`; every index prints explicitly, and a relation writes `index: false` when nothing backs its foreign key, no `index` argument when a relation that says nothing would be backed by the same object (`defaultForeignKeyIndex`), and otherwise `index: ""` naming the index, or the key whose first columns are the foreign key's. Execution generators join back onto their columns, through whatever the configured stack contributes, extensions included. A column that is not a list and whose PSL type stands for its data type prints as the call of the field preset that writes its codec with its generators, built from the preset's print form, when the preset accepts that call and it reads back with the column's codec, type params and generators. A column typed by a named type, a value object or an enum, and a column whose call fails those checks, such as one without a value for a required argument, does not. A column with only a create generator then prints it as `@default((...))`, the call of the default function that declares `printArguments` for it; any other column is refused. - **Database-to-PSL inference**: Implements the `inferPslContract` descriptor hook, which `prisma contract infer` uses. Introspection records each column's element type as the database reports it (`format_type`, its kind, schema and name). Infer resolves that reported type through the data types of the configured stack, the target's and the extension packs', and writes the constructor marked `inferred` for the data type, with the fewest arguments that give the same parameters (`numeric(10,0)` is written `Numeric(10)`); a list column adds `[]`, and a native enum column is written `pg.enum()`. When a column's type is claimed by no data type, or by one with no marked constructor (such as `tsquery`), infer writes nothing and fails with `CONTRACT.INFER_COLUMN_TYPE_UNCLAIMED`, listing every such column as `.
.: `. No configured pack has a type for such a column. When the type comes from a database extension that has a Prisma 8 pack, such as `pgvector` or `postgis`, adding the pack to `extensions` in `prisma.config.ts` lets infer write it, for the forms of the type the pack supports. Otherwise, as for domains, `"char"`, `hstore` and `tsquery`, the column's type must change, or the column must go. The rule itself (resolving a reported type through the data types, the fewest-arguments call, the unclaimed error) lives in the SQL family's `@internal/family-sql/psl-build`; the target adds the walk over its schema tree and the native enum handling in `src/core/psl-infer/`. -- **`Temporal` for the target's own code**: The `Temporal`-backed codecs (`pg/date-temporal@1`, `pg/time-temporal@1`, `pg/timestamp-temporal@1` and `pg/timestamptz-temporal@1`), when they decode, and the `instantNow`, `plainDateTimeNow`, `plainDateNow` and `plainTimeNow` generators get `Temporal` from `requireTemporal()` (`src/core/require-temporal.ts`): the runtime's global `Temporal` when it has one, else the fallback `Temporal`, else it throws `RUNTIME.TEMPORAL_UNAVAILABLE`. The text codecs (`pg/date-string@1`, `pg/time-string@1`, `pg/timestamp-string@1`, `pg/timestamptz-string@1` and `pg/timetz@1`), `pg/timestamptz-date@1`, the `timestampNow` generator and the string generators (`timestampStringNow`, `timestamptzStringNow`, `dateStringNow`, `timeStringNow` and `timetzNow`) do not use `Temporal`, so an application that uses only them reads and writes its date and time columns without one. The target's control entry (`@internal/target-postgres/control`) sets the fallback `Temporal`, from `temporal-polyfill`, when it is loaded. Nothing sets `globalThis.Temporal`. No other entry of this package imports `temporal-polyfill`. `temporal-polyfill` is a required peer dependency of this package and of the published `@prisma/orm-target-postgres` and `@prisma/orm-postgres`, so a project that already installs it for its own code has one copy. npm, pnpm and bun install it automatically; a project that uses Yarn adds `temporal-polyfill` to its own dependencies. In the published package, `@prisma/orm-target-postgres/target` is the aggregate of every entry of this package, the control entry included, so it loads the control entry too; an application imports the entry it needs, for example `@prisma/orm-target-postgres/target/runtime`. The fallback is held once per process, and every `Temporal`-backed codec in the process uses it. An application process that loads no control-plane code and has no global `Temporal` gets `RUNTIME.TEMPORAL_UNAVAILABLE` when one of those codecs decodes or one of those generators runs. An application that loads control-plane code in its own process, such as server code under `vite dev` with the Prisma Vite plugin or a script that calls the control client, decodes dates without its own `Temporal`, and then fails in production. An application that uses the Temporal codecs must load its own `Temporal`. +- **`Temporal` for the target's own code**: The `Temporal`-backed codecs (`pg/date-temporal@1`, `pg/time-temporal@1`, `pg/timestamp-temporal@1`, `pg/timestamp-instant@1` and `pg/timestamptz-temporal@1`), when they decode, and the `instantNow`, `plainDateTimeNow`, `plainDateNow` and `plainTimeNow` generators get `Temporal` from `requireTemporal()` (`src/core/require-temporal.ts`): the runtime's global `Temporal` when it has one, else the fallback `Temporal`, else it throws `RUNTIME.TEMPORAL_UNAVAILABLE`. The text codecs (`pg/date-string@1`, `pg/time-string@1`, `pg/timestamp-string@1`, `pg/timestamptz-string@1` and `pg/timetz@1`), `pg/timestamptz-date@1`, `pg/timestamp-date@1`, `pg/interval@1`, `pg/interval-string@1`, the `timestampNow` generator and the string generators (`timestampStringNow`, `timestamptzStringNow`, `dateStringNow`, `timeStringNow` and `timetzNow`) do not use `Temporal`, so an application that uses only them reads and writes its date and time columns without one. The target's control entry (`@internal/target-postgres/control`) sets the fallback `Temporal`, from `temporal-polyfill`, when it is loaded. Nothing sets `globalThis.Temporal`. No other entry of this package imports `temporal-polyfill`. `temporal-polyfill` is a required peer dependency of this package and of the published `@prisma/orm-target-postgres` and `@prisma/orm-postgres`, so a project that already installs it for its own code has one copy. npm, pnpm and bun install it automatically; a project that uses Yarn adds `temporal-polyfill` to its own dependencies. In the published package, `@prisma/orm-target-postgres/target` is the aggregate of every entry of this package, the control entry included, so it loads the control entry too; an application imports the entry it needs, for example `@prisma/orm-target-postgres/target/runtime`. The fallback is held once per process, and every `Temporal`-backed codec in the process uses it. An application process that loads no control-plane code and has no global `Temporal` gets `RUNTIME.TEMPORAL_UNAVAILABLE` when one of those codecs decodes or one of those generators runs. An application that loads control-plane code in its own process, such as server code under `vite dev` with the Prisma Vite plugin or a script that calls the control client, decodes dates without its own `Temporal`, and then fails in production. An application that uses the Temporal codecs must load its own `Temporal`. Every signature that names a `Temporal` type takes it from `temporal-spec`, a package of type declarations with no runtime code (`import type { Temporal } from 'temporal-spec'`), never from a global declaration, so a Temporal field is typed precisely in a project that has no global `Temporal` declarations. `tsconfig.no-temporal-global.json` type-checks `src` without them. - **Runtime List Framing**: Parses inbound Postgres array text for contract-declared list columns before applying the scalar element codec. Builtin arrays and enum arrays therefore share one target-owned decode path; fixed-scale `numeric(30,10)[]` reads database-normalized text such as `"1.5000000000"`, matching scalar numeric decoding rather than the previous driver numeric-array float spelling `"1.5"`. This package spans multiple planes: diff --git a/packages/3-targets/postgres/1-target/package.json b/packages/3-targets/postgres/1-target/package.json index e40b3a403eb6..9ee4e4aac153 100644 --- a/packages/3-targets/postgres/1-target/package.json +++ b/packages/3-targets/postgres/1-target/package.json @@ -9,7 +9,7 @@ "scripts": { "build": "tsdown", "test": "vitest run --passWithNoTests", - "typecheck": "tsc --project tsconfig.json --noEmit", + "typecheck": "tsc --project tsconfig.json --noEmit && tsc --project tsconfig.no-temporal-global.json --noEmit", "lint": "biome check . --error-on-warnings", "lint:fix": "biome check --write .", "lint:fix:unsafe": "biome check --write --unsafe .", @@ -34,7 +34,8 @@ "@standard-schema/spec": "^1.1.0", "arktype": "^2.2.2", "pathe": "^2.0.3", - "postgres-array": "2.0.0" + "postgres-array": "2.0.0", + "temporal-spec": "1.0.1" }, "devDependencies": { "@electric-sql/pglite": "0.5.4", diff --git a/packages/3-targets/postgres/1-target/src/core/aggregates.ts b/packages/3-targets/postgres/1-target/src/core/aggregates.ts index 1f9d4ccf4c8e..3529b3494cba 100644 --- a/packages/3-targets/postgres/1-target/src/core/aggregates.ts +++ b/packages/3-targets/postgres/1-target/src/core/aggregates.ts @@ -28,11 +28,14 @@ import { PG_INT8_CODEC_ID, PG_INT8_NUMBER_CODEC_ID, PG_INTERVAL_CODEC_ID, + PG_INTERVAL_STRING_CODEC_ID, PG_NUMERIC_CODEC_ID, PG_TEXT_ARRAY_CODEC_ID, PG_TEXT_CODEC_ID, PG_TIME_STRING_CODEC_ID, PG_TIME_TEMPORAL_CODEC_ID, + PG_TIMESTAMP_DATE_CODEC_ID, + PG_TIMESTAMP_INSTANT_CODEC_ID, PG_TIMESTAMP_STRING_CODEC_ID, PG_TIMESTAMP_TEMPORAL_CODEC_ID, PG_TIMESTAMPTZ_DATE_CODEC_ID, @@ -137,12 +140,15 @@ const MIN_MAX_PRESERVING_CODECS = [ PG_ENUM_CODEC_ID, PG_TIMETZ_CODEC_ID, PG_INTERVAL_CODEC_ID, + PG_INTERVAL_STRING_CODEC_ID, PG_INET_CODEC_ID, PG_TEXT_ARRAY_CODEC_ID, PG_DATE_STRING_CODEC_ID, PG_TIMESTAMP_STRING_CODEC_ID, PG_TIMESTAMPTZ_STRING_CODEC_ID, PG_TIMESTAMPTZ_DATE_CODEC_ID, + PG_TIMESTAMP_DATE_CODEC_ID, + PG_TIMESTAMP_INSTANT_CODEC_ID, PG_TIME_STRING_CODEC_ID, PG_DATE_TEMPORAL_CODEC_ID, PG_TIMESTAMP_TEMPORAL_CODEC_ID, @@ -219,6 +225,11 @@ export const postgresAggregateDescriptors = [ // `sum` over the unbounded integer keeps its codec: the expression's SQL type is `numeric`, and a sum of integral values is integral, so the codec's integrality-checked `bigint` decode is the right reader for the total. produces('sum', { kind: 'codec', codecId: PG_UNBOUNDED_INT_CODEC_ID }, PG_UNBOUNDED_INT_CODEC_ID), produces('sum', { kind: 'codec', codecId: PG_INTERVAL_CODEC_ID }, PG_INTERVAL_CODEC_ID), + produces( + 'sum', + { kind: 'codec', codecId: PG_INTERVAL_STRING_CODEC_ID }, + PG_INTERVAL_STRING_CODEC_ID, + ), produces('sum', { kind: 'codec', codecId: PG_TIME_STRING_CODEC_ID }, PG_INTERVAL_CODEC_ID), produces('sum', { kind: 'codec', codecId: PG_TIME_TEMPORAL_CODEC_ID }, PG_INTERVAL_CODEC_ID), @@ -234,6 +245,11 @@ export const postgresAggregateDescriptors = [ ), produces('avg', { kind: 'codec', codecId: PG_NUMERIC_CODEC_ID }, PG_NUMERIC_CODEC_ID), produces('avg', { kind: 'codec', codecId: PG_INTERVAL_CODEC_ID }, PG_INTERVAL_CODEC_ID), + produces( + 'avg', + { kind: 'codec', codecId: PG_INTERVAL_STRING_CODEC_ID }, + PG_INTERVAL_STRING_CODEC_ID, + ), produces('avg', { kind: 'codec', codecId: PG_TIME_STRING_CODEC_ID }, PG_INTERVAL_CODEC_ID), produces('avg', { kind: 'codec', codecId: PG_TIME_TEMPORAL_CODEC_ID }, PG_INTERVAL_CODEC_ID), diff --git a/packages/3-targets/postgres/1-target/src/core/codec-ids.ts b/packages/3-targets/postgres/1-target/src/core/codec-ids.ts index a2a83be332a2..bf17f286ce8d 100644 --- a/packages/3-targets/postgres/1-target/src/core/codec-ids.ts +++ b/packages/3-targets/postgres/1-target/src/core/codec-ids.ts @@ -32,9 +32,12 @@ export const PG_DATE_STRING_CODEC_ID = 'pg/date-string@1' as const; export const PG_TIMESTAMP_STRING_CODEC_ID = 'pg/timestamp-string@1' as const; export const PG_TIMESTAMPTZ_STRING_CODEC_ID = 'pg/timestamptz-string@1' as const; export const PG_TIMESTAMPTZ_DATE_CODEC_ID = 'pg/timestamptz-date@1' as const; +export const PG_TIMESTAMP_DATE_CODEC_ID = 'pg/timestamp-date@1' as const; +export const PG_TIMESTAMP_INSTANT_CODEC_ID = 'pg/timestamp-instant@1' as const; export const PG_TIME_STRING_CODEC_ID = 'pg/time-string@1' as const; export const PG_TIMETZ_CODEC_ID = 'pg/timetz@1' as const; export const PG_INTERVAL_CODEC_ID = 'pg/interval@1' as const; +export const PG_INTERVAL_STRING_CODEC_ID = 'pg/interval-string@1' as const; export const PG_JSON_CODEC_ID = 'pg/json@1' as const; export const PG_JSONB_CODEC_ID = 'pg/jsonb@1' as const; export const PG_BYTEA_CODEC_ID = 'pg/bytea@1' as const; diff --git a/packages/3-targets/postgres/1-target/src/core/codec-type-map.ts b/packages/3-targets/postgres/1-target/src/core/codec-type-map.ts index 10b47e32eabe..692d512fd453 100644 --- a/packages/3-targets/postgres/1-target/src/core/codec-type-map.ts +++ b/packages/3-targets/postgres/1-target/src/core/codec-type-map.ts @@ -41,9 +41,11 @@ import { postgresSqlTextDescriptor, postgresSqlVarcharDescriptor, } from './codecs'; -import { pgTimestamptzDateDescriptor } from './date-codecs'; +import { pgTimestampDateDescriptor, pgTimestamptzDateDescriptor } from './date-codecs'; +import { pgIntervalStringDescriptor } from './interval-string-codec'; import { pgDateTemporalDescriptor, + pgTimestampInstantDescriptor, pgTimestampTemporalDescriptor, pgTimestamptzTemporalDescriptor, pgTimeTemporalDescriptor, @@ -83,6 +85,8 @@ export const codecDescriptorMap = { 'timestamp-string': pgTimestampStringDescriptor, 'timestamptz-string': pgTimestamptzStringDescriptor, 'timestamptz-date': pgTimestamptzDateDescriptor, + 'timestamp-date': pgTimestampDateDescriptor, + 'timestamp-instant': pgTimestampInstantDescriptor, 'time-string': pgTimeStringDescriptor, timetz: pgTimetzDescriptor, bool: pgBoolDescriptor, @@ -92,6 +96,7 @@ export const codecDescriptorMap = { uuid: pgUuidDescriptor, inet: pgInetDescriptor, interval: pgIntervalDescriptor, + 'interval-string': pgIntervalStringDescriptor, json: pgJsonDescriptor, jsonb: pgJsonbDescriptor, tsquery: pgTsqueryDescriptor, diff --git a/packages/3-targets/postgres/1-target/src/core/codecs.ts b/packages/3-targets/postgres/1-target/src/core/codecs.ts index 5b46bcac1ed7..76b22c30f281 100644 --- a/packages/3-targets/postgres/1-target/src/core/codecs.ts +++ b/packages/3-targets/postgres/1-target/src/core/codecs.ts @@ -135,13 +135,15 @@ import { pgVarbit, pgVarchar, } from './data-types'; -import { pgTimestamptzDateDescriptor } from './date-codecs'; +import { pgTimestampDateDescriptor, pgTimestamptzDateDescriptor } from './date-codecs'; import { postgresError } from './errors'; +import { pgIntervalStringDescriptor } from './interval-string-codec'; import { parsePostgresListText } from './list-decoder'; import { DEFAULT_NAMESPACE_ID } from './namespace-ids'; import { PostgresNativeEnum } from './postgres-native-enum'; import { pgDateTemporalDescriptor, + pgTimestampInstantDescriptor, pgTimestampTemporalDescriptor, pgTimestamptzTemporalDescriptor, pgTimeTemporalDescriptor, @@ -1539,6 +1541,8 @@ export const codecDescriptors = definePostgresCodecs([ pgTimestampStringDescriptor, pgTimestamptzStringDescriptor, pgTimestamptzDateDescriptor, + pgTimestampDateDescriptor, + pgTimestampInstantDescriptor, pgTimeStringDescriptor, pgTimetzDescriptor, pgBoolDescriptor, @@ -1548,6 +1552,7 @@ export const codecDescriptors = definePostgresCodecs([ pgUuidDescriptor, pgInetDescriptor, pgIntervalDescriptor, + pgIntervalStringDescriptor, pgJsonDescriptor, pgJsonbDescriptor, pgTextArrayDescriptor, diff --git a/packages/3-targets/postgres/1-target/src/core/date-codecs.ts b/packages/3-targets/postgres/1-target/src/core/date-codecs.ts index b69c91e27205..0317c589213f 100644 --- a/packages/3-targets/postgres/1-target/src/core/date-codecs.ts +++ b/packages/3-targets/postgres/1-target/src/core/date-codecs.ts @@ -13,16 +13,54 @@ import { isStructuredError } from '@internal/utils/structured-error'; import type { StandardSchemaV1 } from '@standard-schema/spec'; import { PostgresCodecDescriptor } from './codec-descriptor'; import type { PrecisionParams } from './codec-helpers'; -import { PG_TIMESTAMPTZ_DATE_CODEC_ID, PG_TIMESTAMPTZ_STRING_CODEC_ID } from './codec-ids'; -import { pgPrecisionParams, pgTimestamptz, pgTimestamptzStoredText } from './data-types'; +import { + PG_TIMESTAMP_DATE_CODEC_ID, + PG_TIMESTAMP_STRING_CODEC_ID, + PG_TIMESTAMPTZ_DATE_CODEC_ID, + PG_TIMESTAMPTZ_STRING_CODEC_ID, +} from './codec-ids'; +import { + pgPrecisionParams, + pgTimestamp, + pgTimestampStoredText, + pgTimestamptz, + pgTimestamptzStoredText, +} from './data-types'; import { EARLIEST_POSTGRES_TIMESTAMP_MILLISECONDS, + utcTimestampText, utcTimestamptzText, } from './temporal-codec-helpers'; -function invalidDate(): RangeError { +/** What a `Date` codec reads, and the text type that holds what a `Date` cannot. */ +interface DateCodecIdentity { + readonly codecId: string; + readonly typeDescription: string; + readonly stringType: string; + readonly textCodecId: string; + /** The instant, in the stored spelling of `pg/timestamptz`, that text of the codec's data type names. */ + readonly instantText: (text: string) => string; +} + +const TIMESTAMPTZ_DATE: DateCodecIdentity = { + codecId: PG_TIMESTAMPTZ_DATE_CODEC_ID, + typeDescription: 'timestamp with time zone', + stringType: 'TimestamptzString', + textCodecId: PG_TIMESTAMPTZ_STRING_CODEC_ID, + instantText: pgTimestamptzStoredText, +}; + +const TIMESTAMP_DATE: DateCodecIdentity = { + codecId: PG_TIMESTAMP_DATE_CODEC_ID, + typeDescription: 'timestamp, read as UTC', + stringType: 'TimestampString', + textCodecId: PG_TIMESTAMP_STRING_CODEC_ID, + instantText: (text) => pgTimestamptzStoredText(`${pgTimestampStoredText(text)}Z`), +}; + +function invalidDate(identity: DateCodecIdentity): RangeError { return new RangeError( - `${PG_TIMESTAMPTZ_DATE_CODEC_ID} requires a valid Date or a representable ISO PostgreSQL timestamp with time zone; use TimestamptzString for unsupported values`, + `${identity.codecId} requires a valid Date or a representable ISO PostgreSQL ${identity.typeDescription}; use ${identity.stringType} for unsupported values`, ); } @@ -34,47 +72,43 @@ function isRepresentable(value: Date): boolean { ); } -function validateDate(value: Date): Date { - if (!isRepresentable(value)) throw invalidDate(); +function validateDate(identity: DateCodecIdentity, value: Date): Date { + if (!isRepresentable(value)) throw invalidDate(identity); return value; } -function decodeDate(wire: unknown): Date { - const instant = typeof wire === 'string' ? storedInstant(wire) : undefined; - const date = instant === undefined ? undefined : new Date(instant); - if (date === undefined || !isRepresentable(date)) throw invalidDate(); - return date; -} - -/** The instant, in its stored spelling, a stored timestamp with time zone names, or `undefined` for `infinity` and an instant outside the range a `Date` holds. */ -function storedInstant(text: string): string | undefined { +/** The instant a stored or printed value names, or `undefined` for `infinity` and a value outside the range a `Date` holds. */ +function storedInstant(identity: DateCodecIdentity, text: string): string | undefined { if (text === 'infinity' || text === '-infinity') return undefined; try { - return pgTimestamptzStoredText(text); + return identity.instantText(text); } catch (error) { if (isStructuredError(error) && error.code === 'CONTRACT.CAST_REFUSED') return undefined; throw error; } } -/** The `Date` a stored timestamp with time zone names. A `Date` holds neither `infinity` nor an instant before 4714-11-24 BC or after +275760-09-13, so those are refused. */ -function dateOfStoredText(codecId: string, text: string): Date { - const instant = storedInstant(text); +function decodeDate(identity: DateCodecIdentity, wire: unknown): Date { + const instant = typeof wire === 'string' ? storedInstant(identity, wire) : undefined; + const date = instant === undefined ? undefined : new Date(instant); + if (date === undefined || !isRepresentable(date)) throw invalidDate(identity); + return date; +} + +/** The `Date` a stored value names. A `Date` holds neither `infinity` nor an instant before 4714-11-24 BC or after +275760-09-13, so those are refused. */ +function dateOfStoredText(identity: DateCodecIdentity, text: string): Date { + const instant = storedInstant(identity, text); const date = instant === undefined ? undefined : new Date(instant); if (date === undefined || !isRepresentable(date)) { return refuseJsonValue( - codecId, - `an instant a Date holds, from 4714-11-24 BC to +275760-09-13 and not infinity or -infinity, which ${PG_TIMESTAMPTZ_STRING_CODEC_ID} holds`, + identity.codecId, + `an instant a Date holds, from 4714-11-24 BC to +275760-09-13 and not infinity or -infinity, which ${identity.textCodecId} holds`, text, ); } return date; } -function encodeDate(value: Date): string { - return utcTimestamptzText(validateDate(value), PG_TIMESTAMPTZ_DATE_CODEC_ID); -} - export class PgTimestamptzDateCodec extends CodecImpl< typeof PG_TIMESTAMPTZ_DATE_CODEC_ID, readonly ['equality', 'order'], @@ -82,16 +116,16 @@ export class PgTimestamptzDateCodec extends CodecImpl< Date > { async toWire(value: Date, _ctx: CodecCallContext): Promise { - return encodeDate(value); + return utcTimestamptzText(validateDate(TIMESTAMPTZ_DATE, value), this.id); } async fromWire(wire: string, _ctx: CodecCallContext): Promise { - return decodeDate(wire); + return decodeDate(TIMESTAMPTZ_DATE, wire); } fromDataTypeValue(value: DataTypeValue): Date { - return dateOfStoredText(this.id, value.value); + return dateOfStoredText(TIMESTAMPTZ_DATE, value.value); } toDataTypeValue(input: Date): DataTypeValue { - return this.dataTypeValueOf(validateDate(input).toISOString()); + return this.dataTypeValueOf(validateDate(TIMESTAMPTZ_DATE, input).toISOString()); } } @@ -115,3 +149,47 @@ export const pgTimestamptzDateColumn = (params: PrecisionParams = {}) => pgTimestamptzDateColumn satisfies ColumnHelperFor; pgTimestamptzDateColumn satisfies ColumnHelperForStrict; + +/** + * A `timestamp` column read and written as a JavaScript `Date`, treating the stored date and time as UTC. A read appends `Z` to the stored value; a write sends the `Date`'s UTC date and time with no offset, so a written value reads back unchanged whatever the server's, the session's or the process's time zone. PostgreSQL fills a database default `now()` in the session's time zone, so under a `TimeZone` other than UTC that value reads back shifted; `@onCreate(now())` writes UTC from the client. + */ +export class PgTimestampDateCodec extends CodecImpl< + typeof PG_TIMESTAMP_DATE_CODEC_ID, + readonly ['equality', 'order'], + string, + Date +> { + async toWire(value: Date, _ctx: CodecCallContext): Promise { + return utcTimestampText(validateDate(TIMESTAMP_DATE, value), this.id); + } + async fromWire(wire: string, _ctx: CodecCallContext): Promise { + return decodeDate(TIMESTAMP_DATE, wire); + } + fromDataTypeValue(value: DataTypeValue): Date { + return dateOfStoredText(TIMESTAMP_DATE, value.value); + } + toDataTypeValue(input: Date): DataTypeValue { + return this.dataTypeValueOf(utcTimestampText(validateDate(TIMESTAMP_DATE, input), this.id)); + } +} + +export class PgTimestampDateDescriptor extends PostgresCodecDescriptor { + protected override jsonProjection(expression: ProjectionExpr): ProjectionExpr { + return CastExpr.as(expression, 'text'); + } + override readonly dataType = pgTimestamp.id; + override readonly codecId = PG_TIMESTAMP_DATE_CODEC_ID; + override readonly traits = ['equality', 'order'] as const; + override readonly paramsSchema = pgPrecisionParams satisfies StandardSchemaV1; + override factory(params: PrecisionParams): (ctx: CodecInstanceContext) => PgTimestampDateCodec { + return () => new PgTimestampDateCodec(this, pgTimestamp, params ?? {}); + } +} + +export const pgTimestampDateDescriptor = new PgTimestampDateDescriptor(); + +export const pgTimestampDateColumn = (params: PrecisionParams = {}) => + column(pgTimestampDateDescriptor.factory(params), pgTimestampDateDescriptor.codecId, params); + +pgTimestampDateColumn satisfies ColumnHelperFor; +pgTimestampDateColumn satisfies ColumnHelperForStrict; diff --git a/packages/3-targets/postgres/1-target/src/core/date-time-now-generators.ts b/packages/3-targets/postgres/1-target/src/core/date-time-now-generators.ts index f4bc162001b8..87b1aaa79da8 100644 --- a/packages/3-targets/postgres/1-target/src/core/date-time-now-generators.ts +++ b/packages/3-targets/postgres/1-target/src/core/date-time-now-generators.ts @@ -1,4 +1,5 @@ import type { MutationDefaultGeneratorDescriptor } from '@internal/framework-components/control'; +import type { Temporal } from 'temporal-spec'; import { requireTemporal } from './require-temporal'; /** diff --git a/packages/3-targets/postgres/1-target/src/core/interval-string-codec.ts b/packages/3-targets/postgres/1-target/src/core/interval-string-codec.ts new file mode 100644 index 000000000000..56bd9c26df92 --- /dev/null +++ b/packages/3-targets/postgres/1-target/src/core/interval-string-codec.ts @@ -0,0 +1,92 @@ +import { + type CodecCallContext, + CodecImpl, + type CodecInstanceContext, + type ColumnHelperFor, + type ColumnHelperForStrict, + column, + type DataTypeValue, +} from '@internal/framework-components/codec'; +import { CastExpr, type ProjectionExpr } from '@internal/sql-relational-core/ast'; +import type { StandardSchemaV1 } from '@standard-schema/spec'; +import { PostgresCodecDescriptor } from './codec-descriptor'; +import { type PrecisionParams, pgIntervalDecode } from './codec-helpers'; +import { PG_INTERVAL_STRING_CODEC_ID } from './codec-ids'; +import { formatIsoDuration, ISO_DURATION, pgIntervalStoredText } from './data-type-values'; +import { pgInterval, pgPrecisionParams } from './data-types'; +import { postgresError } from './errors'; + +const MAX_FRACTION_DIGITS = 6; + +function describeReceived(value: unknown): string { + if (typeof value === 'string') return JSON.stringify(value); + return value === null ? 'null' : `a ${typeof value}`; +} + +function refusedDuration(value: unknown, reason: string): never { + throw postgresError( + 'RUNTIME.ENCODE_FAILED', + `${PG_INTERVAL_STRING_CODEC_ID} writes ${reason}, got ${describeReceived(value)}`, + { meta: { codecId: PG_INTERVAL_STRING_CODEC_ID, received: typeof value } }, + ); +} + +/** The duration `pg/interval` stores for an ISO 8601 duration the application wrote. */ +function storedDuration(value: unknown): string { + if (typeof value !== 'string' || !ISO_DURATION.test(value)) { + return refusedDuration(value, 'an ISO 8601 duration, as in "P1Y2M3DT4H5M6.5S"'); + } + const fractionDigits = /\.(\d+)S$/.exec(value)?.[1]?.length ?? 0; + if (fractionDigits > MAX_FRACTION_DIGITS) { + return refusedDuration( + value, + `a duration with at most ${MAX_FRACTION_DIGITS} digits after the decimal point, because pg/interval holds microseconds`, + ); + } + return pgIntervalStoredText(value); +} + +/** + * An interval read and written as the ISO 8601 duration `pg/interval` stores, such as `P1Y2M3DT4H5M6.5S`. A read gives that form whatever `IntervalStyle` the server prints in. Equal intervals can have different forms, `P1D` and `PT24H`, so the codec keeps the form a value was written in. + */ +export class PgIntervalStringCodec extends CodecImpl< + typeof PG_INTERVAL_STRING_CODEC_ID, + readonly ['equality', 'order'], + string | Record, + string +> { + async toWire(value: string, _ctx: CodecCallContext): Promise { + return storedDuration(value); + } + async fromWire(wire: string | Record, _ctx: CodecCallContext): Promise { + return formatIsoDuration(pgIntervalDecode(wire)); + } + fromDataTypeValue(value: DataTypeValue): string { + return value.value; + } + toDataTypeValue(input: string): DataTypeValue { + return this.dataTypeValueOf(storedDuration(input)); + } +} + +export class PgIntervalStringDescriptor extends PostgresCodecDescriptor { + protected override jsonProjection(expression: ProjectionExpr): ProjectionExpr { + return CastExpr.as(expression, 'text'); + } + override readonly dataType = pgInterval.id; + override readonly codecId = PG_INTERVAL_STRING_CODEC_ID; + override readonly traits = ['equality', 'order'] as const; + override readonly keepsWrittenForm = true as const; + override readonly paramsSchema = pgPrecisionParams satisfies StandardSchemaV1; + override factory(params: PrecisionParams): (ctx: CodecInstanceContext) => PgIntervalStringCodec { + return () => new PgIntervalStringCodec(this, pgInterval, params ?? {}); + } +} + +export const pgIntervalStringDescriptor = new PgIntervalStringDescriptor(); + +export const pgIntervalStringColumn = (params: PrecisionParams = {}) => + column(pgIntervalStringDescriptor.factory(params), pgIntervalStringDescriptor.codecId, params); + +pgIntervalStringColumn satisfies ColumnHelperFor; +pgIntervalStringColumn satisfies ColumnHelperForStrict; diff --git a/packages/3-targets/postgres/1-target/src/core/now-generators.ts b/packages/3-targets/postgres/1-target/src/core/now-generators.ts index 5b31a37b0bd4..964215258c15 100644 --- a/packages/3-targets/postgres/1-target/src/core/now-generators.ts +++ b/packages/3-targets/postgres/1-target/src/core/now-generators.ts @@ -6,6 +6,8 @@ import { PG_DATE_TEMPORAL_CODEC_ID, PG_TIME_STRING_CODEC_ID, PG_TIME_TEMPORAL_CODEC_ID, + PG_TIMESTAMP_DATE_CODEC_ID, + PG_TIMESTAMP_INSTANT_CODEC_ID, PG_TIMESTAMP_STRING_CODEC_ID, PG_TIMESTAMP_TEMPORAL_CODEC_ID, PG_TIMESTAMPTZ_DATE_CODEC_ID, @@ -37,6 +39,8 @@ export const postgresNowGeneratorIds = { [PG_TIMESTAMPTZ_TEMPORAL_CODEC_ID]: INSTANT_NOW_GENERATOR_ID, [PG_TIMESTAMPTZ_STRING_CODEC_ID]: TIMESTAMPTZ_STRING_NOW_GENERATOR_ID, [PG_TIMESTAMPTZ_DATE_CODEC_ID]: TIMESTAMP_NOW_GENERATOR_ID, + [PG_TIMESTAMP_DATE_CODEC_ID]: TIMESTAMP_NOW_GENERATOR_ID, + [PG_TIMESTAMP_INSTANT_CODEC_ID]: INSTANT_NOW_GENERATOR_ID, } as const; const nowGeneratorIdByCodecId: ReadonlyMap = new Map( diff --git a/packages/3-targets/postgres/1-target/src/core/psl-print/column-types.ts b/packages/3-targets/postgres/1-target/src/core/psl-print/column-types.ts index 8f4ae0a2eb0e..b48d005483dd 100644 --- a/packages/3-targets/postgres/1-target/src/core/psl-print/column-types.ts +++ b/packages/3-targets/postgres/1-target/src/core/psl-print/column-types.ts @@ -20,7 +20,7 @@ import { positionalArg, SYNTHETIC_SPAN } from '../psl-build/psl-literals'; import { refuseColumnWithoutPslType, refuseUnwritableTypeArgument } from './refusals'; /** - * What a column's PSL type stands for. `dataType` is a type that stands for the column's codec and type params alone, written as a bare name such as `String` or a call such as `Timestamptz(3)`. A value object or an enum, including a native enum written `pg.enum()`, names something more. + * What a column's PSL type stands for. `dataType` is a type that stands for the column's codec and type params alone, written as a bare name such as `String` or a call such as `TimestamptzTemporal(3)`. A value object or an enum, including a native enum written `pg.enum()`, names something more. */ export type PslColumnTypeKind = 'dataType' | 'valueObject' | 'enum'; diff --git a/packages/3-targets/postgres/1-target/src/core/require-temporal.ts b/packages/3-targets/postgres/1-target/src/core/require-temporal.ts index c751b1217062..330b49ba0fd5 100644 --- a/packages/3-targets/postgres/1-target/src/core/require-temporal.ts +++ b/packages/3-targets/postgres/1-target/src/core/require-temporal.ts @@ -1,3 +1,5 @@ +import { blindCast } from '@internal/utils/casts'; +import type { Temporal } from 'temporal-spec'; import { errorTemporalUnavailable, errorTemporalUnavailableForDefault } from './errors'; export type TemporalImplementation = typeof Temporal; @@ -22,8 +24,12 @@ export function setFallbackTemporal(implementation: TemporalImplementation): voi * `RUNTIME.TEMPORAL_UNAVAILABLE` when there is neither. */ export function requireTemporal(meta: TemporalUnavailableMeta): TemporalImplementation { - if (typeof Temporal !== 'undefined') { - return Temporal; + const runtimeTemporal = blindCast< + { readonly Temporal?: TemporalImplementation }, + 'globalThis holds the runtime Temporal when the runtime has one' + >(globalThis).Temporal; + if (runtimeTemporal !== undefined) { + return runtimeTemporal; } if (fallback !== undefined) { return fallback; diff --git a/packages/3-targets/postgres/1-target/src/core/temporal-codec-helpers.ts b/packages/3-targets/postgres/1-target/src/core/temporal-codec-helpers.ts index 76d90c6e7710..e82e7903b291 100644 --- a/packages/3-targets/postgres/1-target/src/core/temporal-codec-helpers.ts +++ b/packages/3-targets/postgres/1-target/src/core/temporal-codec-helpers.ts @@ -1,10 +1,13 @@ import { refuseJsonValue } from '@internal/framework-components/codec'; +import type { Temporal } from 'temporal-spec'; import { PG_DATE_TEMPORAL_CODEC_ID, PG_TIME_TEMPORAL_CODEC_ID, + PG_TIMESTAMP_INSTANT_CODEC_ID, PG_TIMESTAMP_TEMPORAL_CODEC_ID, PG_TIMESTAMPTZ_TEMPORAL_CODEC_ID, } from './codec-ids'; +import { pgTimestampStoredText } from './data-types'; import { errorTemporalNonIsoCalendar, errorTemporalUnrepresentable, @@ -48,9 +51,15 @@ function decodeTemporalText( } } +interface TemporalValue { + readonly calendarId?: string; + toString: () => string; +} + function encodeTemporalValue( identity: TemporalCodecIdentity, - value: { readonly calendarId?: string; toString: () => string }, + value: TemporalValue, + isoText: (value: TemporalValue) => string = (temporalValue) => temporalValue.toString(), ): string { const tag: unknown = typeof value === 'object' && value !== null @@ -67,7 +76,7 @@ function encodeTemporalValue( if (value.calendarId !== undefined && value.calendarId !== 'iso8601') { throw errorTemporalNonIsoCalendar(identity.codecId, value.calendarId); } - return postgresYearText(value.toString()); + return postgresYearText(isoText(value)); } function describeEncodeInput(value: unknown, tag: unknown): string { @@ -109,6 +118,11 @@ const TIMESTAMPTZ_TEMPORAL: TemporalCodecIdentity = { stringType: 'TimestamptzString(p)', temporalTag: 'Temporal.Instant', }; +const TIMESTAMP_INSTANT: TemporalCodecIdentity = { + codecId: PG_TIMESTAMP_INSTANT_CODEC_ID, + stringType: 'TimestampString(p)', + temporalTag: 'Temporal.Instant', +}; const TIME_TEMPORAL: TemporalCodecIdentity = { codecId: PG_TIME_TEMPORAL_CODEC_ID, stringType: 'TimeString(p)', @@ -135,6 +149,18 @@ export const pgTimestamptzTemporalDecode = (wire: string): Temporal.Instant => export const pgTimestamptzTemporalEncode = (value: Temporal.Instant): string => encodeTemporalValue(TIMESTAMPTZ_TEMPORAL, value); +/** Reads a timestamp without a time zone as the instant it names in UTC. */ +export const pgTimestampInstantDecode = (wire: string): Temporal.Instant => + decodeTemporalText(TIMESTAMP_INSTANT, wire, (temporal, text) => + temporal.Instant.from(`${pgTimestampStoredText(text)}Z`), + ); + +/** Writes an instant as its UTC date and time, with no offset, as a timestamp without a time zone holds it. */ +export const pgTimestampInstantEncode = (value: Temporal.Instant): string => + encodeTemporalValue(TIMESTAMP_INSTANT, value, (instant) => + withoutUtcDesignator(instant.toString()), + ); + export const pgTimeTemporalDecode = (wire: string): Temporal.PlainTime => decodeTemporalText(TIME_TEMPORAL, wire, (temporal, text) => temporal.PlainTime.from(text)); @@ -145,12 +171,26 @@ export const EARLIEST_POSTGRES_TIMESTAMP_MILLISECONDS = new Date( '-004713-11-24T00:00:00Z', ).getTime(); -export function utcTimestamptzText(value: Date, codecId: string): string { +function utcIsoText(value: Date, codecId: string): string { const milliseconds = value.getTime(); if (!Number.isFinite(milliseconds) || milliseconds < EARLIEST_POSTGRES_TIMESTAMP_MILLISECONDS) { throw new RangeError( `${codecId} writes a Date as UTC text and needs a valid Date on or after 4714-11-24 BC, the earliest timestamp PostgreSQL holds; got ${String(value)}`, ); } - return postgresYearText(value.toISOString()); + return value.toISOString(); +} + +export function utcTimestamptzText(value: Date, codecId: string): string { + return postgresYearText(utcIsoText(value, codecId)); +} + +/** The UTC wall-clock time of `value` with no offset, as PostgreSQL reads a timestamp without a time zone. */ +export function utcTimestampText(value: Date, codecId: string): string { + return postgresYearText(withoutUtcDesignator(utcIsoText(value, codecId))); +} + +/** ISO text in UTC without its trailing `Z`. */ +function withoutUtcDesignator(isoText: string): string { + return isoText.endsWith('Z') ? isoText.slice(0, -1) : isoText; } diff --git a/packages/3-targets/postgres/1-target/src/core/temporal-codecs.ts b/packages/3-targets/postgres/1-target/src/core/temporal-codecs.ts index b31277ac827a..dd5fba1d7b96 100644 --- a/packages/3-targets/postgres/1-target/src/core/temporal-codecs.ts +++ b/packages/3-targets/postgres/1-target/src/core/temporal-codecs.ts @@ -9,12 +9,14 @@ import { } from '@internal/framework-components/codec'; import { CastExpr, type ProjectionExpr } from '@internal/sql-relational-core/ast'; import type { StandardSchemaV1 } from '@standard-schema/spec'; +import type { Temporal } from 'temporal-spec'; import { PostgresCodecDescriptor } from './codec-descriptor'; import type { PrecisionParams } from './codec-helpers'; import { PG_DATE_STRING_CODEC_ID, PG_DATE_TEMPORAL_CODEC_ID, PG_TIME_TEMPORAL_CODEC_ID, + PG_TIMESTAMP_INSTANT_CODEC_ID, PG_TIMESTAMP_STRING_CODEC_ID, PG_TIMESTAMP_TEMPORAL_CODEC_ID, PG_TIMESTAMPTZ_STRING_CODEC_ID, @@ -24,6 +26,8 @@ import { pgDate, pgPrecisionParams, pgTime, pgTimestamp, pgTimestamptz } from '. import { pgDateTemporalDecode, pgDateTemporalEncode, + pgTimestampInstantDecode, + pgTimestampInstantEncode, pgTimestampTemporalDecode, pgTimestampTemporalEncode, pgTimestamptzTemporalDecode, @@ -133,6 +137,62 @@ export const pgTimestampTemporalColumn = (params: PrecisionParams = {}) => pgTimestampTemporalColumn satisfies ColumnHelperFor; pgTimestampTemporalColumn satisfies ColumnHelperForStrict; +/** + * A `timestamp` column read and written as a `Temporal.Instant`, treating the stored date and time as UTC. A write sends the instant's UTC date and time with no offset. PostgreSQL fills a database default `now()` in the session's time zone, so under a `TimeZone` other than UTC that value reads back shifted; `@onCreate(now())` writes UTC from the client. + */ +export class PgTimestampInstantCodec extends CodecImpl< + typeof PG_TIMESTAMP_INSTANT_CODEC_ID, + readonly ['equality', 'order'], + string, + Temporal.Instant +> { + async toWire(value: Temporal.Instant, _ctx: CodecCallContext): Promise { + return pgTimestampInstantEncode(value); + } + async fromWire(wire: string, _ctx: CodecCallContext): Promise { + return pgTimestampInstantDecode(wire); + } + fromDataTypeValue(value: DataTypeValue): Temporal.Instant { + refuseInfinity( + this.id, + 'an instant a Temporal.Instant holds', + PG_TIMESTAMP_STRING_CODEC_ID, + value.value, + ); + return pgTimestampInstantDecode(value.value); + } + toDataTypeValue(input: Temporal.Instant): DataTypeValue { + return this.dataTypeValueOf(pgTimestampInstantEncode(input)); + } +} + +export class PgTimestampInstantDescriptor extends PostgresCodecDescriptor { + protected override jsonProjection(expression: ProjectionExpr): ProjectionExpr { + return CastExpr.as(expression, 'text'); + } + override readonly dataType = pgTimestamp.id; + override readonly codecId = PG_TIMESTAMP_INSTANT_CODEC_ID; + override readonly traits = ['equality', 'order'] as const; + override readonly paramsSchema = pgPrecisionParams satisfies StandardSchemaV1; + override factory( + params: PrecisionParams, + ): (ctx: CodecInstanceContext) => PgTimestampInstantCodec { + return () => new PgTimestampInstantCodec(this, pgTimestamp, params ?? {}); + } +} + +export const pgTimestampInstantDescriptor = new PgTimestampInstantDescriptor(); + +export const pgTimestampInstantColumn = (params: PrecisionParams = {}) => + column( + pgTimestampInstantDescriptor.factory(params), + pgTimestampInstantDescriptor.codecId, + params, + ); + +pgTimestampInstantColumn satisfies ColumnHelperFor; +pgTimestampInstantColumn satisfies ColumnHelperForStrict; + export class PgTimestamptzTemporalCodec extends CodecImpl< typeof PG_TIMESTAMPTZ_TEMPORAL_CODEC_ID, readonly ['equality', 'order'], diff --git a/packages/3-targets/postgres/1-target/src/core/type-constructors.ts b/packages/3-targets/postgres/1-target/src/core/type-constructors.ts index 23a6789c402b..0cda1602c106 100644 --- a/packages/3-targets/postgres/1-target/src/core/type-constructors.ts +++ b/packages/3-targets/postgres/1-target/src/core/type-constructors.ts @@ -1,5 +1,10 @@ import type { AuthoringTypeNamespace } from '@internal/framework-components/authoring'; -import { PG_TIMESTAMPTZ_DATE_CODEC_ID } from './codec-ids'; +import { + PG_INTERVAL_STRING_CODEC_ID, + PG_TIMESTAMP_DATE_CODEC_ID, + PG_TIMESTAMP_INSTANT_CODEC_ID, + PG_TIMESTAMPTZ_DATE_CODEC_ID, +} from './codec-ids'; /** The data type of every size argument on Postgres, such as a length or a precision: a typmod is an `int4`. */ export const PG_SIZE_TYPE = 'pg/int4' as const; @@ -110,7 +115,7 @@ export const postgresNativeAuthoringTypes = { }, }, }, - Timestamp: { + TimestampTemporal: { kind: 'typeConstructor', documentation: 'A date and time without a time zone, represented as Temporal.PlainDateTime.', args: [{ kind: 'value', dataType: PG_SIZE_TYPE, name: 'precision', optional: true }], @@ -119,7 +124,7 @@ export const postgresNativeAuthoringTypes = { typeParams: { precision: { kind: 'arg', index: 0 } }, }, }, - Timestamptz: { + TimestamptzTemporal: { kind: 'typeConstructor', documentation: 'An instant represented as Temporal.Instant, with optional fractional-second precision.', @@ -129,6 +134,15 @@ export const postgresNativeAuthoringTypes = { typeParams: { precision: { kind: 'arg', index: 0 } }, }, }, + TimeTemporal: { + kind: 'typeConstructor', + documentation: 'A time of day without a time zone, represented as Temporal.PlainTime.', + args: [{ kind: 'value', dataType: PG_SIZE_TYPE, name: 'precision', optional: true }], + output: { + codecId: 'pg/time-temporal@1', + typeParams: { precision: { kind: 'arg', index: 0 } }, + }, + }, Time: { kind: 'typeConstructor', documentation: 'A time of day without a time zone, represented as Temporal.PlainTime.', @@ -172,6 +186,11 @@ export const postgresNativeAuthoringTypes = { documentation: 'A single-precision floating-point number.', output: { codecId: 'pg/float4@1' }, }, + DateTemporal: { + kind: 'typeConstructor', + documentation: 'A calendar date represented as Temporal.PlainDate.', + output: { codecId: 'pg/date-temporal@1' }, + }, Date: { kind: 'typeConstructor', documentation: 'A calendar date represented as Temporal.PlainDate.', @@ -206,6 +225,26 @@ export const postgresNativeAuthoringTypes = { typeParams: { precision: { kind: 'arg', index: 0 } }, }, }, + TimestampJsDate: { + kind: 'typeConstructor', + documentation: + 'A date and time stored as PostgreSQL timestamp and represented as a JavaScript Date, read and written as UTC.', + args: [{ kind: 'value', dataType: PG_SIZE_TYPE, name: 'precision', optional: true }], + output: { + codecId: PG_TIMESTAMP_DATE_CODEC_ID, + typeParams: { precision: { kind: 'arg', index: 0 } }, + }, + }, + TimestampInstant: { + kind: 'typeConstructor', + documentation: + 'A date and time stored as PostgreSQL timestamp and represented as Temporal.Instant, read and written as UTC.', + args: [{ kind: 'value', dataType: PG_SIZE_TYPE, name: 'precision', optional: true }], + output: { + codecId: PG_TIMESTAMP_INSTANT_CODEC_ID, + typeParams: { precision: { kind: 'arg', index: 0 } }, + }, + }, TimestamptzString: { kind: 'typeConstructor', inferred: true, @@ -246,23 +285,33 @@ export const postgresNativeAuthoringTypes = { typeParams: { length: { kind: 'arg', index: 0 } }, }, }, - Interval: { + IntervalParts: { kind: 'typeConstructor', inferred: true, documentation: - 'A span of time stored as PostgreSQL interval, with optional fractional-second precision.', + 'A span of time stored as PostgreSQL interval and represented as { months, days, micros }, with optional fractional-second precision.', args: [{ kind: 'value', dataType: PG_SIZE_TYPE, name: 'precision', optional: true }], output: { codecId: 'pg/interval@1', typeParams: { precision: { kind: 'arg', index: 0 } }, }, }, + IntervalString: { + kind: 'typeConstructor', + documentation: + 'A span of time stored as PostgreSQL interval and represented as an ISO 8601 duration such as P1Y2M, with optional fractional-second precision.', + args: [{ kind: 'value', dataType: PG_SIZE_TYPE, name: 'precision', optional: true }], + output: { + codecId: PG_INTERVAL_STRING_CODEC_ID, + typeParams: { precision: { kind: 'arg', index: 0 } }, + }, + }, } as const satisfies AuthoringTypeNamespace; /** * The base scalars and native types as type constructors. The target contributes them, so PSL and the TypeScript builder's `type.*` see the same names. */ export const postgresScalarAndNativeTypeConstructors = { - ...postgresScalarAuthoringTypes, ...postgresNativeAuthoringTypes, + ...postgresScalarAuthoringTypes, } as const satisfies AuthoringTypeNamespace; diff --git a/packages/3-targets/postgres/1-target/src/exports/codec-types.ts b/packages/3-targets/postgres/1-target/src/exports/codec-types.ts index 4a31b5552619..f426c3e5b7c3 100644 --- a/packages/3-targets/postgres/1-target/src/exports/codec-types.ts +++ b/packages/3-targets/postgres/1-target/src/exports/codec-types.ts @@ -34,13 +34,6 @@ export type Numeric

}>; export type Bit = BrandedString<{ __bitLength: N }>; export type VarBit = BrandedString<{ __varbitLength: N }>; -export type Timestamp

= BrandedString<{ - __timestampPrecision: P; -}>; -export type Timestamptz

= BrandedString<{ - __timestamptzPrecision: P; -}>; -export type Time

= BrandedString<{ __timePrecision: P }>; export type Timetz

= BrandedString<{ __timetzPrecision: P; }>; @@ -53,9 +46,6 @@ export type TimestamptzString

= Brande export type TimeString

= BrandedString<{ __timeStringPrecision: P; }>; -export type Interval

= BrandedString<{ - __intervalPrecision: P; -}>; type LengthParam = TypeParams extends { readonly length: infer N extends number } ? N @@ -84,7 +74,6 @@ type OutputTypeMap = { readonly 'pg/bit@1': LengthOutput>>; readonly 'pg/varbit@1': LengthOutput>>; readonly 'pg/timetz@1': Timetz>; - readonly 'pg/interval@1': Interval>; readonly 'pg/time-string@1': TimeString>; readonly 'pg/timestamp-string@1': TimestampString>; readonly 'pg/timestamptz-string@1': TimestamptzString>; diff --git a/packages/3-targets/postgres/1-target/src/exports/codecs.ts b/packages/3-targets/postgres/1-target/src/exports/codecs.ts index 57303075732d..4175a27ffd22 100644 --- a/packages/3-targets/postgres/1-target/src/exports/codecs.ts +++ b/packages/3-targets/postgres/1-target/src/exports/codecs.ts @@ -60,11 +60,21 @@ export { } from '../core/codecs'; export type { PgInterval } from '../core/data-type-values'; export { + PgTimestampDateCodec, + PgTimestampDateDescriptor, PgTimestamptzDateCodec, PgTimestamptzDateDescriptor, + pgTimestampDateColumn, + pgTimestampDateDescriptor, pgTimestamptzDateColumn, pgTimestamptzDateDescriptor, } from '../core/date-codecs'; +export { + PgIntervalStringCodec, + PgIntervalStringDescriptor, + pgIntervalStringColumn, + pgIntervalStringDescriptor, +} from '../core/interval-string-codec'; export { parsePostgresListText } from '../core/list-decoder'; export { postgresCodecDescriptorRegistry, diff --git a/packages/3-targets/postgres/1-target/test/codec-render-value-literal.test.ts b/packages/3-targets/postgres/1-target/test/codec-render-value-literal.test.ts index eb911100de41..eb081cb2c37d 100644 --- a/packages/3-targets/postgres/1-target/test/codec-render-value-literal.test.ts +++ b/packages/3-targets/postgres/1-target/test/codec-render-value-literal.test.ts @@ -195,6 +195,8 @@ describe('the literal each registered codec renders', () => { 'pg/timestamp-string@1': undefined, 'pg/timestamptz-string@1': undefined, 'pg/timestamptz-date@1': undefined, + 'pg/timestamp-date@1': undefined, + 'pg/timestamp-instant@1': undefined, 'pg/time-string@1': '"12:34:56"', 'pg/timetz@1': undefined, 'pg/bool@1': 'true', @@ -204,6 +206,7 @@ describe('the literal each registered codec renders', () => { 'pg/uuid@1': '"a0eebc99-9c0b-4ef8-bb6d-6bb9bd380a11"', 'pg/inet@1': '"127.0.0.1"', 'pg/interval@1': undefined, + 'pg/interval-string@1': undefined, 'pg/json@1': undefined, 'pg/jsonb@1': undefined, 'pg/text-array@1': undefined, diff --git a/packages/3-targets/postgres/1-target/test/data-type-inventory.test.ts b/packages/3-targets/postgres/1-target/test/data-type-inventory.test.ts index 44d1d4d6c253..079dd6c0dec1 100644 --- a/packages/3-targets/postgres/1-target/test/data-type-inventory.test.ts +++ b/packages/3-targets/postgres/1-target/test/data-type-inventory.test.ts @@ -21,6 +21,7 @@ const EXPECTED: Readonly> = { 'pg/varbit@1': 'pg/varbit', 'pg/bytea@1': 'pg/bytea', 'pg/interval@1': 'pg/interval', + 'pg/interval-string@1': 'pg/interval', 'pg/timetz@1': 'pg/timetz', 'pg/date-temporal@1': 'pg/date', 'pg/date-string@1': 'pg/date', @@ -28,6 +29,8 @@ const EXPECTED: Readonly> = { 'pg/time-string@1': 'pg/time', 'pg/timestamp-temporal@1': 'pg/timestamp', 'pg/timestamp-string@1': 'pg/timestamp', + 'pg/timestamp-date@1': 'pg/timestamp', + 'pg/timestamp-instant@1': 'pg/timestamp', 'pg/timestamptz-temporal@1': 'pg/timestamptz', 'pg/timestamptz-string@1': 'pg/timestamptz', 'pg/timestamptz-date@1': 'pg/timestamptz', diff --git a/packages/3-targets/postgres/1-target/test/data-types.test.ts b/packages/3-targets/postgres/1-target/test/data-types.test.ts index 12372a2f96f1..ec68908b9560 100644 --- a/packages/3-targets/postgres/1-target/test/data-types.test.ts +++ b/packages/3-targets/postgres/1-target/test/data-types.test.ts @@ -264,7 +264,10 @@ describe("each codec's equality-key flag", () => { withoutKey: flagged .filter((descriptor) => types.get(descriptor.dataType)?.equalityKey === undefined) .map((descriptor) => descriptor.codecId), - }).toEqual({ flagged: ['pg/numeric@1', 'pg/interval@1'], withoutKey: [] }); + }).toEqual({ + flagged: ['pg/numeric@1', 'pg/interval@1', 'pg/interval-string@1'], + withoutKey: [], + }); }); it('is declared only by codecs that render no literal, since a read can return a member in another form', async () => { diff --git a/packages/3-targets/postgres/1-target/test/explicit-type-codec-wiring.test.ts b/packages/3-targets/postgres/1-target/test/explicit-type-codec-wiring.test.ts new file mode 100644 index 000000000000..cb2451195dd6 --- /dev/null +++ b/packages/3-targets/postgres/1-target/test/explicit-type-codec-wiring.test.ts @@ -0,0 +1,80 @@ +import { describe, expect, it } from 'vitest'; +import { postgresAggregateDescriptors } from '../src/core/aggregates'; +import { codecDescriptorMap } from '../src/core/codec-type-map'; +import { codecDescriptors } from '../src/core/codecs'; +import * as codecIds from '../src/exports/codec-ids'; +import * as codecs from '../src/exports/codecs'; + +const rowsFor = (codecId: string) => + postgresAggregateDescriptors.filter( + (row) => row.input.kind === 'codec' && row.input.codecId === codecId, + ); + +const preserved = (codecId: string) => + ['min', 'max'].map((operation) => ({ + operation, + input: { kind: 'codec', codecId }, + output: { kind: 'self' }, + nullable: true, + })); + +describe.each([ + { + codecId: 'pg/timestamp-date@1', + constant: 'PG_TIMESTAMP_DATE_CODEC_ID', + mapKey: 'timestamp-date', + exported: [ + 'PgTimestampDateCodec', + 'PgTimestampDateDescriptor', + 'pgTimestampDateColumn', + 'pgTimestampDateDescriptor', + ], + }, + { + codecId: 'pg/timestamp-instant@1', + constant: 'PG_TIMESTAMP_INSTANT_CODEC_ID', + mapKey: 'timestamp-instant', + exported: [], + }, + { + codecId: 'pg/interval-string@1', + constant: 'PG_INTERVAL_STRING_CODEC_ID', + mapKey: 'interval-string', + exported: [ + 'PgIntervalStringCodec', + 'PgIntervalStringDescriptor', + 'pgIntervalStringColumn', + 'pgIntervalStringDescriptor', + ], + }, +])('$codecId', ({ codecId, constant, mapKey, exported }) => { + it('has an exported id, is registered once and is in the codec type map', () => { + const registered = codecs.postgresCodecRegistry.descriptorFor(codecId); + expect(codecIds).toHaveProperty(constant, codecId); + for (const name of exported) expect(codecs).toHaveProperty(name); + expect(codecDescriptorMap).toHaveProperty(mapKey, registered); + expect(codecDescriptors.filter((entry) => entry.codecId === codecId)).toEqual([registered]); + }); +}); + +describe('the aggregates over the explicit timestamp and interval codecs', () => { + it.each(['pg/timestamp-date@1', 'pg/timestamp-instant@1'])( + 'keep %s for min and max and offer no sum or avg', + (codecId) => { + expect(rowsFor(codecId)).toEqual(preserved(codecId)); + }, + ); + + it('sum and average an interval read as a string into the same codec, as PostgreSQL does', () => { + const codecId = 'pg/interval-string@1'; + expect(rowsFor(codecId)).toEqual([ + ...['sum', 'avg'].map((operation) => ({ + operation, + input: { kind: 'codec', codecId }, + output: { kind: 'codec', codecId }, + nullable: true, + })), + ...preserved(codecId), + ]); + }); +}); diff --git a/packages/3-targets/postgres/1-target/test/fixtures/namespaced-contract.d.ts b/packages/3-targets/postgres/1-target/test/fixtures/namespaced-contract.d.ts index f19300ad8467..573fff273ee9 100644 --- a/packages/3-targets/postgres/1-target/test/fixtures/namespaced-contract.d.ts +++ b/packages/3-targets/postgres/1-target/test/fixtures/namespaced-contract.d.ts @@ -6,12 +6,8 @@ import type { Bit, Char, CodecTypes as PgTypes, - Interval, JsonValue, Numeric, - Time, - Timestamp, - Timestamptz, Timetz, VarBit, Varchar, diff --git a/packages/3-targets/postgres/1-target/test/generator-functions.test.ts b/packages/3-targets/postgres/1-target/test/generator-functions.test.ts index 2e551c71e01b..3a809de01207 100644 --- a/packages/3-targets/postgres/1-target/test/generator-functions.test.ts +++ b/packages/3-targets/postgres/1-target/test/generator-functions.test.ts @@ -16,6 +16,8 @@ describe('the Postgres generator functions', () => { ['pg/timestamptz-string@1', 'timestamptzStringNow'], ['pg/timestamp-string@1', 'timestampStringNow'], ['pg/timestamptz-date@1', 'timestampNow'], + ['pg/timestamp-date@1', 'timestampNow'], + ['pg/timestamp-instant@1', 'instantNow'], ['pg/date-temporal@1', 'plainDateNow'], ['pg/date-string@1', 'dateStringNow'], ])('resolve now() on %s to %s with no params', (codecId, generatorId) => { diff --git a/packages/3-targets/postgres/1-target/test/interval-string-codec.test.ts b/packages/3-targets/postgres/1-target/test/interval-string-codec.test.ts new file mode 100644 index 000000000000..f11933f276cb --- /dev/null +++ b/packages/3-targets/postgres/1-target/test/interval-string-codec.test.ts @@ -0,0 +1,72 @@ +import { describe, expect, it } from 'vitest'; +import { pgIntervalStringDescriptor } from '../src/core/interval-string-codec'; +import { fromContractJson, toContractJson } from './contract-json'; + +const codec = pgIntervalStringDescriptor.factory({})({ name: 'test' }); + +describe('pg/interval-string@1', () => { + it('represents pg/interval, keeping the form a value was written in', () => { + expect({ + dataType: pgIntervalStringDescriptor.dataType, + traits: pgIntervalStringDescriptor.traits, + keepsWrittenForm: pgIntervalStringDescriptor.keepsWrittenForm, + }).toEqual({ dataType: 'pg/interval', traits: ['equality', 'order'], keepsWrittenForm: true }); + }); + + it.each([ + ['1 year 2 mons 3 days 04:05:06.5', 'P1Y2M3DT4H5M6.5S'], + ['-1 years -2 mons +3 days -04:00:00', 'P-1Y-2M3DT-4H'], + ['00:00:00', 'PT0S'], + ['@ 1 year 2 mons -3 days 4 hours 5 mins 6.5 secs ago', 'P-1Y-2M3DT-4H-5M-6.5S'], + ['+1-2 -3 +4:05:06.5', 'P1Y2M-3DT4H5M6.5S'], + ['-1 2:03:04', 'P-1DT-2H-3M-4S'], + ['P1Y2M3DT4H5M6.5S', 'P1Y2M3DT4H5M6.5S'], + ['P0D', 'PT0S'], + ])('reads the text %s, printed under any IntervalStyle, as %s', async (wire, iso) => { + expect(await codec.fromWire(wire, {})).toBe(iso); + }); + + it('reads the fields object a driver may parse an interval into', async () => { + expect(await codec.fromWire({ years: 1, months: 2, days: 3, hours: 4 }, {})).toBe('P1Y2M3DT4H'); + }); + + it.each([ + ['P1Y2M', 'P1Y2M'], + ['P14M', 'P1Y2M'], + ['PT90M', 'PT1H30M'], + ['P1DT-1H', 'P1DT-1H'], + ['PT0.000001S', 'PT0.000001S'], + ])('writes %s as the duration pg/interval stores, %s', async (written, stored) => { + expect({ + wire: await codec.toWire(written, {}), + json: toContractJson(codec, written), + fromJson: fromContractJson(codec, stored), + }).toEqual({ wire: stored, json: stored, fromJson: stored }); + }); + + it.each([ + ['1 day', 'an ISO 8601 duration'], + ['P', 'an ISO 8601 duration'], + ['', 'an ISO 8601 duration'], + ['PT1.1234567S', 'at most 6'], + ])('refuses %s on write', async (written, reason) => { + await expect(codec.toWire(written, {})).rejects.toThrow( + expect.objectContaining({ + code: 'RUNTIME.ENCODE_FAILED', + message: expect.stringContaining(reason), + }), + ); + expect(() => toContractJson(codec, written)).toThrow( + expect.objectContaining({ code: 'RUNTIME.ENCODE_FAILED' }), + ); + }); + + it.each([42, null, { months: 1, days: 0, micros: 0n }])( + 'refuses %s, which is not a string, on write', + async (value) => { + await expect(codec.toWire(value as unknown as string, {})).rejects.toThrow( + expect.objectContaining({ code: 'RUNTIME.ENCODE_FAILED' }), + ); + }, + ); +}); diff --git a/packages/3-targets/postgres/1-target/test/now-generators.test.ts b/packages/3-targets/postgres/1-target/test/now-generators.test.ts index c45cba1a51e5..114c1585ff6c 100644 --- a/packages/3-targets/postgres/1-target/test/now-generators.test.ts +++ b/packages/3-targets/postgres/1-target/test/now-generators.test.ts @@ -42,6 +42,8 @@ const DATE_AND_TIME_CODEC_IDS = [ 'pg/timestamptz-temporal@1', 'pg/timestamptz-string@1', 'pg/timestamptz-date@1', + 'pg/timestamp-date@1', + 'pg/timestamp-instant@1', ]; function codecFor(codecId: string) { @@ -70,10 +72,9 @@ describe('the "now" generator for each codec', () => { }); it('has no generator for a codec without one', () => { - expect(['pg/text@1', 'pg/interval@1'].map(postgresNowGeneratorIdFor)).toEqual([ - undefined, - undefined, - ]); + expect( + ['pg/text@1', 'pg/interval@1', 'pg/interval-string@1'].map(postgresNowGeneratorIdFor), + ).toEqual([undefined, undefined, undefined]); }); it('pairs each date and time codec with its generator', () => { @@ -88,6 +89,8 @@ describe('the "now" generator for each codec', () => { 'pg/timestamptz-temporal@1': 'instantNow', 'pg/timestamptz-string@1': 'timestamptzStringNow', 'pg/timestamptz-date@1': 'timestampNow', + 'pg/timestamp-date@1': 'timestampNow', + 'pg/timestamp-instant@1': 'instantNow', }); }); diff --git a/packages/3-targets/postgres/1-target/test/parameterized-output-types.test-d.ts b/packages/3-targets/postgres/1-target/test/parameterized-output-types.test-d.ts index 3b6223649cbb..b55308dee872 100644 --- a/packages/3-targets/postgres/1-target/test/parameterized-output-types.test-d.ts +++ b/packages/3-targets/postgres/1-target/test/parameterized-output-types.test-d.ts @@ -4,9 +4,9 @@ import type { Bit, Char, CodecTypes, - Interval, Numeric, ParameterizedOutputTypesForTypeParams, + PgInterval, TimeString, TimestampString, TimestamptzString, @@ -54,9 +54,12 @@ describe('the output type of a Postgres column with type parameters', () => { >(); }); + test('keeps the interval parts an interval with a precision reads', () => { + expectTypeOf>().toEqualTypeOf(); + }); + test('carries a time precision, or none', () => { expectTypeOf>().toEqualTypeOf>(); - expectTypeOf>().toEqualTypeOf>(); expectTypeOf>().toEqualTypeOf< TimestampString<6> >(); diff --git a/packages/3-targets/postgres/1-target/test/psl-infer/infer-column-types.test.ts b/packages/3-targets/postgres/1-target/test/psl-infer/infer-column-types.test.ts index 891e54124c1c..b304447c2e26 100644 --- a/packages/3-targets/postgres/1-target/test/psl-infer/infer-column-types.test.ts +++ b/packages/3-targets/postgres/1-target/test/psl-infer/infer-column-types.test.ts @@ -42,8 +42,8 @@ const COLUMN_TYPES = [ ['bit(8)', 'Bit(8)'], ['bit varying', 'VarBit'], ['bit varying(16)', 'VarBit(16)'], - ['interval', 'Interval'], - ['interval(3)', 'Interval(3)'], + ['interval', 'IntervalParts'], + ['interval(3)', 'IntervalParts(3)'], ] as const; function tableWith(columns: Record) { diff --git a/packages/3-targets/postgres/1-target/test/psl-print/generated-values.test.ts b/packages/3-targets/postgres/1-target/test/psl-print/generated-values.test.ts index 269cbcac0948..5a3d69c2c60c 100644 --- a/packages/3-targets/postgres/1-target/test/psl-print/generated-values.test.ts +++ b/packages/3-targets/postgres/1-target/test/psl-print/generated-values.test.ts @@ -124,13 +124,13 @@ describe('generated values', () => { it('writes a now generator on create and on update', () => { const now = generator(postgresNowGeneratorIds[PG_TIMESTAMPTZ_TEMPORAL_CODEC_ID]); expect(withGenerator(timestamptz, { onCreate: now, onUpdate: now })).toBe( - 'value DateTime @onCreate(now()) @onUpdate(now())', + 'value TimestamptzTemporal @onCreate(now()) @onUpdate(now())', ); }); it("writes a time now generator whose precision is the column's as @onUpdate(now())", () => { expect(withGenerator(time3, { onUpdate: generator('plainTimeNow', { precision: 3 }) })).toBe( - 'value Time(3) @onUpdate(now())', + 'value TimeTemporal(3) @onUpdate(now())', ); }); @@ -140,7 +140,7 @@ describe('generated values', () => { { ...timestamptz, default: { kind: 'function', expression: 'now()' } }, { onCreate: generator('instantNow') }, ), - ).toBe('value DateTime @default(now()) @onCreate(now())'); + ).toBe('value TimestamptzTemporal @default(now()) @onCreate(now())'); }); it('writes a generator through the generator function an extension contributes', () => { diff --git a/packages/3-targets/postgres/1-target/src/temporal-global.d.ts b/packages/3-targets/postgres/1-target/test/temporal-global.d.ts similarity index 100% rename from packages/3-targets/postgres/1-target/src/temporal-global.d.ts rename to packages/3-targets/postgres/1-target/test/temporal-global.d.ts diff --git a/packages/3-targets/postgres/1-target/test/temporal-representation-taxonomy.test.ts b/packages/3-targets/postgres/1-target/test/temporal-representation-taxonomy.test.ts index 226d7c78bafb..898f772667b0 100644 --- a/packages/3-targets/postgres/1-target/test/temporal-representation-taxonomy.test.ts +++ b/packages/3-targets/postgres/1-target/test/temporal-representation-taxonomy.test.ts @@ -1,9 +1,16 @@ import { ColumnRef, type ProjectionExpr } from '@internal/sql-relational-core/ast'; import { describe, expect, it } from 'vitest'; -import { pgTimestamptzDateColumn, pgTimestamptzDateDescriptor } from '../src/core/date-codecs'; +import { + pgTimestampDateColumn, + pgTimestampDateDescriptor, + pgTimestamptzDateColumn, + pgTimestamptzDateDescriptor, +} from '../src/core/date-codecs'; import { pgDateTemporalColumn, pgDateTemporalDescriptor, + pgTimestampInstantColumn, + pgTimestampInstantDescriptor, pgTimestampTemporalColumn, pgTimestampTemporalDescriptor, pgTimestamptzTemporalColumn, @@ -50,6 +57,7 @@ interface TaxonomyRow { readonly temporal: Representation; readonly string: Representation; readonly date?: Representation; + readonly instant?: Representation; } const TAXONOMY: readonly TaxonomyRow[] = [ @@ -82,6 +90,16 @@ const TAXONOMY: readonly TaxonomyRow[] = [ descriptor: pgTimestampStringDescriptor, column: pgTimestampStringColumn, }, + date: { + codecId: 'pg/timestamp-date@1', + descriptor: pgTimestampDateDescriptor, + column: pgTimestampDateColumn, + }, + instant: { + codecId: 'pg/timestamp-instant@1', + descriptor: pgTimestampInstantDescriptor, + column: pgTimestampInstantColumn, + }, }, { typeText: 'timestamptz', @@ -124,11 +142,12 @@ const representations = TAXONOMY.flatMap((row) => [ { row, kind: 'temporal' as const, rep: row.temporal }, { row, kind: 'string' as const, rep: row.string }, ...(row.date ? [{ row, kind: 'date' as const, rep: row.date }] : []), + ...(row.instant ? [{ row, kind: 'instant' as const, rep: row.instant }] : []), ]); const sourceExpression = ColumnRef.of('reading', 'at'); -describe('the nine representation-explicit temporal codecs', () => { +describe('the eleven representation-explicit temporal codecs', () => { describe.each(representations)('$rep.codecId', ({ row, rep }) => { it('declares the id the taxonomy names', () => { expect(rep.descriptor.codecId).toBe(rep.codecId); diff --git a/packages/3-targets/postgres/1-target/test/timestamp-utc-codecs.test.ts b/packages/3-targets/postgres/1-target/test/timestamp-utc-codecs.test.ts new file mode 100644 index 000000000000..fd3d5a22458e --- /dev/null +++ b/packages/3-targets/postgres/1-target/test/timestamp-utc-codecs.test.ts @@ -0,0 +1,158 @@ +import { describe, expect, it } from 'vitest'; +import { pgTimestampDateDescriptor } from '../src/core/date-codecs'; +import { pgTimestampInstantDescriptor } from '../src/core/temporal-codecs'; +import { fromContractJson, toContractJson } from './contract-json'; + +describe('pg/timestamp-date@1', () => { + const codec = pgTimestampDateDescriptor.factory({})({ name: 'test' }); + + it('represents pg/timestamp with the order traits', () => { + expect({ + dataType: pgTimestampDateDescriptor.dataType, + traits: pgTimestampDateDescriptor.traits, + }).toEqual({ dataType: 'pg/timestamp', traits: ['equality', 'order'] }); + }); + + it.each([ + ['2026-01-02 03:04:05.123456', '2026-01-02T03:04:05.123Z'], + ['2026-01-02 03:04:05', '2026-01-02T03:04:05.000Z'], + ['0001-01-01 00:00:00 BC', '0000-01-01T00:00:00.000Z'], + ['0044-03-15 12:00:00 BC', '-000043-03-15T12:00:00.000Z'], + ['4714-11-24 00:00:00 BC', '-004713-11-24T00:00:00.000Z'], + ['12026-01-02 03:04:05', '+012026-01-02T03:04:05.000Z'], + ['275760-09-13 00:00:00', '+275760-09-13T00:00:00.000Z'], + ])('reads the wire text %s as UTC', async (wire, iso) => { + expect(await codec.fromWire(wire, {})).toEqual(new Date(iso)); + }); + + it.each([ + ['2026-01-02T03:04:05.123Z', '2026-01-02T03:04:05.123', '2026-01-02T03:04:05.123'], + ['2026-01-02T03:04:05.000Z', '2026-01-02T03:04:05.000', '2026-01-02T03:04:05'], + ['-000043-03-15T12:00:00.000Z', '0044-03-15T12:00:00.000 BC', '-000043-03-15T12:00:00'], + ['+012026-01-02T03:04:05.000Z', '12026-01-02T03:04:05.000', '+012026-01-02T03:04:05'], + ])( + 'writes %s as its UTC wall-clock time, with no offset, on the wire and in JSON', + async (iso, wire, json) => { + const value = new Date(iso); + expect({ + wire: await codec.toWire(value, {}), + json: toContractJson(codec, value), + fromWire: await codec.fromWire(wire, {}), + fromJson: fromContractJson(codec, json), + }).toEqual({ wire, json, fromWire: value, fromJson: value }); + }, + ); + + it.each(['infinity', '-infinity', '275760-09-13 00:00:01'])( + 'refuses the wire text %s, which no Date holds, naming TimestampString', + async (wire) => { + await expect(codec.fromWire(wire, {})).rejects.toThrow(/TimestampString/); + }, + ); + + it.each(['infinity', '-infinity', '+275760-09-13T00:00:01'])( + 'refuses the stored value %s, which no Date holds, naming pg/timestamp-string@1', + (json) => { + expect(() => fromContractJson(codec, json)).toThrow( + expect.objectContaining({ + code: 'RUNTIME.DECODE_FAILED', + message: expect.stringContaining('pg/timestamp-string@1'), + }), + ); + }, + ); + + it('refuses wire text with an offset, which a timestamp column never prints', async () => { + await expect(codec.fromWire('2026-01-02 03:04:05+00', {})).rejects.toThrow( + 'pg/timestamp-date@1', + ); + }); + + it.each([new Date(Number.NaN), new Date('-004713-11-23T23:59:59.999Z'), '2026-01-01', null])( + 'refuses %s on write', + async (value) => { + await expect(codec.toWire(value as Date, {})).rejects.toThrow('pg/timestamp-date@1'); + expect(() => toContractJson(codec, value as Date)).toThrow('pg/timestamp-date@1'); + }, + ); +}); + +describe('pg/timestamp-instant@1', () => { + const codec = pgTimestampInstantDescriptor.factory({})({ name: 'test' }); + + it('represents pg/timestamp with the order traits', () => { + expect({ + dataType: pgTimestampInstantDescriptor.dataType, + traits: pgTimestampInstantDescriptor.traits, + }).toEqual({ dataType: 'pg/timestamp', traits: ['equality', 'order'] }); + }); + + it.each([ + ['2026-01-02 03:04:05.123456', '2026-01-02T03:04:05.123456Z'], + ['2026-01-02 03:04:05', '2026-01-02T03:04:05Z'], + ['0044-03-15 12:00:00 BC', '-000043-03-15T12:00:00Z'], + ['12026-01-02 03:04:05', '+012026-01-02T03:04:05Z'], + ['275760-09-13 00:00:00', '+275760-09-13T00:00:00Z'], + ])('reads the wire text %s as an instant in UTC', async (wire, iso) => { + const read = await codec.fromWire(wire, {}); + expect(read.equals(Temporal.Instant.from(iso))).toBe(true); + }); + + it.each([ + ['2026-01-02T03:04:05.123456Z', '2026-01-02T03:04:05.123456', '2026-01-02T03:04:05.123456'], + ['2026-01-02T03:04:05Z', '2026-01-02T03:04:05', '2026-01-02T03:04:05'], + ['-000043-03-15T12:00:00Z', '0044-03-15T12:00:00 BC', '-000043-03-15T12:00:00'], + ])( + 'writes %s as its UTC wall-clock time, with no offset, on the wire and in JSON', + async (iso, wire, json) => { + const value = Temporal.Instant.from(iso); + const read = { + wire: await codec.toWire(value, {}), + json: toContractJson(codec, value), + fromWire: (await codec.fromWire(wire, {})).equals(value), + fromJson: fromContractJson(codec, json).equals(value), + }; + expect(read).toEqual({ wire, json, fromWire: true, fromJson: true }); + }, + ); + + it.each(['infinity', '-infinity', '275760-09-13 00:00:01'])( + 'refuses the wire text %s, which no Temporal.Instant holds, naming TimestampString', + async (wire) => { + await expect(codec.fromWire(wire, {})).rejects.toThrow( + expect.objectContaining({ + code: 'RUNTIME.DECODE_FAILED', + message: expect.stringContaining('TimestampString'), + }), + ); + }, + ); + + it.each(['infinity', '-infinity'])( + 'refuses the stored value %s, naming pg/timestamp-string@1', + (json) => { + expect(() => fromContractJson(codec, json)).toThrow( + expect.objectContaining({ message: expect.stringContaining('pg/timestamp-string@1') }), + ); + }, + ); + + it('refuses the stored value +275760-09-13T00:00:01, past the last Temporal.Instant, naming TimestampString', () => { + expect(() => fromContractJson(codec, '+275760-09-13T00:00:01')).toThrow( + expect.objectContaining({ + code: 'RUNTIME.DECODE_FAILED', + message: expect.stringContaining('TimestampString'), + }), + ); + }); + + it.each([ + [Temporal.PlainDateTime.from('2026-01-02T03:04:05'), 'a Temporal.PlainDateTime'], + [new Date('2026-01-02T03:04:05Z'), 'a Date'], + ['2026-01-02T03:04:05Z', 'a string'], + ])('refuses %s on write, naming Temporal.Instant', async (value, received) => { + await expect(codec.toWire(value as unknown as Temporal.Instant, {})).rejects.toThrow( + `Codec 'pg/timestamp-instant@1' encodes a Temporal.Instant, but received ${received}.`, + ); + }); +}); diff --git a/packages/3-targets/postgres/1-target/test/timestamp-utc-codecs.time-zone.test.ts b/packages/3-targets/postgres/1-target/test/timestamp-utc-codecs.time-zone.test.ts new file mode 100644 index 000000000000..7103785d865d --- /dev/null +++ b/packages/3-targets/postgres/1-target/test/timestamp-utc-codecs.time-zone.test.ts @@ -0,0 +1,43 @@ +import { afterEach, describe, expect, it } from 'vitest'; +import { pgTimestampDateDescriptor } from '../src/core/date-codecs'; +import { pgTimestampInstantDescriptor } from '../src/core/temporal-codecs'; +import { fromContractJson, toContractJson } from './contract-json'; + +const dateCodec = pgTimestampDateDescriptor.factory({})({ name: 'at' }); +const instantCodec = pgTimestampInstantDescriptor.factory({})({ name: 'at' }); +const originalTimeZone = process.env['TZ']; + +afterEach(() => { + if (originalTimeZone === undefined) delete process.env['TZ']; + else process.env['TZ'] = originalTimeZone; +}); + +describe.each(['UTC', 'Asia/Kolkata', 'America/Los_Angeles'])( + 'the UTC timestamp codecs under TZ=%s', + (timeZone) => { + it('read and write the stored date and time as UTC, whatever the host time zone', async () => { + process.env['TZ'] = timeZone; + const date = new Date('2026-01-02T03:04:05.123Z'); + const instant = Temporal.Instant.from('2026-01-02T03:04:05.123456Z'); + expect({ + dateRead: (await dateCodec.fromWire('2026-01-02 03:04:05.123', {})).toISOString(), + dateWire: await dateCodec.toWire(date, {}), + dateJson: toContractJson(dateCodec, date), + dateFromJson: fromContractJson(dateCodec, '2026-01-02T03:04:05.123').toISOString(), + instantRead: String(await instantCodec.fromWire('2026-01-02 03:04:05.123456', {})), + instantWire: await instantCodec.toWire(instant, {}), + instantJson: toContractJson(instantCodec, instant), + instantFromJson: String(fromContractJson(instantCodec, '2026-01-02T03:04:05.123456')), + }).toEqual({ + dateRead: '2026-01-02T03:04:05.123Z', + dateWire: '2026-01-02T03:04:05.123', + dateJson: '2026-01-02T03:04:05.123', + dateFromJson: '2026-01-02T03:04:05.123Z', + instantRead: '2026-01-02T03:04:05.123456Z', + instantWire: '2026-01-02T03:04:05.123456', + instantJson: '2026-01-02T03:04:05.123456', + instantFromJson: '2026-01-02T03:04:05.123456Z', + }); + }); + }, +); diff --git a/packages/3-targets/postgres/1-target/test/type-constructors.test.ts b/packages/3-targets/postgres/1-target/test/type-constructors.test.ts index 1e8d65fbbb5b..0f70d1958bbe 100644 --- a/packages/3-targets/postgres/1-target/test/type-constructors.test.ts +++ b/packages/3-targets/postgres/1-target/test/type-constructors.test.ts @@ -1,6 +1,7 @@ import type { AuthoringTypeNamespace } from '@internal/framework-components/authoring'; import { collectScalarTypeConstructors, + findAuthoringTypeConstructorCall, instantiateAuthoringTypeConstructor, validateAuthoringTypeParams, } from '@internal/framework-components/authoring'; @@ -17,6 +18,22 @@ import { } from '../src/core/type-constructors'; import postgresTargetPack from '../src/exports/pack'; +describe('the constructor contract print writes for a Temporal column', () => { + it.each([ + [{ codecId: 'pg/timestamptz-temporal@1' }, ['TimestamptzTemporal'], []], + [{ codecId: 'pg/date-temporal@1' }, ['DateTemporal'], []], + [{ codecId: 'pg/time-temporal@1', typeParams: { precision: 0 } }, ['TimeTemporal'], [0]], + ])( + 'is the explicit type for %o, not a name the convenience types will take', + (output, path, args) => { + expect(findAuthoringTypeConstructorCall(postgresTargetPack.authoring.type, output)).toEqual({ + path, + args, + }); + }, + ); +}); + describe('the type constructors the target contributes', () => { it('are the scalar, native and target-only ones, so PSL and type.* see the same names', () => { expect(postgresTargetPack.authoring.type).toEqual({ @@ -148,7 +165,7 @@ const INFERRED = [ 'TimestamptzString', 'Bit', 'VarBit', - 'Interval', + 'IntervalParts', 'pg.enum', ]; @@ -241,15 +258,17 @@ describe('postgresNativeAuthoringTypes', () => { VarChar: { codecId: 'sql/varchar@1' }, Char: { codecId: 'sql/char@1' }, Numeric: { codecId: 'pg/numeric@1' }, - Timestamp: { codecId: 'pg/timestamp-temporal@1' }, - Timestamptz: { codecId: 'pg/timestamptz-temporal@1' }, + TimestampTemporal: { codecId: 'pg/timestamp-temporal@1' }, + TimestamptzTemporal: { codecId: 'pg/timestamptz-temporal@1' }, Time: { codecId: 'pg/time-temporal@1' }, + TimeTemporal: { codecId: 'pg/time-temporal@1' }, Timetz: { codecId: 'pg/timetz@1' }, Uuid: { codecId: 'pg/uuid@1' }, Inet: { codecId: 'pg/inet@1' }, SmallInt: { codecId: 'pg/int2@1' }, Real: { codecId: 'pg/float4@1' }, Date: { codecId: 'pg/date-temporal@1' }, + DateTemporal: { codecId: 'pg/date-temporal@1' }, DateString: { codecId: 'pg/date-string@1' }, TimestampString: { codecId: 'pg/timestamp-string@1' }, TimestamptzString: { codecId: 'pg/timestamptz-string@1' }, @@ -257,7 +276,10 @@ describe('postgresNativeAuthoringTypes', () => { TimeString: { codecId: 'pg/time-string@1' }, Bit: { codecId: 'pg/bit@1' }, VarBit: { codecId: 'pg/varbit@1' }, - Interval: { codecId: 'pg/interval@1' }, + IntervalParts: { codecId: 'pg/interval@1' }, + IntervalString: { codecId: 'pg/interval-string@1' }, + TimestampJsDate: { codecId: 'pg/timestamp-date@1' }, + TimestampInstant: { codecId: 'pg/timestamp-instant@1' }, }); }); @@ -291,8 +313,11 @@ describe('postgresNativeAuthoringTypes', () => { ['Bit', [8], { codecId: 'pg/bit@1', typeParams: { length: 8 } }], ['VarBit', [], { codecId: 'pg/varbit@1' }], ['VarBit', [16], { codecId: 'pg/varbit@1', typeParams: { length: 16 } }], - ['Interval', [], { codecId: 'pg/interval@1' }], - ['Interval', [3], { codecId: 'pg/interval@1', typeParams: { precision: 3 } }], + ['IntervalParts', [], { codecId: 'pg/interval@1' }], + ['IntervalParts', [3], { codecId: 'pg/interval@1', typeParams: { precision: 3 } }], + ['IntervalString', [3], { codecId: 'pg/interval-string@1', typeParams: { precision: 3 } }], + ['TimestampJsDate', [3], { codecId: 'pg/timestamp-date@1', typeParams: { precision: 3 } }], + ['TimestampInstant', [6], { codecId: 'pg/timestamp-instant@1', typeParams: { precision: 6 } }], ] as const)('maps %s(%s) onto the parameter of the same name', (name, args, expected) => { expect(instantiateAuthoringTypeConstructor(postgresNativeAuthoringTypes[name], args)).toEqual( expected, @@ -320,15 +345,18 @@ describe('postgresNativeAuthoringTypes', () => { ['Numeric', [0]], ['Numeric', [1001]], ['Numeric', [10, 1001]], - ['Timestamp', [-1]], - ['Timestamp', [7]], - ['Timestamptz', [7]], + ['TimestampTemporal', [-1]], + ['TimestampTemporal', [7]], + ['TimestamptzTemporal', [7]], ['Time', [7]], ['Timetz', [7]], ['TimestamptzJsDate', [7]], ['Bit', [0]], ['VarBit', [0]], - ['Interval', [7]], + ['IntervalParts', [7]], + ['IntervalString', [7]], + ['TimestampInstant', [7]], + ['TimestampJsDate', [7]], ] as const)('refuses %s(%s)', (name, args) => { expect(() => check(name, args)).toThrow( expect.objectContaining({ code: 'CONTRACT.ARGUMENT_INVALID' }), @@ -341,9 +369,13 @@ describe('postgresNativeAuthoringTypes', () => { ['Char', [10485760]], ['Numeric', [1000, 1000]], ['Numeric', [1, 0]], - ['Timestamp', [0]], - ['Timestamp', [6]], + ['TimestampTemporal', [0]], + ['TimestampTemporal', [6]], ['Timetz', [6]], + ['TimestampJsDate', [0]], + ['TimestampJsDate', [6]], + ['TimestampInstant', [6]], + ['IntervalString', [6]], ] as const)('accepts %s(%s)', (name, args) => { expect(() => check(name, args)).not.toThrow(); }); @@ -358,6 +390,22 @@ describe('postgresNativeAuthoringTypes', () => { }); }); + it('has no type named Timestamptz, Timestamp or Interval', () => { + for (const removed of ['Timestamptz', 'Timestamp', 'Interval']) { + expect(everyPostgresConstructor).not.toHaveProperty(removed); + } + }); + + it.each([ + ['DateTemporal', 'Date'], + ['TimeTemporal', 'Time'], + ] as const)('%s is the same type as %s', (name, sameAs) => { + expect(postgresNativeAuthoringTypes[name]).toEqual({ + ...postgresNativeAuthoringTypes[sameAs], + documentation: expect.stringMatching(/\S/), + }); + }); + it('offers only the precision-bearing TimestamptzJsDate for a Date-backed timestamptz', () => { expect(everyPostgresConstructor).not.toHaveProperty('DateTimeDate'); expect(everyPostgresConstructor).not.toHaveProperty('TimestamptzDate'); @@ -372,7 +420,7 @@ describe('postgresNativeAuthoringTypes', () => { }, }); expect(postgresScalarAuthoringTypes.DateTime.output.codecId).toBe('pg/timestamptz-temporal@1'); - expect(postgresNativeAuthoringTypes.Timestamptz.output.codecId).toBe( + expect(postgresNativeAuthoringTypes.TimestamptzTemporal.output.codecId).toBe( 'pg/timestamptz-temporal@1', ); }); @@ -380,9 +428,10 @@ describe('postgresNativeAuthoringTypes', () => { describe('precision is a value of pg/int4, bounded by the data type', () => { it.each([ - 'Timestamp', - 'Timestamptz', + 'TimestampTemporal', + 'TimestamptzTemporal', 'Time', + 'TimeTemporal', 'Timetz', 'TimestampString', 'TimestamptzString', diff --git a/packages/3-targets/postgres/1-target/tsconfig.no-temporal-global.json b/packages/3-targets/postgres/1-target/tsconfig.no-temporal-global.json new file mode 100644 index 000000000000..bdc63200b33d --- /dev/null +++ b/packages/3-targets/postgres/1-target/tsconfig.no-temporal-global.json @@ -0,0 +1,4 @@ +{ + "extends": "./tsconfig.json", + "include": ["src/**/*"] +} diff --git a/packages/3-targets/postgres/2-adapter/README.md b/packages/3-targets/postgres/2-adapter/README.md index 24b01227e171..42980c75cfb2 100644 --- a/packages/3-targets/postgres/2-adapter/README.md +++ b/packages/3-targets/postgres/2-adapter/README.md @@ -140,7 +140,7 @@ flowchart TD **Column Types Export (`column-types.ts`)** - Exports column descriptors for built-in types; enums are declared with `enumType` from the contract builder -- Parameterized helpers: ``rawColumn(sql`tsvector`)`` for a column of a type no other column type covers, its type written as a `sql` literal (ADR 273), `charColumn(length)`, `varcharColumn(length)`, `numericColumn(precision, scale?)`, `bitColumn(length)`, `varbitColumn(length)`, `timeColumn(precision?)`, `timetzColumn(precision?)`, `intervalColumn(precision?)` +- Parameterized helpers: ``rawColumn(sql`tsvector`)`` for a column of a type no other column type covers, its type written as a `sql` literal (ADR 273), `charColumn(length)`, `varcharColumn(length)`, `numericColumn(precision, scale?)`, `bitColumn(length)`, `varbitColumn(length)`, `timeTemporalColumn(precision?)`, `timeStringColumn(precision?)`, `timetzColumn(precision?)`, `intervalPartsColumn(precision?)`, `intervalStringColumn(precision?)` - Exports raw JSON helpers: - `jsonColumn`, `jsonbColumn` — untyped raw JSON / JSONB column descriptors diff --git a/packages/3-targets/postgres/2-adapter/src/exports/column-types.ts b/packages/3-targets/postgres/2-adapter/src/exports/column-types.ts index 5bc52e8bf54e..16ecfdafa58e 100644 --- a/packages/3-targets/postgres/2-adapter/src/exports/column-types.ts +++ b/packages/3-targets/postgres/2-adapter/src/exports/column-types.ts @@ -18,12 +18,15 @@ import { PG_INT4_CODEC_ID, PG_INT8_CODEC_ID, PG_INTERVAL_CODEC_ID, + PG_INTERVAL_STRING_CODEC_ID, PG_JSON_CODEC_ID, PG_JSONB_CODEC_ID, PG_NUMERIC_CODEC_ID, PG_TEXT_CODEC_ID, PG_TIME_STRING_CODEC_ID, PG_TIME_TEMPORAL_CODEC_ID, + PG_TIMESTAMP_DATE_CODEC_ID, + PG_TIMESTAMP_INSTANT_CODEC_ID, PG_TIMESTAMP_STRING_CODEC_ID, PG_TIMESTAMP_TEMPORAL_CODEC_ID, PG_TIMESTAMPTZ_DATE_CODEC_ID, @@ -147,6 +150,14 @@ export const timestampStringColumn = { codecId: PG_TIMESTAMP_STRING_CODEC_ID, } as const satisfies ColumnTypeDescriptor; +export const timestampJsDateColumn = { + codecId: PG_TIMESTAMP_DATE_CODEC_ID, +} as const satisfies ColumnTypeDescriptor; + +export const timestampInstantColumn = { + codecId: PG_TIMESTAMP_INSTANT_CODEC_ID, +} as const satisfies ColumnTypeDescriptor; + export const timestamptzTemporalColumn = { codecId: PG_TIMESTAMPTZ_TEMPORAL_CODEC_ID, } as const satisfies ColumnTypeDescriptor; @@ -266,18 +277,18 @@ export const byteaColumn = { codecId: PG_BYTEA_CODEC_ID, } as const satisfies ColumnTypeDescriptor; -export function intervalColumn(): ColumnTypeDescriptor; -export function intervalColumn( +export function intervalPartsColumn(): ColumnTypeDescriptor; +export function intervalPartsColumn( precision: Precision, ): ColumnTypeDescriptor & { readonly typeParams: { readonly precision: Precision }; }; -export function intervalColumn(precision?: number): ColumnTypeDescriptor< +export function intervalPartsColumn(precision?: number): ColumnTypeDescriptor< typeof PG_INTERVAL_CODEC_ID > & { readonly typeParams?: { readonly precision: number }; }; -export function intervalColumn(precision?: number): ColumnTypeDescriptor< +export function intervalPartsColumn(precision?: number): ColumnTypeDescriptor< typeof PG_INTERVAL_CODEC_ID > & { readonly typeParams?: { readonly precision: number }; @@ -288,6 +299,28 @@ export function intervalColumn(precision?: number): ColumnTypeDescriptor< } as const; } +export function intervalStringColumn(): ColumnTypeDescriptor; +export function intervalStringColumn( + precision: Precision, +): ColumnTypeDescriptor & { + readonly typeParams: { readonly precision: Precision }; +}; +export function intervalStringColumn(precision?: number): ColumnTypeDescriptor< + typeof PG_INTERVAL_STRING_CODEC_ID +> & { + readonly typeParams?: { readonly precision: number }; +}; +export function intervalStringColumn(precision?: number): ColumnTypeDescriptor< + typeof PG_INTERVAL_STRING_CODEC_ID +> & { + readonly typeParams?: { readonly precision: number }; +} { + return { + codecId: PG_INTERVAL_STRING_CODEC_ID, + ...(precision === undefined ? {} : { typeParams: { precision } }), + } as const; +} + /** * Postgres `json` column descriptor — untyped raw JSON. * diff --git a/packages/3-targets/postgres/2-adapter/test/date-time-column-types.test.ts b/packages/3-targets/postgres/2-adapter/test/date-time-column-types.test.ts new file mode 100644 index 000000000000..be0cbca2f9ac --- /dev/null +++ b/packages/3-targets/postgres/2-adapter/test/date-time-column-types.test.ts @@ -0,0 +1,30 @@ +import { describe, expect, it } from 'vitest'; +import { + intervalPartsColumn, + intervalStringColumn, + timestampInstantColumn, + timestampJsDateColumn, +} from '../src/exports/column-types'; + +describe('the explicit timestamp and interval column helpers', () => { + it('name the codec of each explicit type', () => { + expect({ + timestampJsDate: timestampJsDateColumn, + timestampInstant: timestampInstantColumn, + intervalParts: intervalPartsColumn(), + intervalString: intervalStringColumn(), + }).toEqual({ + timestampJsDate: { codecId: 'pg/timestamp-date@1' }, + timestampInstant: { codecId: 'pg/timestamp-instant@1' }, + intervalParts: { codecId: 'pg/interval@1' }, + intervalString: { codecId: 'pg/interval-string@1' }, + }); + }); + + it('carry an interval precision when one is given', () => { + expect(intervalStringColumn(3)).toEqual({ + codecId: 'pg/interval-string@1', + typeParams: { precision: 3 }, + }); + }); +}); diff --git a/packages/3-targets/postgres/2-adapter/test/migrations/bit-interval-columns.integration.test.ts b/packages/3-targets/postgres/2-adapter/test/migrations/bit-interval-columns.integration.test.ts index f0d230203f29..b1e071c0352b 100644 --- a/packages/3-targets/postgres/2-adapter/test/migrations/bit-interval-columns.integration.test.ts +++ b/packages/3-targets/postgres/2-adapter/test/migrations/bit-interval-columns.integration.test.ts @@ -24,7 +24,7 @@ import { testTimeout, } from './fixtures/runner-fixtures'; -/** What `Bit`, `Bit(8)`, `VarBit`, `VarBit(16)`, `Interval` and `Interval(3)` emit. */ +/** What `Bit`, `Bit(8)`, `VarBit`, `VarBit(16)`, `IntervalParts` and `IntervalParts(3)` emit. */ const COLUMNS = { bit: { dataType: 'pg/bit', codecId: 'pg/bit@1' }, bit8: { dataType: 'pg/bit', codecId: 'pg/bit@1', typeParams: { length: 8 } }, @@ -75,7 +75,7 @@ function buildContract(): Contract { }; } -describe('Bit, VarBit and Interval columns', { concurrent: false }, () => { +describe('Bit, VarBit and IntervalParts columns', { concurrent: false }, () => { let database: Awaited>; let driver: PostgresControlDriver | undefined; diff --git a/packages/3-targets/postgres/4-codec-testkit/test/aggregate-defaults.integration.test.ts b/packages/3-targets/postgres/4-codec-testkit/test/aggregate-defaults.integration.test.ts index a567114531b0..d93f44311434 100644 --- a/packages/3-targets/postgres/4-codec-testkit/test/aggregate-defaults.integration.test.ts +++ b/packages/3-targets/postgres/4-codec-testkit/test/aggregate-defaults.integration.test.ts @@ -118,6 +118,7 @@ describe('PostgreSQL aggregate defaults', { concurrent: false }, () => { 'sum(pg/float4@1)': output('sum', 'pg/float4@1'), 'sum(pg/float8@1)': output('sum', 'pg/float8@1'), 'sum(pg/interval@1)': output('sum', 'pg/interval@1'), + 'sum(pg/interval-string@1)': output('sum', 'pg/interval-string@1'), 'sumBigInt(pg/int2@1)': output('sumBigInt', 'pg/int2@1'), 'sumBigInt(pg/int4@1)': output('sumBigInt', 'pg/int4@1'), 'sumBigInt(pg/int8@1)': output('sumBigInt', 'pg/int8@1'), @@ -130,6 +131,7 @@ describe('PostgreSQL aggregate defaults', { concurrent: false }, () => { 'avg(pg/numeric@1)': output('avg', 'pg/numeric@1'), 'avg(pg/float4@1)': output('avg', 'pg/float4@1'), 'avg(pg/interval@1)': output('avg', 'pg/interval@1'), + 'avg(pg/interval-string@1)': output('avg', 'pg/interval-string@1'), 'avgDecimal(pg/int4@1)': output('avgDecimal', 'pg/int4@1'), 'avgDecimal(pg/int8@1)': output('avgDecimal', 'pg/int8@1'), 'avgDecimal(pg/unboundedint@1)': output('avgDecimal', 'pg/unboundedint@1'), @@ -150,6 +152,7 @@ describe('PostgreSQL aggregate defaults', { concurrent: false }, () => { 'sum(pg/float4@1)': 'pg/float4@1', 'sum(pg/float8@1)': 'pg/float8@1', 'sum(pg/interval@1)': 'pg/interval@1', + 'sum(pg/interval-string@1)': 'pg/interval-string@1', 'sumBigInt(pg/int2@1)': 'pg/int8@1', 'sumBigInt(pg/int4@1)': 'pg/int8@1', 'sumBigInt(pg/int8@1)': 'pg/unboundedint@1', @@ -162,6 +165,7 @@ describe('PostgreSQL aggregate defaults', { concurrent: false }, () => { 'avg(pg/numeric@1)': 'pg/numeric@1', 'avg(pg/float4@1)': 'pg/float8@1', 'avg(pg/interval@1)': 'pg/interval@1', + 'avg(pg/interval-string@1)': 'pg/interval-string@1', 'avgDecimal(pg/int4@1)': 'pg/numeric@1', 'avgDecimal(pg/int8@1)': 'pg/numeric@1', 'avgDecimal(pg/unboundedint@1)': 'pg/numeric@1', diff --git a/packages/3-targets/postgres/4-codec-testkit/test/aggregate-matrix.ts b/packages/3-targets/postgres/4-codec-testkit/test/aggregate-matrix.ts index 84d2f3341ebc..ed7ccf080176 100644 --- a/packages/3-targets/postgres/4-codec-testkit/test/aggregate-matrix.ts +++ b/packages/3-targets/postgres/4-codec-testkit/test/aggregate-matrix.ts @@ -159,6 +159,16 @@ export const FIXTURES: readonly AggregateFixture[] = [ typeParams: { precision: 6 }, samples: ["'2024-01-01T10:00:00.123456'", "'2024-02-01T10:00:00.654321'"], }, + { + codecId: 'pg/timestamp-date@1', + typeParams: { precision: 3 }, + samples: ["'2024-01-01T10:00:00.123'", "'2024-02-01T10:00:00.654'"], + }, + { + codecId: 'pg/timestamp-instant@1', + typeParams: { precision: 6 }, + samples: ["'2024-01-01T10:00:00.123456'", "'2024-02-01T10:00:00.654321'"], + }, { codecId: 'pg/timestamptz-date@1', typeParams: { precision: 3 }, @@ -186,6 +196,7 @@ export const FIXTURES: readonly AggregateFixture[] = [ { codecId: 'pg/inet@1', samples: ["'10.0.0.1'", "'10.0.0.2'"] }, { codecId: 'pg/tsquery@1', samples: ["'graze'", "'zebra'"] }, { codecId: 'pg/interval@1', samples: ["'1 day'", "'2 days'"] }, + { codecId: 'pg/interval-string@1', samples: ["'1 day'", "'2 days 00:00:00.5'"] }, { codecId: 'pg/json@1', samples: ['\'{"a":1}\'', '\'{"b":2}\''] }, { codecId: 'pg/jsonb@1', samples: ['\'{"a":1}\'', '\'{"b":2}\''] }, { codecId: 'pg/text-array@1', samples: ["ARRAY['a']", "ARRAY['b']"] }, diff --git a/packages/3-targets/postgres/4-codec-testkit/test/codec-conformance/cases.ts b/packages/3-targets/postgres/4-codec-testkit/test/codec-conformance/cases.ts index dff64d8226e5..68d42c0e1ffd 100644 --- a/packages/3-targets/postgres/4-codec-testkit/test/codec-conformance/cases.ts +++ b/packages/3-targets/postgres/4-codec-testkit/test/codec-conformance/cases.ts @@ -29,6 +29,11 @@ const instantsEqual = (roundTripped: unknown, value: unknown): boolean => value instanceof Temporal.Instant && roundTripped.equals(value); +const datesEqual = (roundTripped: unknown, value: unknown): boolean => + roundTripped instanceof Date && + value instanceof Date && + roundTripped.getTime() === value.getTime(); + const plainDateTimesEqual = (roundTripped: unknown, value: unknown): boolean => roundTripped instanceof Temporal.PlainDateTime && value instanceof Temporal.PlainDateTime && @@ -276,6 +281,53 @@ export const postgresConformanceCases: readonly PostgresCodecConformanceCase[] = projectionEquality: (left, right) => left instanceof Date && right instanceof Date && left.getTime() === right.getTime(), }, + // A timestamp read as a Date or a Temporal.Instant names the stored date and time in UTC, so a + // hostile session time zone must not move it. + { + codecId: 'pg/timestamp-date@1', + label: 'millisecond precision', + value: new Date('2026-01-02T03:04:05.123Z'), + typeParams: { precision: 3 }, + valueEquality: datesEqual, + projectionEquality: datesEqual, + }, + { + codecId: 'pg/timestamp-date@1', + label: 'a year before Christ under a hostile time zone', + value: new Date('-000043-03-15T12:00:00.000Z'), + setupSql: ["SET TimeZone = 'Asia/Kolkata'"], + valueEquality: datesEqual, + projectionEquality: datesEqual, + }, + { + codecId: 'pg/timestamp-date@1', + label: 'null', + value: undefined, + typeParams: { precision: 3 }, + nullValue: true, + }, + { + codecId: 'pg/timestamp-instant@1', + label: 'microsecond precision', + value: Temporal.Instant.from('2026-01-02T03:04:05.123456Z'), + typeParams: { precision: 6 }, + valueEquality: instantsEqual, + projectionEquality: instantsEqual, + }, + { + codecId: 'pg/timestamp-instant@1', + label: 'a year past 9999 under a hostile time zone', + value: Temporal.Instant.from('+012026-01-02T03:04:05Z'), + setupSql: ["SET TimeZone = 'Asia/Kolkata'"], + valueEquality: instantsEqual, + projectionEquality: instantsEqual, + }, + { + codecId: 'pg/timestamp-instant@1', + label: 'null', + value: undefined, + nullValue: true, + }, // The `*-string` codecs' application value is PostgreSQL's own rendering, so each case is written // the way the server writes it — space separator, two-digit offset, microseconds. That is now // what the projection returns too, which is the whole point of the text cast: one spelling, @@ -396,6 +448,24 @@ export const postgresConformanceCases: readonly PostgresCodecConformanceCase[] = value: interval({ months: 1, days: -1 }), setupSql: HOSTILE_TEMPORAL_SESSION, }, + // An interval read as a string is the ISO 8601 duration pg/interval stores, whatever the session's IntervalStyle. + { codecId: 'pg/interval-string@1', label: 'one month', value: 'P1M' }, + { codecId: 'pg/interval-string@1', label: 'every component', value: 'P1Y2M3DT4H5M6.5S' }, + { codecId: 'pg/interval-string@1', label: 'mixed signs', value: 'P1M-1DT-1H' }, + { codecId: 'pg/interval-string@1', label: 'zero', value: 'PT0S' }, + ...(['postgres_verbose', 'sql_standard', 'iso_8601'] as const).map((style) => ({ + codecId: 'pg/interval-string@1', + label: `mixed signs under IntervalStyle ${style}`, + value: 'P1Y2M-3DT4H', + setupSql: [`SET IntervalStyle = '${style}'`], + })), + { + codecId: 'pg/interval-string@1', + label: 'every component under a hostile session', + value: 'P1Y2M3DT4H5M6.5S', + setupSql: HOSTILE_TEMPORAL_SESSION, + }, + { codecId: 'pg/interval-string@1', label: 'null', value: undefined, nullValue: true }, { codecId: 'pg/json@1', label: 'document', value: { a: 1, b: ['x'] } }, { codecId: 'pg/jsonb@1', label: 'document', value: { a: 1, b: ['x'] } }, // A document that is a string is where a JSON value and the JSON text of a document differ: the diff --git a/packages/3-targets/postgres/4-codec-testkit/test/timestamp-utc-now-defaults.integration.test.ts b/packages/3-targets/postgres/4-codec-testkit/test/timestamp-utc-now-defaults.integration.test.ts new file mode 100644 index 000000000000..d1f3279ae92a --- /dev/null +++ b/packages/3-targets/postgres/4-codec-testkit/test/timestamp-utc-now-defaults.integration.test.ts @@ -0,0 +1,77 @@ +import { postgresCodecDescriptorRegistry } from '@internal/target-postgres/codecs'; +import { createDevDatabase, timeouts } from '@repo/test-utils'; +import { afterAll, beforeAll, describe, expect, it } from 'vitest'; +import { connectRuntimeDriver } from './runtime-connection'; + +const KOLKATA_OFFSET_MILLISECONDS = (5 * 60 + 30) * 60 * 1000; +const SLACK_MILLISECONDS = 5_000; + +const codecFor = (codecId: string) => + postgresCodecDescriptorRegistry.descriptorFor(codecId)!.factory(undefined)({ + name: 'timestamp-utc-now-defaults', + }); + +const epochMillisecondsOf = (value: unknown): number => + value instanceof Date ? value.getTime() : Number(Reflect.get(Object(value), 'epochMilliseconds')); + +describe.each(['pg/timestamp-date@1', 'pg/timestamp-instant@1'])( + '%s under a session whose TimeZone is Asia/Kolkata', + (codecId) => { + let database: Awaited> | undefined; + let runtime: Awaited> | undefined; + const codec = codecFor(codecId); + + beforeAll(async () => { + database = await createDevDatabase(); + runtime = await connectRuntimeDriver(database.connectionString); + await runtime.connection.query("SET TimeZone = 'Asia/Kolkata'"); + await runtime.connection.query( + 'create table stamps (id int primary key, v timestamp(3) not null default now())', + ); + }, timeouts.spinUpPpgDev); + + afterAll(async () => { + await runtime?.close(); + await database?.close(); + database = undefined; + }, timeouts.spinUpPpgDev); + + async function readBack(id: number): Promise { + const rows = await runtime!.connection.query('select v from stamps where id = $1', [id]); + return epochMillisecondsOf(await codec.fromWire(rows[0]?.['v'], {})); + } + + it( + 'reads the database default now(), which PostgreSQL fills in the session time zone, shifted by that zone’s offset', + async () => { + const before = Date.now(); + await runtime!.connection.query('insert into stamps (id) values (1)'); + const after = Date.now(); + + const shift = (await readBack(1)) - after; + expect(shift).toBeGreaterThan( + KOLKATA_OFFSET_MILLISECONDS - (after - before) - SLACK_MILLISECONDS, + ); + expect(shift).toBeLessThan(KOLKATA_OFFSET_MILLISECONDS + SLACK_MILLISECONDS); + }, + timeouts.spinUpPpgDev, + ); + + it( + 'reads a client now(), written through the codec as @onCreate(now()) writes it, as the same instant', + async () => { + const written = new Date(Date.now() - (Date.now() % 1000)); + const value = + codecId === 'pg/timestamp-date@1' + ? written + : Temporal.Instant.fromEpochMilliseconds(written.getTime()); + await runtime!.connection.query('insert into stamps (id, v) values (2, $1)', [ + await codec.toWire(value, {}), + ]); + + expect(await readBack(2)).toBe(written.getTime()); + }, + timeouts.spinUpPpgDev, + ); + }, +); diff --git a/packages/3-targets/postgres/4-codec-testkit/test/timestamp-utc-runtime-read.integration.test.ts b/packages/3-targets/postgres/4-codec-testkit/test/timestamp-utc-runtime-read.integration.test.ts new file mode 100644 index 000000000000..4a48da993c55 --- /dev/null +++ b/packages/3-targets/postgres/4-codec-testkit/test/timestamp-utc-runtime-read.integration.test.ts @@ -0,0 +1,78 @@ +import { postgresCodecDescriptorRegistry } from '@internal/target-postgres/codecs'; +import { createDevDatabase, timeouts } from '@repo/test-utils'; +import { afterAll, beforeAll, describe, expect, it } from 'vitest'; +import { connectRuntimeDriver } from './runtime-connection'; + +const codecFor = (codecId: string) => + postgresCodecDescriptorRegistry.descriptorFor(codecId)!.factory(undefined)({ + name: 'timestamp-utc-runtime-read', + }); + +describe('the timestamp codecs that read UTC, on values the runtime driver returns', () => { + let database: Awaited> | undefined; + let runtime: Awaited> | undefined; + + beforeAll(async () => { + database = await createDevDatabase(); + runtime = await connectRuntimeDriver(database.connectionString); + await runtime.connection.query('create table stamps (label text primary key, v timestamp)'); + await runtime.connection.query(`insert into stamps values + ('ordinary', timestamp '2026-01-02 03:04:05.123456'), + ('before Christ', timestamp '0044-03-15 12:00:00 BC'), + ('infinity', 'infinity'), + ('minus infinity', '-infinity'), + ('past the last instant', timestamp '275760-09-13 12:00:00')`); + }, timeouts.spinUpPpgDev); + + afterAll(async () => { + await runtime?.close(); + await database?.close(); + database = undefined; + }, timeouts.spinUpPpgDev); + + async function wireOf(label: string): Promise { + await runtime!.connection.query("SET TimeZone = 'Asia/Kolkata'"); + const rows = await runtime!.connection.query('select v from stamps where label = $1', [label]); + return rows[0]?.['v']; + } + + it( + 'read the stored date and time as UTC under a session in another time zone', + async () => { + const ordinary = await wireOf('ordinary'); + const beforeChrist = await wireOf('before Christ'); + const date = codecFor('pg/timestamp-date@1'); + const instant = codecFor('pg/timestamp-instant@1'); + + expect({ + date: await date.fromWire(ordinary, {}), + dateBeforeChrist: await date.fromWire(beforeChrist, {}), + instant: String(await instant.fromWire(ordinary, {})), + instantBeforeChrist: String(await instant.fromWire(beforeChrist, {})), + }).toEqual({ + date: new Date('2026-01-02T03:04:05.123Z'), + dateBeforeChrist: new Date('-000043-03-15T12:00:00.000Z'), + instant: '2026-01-02T03:04:05.123456Z', + instantBeforeChrist: '-000043-03-15T12:00:00Z', + }); + }, + timeouts.spinUpPpgDev, + ); + + it.each([ + ['infinity', 'pg/timestamp-date@1'], + ['minus infinity', 'pg/timestamp-date@1'], + ['past the last instant', 'pg/timestamp-date@1'], + ['infinity', 'pg/timestamp-instant@1'], + ['minus infinity', 'pg/timestamp-instant@1'], + ['past the last instant', 'pg/timestamp-instant@1'], + ])( + 'refuse the stored %s through %s, naming TimestampString', + async (label, codecId) => { + await expect(codecFor(codecId).fromWire(await wireOf(label), {})).rejects.toThrow( + /TimestampString/, + ); + }, + timeouts.spinUpPpgDev, + ); +}); diff --git a/packages/3-targets/postgres/9-facade/package.json b/packages/3-targets/postgres/9-facade/package.json index 9a1579aeec46..22444965fb20 100644 --- a/packages/3-targets/postgres/9-facade/package.json +++ b/packages/3-targets/postgres/9-facade/package.json @@ -11,7 +11,7 @@ "emit": "cd ../../../../test/integration && node ../../packages/1-framework/3-tooling/cli/dist/bin.mjs contract emit --config test/sql-builder/fixtures/prisma.config.no-pgvector.ts && cp test/sql-builder/fixtures/generated-no-pgvector/contract.json test/sql-builder/fixtures/generated-no-pgvector/contract.d.ts ../../packages/3-targets/postgres/9-facade/test/fixtures/generated/", "emit:check": "pnpm emit && git diff --exit-code test/fixtures/generated/", "test": "vitest run", - "typecheck": "tsc --project tsconfig.json --noEmit && tsc --project tsconfig.no-exact-optional.json --noEmit", + "typecheck": "tsc --project tsconfig.json --noEmit && tsc --project tsconfig.no-exact-optional.json --noEmit && tsc --project tsconfig.no-temporal-global.json --noEmit", "lint": "biome check . --error-on-warnings", "lint:fix": "biome check --write .", "lint:fix:unsafe": "biome check --write --unsafe .", @@ -40,6 +40,7 @@ "arktype": "catalog:", "pathe": "^2.0.3", "pg": "catalog:", + "temporal-spec": "1.0.1", "tinyglobby": "catalog:" }, "devDependencies": { diff --git a/packages/3-targets/postgres/9-facade/src/exports/contract-builder.ts b/packages/3-targets/postgres/9-facade/src/exports/contract-builder.ts index 8b639edfdbe4..9cc5dcbf5823 100644 --- a/packages/3-targets/postgres/9-facade/src/exports/contract-builder.ts +++ b/packages/3-targets/postgres/9-facade/src/exports/contract-builder.ts @@ -30,7 +30,6 @@ export { export type { Bit, Char, - Interval, Numeric, TimeString, TimestampString, diff --git a/packages/3-targets/postgres/9-facade/test/contract-builder/type-helpers.test-d.ts b/packages/3-targets/postgres/9-facade/test/contract-builder/type-helpers.test-d.ts index 5e8d60c9c74d..959d05da48df 100644 --- a/packages/3-targets/postgres/9-facade/test/contract-builder/type-helpers.test-d.ts +++ b/packages/3-targets/postgres/9-facade/test/contract-builder/type-helpers.test-d.ts @@ -45,7 +45,7 @@ test('a field typed by temporal.CreatedAt is optional on create', () => { defineContract({}, ({ type, field }) => { const createdAt = field.column(type.temporal.CreatedAt()); const updatedAt = field.column(type.temporal.UpdatedAtString()).optional(); - const plain = field.column(type.Timestamptz()); + const plain = field.column(type.TimestamptzTemporal()); expectTypeOf>().toEqualTypeOf(); expectTypeOf>().toEqualTypeOf(); expectTypeOf>().toEqualTypeOf(); diff --git a/packages/3-targets/postgres/9-facade/test/fixtures/generated/contract.d.ts b/packages/3-targets/postgres/9-facade/test/fixtures/generated/contract.d.ts index 80b3d3eda96a..7a2058a0a1fe 100644 --- a/packages/3-targets/postgres/9-facade/test/fixtures/generated/contract.d.ts +++ b/packages/3-targets/postgres/9-facade/test/fixtures/generated/contract.d.ts @@ -43,6 +43,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; @@ -92,6 +96,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; @@ -101,6 +109,14 @@ export type AggregateTypes = { readonly output: 'pg/time-temporal@1'; readonly nullable: true; }; + readonly 'pg/timestamp-date@1': { + readonly output: 'pg/timestamp-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-instant@1': { + readonly output: 'pg/timestamp-instant@1'; + readonly nullable: true; + }; readonly 'pg/timestamp-string@1': { readonly output: 'pg/timestamp-string@1'; readonly nullable: true; @@ -152,6 +168,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; @@ -161,6 +181,14 @@ export type AggregateTypes = { readonly output: 'pg/time-temporal@1'; readonly nullable: true; }; + readonly 'pg/timestamp-date@1': { + readonly output: 'pg/timestamp-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-instant@1': { + readonly output: 'pg/timestamp-instant@1'; + readonly nullable: true; + }; readonly 'pg/timestamp-string@1': { readonly output: 'pg/timestamp-string@1'; readonly nullable: true; @@ -204,6 +232,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; diff --git a/packages/3-targets/postgres/9-facade/test/native-type-parity.test.ts b/packages/3-targets/postgres/9-facade/test/native-type-parity.test.ts index 005827377c4f..0969fdc1084b 100644 --- a/packages/3-targets/postgres/9-facade/test/native-type-parity.test.ts +++ b/packages/3-targets/postgres/9-facade/test/native-type-parity.test.ts @@ -156,9 +156,9 @@ const parityCases: readonly ParityCase[] = [ expected: { ...numericOut, typeParams: {} }, }, { - title: 'Timestamp(3)', - bare: 'Timestamp(3)', - alias: 'Timestamp(3)', + title: 'TimestampTemporal(3)', + bare: 'TimestampTemporal(3)', + alias: 'TimestampTemporal(3)', expected: { dataType: 'pg/timestamp', codecId: 'pg/timestamp-temporal@1', @@ -166,15 +166,15 @@ const parityCases: readonly ParityCase[] = [ }, }, { - title: 'Timestamp — bare', - bare: 'Timestamp', - alias: 'Timestamp', + title: 'TimestampTemporal — bare', + bare: 'TimestampTemporal', + alias: 'TimestampTemporal', expected: { dataType: 'pg/timestamp', codecId: 'pg/timestamp-temporal@1', typeParams: {} }, }, { - title: 'Timestamptz(6)', - bare: 'Timestamptz(6)', - alias: 'Timestamptz(6)', + title: 'TimestamptzTemporal(6)', + bare: 'TimestamptzTemporal(6)', + alias: 'TimestamptzTemporal(6)', expected: { dataType: 'pg/timestamptz', codecId: 'pg/timestamptz-temporal@1', @@ -182,9 +182,9 @@ const parityCases: readonly ParityCase[] = [ }, }, { - title: 'Timestamptz — bare', - bare: 'Timestamptz', - alias: 'Timestamptz', + title: 'TimestamptzTemporal — bare', + bare: 'TimestamptzTemporal', + alias: 'TimestamptzTemporal', expected: { dataType: 'pg/timestamptz', codecId: 'pg/timestamptz-temporal@1', typeParams: {} }, }, { @@ -306,11 +306,11 @@ const bitAndIntervalCases = [ ['Bit(8)', 'pg/bit', 'pg/bit@1', { length: 8 }], ['VarBit', 'pg/varbit', 'pg/varbit@1', {}], ['VarBit(16)', 'pg/varbit', 'pg/varbit@1', { length: 16 }], - ['Interval', 'pg/interval', 'pg/interval@1', {}], - ['Interval(3)', 'pg/interval', 'pg/interval@1', { precision: 3 }], + ['IntervalParts', 'pg/interval', 'pg/interval@1', {}], + ['IntervalParts(3)', 'pg/interval', 'pg/interval@1', { precision: 3 }], ] as const; -describe('Bit, VarBit and Interval', () => { +describe('Bit, VarBit and IntervalParts', () => { it.each(bitAndIntervalCases)( '%s gives its codec and parameters as a scalar and as a list', (spelling, dataType, codecId, typeParams) => { @@ -501,7 +501,7 @@ model sample { it('rejects a non-integer precision, which pg/int4 cannot hold', () => { const result = emit(`model sample { id Int @id - at Timestamp(1.5) + at TimestampTemporal(1.5) } `); expect(result.ok).toBe(false); diff --git a/packages/3-targets/postgres/9-facade/test/no-temporal-global/temporal-field-types.test-d.ts b/packages/3-targets/postgres/9-facade/test/no-temporal-global/temporal-field-types.test-d.ts new file mode 100644 index 000000000000..0f7e47614d34 --- /dev/null +++ b/packages/3-targets/postgres/9-facade/test/no-temporal-global/temporal-field-types.test-d.ts @@ -0,0 +1,55 @@ +import type { DefaultModelRow } from '@internal/sql-orm-client'; +import type { CodecTypes } from '@internal/target-postgres/codec-types'; +import type { Temporal } from 'temporal-spec'; +import { expectTypeOf, test } from 'vitest'; +import { defineContract } from '../../src/exports/contract-builder'; + +const contract = defineContract({}, ({ type, field, model }) => ({ + models: { + Event: model('Event', { + fields: { + id: field.id.uuidv4String(), + at: field.column(type.TimestamptzTemporal(3)), + local: field.column(type.TimestampTemporal()), + day: field.column(type.DateTemporal()), + opens: field.column(type.TimeTemporal(0)), + legacyDay: field.column(type.Date()), + utcAt: field.column(type.TimestampInstant(6)), + utcDate: field.column(type.TimestampJsDate(3)), + span: field.column(type.IntervalString(3)), + }, + }), + }, +})); + +type EventRow = DefaultModelRow; + +test('a Temporal field is typed precisely in a project without the global Temporal declarations', () => { + expectTypeOf().not.toBeAny(); + expectTypeOf().toEqualTypeOf(); + expectTypeOf().toEqualTypeOf(); + expectTypeOf().toEqualTypeOf(); + expectTypeOf().toEqualTypeOf(); + expectTypeOf().toEqualTypeOf(); + expectTypeOf().not.toBeAny(); + expectTypeOf().toEqualTypeOf(); +}); + +test('every Temporal codec names its type through an import', () => { + expectTypeOf().not.toBeAny(); + expectTypeOf< + CodecTypes['pg/timestamptz-temporal@1']['output'] + >().toEqualTypeOf(); + expectTypeOf< + CodecTypes['pg/timestamp-temporal@1']['output'] + >().toEqualTypeOf(); + expectTypeOf().toEqualTypeOf(); + expectTypeOf().toEqualTypeOf(); + expectTypeOf().not.toBeAny(); + expectTypeOf().toEqualTypeOf(); +}); + +test('a timestamp read as a Date reads a Date, and an interval read as a string reads a string', () => { + expectTypeOf().toEqualTypeOf(); + expectTypeOf().toEqualTypeOf(); +}); diff --git a/packages/3-targets/postgres/9-facade/test/scalar-type-parity.test.ts b/packages/3-targets/postgres/9-facade/test/scalar-type-parity.test.ts index 502dcabcf8cb..c61dc8382f4c 100644 --- a/packages/3-targets/postgres/9-facade/test/scalar-type-parity.test.ts +++ b/packages/3-targets/postgres/9-facade/test/scalar-type-parity.test.ts @@ -75,14 +75,16 @@ describe('postgres scalar types derived from the unified namespace', () => { VarChar: { codecId: 'sql/varchar@1' }, Char: { codecId: 'sql/char@1' }, Numeric: { codecId: 'pg/numeric@1' }, - Timestamp: { codecId: 'pg/timestamp-temporal@1' }, - Timestamptz: { codecId: 'pg/timestamptz-temporal@1' }, + TimestampTemporal: { codecId: 'pg/timestamp-temporal@1' }, + TimestamptzTemporal: { codecId: 'pg/timestamptz-temporal@1' }, Time: { codecId: 'pg/time-temporal@1' }, + TimeTemporal: { codecId: 'pg/time-temporal@1' }, Timetz: { codecId: 'pg/timetz@1' }, Uuid: { codecId: 'pg/uuid@1' }, SmallInt: { codecId: 'pg/int2@1' }, Real: { codecId: 'pg/float4@1' }, Date: { codecId: 'pg/date-temporal@1' }, + DateTemporal: { codecId: 'pg/date-temporal@1' }, DateString: { codecId: 'pg/date-string@1' }, TimestampString: { codecId: 'pg/timestamp-string@1' }, TimestamptzString: { codecId: 'pg/timestamptz-string@1' }, @@ -90,7 +92,10 @@ describe('postgres scalar types derived from the unified namespace', () => { TimeString: { codecId: 'pg/time-string@1' }, Bit: { codecId: 'pg/bit@1' }, VarBit: { codecId: 'pg/varbit@1' }, - Interval: { codecId: 'pg/interval@1' }, + IntervalParts: { codecId: 'pg/interval@1' }, + IntervalString: { codecId: 'pg/interval-string@1' }, + TimestampJsDate: { codecId: 'pg/timestamp-date@1' }, + TimestampInstant: { codecId: 'pg/timestamp-instant@1' }, }); }); @@ -104,12 +109,14 @@ describe('postgres scalar types derived from the unified namespace', () => { 'Char', 'Date', 'DateString', + 'DateTemporal', 'DateTime', 'Decimal', 'Float', 'Inet', 'Int', - 'Interval', + 'IntervalParts', + 'IntervalString', 'Json', 'Jsonb', 'Numeric', @@ -118,11 +125,14 @@ describe('postgres scalar types derived from the unified namespace', () => { 'String', 'Time', 'TimeString', - 'Timestamp', + 'TimeTemporal', + 'TimestampInstant', + 'TimestampJsDate', 'TimestampString', - 'Timestamptz', + 'TimestampTemporal', 'TimestamptzJsDate', 'TimestamptzString', + 'TimestamptzTemporal', 'Timetz', 'UnboundedInt', 'Uuid', diff --git a/packages/3-targets/postgres/9-facade/tsconfig.no-temporal-global.json b/packages/3-targets/postgres/9-facade/tsconfig.no-temporal-global.json new file mode 100644 index 000000000000..4ca6a1fd62ee --- /dev/null +++ b/packages/3-targets/postgres/9-facade/tsconfig.no-temporal-global.json @@ -0,0 +1,4 @@ +{ + "extends": "./tsconfig.json", + "include": ["test/no-temporal-global/**/*.ts"] +} diff --git a/packages/3-targets/sqlite/9-facade/test/contract-builder/type-helpers.test-d.ts b/packages/3-targets/sqlite/9-facade/test/contract-builder/type-helpers.test-d.ts index 83e579f4cba7..68b2ee4a6711 100644 --- a/packages/3-targets/sqlite/9-facade/test/contract-builder/type-helpers.test-d.ts +++ b/packages/3-targets/sqlite/9-facade/test/contract-builder/type-helpers.test-d.ts @@ -4,7 +4,7 @@ import { defineContract } from '../../src/exports/contract-builder'; test('type.* has a helper for every SQLite PSL type constructor and no Postgres one', () => { defineContract({}, ({ type }) => { expectTypeOf(type.DateTime().codecId).toEqualTypeOf<'sqlite/datetime@1'>(); - expectTypeOf(type).not.toHaveProperty('Timestamptz'); + expectTypeOf(type).not.toHaveProperty('TimestamptzTemporal'); return { models: {} }; }); }); diff --git a/packages/9-public/@prisma/orm-postgres/package.json b/packages/9-public/@prisma/orm-postgres/package.json index ca2c2c64a8ed..5388f6c28331 100644 --- a/packages/9-public/@prisma/orm-postgres/package.json +++ b/packages/9-public/@prisma/orm-postgres/package.json @@ -25,6 +25,7 @@ "arktype": "catalog:", "pathe": "^2.0.3", "pg": "catalog:", + "temporal-spec": "1.0.1", "tinyglobby": "catalog:" }, "devDependencies": { diff --git a/packages/9-public/@prisma/orm-target-postgres/package.json b/packages/9-public/@prisma/orm-target-postgres/package.json index dd5eaa80854b..382b44f8eef6 100644 --- a/packages/9-public/@prisma/orm-target-postgres/package.json +++ b/packages/9-public/@prisma/orm-target-postgres/package.json @@ -25,7 +25,8 @@ "pathe": "^2.0.3", "pg": "catalog:", "pg-cursor": "^2.21.0", - "postgres-array": "2.0.0" + "postgres-array": "2.0.0", + "temporal-spec": "1.0.1" }, "devDependencies": { "@internal/adapter-postgres": "workspace:8.0.0-rc.20", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index d7f7f310a654..47583738a745 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -607,6 +607,9 @@ importers: temporal-polyfill: specifier: ^1.0.4 version: 1.0.4 + temporal-spec: + specifier: 1.0.1 + version: 1.0.1 tsx: specifier: 'catalog:' version: 4.23.12 @@ -4153,6 +4156,9 @@ importers: postgres-array: specifier: 2.0.0 version: 2.0.0 + temporal-spec: + specifier: 1.0.1 + version: 1.0.1 devDependencies: '@electric-sql/pglite': specifier: 0.5.4 @@ -4475,6 +4481,9 @@ importers: pg: specifier: 'catalog:' version: 8.22.0 + temporal-spec: + specifier: 1.0.1 + version: 1.0.1 tinyglobby: specifier: 'catalog:' version: 0.2.17 @@ -5358,6 +5367,9 @@ importers: temporal-polyfill: specifier: ^1.0.4 version: 1.0.4 + temporal-spec: + specifier: 1.0.1 + version: 1.0.1 tinyglobby: specifier: 'catalog:' version: 0.2.17 @@ -5496,6 +5508,9 @@ importers: temporal-polyfill: specifier: ^1.0.4 version: 1.0.4 + temporal-spec: + specifier: 1.0.1 + version: 1.0.1 devDependencies: '@internal/adapter-postgres': specifier: workspace:8.0.0-rc.20 @@ -5969,6 +5984,9 @@ importers: temporal-polyfill: specifier: ^1.0.4 version: 1.0.4 + temporal-spec: + specifier: 1.0.1 + version: 1.0.1 typescript: specifier: 'catalog:' version: 5.9.3 diff --git a/skills/prisma-8/references/contract.md b/skills/prisma-8/references/contract.md index d6b720eae24a..129ff86b6e32 100644 --- a/skills/prisma-8/references/contract.md +++ b/skills/prisma-8/references/contract.md @@ -117,7 +117,7 @@ model Post { Then run `pnpm prisma contract emit` (or rely on the Vite plugin — see `references/build.md`). Specify cascade behaviour explicitly with `onDelete` / `onUpdate`; the default is `Restrict`. -**Temporal columns.** On PostgreSQL, `Date`, `Timestamp(p)`, `Timestamptz(p)` and `Time(p)` read and write `Temporal` values (`Temporal.PlainDate`, `PlainDateTime`, `Instant`, `PlainTime`), never JavaScript `Date`. They need a global `Temporal` at query time: Node.js 26.8.2 and later ship `globalThis.Temporal`; 26.8.1 and earlier — including every 22 and 24 release — do not, and the first read or write of such a column throws `RUNTIME.TEMPORAL_UNAVAILABLE`. On those runtimes either `import 'temporal-polyfill/full/global'` before the first query (add `temporal-polyfill` as a dependency) or author the column as `DateString` / `TimestampString(p)` / `TimestamptzString(p)` / `TimeString(p)`, which carry PostgreSQL's own text and need no `Temporal`. `prisma contract emit`, `prisma db init` and the other commands need no global `Temporal`: the Postgres target loads `temporal-polyfill` as a fallback `Temporal` for the control plane, and sets no global. `temporal-polyfill` is a required peer dependency of `@prisma/orm-postgres`. npm, pnpm and bun install it automatically. With Yarn, add `temporal-polyfill` (`^1.0.4`) to the project's dependencies, or the commands fail because Node.js cannot find the package. That fallback is held once per process. An application that loads control-plane code in its own process, such as server code under `vite dev` with the Prisma Vite plugin or a script that calls the control client, decodes dates without its own `Temporal`, and then fails in production. An application that uses the Temporal codecs must load its own `Temporal`. A TypeScript contract file that constructs a `Temporal` value must load an implementation too. +**Temporal columns.** On PostgreSQL, `DateTemporal`, `TimestampTemporal(p)`, `TimestampInstant(p)`, `TimestamptzTemporal(p)` and `TimeTemporal(p)` (and `Date`, `DateTime` and `Time`, which today mean the same as `DateTemporal`, `TimestamptzTemporal` and `TimeTemporal`) read and write `Temporal` values (`Temporal.PlainDate`, `PlainDateTime`, `Instant` for both instant types, `PlainTime`), never JavaScript `Date`. They need a global `Temporal` at query time: Node.js 26.8.2 and later ship `globalThis.Temporal`; 26.8.1 and earlier — including every 22 and 24 release — do not, and the first read or write of such a column throws `RUNTIME.TEMPORAL_UNAVAILABLE`. On those runtimes either `import 'temporal-polyfill/full/global'` before the first query (add `temporal-polyfill` as a dependency) or author the column as `DateString` / `TimestampString(p)` / `TimestamptzString(p)` / `TimeString(p)`, which carry text and need no `Temporal`, or as `TimestamptzJsDate(p)` / `TimestampJsDate(p)`, which read a JavaScript `Date`. `IntervalParts(p)` reads `{ months, days, micros }` and `IntervalString(p)` an ISO 8601 duration such as `P1Y2M`. `prisma contract emit`, `prisma db init` and the other commands need no global `Temporal`: the Postgres target loads `temporal-polyfill` as a fallback `Temporal` for the control plane, and sets no global. `temporal-polyfill` is a required peer dependency of `@prisma/orm-postgres`. npm, pnpm and bun install it automatically. With Yarn, add `temporal-polyfill` (`^1.0.4`) to the project's dependencies, or the commands fail because Node.js cannot find the package. That fallback is held once per process. An application that loads control-plane code in its own process, such as server code under `vite dev` with the Prisma Vite plugin or a script that calls the control client, decodes dates without its own `Temporal`, and then fails in production. An application that uses the Temporal codecs must load its own `Temporal`. A TypeScript contract file that constructs a `Temporal` value must load an implementation too. `@@index` also accepts `expression:` (instead of a fields list), `where:` (partial-index predicate), `unique:`, `type:`/`options:` (target-registered access method), and `name:` xor `map:`. Raw SQL in `expression:` and `where:` is a `sql` literal; a quoted string is refused with `PSL_VALUE_TYPE_INCOMPATIBLE`, whose message gives the rewrite: @@ -156,7 +156,7 @@ model User { } ``` -An alias is expanded into each field that uses it: the column stores the alias's codec and type params, and the contract stores no alias. An alias may carry `@default`, `@onCreate` and `@onUpdate`, which every field typed by it takes: `UpdatedAt = TimestampString(3) @onCreate(now()) @onUpdate(now())`. It may not carry `@id` or `@unique`; write those on the field. A field may add attributes its alias does not set (`updatedAt UpdatedAt @default(now())`), but repeating one the alias sets is `PSL_DUPLICATE_ATTRIBUTE`. An alias may not name another alias. The targets contribute `temporal.CreatedAt` (generated on create) and `temporal.UpdatedAt` (on create and on update), written bare: `createdAt temporal.CreatedAt`. On Postgres they are `Timestamptz` columns, with `temporal.CreatedAtString` / `UpdatedAtString` and `CreatedAtJsDate` / `UpdatedAtJsDate` variants; on SQLite `DateTime`; on Mongo `Date`. They take no arguments; for a precision, write the type and attributes, as in `Timestamptz(3) @onCreate(now())`. +An alias is expanded into each field that uses it: the column stores the alias's codec and type params, and the contract stores no alias. An alias may carry `@default`, `@onCreate` and `@onUpdate`, which every field typed by it takes: `UpdatedAt = TimestampString(3) @onCreate(now()) @onUpdate(now())`. It may not carry `@id` or `@unique`; write those on the field. A field may add attributes its alias does not set (`updatedAt UpdatedAt @default(now())`), but repeating one the alias sets is `PSL_DUPLICATE_ATTRIBUTE`. An alias may not name another alias. The targets contribute `temporal.CreatedAt` (generated on create) and `temporal.UpdatedAt` (on create and on update), written bare: `createdAt temporal.CreatedAt`. On Postgres they are `TimestamptzTemporal` columns, with `temporal.CreatedAtString` / `UpdatedAtString` and `CreatedAtJsDate` / `UpdatedAtJsDate` variants; on SQLite `DateTime`; on Mongo `Date`. They take no arguments; for a precision, write the type and attributes, as in `TimestamptzTemporal(3) @onCreate(now())`. Values the client generates are written `@onCreate(fn())` and `@onUpdate(fn())`: `id String @id @onCreate(uuid(7))`, `slug String @onCreate(nanoid(12))`, `updatedAt DateTime @onCreate(now()) @onUpdate(now())`. `@default(...)` is a database default only (`now()`, `autoincrement()`, a literal, or `` sql`...` ``); `@default(uuid())` is refused with `PSL_GENERATOR_IN_DEFAULT`. In the TS builder, write `.onCreate(gen.uuid(7))` and `.onUpdate(gen.now())`. Column defaults and generators can sit on one field: `@default(now()) @onCreate(now())`. diff --git a/skills/prisma-8/references/debug.md b/skills/prisma-8/references/debug.md index 1f7e48b672da..e1cd66468bf8 100644 --- a/skills/prisma-8/references/debug.md +++ b/skills/prisma-8/references/debug.md @@ -110,7 +110,7 @@ The single source of truth: read the envelope, find the row by `code`, follow th | `BUDGET.ROWS_EXCEEDED` / `BUDGET.TIME_EXCEEDED` | Runtime, when the `budgets` middleware is active | Tune `budgets({ maxRows, maxLatencyMs, ... })` or rewrite the query. See `references/runtime.md`. | | `LINT.SELECT_STAR` / `LINT.NO_LIMIT` / `LINT.DELETE_WITHOUT_WHERE` / `LINT.UPDATE_WITHOUT_WHERE` / `LINT.READ_ONLY_MUTATION` | Runtime, when the `lints` middleware is active | Fix the query (add a `WHERE` / `LIMIT` / explicit columns), or relax the lint config. See `references/runtime.md`. | | `PLAN.HASH_MISMATCH` | Runtime, executing a precompiled plan | The contract the plan was built against does not match the runtime contract. Re-emit, rebuild, redeploy. | -| `RUNTIME.TEMPORAL_UNAVAILABLE` | Runtime, first read or write touching a Temporal-backed column (`Date`, `Timestamp(p)`, `Timestamptz(p)`, `Time(p)`) or a `temporal.updatedAt()` clock | No global `Temporal`. Node.js 26.8.2 and later ship `globalThis.Temporal`; 26.8.1 and earlier — including every 22 and 24 — do not. Either `import 'temporal-polyfill/full/global'` before the first query, or author the column as `DateString` / `TimestampString(p)` / `TimestamptzString(p)` / `TimeString(p)` to read PostgreSQL's own text. | +| `RUNTIME.TEMPORAL_UNAVAILABLE` | Runtime, first read or write touching a Temporal-backed column (`DateTemporal`, `TimestampTemporal(p)`, `TimestampInstant(p)`, `TimestamptzTemporal(p)`, `TimeTemporal(p)`, and `Date`, `DateTime` and `Time`) or a `temporal.updatedAt()` clock | No global `Temporal`. Node.js 26.8.2 and later ship `globalThis.Temporal`; 26.8.1 and earlier — including every 22 and 24 — do not. Either `import 'temporal-polyfill/full/global'` before the first query, or author the column as `DateString` / `TimestampString(p)` / `TimestamptzString(p)` / `TimeString(p)` to read PostgreSQL's own text. | | `ORM.RELATION_MUTATION_UNSUPPORTED` | ORM nested `create` / `connect` on an N:M relation whose junction has required payload columns | Write the junction table directly or use the SQL builder (`meta.junction`). | | `RUNTIME.ABORTED` (`details.phase` = `encode\|decode\|stream\|beforeEncode\|onRow`) | Runtime, when an `AbortSignal` fires mid-execute | Cancellation, not a bug; surface to the caller. | | `SqlQueryError` (no dotted code) | Raw-SQL paths surfacing a driver error | Inspect `sqlState` + `constraint` + `table` + `column`. Postgres `23505` = unique violation, `23503` = foreign-key violation, etc. Fix the data or the schema. | diff --git a/test/e2e/framework/test/fixtures/contract.ts b/test/e2e/framework/test/fixtures/contract.ts index 9d5a52a3b260..9e39e96f901c 100644 --- a/test/e2e/framework/test/fixtures/contract.ts +++ b/test/e2e/framework/test/fixtures/contract.ts @@ -9,7 +9,7 @@ import { float8Column, int4Column, int8Column, - intervalColumn, + intervalPartsColumn, jsonbColumn, jsonColumn, numericColumn, @@ -106,7 +106,7 @@ export const contract = defineContract( .column('created_at'), startsAt: field.column(timeTemporalColumn(2)).optional().column('starts_at'), startsAtTz: field.column(timetzColumn(2)).optional().column('starts_at_tz'), - duration: field.column(intervalColumn(6)).optional().column('duration'), + duration: field.column(intervalPartsColumn(6)).optional().column('duration'), }, }).sql({ table: 'param_types' }), diff --git a/test/e2e/framework/test/fixtures/generated/contract.d.ts b/test/e2e/framework/test/fixtures/generated/contract.d.ts index aacd1de40319..6969447cbf1f 100644 --- a/test/e2e/framework/test/fixtures/generated/contract.d.ts +++ b/test/e2e/framework/test/fixtures/generated/contract.d.ts @@ -50,6 +50,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; @@ -99,6 +103,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; @@ -108,6 +116,14 @@ export type AggregateTypes = { readonly output: 'pg/time-temporal@1'; readonly nullable: true; }; + readonly 'pg/timestamp-date@1': { + readonly output: 'pg/timestamp-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-instant@1': { + readonly output: 'pg/timestamp-instant@1'; + readonly nullable: true; + }; readonly 'pg/timestamp-string@1': { readonly output: 'pg/timestamp-string@1'; readonly nullable: true; @@ -159,6 +175,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; @@ -168,6 +188,14 @@ export type AggregateTypes = { readonly output: 'pg/time-temporal@1'; readonly nullable: true; }; + readonly 'pg/timestamp-date@1': { + readonly output: 'pg/timestamp-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-instant@1': { + readonly output: 'pg/timestamp-instant@1'; + readonly nullable: true; + }; readonly 'pg/timestamp-string@1': { readonly output: 'pg/timestamp-string@1'; readonly nullable: true; @@ -211,6 +239,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; diff --git a/test/integration/package.json b/test/integration/package.json index c0881640db5f..fdd876a07230 100644 --- a/test/integration/package.json +++ b/test/integration/package.json @@ -96,6 +96,7 @@ "pg": "catalog:", "strip-ansi": "^7.2.0", "temporal-polyfill": "^1.0.4", + "temporal-spec": "1.0.1", "typescript": "catalog:", "vite": "catalog:", "vite7": "npm:vite@7.3.1", diff --git a/test/integration/test/authoring/defaults-on-existing-tables.integration.test.ts b/test/integration/test/authoring/defaults-on-existing-tables.integration.test.ts index 37e86ec3b8e0..18500a960d2a 100644 --- a/test/integration/test/authoring/defaults-on-existing-tables.integration.test.ts +++ b/test/integration/test/authoring/defaults-on-existing-tables.integration.test.ts @@ -180,7 +180,7 @@ model Box { token Uuid @default("A0EEBC99-9C0B-4EF8-BB6D-6BB9BD380A11") addedTags String[] @default(["a", "b"]) addedEmpty String[] @default([]) - addedAt Timestamp @default(timestamp\`0044-03-15 12:00:00 BC\`) + addedAt TimestampTemporal @default(timestamp\`0044-03-15 12:00:00 BC\`) addedNumber Numeric(5, -2) @default(-9999900) addedCode Char(5) @default("ab") addedToken Uuid @default("{a0eebc99-9c0b-4ef8-bb6d-6bb9bd380a11}") diff --git a/test/integration/test/authoring/parity/default-date-time-types/schema.prisma b/test/integration/test/authoring/parity/default-date-time-types/schema.prisma index 40f28804bff3..e5cad60f1dcc 100644 --- a/test/integration/test/authoring/parity/default-date-time-types/schema.prisma +++ b/test/integration/test/authoring/parity/default-date-time-types/schema.prisma @@ -3,7 +3,7 @@ model T { id Int @id instant DateTime @default(timestamptz`2024-01-01T01:00:00.500+01:00`) - local Timestamp @default(timestamp`2024-01-01 12:34:56.500`) + local TimestampTemporal @default(timestamp`2024-01-01 12:34:56.500`) day Date @default(date`0044-03-15 BC`) clock Time @default(time`12:34`) zoned Timetz @default(timetz`12:34:56+02`) diff --git a/test/integration/test/cli-journeys/control-plane-commands-without-temporal.e2e.test.ts b/test/integration/test/cli-journeys/control-plane-commands-without-temporal.e2e.test.ts index dfd9d4a76184..492da66eb7d3 100644 --- a/test/integration/test/cli-journeys/control-plane-commands-without-temporal.e2e.test.ts +++ b/test/integration/test/cli-journeys/control-plane-commands-without-temporal.e2e.test.ts @@ -61,9 +61,9 @@ const DEFAULTS_SCHEMA = `// use prisma-8 model Event { id Int @id - localAt Timestamp(3) @default(timestamp\`2024-01-01 00:00:00\`) - instantAt Timestamptz @default(timestamptz\`2024-01-01T00:00:00Z\`) - history Timestamp(3)[] @default([timestamp\`2024-01-01 00:00:00\`, timestamp\`2024-06-30 12:34:56.789\`]) + localAt TimestampTemporal(3) @default(timestamp\`2024-01-01 00:00:00\`) + instantAt TimestamptzTemporal @default(timestamptz\`2024-01-01T00:00:00Z\`) + history TimestampTemporal(3)[] @default([timestamp\`2024-01-01 00:00:00\`, timestamp\`2024-06-30 12:34:56.789\`]) @@map("event") } diff --git a/test/integration/test/cli-journeys/date-time-default-canonical-form.e2e.test.ts b/test/integration/test/cli-journeys/date-time-default-canonical-form.e2e.test.ts index fa9772de7936..1b602e624422 100644 --- a/test/integration/test/cli-journeys/date-time-default-canonical-form.e2e.test.ts +++ b/test/integration/test/cli-journeys/date-time-default-canonical-form.e2e.test.ts @@ -180,7 +180,7 @@ model Created { id Int @id instant DateTime @default(timestamptz\`0044-03-15 00:00:00+00 BC\`) day Date @default(date\`0044-03-15 BC\`) - local Timestamp @default(timestamp\`-000043-03-15T12:00:00\`) + local TimestampTemporal @default(timestamp\`-000043-03-15T12:00:00\`) @@map("created") } @@ -189,7 +189,7 @@ model Created { id Int @id instant DateTime ${defaults ? '@default(timestamptz`-000043-03-15T00:00:00Z`)' : ''} day Date ${defaults ? '@default(date`-000043-03-15`)' : ''} - local Timestamp ${defaults ? '@default(timestamp`0044-03-15 12:00:00 BC`)' : ''} + local TimestampTemporal ${defaults ? '@default(timestamp`0044-03-15 12:00:00 BC`)' : ''} @@map("altered") } diff --git a/test/integration/test/cli-journeys/defaults-read-by-codec.e2e.test.ts b/test/integration/test/cli-journeys/defaults-read-by-codec.e2e.test.ts index 6befbf8810a6..2d7bfb98127e 100644 --- a/test/integration/test/cli-journeys/defaults-read-by-codec.e2e.test.ts +++ b/test/integration/test/cli-journeys/defaults-read-by-codec.e2e.test.ts @@ -44,8 +44,8 @@ model Sample { varcode VarChar(10) @default("vc") code sql.String(10) @default("vc") initials Char(3) @default("ab") - local Timestamp(3) @default(${tagged('timestamp', '2024-01-01T12:34:56.5')}) - precise Timestamptz(6) @default(${tagged('timestamptz', '2024-01-01T00:00:00.123456Z')}) + local TimestampTemporal(3) @default(${tagged('timestamp', '2024-01-01T12:34:56.5')}) + precise TimestamptzTemporal(6) @default(${tagged('timestamptz', '2024-01-01T00:00:00.123456Z')}) clock Time @default(${tagged('time', '12:34:56')}) zoned Timetz @default(${tagged('timetz', '12:34:56+02:00')}) token Uuid @default("a0eebc99-9c0b-4ef8-bb6d-6bb9bd380a11") @@ -60,7 +60,7 @@ model Sample { clockText TimeString @default(${tagged('time', '12:00:00')}) bits Bit(3) @default("101") varbits VarBit(5) @default("1") - span Interval @default(${tagged('interval', 'P1DT2H')}) + span IntervalParts @default(${tagged('interval', 'P1DT2H')}) tags String[] @default(["a", "b"]) counts Int[] @default([1, 2]) plan pg.enum(Plan) @default("pro") @@ -74,21 +74,21 @@ model Edge { id Int @id dayBc Date @default(${tagged('date', '0044-03-15 BC')}) dayFar Date @default(${tagged('date', '+012026-01-02')}) - localBc Timestamp(3) @default(${tagged('timestamp', '0044-03-15 12:00:00 BC')}) - instantFar Timestamptz(6) @default(${tagged('timestamptz', '+012026-01-02T03:04:05Z')}) + localBc TimestampTemporal(3) @default(${tagged('timestamp', '0044-03-15 12:00:00 BC')}) + instantFar TimestamptzTemporal(6) @default(${tagged('timestamptz', '+012026-01-02T03:04:05Z')}) forever TimestamptzString @default(${tagged('timestamptz', 'infinity')}) dawn DateString @default(${tagged('date', '-infinity')}) localForever TimestampString @default(${tagged('timestamp', 'infinity')}) - mixed Interval @default(${tagged('interval', 'P1Y-2M3DT-4H5M-6.5S')}) - backwards Interval @default(${tagged('interval', 'P-1DT-4H-5M')}) + mixed IntervalParts @default(${tagged('interval', 'P1Y-2M3DT-4H5M-6.5S')}) + backwards IntervalParts @default(${tagged('interval', 'P-1DT-4H-5M')}) quoteBytes Bytes @default(${tagged('bytea', '\\x27225c00ff')}) quoteText String @default("it's a \\\\ backslash") notANumber Float @default(NaN) above Float @default(Infinity) below Real @default(-Infinity) numericNaN Decimal @default(NaN) - stamps Timestamp(3)[] @default([${tagged('timestamp', '0044-03-15 12:00:00 BC')}, ${tagged('timestamp', '2024-01-01T00:00:00')}]) - spans Interval[] @default([${tagged('interval', 'P1Y-2M')}, ${tagged('interval', 'PT0S')}]) + stamps TimestampTemporal(3)[] @default([${tagged('timestamp', '0044-03-15 12:00:00 BC')}, ${tagged('timestamp', '2024-01-01T00:00:00')}]) + spans IntervalParts[] @default([${tagged('interval', 'P1Y-2M')}, ${tagged('interval', 'PT0S')}]) blobs Bytes[] @default([${tagged('bytea', '\\x27')}, ${tagged('bytea', '\\x5c')}]) texts String[] @default(["a'b", "c\\\\d", ""]) floats Float[] @default([NaN, Infinity, -Infinity, 1.5]) diff --git a/test/integration/test/contract-builder.types.test-d.ts b/test/integration/test/contract-builder.types.test-d.ts index 7e8808348da0..b678b93326e0 100644 --- a/test/integration/test/contract-builder.types.test-d.ts +++ b/test/integration/test/contract-builder.types.test-d.ts @@ -27,6 +27,7 @@ import type { JsonValue } from '@internal/target-postgres/codec-types'; import postgresPack from '@internal/target-postgres/pack'; import { postgresCreateNamespace } from '@internal/target-postgres/types'; import { type as arktype } from 'arktype'; +import type { Temporal as TemporalTypes } from 'temporal-spec'; import { expectTypeOf, test } from 'vitest'; import { TestSqlContractSerializer as SqlContractSerializer } from '../../../packages/2-sql-family/9-family/test/test-sql-contract-serializer'; import type { Contract } from './fixtures/contract.d'; @@ -598,7 +599,7 @@ test('read: non-enum fields keep their codec output, unchanged from main', () => type Row = ResultType; expectTypeOf().toEqualTypeOf(); expectTypeOf().toEqualTypeOf(); - expectTypeOf().toEqualTypeOf(); + expectTypeOf().toEqualTypeOf(); }); declare const createdAt: Temporal.Instant; diff --git a/test/integration/test/contract-parity/fixtures/postgres/generated/contract.d.ts b/test/integration/test/contract-parity/fixtures/postgres/generated/contract.d.ts index d06049f6d9e6..278e9edeeaa5 100644 --- a/test/integration/test/contract-parity/fixtures/postgres/generated/contract.d.ts +++ b/test/integration/test/contract-parity/fixtures/postgres/generated/contract.d.ts @@ -44,6 +44,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; @@ -93,6 +97,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; @@ -102,6 +110,14 @@ export type AggregateTypes = { readonly output: 'pg/time-temporal@1'; readonly nullable: true; }; + readonly 'pg/timestamp-date@1': { + readonly output: 'pg/timestamp-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-instant@1': { + readonly output: 'pg/timestamp-instant@1'; + readonly nullable: true; + }; readonly 'pg/timestamp-string@1': { readonly output: 'pg/timestamp-string@1'; readonly nullable: true; @@ -153,6 +169,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; @@ -162,6 +182,14 @@ export type AggregateTypes = { readonly output: 'pg/time-temporal@1'; readonly nullable: true; }; + readonly 'pg/timestamp-date@1': { + readonly output: 'pg/timestamp-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-instant@1': { + readonly output: 'pg/timestamp-instant@1'; + readonly nullable: true; + }; readonly 'pg/timestamp-string@1': { readonly output: 'pg/timestamp-string@1'; readonly nullable: true; @@ -205,6 +233,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; diff --git a/test/integration/test/contract-parity/rendered-output-types.test.ts b/test/integration/test/contract-parity/rendered-output-types.test.ts index 19f1a5872c1f..bfbd214dcba6 100644 --- a/test/integration/test/contract-parity/rendered-output-types.test.ts +++ b/test/integration/test/contract-parity/rendered-output-types.test.ts @@ -32,7 +32,6 @@ const declaredByTypeFunction = [ 'pg/bit@1', 'pg/varbit@1', 'pg/timetz@1', - 'pg/interval@1', 'pg/time-string@1', 'pg/timestamp-string@1', 'pg/timestamptz-string@1', @@ -57,7 +56,13 @@ const stacks: Readonly is the same type for every SRID.', + 'pg/interval@1': + 'An interval reads as its months, days and microseconds whatever its precision.', + 'pg/interval-string@1': + 'An interval read as a string is an ISO 8601 duration whatever its precision.', 'pg/time-temporal@1': temporalPrecision, + 'pg/timestamp-date@1': 'A timestamp read as a Date is a Date whatever its precision.', + 'pg/timestamp-instant@1': temporalPrecision, 'pg/timestamp-temporal@1': temporalPrecision, 'pg/timestamptz-temporal@1': temporalPrecision, 'pg/unboundedint@1': 'A numeric(P, 0) column reads as a bigint whatever its precision.', diff --git a/test/integration/test/date-time-defaults/_fixture-before-canonical-form/contract-with-tags.prisma b/test/integration/test/date-time-defaults/_fixture-before-canonical-form/contract-with-tags.prisma index eff1589a5025..a188c9678979 100644 --- a/test/integration/test/date-time-defaults/_fixture-before-canonical-form/contract-with-tags.prisma +++ b/test/integration/test/date-time-defaults/_fixture-before-canonical-form/contract-with-tags.prisma @@ -5,7 +5,7 @@ model Event { a DateTime @default(timestamptz`2024-01-01T00:00:00Z`) b DateTime @default(timestamptz`2024-01-01T00:00:00.000Z`) c DateTime @default(timestamptz`2024-01-01T01:00:00+01:00`) - localAt Timestamp(3) @default(timestamp`2024-01-01 12:34:56.500`) + localAt TimestampTemporal(3) @default(timestamp`2024-01-01 12:34:56.500`) day Date @default(date`2024-01-01`) clock Time @default(time`12:34`) zoned Timetz @default(timetz`12:34:56+02`) diff --git a/test/integration/test/date-time-defaults/_fixture-before-canonical-form/contract.prisma b/test/integration/test/date-time-defaults/_fixture-before-canonical-form/contract.prisma index 071d505ec5b8..400a07fa0e3e 100644 --- a/test/integration/test/date-time-defaults/_fixture-before-canonical-form/contract.prisma +++ b/test/integration/test/date-time-defaults/_fixture-before-canonical-form/contract.prisma @@ -5,7 +5,7 @@ model Event { a DateTime @default("2024-01-01T00:00:00Z") b DateTime @default("2024-01-01T00:00:00.000Z") c DateTime @default("2024-01-01T01:00:00+01:00") - localAt Timestamp(3) @default("2024-01-01 12:34:56.500") + localAt TimestampTemporal(3) @default("2024-01-01 12:34:56.500") day Date @default("2024-01-01") clock Time @default("12:34") zoned Timetz @default("12:34:56+02") diff --git a/test/integration/test/date-time-defaults/psl-date-time-defaults.integration.test.ts b/test/integration/test/date-time-defaults/psl-date-time-defaults.integration.test.ts index c2674e511a6a..e3647df39d7d 100644 --- a/test/integration/test/date-time-defaults/psl-date-time-defaults.integration.test.ts +++ b/test/integration/test/date-time-defaults/psl-date-time-defaults.integration.test.ts @@ -19,7 +19,7 @@ const literal = (value: unknown) => ({ kind: 'literal', value }); /** The tag that writes a value of each Postgres column type these tests use. */ const TAG_OF: Readonly> = { DateTime: 'timestamptz', - Timestamp: 'timestamp', + TimestampTemporal: 'timestamp', Date: 'date', Time: 'time', Timetz: 'timetz', @@ -49,15 +49,15 @@ describe('a PSL date or time default is stored in its data type canonical form', await postgresDefaults( `model Event { id Int @id - instant Timestamptz(3) @default(timestamptz\`2024-01-01 01:00:00+01\`) + instant TimestamptzTemporal(3) @default(timestamptz\`2024-01-01 01:00:00+01\`) jsDate TimestamptzJsDate @default(timestamptz\`2024-01-01 00:00:00.500+00\`) - local Timestamp @default(timestamp\`2024-01-01 12:34:56.500\`) + local TimestampTemporal @default(timestamp\`2024-01-01 12:34:56.500\`) localText TimestampString @default(timestamp\`2024-01-01 12:34:56\`) day Date @default(date\`0044-03-15 BC\`) dayText DateString @default(date\`12026-01-02\`) clock Time @default(time\`12:34\`) zoned Timetz @default(timetz\`12:34:56+02\`) - history Timestamp(3)[] @default([timestamp\`2024-01-01 00:00:00\`, timestamp\`2024-06-30 12:34:56.789000\`]) + history TimestampTemporal(3)[] @default([timestamp\`2024-01-01 00:00:00\`, timestamp\`2024-06-30 12:34:56.789000\`]) }`, ['instant', 'jsDate', 'local', 'localText', 'day', 'dayText', 'clock', 'zoned', 'history'], ), @@ -87,7 +87,7 @@ describe('a PSL date or time default is stored in its data type canonical form', it.each([ [ - 'Timestamp', + 'TimestampTemporal', '2024-01-01T00:00:00Z', 'pg/timestamp holds no UTC offset, but "2024-01-01T00:00:00Z" has one. Leave it out, as in "2024-01-01T12:34:56".', ], @@ -158,11 +158,11 @@ describe('a PSL date or time default is stored in its data type canonical form', describe('a quoted string on a Postgres date, time, interval or bytes column', () => { it.each([ ['DateTime', '2024-01-01T00:00:00Z', 'timestamptz'], - ['Timestamp', '2024-01-01 12:34:56', 'timestamp'], + ['TimestampTemporal', '2024-01-01 12:34:56', 'timestamp'], ['Date', '2024-01-01', 'date'], ['Time', '12:34:56', 'time'], ['Timetz', '12:34:56+02:00', 'timetz'], - ['Interval', 'P1D', 'interval'], + ['IntervalParts', 'P1D', 'interval'], ['Bytes', '\\x68656c6c6f', 'bytea'], ])('on a %s column is refused, showing the tag to write', async (type, written, tag) => { const result = await authorSqlContractFromPsl( @@ -181,7 +181,7 @@ describe('a quoted string on a Postgres date, time, interval or bytes column', ( await postgresDefaults( `model Event { id Int @id - span Interval @default(interval\`1 day 02:00:00\`) + span IntervalParts @default(interval\`1 day 02:00:00\`) bytes Bytes @default(bytea\`\\x68656c6c6f\`) }`, ['span', 'bytes'], diff --git a/test/integration/test/enum-members/_fixture-native-enums/generated/contract.d.ts b/test/integration/test/enum-members/_fixture-native-enums/generated/contract.d.ts index 172eb296c6ab..49594122fd37 100644 --- a/test/integration/test/enum-members/_fixture-native-enums/generated/contract.d.ts +++ b/test/integration/test/enum-members/_fixture-native-enums/generated/contract.d.ts @@ -42,6 +42,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; @@ -91,6 +95,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; @@ -100,6 +108,14 @@ export type AggregateTypes = { readonly output: 'pg/time-temporal@1'; readonly nullable: true; }; + readonly 'pg/timestamp-date@1': { + readonly output: 'pg/timestamp-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-instant@1': { + readonly output: 'pg/timestamp-instant@1'; + readonly nullable: true; + }; readonly 'pg/timestamp-string@1': { readonly output: 'pg/timestamp-string@1'; readonly nullable: true; @@ -151,6 +167,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; @@ -160,6 +180,14 @@ export type AggregateTypes = { readonly output: 'pg/time-temporal@1'; readonly nullable: true; }; + readonly 'pg/timestamp-date@1': { + readonly output: 'pg/timestamp-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-instant@1': { + readonly output: 'pg/timestamp-instant@1'; + readonly nullable: true; + }; readonly 'pg/timestamp-string@1': { readonly output: 'pg/timestamp-string@1'; readonly nullable: true; @@ -203,6 +231,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; diff --git a/test/integration/test/enum-members/_fixture-postgres/generated/contract.d.ts b/test/integration/test/enum-members/_fixture-postgres/generated/contract.d.ts index b6ddb313c349..e186f4f346df 100644 --- a/test/integration/test/enum-members/_fixture-postgres/generated/contract.d.ts +++ b/test/integration/test/enum-members/_fixture-postgres/generated/contract.d.ts @@ -42,6 +42,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; @@ -91,6 +95,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; @@ -100,6 +108,14 @@ export type AggregateTypes = { readonly output: 'pg/time-temporal@1'; readonly nullable: true; }; + readonly 'pg/timestamp-date@1': { + readonly output: 'pg/timestamp-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-instant@1': { + readonly output: 'pg/timestamp-instant@1'; + readonly nullable: true; + }; readonly 'pg/timestamp-string@1': { readonly output: 'pg/timestamp-string@1'; readonly nullable: true; @@ -151,6 +167,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; @@ -160,6 +180,14 @@ export type AggregateTypes = { readonly output: 'pg/time-temporal@1'; readonly nullable: true; }; + readonly 'pg/timestamp-date@1': { + readonly output: 'pg/timestamp-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-instant@1': { + readonly output: 'pg/timestamp-instant@1'; + readonly nullable: true; + }; readonly 'pg/timestamp-string@1': { readonly output: 'pg/timestamp-string@1'; readonly nullable: true; @@ -203,6 +231,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; diff --git a/test/integration/test/enum-members/enum-members.postgres.test.ts b/test/integration/test/enum-members/enum-members.postgres.test.ts index c0c9d4cd7497..b8a7420f70a2 100644 --- a/test/integration/test/enum-members/enum-members.postgres.test.ts +++ b/test/integration/test/enum-members/enum-members.postgres.test.ts @@ -1,3 +1,4 @@ +import type { Temporal as TemporalTypes } from 'temporal-spec'; import { describe, expect, expectTypeOf, it } from 'vitest'; import { type PortContext, timeouts, withPostgresPort } from '../_harness/postgres'; import type { Contract } from './_fixture-postgres/generated/contract'; @@ -226,8 +227,12 @@ describe('db.enums on a contract emitted from PSL, against Postgres', () => { expectTypeOf().toEqualTypeOf<1>(); expectTypeOf().toEqualTypeOf<1n>(); expectTypeOf().toEqualTypeOf(); - expectTypeOf().toEqualTypeOf(); - expectTypeOf().toEqualTypeOf(); + expectTypeOf< + Levels['StampLevel']['members']['Launch'] + >().toEqualTypeOf(); + expectTypeOf< + Levels['DayLevel']['members']['Launch'] + >().toEqualTypeOf(); expectTypeOf().toEqualTypeOf<1.5>(); expectTypeOf().toEqualTypeOf(); expectTypeOf().toEqualTypeOf(); diff --git a/test/integration/test/enum-order-by/_fixture/generated/contract.d.ts b/test/integration/test/enum-order-by/_fixture/generated/contract.d.ts index 3e7648083dd0..e3aca11c0c88 100644 --- a/test/integration/test/enum-order-by/_fixture/generated/contract.d.ts +++ b/test/integration/test/enum-order-by/_fixture/generated/contract.d.ts @@ -42,6 +42,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; @@ -91,6 +95,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; @@ -100,6 +108,14 @@ export type AggregateTypes = { readonly output: 'pg/time-temporal@1'; readonly nullable: true; }; + readonly 'pg/timestamp-date@1': { + readonly output: 'pg/timestamp-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-instant@1': { + readonly output: 'pg/timestamp-instant@1'; + readonly nullable: true; + }; readonly 'pg/timestamp-string@1': { readonly output: 'pg/timestamp-string@1'; readonly nullable: true; @@ -151,6 +167,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; @@ -160,6 +180,14 @@ export type AggregateTypes = { readonly output: 'pg/time-temporal@1'; readonly nullable: true; }; + readonly 'pg/timestamp-date@1': { + readonly output: 'pg/timestamp-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-instant@1': { + readonly output: 'pg/timestamp-instant@1'; + readonly nullable: true; + }; readonly 'pg/timestamp-string@1': { readonly output: 'pg/timestamp-string@1'; readonly nullable: true; @@ -203,6 +231,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; diff --git a/test/integration/test/fixtures/cli/cli-e2e-test-app/fixtures/cli-journeys/contract-date-time-defaults.ts b/test/integration/test/fixtures/cli/cli-e2e-test-app/fixtures/cli-journeys/contract-date-time-defaults.ts index 63a46fb72590..ce27258a6b8c 100644 --- a/test/integration/test/fixtures/cli/cli-e2e-test-app/fixtures/cli-journeys/contract-date-time-defaults.ts +++ b/test/integration/test/fixtures/cli/cli-e2e-test-app/fixtures/cli-journeys/contract-date-time-defaults.ts @@ -1,7 +1,7 @@ import { dateStringColumn, int4Column, - intervalColumn, + intervalPartsColumn, timeStringColumn, timestampStringColumn, timestamptzJsDateColumn, @@ -21,7 +21,9 @@ export const contract = defineContract({ dayText: field.column(dateStringColumn).default('0044-03-15 BC'), clockText: field.column(timeStringColumn()).default('12:34:56.500'), zoned: field.column(timetzColumn()).default('12:34:56+02'), - span: field.column(intervalColumn()).default({ months: 14, days: 3, micros: 14706500000n }), + span: field + .column(intervalPartsColumn()) + .default({ months: 14, days: 3, micros: 14706500000n }), }, }).sql({ table: 'moment' }), }, diff --git a/test/integration/test/fixtures/contract.d.ts b/test/integration/test/fixtures/contract.d.ts index a6ed1651533f..f7f04d0c7b46 100644 --- a/test/integration/test/fixtures/contract.d.ts +++ b/test/integration/test/fixtures/contract.d.ts @@ -42,6 +42,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; @@ -91,6 +95,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; @@ -100,6 +108,14 @@ export type AggregateTypes = { readonly output: 'pg/time-temporal@1'; readonly nullable: true; }; + readonly 'pg/timestamp-date@1': { + readonly output: 'pg/timestamp-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-instant@1': { + readonly output: 'pg/timestamp-instant@1'; + readonly nullable: true; + }; readonly 'pg/timestamp-string@1': { readonly output: 'pg/timestamp-string@1'; readonly nullable: true; @@ -151,6 +167,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; @@ -160,6 +180,14 @@ export type AggregateTypes = { readonly output: 'pg/time-temporal@1'; readonly nullable: true; }; + readonly 'pg/timestamp-date@1': { + readonly output: 'pg/timestamp-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-instant@1': { + readonly output: 'pg/timestamp-instant@1'; + readonly nullable: true; + }; readonly 'pg/timestamp-string@1': { readonly output: 'pg/timestamp-string@1'; readonly nullable: true; @@ -203,6 +231,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; diff --git a/test/integration/test/namespaced-accessors/fixtures/generated/contract.d.ts b/test/integration/test/namespaced-accessors/fixtures/generated/contract.d.ts index d9eb2abfc44b..e4b9cd5dccbc 100644 --- a/test/integration/test/namespaced-accessors/fixtures/generated/contract.d.ts +++ b/test/integration/test/namespaced-accessors/fixtures/generated/contract.d.ts @@ -42,6 +42,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; @@ -91,6 +95,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; @@ -100,6 +108,14 @@ export type AggregateTypes = { readonly output: 'pg/time-temporal@1'; readonly nullable: true; }; + readonly 'pg/timestamp-date@1': { + readonly output: 'pg/timestamp-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-instant@1': { + readonly output: 'pg/timestamp-instant@1'; + readonly nullable: true; + }; readonly 'pg/timestamp-string@1': { readonly output: 'pg/timestamp-string@1'; readonly nullable: true; @@ -151,6 +167,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; @@ -160,6 +180,14 @@ export type AggregateTypes = { readonly output: 'pg/time-temporal@1'; readonly nullable: true; }; + readonly 'pg/timestamp-date@1': { + readonly output: 'pg/timestamp-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-instant@1': { + readonly output: 'pg/timestamp-instant@1'; + readonly nullable: true; + }; readonly 'pg/timestamp-string@1': { readonly output: 'pg/timestamp-string@1'; readonly nullable: true; @@ -203,6 +231,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; diff --git a/test/integration/test/planner-golden/fixtures/postgres/contract.ts b/test/integration/test/planner-golden/fixtures/postgres/contract.ts index 2d8c6147d8ed..43e3d08e5db6 100644 --- a/test/integration/test/planner-golden/fixtures/postgres/contract.ts +++ b/test/integration/test/planner-golden/fixtures/postgres/contract.ts @@ -16,7 +16,7 @@ import { int2Column, int4Column, int8Column, - intervalColumn, + intervalPartsColumn, jsonbColumn, jsonColumn, numericColumn, @@ -125,8 +125,8 @@ export const contract = defineContract({ timestampPrecision: field.column(timestamp3), timestamptz: field.column(timestamptzTemporalColumn), timestamptzPrecision: field.column(timestamptz3), - interval: field.column(intervalColumn()), - intervalPrecision: field.column(intervalColumn(3)), + interval: field.column(intervalPartsColumn()), + intervalPrecision: field.column(intervalPartsColumn(3)), mood: field.column(pg.enum(Mood)), vector: field.column(vector(3)), geometry: field.column(geometryColumn), @@ -171,7 +171,7 @@ export const contract = defineContract({ timePrecision: field.column(timeTemporalColumn(3)).many(), timestamptz: field.column(timestamptzTemporalColumn).many(), timestamptzPrecision: field.column(timestamptz3).many(), - interval: field.column(intervalColumn()).many(), + interval: field.column(intervalPartsColumn()).many(), jsonb: field.column(jsonbColumn).many(), uuid: field.column(uuid).many(), mood: field.column(pg.enum(Mood)).many(), diff --git a/test/integration/test/planner-golden/fixtures/postgres/generated/contract.d.ts b/test/integration/test/planner-golden/fixtures/postgres/generated/contract.d.ts index 9819eca2b0a6..f57107eb2aae 100644 --- a/test/integration/test/planner-golden/fixtures/postgres/generated/contract.d.ts +++ b/test/integration/test/planner-golden/fixtures/postgres/generated/contract.d.ts @@ -51,6 +51,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; @@ -100,6 +104,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; @@ -109,6 +117,14 @@ export type AggregateTypes = { readonly output: 'pg/time-temporal@1'; readonly nullable: true; }; + readonly 'pg/timestamp-date@1': { + readonly output: 'pg/timestamp-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-instant@1': { + readonly output: 'pg/timestamp-instant@1'; + readonly nullable: true; + }; readonly 'pg/timestamp-string@1': { readonly output: 'pg/timestamp-string@1'; readonly nullable: true; @@ -160,6 +176,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; @@ -169,6 +189,14 @@ export type AggregateTypes = { readonly output: 'pg/time-temporal@1'; readonly nullable: true; }; + readonly 'pg/timestamp-date@1': { + readonly output: 'pg/timestamp-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-instant@1': { + readonly output: 'pg/timestamp-instant@1'; + readonly nullable: true; + }; readonly 'pg/timestamp-string@1': { readonly output: 'pg/timestamp-string@1'; readonly nullable: true; @@ -212,6 +240,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; diff --git a/test/integration/test/ports/engines/queries/aggregation/avg/_fixture/decimal/generated/contract.d.ts b/test/integration/test/ports/engines/queries/aggregation/avg/_fixture/decimal/generated/contract.d.ts index 1ff533a6d9a1..6c6df1fc1559 100644 --- a/test/integration/test/ports/engines/queries/aggregation/avg/_fixture/decimal/generated/contract.d.ts +++ b/test/integration/test/ports/engines/queries/aggregation/avg/_fixture/decimal/generated/contract.d.ts @@ -42,6 +42,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; @@ -91,6 +95,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; @@ -100,6 +108,14 @@ export type AggregateTypes = { readonly output: 'pg/time-temporal@1'; readonly nullable: true; }; + readonly 'pg/timestamp-date@1': { + readonly output: 'pg/timestamp-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-instant@1': { + readonly output: 'pg/timestamp-instant@1'; + readonly nullable: true; + }; readonly 'pg/timestamp-string@1': { readonly output: 'pg/timestamp-string@1'; readonly nullable: true; @@ -151,6 +167,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; @@ -160,6 +180,14 @@ export type AggregateTypes = { readonly output: 'pg/time-temporal@1'; readonly nullable: true; }; + readonly 'pg/timestamp-date@1': { + readonly output: 'pg/timestamp-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-instant@1': { + readonly output: 'pg/timestamp-instant@1'; + readonly nullable: true; + }; readonly 'pg/timestamp-string@1': { readonly output: 'pg/timestamp-string@1'; readonly nullable: true; @@ -203,6 +231,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; diff --git a/test/integration/test/ports/engines/queries/aggregation/avg/_fixture/numeric/generated/contract.d.ts b/test/integration/test/ports/engines/queries/aggregation/avg/_fixture/numeric/generated/contract.d.ts index b40f191cc247..f62f8dfc0029 100644 --- a/test/integration/test/ports/engines/queries/aggregation/avg/_fixture/numeric/generated/contract.d.ts +++ b/test/integration/test/ports/engines/queries/aggregation/avg/_fixture/numeric/generated/contract.d.ts @@ -42,6 +42,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; @@ -91,6 +95,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; @@ -100,6 +108,14 @@ export type AggregateTypes = { readonly output: 'pg/time-temporal@1'; readonly nullable: true; }; + readonly 'pg/timestamp-date@1': { + readonly output: 'pg/timestamp-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-instant@1': { + readonly output: 'pg/timestamp-instant@1'; + readonly nullable: true; + }; readonly 'pg/timestamp-string@1': { readonly output: 'pg/timestamp-string@1'; readonly nullable: true; @@ -151,6 +167,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; @@ -160,6 +180,14 @@ export type AggregateTypes = { readonly output: 'pg/time-temporal@1'; readonly nullable: true; }; + readonly 'pg/timestamp-date@1': { + readonly output: 'pg/timestamp-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-instant@1': { + readonly output: 'pg/timestamp-instant@1'; + readonly nullable: true; + }; readonly 'pg/timestamp-string@1': { readonly output: 'pg/timestamp-string@1'; readonly nullable: true; @@ -203,6 +231,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; diff --git a/test/integration/test/ports/engines/queries/aggregation/count/_fixture/generated/contract.d.ts b/test/integration/test/ports/engines/queries/aggregation/count/_fixture/generated/contract.d.ts index cf24259e9beb..f7a2f4f95ce5 100644 --- a/test/integration/test/ports/engines/queries/aggregation/count/_fixture/generated/contract.d.ts +++ b/test/integration/test/ports/engines/queries/aggregation/count/_fixture/generated/contract.d.ts @@ -42,6 +42,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; @@ -91,6 +95,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; @@ -100,6 +108,14 @@ export type AggregateTypes = { readonly output: 'pg/time-temporal@1'; readonly nullable: true; }; + readonly 'pg/timestamp-date@1': { + readonly output: 'pg/timestamp-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-instant@1': { + readonly output: 'pg/timestamp-instant@1'; + readonly nullable: true; + }; readonly 'pg/timestamp-string@1': { readonly output: 'pg/timestamp-string@1'; readonly nullable: true; @@ -151,6 +167,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; @@ -160,6 +180,14 @@ export type AggregateTypes = { readonly output: 'pg/time-temporal@1'; readonly nullable: true; }; + readonly 'pg/timestamp-date@1': { + readonly output: 'pg/timestamp-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-instant@1': { + readonly output: 'pg/timestamp-instant@1'; + readonly nullable: true; + }; readonly 'pg/timestamp-string@1': { readonly output: 'pg/timestamp-string@1'; readonly nullable: true; @@ -203,6 +231,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; diff --git a/test/integration/test/ports/engines/queries/aggregation/group_by/_fixture/main/generated/contract.d.ts b/test/integration/test/ports/engines/queries/aggregation/group_by/_fixture/main/generated/contract.d.ts index 9e45bbdfb0e0..0a3850f07ce1 100644 --- a/test/integration/test/ports/engines/queries/aggregation/group_by/_fixture/main/generated/contract.d.ts +++ b/test/integration/test/ports/engines/queries/aggregation/group_by/_fixture/main/generated/contract.d.ts @@ -42,6 +42,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; @@ -91,6 +95,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; @@ -100,6 +108,14 @@ export type AggregateTypes = { readonly output: 'pg/time-temporal@1'; readonly nullable: true; }; + readonly 'pg/timestamp-date@1': { + readonly output: 'pg/timestamp-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-instant@1': { + readonly output: 'pg/timestamp-instant@1'; + readonly nullable: true; + }; readonly 'pg/timestamp-string@1': { readonly output: 'pg/timestamp-string@1'; readonly nullable: true; @@ -151,6 +167,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; @@ -160,6 +180,14 @@ export type AggregateTypes = { readonly output: 'pg/time-temporal@1'; readonly nullable: true; }; + readonly 'pg/timestamp-date@1': { + readonly output: 'pg/timestamp-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-instant@1': { + readonly output: 'pg/timestamp-instant@1'; + readonly nullable: true; + }; readonly 'pg/timestamp-string@1': { readonly output: 'pg/timestamp-string@1'; readonly nullable: true; @@ -203,6 +231,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; diff --git a/test/integration/test/ports/engines/queries/aggregation/group_by/_fixture/regression-21789/generated/contract.d.ts b/test/integration/test/ports/engines/queries/aggregation/group_by/_fixture/regression-21789/generated/contract.d.ts index cb88044c5fa5..2f2982eabc53 100644 --- a/test/integration/test/ports/engines/queries/aggregation/group_by/_fixture/regression-21789/generated/contract.d.ts +++ b/test/integration/test/ports/engines/queries/aggregation/group_by/_fixture/regression-21789/generated/contract.d.ts @@ -42,6 +42,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; @@ -91,6 +95,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; @@ -100,6 +108,14 @@ export type AggregateTypes = { readonly output: 'pg/time-temporal@1'; readonly nullable: true; }; + readonly 'pg/timestamp-date@1': { + readonly output: 'pg/timestamp-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-instant@1': { + readonly output: 'pg/timestamp-instant@1'; + readonly nullable: true; + }; readonly 'pg/timestamp-string@1': { readonly output: 'pg/timestamp-string@1'; readonly nullable: true; @@ -151,6 +167,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; @@ -160,6 +180,14 @@ export type AggregateTypes = { readonly output: 'pg/time-temporal@1'; readonly nullable: true; }; + readonly 'pg/timestamp-date@1': { + readonly output: 'pg/timestamp-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-instant@1': { + readonly output: 'pg/timestamp-instant@1'; + readonly nullable: true; + }; readonly 'pg/timestamp-string@1': { readonly output: 'pg/timestamp-string@1'; readonly nullable: true; @@ -203,6 +231,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; diff --git a/test/integration/test/ports/engines/queries/aggregation/group_by_having/_fixture/common/generated/contract.d.ts b/test/integration/test/ports/engines/queries/aggregation/group_by_having/_fixture/common/generated/contract.d.ts index ac360de4d4d3..6ec0331c00bf 100644 --- a/test/integration/test/ports/engines/queries/aggregation/group_by_having/_fixture/common/generated/contract.d.ts +++ b/test/integration/test/ports/engines/queries/aggregation/group_by_having/_fixture/common/generated/contract.d.ts @@ -42,6 +42,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; @@ -91,6 +95,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; @@ -100,6 +108,14 @@ export type AggregateTypes = { readonly output: 'pg/time-temporal@1'; readonly nullable: true; }; + readonly 'pg/timestamp-date@1': { + readonly output: 'pg/timestamp-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-instant@1': { + readonly output: 'pg/timestamp-instant@1'; + readonly nullable: true; + }; readonly 'pg/timestamp-string@1': { readonly output: 'pg/timestamp-string@1'; readonly nullable: true; @@ -151,6 +167,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; @@ -160,6 +180,14 @@ export type AggregateTypes = { readonly output: 'pg/time-temporal@1'; readonly nullable: true; }; + readonly 'pg/timestamp-date@1': { + readonly output: 'pg/timestamp-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-instant@1': { + readonly output: 'pg/timestamp-instant@1'; + readonly nullable: true; + }; readonly 'pg/timestamp-string@1': { readonly output: 'pg/timestamp-string@1'; readonly nullable: true; @@ -203,6 +231,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; diff --git a/test/integration/test/ports/engines/queries/aggregation/group_by_having/_fixture/decimal/generated/contract.d.ts b/test/integration/test/ports/engines/queries/aggregation/group_by_having/_fixture/decimal/generated/contract.d.ts index 4d36ded14a38..6974240b4843 100644 --- a/test/integration/test/ports/engines/queries/aggregation/group_by_having/_fixture/decimal/generated/contract.d.ts +++ b/test/integration/test/ports/engines/queries/aggregation/group_by_having/_fixture/decimal/generated/contract.d.ts @@ -42,6 +42,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; @@ -91,6 +95,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; @@ -100,6 +108,14 @@ export type AggregateTypes = { readonly output: 'pg/time-temporal@1'; readonly nullable: true; }; + readonly 'pg/timestamp-date@1': { + readonly output: 'pg/timestamp-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-instant@1': { + readonly output: 'pg/timestamp-instant@1'; + readonly nullable: true; + }; readonly 'pg/timestamp-string@1': { readonly output: 'pg/timestamp-string@1'; readonly nullable: true; @@ -151,6 +167,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; @@ -160,6 +180,14 @@ export type AggregateTypes = { readonly output: 'pg/time-temporal@1'; readonly nullable: true; }; + readonly 'pg/timestamp-date@1': { + readonly output: 'pg/timestamp-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-instant@1': { + readonly output: 'pg/timestamp-instant@1'; + readonly nullable: true; + }; readonly 'pg/timestamp-string@1': { readonly output: 'pg/timestamp-string@1'; readonly nullable: true; @@ -203,6 +231,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; diff --git a/test/integration/test/ports/engines/queries/aggregation/many_count_relation/_fixture/compound/generated/contract.d.ts b/test/integration/test/ports/engines/queries/aggregation/many_count_relation/_fixture/compound/generated/contract.d.ts index dcf67076ee4f..d3af49d9e52f 100644 --- a/test/integration/test/ports/engines/queries/aggregation/many_count_relation/_fixture/compound/generated/contract.d.ts +++ b/test/integration/test/ports/engines/queries/aggregation/many_count_relation/_fixture/compound/generated/contract.d.ts @@ -42,6 +42,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; @@ -91,6 +95,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; @@ -100,6 +108,14 @@ export type AggregateTypes = { readonly output: 'pg/time-temporal@1'; readonly nullable: true; }; + readonly 'pg/timestamp-date@1': { + readonly output: 'pg/timestamp-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-instant@1': { + readonly output: 'pg/timestamp-instant@1'; + readonly nullable: true; + }; readonly 'pg/timestamp-string@1': { readonly output: 'pg/timestamp-string@1'; readonly nullable: true; @@ -151,6 +167,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; @@ -160,6 +180,14 @@ export type AggregateTypes = { readonly output: 'pg/time-temporal@1'; readonly nullable: true; }; + readonly 'pg/timestamp-date@1': { + readonly output: 'pg/timestamp-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-instant@1': { + readonly output: 'pg/timestamp-instant@1'; + readonly nullable: true; + }; readonly 'pg/timestamp-string@1': { readonly output: 'pg/timestamp-string@1'; readonly nullable: true; @@ -203,6 +231,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; diff --git a/test/integration/test/ports/engines/queries/aggregation/many_count_relation/_fixture/generated/contract.d.ts b/test/integration/test/ports/engines/queries/aggregation/many_count_relation/_fixture/generated/contract.d.ts index 6a12f4eaaacd..b2cf70e906aa 100644 --- a/test/integration/test/ports/engines/queries/aggregation/many_count_relation/_fixture/generated/contract.d.ts +++ b/test/integration/test/ports/engines/queries/aggregation/many_count_relation/_fixture/generated/contract.d.ts @@ -42,6 +42,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; @@ -91,6 +95,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; @@ -100,6 +108,14 @@ export type AggregateTypes = { readonly output: 'pg/time-temporal@1'; readonly nullable: true; }; + readonly 'pg/timestamp-date@1': { + readonly output: 'pg/timestamp-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-instant@1': { + readonly output: 'pg/timestamp-instant@1'; + readonly nullable: true; + }; readonly 'pg/timestamp-string@1': { readonly output: 'pg/timestamp-string@1'; readonly nullable: true; @@ -151,6 +167,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; @@ -160,6 +180,14 @@ export type AggregateTypes = { readonly output: 'pg/time-temporal@1'; readonly nullable: true; }; + readonly 'pg/timestamp-date@1': { + readonly output: 'pg/timestamp-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-instant@1': { + readonly output: 'pg/timestamp-instant@1'; + readonly nullable: true; + }; readonly 'pg/timestamp-string@1': { readonly output: 'pg/timestamp-string@1'; readonly nullable: true; @@ -203,6 +231,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; diff --git a/test/integration/test/ports/engines/queries/aggregation/many_count_relation/_fixture/nested/generated/contract.d.ts b/test/integration/test/ports/engines/queries/aggregation/many_count_relation/_fixture/nested/generated/contract.d.ts index 02dc11d0e377..239906d2ec26 100644 --- a/test/integration/test/ports/engines/queries/aggregation/many_count_relation/_fixture/nested/generated/contract.d.ts +++ b/test/integration/test/ports/engines/queries/aggregation/many_count_relation/_fixture/nested/generated/contract.d.ts @@ -42,6 +42,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; @@ -91,6 +95,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; @@ -100,6 +108,14 @@ export type AggregateTypes = { readonly output: 'pg/time-temporal@1'; readonly nullable: true; }; + readonly 'pg/timestamp-date@1': { + readonly output: 'pg/timestamp-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-instant@1': { + readonly output: 'pg/timestamp-instant@1'; + readonly nullable: true; + }; readonly 'pg/timestamp-string@1': { readonly output: 'pg/timestamp-string@1'; readonly nullable: true; @@ -151,6 +167,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; @@ -160,6 +180,14 @@ export type AggregateTypes = { readonly output: 'pg/time-temporal@1'; readonly nullable: true; }; + readonly 'pg/timestamp-date@1': { + readonly output: 'pg/timestamp-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-instant@1': { + readonly output: 'pg/timestamp-instant@1'; + readonly nullable: true; + }; readonly 'pg/timestamp-string@1': { readonly output: 'pg/timestamp-string@1'; readonly nullable: true; @@ -203,6 +231,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; diff --git a/test/integration/test/ports/engines/queries/aggregation/many_count_relation/_fixture/self/generated/contract.d.ts b/test/integration/test/ports/engines/queries/aggregation/many_count_relation/_fixture/self/generated/contract.d.ts index 85f7eb4d6b2c..6bc90cc15bb3 100644 --- a/test/integration/test/ports/engines/queries/aggregation/many_count_relation/_fixture/self/generated/contract.d.ts +++ b/test/integration/test/ports/engines/queries/aggregation/many_count_relation/_fixture/self/generated/contract.d.ts @@ -42,6 +42,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; @@ -91,6 +95,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; @@ -100,6 +108,14 @@ export type AggregateTypes = { readonly output: 'pg/time-temporal@1'; readonly nullable: true; }; + readonly 'pg/timestamp-date@1': { + readonly output: 'pg/timestamp-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-instant@1': { + readonly output: 'pg/timestamp-instant@1'; + readonly nullable: true; + }; readonly 'pg/timestamp-string@1': { readonly output: 'pg/timestamp-string@1'; readonly nullable: true; @@ -151,6 +167,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; @@ -160,6 +180,14 @@ export type AggregateTypes = { readonly output: 'pg/time-temporal@1'; readonly nullable: true; }; + readonly 'pg/timestamp-date@1': { + readonly output: 'pg/timestamp-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-instant@1': { + readonly output: 'pg/timestamp-instant@1'; + readonly nullable: true; + }; readonly 'pg/timestamp-string@1': { readonly output: 'pg/timestamp-string@1'; readonly nullable: true; @@ -203,6 +231,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; diff --git a/test/integration/test/ports/engines/queries/aggregation/max/_fixture/common/generated/contract.d.ts b/test/integration/test/ports/engines/queries/aggregation/max/_fixture/common/generated/contract.d.ts index b8784e99954f..341641c1724f 100644 --- a/test/integration/test/ports/engines/queries/aggregation/max/_fixture/common/generated/contract.d.ts +++ b/test/integration/test/ports/engines/queries/aggregation/max/_fixture/common/generated/contract.d.ts @@ -42,6 +42,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; @@ -91,6 +95,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; @@ -100,6 +108,14 @@ export type AggregateTypes = { readonly output: 'pg/time-temporal@1'; readonly nullable: true; }; + readonly 'pg/timestamp-date@1': { + readonly output: 'pg/timestamp-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-instant@1': { + readonly output: 'pg/timestamp-instant@1'; + readonly nullable: true; + }; readonly 'pg/timestamp-string@1': { readonly output: 'pg/timestamp-string@1'; readonly nullable: true; @@ -151,6 +167,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; @@ -160,6 +180,14 @@ export type AggregateTypes = { readonly output: 'pg/time-temporal@1'; readonly nullable: true; }; + readonly 'pg/timestamp-date@1': { + readonly output: 'pg/timestamp-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-instant@1': { + readonly output: 'pg/timestamp-instant@1'; + readonly nullable: true; + }; readonly 'pg/timestamp-string@1': { readonly output: 'pg/timestamp-string@1'; readonly nullable: true; @@ -203,6 +231,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; diff --git a/test/integration/test/ports/engines/queries/aggregation/max/_fixture/decimal/generated/contract.d.ts b/test/integration/test/ports/engines/queries/aggregation/max/_fixture/decimal/generated/contract.d.ts index 1ff533a6d9a1..6c6df1fc1559 100644 --- a/test/integration/test/ports/engines/queries/aggregation/max/_fixture/decimal/generated/contract.d.ts +++ b/test/integration/test/ports/engines/queries/aggregation/max/_fixture/decimal/generated/contract.d.ts @@ -42,6 +42,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; @@ -91,6 +95,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; @@ -100,6 +108,14 @@ export type AggregateTypes = { readonly output: 'pg/time-temporal@1'; readonly nullable: true; }; + readonly 'pg/timestamp-date@1': { + readonly output: 'pg/timestamp-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-instant@1': { + readonly output: 'pg/timestamp-instant@1'; + readonly nullable: true; + }; readonly 'pg/timestamp-string@1': { readonly output: 'pg/timestamp-string@1'; readonly nullable: true; @@ -151,6 +167,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; @@ -160,6 +180,14 @@ export type AggregateTypes = { readonly output: 'pg/time-temporal@1'; readonly nullable: true; }; + readonly 'pg/timestamp-date@1': { + readonly output: 'pg/timestamp-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-instant@1': { + readonly output: 'pg/timestamp-instant@1'; + readonly nullable: true; + }; readonly 'pg/timestamp-string@1': { readonly output: 'pg/timestamp-string@1'; readonly nullable: true; @@ -203,6 +231,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; diff --git a/test/integration/test/ports/engines/queries/aggregation/min/_fixture/common/generated/contract.d.ts b/test/integration/test/ports/engines/queries/aggregation/min/_fixture/common/generated/contract.d.ts index b8784e99954f..341641c1724f 100644 --- a/test/integration/test/ports/engines/queries/aggregation/min/_fixture/common/generated/contract.d.ts +++ b/test/integration/test/ports/engines/queries/aggregation/min/_fixture/common/generated/contract.d.ts @@ -42,6 +42,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; @@ -91,6 +95,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; @@ -100,6 +108,14 @@ export type AggregateTypes = { readonly output: 'pg/time-temporal@1'; readonly nullable: true; }; + readonly 'pg/timestamp-date@1': { + readonly output: 'pg/timestamp-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-instant@1': { + readonly output: 'pg/timestamp-instant@1'; + readonly nullable: true; + }; readonly 'pg/timestamp-string@1': { readonly output: 'pg/timestamp-string@1'; readonly nullable: true; @@ -151,6 +167,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; @@ -160,6 +180,14 @@ export type AggregateTypes = { readonly output: 'pg/time-temporal@1'; readonly nullable: true; }; + readonly 'pg/timestamp-date@1': { + readonly output: 'pg/timestamp-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-instant@1': { + readonly output: 'pg/timestamp-instant@1'; + readonly nullable: true; + }; readonly 'pg/timestamp-string@1': { readonly output: 'pg/timestamp-string@1'; readonly nullable: true; @@ -203,6 +231,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; diff --git a/test/integration/test/ports/engines/queries/aggregation/min/_fixture/decimal/generated/contract.d.ts b/test/integration/test/ports/engines/queries/aggregation/min/_fixture/decimal/generated/contract.d.ts index 1ff533a6d9a1..6c6df1fc1559 100644 --- a/test/integration/test/ports/engines/queries/aggregation/min/_fixture/decimal/generated/contract.d.ts +++ b/test/integration/test/ports/engines/queries/aggregation/min/_fixture/decimal/generated/contract.d.ts @@ -42,6 +42,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; @@ -91,6 +95,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; @@ -100,6 +108,14 @@ export type AggregateTypes = { readonly output: 'pg/time-temporal@1'; readonly nullable: true; }; + readonly 'pg/timestamp-date@1': { + readonly output: 'pg/timestamp-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-instant@1': { + readonly output: 'pg/timestamp-instant@1'; + readonly nullable: true; + }; readonly 'pg/timestamp-string@1': { readonly output: 'pg/timestamp-string@1'; readonly nullable: true; @@ -151,6 +167,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; @@ -160,6 +180,14 @@ export type AggregateTypes = { readonly output: 'pg/time-temporal@1'; readonly nullable: true; }; + readonly 'pg/timestamp-date@1': { + readonly output: 'pg/timestamp-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-instant@1': { + readonly output: 'pg/timestamp-instant@1'; + readonly nullable: true; + }; readonly 'pg/timestamp-string@1': { readonly output: 'pg/timestamp-string@1'; readonly nullable: true; @@ -203,6 +231,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; diff --git a/test/integration/test/ports/engines/queries/aggregation/sum/_fixture/decimal/generated/contract.d.ts b/test/integration/test/ports/engines/queries/aggregation/sum/_fixture/decimal/generated/contract.d.ts index 1ff533a6d9a1..6c6df1fc1559 100644 --- a/test/integration/test/ports/engines/queries/aggregation/sum/_fixture/decimal/generated/contract.d.ts +++ b/test/integration/test/ports/engines/queries/aggregation/sum/_fixture/decimal/generated/contract.d.ts @@ -42,6 +42,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; @@ -91,6 +95,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; @@ -100,6 +108,14 @@ export type AggregateTypes = { readonly output: 'pg/time-temporal@1'; readonly nullable: true; }; + readonly 'pg/timestamp-date@1': { + readonly output: 'pg/timestamp-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-instant@1': { + readonly output: 'pg/timestamp-instant@1'; + readonly nullable: true; + }; readonly 'pg/timestamp-string@1': { readonly output: 'pg/timestamp-string@1'; readonly nullable: true; @@ -151,6 +167,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; @@ -160,6 +180,14 @@ export type AggregateTypes = { readonly output: 'pg/time-temporal@1'; readonly nullable: true; }; + readonly 'pg/timestamp-date@1': { + readonly output: 'pg/timestamp-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-instant@1': { + readonly output: 'pg/timestamp-instant@1'; + readonly nullable: true; + }; readonly 'pg/timestamp-string@1': { readonly output: 'pg/timestamp-string@1'; readonly nullable: true; @@ -203,6 +231,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; diff --git a/test/integration/test/ports/engines/queries/aggregation/sum/_fixture/numeric/generated/contract.d.ts b/test/integration/test/ports/engines/queries/aggregation/sum/_fixture/numeric/generated/contract.d.ts index b40f191cc247..f62f8dfc0029 100644 --- a/test/integration/test/ports/engines/queries/aggregation/sum/_fixture/numeric/generated/contract.d.ts +++ b/test/integration/test/ports/engines/queries/aggregation/sum/_fixture/numeric/generated/contract.d.ts @@ -42,6 +42,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; @@ -91,6 +95,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; @@ -100,6 +108,14 @@ export type AggregateTypes = { readonly output: 'pg/time-temporal@1'; readonly nullable: true; }; + readonly 'pg/timestamp-date@1': { + readonly output: 'pg/timestamp-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-instant@1': { + readonly output: 'pg/timestamp-instant@1'; + readonly nullable: true; + }; readonly 'pg/timestamp-string@1': { readonly output: 'pg/timestamp-string@1'; readonly nullable: true; @@ -151,6 +167,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; @@ -160,6 +180,14 @@ export type AggregateTypes = { readonly output: 'pg/time-temporal@1'; readonly nullable: true; }; + readonly 'pg/timestamp-date@1': { + readonly output: 'pg/timestamp-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-instant@1': { + readonly output: 'pg/timestamp-instant@1'; + readonly nullable: true; + }; readonly 'pg/timestamp-string@1': { readonly output: 'pg/timestamp-string@1'; readonly nullable: true; @@ -203,6 +231,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; diff --git a/test/integration/test/ports/engines/queries/aggregation/uniq-count-relation/_fixture/base/generated/contract.d.ts b/test/integration/test/ports/engines/queries/aggregation/uniq-count-relation/_fixture/base/generated/contract.d.ts index 6a12f4eaaacd..b2cf70e906aa 100644 --- a/test/integration/test/ports/engines/queries/aggregation/uniq-count-relation/_fixture/base/generated/contract.d.ts +++ b/test/integration/test/ports/engines/queries/aggregation/uniq-count-relation/_fixture/base/generated/contract.d.ts @@ -42,6 +42,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; @@ -91,6 +95,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; @@ -100,6 +108,14 @@ export type AggregateTypes = { readonly output: 'pg/time-temporal@1'; readonly nullable: true; }; + readonly 'pg/timestamp-date@1': { + readonly output: 'pg/timestamp-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-instant@1': { + readonly output: 'pg/timestamp-instant@1'; + readonly nullable: true; + }; readonly 'pg/timestamp-string@1': { readonly output: 'pg/timestamp-string@1'; readonly nullable: true; @@ -151,6 +167,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; @@ -160,6 +180,14 @@ export type AggregateTypes = { readonly output: 'pg/time-temporal@1'; readonly nullable: true; }; + readonly 'pg/timestamp-date@1': { + readonly output: 'pg/timestamp-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-instant@1': { + readonly output: 'pg/timestamp-instant@1'; + readonly nullable: true; + }; readonly 'pg/timestamp-string@1': { readonly output: 'pg/timestamp-string@1'; readonly nullable: true; @@ -203,6 +231,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; diff --git a/test/integration/test/ports/engines/queries/aggregation/uniq-count-relation/_fixture/nested/generated/contract.d.ts b/test/integration/test/ports/engines/queries/aggregation/uniq-count-relation/_fixture/nested/generated/contract.d.ts index 02dc11d0e377..239906d2ec26 100644 --- a/test/integration/test/ports/engines/queries/aggregation/uniq-count-relation/_fixture/nested/generated/contract.d.ts +++ b/test/integration/test/ports/engines/queries/aggregation/uniq-count-relation/_fixture/nested/generated/contract.d.ts @@ -42,6 +42,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; @@ -91,6 +95,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; @@ -100,6 +108,14 @@ export type AggregateTypes = { readonly output: 'pg/time-temporal@1'; readonly nullable: true; }; + readonly 'pg/timestamp-date@1': { + readonly output: 'pg/timestamp-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-instant@1': { + readonly output: 'pg/timestamp-instant@1'; + readonly nullable: true; + }; readonly 'pg/timestamp-string@1': { readonly output: 'pg/timestamp-string@1'; readonly nullable: true; @@ -151,6 +167,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; @@ -160,6 +180,14 @@ export type AggregateTypes = { readonly output: 'pg/time-temporal@1'; readonly nullable: true; }; + readonly 'pg/timestamp-date@1': { + readonly output: 'pg/timestamp-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-instant@1': { + readonly output: 'pg/timestamp-instant@1'; + readonly nullable: true; + }; readonly 'pg/timestamp-string@1': { readonly output: 'pg/timestamp-string@1'; readonly nullable: true; @@ -203,6 +231,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; diff --git a/test/integration/test/ports/engines/queries/data_types/bigint/_fixture/generated/contract.d.ts b/test/integration/test/ports/engines/queries/data_types/bigint/_fixture/generated/contract.d.ts index cf24259e9beb..f7a2f4f95ce5 100644 --- a/test/integration/test/ports/engines/queries/data_types/bigint/_fixture/generated/contract.d.ts +++ b/test/integration/test/ports/engines/queries/data_types/bigint/_fixture/generated/contract.d.ts @@ -42,6 +42,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; @@ -91,6 +95,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; @@ -100,6 +108,14 @@ export type AggregateTypes = { readonly output: 'pg/time-temporal@1'; readonly nullable: true; }; + readonly 'pg/timestamp-date@1': { + readonly output: 'pg/timestamp-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-instant@1': { + readonly output: 'pg/timestamp-instant@1'; + readonly nullable: true; + }; readonly 'pg/timestamp-string@1': { readonly output: 'pg/timestamp-string@1'; readonly nullable: true; @@ -151,6 +167,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; @@ -160,6 +180,14 @@ export type AggregateTypes = { readonly output: 'pg/time-temporal@1'; readonly nullable: true; }; + readonly 'pg/timestamp-date@1': { + readonly output: 'pg/timestamp-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-instant@1': { + readonly output: 'pg/timestamp-instant@1'; + readonly nullable: true; + }; readonly 'pg/timestamp-string@1': { readonly output: 'pg/timestamp-string@1'; readonly nullable: true; @@ -203,6 +231,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; diff --git a/test/integration/test/ports/engines/queries/data_types/bool/_fixture/generated/contract.d.ts b/test/integration/test/ports/engines/queries/data_types/bool/_fixture/generated/contract.d.ts index cf24259e9beb..f7a2f4f95ce5 100644 --- a/test/integration/test/ports/engines/queries/data_types/bool/_fixture/generated/contract.d.ts +++ b/test/integration/test/ports/engines/queries/data_types/bool/_fixture/generated/contract.d.ts @@ -42,6 +42,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; @@ -91,6 +95,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; @@ -100,6 +108,14 @@ export type AggregateTypes = { readonly output: 'pg/time-temporal@1'; readonly nullable: true; }; + readonly 'pg/timestamp-date@1': { + readonly output: 'pg/timestamp-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-instant@1': { + readonly output: 'pg/timestamp-instant@1'; + readonly nullable: true; + }; readonly 'pg/timestamp-string@1': { readonly output: 'pg/timestamp-string@1'; readonly nullable: true; @@ -151,6 +167,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; @@ -160,6 +180,14 @@ export type AggregateTypes = { readonly output: 'pg/time-temporal@1'; readonly nullable: true; }; + readonly 'pg/timestamp-date@1': { + readonly output: 'pg/timestamp-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-instant@1': { + readonly output: 'pg/timestamp-instant@1'; + readonly nullable: true; + }; readonly 'pg/timestamp-string@1': { readonly output: 'pg/timestamp-string@1'; readonly nullable: true; @@ -203,6 +231,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; diff --git a/test/integration/test/ports/engines/queries/data_types/bytes/_fixture/relations/generated/contract.d.ts b/test/integration/test/ports/engines/queries/data_types/bytes/_fixture/relations/generated/contract.d.ts index 97890ba8fe1a..06ee8b61aed2 100644 --- a/test/integration/test/ports/engines/queries/data_types/bytes/_fixture/relations/generated/contract.d.ts +++ b/test/integration/test/ports/engines/queries/data_types/bytes/_fixture/relations/generated/contract.d.ts @@ -42,6 +42,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; @@ -91,6 +95,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; @@ -100,6 +108,14 @@ export type AggregateTypes = { readonly output: 'pg/time-temporal@1'; readonly nullable: true; }; + readonly 'pg/timestamp-date@1': { + readonly output: 'pg/timestamp-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-instant@1': { + readonly output: 'pg/timestamp-instant@1'; + readonly nullable: true; + }; readonly 'pg/timestamp-string@1': { readonly output: 'pg/timestamp-string@1'; readonly nullable: true; @@ -151,6 +167,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; @@ -160,6 +180,14 @@ export type AggregateTypes = { readonly output: 'pg/time-temporal@1'; readonly nullable: true; }; + readonly 'pg/timestamp-date@1': { + readonly output: 'pg/timestamp-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-instant@1': { + readonly output: 'pg/timestamp-instant@1'; + readonly nullable: true; + }; readonly 'pg/timestamp-string@1': { readonly output: 'pg/timestamp-string@1'; readonly nullable: true; @@ -203,6 +231,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; diff --git a/test/integration/test/ports/engines/queries/data_types/bytes/_fixture/scalars/generated/contract.d.ts b/test/integration/test/ports/engines/queries/data_types/bytes/_fixture/scalars/generated/contract.d.ts index cf24259e9beb..f7a2f4f95ce5 100644 --- a/test/integration/test/ports/engines/queries/data_types/bytes/_fixture/scalars/generated/contract.d.ts +++ b/test/integration/test/ports/engines/queries/data_types/bytes/_fixture/scalars/generated/contract.d.ts @@ -42,6 +42,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; @@ -91,6 +95,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; @@ -100,6 +108,14 @@ export type AggregateTypes = { readonly output: 'pg/time-temporal@1'; readonly nullable: true; }; + readonly 'pg/timestamp-date@1': { + readonly output: 'pg/timestamp-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-instant@1': { + readonly output: 'pg/timestamp-instant@1'; + readonly nullable: true; + }; readonly 'pg/timestamp-string@1': { readonly output: 'pg/timestamp-string@1'; readonly nullable: true; @@ -151,6 +167,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; @@ -160,6 +180,14 @@ export type AggregateTypes = { readonly output: 'pg/time-temporal@1'; readonly nullable: true; }; + readonly 'pg/timestamp-date@1': { + readonly output: 'pg/timestamp-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-instant@1': { + readonly output: 'pg/timestamp-instant@1'; + readonly nullable: true; + }; readonly 'pg/timestamp-string@1': { readonly output: 'pg/timestamp-string@1'; readonly nullable: true; @@ -203,6 +231,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; diff --git a/test/integration/test/ports/engines/queries/data_types/datetime/_fixture/generated/contract.d.ts b/test/integration/test/ports/engines/queries/data_types/datetime/_fixture/generated/contract.d.ts index cf24259e9beb..f7a2f4f95ce5 100644 --- a/test/integration/test/ports/engines/queries/data_types/datetime/_fixture/generated/contract.d.ts +++ b/test/integration/test/ports/engines/queries/data_types/datetime/_fixture/generated/contract.d.ts @@ -42,6 +42,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; @@ -91,6 +95,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; @@ -100,6 +108,14 @@ export type AggregateTypes = { readonly output: 'pg/time-temporal@1'; readonly nullable: true; }; + readonly 'pg/timestamp-date@1': { + readonly output: 'pg/timestamp-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-instant@1': { + readonly output: 'pg/timestamp-instant@1'; + readonly nullable: true; + }; readonly 'pg/timestamp-string@1': { readonly output: 'pg/timestamp-string@1'; readonly nullable: true; @@ -151,6 +167,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; @@ -160,6 +180,14 @@ export type AggregateTypes = { readonly output: 'pg/time-temporal@1'; readonly nullable: true; }; + readonly 'pg/timestamp-date@1': { + readonly output: 'pg/timestamp-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-instant@1': { + readonly output: 'pg/timestamp-instant@1'; + readonly nullable: true; + }; readonly 'pg/timestamp-string@1': { readonly output: 'pg/timestamp-string@1'; readonly nullable: true; @@ -203,6 +231,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; diff --git a/test/integration/test/ports/engines/queries/data_types/decimal/_fixture/generated/contract.d.ts b/test/integration/test/ports/engines/queries/data_types/decimal/_fixture/generated/contract.d.ts index 6636199576e6..e2d8eb53b59d 100644 --- a/test/integration/test/ports/engines/queries/data_types/decimal/_fixture/generated/contract.d.ts +++ b/test/integration/test/ports/engines/queries/data_types/decimal/_fixture/generated/contract.d.ts @@ -42,6 +42,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; @@ -91,6 +95,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; @@ -100,6 +108,14 @@ export type AggregateTypes = { readonly output: 'pg/time-temporal@1'; readonly nullable: true; }; + readonly 'pg/timestamp-date@1': { + readonly output: 'pg/timestamp-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-instant@1': { + readonly output: 'pg/timestamp-instant@1'; + readonly nullable: true; + }; readonly 'pg/timestamp-string@1': { readonly output: 'pg/timestamp-string@1'; readonly nullable: true; @@ -151,6 +167,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; @@ -160,6 +180,14 @@ export type AggregateTypes = { readonly output: 'pg/time-temporal@1'; readonly nullable: true; }; + readonly 'pg/timestamp-date@1': { + readonly output: 'pg/timestamp-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-instant@1': { + readonly output: 'pg/timestamp-instant@1'; + readonly nullable: true; + }; readonly 'pg/timestamp-string@1': { readonly output: 'pg/timestamp-string@1'; readonly nullable: true; @@ -203,6 +231,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; diff --git a/test/integration/test/ports/engines/queries/data_types/enum_type/_fixture/postgres/generated/contract.d.ts b/test/integration/test/ports/engines/queries/data_types/enum_type/_fixture/postgres/generated/contract.d.ts index 6e84ee53985e..7a627cb1b347 100644 --- a/test/integration/test/ports/engines/queries/data_types/enum_type/_fixture/postgres/generated/contract.d.ts +++ b/test/integration/test/ports/engines/queries/data_types/enum_type/_fixture/postgres/generated/contract.d.ts @@ -42,6 +42,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; @@ -91,6 +95,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; @@ -100,6 +108,14 @@ export type AggregateTypes = { readonly output: 'pg/time-temporal@1'; readonly nullable: true; }; + readonly 'pg/timestamp-date@1': { + readonly output: 'pg/timestamp-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-instant@1': { + readonly output: 'pg/timestamp-instant@1'; + readonly nullable: true; + }; readonly 'pg/timestamp-string@1': { readonly output: 'pg/timestamp-string@1'; readonly nullable: true; @@ -151,6 +167,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; @@ -160,6 +180,14 @@ export type AggregateTypes = { readonly output: 'pg/time-temporal@1'; readonly nullable: true; }; + readonly 'pg/timestamp-date@1': { + readonly output: 'pg/timestamp-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-instant@1': { + readonly output: 'pg/timestamp-instant@1'; + readonly nullable: true; + }; readonly 'pg/timestamp-string@1': { readonly output: 'pg/timestamp-string@1'; readonly nullable: true; @@ -203,6 +231,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; diff --git a/test/integration/test/ports/engines/queries/data_types/float/_fixture/generated/contract.d.ts b/test/integration/test/ports/engines/queries/data_types/float/_fixture/generated/contract.d.ts index cf24259e9beb..f7a2f4f95ce5 100644 --- a/test/integration/test/ports/engines/queries/data_types/float/_fixture/generated/contract.d.ts +++ b/test/integration/test/ports/engines/queries/data_types/float/_fixture/generated/contract.d.ts @@ -42,6 +42,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; @@ -91,6 +95,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; @@ -100,6 +108,14 @@ export type AggregateTypes = { readonly output: 'pg/time-temporal@1'; readonly nullable: true; }; + readonly 'pg/timestamp-date@1': { + readonly output: 'pg/timestamp-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-instant@1': { + readonly output: 'pg/timestamp-instant@1'; + readonly nullable: true; + }; readonly 'pg/timestamp-string@1': { readonly output: 'pg/timestamp-string@1'; readonly nullable: true; @@ -151,6 +167,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; @@ -160,6 +180,14 @@ export type AggregateTypes = { readonly output: 'pg/time-temporal@1'; readonly nullable: true; }; + readonly 'pg/timestamp-date@1': { + readonly output: 'pg/timestamp-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-instant@1': { + readonly output: 'pg/timestamp-instant@1'; + readonly nullable: true; + }; readonly 'pg/timestamp-string@1': { readonly output: 'pg/timestamp-string@1'; readonly nullable: true; @@ -203,6 +231,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; diff --git a/test/integration/test/ports/engines/queries/data_types/int/_fixture/generated/contract.d.ts b/test/integration/test/ports/engines/queries/data_types/int/_fixture/generated/contract.d.ts index cf24259e9beb..f7a2f4f95ce5 100644 --- a/test/integration/test/ports/engines/queries/data_types/int/_fixture/generated/contract.d.ts +++ b/test/integration/test/ports/engines/queries/data_types/int/_fixture/generated/contract.d.ts @@ -42,6 +42,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; @@ -91,6 +95,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; @@ -100,6 +108,14 @@ export type AggregateTypes = { readonly output: 'pg/time-temporal@1'; readonly nullable: true; }; + readonly 'pg/timestamp-date@1': { + readonly output: 'pg/timestamp-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-instant@1': { + readonly output: 'pg/timestamp-instant@1'; + readonly nullable: true; + }; readonly 'pg/timestamp-string@1': { readonly output: 'pg/timestamp-string@1'; readonly nullable: true; @@ -151,6 +167,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; @@ -160,6 +180,14 @@ export type AggregateTypes = { readonly output: 'pg/time-temporal@1'; readonly nullable: true; }; + readonly 'pg/timestamp-date@1': { + readonly output: 'pg/timestamp-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-instant@1': { + readonly output: 'pg/timestamp-instant@1'; + readonly nullable: true; + }; readonly 'pg/timestamp-string@1': { readonly output: 'pg/timestamp-string@1'; readonly nullable: true; @@ -203,6 +231,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; diff --git a/test/integration/test/ports/engines/queries/data_types/json/_fixture/scalar/generated/contract.d.ts b/test/integration/test/ports/engines/queries/data_types/json/_fixture/scalar/generated/contract.d.ts index e130b6241483..7954f1c9e0f4 100644 --- a/test/integration/test/ports/engines/queries/data_types/json/_fixture/scalar/generated/contract.d.ts +++ b/test/integration/test/ports/engines/queries/data_types/json/_fixture/scalar/generated/contract.d.ts @@ -42,6 +42,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; @@ -91,6 +95,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; @@ -100,6 +108,14 @@ export type AggregateTypes = { readonly output: 'pg/time-temporal@1'; readonly nullable: true; }; + readonly 'pg/timestamp-date@1': { + readonly output: 'pg/timestamp-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-instant@1': { + readonly output: 'pg/timestamp-instant@1'; + readonly nullable: true; + }; readonly 'pg/timestamp-string@1': { readonly output: 'pg/timestamp-string@1'; readonly nullable: true; @@ -151,6 +167,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; @@ -160,6 +180,14 @@ export type AggregateTypes = { readonly output: 'pg/time-temporal@1'; readonly nullable: true; }; + readonly 'pg/timestamp-date@1': { + readonly output: 'pg/timestamp-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-instant@1': { + readonly output: 'pg/timestamp-instant@1'; + readonly nullable: true; + }; readonly 'pg/timestamp-string@1': { readonly output: 'pg/timestamp-string@1'; readonly nullable: true; @@ -203,6 +231,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; diff --git a/test/integration/test/ports/engines/queries/data_types/native/postgres/_fixture/other/generated/contract.d.ts b/test/integration/test/ports/engines/queries/data_types/native/postgres/_fixture/other/generated/contract.d.ts index 360807b20de6..92edb32bc11f 100644 --- a/test/integration/test/ports/engines/queries/data_types/native/postgres/_fixture/other/generated/contract.d.ts +++ b/test/integration/test/ports/engines/queries/data_types/native/postgres/_fixture/other/generated/contract.d.ts @@ -42,6 +42,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; @@ -91,6 +95,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; @@ -100,6 +108,14 @@ export type AggregateTypes = { readonly output: 'pg/time-temporal@1'; readonly nullable: true; }; + readonly 'pg/timestamp-date@1': { + readonly output: 'pg/timestamp-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-instant@1': { + readonly output: 'pg/timestamp-instant@1'; + readonly nullable: true; + }; readonly 'pg/timestamp-string@1': { readonly output: 'pg/timestamp-string@1'; readonly nullable: true; @@ -151,6 +167,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; @@ -160,6 +180,14 @@ export type AggregateTypes = { readonly output: 'pg/time-temporal@1'; readonly nullable: true; }; + readonly 'pg/timestamp-date@1': { + readonly output: 'pg/timestamp-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-instant@1': { + readonly output: 'pg/timestamp-instant@1'; + readonly nullable: true; + }; readonly 'pg/timestamp-string@1': { readonly output: 'pg/timestamp-string@1'; readonly nullable: true; @@ -203,6 +231,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; diff --git a/test/integration/test/ports/engines/queries/data_types/native/postgres/_fixture/string/generated/contract.d.ts b/test/integration/test/ports/engines/queries/data_types/native/postgres/_fixture/string/generated/contract.d.ts index b30fa3aa4b8f..798ac18f2e96 100644 --- a/test/integration/test/ports/engines/queries/data_types/native/postgres/_fixture/string/generated/contract.d.ts +++ b/test/integration/test/ports/engines/queries/data_types/native/postgres/_fixture/string/generated/contract.d.ts @@ -42,6 +42,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; @@ -91,6 +95,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; @@ -100,6 +108,14 @@ export type AggregateTypes = { readonly output: 'pg/time-temporal@1'; readonly nullable: true; }; + readonly 'pg/timestamp-date@1': { + readonly output: 'pg/timestamp-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-instant@1': { + readonly output: 'pg/timestamp-instant@1'; + readonly nullable: true; + }; readonly 'pg/timestamp-string@1': { readonly output: 'pg/timestamp-string@1'; readonly nullable: true; @@ -151,6 +167,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; @@ -160,6 +180,14 @@ export type AggregateTypes = { readonly output: 'pg/time-temporal@1'; readonly nullable: true; }; + readonly 'pg/timestamp-date@1': { + readonly output: 'pg/timestamp-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-instant@1': { + readonly output: 'pg/timestamp-instant@1'; + readonly nullable: true; + }; readonly 'pg/timestamp-string@1': { readonly output: 'pg/timestamp-string@1'; readonly nullable: true; @@ -203,6 +231,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; diff --git a/test/integration/test/ports/engines/queries/data_types/string/_fixture/generated/contract.d.ts b/test/integration/test/ports/engines/queries/data_types/string/_fixture/generated/contract.d.ts index cf24259e9beb..f7a2f4f95ce5 100644 --- a/test/integration/test/ports/engines/queries/data_types/string/_fixture/generated/contract.d.ts +++ b/test/integration/test/ports/engines/queries/data_types/string/_fixture/generated/contract.d.ts @@ -42,6 +42,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; @@ -91,6 +95,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; @@ -100,6 +108,14 @@ export type AggregateTypes = { readonly output: 'pg/time-temporal@1'; readonly nullable: true; }; + readonly 'pg/timestamp-date@1': { + readonly output: 'pg/timestamp-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-instant@1': { + readonly output: 'pg/timestamp-instant@1'; + readonly nullable: true; + }; readonly 'pg/timestamp-string@1': { readonly output: 'pg/timestamp-string@1'; readonly nullable: true; @@ -151,6 +167,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; @@ -160,6 +180,14 @@ export type AggregateTypes = { readonly output: 'pg/time-temporal@1'; readonly nullable: true; }; + readonly 'pg/timestamp-date@1': { + readonly output: 'pg/timestamp-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-instant@1': { + readonly output: 'pg/timestamp-instant@1'; + readonly nullable: true; + }; readonly 'pg/timestamp-string@1': { readonly output: 'pg/timestamp-string@1'; readonly nullable: true; @@ -203,6 +231,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; diff --git a/test/integration/test/ports/engines/queries/data_types/through_relation/_fixture/common/generated/contract.d.ts b/test/integration/test/ports/engines/queries/data_types/through_relation/_fixture/common/generated/contract.d.ts index 41b383a057c8..c00dba970752 100644 --- a/test/integration/test/ports/engines/queries/data_types/through_relation/_fixture/common/generated/contract.d.ts +++ b/test/integration/test/ports/engines/queries/data_types/through_relation/_fixture/common/generated/contract.d.ts @@ -42,6 +42,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; @@ -91,6 +95,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; @@ -100,6 +108,14 @@ export type AggregateTypes = { readonly output: 'pg/time-temporal@1'; readonly nullable: true; }; + readonly 'pg/timestamp-date@1': { + readonly output: 'pg/timestamp-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-instant@1': { + readonly output: 'pg/timestamp-instant@1'; + readonly nullable: true; + }; readonly 'pg/timestamp-string@1': { readonly output: 'pg/timestamp-string@1'; readonly nullable: true; @@ -151,6 +167,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; @@ -160,6 +180,14 @@ export type AggregateTypes = { readonly output: 'pg/time-temporal@1'; readonly nullable: true; }; + readonly 'pg/timestamp-date@1': { + readonly output: 'pg/timestamp-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-instant@1': { + readonly output: 'pg/timestamp-instant@1'; + readonly nullable: true; + }; readonly 'pg/timestamp-string@1': { readonly output: 'pg/timestamp-string@1'; readonly nullable: true; @@ -203,6 +231,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; diff --git a/test/integration/test/ports/engines/queries/data_types/through_relation/_fixture/decimal/generated/contract.d.ts b/test/integration/test/ports/engines/queries/data_types/through_relation/_fixture/decimal/generated/contract.d.ts index 5a23dcace66b..776330827bce 100644 --- a/test/integration/test/ports/engines/queries/data_types/through_relation/_fixture/decimal/generated/contract.d.ts +++ b/test/integration/test/ports/engines/queries/data_types/through_relation/_fixture/decimal/generated/contract.d.ts @@ -42,6 +42,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; @@ -91,6 +95,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; @@ -100,6 +108,14 @@ export type AggregateTypes = { readonly output: 'pg/time-temporal@1'; readonly nullable: true; }; + readonly 'pg/timestamp-date@1': { + readonly output: 'pg/timestamp-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-instant@1': { + readonly output: 'pg/timestamp-instant@1'; + readonly nullable: true; + }; readonly 'pg/timestamp-string@1': { readonly output: 'pg/timestamp-string@1'; readonly nullable: true; @@ -151,6 +167,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; @@ -160,6 +180,14 @@ export type AggregateTypes = { readonly output: 'pg/time-temporal@1'; readonly nullable: true; }; + readonly 'pg/timestamp-date@1': { + readonly output: 'pg/timestamp-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-instant@1': { + readonly output: 'pg/timestamp-instant@1'; + readonly nullable: true; + }; readonly 'pg/timestamp-string@1': { readonly output: 'pg/timestamp-string@1'; readonly nullable: true; @@ -203,6 +231,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; diff --git a/test/integration/test/ports/engines/queries/data_types/through_relation/_fixture/enum/generated/contract.d.ts b/test/integration/test/ports/engines/queries/data_types/through_relation/_fixture/enum/generated/contract.d.ts index 8323e337284a..eada9b85d619 100644 --- a/test/integration/test/ports/engines/queries/data_types/through_relation/_fixture/enum/generated/contract.d.ts +++ b/test/integration/test/ports/engines/queries/data_types/through_relation/_fixture/enum/generated/contract.d.ts @@ -42,6 +42,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; @@ -91,6 +95,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; @@ -100,6 +108,14 @@ export type AggregateTypes = { readonly output: 'pg/time-temporal@1'; readonly nullable: true; }; + readonly 'pg/timestamp-date@1': { + readonly output: 'pg/timestamp-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-instant@1': { + readonly output: 'pg/timestamp-instant@1'; + readonly nullable: true; + }; readonly 'pg/timestamp-string@1': { readonly output: 'pg/timestamp-string@1'; readonly nullable: true; @@ -151,6 +167,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; @@ -160,6 +180,14 @@ export type AggregateTypes = { readonly output: 'pg/time-temporal@1'; readonly nullable: true; }; + readonly 'pg/timestamp-date@1': { + readonly output: 'pg/timestamp-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-instant@1': { + readonly output: 'pg/timestamp-instant@1'; + readonly nullable: true; + }; readonly 'pg/timestamp-string@1': { readonly output: 'pg/timestamp-string@1'; readonly nullable: true; @@ -203,6 +231,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; diff --git a/test/integration/test/ports/engines/queries/data_types/through_relation/_fixture/json/generated/contract.d.ts b/test/integration/test/ports/engines/queries/data_types/through_relation/_fixture/json/generated/contract.d.ts index c5827a746e04..505fe2600868 100644 --- a/test/integration/test/ports/engines/queries/data_types/through_relation/_fixture/json/generated/contract.d.ts +++ b/test/integration/test/ports/engines/queries/data_types/through_relation/_fixture/json/generated/contract.d.ts @@ -42,6 +42,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; @@ -91,6 +95,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; @@ -100,6 +108,14 @@ export type AggregateTypes = { readonly output: 'pg/time-temporal@1'; readonly nullable: true; }; + readonly 'pg/timestamp-date@1': { + readonly output: 'pg/timestamp-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-instant@1': { + readonly output: 'pg/timestamp-instant@1'; + readonly nullable: true; + }; readonly 'pg/timestamp-string@1': { readonly output: 'pg/timestamp-string@1'; readonly nullable: true; @@ -151,6 +167,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; @@ -160,6 +180,14 @@ export type AggregateTypes = { readonly output: 'pg/time-temporal@1'; readonly nullable: true; }; + readonly 'pg/timestamp-date@1': { + readonly output: 'pg/timestamp-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-instant@1': { + readonly output: 'pg/timestamp-instant@1'; + readonly nullable: true; + }; readonly 'pg/timestamp-string@1': { readonly output: 'pg/timestamp-string@1'; readonly nullable: true; @@ -203,6 +231,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; diff --git a/test/integration/test/ports/engines/queries/data_types/through_relation/_fixture/lists/generated/contract.d.ts b/test/integration/test/ports/engines/queries/data_types/through_relation/_fixture/lists/generated/contract.d.ts index 350d0a549ed6..ca304388dfc4 100644 --- a/test/integration/test/ports/engines/queries/data_types/through_relation/_fixture/lists/generated/contract.d.ts +++ b/test/integration/test/ports/engines/queries/data_types/through_relation/_fixture/lists/generated/contract.d.ts @@ -42,6 +42,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; @@ -91,6 +95,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; @@ -100,6 +108,14 @@ export type AggregateTypes = { readonly output: 'pg/time-temporal@1'; readonly nullable: true; }; + readonly 'pg/timestamp-date@1': { + readonly output: 'pg/timestamp-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-instant@1': { + readonly output: 'pg/timestamp-instant@1'; + readonly nullable: true; + }; readonly 'pg/timestamp-string@1': { readonly output: 'pg/timestamp-string@1'; readonly nullable: true; @@ -151,6 +167,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; @@ -160,6 +180,14 @@ export type AggregateTypes = { readonly output: 'pg/time-temporal@1'; readonly nullable: true; }; + readonly 'pg/timestamp-date@1': { + readonly output: 'pg/timestamp-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-instant@1': { + readonly output: 'pg/timestamp-instant@1'; + readonly nullable: true; + }; readonly 'pg/timestamp-string@1': { readonly output: 'pg/timestamp-string@1'; readonly nullable: true; @@ -203,6 +231,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; diff --git a/test/integration/test/ports/engines/queries/distinct/_fixture/generated/contract.d.ts b/test/integration/test/ports/engines/queries/distinct/_fixture/generated/contract.d.ts index 966a66ef2215..aa3bb4d5f89d 100644 --- a/test/integration/test/ports/engines/queries/distinct/_fixture/generated/contract.d.ts +++ b/test/integration/test/ports/engines/queries/distinct/_fixture/generated/contract.d.ts @@ -42,6 +42,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; @@ -91,6 +95,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; @@ -100,6 +108,14 @@ export type AggregateTypes = { readonly output: 'pg/time-temporal@1'; readonly nullable: true; }; + readonly 'pg/timestamp-date@1': { + readonly output: 'pg/timestamp-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-instant@1': { + readonly output: 'pg/timestamp-instant@1'; + readonly nullable: true; + }; readonly 'pg/timestamp-string@1': { readonly output: 'pg/timestamp-string@1'; readonly nullable: true; @@ -151,6 +167,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; @@ -160,6 +180,14 @@ export type AggregateTypes = { readonly output: 'pg/time-temporal@1'; readonly nullable: true; }; + readonly 'pg/timestamp-date@1': { + readonly output: 'pg/timestamp-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-instant@1': { + readonly output: 'pg/timestamp-instant@1'; + readonly nullable: true; + }; readonly 'pg/timestamp-string@1': { readonly output: 'pg/timestamp-string@1'; readonly nullable: true; @@ -203,6 +231,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; diff --git a/test/integration/test/ports/engines/queries/filters/bigint_filter/_fixture/generated/contract.d.ts b/test/integration/test/ports/engines/queries/filters/bigint_filter/_fixture/generated/contract.d.ts index cf24259e9beb..f7a2f4f95ce5 100644 --- a/test/integration/test/ports/engines/queries/filters/bigint_filter/_fixture/generated/contract.d.ts +++ b/test/integration/test/ports/engines/queries/filters/bigint_filter/_fixture/generated/contract.d.ts @@ -42,6 +42,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; @@ -91,6 +95,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; @@ -100,6 +108,14 @@ export type AggregateTypes = { readonly output: 'pg/time-temporal@1'; readonly nullable: true; }; + readonly 'pg/timestamp-date@1': { + readonly output: 'pg/timestamp-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-instant@1': { + readonly output: 'pg/timestamp-instant@1'; + readonly nullable: true; + }; readonly 'pg/timestamp-string@1': { readonly output: 'pg/timestamp-string@1'; readonly nullable: true; @@ -151,6 +167,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; @@ -160,6 +180,14 @@ export type AggregateTypes = { readonly output: 'pg/time-temporal@1'; readonly nullable: true; }; + readonly 'pg/timestamp-date@1': { + readonly output: 'pg/timestamp-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-instant@1': { + readonly output: 'pg/timestamp-instant@1'; + readonly nullable: true; + }; readonly 'pg/timestamp-string@1': { readonly output: 'pg/timestamp-string@1'; readonly nullable: true; @@ -203,6 +231,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; diff --git a/test/integration/test/ports/engines/queries/filters/bytes_filter/_fixture/generated/contract.d.ts b/test/integration/test/ports/engines/queries/filters/bytes_filter/_fixture/generated/contract.d.ts index cf24259e9beb..f7a2f4f95ce5 100644 --- a/test/integration/test/ports/engines/queries/filters/bytes_filter/_fixture/generated/contract.d.ts +++ b/test/integration/test/ports/engines/queries/filters/bytes_filter/_fixture/generated/contract.d.ts @@ -42,6 +42,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; @@ -91,6 +95,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; @@ -100,6 +108,14 @@ export type AggregateTypes = { readonly output: 'pg/time-temporal@1'; readonly nullable: true; }; + readonly 'pg/timestamp-date@1': { + readonly output: 'pg/timestamp-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-instant@1': { + readonly output: 'pg/timestamp-instant@1'; + readonly nullable: true; + }; readonly 'pg/timestamp-string@1': { readonly output: 'pg/timestamp-string@1'; readonly nullable: true; @@ -151,6 +167,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; @@ -160,6 +180,14 @@ export type AggregateTypes = { readonly output: 'pg/time-temporal@1'; readonly nullable: true; }; + readonly 'pg/timestamp-date@1': { + readonly output: 'pg/timestamp-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-instant@1': { + readonly output: 'pg/timestamp-instant@1'; + readonly nullable: true; + }; readonly 'pg/timestamp-string@1': { readonly output: 'pg/timestamp-string@1'; readonly nullable: true; @@ -203,6 +231,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; diff --git a/test/integration/test/ports/engines/queries/filters/decimal_filter/_fixture/generated/contract.d.ts b/test/integration/test/ports/engines/queries/filters/decimal_filter/_fixture/generated/contract.d.ts index 079b9b6c72ac..aaa2f4e4d2d3 100644 --- a/test/integration/test/ports/engines/queries/filters/decimal_filter/_fixture/generated/contract.d.ts +++ b/test/integration/test/ports/engines/queries/filters/decimal_filter/_fixture/generated/contract.d.ts @@ -42,6 +42,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; @@ -91,6 +95,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; @@ -100,6 +108,14 @@ export type AggregateTypes = { readonly output: 'pg/time-temporal@1'; readonly nullable: true; }; + readonly 'pg/timestamp-date@1': { + readonly output: 'pg/timestamp-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-instant@1': { + readonly output: 'pg/timestamp-instant@1'; + readonly nullable: true; + }; readonly 'pg/timestamp-string@1': { readonly output: 'pg/timestamp-string@1'; readonly nullable: true; @@ -151,6 +167,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; @@ -160,6 +180,14 @@ export type AggregateTypes = { readonly output: 'pg/time-temporal@1'; readonly nullable: true; }; + readonly 'pg/timestamp-date@1': { + readonly output: 'pg/timestamp-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-instant@1': { + readonly output: 'pg/timestamp-instant@1'; + readonly nullable: true; + }; readonly 'pg/timestamp-string@1': { readonly output: 'pg/timestamp-string@1'; readonly nullable: true; @@ -203,6 +231,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; diff --git a/test/integration/test/ports/engines/queries/filters/field_reference/_fixture/decimal-list/generated/contract.d.ts b/test/integration/test/ports/engines/queries/filters/field_reference/_fixture/decimal-list/generated/contract.d.ts index e47dcc59abd5..6374d6ff5b37 100644 --- a/test/integration/test/ports/engines/queries/filters/field_reference/_fixture/decimal-list/generated/contract.d.ts +++ b/test/integration/test/ports/engines/queries/filters/field_reference/_fixture/decimal-list/generated/contract.d.ts @@ -42,6 +42,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; @@ -91,6 +95,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; @@ -100,6 +108,14 @@ export type AggregateTypes = { readonly output: 'pg/time-temporal@1'; readonly nullable: true; }; + readonly 'pg/timestamp-date@1': { + readonly output: 'pg/timestamp-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-instant@1': { + readonly output: 'pg/timestamp-instant@1'; + readonly nullable: true; + }; readonly 'pg/timestamp-string@1': { readonly output: 'pg/timestamp-string@1'; readonly nullable: true; @@ -151,6 +167,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; @@ -160,6 +180,14 @@ export type AggregateTypes = { readonly output: 'pg/time-temporal@1'; readonly nullable: true; }; + readonly 'pg/timestamp-date@1': { + readonly output: 'pg/timestamp-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-instant@1': { + readonly output: 'pg/timestamp-instant@1'; + readonly nullable: true; + }; readonly 'pg/timestamp-string@1': { readonly output: 'pg/timestamp-string@1'; readonly nullable: true; @@ -203,6 +231,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; diff --git a/test/integration/test/ports/engines/queries/filters/field_reference/_fixture/enum/generated/contract.d.ts b/test/integration/test/ports/engines/queries/filters/field_reference/_fixture/enum/generated/contract.d.ts index a5ebbab30e24..53cef6d0078b 100644 --- a/test/integration/test/ports/engines/queries/filters/field_reference/_fixture/enum/generated/contract.d.ts +++ b/test/integration/test/ports/engines/queries/filters/field_reference/_fixture/enum/generated/contract.d.ts @@ -42,6 +42,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; @@ -91,6 +95,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; @@ -100,6 +108,14 @@ export type AggregateTypes = { readonly output: 'pg/time-temporal@1'; readonly nullable: true; }; + readonly 'pg/timestamp-date@1': { + readonly output: 'pg/timestamp-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-instant@1': { + readonly output: 'pg/timestamp-instant@1'; + readonly nullable: true; + }; readonly 'pg/timestamp-string@1': { readonly output: 'pg/timestamp-string@1'; readonly nullable: true; @@ -151,6 +167,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; @@ -160,6 +180,14 @@ export type AggregateTypes = { readonly output: 'pg/time-temporal@1'; readonly nullable: true; }; + readonly 'pg/timestamp-date@1': { + readonly output: 'pg/timestamp-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-instant@1': { + readonly output: 'pg/timestamp-instant@1'; + readonly nullable: true; + }; readonly 'pg/timestamp-string@1': { readonly output: 'pg/timestamp-string@1'; readonly nullable: true; @@ -203,6 +231,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; diff --git a/test/integration/test/ports/engines/queries/filters/field_reference/_fixture/list/generated/contract.d.ts b/test/integration/test/ports/engines/queries/filters/field_reference/_fixture/list/generated/contract.d.ts index 7c3b8d87c15b..5f972eed7d0a 100644 --- a/test/integration/test/ports/engines/queries/filters/field_reference/_fixture/list/generated/contract.d.ts +++ b/test/integration/test/ports/engines/queries/filters/field_reference/_fixture/list/generated/contract.d.ts @@ -42,6 +42,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; @@ -91,6 +95,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; @@ -100,6 +108,14 @@ export type AggregateTypes = { readonly output: 'pg/time-temporal@1'; readonly nullable: true; }; + readonly 'pg/timestamp-date@1': { + readonly output: 'pg/timestamp-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-instant@1': { + readonly output: 'pg/timestamp-instant@1'; + readonly nullable: true; + }; readonly 'pg/timestamp-string@1': { readonly output: 'pg/timestamp-string@1'; readonly nullable: true; @@ -151,6 +167,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; @@ -160,6 +180,14 @@ export type AggregateTypes = { readonly output: 'pg/time-temporal@1'; readonly nullable: true; }; + readonly 'pg/timestamp-date@1': { + readonly output: 'pg/timestamp-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-instant@1': { + readonly output: 'pg/timestamp-instant@1'; + readonly nullable: true; + }; readonly 'pg/timestamp-string@1': { readonly output: 'pg/timestamp-string@1'; readonly nullable: true; @@ -203,6 +231,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; diff --git a/test/integration/test/ports/engines/queries/filters/field_reference/_fixture/mixed/generated/contract.d.ts b/test/integration/test/ports/engines/queries/filters/field_reference/_fixture/mixed/generated/contract.d.ts index cd1448866210..6ea69e18eddb 100644 --- a/test/integration/test/ports/engines/queries/filters/field_reference/_fixture/mixed/generated/contract.d.ts +++ b/test/integration/test/ports/engines/queries/filters/field_reference/_fixture/mixed/generated/contract.d.ts @@ -42,6 +42,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; @@ -91,6 +95,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; @@ -100,6 +108,14 @@ export type AggregateTypes = { readonly output: 'pg/time-temporal@1'; readonly nullable: true; }; + readonly 'pg/timestamp-date@1': { + readonly output: 'pg/timestamp-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-instant@1': { + readonly output: 'pg/timestamp-instant@1'; + readonly nullable: true; + }; readonly 'pg/timestamp-string@1': { readonly output: 'pg/timestamp-string@1'; readonly nullable: true; @@ -151,6 +167,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; @@ -160,6 +180,14 @@ export type AggregateTypes = { readonly output: 'pg/time-temporal@1'; readonly nullable: true; }; + readonly 'pg/timestamp-date@1': { + readonly output: 'pg/timestamp-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-instant@1': { + readonly output: 'pg/timestamp-instant@1'; + readonly nullable: true; + }; readonly 'pg/timestamp-string@1': { readonly output: 'pg/timestamp-string@1'; readonly nullable: true; @@ -203,6 +231,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; diff --git a/test/integration/test/ports/engines/queries/filters/field_reference/bigint_filter/_fixture/generated/contract.d.ts b/test/integration/test/ports/engines/queries/filters/field_reference/bigint_filter/_fixture/generated/contract.d.ts index 78c4ea4ee058..24984f967a5b 100644 --- a/test/integration/test/ports/engines/queries/filters/field_reference/bigint_filter/_fixture/generated/contract.d.ts +++ b/test/integration/test/ports/engines/queries/filters/field_reference/bigint_filter/_fixture/generated/contract.d.ts @@ -42,6 +42,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; @@ -91,6 +95,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; @@ -100,6 +108,14 @@ export type AggregateTypes = { readonly output: 'pg/time-temporal@1'; readonly nullable: true; }; + readonly 'pg/timestamp-date@1': { + readonly output: 'pg/timestamp-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-instant@1': { + readonly output: 'pg/timestamp-instant@1'; + readonly nullable: true; + }; readonly 'pg/timestamp-string@1': { readonly output: 'pg/timestamp-string@1'; readonly nullable: true; @@ -151,6 +167,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; @@ -160,6 +180,14 @@ export type AggregateTypes = { readonly output: 'pg/time-temporal@1'; readonly nullable: true; }; + readonly 'pg/timestamp-date@1': { + readonly output: 'pg/timestamp-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-instant@1': { + readonly output: 'pg/timestamp-instant@1'; + readonly nullable: true; + }; readonly 'pg/timestamp-string@1': { readonly output: 'pg/timestamp-string@1'; readonly nullable: true; @@ -203,6 +231,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; diff --git a/test/integration/test/ports/engines/queries/filters/field_reference/bytes_filter/_fixture/generated/contract.d.ts b/test/integration/test/ports/engines/queries/filters/field_reference/bytes_filter/_fixture/generated/contract.d.ts index 78c4ea4ee058..24984f967a5b 100644 --- a/test/integration/test/ports/engines/queries/filters/field_reference/bytes_filter/_fixture/generated/contract.d.ts +++ b/test/integration/test/ports/engines/queries/filters/field_reference/bytes_filter/_fixture/generated/contract.d.ts @@ -42,6 +42,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; @@ -91,6 +95,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; @@ -100,6 +108,14 @@ export type AggregateTypes = { readonly output: 'pg/time-temporal@1'; readonly nullable: true; }; + readonly 'pg/timestamp-date@1': { + readonly output: 'pg/timestamp-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-instant@1': { + readonly output: 'pg/timestamp-instant@1'; + readonly nullable: true; + }; readonly 'pg/timestamp-string@1': { readonly output: 'pg/timestamp-string@1'; readonly nullable: true; @@ -151,6 +167,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; @@ -160,6 +180,14 @@ export type AggregateTypes = { readonly output: 'pg/time-temporal@1'; readonly nullable: true; }; + readonly 'pg/timestamp-date@1': { + readonly output: 'pg/timestamp-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-instant@1': { + readonly output: 'pg/timestamp-instant@1'; + readonly nullable: true; + }; readonly 'pg/timestamp-string@1': { readonly output: 'pg/timestamp-string@1'; readonly nullable: true; @@ -203,6 +231,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; diff --git a/test/integration/test/ports/engines/queries/filters/field_reference/datetime_filter/_fixture/generated/contract.d.ts b/test/integration/test/ports/engines/queries/filters/field_reference/datetime_filter/_fixture/generated/contract.d.ts index 78c4ea4ee058..24984f967a5b 100644 --- a/test/integration/test/ports/engines/queries/filters/field_reference/datetime_filter/_fixture/generated/contract.d.ts +++ b/test/integration/test/ports/engines/queries/filters/field_reference/datetime_filter/_fixture/generated/contract.d.ts @@ -42,6 +42,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; @@ -91,6 +95,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; @@ -100,6 +108,14 @@ export type AggregateTypes = { readonly output: 'pg/time-temporal@1'; readonly nullable: true; }; + readonly 'pg/timestamp-date@1': { + readonly output: 'pg/timestamp-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-instant@1': { + readonly output: 'pg/timestamp-instant@1'; + readonly nullable: true; + }; readonly 'pg/timestamp-string@1': { readonly output: 'pg/timestamp-string@1'; readonly nullable: true; @@ -151,6 +167,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; @@ -160,6 +180,14 @@ export type AggregateTypes = { readonly output: 'pg/time-temporal@1'; readonly nullable: true; }; + readonly 'pg/timestamp-date@1': { + readonly output: 'pg/timestamp-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-instant@1': { + readonly output: 'pg/timestamp-instant@1'; + readonly nullable: true; + }; readonly 'pg/timestamp-string@1': { readonly output: 'pg/timestamp-string@1'; readonly nullable: true; @@ -203,6 +231,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; diff --git a/test/integration/test/ports/engines/queries/filters/field_reference/decimal_filter/_fixture/generated/contract.d.ts b/test/integration/test/ports/engines/queries/filters/field_reference/decimal_filter/_fixture/generated/contract.d.ts index d6a5f215a045..21d4bc610dd2 100644 --- a/test/integration/test/ports/engines/queries/filters/field_reference/decimal_filter/_fixture/generated/contract.d.ts +++ b/test/integration/test/ports/engines/queries/filters/field_reference/decimal_filter/_fixture/generated/contract.d.ts @@ -42,6 +42,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; @@ -91,6 +95,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; @@ -100,6 +108,14 @@ export type AggregateTypes = { readonly output: 'pg/time-temporal@1'; readonly nullable: true; }; + readonly 'pg/timestamp-date@1': { + readonly output: 'pg/timestamp-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-instant@1': { + readonly output: 'pg/timestamp-instant@1'; + readonly nullable: true; + }; readonly 'pg/timestamp-string@1': { readonly output: 'pg/timestamp-string@1'; readonly nullable: true; @@ -151,6 +167,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; @@ -160,6 +180,14 @@ export type AggregateTypes = { readonly output: 'pg/time-temporal@1'; readonly nullable: true; }; + readonly 'pg/timestamp-date@1': { + readonly output: 'pg/timestamp-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-instant@1': { + readonly output: 'pg/timestamp-instant@1'; + readonly nullable: true; + }; readonly 'pg/timestamp-string@1': { readonly output: 'pg/timestamp-string@1'; readonly nullable: true; @@ -203,6 +231,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; diff --git a/test/integration/test/ports/engines/queries/filters/field_reference/failure/_fixture/common/generated/contract.d.ts b/test/integration/test/ports/engines/queries/filters/field_reference/failure/_fixture/common/generated/contract.d.ts index 78c4ea4ee058..24984f967a5b 100644 --- a/test/integration/test/ports/engines/queries/filters/field_reference/failure/_fixture/common/generated/contract.d.ts +++ b/test/integration/test/ports/engines/queries/filters/field_reference/failure/_fixture/common/generated/contract.d.ts @@ -42,6 +42,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; @@ -91,6 +95,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; @@ -100,6 +108,14 @@ export type AggregateTypes = { readonly output: 'pg/time-temporal@1'; readonly nullable: true; }; + readonly 'pg/timestamp-date@1': { + readonly output: 'pg/timestamp-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-instant@1': { + readonly output: 'pg/timestamp-instant@1'; + readonly nullable: true; + }; readonly 'pg/timestamp-string@1': { readonly output: 'pg/timestamp-string@1'; readonly nullable: true; @@ -151,6 +167,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; @@ -160,6 +180,14 @@ export type AggregateTypes = { readonly output: 'pg/time-temporal@1'; readonly nullable: true; }; + readonly 'pg/timestamp-date@1': { + readonly output: 'pg/timestamp-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-instant@1': { + readonly output: 'pg/timestamp-instant@1'; + readonly nullable: true; + }; readonly 'pg/timestamp-string@1': { readonly output: 'pg/timestamp-string@1'; readonly nullable: true; @@ -203,6 +231,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; diff --git a/test/integration/test/ports/engines/queries/filters/field_reference/failure/_fixture/default/generated/contract.d.ts b/test/integration/test/ports/engines/queries/filters/field_reference/failure/_fixture/default/generated/contract.d.ts index cee01631d909..8ad50d276bf8 100644 --- a/test/integration/test/ports/engines/queries/filters/field_reference/failure/_fixture/default/generated/contract.d.ts +++ b/test/integration/test/ports/engines/queries/filters/field_reference/failure/_fixture/default/generated/contract.d.ts @@ -42,6 +42,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; @@ -91,6 +95,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; @@ -100,6 +108,14 @@ export type AggregateTypes = { readonly output: 'pg/time-temporal@1'; readonly nullable: true; }; + readonly 'pg/timestamp-date@1': { + readonly output: 'pg/timestamp-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-instant@1': { + readonly output: 'pg/timestamp-instant@1'; + readonly nullable: true; + }; readonly 'pg/timestamp-string@1': { readonly output: 'pg/timestamp-string@1'; readonly nullable: true; @@ -151,6 +167,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; @@ -160,6 +180,14 @@ export type AggregateTypes = { readonly output: 'pg/time-temporal@1'; readonly nullable: true; }; + readonly 'pg/timestamp-date@1': { + readonly output: 'pg/timestamp-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-instant@1': { + readonly output: 'pg/timestamp-instant@1'; + readonly nullable: true; + }; readonly 'pg/timestamp-string@1': { readonly output: 'pg/timestamp-string@1'; readonly nullable: true; @@ -203,6 +231,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; diff --git a/test/integration/test/ports/engines/queries/filters/field_reference/failure/_fixture/list/generated/contract.d.ts b/test/integration/test/ports/engines/queries/filters/field_reference/failure/_fixture/list/generated/contract.d.ts index 8fd52a04c4a6..ea3cd3f6daa5 100644 --- a/test/integration/test/ports/engines/queries/filters/field_reference/failure/_fixture/list/generated/contract.d.ts +++ b/test/integration/test/ports/engines/queries/filters/field_reference/failure/_fixture/list/generated/contract.d.ts @@ -42,6 +42,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; @@ -91,6 +95,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; @@ -100,6 +108,14 @@ export type AggregateTypes = { readonly output: 'pg/time-temporal@1'; readonly nullable: true; }; + readonly 'pg/timestamp-date@1': { + readonly output: 'pg/timestamp-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-instant@1': { + readonly output: 'pg/timestamp-instant@1'; + readonly nullable: true; + }; readonly 'pg/timestamp-string@1': { readonly output: 'pg/timestamp-string@1'; readonly nullable: true; @@ -151,6 +167,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; @@ -160,6 +180,14 @@ export type AggregateTypes = { readonly output: 'pg/time-temporal@1'; readonly nullable: true; }; + readonly 'pg/timestamp-date@1': { + readonly output: 'pg/timestamp-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-instant@1': { + readonly output: 'pg/timestamp-instant@1'; + readonly nullable: true; + }; readonly 'pg/timestamp-string@1': { readonly output: 'pg/timestamp-string@1'; readonly nullable: true; @@ -203,6 +231,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; diff --git a/test/integration/test/ports/engines/queries/filters/field_reference/float_filter/_fixture/generated/contract.d.ts b/test/integration/test/ports/engines/queries/filters/field_reference/float_filter/_fixture/generated/contract.d.ts index 78c4ea4ee058..24984f967a5b 100644 --- a/test/integration/test/ports/engines/queries/filters/field_reference/float_filter/_fixture/generated/contract.d.ts +++ b/test/integration/test/ports/engines/queries/filters/field_reference/float_filter/_fixture/generated/contract.d.ts @@ -42,6 +42,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; @@ -91,6 +95,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; @@ -100,6 +108,14 @@ export type AggregateTypes = { readonly output: 'pg/time-temporal@1'; readonly nullable: true; }; + readonly 'pg/timestamp-date@1': { + readonly output: 'pg/timestamp-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-instant@1': { + readonly output: 'pg/timestamp-instant@1'; + readonly nullable: true; + }; readonly 'pg/timestamp-string@1': { readonly output: 'pg/timestamp-string@1'; readonly nullable: true; @@ -151,6 +167,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; @@ -160,6 +180,14 @@ export type AggregateTypes = { readonly output: 'pg/time-temporal@1'; readonly nullable: true; }; + readonly 'pg/timestamp-date@1': { + readonly output: 'pg/timestamp-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-instant@1': { + readonly output: 'pg/timestamp-instant@1'; + readonly nullable: true; + }; readonly 'pg/timestamp-string@1': { readonly output: 'pg/timestamp-string@1'; readonly nullable: true; @@ -203,6 +231,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; diff --git a/test/integration/test/ports/engines/queries/filters/field_reference/having_filter/_fixture/generated/contract.d.ts b/test/integration/test/ports/engines/queries/filters/field_reference/having_filter/_fixture/generated/contract.d.ts index 78c4ea4ee058..24984f967a5b 100644 --- a/test/integration/test/ports/engines/queries/filters/field_reference/having_filter/_fixture/generated/contract.d.ts +++ b/test/integration/test/ports/engines/queries/filters/field_reference/having_filter/_fixture/generated/contract.d.ts @@ -42,6 +42,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; @@ -91,6 +95,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; @@ -100,6 +108,14 @@ export type AggregateTypes = { readonly output: 'pg/time-temporal@1'; readonly nullable: true; }; + readonly 'pg/timestamp-date@1': { + readonly output: 'pg/timestamp-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-instant@1': { + readonly output: 'pg/timestamp-instant@1'; + readonly nullable: true; + }; readonly 'pg/timestamp-string@1': { readonly output: 'pg/timestamp-string@1'; readonly nullable: true; @@ -151,6 +167,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; @@ -160,6 +180,14 @@ export type AggregateTypes = { readonly output: 'pg/time-temporal@1'; readonly nullable: true; }; + readonly 'pg/timestamp-date@1': { + readonly output: 'pg/timestamp-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-instant@1': { + readonly output: 'pg/timestamp-instant@1'; + readonly nullable: true; + }; readonly 'pg/timestamp-string@1': { readonly output: 'pg/timestamp-string@1'; readonly nullable: true; @@ -203,6 +231,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; diff --git a/test/integration/test/ports/engines/queries/filters/field_reference/int_filter/_fixture/generated/contract.d.ts b/test/integration/test/ports/engines/queries/filters/field_reference/int_filter/_fixture/generated/contract.d.ts index 78c4ea4ee058..24984f967a5b 100644 --- a/test/integration/test/ports/engines/queries/filters/field_reference/int_filter/_fixture/generated/contract.d.ts +++ b/test/integration/test/ports/engines/queries/filters/field_reference/int_filter/_fixture/generated/contract.d.ts @@ -42,6 +42,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; @@ -91,6 +95,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; @@ -100,6 +108,14 @@ export type AggregateTypes = { readonly output: 'pg/time-temporal@1'; readonly nullable: true; }; + readonly 'pg/timestamp-date@1': { + readonly output: 'pg/timestamp-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-instant@1': { + readonly output: 'pg/timestamp-instant@1'; + readonly nullable: true; + }; readonly 'pg/timestamp-string@1': { readonly output: 'pg/timestamp-string@1'; readonly nullable: true; @@ -151,6 +167,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; @@ -160,6 +180,14 @@ export type AggregateTypes = { readonly output: 'pg/time-temporal@1'; readonly nullable: true; }; + readonly 'pg/timestamp-date@1': { + readonly output: 'pg/timestamp-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-instant@1': { + readonly output: 'pg/timestamp-instant@1'; + readonly nullable: true; + }; readonly 'pg/timestamp-string@1': { readonly output: 'pg/timestamp-string@1'; readonly nullable: true; @@ -203,6 +231,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; diff --git a/test/integration/test/ports/engines/queries/filters/field_reference/json_filter/_fixture/generated/contract.d.ts b/test/integration/test/ports/engines/queries/filters/field_reference/json_filter/_fixture/generated/contract.d.ts index f68e7448e5ed..0f8ee1734935 100644 --- a/test/integration/test/ports/engines/queries/filters/field_reference/json_filter/_fixture/generated/contract.d.ts +++ b/test/integration/test/ports/engines/queries/filters/field_reference/json_filter/_fixture/generated/contract.d.ts @@ -42,6 +42,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; @@ -91,6 +95,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; @@ -100,6 +108,14 @@ export type AggregateTypes = { readonly output: 'pg/time-temporal@1'; readonly nullable: true; }; + readonly 'pg/timestamp-date@1': { + readonly output: 'pg/timestamp-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-instant@1': { + readonly output: 'pg/timestamp-instant@1'; + readonly nullable: true; + }; readonly 'pg/timestamp-string@1': { readonly output: 'pg/timestamp-string@1'; readonly nullable: true; @@ -151,6 +167,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; @@ -160,6 +180,14 @@ export type AggregateTypes = { readonly output: 'pg/time-temporal@1'; readonly nullable: true; }; + readonly 'pg/timestamp-date@1': { + readonly output: 'pg/timestamp-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-instant@1': { + readonly output: 'pg/timestamp-instant@1'; + readonly nullable: true; + }; readonly 'pg/timestamp-string@1': { readonly output: 'pg/timestamp-string@1'; readonly nullable: true; @@ -203,6 +231,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; diff --git a/test/integration/test/ports/engines/queries/filters/field_reference/json_filter/_fixture/list/generated/contract.d.ts b/test/integration/test/ports/engines/queries/filters/field_reference/json_filter/_fixture/list/generated/contract.d.ts index ce6bdda94c3d..b6a440e20503 100644 --- a/test/integration/test/ports/engines/queries/filters/field_reference/json_filter/_fixture/list/generated/contract.d.ts +++ b/test/integration/test/ports/engines/queries/filters/field_reference/json_filter/_fixture/list/generated/contract.d.ts @@ -42,6 +42,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; @@ -91,6 +95,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; @@ -100,6 +108,14 @@ export type AggregateTypes = { readonly output: 'pg/time-temporal@1'; readonly nullable: true; }; + readonly 'pg/timestamp-date@1': { + readonly output: 'pg/timestamp-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-instant@1': { + readonly output: 'pg/timestamp-instant@1'; + readonly nullable: true; + }; readonly 'pg/timestamp-string@1': { readonly output: 'pg/timestamp-string@1'; readonly nullable: true; @@ -151,6 +167,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; @@ -160,6 +180,14 @@ export type AggregateTypes = { readonly output: 'pg/time-temporal@1'; readonly nullable: true; }; + readonly 'pg/timestamp-date@1': { + readonly output: 'pg/timestamp-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-instant@1': { + readonly output: 'pg/timestamp-instant@1'; + readonly nullable: true; + }; readonly 'pg/timestamp-string@1': { readonly output: 'pg/timestamp-string@1'; readonly nullable: true; @@ -203,6 +231,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; diff --git a/test/integration/test/ports/engines/queries/filters/field_reference/relation_filter/_fixture/complex/generated/contract.d.ts b/test/integration/test/ports/engines/queries/filters/field_reference/relation_filter/_fixture/complex/generated/contract.d.ts index 5d35b0801bda..db4f0bd497dd 100644 --- a/test/integration/test/ports/engines/queries/filters/field_reference/relation_filter/_fixture/complex/generated/contract.d.ts +++ b/test/integration/test/ports/engines/queries/filters/field_reference/relation_filter/_fixture/complex/generated/contract.d.ts @@ -42,6 +42,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; @@ -91,6 +95,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; @@ -100,6 +108,14 @@ export type AggregateTypes = { readonly output: 'pg/time-temporal@1'; readonly nullable: true; }; + readonly 'pg/timestamp-date@1': { + readonly output: 'pg/timestamp-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-instant@1': { + readonly output: 'pg/timestamp-instant@1'; + readonly nullable: true; + }; readonly 'pg/timestamp-string@1': { readonly output: 'pg/timestamp-string@1'; readonly nullable: true; @@ -151,6 +167,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; @@ -160,6 +180,14 @@ export type AggregateTypes = { readonly output: 'pg/time-temporal@1'; readonly nullable: true; }; + readonly 'pg/timestamp-date@1': { + readonly output: 'pg/timestamp-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-instant@1': { + readonly output: 'pg/timestamp-instant@1'; + readonly nullable: true; + }; readonly 'pg/timestamp-string@1': { readonly output: 'pg/timestamp-string@1'; readonly nullable: true; @@ -203,6 +231,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; diff --git a/test/integration/test/ports/engines/queries/filters/field_reference/relation_filter/_fixture/one-to-many/generated/contract.d.ts b/test/integration/test/ports/engines/queries/filters/field_reference/relation_filter/_fixture/one-to-many/generated/contract.d.ts index 69de1382debd..676b2f57ae9d 100644 --- a/test/integration/test/ports/engines/queries/filters/field_reference/relation_filter/_fixture/one-to-many/generated/contract.d.ts +++ b/test/integration/test/ports/engines/queries/filters/field_reference/relation_filter/_fixture/one-to-many/generated/contract.d.ts @@ -42,6 +42,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; @@ -91,6 +95,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; @@ -100,6 +108,14 @@ export type AggregateTypes = { readonly output: 'pg/time-temporal@1'; readonly nullable: true; }; + readonly 'pg/timestamp-date@1': { + readonly output: 'pg/timestamp-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-instant@1': { + readonly output: 'pg/timestamp-instant@1'; + readonly nullable: true; + }; readonly 'pg/timestamp-string@1': { readonly output: 'pg/timestamp-string@1'; readonly nullable: true; @@ -151,6 +167,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; @@ -160,6 +180,14 @@ export type AggregateTypes = { readonly output: 'pg/time-temporal@1'; readonly nullable: true; }; + readonly 'pg/timestamp-date@1': { + readonly output: 'pg/timestamp-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-instant@1': { + readonly output: 'pg/timestamp-instant@1'; + readonly nullable: true; + }; readonly 'pg/timestamp-string@1': { readonly output: 'pg/timestamp-string@1'; readonly nullable: true; @@ -203,6 +231,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; diff --git a/test/integration/test/ports/engines/queries/filters/field_reference/relation_filter/_fixture/one-to-one-list/generated/contract.d.ts b/test/integration/test/ports/engines/queries/filters/field_reference/relation_filter/_fixture/one-to-one-list/generated/contract.d.ts index 41b4afc38621..13a53f7c85cd 100644 --- a/test/integration/test/ports/engines/queries/filters/field_reference/relation_filter/_fixture/one-to-one-list/generated/contract.d.ts +++ b/test/integration/test/ports/engines/queries/filters/field_reference/relation_filter/_fixture/one-to-one-list/generated/contract.d.ts @@ -42,6 +42,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; @@ -91,6 +95,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; @@ -100,6 +108,14 @@ export type AggregateTypes = { readonly output: 'pg/time-temporal@1'; readonly nullable: true; }; + readonly 'pg/timestamp-date@1': { + readonly output: 'pg/timestamp-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-instant@1': { + readonly output: 'pg/timestamp-instant@1'; + readonly nullable: true; + }; readonly 'pg/timestamp-string@1': { readonly output: 'pg/timestamp-string@1'; readonly nullable: true; @@ -151,6 +167,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; @@ -160,6 +180,14 @@ export type AggregateTypes = { readonly output: 'pg/time-temporal@1'; readonly nullable: true; }; + readonly 'pg/timestamp-date@1': { + readonly output: 'pg/timestamp-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-instant@1': { + readonly output: 'pg/timestamp-instant@1'; + readonly nullable: true; + }; readonly 'pg/timestamp-string@1': { readonly output: 'pg/timestamp-string@1'; readonly nullable: true; @@ -203,6 +231,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; diff --git a/test/integration/test/ports/engines/queries/filters/field_reference/relation_filter/_fixture/one-to-one/generated/contract.d.ts b/test/integration/test/ports/engines/queries/filters/field_reference/relation_filter/_fixture/one-to-one/generated/contract.d.ts index b7e60375faf5..d9e48285c554 100644 --- a/test/integration/test/ports/engines/queries/filters/field_reference/relation_filter/_fixture/one-to-one/generated/contract.d.ts +++ b/test/integration/test/ports/engines/queries/filters/field_reference/relation_filter/_fixture/one-to-one/generated/contract.d.ts @@ -42,6 +42,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; @@ -91,6 +95,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; @@ -100,6 +108,14 @@ export type AggregateTypes = { readonly output: 'pg/time-temporal@1'; readonly nullable: true; }; + readonly 'pg/timestamp-date@1': { + readonly output: 'pg/timestamp-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-instant@1': { + readonly output: 'pg/timestamp-instant@1'; + readonly nullable: true; + }; readonly 'pg/timestamp-string@1': { readonly output: 'pg/timestamp-string@1'; readonly nullable: true; @@ -151,6 +167,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; @@ -160,6 +180,14 @@ export type AggregateTypes = { readonly output: 'pg/time-temporal@1'; readonly nullable: true; }; + readonly 'pg/timestamp-date@1': { + readonly output: 'pg/timestamp-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-instant@1': { + readonly output: 'pg/timestamp-instant@1'; + readonly nullable: true; + }; readonly 'pg/timestamp-string@1': { readonly output: 'pg/timestamp-string@1'; readonly nullable: true; @@ -203,6 +231,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; diff --git a/test/integration/test/ports/engines/queries/filters/field_reference/string_filter/_fixture/generated/contract.d.ts b/test/integration/test/ports/engines/queries/filters/field_reference/string_filter/_fixture/generated/contract.d.ts index 78c4ea4ee058..24984f967a5b 100644 --- a/test/integration/test/ports/engines/queries/filters/field_reference/string_filter/_fixture/generated/contract.d.ts +++ b/test/integration/test/ports/engines/queries/filters/field_reference/string_filter/_fixture/generated/contract.d.ts @@ -42,6 +42,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; @@ -91,6 +95,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; @@ -100,6 +108,14 @@ export type AggregateTypes = { readonly output: 'pg/time-temporal@1'; readonly nullable: true; }; + readonly 'pg/timestamp-date@1': { + readonly output: 'pg/timestamp-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-instant@1': { + readonly output: 'pg/timestamp-instant@1'; + readonly nullable: true; + }; readonly 'pg/timestamp-string@1': { readonly output: 'pg/timestamp-string@1'; readonly nullable: true; @@ -151,6 +167,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; @@ -160,6 +180,14 @@ export type AggregateTypes = { readonly output: 'pg/time-temporal@1'; readonly nullable: true; }; + readonly 'pg/timestamp-date@1': { + readonly output: 'pg/timestamp-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-instant@1': { + readonly output: 'pg/timestamp-instant@1'; + readonly nullable: true; + }; readonly 'pg/timestamp-string@1': { readonly output: 'pg/timestamp-string@1'; readonly nullable: true; @@ -203,6 +231,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; diff --git a/test/integration/test/ports/engines/queries/filters/filter_regression/_fixture/compound-one-to-many/generated/contract.d.ts b/test/integration/test/ports/engines/queries/filters/filter_regression/_fixture/compound-one-to-many/generated/contract.d.ts index 4174a8373043..b031cff28bbe 100644 --- a/test/integration/test/ports/engines/queries/filters/filter_regression/_fixture/compound-one-to-many/generated/contract.d.ts +++ b/test/integration/test/ports/engines/queries/filters/filter_regression/_fixture/compound-one-to-many/generated/contract.d.ts @@ -42,6 +42,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; @@ -91,6 +95,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; @@ -100,6 +108,14 @@ export type AggregateTypes = { readonly output: 'pg/time-temporal@1'; readonly nullable: true; }; + readonly 'pg/timestamp-date@1': { + readonly output: 'pg/timestamp-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-instant@1': { + readonly output: 'pg/timestamp-instant@1'; + readonly nullable: true; + }; readonly 'pg/timestamp-string@1': { readonly output: 'pg/timestamp-string@1'; readonly nullable: true; @@ -151,6 +167,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; @@ -160,6 +180,14 @@ export type AggregateTypes = { readonly output: 'pg/time-temporal@1'; readonly nullable: true; }; + readonly 'pg/timestamp-date@1': { + readonly output: 'pg/timestamp-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-instant@1': { + readonly output: 'pg/timestamp-instant@1'; + readonly nullable: true; + }; readonly 'pg/timestamp-string@1': { readonly output: 'pg/timestamp-string@1'; readonly nullable: true; @@ -203,6 +231,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; diff --git a/test/integration/test/ports/engines/queries/filters/filter_regression/_fixture/many-to-many/generated/contract.d.ts b/test/integration/test/ports/engines/queries/filters/filter_regression/_fixture/many-to-many/generated/contract.d.ts index 5e5fa69ad8bc..2227733b694d 100644 --- a/test/integration/test/ports/engines/queries/filters/filter_regression/_fixture/many-to-many/generated/contract.d.ts +++ b/test/integration/test/ports/engines/queries/filters/filter_regression/_fixture/many-to-many/generated/contract.d.ts @@ -42,6 +42,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; @@ -91,6 +95,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; @@ -100,6 +108,14 @@ export type AggregateTypes = { readonly output: 'pg/time-temporal@1'; readonly nullable: true; }; + readonly 'pg/timestamp-date@1': { + readonly output: 'pg/timestamp-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-instant@1': { + readonly output: 'pg/timestamp-instant@1'; + readonly nullable: true; + }; readonly 'pg/timestamp-string@1': { readonly output: 'pg/timestamp-string@1'; readonly nullable: true; @@ -151,6 +167,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; @@ -160,6 +180,14 @@ export type AggregateTypes = { readonly output: 'pg/time-temporal@1'; readonly nullable: true; }; + readonly 'pg/timestamp-date@1': { + readonly output: 'pg/timestamp-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-instant@1': { + readonly output: 'pg/timestamp-instant@1'; + readonly nullable: true; + }; readonly 'pg/timestamp-string@1': { readonly output: 'pg/timestamp-string@1'; readonly nullable: true; @@ -203,6 +231,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; diff --git a/test/integration/test/ports/engines/queries/filters/filter_regression/_fixture/one-to-many/generated/contract.d.ts b/test/integration/test/ports/engines/queries/filters/filter_regression/_fixture/one-to-many/generated/contract.d.ts index 67ae535f7b43..1f437aea1161 100644 --- a/test/integration/test/ports/engines/queries/filters/filter_regression/_fixture/one-to-many/generated/contract.d.ts +++ b/test/integration/test/ports/engines/queries/filters/filter_regression/_fixture/one-to-many/generated/contract.d.ts @@ -42,6 +42,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; @@ -91,6 +95,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; @@ -100,6 +108,14 @@ export type AggregateTypes = { readonly output: 'pg/time-temporal@1'; readonly nullable: true; }; + readonly 'pg/timestamp-date@1': { + readonly output: 'pg/timestamp-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-instant@1': { + readonly output: 'pg/timestamp-instant@1'; + readonly nullable: true; + }; readonly 'pg/timestamp-string@1': { readonly output: 'pg/timestamp-string@1'; readonly nullable: true; @@ -151,6 +167,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; @@ -160,6 +180,14 @@ export type AggregateTypes = { readonly output: 'pg/time-temporal@1'; readonly nullable: true; }; + readonly 'pg/timestamp-date@1': { + readonly output: 'pg/timestamp-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-instant@1': { + readonly output: 'pg/timestamp-instant@1'; + readonly nullable: true; + }; readonly 'pg/timestamp-string@1': { readonly output: 'pg/timestamp-string@1'; readonly nullable: true; @@ -203,6 +231,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; diff --git a/test/integration/test/ports/engines/queries/filters/filters/_fixture/generated/contract.d.ts b/test/integration/test/ports/engines/queries/filters/filters/_fixture/generated/contract.d.ts index ad1c8d24c302..2ddabd42d4d6 100644 --- a/test/integration/test/ports/engines/queries/filters/filters/_fixture/generated/contract.d.ts +++ b/test/integration/test/ports/engines/queries/filters/filters/_fixture/generated/contract.d.ts @@ -42,6 +42,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; @@ -91,6 +95,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; @@ -100,6 +108,14 @@ export type AggregateTypes = { readonly output: 'pg/time-temporal@1'; readonly nullable: true; }; + readonly 'pg/timestamp-date@1': { + readonly output: 'pg/timestamp-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-instant@1': { + readonly output: 'pg/timestamp-instant@1'; + readonly nullable: true; + }; readonly 'pg/timestamp-string@1': { readonly output: 'pg/timestamp-string@1'; readonly nullable: true; @@ -151,6 +167,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; @@ -160,6 +180,14 @@ export type AggregateTypes = { readonly output: 'pg/time-temporal@1'; readonly nullable: true; }; + readonly 'pg/timestamp-date@1': { + readonly output: 'pg/timestamp-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-instant@1': { + readonly output: 'pg/timestamp-instant@1'; + readonly nullable: true; + }; readonly 'pg/timestamp-string@1': { readonly output: 'pg/timestamp-string@1'; readonly nullable: true; @@ -203,6 +231,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; diff --git a/test/integration/test/ports/engines/queries/filters/json/_fixture/optional/generated/contract.d.ts b/test/integration/test/ports/engines/queries/filters/json/_fixture/optional/generated/contract.d.ts index e130b6241483..7954f1c9e0f4 100644 --- a/test/integration/test/ports/engines/queries/filters/json/_fixture/optional/generated/contract.d.ts +++ b/test/integration/test/ports/engines/queries/filters/json/_fixture/optional/generated/contract.d.ts @@ -42,6 +42,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; @@ -91,6 +95,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; @@ -100,6 +108,14 @@ export type AggregateTypes = { readonly output: 'pg/time-temporal@1'; readonly nullable: true; }; + readonly 'pg/timestamp-date@1': { + readonly output: 'pg/timestamp-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-instant@1': { + readonly output: 'pg/timestamp-instant@1'; + readonly nullable: true; + }; readonly 'pg/timestamp-string@1': { readonly output: 'pg/timestamp-string@1'; readonly nullable: true; @@ -151,6 +167,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; @@ -160,6 +180,14 @@ export type AggregateTypes = { readonly output: 'pg/time-temporal@1'; readonly nullable: true; }; + readonly 'pg/timestamp-date@1': { + readonly output: 'pg/timestamp-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-instant@1': { + readonly output: 'pg/timestamp-instant@1'; + readonly nullable: true; + }; readonly 'pg/timestamp-string@1': { readonly output: 'pg/timestamp-string@1'; readonly nullable: true; @@ -203,6 +231,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; diff --git a/test/integration/test/ports/engines/queries/filters/list_filters/_fixture/base/generated/contract.d.ts b/test/integration/test/ports/engines/queries/filters/list_filters/_fixture/base/generated/contract.d.ts index 098ab31609a3..30c19adfe6a7 100644 --- a/test/integration/test/ports/engines/queries/filters/list_filters/_fixture/base/generated/contract.d.ts +++ b/test/integration/test/ports/engines/queries/filters/list_filters/_fixture/base/generated/contract.d.ts @@ -42,6 +42,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; @@ -91,6 +95,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; @@ -100,6 +108,14 @@ export type AggregateTypes = { readonly output: 'pg/time-temporal@1'; readonly nullable: true; }; + readonly 'pg/timestamp-date@1': { + readonly output: 'pg/timestamp-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-instant@1': { + readonly output: 'pg/timestamp-instant@1'; + readonly nullable: true; + }; readonly 'pg/timestamp-string@1': { readonly output: 'pg/timestamp-string@1'; readonly nullable: true; @@ -151,6 +167,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; @@ -160,6 +180,14 @@ export type AggregateTypes = { readonly output: 'pg/time-temporal@1'; readonly nullable: true; }; + readonly 'pg/timestamp-date@1': { + readonly output: 'pg/timestamp-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-instant@1': { + readonly output: 'pg/timestamp-instant@1'; + readonly nullable: true; + }; readonly 'pg/timestamp-string@1': { readonly output: 'pg/timestamp-string@1'; readonly nullable: true; @@ -203,6 +231,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; diff --git a/test/integration/test/ports/engines/queries/filters/list_filters/_fixture/decimal/generated/contract.d.ts b/test/integration/test/ports/engines/queries/filters/list_filters/_fixture/decimal/generated/contract.d.ts index 5685dba19d73..dd1442dba7fc 100644 --- a/test/integration/test/ports/engines/queries/filters/list_filters/_fixture/decimal/generated/contract.d.ts +++ b/test/integration/test/ports/engines/queries/filters/list_filters/_fixture/decimal/generated/contract.d.ts @@ -42,6 +42,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; @@ -91,6 +95,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; @@ -100,6 +108,14 @@ export type AggregateTypes = { readonly output: 'pg/time-temporal@1'; readonly nullable: true; }; + readonly 'pg/timestamp-date@1': { + readonly output: 'pg/timestamp-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-instant@1': { + readonly output: 'pg/timestamp-instant@1'; + readonly nullable: true; + }; readonly 'pg/timestamp-string@1': { readonly output: 'pg/timestamp-string@1'; readonly nullable: true; @@ -151,6 +167,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; @@ -160,6 +180,14 @@ export type AggregateTypes = { readonly output: 'pg/time-temporal@1'; readonly nullable: true; }; + readonly 'pg/timestamp-date@1': { + readonly output: 'pg/timestamp-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-instant@1': { + readonly output: 'pg/timestamp-instant@1'; + readonly nullable: true; + }; readonly 'pg/timestamp-string@1': { readonly output: 'pg/timestamp-string@1'; readonly nullable: true; @@ -203,6 +231,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; diff --git a/test/integration/test/ports/engines/queries/filters/list_filters/_fixture/json/generated/contract.d.ts b/test/integration/test/ports/engines/queries/filters/list_filters/_fixture/json/generated/contract.d.ts index d45c4f6c6374..dc87644ec604 100644 --- a/test/integration/test/ports/engines/queries/filters/list_filters/_fixture/json/generated/contract.d.ts +++ b/test/integration/test/ports/engines/queries/filters/list_filters/_fixture/json/generated/contract.d.ts @@ -42,6 +42,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; @@ -91,6 +95,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; @@ -100,6 +108,14 @@ export type AggregateTypes = { readonly output: 'pg/time-temporal@1'; readonly nullable: true; }; + readonly 'pg/timestamp-date@1': { + readonly output: 'pg/timestamp-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-instant@1': { + readonly output: 'pg/timestamp-instant@1'; + readonly nullable: true; + }; readonly 'pg/timestamp-string@1': { readonly output: 'pg/timestamp-string@1'; readonly nullable: true; @@ -151,6 +167,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; @@ -160,6 +180,14 @@ export type AggregateTypes = { readonly output: 'pg/time-temporal@1'; readonly nullable: true; }; + readonly 'pg/timestamp-date@1': { + readonly output: 'pg/timestamp-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-instant@1': { + readonly output: 'pg/timestamp-instant@1'; + readonly nullable: true; + }; readonly 'pg/timestamp-string@1': { readonly output: 'pg/timestamp-string@1'; readonly nullable: true; @@ -203,6 +231,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; diff --git a/test/integration/test/ports/engines/queries/filters/many_relation/_fixture/25103/generated/contract.d.ts b/test/integration/test/ports/engines/queries/filters/many_relation/_fixture/25103/generated/contract.d.ts index 0d74fe3722f7..1f864c7b304d 100644 --- a/test/integration/test/ports/engines/queries/filters/many_relation/_fixture/25103/generated/contract.d.ts +++ b/test/integration/test/ports/engines/queries/filters/many_relation/_fixture/25103/generated/contract.d.ts @@ -42,6 +42,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; @@ -91,6 +95,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; @@ -100,6 +108,14 @@ export type AggregateTypes = { readonly output: 'pg/time-temporal@1'; readonly nullable: true; }; + readonly 'pg/timestamp-date@1': { + readonly output: 'pg/timestamp-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-instant@1': { + readonly output: 'pg/timestamp-instant@1'; + readonly nullable: true; + }; readonly 'pg/timestamp-string@1': { readonly output: 'pg/timestamp-string@1'; readonly nullable: true; @@ -151,6 +167,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; @@ -160,6 +180,14 @@ export type AggregateTypes = { readonly output: 'pg/time-temporal@1'; readonly nullable: true; }; + readonly 'pg/timestamp-date@1': { + readonly output: 'pg/timestamp-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-instant@1': { + readonly output: 'pg/timestamp-instant@1'; + readonly nullable: true; + }; readonly 'pg/timestamp-string@1': { readonly output: 'pg/timestamp-string@1'; readonly nullable: true; @@ -203,6 +231,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; diff --git a/test/integration/test/ports/engines/queries/filters/many_relation/_fixture/l2-to-one/generated/contract.d.ts b/test/integration/test/ports/engines/queries/filters/many_relation/_fixture/l2-to-one/generated/contract.d.ts index aa3036bb93bf..1c82d448e810 100644 --- a/test/integration/test/ports/engines/queries/filters/many_relation/_fixture/l2-to-one/generated/contract.d.ts +++ b/test/integration/test/ports/engines/queries/filters/many_relation/_fixture/l2-to-one/generated/contract.d.ts @@ -42,6 +42,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; @@ -91,6 +95,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; @@ -100,6 +108,14 @@ export type AggregateTypes = { readonly output: 'pg/time-temporal@1'; readonly nullable: true; }; + readonly 'pg/timestamp-date@1': { + readonly output: 'pg/timestamp-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-instant@1': { + readonly output: 'pg/timestamp-instant@1'; + readonly nullable: true; + }; readonly 'pg/timestamp-string@1': { readonly output: 'pg/timestamp-string@1'; readonly nullable: true; @@ -151,6 +167,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; @@ -160,6 +180,14 @@ export type AggregateTypes = { readonly output: 'pg/time-temporal@1'; readonly nullable: true; }; + readonly 'pg/timestamp-date@1': { + readonly output: 'pg/timestamp-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-instant@1': { + readonly output: 'pg/timestamp-instant@1'; + readonly nullable: true; + }; readonly 'pg/timestamp-string@1': { readonly output: 'pg/timestamp-string@1'; readonly nullable: true; @@ -203,6 +231,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; diff --git a/test/integration/test/ports/engines/queries/filters/one2one_regression/_fixture/generated/contract.d.ts b/test/integration/test/ports/engines/queries/filters/one2one_regression/_fixture/generated/contract.d.ts index 1c8f04641d0d..8a65c47cda27 100644 --- a/test/integration/test/ports/engines/queries/filters/one2one_regression/_fixture/generated/contract.d.ts +++ b/test/integration/test/ports/engines/queries/filters/one2one_regression/_fixture/generated/contract.d.ts @@ -42,6 +42,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; @@ -91,6 +95,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; @@ -100,6 +108,14 @@ export type AggregateTypes = { readonly output: 'pg/time-temporal@1'; readonly nullable: true; }; + readonly 'pg/timestamp-date@1': { + readonly output: 'pg/timestamp-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-instant@1': { + readonly output: 'pg/timestamp-instant@1'; + readonly nullable: true; + }; readonly 'pg/timestamp-string@1': { readonly output: 'pg/timestamp-string@1'; readonly nullable: true; @@ -151,6 +167,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; @@ -160,6 +180,14 @@ export type AggregateTypes = { readonly output: 'pg/time-temporal@1'; readonly nullable: true; }; + readonly 'pg/timestamp-date@1': { + readonly output: 'pg/timestamp-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-instant@1': { + readonly output: 'pg/timestamp-instant@1'; + readonly nullable: true; + }; readonly 'pg/timestamp-string@1': { readonly output: 'pg/timestamp-string@1'; readonly nullable: true; @@ -203,6 +231,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; diff --git a/test/integration/test/ports/engines/queries/filters/one_relation/_fixture/21356/generated/contract.d.ts b/test/integration/test/ports/engines/queries/filters/one_relation/_fixture/21356/generated/contract.d.ts index cc698102ba89..49039430a9a4 100644 --- a/test/integration/test/ports/engines/queries/filters/one_relation/_fixture/21356/generated/contract.d.ts +++ b/test/integration/test/ports/engines/queries/filters/one_relation/_fixture/21356/generated/contract.d.ts @@ -42,6 +42,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; @@ -91,6 +95,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; @@ -100,6 +108,14 @@ export type AggregateTypes = { readonly output: 'pg/time-temporal@1'; readonly nullable: true; }; + readonly 'pg/timestamp-date@1': { + readonly output: 'pg/timestamp-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-instant@1': { + readonly output: 'pg/timestamp-instant@1'; + readonly nullable: true; + }; readonly 'pg/timestamp-string@1': { readonly output: 'pg/timestamp-string@1'; readonly nullable: true; @@ -151,6 +167,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; @@ -160,6 +180,14 @@ export type AggregateTypes = { readonly output: 'pg/time-temporal@1'; readonly nullable: true; }; + readonly 'pg/timestamp-date@1': { + readonly output: 'pg/timestamp-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-instant@1': { + readonly output: 'pg/timestamp-instant@1'; + readonly nullable: true; + }; readonly 'pg/timestamp-string@1': { readonly output: 'pg/timestamp-string@1'; readonly nullable: true; @@ -203,6 +231,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; diff --git a/test/integration/test/ports/engines/queries/filters/one_relation/_fixture/21366/generated/contract.d.ts b/test/integration/test/ports/engines/queries/filters/one_relation/_fixture/21366/generated/contract.d.ts index 3817b98f7b38..7a4d85020407 100644 --- a/test/integration/test/ports/engines/queries/filters/one_relation/_fixture/21366/generated/contract.d.ts +++ b/test/integration/test/ports/engines/queries/filters/one_relation/_fixture/21366/generated/contract.d.ts @@ -42,6 +42,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; @@ -91,6 +95,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; @@ -100,6 +108,14 @@ export type AggregateTypes = { readonly output: 'pg/time-temporal@1'; readonly nullable: true; }; + readonly 'pg/timestamp-date@1': { + readonly output: 'pg/timestamp-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-instant@1': { + readonly output: 'pg/timestamp-instant@1'; + readonly nullable: true; + }; readonly 'pg/timestamp-string@1': { readonly output: 'pg/timestamp-string@1'; readonly nullable: true; @@ -151,6 +167,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; @@ -160,6 +180,14 @@ export type AggregateTypes = { readonly output: 'pg/time-temporal@1'; readonly nullable: true; }; + readonly 'pg/timestamp-date@1': { + readonly output: 'pg/timestamp-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-instant@1': { + readonly output: 'pg/timestamp-instant@1'; + readonly nullable: true; + }; readonly 'pg/timestamp-string@1': { readonly output: 'pg/timestamp-string@1'; readonly nullable: true; @@ -203,6 +231,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; diff --git a/test/integration/test/ports/engines/writes/top_level_mutations/create_many/_fixture/generated/contract.d.ts b/test/integration/test/ports/engines/writes/top_level_mutations/create_many/_fixture/generated/contract.d.ts index f7eb12bb26c1..f51ef42eef15 100644 --- a/test/integration/test/ports/engines/writes/top_level_mutations/create_many/_fixture/generated/contract.d.ts +++ b/test/integration/test/ports/engines/writes/top_level_mutations/create_many/_fixture/generated/contract.d.ts @@ -42,6 +42,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; @@ -91,6 +95,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; @@ -100,6 +108,14 @@ export type AggregateTypes = { readonly output: 'pg/time-temporal@1'; readonly nullable: true; }; + readonly 'pg/timestamp-date@1': { + readonly output: 'pg/timestamp-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-instant@1': { + readonly output: 'pg/timestamp-instant@1'; + readonly nullable: true; + }; readonly 'pg/timestamp-string@1': { readonly output: 'pg/timestamp-string@1'; readonly nullable: true; @@ -151,6 +167,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; @@ -160,6 +180,14 @@ export type AggregateTypes = { readonly output: 'pg/time-temporal@1'; readonly nullable: true; }; + readonly 'pg/timestamp-date@1': { + readonly output: 'pg/timestamp-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-instant@1': { + readonly output: 'pg/timestamp-instant@1'; + readonly nullable: true; + }; readonly 'pg/timestamp-string@1': { readonly output: 'pg/timestamp-string@1'; readonly nullable: true; @@ -203,6 +231,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; diff --git a/test/integration/test/ports/prisma/functional/batching-bigint/_fixture/generated/contract.d.ts b/test/integration/test/ports/prisma/functional/batching-bigint/_fixture/generated/contract.d.ts index 5442c2ece6c6..035c51c5faa0 100644 --- a/test/integration/test/ports/prisma/functional/batching-bigint/_fixture/generated/contract.d.ts +++ b/test/integration/test/ports/prisma/functional/batching-bigint/_fixture/generated/contract.d.ts @@ -42,6 +42,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; @@ -91,6 +95,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; @@ -100,6 +108,14 @@ export type AggregateTypes = { readonly output: 'pg/time-temporal@1'; readonly nullable: true; }; + readonly 'pg/timestamp-date@1': { + readonly output: 'pg/timestamp-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-instant@1': { + readonly output: 'pg/timestamp-instant@1'; + readonly nullable: true; + }; readonly 'pg/timestamp-string@1': { readonly output: 'pg/timestamp-string@1'; readonly nullable: true; @@ -151,6 +167,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; @@ -160,6 +180,14 @@ export type AggregateTypes = { readonly output: 'pg/time-temporal@1'; readonly nullable: true; }; + readonly 'pg/timestamp-date@1': { + readonly output: 'pg/timestamp-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-instant@1': { + readonly output: 'pg/timestamp-instant@1'; + readonly nullable: true; + }; readonly 'pg/timestamp-string@1': { readonly output: 'pg/timestamp-string@1'; readonly nullable: true; @@ -203,6 +231,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; diff --git a/test/integration/test/ports/prisma/functional/batching-bytes/_fixture/generated/contract.d.ts b/test/integration/test/ports/prisma/functional/batching-bytes/_fixture/generated/contract.d.ts index df41e3e37ff0..cd04ffed0ad3 100644 --- a/test/integration/test/ports/prisma/functional/batching-bytes/_fixture/generated/contract.d.ts +++ b/test/integration/test/ports/prisma/functional/batching-bytes/_fixture/generated/contract.d.ts @@ -42,6 +42,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; @@ -91,6 +95,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; @@ -100,6 +108,14 @@ export type AggregateTypes = { readonly output: 'pg/time-temporal@1'; readonly nullable: true; }; + readonly 'pg/timestamp-date@1': { + readonly output: 'pg/timestamp-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-instant@1': { + readonly output: 'pg/timestamp-instant@1'; + readonly nullable: true; + }; readonly 'pg/timestamp-string@1': { readonly output: 'pg/timestamp-string@1'; readonly nullable: true; @@ -151,6 +167,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; @@ -160,6 +180,14 @@ export type AggregateTypes = { readonly output: 'pg/time-temporal@1'; readonly nullable: true; }; + readonly 'pg/timestamp-date@1': { + readonly output: 'pg/timestamp-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-instant@1': { + readonly output: 'pg/timestamp-instant@1'; + readonly nullable: true; + }; readonly 'pg/timestamp-string@1': { readonly output: 'pg/timestamp-string@1'; readonly nullable: true; @@ -203,6 +231,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; diff --git a/test/integration/test/ports/prisma/functional/blog-update/_fixture/generated/contract.d.ts b/test/integration/test/ports/prisma/functional/blog-update/_fixture/generated/contract.d.ts index 212e70c23d1e..76f033b38a65 100644 --- a/test/integration/test/ports/prisma/functional/blog-update/_fixture/generated/contract.d.ts +++ b/test/integration/test/ports/prisma/functional/blog-update/_fixture/generated/contract.d.ts @@ -43,6 +43,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; @@ -92,6 +96,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; @@ -101,6 +109,14 @@ export type AggregateTypes = { readonly output: 'pg/time-temporal@1'; readonly nullable: true; }; + readonly 'pg/timestamp-date@1': { + readonly output: 'pg/timestamp-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-instant@1': { + readonly output: 'pg/timestamp-instant@1'; + readonly nullable: true; + }; readonly 'pg/timestamp-string@1': { readonly output: 'pg/timestamp-string@1'; readonly nullable: true; @@ -152,6 +168,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; @@ -161,6 +181,14 @@ export type AggregateTypes = { readonly output: 'pg/time-temporal@1'; readonly nullable: true; }; + readonly 'pg/timestamp-date@1': { + readonly output: 'pg/timestamp-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-instant@1': { + readonly output: 'pg/timestamp-instant@1'; + readonly nullable: true; + }; readonly 'pg/timestamp-string@1': { readonly output: 'pg/timestamp-string@1'; readonly nullable: true; @@ -204,6 +232,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; diff --git a/test/integration/test/ports/prisma/functional/bytes-upsert/_fixture/generated/contract.d.ts b/test/integration/test/ports/prisma/functional/bytes-upsert/_fixture/generated/contract.d.ts index 11a91f24ad19..e06dfaa7a1d4 100644 --- a/test/integration/test/ports/prisma/functional/bytes-upsert/_fixture/generated/contract.d.ts +++ b/test/integration/test/ports/prisma/functional/bytes-upsert/_fixture/generated/contract.d.ts @@ -43,6 +43,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; @@ -92,6 +96,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; @@ -101,6 +109,14 @@ export type AggregateTypes = { readonly output: 'pg/time-temporal@1'; readonly nullable: true; }; + readonly 'pg/timestamp-date@1': { + readonly output: 'pg/timestamp-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-instant@1': { + readonly output: 'pg/timestamp-instant@1'; + readonly nullable: true; + }; readonly 'pg/timestamp-string@1': { readonly output: 'pg/timestamp-string@1'; readonly nullable: true; @@ -152,6 +168,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; @@ -161,6 +181,14 @@ export type AggregateTypes = { readonly output: 'pg/time-temporal@1'; readonly nullable: true; }; + readonly 'pg/timestamp-date@1': { + readonly output: 'pg/timestamp-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-instant@1': { + readonly output: 'pg/timestamp-instant@1'; + readonly nullable: true; + }; readonly 'pg/timestamp-string@1': { readonly output: 'pg/timestamp-string@1'; readonly nullable: true; @@ -204,6 +232,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; diff --git a/test/integration/test/ports/prisma/functional/chunking-query/_fixture/generated/contract.d.ts b/test/integration/test/ports/prisma/functional/chunking-query/_fixture/generated/contract.d.ts index 4f788737d495..9f9fd7a0ef19 100644 --- a/test/integration/test/ports/prisma/functional/chunking-query/_fixture/generated/contract.d.ts +++ b/test/integration/test/ports/prisma/functional/chunking-query/_fixture/generated/contract.d.ts @@ -42,6 +42,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; @@ -91,6 +95,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; @@ -100,6 +108,14 @@ export type AggregateTypes = { readonly output: 'pg/time-temporal@1'; readonly nullable: true; }; + readonly 'pg/timestamp-date@1': { + readonly output: 'pg/timestamp-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-instant@1': { + readonly output: 'pg/timestamp-instant@1'; + readonly nullable: true; + }; readonly 'pg/timestamp-string@1': { readonly output: 'pg/timestamp-string@1'; readonly nullable: true; @@ -151,6 +167,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; @@ -160,6 +180,14 @@ export type AggregateTypes = { readonly output: 'pg/time-temporal@1'; readonly nullable: true; }; + readonly 'pg/timestamp-date@1': { + readonly output: 'pg/timestamp-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-instant@1': { + readonly output: 'pg/timestamp-instant@1'; + readonly nullable: true; + }; readonly 'pg/timestamp-string@1': { readonly output: 'pg/timestamp-string@1'; readonly nullable: true; @@ -203,6 +231,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; diff --git a/test/integration/test/ports/prisma/functional/create-default-date/_fixture/generated/contract.d.ts b/test/integration/test/ports/prisma/functional/create-default-date/_fixture/generated/contract.d.ts index 7503f6f24167..9772fe413043 100644 --- a/test/integration/test/ports/prisma/functional/create-default-date/_fixture/generated/contract.d.ts +++ b/test/integration/test/ports/prisma/functional/create-default-date/_fixture/generated/contract.d.ts @@ -42,6 +42,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; @@ -91,6 +95,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; @@ -100,6 +108,14 @@ export type AggregateTypes = { readonly output: 'pg/time-temporal@1'; readonly nullable: true; }; + readonly 'pg/timestamp-date@1': { + readonly output: 'pg/timestamp-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-instant@1': { + readonly output: 'pg/timestamp-instant@1'; + readonly nullable: true; + }; readonly 'pg/timestamp-string@1': { readonly output: 'pg/timestamp-string@1'; readonly nullable: true; @@ -151,6 +167,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; @@ -160,6 +180,14 @@ export type AggregateTypes = { readonly output: 'pg/time-temporal@1'; readonly nullable: true; }; + readonly 'pg/timestamp-date@1': { + readonly output: 'pg/timestamp-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-instant@1': { + readonly output: 'pg/timestamp-instant@1'; + readonly nullable: true; + }; readonly 'pg/timestamp-string@1': { readonly output: 'pg/timestamp-string@1'; readonly nullable: true; @@ -203,6 +231,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; diff --git a/test/integration/test/ports/prisma/functional/decimal-list/_fixture/generated/contract.d.ts b/test/integration/test/ports/prisma/functional/decimal-list/_fixture/generated/contract.d.ts index 05c908043cd3..9e9bace0da98 100644 --- a/test/integration/test/ports/prisma/functional/decimal-list/_fixture/generated/contract.d.ts +++ b/test/integration/test/ports/prisma/functional/decimal-list/_fixture/generated/contract.d.ts @@ -43,6 +43,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; @@ -92,6 +96,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; @@ -101,6 +109,14 @@ export type AggregateTypes = { readonly output: 'pg/time-temporal@1'; readonly nullable: true; }; + readonly 'pg/timestamp-date@1': { + readonly output: 'pg/timestamp-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-instant@1': { + readonly output: 'pg/timestamp-instant@1'; + readonly nullable: true; + }; readonly 'pg/timestamp-string@1': { readonly output: 'pg/timestamp-string@1'; readonly nullable: true; @@ -152,6 +168,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; @@ -161,6 +181,14 @@ export type AggregateTypes = { readonly output: 'pg/time-temporal@1'; readonly nullable: true; }; + readonly 'pg/timestamp-date@1': { + readonly output: 'pg/timestamp-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-instant@1': { + readonly output: 'pg/timestamp-instant@1'; + readonly nullable: true; + }; readonly 'pg/timestamp-string@1': { readonly output: 'pg/timestamp-string@1'; readonly nullable: true; @@ -204,6 +232,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; diff --git a/test/integration/test/ports/prisma/functional/decimal-precision/_fixture/generated/contract.d.ts b/test/integration/test/ports/prisma/functional/decimal-precision/_fixture/generated/contract.d.ts index 072b88e9d9ff..7ba79bb02b38 100644 --- a/test/integration/test/ports/prisma/functional/decimal-precision/_fixture/generated/contract.d.ts +++ b/test/integration/test/ports/prisma/functional/decimal-precision/_fixture/generated/contract.d.ts @@ -43,6 +43,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; @@ -92,6 +96,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; @@ -101,6 +109,14 @@ export type AggregateTypes = { readonly output: 'pg/time-temporal@1'; readonly nullable: true; }; + readonly 'pg/timestamp-date@1': { + readonly output: 'pg/timestamp-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-instant@1': { + readonly output: 'pg/timestamp-instant@1'; + readonly nullable: true; + }; readonly 'pg/timestamp-string@1': { readonly output: 'pg/timestamp-string@1'; readonly nullable: true; @@ -152,6 +168,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; @@ -161,6 +181,14 @@ export type AggregateTypes = { readonly output: 'pg/time-temporal@1'; readonly nullable: true; }; + readonly 'pg/timestamp-date@1': { + readonly output: 'pg/timestamp-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-instant@1': { + readonly output: 'pg/timestamp-instant@1'; + readonly nullable: true; + }; readonly 'pg/timestamp-string@1': { readonly output: 'pg/timestamp-string@1'; readonly nullable: true; @@ -204,6 +232,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; diff --git a/test/integration/test/ports/prisma/functional/decimal-scalar/_fixture/generated/contract.d.ts b/test/integration/test/ports/prisma/functional/decimal-scalar/_fixture/generated/contract.d.ts index a2efef065de8..2b9d895de131 100644 --- a/test/integration/test/ports/prisma/functional/decimal-scalar/_fixture/generated/contract.d.ts +++ b/test/integration/test/ports/prisma/functional/decimal-scalar/_fixture/generated/contract.d.ts @@ -43,6 +43,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; @@ -92,6 +96,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; @@ -101,6 +109,14 @@ export type AggregateTypes = { readonly output: 'pg/time-temporal@1'; readonly nullable: true; }; + readonly 'pg/timestamp-date@1': { + readonly output: 'pg/timestamp-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-instant@1': { + readonly output: 'pg/timestamp-instant@1'; + readonly nullable: true; + }; readonly 'pg/timestamp-string@1': { readonly output: 'pg/timestamp-string@1'; readonly nullable: true; @@ -152,6 +168,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; @@ -161,6 +181,14 @@ export type AggregateTypes = { readonly output: 'pg/time-temporal@1'; readonly nullable: true; }; + readonly 'pg/timestamp-date@1': { + readonly output: 'pg/timestamp-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-instant@1': { + readonly output: 'pg/timestamp-instant@1'; + readonly nullable: true; + }; readonly 'pg/timestamp-string@1': { readonly output: 'pg/timestamp-string@1'; readonly nullable: true; @@ -204,6 +232,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; diff --git a/test/integration/test/ports/prisma/functional/default-selection/_fixture/generated/contract.d.ts b/test/integration/test/ports/prisma/functional/default-selection/_fixture/generated/contract.d.ts index ff585733f252..fd9cc7c247a0 100644 --- a/test/integration/test/ports/prisma/functional/default-selection/_fixture/generated/contract.d.ts +++ b/test/integration/test/ports/prisma/functional/default-selection/_fixture/generated/contract.d.ts @@ -42,6 +42,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; @@ -91,6 +95,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; @@ -100,6 +108,14 @@ export type AggregateTypes = { readonly output: 'pg/time-temporal@1'; readonly nullable: true; }; + readonly 'pg/timestamp-date@1': { + readonly output: 'pg/timestamp-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-instant@1': { + readonly output: 'pg/timestamp-instant@1'; + readonly nullable: true; + }; readonly 'pg/timestamp-string@1': { readonly output: 'pg/timestamp-string@1'; readonly nullable: true; @@ -151,6 +167,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; @@ -160,6 +180,14 @@ export type AggregateTypes = { readonly output: 'pg/time-temporal@1'; readonly nullable: true; }; + readonly 'pg/timestamp-date@1': { + readonly output: 'pg/timestamp-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-instant@1': { + readonly output: 'pg/timestamp-instant@1'; + readonly nullable: true; + }; readonly 'pg/timestamp-string@1': { readonly output: 'pg/timestamp-string@1'; readonly nullable: true; @@ -203,6 +231,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; diff --git a/test/integration/test/ports/prisma/functional/distinct/_fixture/generated/contract.d.ts b/test/integration/test/ports/prisma/functional/distinct/_fixture/generated/contract.d.ts index 35a444c683c8..ed89b33c639d 100644 --- a/test/integration/test/ports/prisma/functional/distinct/_fixture/generated/contract.d.ts +++ b/test/integration/test/ports/prisma/functional/distinct/_fixture/generated/contract.d.ts @@ -42,6 +42,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; @@ -91,6 +95,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; @@ -100,6 +108,14 @@ export type AggregateTypes = { readonly output: 'pg/time-temporal@1'; readonly nullable: true; }; + readonly 'pg/timestamp-date@1': { + readonly output: 'pg/timestamp-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-instant@1': { + readonly output: 'pg/timestamp-instant@1'; + readonly nullable: true; + }; readonly 'pg/timestamp-string@1': { readonly output: 'pg/timestamp-string@1'; readonly nullable: true; @@ -151,6 +167,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; @@ -160,6 +180,14 @@ export type AggregateTypes = { readonly output: 'pg/time-temporal@1'; readonly nullable: true; }; + readonly 'pg/timestamp-date@1': { + readonly output: 'pg/timestamp-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-instant@1': { + readonly output: 'pg/timestamp-instant@1'; + readonly nullable: true; + }; readonly 'pg/timestamp-string@1': { readonly output: 'pg/timestamp-string@1'; readonly nullable: true; @@ -203,6 +231,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; diff --git a/test/integration/test/ports/prisma/functional/driver-adapters-error-forwarding/_fixture/generated/contract.d.ts b/test/integration/test/ports/prisma/functional/driver-adapters-error-forwarding/_fixture/generated/contract.d.ts index 434588009d75..2454e4dcaae8 100644 --- a/test/integration/test/ports/prisma/functional/driver-adapters-error-forwarding/_fixture/generated/contract.d.ts +++ b/test/integration/test/ports/prisma/functional/driver-adapters-error-forwarding/_fixture/generated/contract.d.ts @@ -42,6 +42,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; @@ -91,6 +95,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; @@ -100,6 +108,14 @@ export type AggregateTypes = { readonly output: 'pg/time-temporal@1'; readonly nullable: true; }; + readonly 'pg/timestamp-date@1': { + readonly output: 'pg/timestamp-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-instant@1': { + readonly output: 'pg/timestamp-instant@1'; + readonly nullable: true; + }; readonly 'pg/timestamp-string@1': { readonly output: 'pg/timestamp-string@1'; readonly nullable: true; @@ -151,6 +167,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; @@ -160,6 +180,14 @@ export type AggregateTypes = { readonly output: 'pg/time-temporal@1'; readonly nullable: true; }; + readonly 'pg/timestamp-date@1': { + readonly output: 'pg/timestamp-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-instant@1': { + readonly output: 'pg/timestamp-instant@1'; + readonly nullable: true; + }; readonly 'pg/timestamp-string@1': { readonly output: 'pg/timestamp-string@1'; readonly nullable: true; @@ -203,6 +231,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; diff --git a/test/integration/test/ports/prisma/functional/driver-adapters-team-orm-687-bytes/_fixture/generated/contract.d.ts b/test/integration/test/ports/prisma/functional/driver-adapters-team-orm-687-bytes/_fixture/generated/contract.d.ts index cb4957706b68..f616735d3de3 100644 --- a/test/integration/test/ports/prisma/functional/driver-adapters-team-orm-687-bytes/_fixture/generated/contract.d.ts +++ b/test/integration/test/ports/prisma/functional/driver-adapters-team-orm-687-bytes/_fixture/generated/contract.d.ts @@ -42,6 +42,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; @@ -91,6 +95,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; @@ -100,6 +108,14 @@ export type AggregateTypes = { readonly output: 'pg/time-temporal@1'; readonly nullable: true; }; + readonly 'pg/timestamp-date@1': { + readonly output: 'pg/timestamp-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-instant@1': { + readonly output: 'pg/timestamp-instant@1'; + readonly nullable: true; + }; readonly 'pg/timestamp-string@1': { readonly output: 'pg/timestamp-string@1'; readonly nullable: true; @@ -151,6 +167,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; @@ -160,6 +180,14 @@ export type AggregateTypes = { readonly output: 'pg/time-temporal@1'; readonly nullable: true; }; + readonly 'pg/timestamp-date@1': { + readonly output: 'pg/timestamp-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-instant@1': { + readonly output: 'pg/timestamp-instant@1'; + readonly nullable: true; + }; readonly 'pg/timestamp-string@1': { readonly output: 'pg/timestamp-string@1'; readonly nullable: true; @@ -203,6 +231,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; diff --git a/test/integration/test/ports/prisma/functional/driver-adapters-validate-active-provider/_fixture/generated/contract.d.ts b/test/integration/test/ports/prisma/functional/driver-adapters-validate-active-provider/_fixture/generated/contract.d.ts index 8246d093e8e6..6ff68a7d15e8 100644 --- a/test/integration/test/ports/prisma/functional/driver-adapters-validate-active-provider/_fixture/generated/contract.d.ts +++ b/test/integration/test/ports/prisma/functional/driver-adapters-validate-active-provider/_fixture/generated/contract.d.ts @@ -42,6 +42,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; @@ -91,6 +95,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; @@ -100,6 +108,14 @@ export type AggregateTypes = { readonly output: 'pg/time-temporal@1'; readonly nullable: true; }; + readonly 'pg/timestamp-date@1': { + readonly output: 'pg/timestamp-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-instant@1': { + readonly output: 'pg/timestamp-instant@1'; + readonly nullable: true; + }; readonly 'pg/timestamp-string@1': { readonly output: 'pg/timestamp-string@1'; readonly nullable: true; @@ -151,6 +167,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; @@ -160,6 +180,14 @@ export type AggregateTypes = { readonly output: 'pg/time-temporal@1'; readonly nullable: true; }; + readonly 'pg/timestamp-date@1': { + readonly output: 'pg/timestamp-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-instant@1': { + readonly output: 'pg/timestamp-instant@1'; + readonly nullable: true; + }; readonly 'pg/timestamp-string@1': { readonly output: 'pg/timestamp-string@1'; readonly nullable: true; @@ -203,6 +231,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; diff --git a/test/integration/test/ports/prisma/functional/enum-array/_fixture/generated/contract.d.ts b/test/integration/test/ports/prisma/functional/enum-array/_fixture/generated/contract.d.ts index 558b18e08eea..282b6df62c34 100644 --- a/test/integration/test/ports/prisma/functional/enum-array/_fixture/generated/contract.d.ts +++ b/test/integration/test/ports/prisma/functional/enum-array/_fixture/generated/contract.d.ts @@ -43,6 +43,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; @@ -92,6 +96,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; @@ -101,6 +109,14 @@ export type AggregateTypes = { readonly output: 'pg/time-temporal@1'; readonly nullable: true; }; + readonly 'pg/timestamp-date@1': { + readonly output: 'pg/timestamp-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-instant@1': { + readonly output: 'pg/timestamp-instant@1'; + readonly nullable: true; + }; readonly 'pg/timestamp-string@1': { readonly output: 'pg/timestamp-string@1'; readonly nullable: true; @@ -152,6 +168,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; @@ -161,6 +181,14 @@ export type AggregateTypes = { readonly output: 'pg/time-temporal@1'; readonly nullable: true; }; + readonly 'pg/timestamp-date@1': { + readonly output: 'pg/timestamp-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-instant@1': { + readonly output: 'pg/timestamp-instant@1'; + readonly nullable: true; + }; readonly 'pg/timestamp-string@1': { readonly output: 'pg/timestamp-string@1'; readonly nullable: true; @@ -204,6 +232,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; diff --git a/test/integration/test/ports/prisma/functional/extended-where/_fixture/generated/contract.d.ts b/test/integration/test/ports/prisma/functional/extended-where/_fixture/generated/contract.d.ts index 0f252678bacf..e52a151f5303 100644 --- a/test/integration/test/ports/prisma/functional/extended-where/_fixture/generated/contract.d.ts +++ b/test/integration/test/ports/prisma/functional/extended-where/_fixture/generated/contract.d.ts @@ -43,6 +43,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; @@ -92,6 +96,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; @@ -101,6 +109,14 @@ export type AggregateTypes = { readonly output: 'pg/time-temporal@1'; readonly nullable: true; }; + readonly 'pg/timestamp-date@1': { + readonly output: 'pg/timestamp-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-instant@1': { + readonly output: 'pg/timestamp-instant@1'; + readonly nullable: true; + }; readonly 'pg/timestamp-string@1': { readonly output: 'pg/timestamp-string@1'; readonly nullable: true; @@ -152,6 +168,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; @@ -161,6 +181,14 @@ export type AggregateTypes = { readonly output: 'pg/time-temporal@1'; readonly nullable: true; }; + readonly 'pg/timestamp-date@1': { + readonly output: 'pg/timestamp-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-instant@1': { + readonly output: 'pg/timestamp-instant@1'; + readonly nullable: true; + }; readonly 'pg/timestamp-string@1': { readonly output: 'pg/timestamp-string@1'; readonly nullable: true; @@ -204,6 +232,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; diff --git a/test/integration/test/ports/prisma/functional/filter-count-relations/_fixture/generated/contract.d.ts b/test/integration/test/ports/prisma/functional/filter-count-relations/_fixture/generated/contract.d.ts index 7220221939cd..bba4c91b2db8 100644 --- a/test/integration/test/ports/prisma/functional/filter-count-relations/_fixture/generated/contract.d.ts +++ b/test/integration/test/ports/prisma/functional/filter-count-relations/_fixture/generated/contract.d.ts @@ -42,6 +42,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; @@ -91,6 +95,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; @@ -100,6 +108,14 @@ export type AggregateTypes = { readonly output: 'pg/time-temporal@1'; readonly nullable: true; }; + readonly 'pg/timestamp-date@1': { + readonly output: 'pg/timestamp-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-instant@1': { + readonly output: 'pg/timestamp-instant@1'; + readonly nullable: true; + }; readonly 'pg/timestamp-string@1': { readonly output: 'pg/timestamp-string@1'; readonly nullable: true; @@ -151,6 +167,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; @@ -160,6 +180,14 @@ export type AggregateTypes = { readonly output: 'pg/time-temporal@1'; readonly nullable: true; }; + readonly 'pg/timestamp-date@1': { + readonly output: 'pg/timestamp-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-instant@1': { + readonly output: 'pg/timestamp-instant@1'; + readonly nullable: true; + }; readonly 'pg/timestamp-string@1': { readonly output: 'pg/timestamp-string@1'; readonly nullable: true; @@ -203,6 +231,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; diff --git a/test/integration/test/ports/prisma/functional/find-unique-or-throw-batching/_fixture/generated/contract.d.ts b/test/integration/test/ports/prisma/functional/find-unique-or-throw-batching/_fixture/generated/contract.d.ts index ad374b9e2c05..f13dfe64bf20 100644 --- a/test/integration/test/ports/prisma/functional/find-unique-or-throw-batching/_fixture/generated/contract.d.ts +++ b/test/integration/test/ports/prisma/functional/find-unique-or-throw-batching/_fixture/generated/contract.d.ts @@ -42,6 +42,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; @@ -91,6 +95,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; @@ -100,6 +108,14 @@ export type AggregateTypes = { readonly output: 'pg/time-temporal@1'; readonly nullable: true; }; + readonly 'pg/timestamp-date@1': { + readonly output: 'pg/timestamp-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-instant@1': { + readonly output: 'pg/timestamp-instant@1'; + readonly nullable: true; + }; readonly 'pg/timestamp-string@1': { readonly output: 'pg/timestamp-string@1'; readonly nullable: true; @@ -151,6 +167,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; @@ -160,6 +180,14 @@ export type AggregateTypes = { readonly output: 'pg/time-temporal@1'; readonly nullable: true; }; + readonly 'pg/timestamp-date@1': { + readonly output: 'pg/timestamp-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-instant@1': { + readonly output: 'pg/timestamp-instant@1'; + readonly nullable: true; + }; readonly 'pg/timestamp-string@1': { readonly output: 'pg/timestamp-string@1'; readonly nullable: true; @@ -203,6 +231,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; diff --git a/test/integration/test/ports/prisma/functional/handle-int-overflow/_fixture/generated/contract.d.ts b/test/integration/test/ports/prisma/functional/handle-int-overflow/_fixture/generated/contract.d.ts index 441e47645949..f8fe71b68df8 100644 --- a/test/integration/test/ports/prisma/functional/handle-int-overflow/_fixture/generated/contract.d.ts +++ b/test/integration/test/ports/prisma/functional/handle-int-overflow/_fixture/generated/contract.d.ts @@ -43,6 +43,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; @@ -92,6 +96,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; @@ -101,6 +109,14 @@ export type AggregateTypes = { readonly output: 'pg/time-temporal@1'; readonly nullable: true; }; + readonly 'pg/timestamp-date@1': { + readonly output: 'pg/timestamp-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-instant@1': { + readonly output: 'pg/timestamp-instant@1'; + readonly nullable: true; + }; readonly 'pg/timestamp-string@1': { readonly output: 'pg/timestamp-string@1'; readonly nullable: true; @@ -152,6 +168,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; @@ -161,6 +181,14 @@ export type AggregateTypes = { readonly output: 'pg/time-temporal@1'; readonly nullable: true; }; + readonly 'pg/timestamp-date@1': { + readonly output: 'pg/timestamp-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-instant@1': { + readonly output: 'pg/timestamp-instant@1'; + readonly nullable: true; + }; readonly 'pg/timestamp-string@1': { readonly output: 'pg/timestamp-string@1'; readonly nullable: true; @@ -204,6 +232,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; diff --git a/test/integration/test/ports/prisma/functional/interactive-transactions/_fixture/generated/contract.d.ts b/test/integration/test/ports/prisma/functional/interactive-transactions/_fixture/generated/contract.d.ts index ea2602b6aaf1..86b47698325b 100644 --- a/test/integration/test/ports/prisma/functional/interactive-transactions/_fixture/generated/contract.d.ts +++ b/test/integration/test/ports/prisma/functional/interactive-transactions/_fixture/generated/contract.d.ts @@ -43,6 +43,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; @@ -92,6 +96,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; @@ -101,6 +109,14 @@ export type AggregateTypes = { readonly output: 'pg/time-temporal@1'; readonly nullable: true; }; + readonly 'pg/timestamp-date@1': { + readonly output: 'pg/timestamp-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-instant@1': { + readonly output: 'pg/timestamp-instant@1'; + readonly nullable: true; + }; readonly 'pg/timestamp-string@1': { readonly output: 'pg/timestamp-string@1'; readonly nullable: true; @@ -152,6 +168,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; @@ -161,6 +181,14 @@ export type AggregateTypes = { readonly output: 'pg/time-temporal@1'; readonly nullable: true; }; + readonly 'pg/timestamp-date@1': { + readonly output: 'pg/timestamp-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-instant@1': { + readonly output: 'pg/timestamp-instant@1'; + readonly nullable: true; + }; readonly 'pg/timestamp-string@1': { readonly output: 'pg/timestamp-string@1'; readonly nullable: true; @@ -204,6 +232,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; diff --git a/test/integration/test/ports/prisma/functional/issues-11974/_fixture/generated/contract.d.ts b/test/integration/test/ports/prisma/functional/issues-11974/_fixture/generated/contract.d.ts index 67625d8cd789..59c8bad32294 100644 --- a/test/integration/test/ports/prisma/functional/issues-11974/_fixture/generated/contract.d.ts +++ b/test/integration/test/ports/prisma/functional/issues-11974/_fixture/generated/contract.d.ts @@ -42,6 +42,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; @@ -91,6 +95,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; @@ -100,6 +108,14 @@ export type AggregateTypes = { readonly output: 'pg/time-temporal@1'; readonly nullable: true; }; + readonly 'pg/timestamp-date@1': { + readonly output: 'pg/timestamp-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-instant@1': { + readonly output: 'pg/timestamp-instant@1'; + readonly nullable: true; + }; readonly 'pg/timestamp-string@1': { readonly output: 'pg/timestamp-string@1'; readonly nullable: true; @@ -151,6 +167,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; @@ -160,6 +180,14 @@ export type AggregateTypes = { readonly output: 'pg/time-temporal@1'; readonly nullable: true; }; + readonly 'pg/timestamp-date@1': { + readonly output: 'pg/timestamp-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-instant@1': { + readonly output: 'pg/timestamp-instant@1'; + readonly nullable: true; + }; readonly 'pg/timestamp-string@1': { readonly output: 'pg/timestamp-string@1'; readonly nullable: true; @@ -203,6 +231,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; diff --git a/test/integration/test/ports/prisma/functional/issues-12378/_fixture/generated/contract.d.ts b/test/integration/test/ports/prisma/functional/issues-12378/_fixture/generated/contract.d.ts index 27fdf0e9c7c9..df42b2a1f928 100644 --- a/test/integration/test/ports/prisma/functional/issues-12378/_fixture/generated/contract.d.ts +++ b/test/integration/test/ports/prisma/functional/issues-12378/_fixture/generated/contract.d.ts @@ -43,6 +43,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; @@ -92,6 +96,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; @@ -101,6 +109,14 @@ export type AggregateTypes = { readonly output: 'pg/time-temporal@1'; readonly nullable: true; }; + readonly 'pg/timestamp-date@1': { + readonly output: 'pg/timestamp-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-instant@1': { + readonly output: 'pg/timestamp-instant@1'; + readonly nullable: true; + }; readonly 'pg/timestamp-string@1': { readonly output: 'pg/timestamp-string@1'; readonly nullable: true; @@ -152,6 +168,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; @@ -161,6 +181,14 @@ export type AggregateTypes = { readonly output: 'pg/time-temporal@1'; readonly nullable: true; }; + readonly 'pg/timestamp-date@1': { + readonly output: 'pg/timestamp-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-instant@1': { + readonly output: 'pg/timestamp-instant@1'; + readonly nullable: true; + }; readonly 'pg/timestamp-string@1': { readonly output: 'pg/timestamp-string@1'; readonly nullable: true; @@ -204,6 +232,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; diff --git a/test/integration/test/ports/prisma/functional/issues-12557/_fixture/generated/contract.d.ts b/test/integration/test/ports/prisma/functional/issues-12557/_fixture/generated/contract.d.ts index 404aed9799da..5d359deead5d 100644 --- a/test/integration/test/ports/prisma/functional/issues-12557/_fixture/generated/contract.d.ts +++ b/test/integration/test/ports/prisma/functional/issues-12557/_fixture/generated/contract.d.ts @@ -42,6 +42,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; @@ -91,6 +95,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; @@ -100,6 +108,14 @@ export type AggregateTypes = { readonly output: 'pg/time-temporal@1'; readonly nullable: true; }; + readonly 'pg/timestamp-date@1': { + readonly output: 'pg/timestamp-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-instant@1': { + readonly output: 'pg/timestamp-instant@1'; + readonly nullable: true; + }; readonly 'pg/timestamp-string@1': { readonly output: 'pg/timestamp-string@1'; readonly nullable: true; @@ -151,6 +167,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; @@ -160,6 +180,14 @@ export type AggregateTypes = { readonly output: 'pg/time-temporal@1'; readonly nullable: true; }; + readonly 'pg/timestamp-date@1': { + readonly output: 'pg/timestamp-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-instant@1': { + readonly output: 'pg/timestamp-instant@1'; + readonly nullable: true; + }; readonly 'pg/timestamp-string@1': { readonly output: 'pg/timestamp-string@1'; readonly nullable: true; @@ -203,6 +231,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; diff --git a/test/integration/test/ports/prisma/functional/issues-12572/_fixture/generated/contract.d.ts b/test/integration/test/ports/prisma/functional/issues-12572/_fixture/generated/contract.d.ts index 828696196906..63180d3470cc 100644 --- a/test/integration/test/ports/prisma/functional/issues-12572/_fixture/generated/contract.d.ts +++ b/test/integration/test/ports/prisma/functional/issues-12572/_fixture/generated/contract.d.ts @@ -43,6 +43,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; @@ -92,6 +96,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; @@ -101,6 +109,14 @@ export type AggregateTypes = { readonly output: 'pg/time-temporal@1'; readonly nullable: true; }; + readonly 'pg/timestamp-date@1': { + readonly output: 'pg/timestamp-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-instant@1': { + readonly output: 'pg/timestamp-instant@1'; + readonly nullable: true; + }; readonly 'pg/timestamp-string@1': { readonly output: 'pg/timestamp-string@1'; readonly nullable: true; @@ -152,6 +168,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; @@ -161,6 +181,14 @@ export type AggregateTypes = { readonly output: 'pg/time-temporal@1'; readonly nullable: true; }; + readonly 'pg/timestamp-date@1': { + readonly output: 'pg/timestamp-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-instant@1': { + readonly output: 'pg/timestamp-instant@1'; + readonly nullable: true; + }; readonly 'pg/timestamp-string@1': { readonly output: 'pg/timestamp-string@1'; readonly nullable: true; @@ -204,6 +232,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; diff --git a/test/integration/test/ports/prisma/functional/issues-14271/_fixture/generated/contract.d.ts b/test/integration/test/ports/prisma/functional/issues-14271/_fixture/generated/contract.d.ts index 30f3e55f8532..4fdb7a1300e7 100644 --- a/test/integration/test/ports/prisma/functional/issues-14271/_fixture/generated/contract.d.ts +++ b/test/integration/test/ports/prisma/functional/issues-14271/_fixture/generated/contract.d.ts @@ -42,6 +42,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; @@ -91,6 +95,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; @@ -100,6 +108,14 @@ export type AggregateTypes = { readonly output: 'pg/time-temporal@1'; readonly nullable: true; }; + readonly 'pg/timestamp-date@1': { + readonly output: 'pg/timestamp-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-instant@1': { + readonly output: 'pg/timestamp-instant@1'; + readonly nullable: true; + }; readonly 'pg/timestamp-string@1': { readonly output: 'pg/timestamp-string@1'; readonly nullable: true; @@ -151,6 +167,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; @@ -160,6 +180,14 @@ export type AggregateTypes = { readonly output: 'pg/time-temporal@1'; readonly nullable: true; }; + readonly 'pg/timestamp-date@1': { + readonly output: 'pg/timestamp-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-instant@1': { + readonly output: 'pg/timestamp-instant@1'; + readonly nullable: true; + }; readonly 'pg/timestamp-string@1': { readonly output: 'pg/timestamp-string@1'; readonly nullable: true; @@ -203,6 +231,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; diff --git a/test/integration/test/ports/prisma/functional/issues-14954-date-batch/_fixture/generated/contract.d.ts b/test/integration/test/ports/prisma/functional/issues-14954-date-batch/_fixture/generated/contract.d.ts index 5ed008a091e6..b0b0c13f94ea 100644 --- a/test/integration/test/ports/prisma/functional/issues-14954-date-batch/_fixture/generated/contract.d.ts +++ b/test/integration/test/ports/prisma/functional/issues-14954-date-batch/_fixture/generated/contract.d.ts @@ -43,6 +43,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; @@ -92,6 +96,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; @@ -101,6 +109,14 @@ export type AggregateTypes = { readonly output: 'pg/time-temporal@1'; readonly nullable: true; }; + readonly 'pg/timestamp-date@1': { + readonly output: 'pg/timestamp-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-instant@1': { + readonly output: 'pg/timestamp-instant@1'; + readonly nullable: true; + }; readonly 'pg/timestamp-string@1': { readonly output: 'pg/timestamp-string@1'; readonly nullable: true; @@ -152,6 +168,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; @@ -161,6 +181,14 @@ export type AggregateTypes = { readonly output: 'pg/time-temporal@1'; readonly nullable: true; }; + readonly 'pg/timestamp-date@1': { + readonly output: 'pg/timestamp-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-instant@1': { + readonly output: 'pg/timestamp-instant@1'; + readonly nullable: true; + }; readonly 'pg/timestamp-string@1': { readonly output: 'pg/timestamp-string@1'; readonly nullable: true; @@ -204,6 +232,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; diff --git a/test/integration/test/ports/prisma/functional/issues-15044/_fixture/generated/contract.d.ts b/test/integration/test/ports/prisma/functional/issues-15044/_fixture/generated/contract.d.ts index 367761783b63..8e0aece2b546 100644 --- a/test/integration/test/ports/prisma/functional/issues-15044/_fixture/generated/contract.d.ts +++ b/test/integration/test/ports/prisma/functional/issues-15044/_fixture/generated/contract.d.ts @@ -43,6 +43,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; @@ -92,6 +96,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; @@ -101,6 +109,14 @@ export type AggregateTypes = { readonly output: 'pg/time-temporal@1'; readonly nullable: true; }; + readonly 'pg/timestamp-date@1': { + readonly output: 'pg/timestamp-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-instant@1': { + readonly output: 'pg/timestamp-instant@1'; + readonly nullable: true; + }; readonly 'pg/timestamp-string@1': { readonly output: 'pg/timestamp-string@1'; readonly nullable: true; @@ -152,6 +168,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; @@ -161,6 +181,14 @@ export type AggregateTypes = { readonly output: 'pg/time-temporal@1'; readonly nullable: true; }; + readonly 'pg/timestamp-date@1': { + readonly output: 'pg/timestamp-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-instant@1': { + readonly output: 'pg/timestamp-instant@1'; + readonly nullable: true; + }; readonly 'pg/timestamp-string@1': { readonly output: 'pg/timestamp-string@1'; readonly nullable: true; @@ -204,6 +232,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; diff --git a/test/integration/test/ports/prisma/functional/issues-16535-select-enum/_fixture/generated/contract.d.ts b/test/integration/test/ports/prisma/functional/issues-16535-select-enum/_fixture/generated/contract.d.ts index c7fa2039b90d..f1d1e114145d 100644 --- a/test/integration/test/ports/prisma/functional/issues-16535-select-enum/_fixture/generated/contract.d.ts +++ b/test/integration/test/ports/prisma/functional/issues-16535-select-enum/_fixture/generated/contract.d.ts @@ -43,6 +43,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; @@ -92,6 +96,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; @@ -101,6 +109,14 @@ export type AggregateTypes = { readonly output: 'pg/time-temporal@1'; readonly nullable: true; }; + readonly 'pg/timestamp-date@1': { + readonly output: 'pg/timestamp-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-instant@1': { + readonly output: 'pg/timestamp-instant@1'; + readonly nullable: true; + }; readonly 'pg/timestamp-string@1': { readonly output: 'pg/timestamp-string@1'; readonly nullable: true; @@ -152,6 +168,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; @@ -161,6 +181,14 @@ export type AggregateTypes = { readonly output: 'pg/time-temporal@1'; readonly nullable: true; }; + readonly 'pg/timestamp-date@1': { + readonly output: 'pg/timestamp-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-instant@1': { + readonly output: 'pg/timestamp-instant@1'; + readonly nullable: true; + }; readonly 'pg/timestamp-string@1': { readonly output: 'pg/timestamp-string@1'; readonly nullable: true; @@ -204,6 +232,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; diff --git a/test/integration/test/ports/prisma/functional/issues-17005-args-type-conflict/_fixture/generated/contract.d.ts b/test/integration/test/ports/prisma/functional/issues-17005-args-type-conflict/_fixture/generated/contract.d.ts index 82b29c6c5c8b..1f3a4e2a4e8b 100644 --- a/test/integration/test/ports/prisma/functional/issues-17005-args-type-conflict/_fixture/generated/contract.d.ts +++ b/test/integration/test/ports/prisma/functional/issues-17005-args-type-conflict/_fixture/generated/contract.d.ts @@ -43,6 +43,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; @@ -92,6 +96,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; @@ -101,6 +109,14 @@ export type AggregateTypes = { readonly output: 'pg/time-temporal@1'; readonly nullable: true; }; + readonly 'pg/timestamp-date@1': { + readonly output: 'pg/timestamp-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-instant@1': { + readonly output: 'pg/timestamp-instant@1'; + readonly nullable: true; + }; readonly 'pg/timestamp-string@1': { readonly output: 'pg/timestamp-string@1'; readonly nullable: true; @@ -152,6 +168,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; @@ -161,6 +181,14 @@ export type AggregateTypes = { readonly output: 'pg/time-temporal@1'; readonly nullable: true; }; + readonly 'pg/timestamp-date@1': { + readonly output: 'pg/timestamp-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-instant@1': { + readonly output: 'pg/timestamp-instant@1'; + readonly nullable: true; + }; readonly 'pg/timestamp-string@1': { readonly output: 'pg/timestamp-string@1'; readonly nullable: true; @@ -204,6 +232,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; diff --git a/test/integration/test/ports/prisma/functional/issues-17030-args-type-conflict/_fixture/generated/contract.d.ts b/test/integration/test/ports/prisma/functional/issues-17030-args-type-conflict/_fixture/generated/contract.d.ts index 9efcd4e85870..aa9ece970405 100644 --- a/test/integration/test/ports/prisma/functional/issues-17030-args-type-conflict/_fixture/generated/contract.d.ts +++ b/test/integration/test/ports/prisma/functional/issues-17030-args-type-conflict/_fixture/generated/contract.d.ts @@ -42,6 +42,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; @@ -91,6 +95,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; @@ -100,6 +108,14 @@ export type AggregateTypes = { readonly output: 'pg/time-temporal@1'; readonly nullable: true; }; + readonly 'pg/timestamp-date@1': { + readonly output: 'pg/timestamp-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-instant@1': { + readonly output: 'pg/timestamp-instant@1'; + readonly nullable: true; + }; readonly 'pg/timestamp-string@1': { readonly output: 'pg/timestamp-string@1'; readonly nullable: true; @@ -151,6 +167,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; @@ -160,6 +180,14 @@ export type AggregateTypes = { readonly output: 'pg/time-temporal@1'; readonly nullable: true; }; + readonly 'pg/timestamp-date@1': { + readonly output: 'pg/timestamp-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-instant@1': { + readonly output: 'pg/timestamp-instant@1'; + readonly nullable: true; + }; readonly 'pg/timestamp-string@1': { readonly output: 'pg/timestamp-string@1'; readonly nullable: true; @@ -203,6 +231,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; diff --git a/test/integration/test/ports/prisma/functional/issues-18970-invalid-date/_fixture/generated/contract.d.ts b/test/integration/test/ports/prisma/functional/issues-18970-invalid-date/_fixture/generated/contract.d.ts index 0d78e286070e..143d0f821149 100644 --- a/test/integration/test/ports/prisma/functional/issues-18970-invalid-date/_fixture/generated/contract.d.ts +++ b/test/integration/test/ports/prisma/functional/issues-18970-invalid-date/_fixture/generated/contract.d.ts @@ -43,6 +43,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; @@ -92,6 +96,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; @@ -101,6 +109,14 @@ export type AggregateTypes = { readonly output: 'pg/time-temporal@1'; readonly nullable: true; }; + readonly 'pg/timestamp-date@1': { + readonly output: 'pg/timestamp-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-instant@1': { + readonly output: 'pg/timestamp-instant@1'; + readonly nullable: true; + }; readonly 'pg/timestamp-string@1': { readonly output: 'pg/timestamp-string@1'; readonly nullable: true; @@ -152,6 +168,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; @@ -161,6 +181,14 @@ export type AggregateTypes = { readonly output: 'pg/time-temporal@1'; readonly nullable: true; }; + readonly 'pg/timestamp-date@1': { + readonly output: 'pg/timestamp-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-instant@1': { + readonly output: 'pg/timestamp-instant@1'; + readonly nullable: true; + }; readonly 'pg/timestamp-string@1': { readonly output: 'pg/timestamp-string@1'; readonly nullable: true; @@ -204,6 +232,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; diff --git a/test/integration/test/ports/prisma/functional/issues-20261-group-by-shortcut/_fixture/generated/contract.d.ts b/test/integration/test/ports/prisma/functional/issues-20261-group-by-shortcut/_fixture/generated/contract.d.ts index 1039b8a3e3c5..66b887bb68c9 100644 --- a/test/integration/test/ports/prisma/functional/issues-20261-group-by-shortcut/_fixture/generated/contract.d.ts +++ b/test/integration/test/ports/prisma/functional/issues-20261-group-by-shortcut/_fixture/generated/contract.d.ts @@ -43,6 +43,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; @@ -92,6 +96,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; @@ -101,6 +109,14 @@ export type AggregateTypes = { readonly output: 'pg/time-temporal@1'; readonly nullable: true; }; + readonly 'pg/timestamp-date@1': { + readonly output: 'pg/timestamp-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-instant@1': { + readonly output: 'pg/timestamp-instant@1'; + readonly nullable: true; + }; readonly 'pg/timestamp-string@1': { readonly output: 'pg/timestamp-string@1'; readonly nullable: true; @@ -152,6 +168,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; @@ -161,6 +181,14 @@ export type AggregateTypes = { readonly output: 'pg/time-temporal@1'; readonly nullable: true; }; + readonly 'pg/timestamp-date@1': { + readonly output: 'pg/timestamp-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-instant@1': { + readonly output: 'pg/timestamp-instant@1'; + readonly nullable: true; + }; readonly 'pg/timestamp-string@1': { readonly output: 'pg/timestamp-string@1'; readonly nullable: true; @@ -204,6 +232,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; diff --git a/test/integration/test/ports/prisma/functional/issues-21352-id-does-not-exist/_fixture/generated/contract.d.ts b/test/integration/test/ports/prisma/functional/issues-21352-id-does-not-exist/_fixture/generated/contract.d.ts index 93fd9e1aea26..24dc948b1014 100644 --- a/test/integration/test/ports/prisma/functional/issues-21352-id-does-not-exist/_fixture/generated/contract.d.ts +++ b/test/integration/test/ports/prisma/functional/issues-21352-id-does-not-exist/_fixture/generated/contract.d.ts @@ -42,6 +42,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; @@ -91,6 +95,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; @@ -100,6 +108,14 @@ export type AggregateTypes = { readonly output: 'pg/time-temporal@1'; readonly nullable: true; }; + readonly 'pg/timestamp-date@1': { + readonly output: 'pg/timestamp-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-instant@1': { + readonly output: 'pg/timestamp-instant@1'; + readonly nullable: true; + }; readonly 'pg/timestamp-string@1': { readonly output: 'pg/timestamp-string@1'; readonly nullable: true; @@ -151,6 +167,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; @@ -160,6 +180,14 @@ export type AggregateTypes = { readonly output: 'pg/time-temporal@1'; readonly nullable: true; }; + readonly 'pg/timestamp-date@1': { + readonly output: 'pg/timestamp-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-instant@1': { + readonly output: 'pg/timestamp-instant@1'; + readonly nullable: true; + }; readonly 'pg/timestamp-string@1': { readonly output: 'pg/timestamp-string@1'; readonly nullable: true; @@ -203,6 +231,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; diff --git a/test/integration/test/ports/prisma/functional/issues-21454-type-in-json/_fixture/generated/contract.d.ts b/test/integration/test/ports/prisma/functional/issues-21454-type-in-json/_fixture/generated/contract.d.ts index a75262d5b7a2..dc14977ff0e4 100644 --- a/test/integration/test/ports/prisma/functional/issues-21454-type-in-json/_fixture/generated/contract.d.ts +++ b/test/integration/test/ports/prisma/functional/issues-21454-type-in-json/_fixture/generated/contract.d.ts @@ -43,6 +43,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; @@ -92,6 +96,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; @@ -101,6 +109,14 @@ export type AggregateTypes = { readonly output: 'pg/time-temporal@1'; readonly nullable: true; }; + readonly 'pg/timestamp-date@1': { + readonly output: 'pg/timestamp-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-instant@1': { + readonly output: 'pg/timestamp-instant@1'; + readonly nullable: true; + }; readonly 'pg/timestamp-string@1': { readonly output: 'pg/timestamp-string@1'; readonly nullable: true; @@ -152,6 +168,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; @@ -161,6 +181,14 @@ export type AggregateTypes = { readonly output: 'pg/time-temporal@1'; readonly nullable: true; }; + readonly 'pg/timestamp-date@1': { + readonly output: 'pg/timestamp-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-instant@1': { + readonly output: 'pg/timestamp-instant@1'; + readonly nullable: true; + }; readonly 'pg/timestamp-string@1': { readonly output: 'pg/timestamp-string@1'; readonly nullable: true; @@ -204,6 +232,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; diff --git a/test/integration/test/ports/prisma/functional/issues-21631-batching-in-transaction/_fixture/generated/contract.d.ts b/test/integration/test/ports/prisma/functional/issues-21631-batching-in-transaction/_fixture/generated/contract.d.ts index 2ce42f9c0bba..8da6309cc989 100644 --- a/test/integration/test/ports/prisma/functional/issues-21631-batching-in-transaction/_fixture/generated/contract.d.ts +++ b/test/integration/test/ports/prisma/functional/issues-21631-batching-in-transaction/_fixture/generated/contract.d.ts @@ -43,6 +43,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; @@ -92,6 +96,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; @@ -101,6 +109,14 @@ export type AggregateTypes = { readonly output: 'pg/time-temporal@1'; readonly nullable: true; }; + readonly 'pg/timestamp-date@1': { + readonly output: 'pg/timestamp-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-instant@1': { + readonly output: 'pg/timestamp-instant@1'; + readonly nullable: true; + }; readonly 'pg/timestamp-string@1': { readonly output: 'pg/timestamp-string@1'; readonly nullable: true; @@ -152,6 +168,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; @@ -161,6 +181,14 @@ export type AggregateTypes = { readonly output: 'pg/time-temporal@1'; readonly nullable: true; }; + readonly 'pg/timestamp-date@1': { + readonly output: 'pg/timestamp-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-instant@1': { + readonly output: 'pg/timestamp-instant@1'; + readonly nullable: true; + }; readonly 'pg/timestamp-string@1': { readonly output: 'pg/timestamp-string@1'; readonly nullable: true; @@ -204,6 +232,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; diff --git a/test/integration/test/ports/prisma/functional/issues-22098-column-does-not-exist/_fixture/generated/contract.d.ts b/test/integration/test/ports/prisma/functional/issues-22098-column-does-not-exist/_fixture/generated/contract.d.ts index 2dc93b76bc2a..4d666e42e710 100644 --- a/test/integration/test/ports/prisma/functional/issues-22098-column-does-not-exist/_fixture/generated/contract.d.ts +++ b/test/integration/test/ports/prisma/functional/issues-22098-column-does-not-exist/_fixture/generated/contract.d.ts @@ -43,6 +43,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; @@ -92,6 +96,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; @@ -101,6 +109,14 @@ export type AggregateTypes = { readonly output: 'pg/time-temporal@1'; readonly nullable: true; }; + readonly 'pg/timestamp-date@1': { + readonly output: 'pg/timestamp-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-instant@1': { + readonly output: 'pg/timestamp-instant@1'; + readonly nullable: true; + }; readonly 'pg/timestamp-string@1': { readonly output: 'pg/timestamp-string@1'; readonly nullable: true; @@ -152,6 +168,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; @@ -161,6 +181,14 @@ export type AggregateTypes = { readonly output: 'pg/time-temporal@1'; readonly nullable: true; }; + readonly 'pg/timestamp-date@1': { + readonly output: 'pg/timestamp-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-instant@1': { + readonly output: 'pg/timestamp-instant@1'; + readonly nullable: true; + }; readonly 'pg/timestamp-string@1': { readonly output: 'pg/timestamp-string@1'; readonly nullable: true; @@ -204,6 +232,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; diff --git a/test/integration/test/ports/prisma/functional/issues-22610-parallel-batch/_fixture/generated/contract.d.ts b/test/integration/test/ports/prisma/functional/issues-22610-parallel-batch/_fixture/generated/contract.d.ts index 1ae9b1d22e8a..e5d958e9dbc1 100644 --- a/test/integration/test/ports/prisma/functional/issues-22610-parallel-batch/_fixture/generated/contract.d.ts +++ b/test/integration/test/ports/prisma/functional/issues-22610-parallel-batch/_fixture/generated/contract.d.ts @@ -43,6 +43,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; @@ -92,6 +96,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; @@ -101,6 +109,14 @@ export type AggregateTypes = { readonly output: 'pg/time-temporal@1'; readonly nullable: true; }; + readonly 'pg/timestamp-date@1': { + readonly output: 'pg/timestamp-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-instant@1': { + readonly output: 'pg/timestamp-instant@1'; + readonly nullable: true; + }; readonly 'pg/timestamp-string@1': { readonly output: 'pg/timestamp-string@1'; readonly nullable: true; @@ -152,6 +168,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; @@ -161,6 +181,14 @@ export type AggregateTypes = { readonly output: 'pg/time-temporal@1'; readonly nullable: true; }; + readonly 'pg/timestamp-date@1': { + readonly output: 'pg/timestamp-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-instant@1': { + readonly output: 'pg/timestamp-instant@1'; + readonly nullable: true; + }; readonly 'pg/timestamp-string@1': { readonly output: 'pg/timestamp-string@1'; readonly nullable: true; @@ -204,6 +232,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; diff --git a/test/integration/test/ports/prisma/functional/issues-23201-non-ascii-comments/_fixture/generated/contract.d.ts b/test/integration/test/ports/prisma/functional/issues-23201-non-ascii-comments/_fixture/generated/contract.d.ts index 54b45c9f1231..2249158354d1 100644 --- a/test/integration/test/ports/prisma/functional/issues-23201-non-ascii-comments/_fixture/generated/contract.d.ts +++ b/test/integration/test/ports/prisma/functional/issues-23201-non-ascii-comments/_fixture/generated/contract.d.ts @@ -43,6 +43,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; @@ -92,6 +96,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; @@ -101,6 +109,14 @@ export type AggregateTypes = { readonly output: 'pg/time-temporal@1'; readonly nullable: true; }; + readonly 'pg/timestamp-date@1': { + readonly output: 'pg/timestamp-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-instant@1': { + readonly output: 'pg/timestamp-instant@1'; + readonly nullable: true; + }; readonly 'pg/timestamp-string@1': { readonly output: 'pg/timestamp-string@1'; readonly nullable: true; @@ -152,6 +168,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; @@ -161,6 +181,14 @@ export type AggregateTypes = { readonly output: 'pg/time-temporal@1'; readonly nullable: true; }; + readonly 'pg/timestamp-date@1': { + readonly output: 'pg/timestamp-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-instant@1': { + readonly output: 'pg/timestamp-instant@1'; + readonly nullable: true; + }; readonly 'pg/timestamp-string@1': { readonly output: 'pg/timestamp-string@1'; readonly nullable: true; @@ -204,6 +232,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; diff --git a/test/integration/test/ports/prisma/functional/issues-23902/_fixture/generated/contract.d.ts b/test/integration/test/ports/prisma/functional/issues-23902/_fixture/generated/contract.d.ts index a2db97613ab5..f5072b4acbee 100644 --- a/test/integration/test/ports/prisma/functional/issues-23902/_fixture/generated/contract.d.ts +++ b/test/integration/test/ports/prisma/functional/issues-23902/_fixture/generated/contract.d.ts @@ -43,6 +43,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; @@ -92,6 +96,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; @@ -101,6 +109,14 @@ export type AggregateTypes = { readonly output: 'pg/time-temporal@1'; readonly nullable: true; }; + readonly 'pg/timestamp-date@1': { + readonly output: 'pg/timestamp-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-instant@1': { + readonly output: 'pg/timestamp-instant@1'; + readonly nullable: true; + }; readonly 'pg/timestamp-string@1': { readonly output: 'pg/timestamp-string@1'; readonly nullable: true; @@ -152,6 +168,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; @@ -161,6 +181,14 @@ export type AggregateTypes = { readonly output: 'pg/time-temporal@1'; readonly nullable: true; }; + readonly 'pg/timestamp-date@1': { + readonly output: 'pg/timestamp-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-instant@1': { + readonly output: 'pg/timestamp-instant@1'; + readonly nullable: true; + }; readonly 'pg/timestamp-string@1': { readonly output: 'pg/timestamp-string@1'; readonly nullable: true; @@ -204,6 +232,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; diff --git a/test/integration/test/ports/prisma/functional/issues-25404/_fixture/generated/contract.d.ts b/test/integration/test/ports/prisma/functional/issues-25404/_fixture/generated/contract.d.ts index 8b48e0f8fb34..af2d9d860e20 100644 --- a/test/integration/test/ports/prisma/functional/issues-25404/_fixture/generated/contract.d.ts +++ b/test/integration/test/ports/prisma/functional/issues-25404/_fixture/generated/contract.d.ts @@ -42,6 +42,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; @@ -91,6 +95,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; @@ -100,6 +108,14 @@ export type AggregateTypes = { readonly output: 'pg/time-temporal@1'; readonly nullable: true; }; + readonly 'pg/timestamp-date@1': { + readonly output: 'pg/timestamp-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-instant@1': { + readonly output: 'pg/timestamp-instant@1'; + readonly nullable: true; + }; readonly 'pg/timestamp-string@1': { readonly output: 'pg/timestamp-string@1'; readonly nullable: true; @@ -151,6 +167,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; @@ -160,6 +180,14 @@ export type AggregateTypes = { readonly output: 'pg/time-temporal@1'; readonly nullable: true; }; + readonly 'pg/timestamp-date@1': { + readonly output: 'pg/timestamp-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-instant@1': { + readonly output: 'pg/timestamp-instant@1'; + readonly nullable: true; + }; readonly 'pg/timestamp-string@1': { readonly output: 'pg/timestamp-string@1'; readonly nullable: true; @@ -203,6 +231,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; diff --git a/test/integration/test/ports/prisma/functional/issues-27455-bytes-id/_fixture/generated/contract.d.ts b/test/integration/test/ports/prisma/functional/issues-27455-bytes-id/_fixture/generated/contract.d.ts index 4ee1796fcbc1..e2639fb6cce9 100644 --- a/test/integration/test/ports/prisma/functional/issues-27455-bytes-id/_fixture/generated/contract.d.ts +++ b/test/integration/test/ports/prisma/functional/issues-27455-bytes-id/_fixture/generated/contract.d.ts @@ -42,6 +42,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; @@ -91,6 +95,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; @@ -100,6 +108,14 @@ export type AggregateTypes = { readonly output: 'pg/time-temporal@1'; readonly nullable: true; }; + readonly 'pg/timestamp-date@1': { + readonly output: 'pg/timestamp-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-instant@1': { + readonly output: 'pg/timestamp-instant@1'; + readonly nullable: true; + }; readonly 'pg/timestamp-string@1': { readonly output: 'pg/timestamp-string@1'; readonly nullable: true; @@ -151,6 +167,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; @@ -160,6 +180,14 @@ export type AggregateTypes = { readonly output: 'pg/time-temporal@1'; readonly nullable: true; }; + readonly 'pg/timestamp-date@1': { + readonly output: 'pg/timestamp-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-instant@1': { + readonly output: 'pg/timestamp-instant@1'; + readonly nullable: true; + }; readonly 'pg/timestamp-string@1': { readonly output: 'pg/timestamp-string@1'; readonly nullable: true; @@ -203,6 +231,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; diff --git a/test/integration/test/ports/prisma/functional/issues-27511-include-enum-array/_fixture/generated/contract.d.ts b/test/integration/test/ports/prisma/functional/issues-27511-include-enum-array/_fixture/generated/contract.d.ts index bef4771ee869..3f3119168e4f 100644 --- a/test/integration/test/ports/prisma/functional/issues-27511-include-enum-array/_fixture/generated/contract.d.ts +++ b/test/integration/test/ports/prisma/functional/issues-27511-include-enum-array/_fixture/generated/contract.d.ts @@ -42,6 +42,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; @@ -91,6 +95,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; @@ -100,6 +108,14 @@ export type AggregateTypes = { readonly output: 'pg/time-temporal@1'; readonly nullable: true; }; + readonly 'pg/timestamp-date@1': { + readonly output: 'pg/timestamp-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-instant@1': { + readonly output: 'pg/timestamp-instant@1'; + readonly nullable: true; + }; readonly 'pg/timestamp-string@1': { readonly output: 'pg/timestamp-string@1'; readonly nullable: true; @@ -151,6 +167,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; @@ -160,6 +180,14 @@ export type AggregateTypes = { readonly output: 'pg/time-temporal@1'; readonly nullable: true; }; + readonly 'pg/timestamp-date@1': { + readonly output: 'pg/timestamp-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-instant@1': { + readonly output: 'pg/timestamp-instant@1'; + readonly nullable: true; + }; readonly 'pg/timestamp-string@1': { readonly output: 'pg/timestamp-string@1'; readonly nullable: true; @@ -203,6 +231,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; diff --git a/test/integration/test/ports/prisma/functional/issues-28151-broken-nested-set/_fixture/generated/contract.d.ts b/test/integration/test/ports/prisma/functional/issues-28151-broken-nested-set/_fixture/generated/contract.d.ts index 8461b89b101d..0a61ca91bb45 100644 --- a/test/integration/test/ports/prisma/functional/issues-28151-broken-nested-set/_fixture/generated/contract.d.ts +++ b/test/integration/test/ports/prisma/functional/issues-28151-broken-nested-set/_fixture/generated/contract.d.ts @@ -43,6 +43,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; @@ -92,6 +96,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; @@ -101,6 +109,14 @@ export type AggregateTypes = { readonly output: 'pg/time-temporal@1'; readonly nullable: true; }; + readonly 'pg/timestamp-date@1': { + readonly output: 'pg/timestamp-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-instant@1': { + readonly output: 'pg/timestamp-instant@1'; + readonly nullable: true; + }; readonly 'pg/timestamp-string@1': { readonly output: 'pg/timestamp-string@1'; readonly nullable: true; @@ -152,6 +168,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; @@ -161,6 +181,14 @@ export type AggregateTypes = { readonly output: 'pg/time-temporal@1'; readonly nullable: true; }; + readonly 'pg/timestamp-date@1': { + readonly output: 'pg/timestamp-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-instant@1': { + readonly output: 'pg/timestamp-instant@1'; + readonly nullable: true; + }; readonly 'pg/timestamp-string@1': { readonly output: 'pg/timestamp-string@1'; readonly nullable: true; @@ -204,6 +232,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; diff --git a/test/integration/test/ports/prisma/functional/issues-28192-pg-historical-dates/_fixture/contract.prisma b/test/integration/test/ports/prisma/functional/issues-28192-pg-historical-dates/_fixture/contract.prisma index bda3634ae82c..05c765d1e5d8 100644 --- a/test/integration/test/ports/prisma/functional/issues-28192-pg-historical-dates/_fixture/contract.prisma +++ b/test/integration/test/ports/prisma/functional/issues-28192-pg-historical-dates/_fixture/contract.prisma @@ -19,7 +19,7 @@ model TestData { id String @id @onCreate(cuid(2)) date Date - timestamp Timestamp - timestamptz Timestamptz + timestamp TimestampTemporal + timestamptz TimestamptzTemporal @@map("testData") } diff --git a/test/integration/test/ports/prisma/functional/issues-28192-pg-historical-dates/_fixture/generated/contract.d.ts b/test/integration/test/ports/prisma/functional/issues-28192-pg-historical-dates/_fixture/generated/contract.d.ts index e03e2576bda1..a24e89d7cf7a 100644 --- a/test/integration/test/ports/prisma/functional/issues-28192-pg-historical-dates/_fixture/generated/contract.d.ts +++ b/test/integration/test/ports/prisma/functional/issues-28192-pg-historical-dates/_fixture/generated/contract.d.ts @@ -43,6 +43,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; @@ -92,6 +96,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; @@ -101,6 +109,14 @@ export type AggregateTypes = { readonly output: 'pg/time-temporal@1'; readonly nullable: true; }; + readonly 'pg/timestamp-date@1': { + readonly output: 'pg/timestamp-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-instant@1': { + readonly output: 'pg/timestamp-instant@1'; + readonly nullable: true; + }; readonly 'pg/timestamp-string@1': { readonly output: 'pg/timestamp-string@1'; readonly nullable: true; @@ -152,6 +168,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; @@ -161,6 +181,14 @@ export type AggregateTypes = { readonly output: 'pg/time-temporal@1'; readonly nullable: true; }; + readonly 'pg/timestamp-date@1': { + readonly output: 'pg/timestamp-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-instant@1': { + readonly output: 'pg/timestamp-instant@1'; + readonly nullable: true; + }; readonly 'pg/timestamp-string@1': { readonly output: 'pg/timestamp-string@1'; readonly nullable: true; @@ -204,6 +232,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; diff --git a/test/integration/test/ports/prisma/functional/issues-28591-mapped-enums/_fixture/generated/contract.d.ts b/test/integration/test/ports/prisma/functional/issues-28591-mapped-enums/_fixture/generated/contract.d.ts index 6bb3ef95362c..27636c708a42 100644 --- a/test/integration/test/ports/prisma/functional/issues-28591-mapped-enums/_fixture/generated/contract.d.ts +++ b/test/integration/test/ports/prisma/functional/issues-28591-mapped-enums/_fixture/generated/contract.d.ts @@ -42,6 +42,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; @@ -91,6 +95,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; @@ -100,6 +108,14 @@ export type AggregateTypes = { readonly output: 'pg/time-temporal@1'; readonly nullable: true; }; + readonly 'pg/timestamp-date@1': { + readonly output: 'pg/timestamp-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-instant@1': { + readonly output: 'pg/timestamp-instant@1'; + readonly nullable: true; + }; readonly 'pg/timestamp-string@1': { readonly output: 'pg/timestamp-string@1'; readonly nullable: true; @@ -151,6 +167,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; @@ -160,6 +180,14 @@ export type AggregateTypes = { readonly output: 'pg/time-temporal@1'; readonly nullable: true; }; + readonly 'pg/timestamp-date@1': { + readonly output: 'pg/timestamp-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-instant@1': { + readonly output: 'pg/timestamp-instant@1'; + readonly nullable: true; + }; readonly 'pg/timestamp-string@1': { readonly output: 'pg/timestamp-string@1'; readonly nullable: true; @@ -203,6 +231,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; diff --git a/test/integration/test/ports/prisma/functional/issues-29010-bigint-precision-relation-joins/_fixture/generated/contract.d.ts b/test/integration/test/ports/prisma/functional/issues-29010-bigint-precision-relation-joins/_fixture/generated/contract.d.ts index 9b8b0da55d2a..ffaf088a463d 100644 --- a/test/integration/test/ports/prisma/functional/issues-29010-bigint-precision-relation-joins/_fixture/generated/contract.d.ts +++ b/test/integration/test/ports/prisma/functional/issues-29010-bigint-precision-relation-joins/_fixture/generated/contract.d.ts @@ -42,6 +42,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; @@ -91,6 +95,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; @@ -100,6 +108,14 @@ export type AggregateTypes = { readonly output: 'pg/time-temporal@1'; readonly nullable: true; }; + readonly 'pg/timestamp-date@1': { + readonly output: 'pg/timestamp-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-instant@1': { + readonly output: 'pg/timestamp-instant@1'; + readonly nullable: true; + }; readonly 'pg/timestamp-string@1': { readonly output: 'pg/timestamp-string@1'; readonly nullable: true; @@ -151,6 +167,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; @@ -160,6 +180,14 @@ export type AggregateTypes = { readonly output: 'pg/time-temporal@1'; readonly nullable: true; }; + readonly 'pg/timestamp-date@1': { + readonly output: 'pg/timestamp-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-instant@1': { + readonly output: 'pg/timestamp-instant@1'; + readonly nullable: true; + }; readonly 'pg/timestamp-string@1': { readonly output: 'pg/timestamp-string@1'; readonly nullable: true; @@ -203,6 +231,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; diff --git a/test/integration/test/ports/prisma/functional/issues-29174-jsonb-parameter-regression/_fixture/generated/contract.d.ts b/test/integration/test/ports/prisma/functional/issues-29174-jsonb-parameter-regression/_fixture/generated/contract.d.ts index 604b7d5230d5..a59c62e5a743 100644 --- a/test/integration/test/ports/prisma/functional/issues-29174-jsonb-parameter-regression/_fixture/generated/contract.d.ts +++ b/test/integration/test/ports/prisma/functional/issues-29174-jsonb-parameter-regression/_fixture/generated/contract.d.ts @@ -43,6 +43,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; @@ -92,6 +96,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; @@ -101,6 +109,14 @@ export type AggregateTypes = { readonly output: 'pg/time-temporal@1'; readonly nullable: true; }; + readonly 'pg/timestamp-date@1': { + readonly output: 'pg/timestamp-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-instant@1': { + readonly output: 'pg/timestamp-instant@1'; + readonly nullable: true; + }; readonly 'pg/timestamp-string@1': { readonly output: 'pg/timestamp-string@1'; readonly nullable: true; @@ -152,6 +168,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; @@ -161,6 +181,14 @@ export type AggregateTypes = { readonly output: 'pg/time-temporal@1'; readonly nullable: true; }; + readonly 'pg/timestamp-date@1': { + readonly output: 'pg/timestamp-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-instant@1': { + readonly output: 'pg/timestamp-instant@1'; + readonly nullable: true; + }; readonly 'pg/timestamp-string@1': { readonly output: 'pg/timestamp-string@1'; readonly nullable: true; @@ -204,6 +232,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; diff --git a/test/integration/test/ports/prisma/functional/issues-29176-cursor-parameter-regression/_fixture/generated/contract.d.ts b/test/integration/test/ports/prisma/functional/issues-29176-cursor-parameter-regression/_fixture/generated/contract.d.ts index deaf3380d778..803bdb98459f 100644 --- a/test/integration/test/ports/prisma/functional/issues-29176-cursor-parameter-regression/_fixture/generated/contract.d.ts +++ b/test/integration/test/ports/prisma/functional/issues-29176-cursor-parameter-regression/_fixture/generated/contract.d.ts @@ -43,6 +43,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; @@ -92,6 +96,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; @@ -101,6 +109,14 @@ export type AggregateTypes = { readonly output: 'pg/time-temporal@1'; readonly nullable: true; }; + readonly 'pg/timestamp-date@1': { + readonly output: 'pg/timestamp-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-instant@1': { + readonly output: 'pg/timestamp-instant@1'; + readonly nullable: true; + }; readonly 'pg/timestamp-string@1': { readonly output: 'pg/timestamp-string@1'; readonly nullable: true; @@ -152,6 +168,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; @@ -161,6 +181,14 @@ export type AggregateTypes = { readonly output: 'pg/time-temporal@1'; readonly nullable: true; }; + readonly 'pg/timestamp-date@1': { + readonly output: 'pg/timestamp-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-instant@1': { + readonly output: 'pg/timestamp-instant@1'; + readonly nullable: true; + }; readonly 'pg/timestamp-string@1': { readonly output: 'pg/timestamp-string@1'; readonly nullable: true; @@ -204,6 +232,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; diff --git a/test/integration/test/ports/prisma/functional/issues-29254-query-plan-cache-mutation/_fixture/generated/contract.d.ts b/test/integration/test/ports/prisma/functional/issues-29254-query-plan-cache-mutation/_fixture/generated/contract.d.ts index 6f5957de50a0..0324e153c7d3 100644 --- a/test/integration/test/ports/prisma/functional/issues-29254-query-plan-cache-mutation/_fixture/generated/contract.d.ts +++ b/test/integration/test/ports/prisma/functional/issues-29254-query-plan-cache-mutation/_fixture/generated/contract.d.ts @@ -43,6 +43,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; @@ -92,6 +96,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; @@ -101,6 +109,14 @@ export type AggregateTypes = { readonly output: 'pg/time-temporal@1'; readonly nullable: true; }; + readonly 'pg/timestamp-date@1': { + readonly output: 'pg/timestamp-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-instant@1': { + readonly output: 'pg/timestamp-instant@1'; + readonly nullable: true; + }; readonly 'pg/timestamp-string@1': { readonly output: 'pg/timestamp-string@1'; readonly nullable: true; @@ -152,6 +168,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; @@ -161,6 +181,14 @@ export type AggregateTypes = { readonly output: 'pg/time-temporal@1'; readonly nullable: true; }; + readonly 'pg/timestamp-date@1': { + readonly output: 'pg/timestamp-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-instant@1': { + readonly output: 'pg/timestamp-instant@1'; + readonly nullable: true; + }; readonly 'pg/timestamp-string@1': { readonly output: 'pg/timestamp-string@1'; readonly nullable: true; @@ -204,6 +232,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; diff --git a/test/integration/test/ports/prisma/functional/issues-29267-uint8array-in-json/_fixture/generated/contract.d.ts b/test/integration/test/ports/prisma/functional/issues-29267-uint8array-in-json/_fixture/generated/contract.d.ts index 6fbfd0302bfd..3e501b676faa 100644 --- a/test/integration/test/ports/prisma/functional/issues-29267-uint8array-in-json/_fixture/generated/contract.d.ts +++ b/test/integration/test/ports/prisma/functional/issues-29267-uint8array-in-json/_fixture/generated/contract.d.ts @@ -43,6 +43,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; @@ -92,6 +96,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; @@ -101,6 +109,14 @@ export type AggregateTypes = { readonly output: 'pg/time-temporal@1'; readonly nullable: true; }; + readonly 'pg/timestamp-date@1': { + readonly output: 'pg/timestamp-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-instant@1': { + readonly output: 'pg/timestamp-instant@1'; + readonly nullable: true; + }; readonly 'pg/timestamp-string@1': { readonly output: 'pg/timestamp-string@1'; readonly nullable: true; @@ -152,6 +168,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; @@ -161,6 +181,14 @@ export type AggregateTypes = { readonly output: 'pg/time-temporal@1'; readonly nullable: true; }; + readonly 'pg/timestamp-date@1': { + readonly output: 'pg/timestamp-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-instant@1': { + readonly output: 'pg/timestamp-instant@1'; + readonly nullable: true; + }; readonly 'pg/timestamp-string@1': { readonly output: 'pg/timestamp-string@1'; readonly nullable: true; @@ -204,6 +232,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; diff --git a/test/integration/test/ports/prisma/functional/issues-29309-datetime-cursor/_fixture/generated/contract.d.ts b/test/integration/test/ports/prisma/functional/issues-29309-datetime-cursor/_fixture/generated/contract.d.ts index 6930fcdd7f47..0ded7d823444 100644 --- a/test/integration/test/ports/prisma/functional/issues-29309-datetime-cursor/_fixture/generated/contract.d.ts +++ b/test/integration/test/ports/prisma/functional/issues-29309-datetime-cursor/_fixture/generated/contract.d.ts @@ -42,6 +42,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; @@ -91,6 +95,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; @@ -100,6 +108,14 @@ export type AggregateTypes = { readonly output: 'pg/time-temporal@1'; readonly nullable: true; }; + readonly 'pg/timestamp-date@1': { + readonly output: 'pg/timestamp-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-instant@1': { + readonly output: 'pg/timestamp-instant@1'; + readonly nullable: true; + }; readonly 'pg/timestamp-string@1': { readonly output: 'pg/timestamp-string@1'; readonly nullable: true; @@ -151,6 +167,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; @@ -160,6 +180,14 @@ export type AggregateTypes = { readonly output: 'pg/time-temporal@1'; readonly nullable: true; }; + readonly 'pg/timestamp-date@1': { + readonly output: 'pg/timestamp-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-instant@1': { + readonly output: 'pg/timestamp-instant@1'; + readonly nullable: true; + }; readonly 'pg/timestamp-string@1': { readonly output: 'pg/timestamp-string@1'; readonly nullable: true; @@ -203,6 +231,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; diff --git a/test/integration/test/ports/prisma/functional/issues-29331-query-plan-cache-bloat/_fixture/generated/contract.d.ts b/test/integration/test/ports/prisma/functional/issues-29331-query-plan-cache-bloat/_fixture/generated/contract.d.ts index 2366985dc1c5..cc4c03475d9b 100644 --- a/test/integration/test/ports/prisma/functional/issues-29331-query-plan-cache-bloat/_fixture/generated/contract.d.ts +++ b/test/integration/test/ports/prisma/functional/issues-29331-query-plan-cache-bloat/_fixture/generated/contract.d.ts @@ -43,6 +43,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; @@ -92,6 +96,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; @@ -101,6 +109,14 @@ export type AggregateTypes = { readonly output: 'pg/time-temporal@1'; readonly nullable: true; }; + readonly 'pg/timestamp-date@1': { + readonly output: 'pg/timestamp-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-instant@1': { + readonly output: 'pg/timestamp-instant@1'; + readonly nullable: true; + }; readonly 'pg/timestamp-string@1': { readonly output: 'pg/timestamp-string@1'; readonly nullable: true; @@ -152,6 +168,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; @@ -161,6 +181,14 @@ export type AggregateTypes = { readonly output: 'pg/time-temporal@1'; readonly nullable: true; }; + readonly 'pg/timestamp-date@1': { + readonly output: 'pg/timestamp-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-instant@1': { + readonly output: 'pg/timestamp-instant@1'; + readonly nullable: true; + }; readonly 'pg/timestamp-string@1': { readonly output: 'pg/timestamp-string@1'; readonly nullable: true; @@ -204,6 +232,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; diff --git a/test/integration/test/ports/prisma/functional/issues-4004/_fixture/generated/contract.d.ts b/test/integration/test/ports/prisma/functional/issues-4004/_fixture/generated/contract.d.ts index 44d3afa049b7..0caeb9f277d3 100644 --- a/test/integration/test/ports/prisma/functional/issues-4004/_fixture/generated/contract.d.ts +++ b/test/integration/test/ports/prisma/functional/issues-4004/_fixture/generated/contract.d.ts @@ -43,6 +43,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; @@ -92,6 +96,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; @@ -101,6 +109,14 @@ export type AggregateTypes = { readonly output: 'pg/time-temporal@1'; readonly nullable: true; }; + readonly 'pg/timestamp-date@1': { + readonly output: 'pg/timestamp-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-instant@1': { + readonly output: 'pg/timestamp-instant@1'; + readonly nullable: true; + }; readonly 'pg/timestamp-string@1': { readonly output: 'pg/timestamp-string@1'; readonly nullable: true; @@ -152,6 +168,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; @@ -161,6 +181,14 @@ export type AggregateTypes = { readonly output: 'pg/time-temporal@1'; readonly nullable: true; }; + readonly 'pg/timestamp-date@1': { + readonly output: 'pg/timestamp-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-instant@1': { + readonly output: 'pg/timestamp-instant@1'; + readonly nullable: true; + }; readonly 'pg/timestamp-string@1': { readonly output: 'pg/timestamp-string@1'; readonly nullable: true; @@ -204,6 +232,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; diff --git a/test/integration/test/ports/prisma/functional/issues-5952-decimal-batch/_fixture/generated/contract.d.ts b/test/integration/test/ports/prisma/functional/issues-5952-decimal-batch/_fixture/generated/contract.d.ts index 4809252859be..aeeec55736a7 100644 --- a/test/integration/test/ports/prisma/functional/issues-5952-decimal-batch/_fixture/generated/contract.d.ts +++ b/test/integration/test/ports/prisma/functional/issues-5952-decimal-batch/_fixture/generated/contract.d.ts @@ -43,6 +43,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; @@ -92,6 +96,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; @@ -101,6 +109,14 @@ export type AggregateTypes = { readonly output: 'pg/time-temporal@1'; readonly nullable: true; }; + readonly 'pg/timestamp-date@1': { + readonly output: 'pg/timestamp-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-instant@1': { + readonly output: 'pg/timestamp-instant@1'; + readonly nullable: true; + }; readonly 'pg/timestamp-string@1': { readonly output: 'pg/timestamp-string@1'; readonly nullable: true; @@ -152,6 +168,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; @@ -161,6 +181,14 @@ export type AggregateTypes = { readonly output: 'pg/time-temporal@1'; readonly nullable: true; }; + readonly 'pg/timestamp-date@1': { + readonly output: 'pg/timestamp-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-instant@1': { + readonly output: 'pg/timestamp-instant@1'; + readonly nullable: true; + }; readonly 'pg/timestamp-string@1': { readonly output: 'pg/timestamp-string@1'; readonly nullable: true; @@ -204,6 +232,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; diff --git a/test/integration/test/ports/prisma/functional/issues-TML-1664-invalid-enum-value-error/_fixture/generated/contract.d.ts b/test/integration/test/ports/prisma/functional/issues-TML-1664-invalid-enum-value-error/_fixture/generated/contract.d.ts index bd59d3e2460a..c970ea08f99c 100644 --- a/test/integration/test/ports/prisma/functional/issues-TML-1664-invalid-enum-value-error/_fixture/generated/contract.d.ts +++ b/test/integration/test/ports/prisma/functional/issues-TML-1664-invalid-enum-value-error/_fixture/generated/contract.d.ts @@ -42,6 +42,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; @@ -91,6 +95,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; @@ -100,6 +108,14 @@ export type AggregateTypes = { readonly output: 'pg/time-temporal@1'; readonly nullable: true; }; + readonly 'pg/timestamp-date@1': { + readonly output: 'pg/timestamp-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-instant@1': { + readonly output: 'pg/timestamp-instant@1'; + readonly nullable: true; + }; readonly 'pg/timestamp-string@1': { readonly output: 'pg/timestamp-string@1'; readonly nullable: true; @@ -151,6 +167,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; @@ -160,6 +180,14 @@ export type AggregateTypes = { readonly output: 'pg/time-temporal@1'; readonly nullable: true; }; + readonly 'pg/timestamp-date@1': { + readonly output: 'pg/timestamp-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-instant@1': { + readonly output: 'pg/timestamp-instant@1'; + readonly nullable: true; + }; readonly 'pg/timestamp-string@1': { readonly output: 'pg/timestamp-string@1'; readonly nullable: true; @@ -203,6 +231,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; diff --git a/test/integration/test/ports/prisma/functional/issues-TML-1664-unknown-enum-value-read-error/_fixture/generated/contract.d.ts b/test/integration/test/ports/prisma/functional/issues-TML-1664-unknown-enum-value-read-error/_fixture/generated/contract.d.ts index 2997b4fb9517..e7b6774e6178 100644 --- a/test/integration/test/ports/prisma/functional/issues-TML-1664-unknown-enum-value-read-error/_fixture/generated/contract.d.ts +++ b/test/integration/test/ports/prisma/functional/issues-TML-1664-unknown-enum-value-read-error/_fixture/generated/contract.d.ts @@ -42,6 +42,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; @@ -91,6 +95,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; @@ -100,6 +108,14 @@ export type AggregateTypes = { readonly output: 'pg/time-temporal@1'; readonly nullable: true; }; + readonly 'pg/timestamp-date@1': { + readonly output: 'pg/timestamp-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-instant@1': { + readonly output: 'pg/timestamp-instant@1'; + readonly nullable: true; + }; readonly 'pg/timestamp-string@1': { readonly output: 'pg/timestamp-string@1'; readonly nullable: true; @@ -151,6 +167,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; @@ -160,6 +180,14 @@ export type AggregateTypes = { readonly output: 'pg/time-temporal@1'; readonly nullable: true; }; + readonly 'pg/timestamp-date@1': { + readonly output: 'pg/timestamp-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-instant@1': { + readonly output: 'pg/timestamp-instant@1'; + readonly nullable: true; + }; readonly 'pg/timestamp-string@1': { readonly output: 'pg/timestamp-string@1'; readonly nullable: true; @@ -203,6 +231,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; diff --git a/test/integration/test/ports/prisma/functional/issues-unmapped-driver-error-user-facing/_fixture/generated/contract.d.ts b/test/integration/test/ports/prisma/functional/issues-unmapped-driver-error-user-facing/_fixture/generated/contract.d.ts index ec2866dccc62..e4a6e00150ba 100644 --- a/test/integration/test/ports/prisma/functional/issues-unmapped-driver-error-user-facing/_fixture/generated/contract.d.ts +++ b/test/integration/test/ports/prisma/functional/issues-unmapped-driver-error-user-facing/_fixture/generated/contract.d.ts @@ -42,6 +42,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; @@ -91,6 +95,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; @@ -100,6 +108,14 @@ export type AggregateTypes = { readonly output: 'pg/time-temporal@1'; readonly nullable: true; }; + readonly 'pg/timestamp-date@1': { + readonly output: 'pg/timestamp-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-instant@1': { + readonly output: 'pg/timestamp-instant@1'; + readonly nullable: true; + }; readonly 'pg/timestamp-string@1': { readonly output: 'pg/timestamp-string@1'; readonly nullable: true; @@ -151,6 +167,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; @@ -160,6 +180,14 @@ export type AggregateTypes = { readonly output: 'pg/time-temporal@1'; readonly nullable: true; }; + readonly 'pg/timestamp-date@1': { + readonly output: 'pg/timestamp-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-instant@1': { + readonly output: 'pg/timestamp-instant@1'; + readonly nullable: true; + }; readonly 'pg/timestamp-string@1': { readonly output: 'pg/timestamp-string@1'; readonly nullable: true; @@ -203,6 +231,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; diff --git a/test/integration/test/ports/prisma/functional/json-fields/_fixture/generated/contract.d.ts b/test/integration/test/ports/prisma/functional/json-fields/_fixture/generated/contract.d.ts index e3ec8dcf5995..a47ae2c73a53 100644 --- a/test/integration/test/ports/prisma/functional/json-fields/_fixture/generated/contract.d.ts +++ b/test/integration/test/ports/prisma/functional/json-fields/_fixture/generated/contract.d.ts @@ -42,6 +42,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; @@ -91,6 +95,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; @@ -100,6 +108,14 @@ export type AggregateTypes = { readonly output: 'pg/time-temporal@1'; readonly nullable: true; }; + readonly 'pg/timestamp-date@1': { + readonly output: 'pg/timestamp-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-instant@1': { + readonly output: 'pg/timestamp-instant@1'; + readonly nullable: true; + }; readonly 'pg/timestamp-string@1': { readonly output: 'pg/timestamp-string@1'; readonly nullable: true; @@ -151,6 +167,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; @@ -160,6 +180,14 @@ export type AggregateTypes = { readonly output: 'pg/time-temporal@1'; readonly nullable: true; }; + readonly 'pg/timestamp-date@1': { + readonly output: 'pg/timestamp-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-instant@1': { + readonly output: 'pg/timestamp-instant@1'; + readonly nullable: true; + }; readonly 'pg/timestamp-string@1': { readonly output: 'pg/timestamp-string@1'; readonly nullable: true; @@ -203,6 +231,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; diff --git a/test/integration/test/ports/prisma/functional/large-floats/_fixture/generated/contract.d.ts b/test/integration/test/ports/prisma/functional/large-floats/_fixture/generated/contract.d.ts index 56f66ee21946..a1d64a266ab5 100644 --- a/test/integration/test/ports/prisma/functional/large-floats/_fixture/generated/contract.d.ts +++ b/test/integration/test/ports/prisma/functional/large-floats/_fixture/generated/contract.d.ts @@ -42,6 +42,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; @@ -91,6 +95,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; @@ -100,6 +108,14 @@ export type AggregateTypes = { readonly output: 'pg/time-temporal@1'; readonly nullable: true; }; + readonly 'pg/timestamp-date@1': { + readonly output: 'pg/timestamp-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-instant@1': { + readonly output: 'pg/timestamp-instant@1'; + readonly nullable: true; + }; readonly 'pg/timestamp-string@1': { readonly output: 'pg/timestamp-string@1'; readonly nullable: true; @@ -151,6 +167,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; @@ -160,6 +180,14 @@ export type AggregateTypes = { readonly output: 'pg/time-temporal@1'; readonly nullable: true; }; + readonly 'pg/timestamp-date@1': { + readonly output: 'pg/timestamp-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-instant@1': { + readonly output: 'pg/timestamp-instant@1'; + readonly nullable: true; + }; readonly 'pg/timestamp-string@1': { readonly output: 'pg/timestamp-string@1'; readonly nullable: true; @@ -203,6 +231,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; diff --git a/test/integration/test/ports/prisma/functional/legacy-aggregations/_fixture/generated/contract.d.ts b/test/integration/test/ports/prisma/functional/legacy-aggregations/_fixture/generated/contract.d.ts index a39a62ca4873..d12c84abfeb2 100644 --- a/test/integration/test/ports/prisma/functional/legacy-aggregations/_fixture/generated/contract.d.ts +++ b/test/integration/test/ports/prisma/functional/legacy-aggregations/_fixture/generated/contract.d.ts @@ -43,6 +43,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; @@ -92,6 +96,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; @@ -101,6 +109,14 @@ export type AggregateTypes = { readonly output: 'pg/time-temporal@1'; readonly nullable: true; }; + readonly 'pg/timestamp-date@1': { + readonly output: 'pg/timestamp-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-instant@1': { + readonly output: 'pg/timestamp-instant@1'; + readonly nullable: true; + }; readonly 'pg/timestamp-string@1': { readonly output: 'pg/timestamp-string@1'; readonly nullable: true; @@ -152,6 +168,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; @@ -161,6 +181,14 @@ export type AggregateTypes = { readonly output: 'pg/time-temporal@1'; readonly nullable: true; }; + readonly 'pg/timestamp-date@1': { + readonly output: 'pg/timestamp-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-instant@1': { + readonly output: 'pg/timestamp-instant@1'; + readonly nullable: true; + }; readonly 'pg/timestamp-string@1': { readonly output: 'pg/timestamp-string@1'; readonly nullable: true; @@ -204,6 +232,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; diff --git a/test/integration/test/ports/prisma/functional/legacy-json/_fixture/generated/contract.d.ts b/test/integration/test/ports/prisma/functional/legacy-json/_fixture/generated/contract.d.ts index cfebb938dd67..3af051032f68 100644 --- a/test/integration/test/ports/prisma/functional/legacy-json/_fixture/generated/contract.d.ts +++ b/test/integration/test/ports/prisma/functional/legacy-json/_fixture/generated/contract.d.ts @@ -42,6 +42,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; @@ -91,6 +95,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; @@ -100,6 +108,14 @@ export type AggregateTypes = { readonly output: 'pg/time-temporal@1'; readonly nullable: true; }; + readonly 'pg/timestamp-date@1': { + readonly output: 'pg/timestamp-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-instant@1': { + readonly output: 'pg/timestamp-instant@1'; + readonly nullable: true; + }; readonly 'pg/timestamp-string@1': { readonly output: 'pg/timestamp-string@1'; readonly nullable: true; @@ -151,6 +167,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; @@ -160,6 +180,14 @@ export type AggregateTypes = { readonly output: 'pg/time-temporal@1'; readonly nullable: true; }; + readonly 'pg/timestamp-date@1': { + readonly output: 'pg/timestamp-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-instant@1': { + readonly output: 'pg/timestamp-instant@1'; + readonly nullable: true; + }; readonly 'pg/timestamp-string@1': { readonly output: 'pg/timestamp-string@1'; readonly nullable: true; @@ -203,6 +231,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; diff --git a/test/integration/test/ports/prisma/functional/legacy-optional-relation-filters/_fixture/generated/contract.d.ts b/test/integration/test/ports/prisma/functional/legacy-optional-relation-filters/_fixture/generated/contract.d.ts index e151fbd40ecf..0d9e93acb690 100644 --- a/test/integration/test/ports/prisma/functional/legacy-optional-relation-filters/_fixture/generated/contract.d.ts +++ b/test/integration/test/ports/prisma/functional/legacy-optional-relation-filters/_fixture/generated/contract.d.ts @@ -43,6 +43,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; @@ -92,6 +96,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; @@ -101,6 +109,14 @@ export type AggregateTypes = { readonly output: 'pg/time-temporal@1'; readonly nullable: true; }; + readonly 'pg/timestamp-date@1': { + readonly output: 'pg/timestamp-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-instant@1': { + readonly output: 'pg/timestamp-instant@1'; + readonly nullable: true; + }; readonly 'pg/timestamp-string@1': { readonly output: 'pg/timestamp-string@1'; readonly nullable: true; @@ -152,6 +168,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; @@ -161,6 +181,14 @@ export type AggregateTypes = { readonly output: 'pg/time-temporal@1'; readonly nullable: true; }; + readonly 'pg/timestamp-date@1': { + readonly output: 'pg/timestamp-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-instant@1': { + readonly output: 'pg/timestamp-instant@1'; + readonly nullable: true; + }; readonly 'pg/timestamp-string@1': { readonly output: 'pg/timestamp-string@1'; readonly nullable: true; @@ -204,6 +232,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; diff --git a/test/integration/test/ports/prisma/functional/methods-count/_fixture/generated/contract.d.ts b/test/integration/test/ports/prisma/functional/methods-count/_fixture/generated/contract.d.ts index ce9f177f0828..e0c5667dfb02 100644 --- a/test/integration/test/ports/prisma/functional/methods-count/_fixture/generated/contract.d.ts +++ b/test/integration/test/ports/prisma/functional/methods-count/_fixture/generated/contract.d.ts @@ -43,6 +43,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; @@ -92,6 +96,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; @@ -101,6 +109,14 @@ export type AggregateTypes = { readonly output: 'pg/time-temporal@1'; readonly nullable: true; }; + readonly 'pg/timestamp-date@1': { + readonly output: 'pg/timestamp-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-instant@1': { + readonly output: 'pg/timestamp-instant@1'; + readonly nullable: true; + }; readonly 'pg/timestamp-string@1': { readonly output: 'pg/timestamp-string@1'; readonly nullable: true; @@ -152,6 +168,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; @@ -161,6 +181,14 @@ export type AggregateTypes = { readonly output: 'pg/time-temporal@1'; readonly nullable: true; }; + readonly 'pg/timestamp-date@1': { + readonly output: 'pg/timestamp-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-instant@1': { + readonly output: 'pg/timestamp-instant@1'; + readonly nullable: true; + }; readonly 'pg/timestamp-string@1': { readonly output: 'pg/timestamp-string@1'; readonly nullable: true; @@ -204,6 +232,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; diff --git a/test/integration/test/ports/prisma/functional/methods-createMany/_fixture/generated/contract.d.ts b/test/integration/test/ports/prisma/functional/methods-createMany/_fixture/generated/contract.d.ts index 7957de406432..ddd77f3cec1c 100644 --- a/test/integration/test/ports/prisma/functional/methods-createMany/_fixture/generated/contract.d.ts +++ b/test/integration/test/ports/prisma/functional/methods-createMany/_fixture/generated/contract.d.ts @@ -43,6 +43,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; @@ -92,6 +96,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; @@ -101,6 +109,14 @@ export type AggregateTypes = { readonly output: 'pg/time-temporal@1'; readonly nullable: true; }; + readonly 'pg/timestamp-date@1': { + readonly output: 'pg/timestamp-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-instant@1': { + readonly output: 'pg/timestamp-instant@1'; + readonly nullable: true; + }; readonly 'pg/timestamp-string@1': { readonly output: 'pg/timestamp-string@1'; readonly nullable: true; @@ -152,6 +168,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; @@ -161,6 +181,14 @@ export type AggregateTypes = { readonly output: 'pg/time-temporal@1'; readonly nullable: true; }; + readonly 'pg/timestamp-date@1': { + readonly output: 'pg/timestamp-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-instant@1': { + readonly output: 'pg/timestamp-instant@1'; + readonly nullable: true; + }; readonly 'pg/timestamp-string@1': { readonly output: 'pg/timestamp-string@1'; readonly nullable: true; @@ -204,6 +232,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; diff --git a/test/integration/test/ports/prisma/functional/methods-createManyAndReturn/_fixture/generated/contract.d.ts b/test/integration/test/ports/prisma/functional/methods-createManyAndReturn/_fixture/generated/contract.d.ts index 7957de406432..ddd77f3cec1c 100644 --- a/test/integration/test/ports/prisma/functional/methods-createManyAndReturn/_fixture/generated/contract.d.ts +++ b/test/integration/test/ports/prisma/functional/methods-createManyAndReturn/_fixture/generated/contract.d.ts @@ -43,6 +43,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; @@ -92,6 +96,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; @@ -101,6 +109,14 @@ export type AggregateTypes = { readonly output: 'pg/time-temporal@1'; readonly nullable: true; }; + readonly 'pg/timestamp-date@1': { + readonly output: 'pg/timestamp-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-instant@1': { + readonly output: 'pg/timestamp-instant@1'; + readonly nullable: true; + }; readonly 'pg/timestamp-string@1': { readonly output: 'pg/timestamp-string@1'; readonly nullable: true; @@ -152,6 +168,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; @@ -161,6 +181,14 @@ export type AggregateTypes = { readonly output: 'pg/time-temporal@1'; readonly nullable: true; }; + readonly 'pg/timestamp-date@1': { + readonly output: 'pg/timestamp-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-instant@1': { + readonly output: 'pg/timestamp-instant@1'; + readonly nullable: true; + }; readonly 'pg/timestamp-string@1': { readonly output: 'pg/timestamp-string@1'; readonly nullable: true; @@ -204,6 +232,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; diff --git a/test/integration/test/ports/prisma/functional/methods-findFirstOrThrow/_fixture/generated/contract.d.ts b/test/integration/test/ports/prisma/functional/methods-findFirstOrThrow/_fixture/generated/contract.d.ts index e63c1fa50725..d212ca906fff 100644 --- a/test/integration/test/ports/prisma/functional/methods-findFirstOrThrow/_fixture/generated/contract.d.ts +++ b/test/integration/test/ports/prisma/functional/methods-findFirstOrThrow/_fixture/generated/contract.d.ts @@ -43,6 +43,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; @@ -92,6 +96,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; @@ -101,6 +109,14 @@ export type AggregateTypes = { readonly output: 'pg/time-temporal@1'; readonly nullable: true; }; + readonly 'pg/timestamp-date@1': { + readonly output: 'pg/timestamp-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-instant@1': { + readonly output: 'pg/timestamp-instant@1'; + readonly nullable: true; + }; readonly 'pg/timestamp-string@1': { readonly output: 'pg/timestamp-string@1'; readonly nullable: true; @@ -152,6 +168,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; @@ -161,6 +181,14 @@ export type AggregateTypes = { readonly output: 'pg/time-temporal@1'; readonly nullable: true; }; + readonly 'pg/timestamp-date@1': { + readonly output: 'pg/timestamp-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-instant@1': { + readonly output: 'pg/timestamp-instant@1'; + readonly nullable: true; + }; readonly 'pg/timestamp-string@1': { readonly output: 'pg/timestamp-string@1'; readonly nullable: true; @@ -204,6 +232,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; diff --git a/test/integration/test/ports/prisma/functional/methods-findUniqueOrThrow/_fixture/generated/contract.d.ts b/test/integration/test/ports/prisma/functional/methods-findUniqueOrThrow/_fixture/generated/contract.d.ts index eb999fd63451..664ab8fcdeeb 100644 --- a/test/integration/test/ports/prisma/functional/methods-findUniqueOrThrow/_fixture/generated/contract.d.ts +++ b/test/integration/test/ports/prisma/functional/methods-findUniqueOrThrow/_fixture/generated/contract.d.ts @@ -43,6 +43,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; @@ -92,6 +96,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; @@ -101,6 +109,14 @@ export type AggregateTypes = { readonly output: 'pg/time-temporal@1'; readonly nullable: true; }; + readonly 'pg/timestamp-date@1': { + readonly output: 'pg/timestamp-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-instant@1': { + readonly output: 'pg/timestamp-instant@1'; + readonly nullable: true; + }; readonly 'pg/timestamp-string@1': { readonly output: 'pg/timestamp-string@1'; readonly nullable: true; @@ -152,6 +168,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; @@ -161,6 +181,14 @@ export type AggregateTypes = { readonly output: 'pg/time-temporal@1'; readonly nullable: true; }; + readonly 'pg/timestamp-date@1': { + readonly output: 'pg/timestamp-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-instant@1': { + readonly output: 'pg/timestamp-instant@1'; + readonly nullable: true; + }; readonly 'pg/timestamp-string@1': { readonly output: 'pg/timestamp-string@1'; readonly nullable: true; @@ -204,6 +232,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; diff --git a/test/integration/test/ports/prisma/functional/methods-updateManyAndReturn/_fixture/generated/contract.d.ts b/test/integration/test/ports/prisma/functional/methods-updateManyAndReturn/_fixture/generated/contract.d.ts index 7957de406432..ddd77f3cec1c 100644 --- a/test/integration/test/ports/prisma/functional/methods-updateManyAndReturn/_fixture/generated/contract.d.ts +++ b/test/integration/test/ports/prisma/functional/methods-updateManyAndReturn/_fixture/generated/contract.d.ts @@ -43,6 +43,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; @@ -92,6 +96,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; @@ -101,6 +109,14 @@ export type AggregateTypes = { readonly output: 'pg/time-temporal@1'; readonly nullable: true; }; + readonly 'pg/timestamp-date@1': { + readonly output: 'pg/timestamp-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-instant@1': { + readonly output: 'pg/timestamp-instant@1'; + readonly nullable: true; + }; readonly 'pg/timestamp-string@1': { readonly output: 'pg/timestamp-string@1'; readonly nullable: true; @@ -152,6 +168,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; @@ -161,6 +181,14 @@ export type AggregateTypes = { readonly output: 'pg/time-temporal@1'; readonly nullable: true; }; + readonly 'pg/timestamp-date@1': { + readonly output: 'pg/timestamp-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-instant@1': { + readonly output: 'pg/timestamp-instant@1'; + readonly nullable: true; + }; readonly 'pg/timestamp-string@1': { readonly output: 'pg/timestamp-string@1'; readonly nullable: true; @@ -204,6 +232,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; diff --git a/test/integration/test/ports/prisma/functional/methods-upsert-native-atomic/_fixture/generated/contract.d.ts b/test/integration/test/ports/prisma/functional/methods-upsert-native-atomic/_fixture/generated/contract.d.ts index 728eb727f31b..80593281bafc 100644 --- a/test/integration/test/ports/prisma/functional/methods-upsert-native-atomic/_fixture/generated/contract.d.ts +++ b/test/integration/test/ports/prisma/functional/methods-upsert-native-atomic/_fixture/generated/contract.d.ts @@ -43,6 +43,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; @@ -92,6 +96,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; @@ -101,6 +109,14 @@ export type AggregateTypes = { readonly output: 'pg/time-temporal@1'; readonly nullable: true; }; + readonly 'pg/timestamp-date@1': { + readonly output: 'pg/timestamp-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-instant@1': { + readonly output: 'pg/timestamp-instant@1'; + readonly nullable: true; + }; readonly 'pg/timestamp-string@1': { readonly output: 'pg/timestamp-string@1'; readonly nullable: true; @@ -152,6 +168,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; @@ -161,6 +181,14 @@ export type AggregateTypes = { readonly output: 'pg/time-temporal@1'; readonly nullable: true; }; + readonly 'pg/timestamp-date@1': { + readonly output: 'pg/timestamp-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-instant@1': { + readonly output: 'pg/timestamp-instant@1'; + readonly nullable: true; + }; readonly 'pg/timestamp-string@1': { readonly output: 'pg/timestamp-string@1'; readonly nullable: true; @@ -204,6 +232,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; diff --git a/test/integration/test/ports/prisma/functional/methods-upsert-simple/_fixture/generated/contract.d.ts b/test/integration/test/ports/prisma/functional/methods-upsert-simple/_fixture/generated/contract.d.ts index 10df8d913b94..6d5cc4f4b852 100644 --- a/test/integration/test/ports/prisma/functional/methods-upsert-simple/_fixture/generated/contract.d.ts +++ b/test/integration/test/ports/prisma/functional/methods-upsert-simple/_fixture/generated/contract.d.ts @@ -43,6 +43,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; @@ -92,6 +96,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; @@ -101,6 +109,14 @@ export type AggregateTypes = { readonly output: 'pg/time-temporal@1'; readonly nullable: true; }; + readonly 'pg/timestamp-date@1': { + readonly output: 'pg/timestamp-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-instant@1': { + readonly output: 'pg/timestamp-instant@1'; + readonly nullable: true; + }; readonly 'pg/timestamp-string@1': { readonly output: 'pg/timestamp-string@1'; readonly nullable: true; @@ -152,6 +168,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; @@ -161,6 +181,14 @@ export type AggregateTypes = { readonly output: 'pg/time-temporal@1'; readonly nullable: true; }; + readonly 'pg/timestamp-date@1': { + readonly output: 'pg/timestamp-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-instant@1': { + readonly output: 'pg/timestamp-instant@1'; + readonly nullable: true; + }; readonly 'pg/timestamp-string@1': { readonly output: 'pg/timestamp-string@1'; readonly nullable: true; @@ -204,6 +232,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; diff --git a/test/integration/test/ports/prisma/functional/mixed-string-uuid-datetime-list-inputs/_fixture/generated/contract.d.ts b/test/integration/test/ports/prisma/functional/mixed-string-uuid-datetime-list-inputs/_fixture/generated/contract.d.ts index a34339912d4e..26424054f0f8 100644 --- a/test/integration/test/ports/prisma/functional/mixed-string-uuid-datetime-list-inputs/_fixture/generated/contract.d.ts +++ b/test/integration/test/ports/prisma/functional/mixed-string-uuid-datetime-list-inputs/_fixture/generated/contract.d.ts @@ -43,6 +43,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; @@ -92,6 +96,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; @@ -101,6 +109,14 @@ export type AggregateTypes = { readonly output: 'pg/time-temporal@1'; readonly nullable: true; }; + readonly 'pg/timestamp-date@1': { + readonly output: 'pg/timestamp-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-instant@1': { + readonly output: 'pg/timestamp-instant@1'; + readonly nullable: true; + }; readonly 'pg/timestamp-string@1': { readonly output: 'pg/timestamp-string@1'; readonly nullable: true; @@ -152,6 +168,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; @@ -161,6 +181,14 @@ export type AggregateTypes = { readonly output: 'pg/time-temporal@1'; readonly nullable: true; }; + readonly 'pg/timestamp-date@1': { + readonly output: 'pg/timestamp-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-instant@1': { + readonly output: 'pg/timestamp-instant@1'; + readonly nullable: true; + }; readonly 'pg/timestamp-string@1': { readonly output: 'pg/timestamp-string@1'; readonly nullable: true; @@ -204,6 +232,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; diff --git a/test/integration/test/ports/prisma/functional/multi-schema/_fixture/different-names/generated/contract.d.ts b/test/integration/test/ports/prisma/functional/multi-schema/_fixture/different-names/generated/contract.d.ts index a2ba82ad58c5..3b33f9ee76b7 100644 --- a/test/integration/test/ports/prisma/functional/multi-schema/_fixture/different-names/generated/contract.d.ts +++ b/test/integration/test/ports/prisma/functional/multi-schema/_fixture/different-names/generated/contract.d.ts @@ -43,6 +43,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; @@ -92,6 +96,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; @@ -101,6 +109,14 @@ export type AggregateTypes = { readonly output: 'pg/time-temporal@1'; readonly nullable: true; }; + readonly 'pg/timestamp-date@1': { + readonly output: 'pg/timestamp-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-instant@1': { + readonly output: 'pg/timestamp-instant@1'; + readonly nullable: true; + }; readonly 'pg/timestamp-string@1': { readonly output: 'pg/timestamp-string@1'; readonly nullable: true; @@ -152,6 +168,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; @@ -161,6 +181,14 @@ export type AggregateTypes = { readonly output: 'pg/time-temporal@1'; readonly nullable: true; }; + readonly 'pg/timestamp-date@1': { + readonly output: 'pg/timestamp-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-instant@1': { + readonly output: 'pg/timestamp-instant@1'; + readonly nullable: true; + }; readonly 'pg/timestamp-string@1': { readonly output: 'pg/timestamp-string@1'; readonly nullable: true; @@ -204,6 +232,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; diff --git a/test/integration/test/ports/prisma/functional/multi-schema/_fixture/identical-names/generated/contract.d.ts b/test/integration/test/ports/prisma/functional/multi-schema/_fixture/identical-names/generated/contract.d.ts index 73c3316d7dbe..3f09775ce37b 100644 --- a/test/integration/test/ports/prisma/functional/multi-schema/_fixture/identical-names/generated/contract.d.ts +++ b/test/integration/test/ports/prisma/functional/multi-schema/_fixture/identical-names/generated/contract.d.ts @@ -43,6 +43,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; @@ -92,6 +96,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; @@ -101,6 +109,14 @@ export type AggregateTypes = { readonly output: 'pg/time-temporal@1'; readonly nullable: true; }; + readonly 'pg/timestamp-date@1': { + readonly output: 'pg/timestamp-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-instant@1': { + readonly output: 'pg/timestamp-instant@1'; + readonly nullable: true; + }; readonly 'pg/timestamp-string@1': { readonly output: 'pg/timestamp-string@1'; readonly nullable: true; @@ -152,6 +168,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; @@ -161,6 +181,14 @@ export type AggregateTypes = { readonly output: 'pg/time-temporal@1'; readonly nullable: true; }; + readonly 'pg/timestamp-date@1': { + readonly output: 'pg/timestamp-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-instant@1': { + readonly output: 'pg/timestamp-instant@1'; + readonly nullable: true; + }; readonly 'pg/timestamp-string@1': { readonly output: 'pg/timestamp-string@1'; readonly nullable: true; @@ -204,6 +232,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; diff --git a/test/integration/test/ports/prisma/functional/multi-schema/_fixture/no-map/generated/contract.d.ts b/test/integration/test/ports/prisma/functional/multi-schema/_fixture/no-map/generated/contract.d.ts index c2051d019797..01f327375997 100644 --- a/test/integration/test/ports/prisma/functional/multi-schema/_fixture/no-map/generated/contract.d.ts +++ b/test/integration/test/ports/prisma/functional/multi-schema/_fixture/no-map/generated/contract.d.ts @@ -43,6 +43,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; @@ -92,6 +96,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; @@ -101,6 +109,14 @@ export type AggregateTypes = { readonly output: 'pg/time-temporal@1'; readonly nullable: true; }; + readonly 'pg/timestamp-date@1': { + readonly output: 'pg/timestamp-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-instant@1': { + readonly output: 'pg/timestamp-instant@1'; + readonly nullable: true; + }; readonly 'pg/timestamp-string@1': { readonly output: 'pg/timestamp-string@1'; readonly nullable: true; @@ -152,6 +168,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; @@ -161,6 +181,14 @@ export type AggregateTypes = { readonly output: 'pg/time-temporal@1'; readonly nullable: true; }; + readonly 'pg/timestamp-date@1': { + readonly output: 'pg/timestamp-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-instant@1': { + readonly output: 'pg/timestamp-instant@1'; + readonly nullable: true; + }; readonly 'pg/timestamp-string@1': { readonly output: 'pg/timestamp-string@1'; readonly nullable: true; @@ -204,6 +232,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; diff --git a/test/integration/test/ports/prisma/functional/multiple-types/_fixture/generated/contract.d.ts b/test/integration/test/ports/prisma/functional/multiple-types/_fixture/generated/contract.d.ts index 6607bbc7e2ec..c61eb1355df1 100644 --- a/test/integration/test/ports/prisma/functional/multiple-types/_fixture/generated/contract.d.ts +++ b/test/integration/test/ports/prisma/functional/multiple-types/_fixture/generated/contract.d.ts @@ -43,6 +43,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; @@ -92,6 +96,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; @@ -101,6 +109,14 @@ export type AggregateTypes = { readonly output: 'pg/time-temporal@1'; readonly nullable: true; }; + readonly 'pg/timestamp-date@1': { + readonly output: 'pg/timestamp-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-instant@1': { + readonly output: 'pg/timestamp-instant@1'; + readonly nullable: true; + }; readonly 'pg/timestamp-string@1': { readonly output: 'pg/timestamp-string@1'; readonly nullable: true; @@ -152,6 +168,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; @@ -161,6 +181,14 @@ export type AggregateTypes = { readonly output: 'pg/time-temporal@1'; readonly nullable: true; }; + readonly 'pg/timestamp-date@1': { + readonly output: 'pg/timestamp-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-instant@1': { + readonly output: 'pg/timestamp-instant@1'; + readonly nullable: true; + }; readonly 'pg/timestamp-string@1': { readonly output: 'pg/timestamp-string@1'; readonly nullable: true; @@ -204,6 +232,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; diff --git a/test/integration/test/ports/prisma/functional/optimistic-concurrency-control/_fixture/generated/contract.d.ts b/test/integration/test/ports/prisma/functional/optimistic-concurrency-control/_fixture/generated/contract.d.ts index c2ac9b36d543..872dbe4b6486 100644 --- a/test/integration/test/ports/prisma/functional/optimistic-concurrency-control/_fixture/generated/contract.d.ts +++ b/test/integration/test/ports/prisma/functional/optimistic-concurrency-control/_fixture/generated/contract.d.ts @@ -43,6 +43,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; @@ -92,6 +96,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; @@ -101,6 +109,14 @@ export type AggregateTypes = { readonly output: 'pg/time-temporal@1'; readonly nullable: true; }; + readonly 'pg/timestamp-date@1': { + readonly output: 'pg/timestamp-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-instant@1': { + readonly output: 'pg/timestamp-instant@1'; + readonly nullable: true; + }; readonly 'pg/timestamp-string@1': { readonly output: 'pg/timestamp-string@1'; readonly nullable: true; @@ -152,6 +168,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; @@ -161,6 +181,14 @@ export type AggregateTypes = { readonly output: 'pg/time-temporal@1'; readonly nullable: true; }; + readonly 'pg/timestamp-date@1': { + readonly output: 'pg/timestamp-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-instant@1': { + readonly output: 'pg/timestamp-instant@1'; + readonly nullable: true; + }; readonly 'pg/timestamp-string@1': { readonly output: 'pg/timestamp-string@1'; readonly nullable: true; @@ -204,6 +232,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; diff --git a/test/integration/test/ports/prisma/functional/referential-actions-set-default-1to1/_fixture/generated/contract.d.ts b/test/integration/test/ports/prisma/functional/referential-actions-set-default-1to1/_fixture/generated/contract.d.ts index b8d42779829b..76b7076e2082 100644 --- a/test/integration/test/ports/prisma/functional/referential-actions-set-default-1to1/_fixture/generated/contract.d.ts +++ b/test/integration/test/ports/prisma/functional/referential-actions-set-default-1to1/_fixture/generated/contract.d.ts @@ -42,6 +42,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; @@ -91,6 +95,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; @@ -100,6 +108,14 @@ export type AggregateTypes = { readonly output: 'pg/time-temporal@1'; readonly nullable: true; }; + readonly 'pg/timestamp-date@1': { + readonly output: 'pg/timestamp-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-instant@1': { + readonly output: 'pg/timestamp-instant@1'; + readonly nullable: true; + }; readonly 'pg/timestamp-string@1': { readonly output: 'pg/timestamp-string@1'; readonly nullable: true; @@ -151,6 +167,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; @@ -160,6 +180,14 @@ export type AggregateTypes = { readonly output: 'pg/time-temporal@1'; readonly nullable: true; }; + readonly 'pg/timestamp-date@1': { + readonly output: 'pg/timestamp-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-instant@1': { + readonly output: 'pg/timestamp-instant@1'; + readonly nullable: true; + }; readonly 'pg/timestamp-string@1': { readonly output: 'pg/timestamp-string@1'; readonly nullable: true; @@ -203,6 +231,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; diff --git a/test/integration/test/ports/prisma/functional/referential-actions-set-default-1ton/_fixture/generated/contract.d.ts b/test/integration/test/ports/prisma/functional/referential-actions-set-default-1ton/_fixture/generated/contract.d.ts index b8d42779829b..76b7076e2082 100644 --- a/test/integration/test/ports/prisma/functional/referential-actions-set-default-1ton/_fixture/generated/contract.d.ts +++ b/test/integration/test/ports/prisma/functional/referential-actions-set-default-1ton/_fixture/generated/contract.d.ts @@ -42,6 +42,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; @@ -91,6 +95,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; @@ -100,6 +108,14 @@ export type AggregateTypes = { readonly output: 'pg/time-temporal@1'; readonly nullable: true; }; + readonly 'pg/timestamp-date@1': { + readonly output: 'pg/timestamp-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-instant@1': { + readonly output: 'pg/timestamp-instant@1'; + readonly nullable: true; + }; readonly 'pg/timestamp-string@1': { readonly output: 'pg/timestamp-string@1'; readonly nullable: true; @@ -151,6 +167,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; @@ -160,6 +180,14 @@ export type AggregateTypes = { readonly output: 'pg/time-temporal@1'; readonly nullable: true; }; + readonly 'pg/timestamp-date@1': { + readonly output: 'pg/timestamp-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-instant@1': { + readonly output: 'pg/timestamp-instant@1'; + readonly nullable: true; + }; readonly 'pg/timestamp-string@1': { readonly output: 'pg/timestamp-string@1'; readonly nullable: true; @@ -203,6 +231,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; diff --git a/test/integration/test/ports/prisma/functional/relation-mode-17255-mixed-actions/_fixture/generated/contract.d.ts b/test/integration/test/ports/prisma/functional/relation-mode-17255-mixed-actions/_fixture/generated/contract.d.ts index 738396e1fd8c..fa52219d1e56 100644 --- a/test/integration/test/ports/prisma/functional/relation-mode-17255-mixed-actions/_fixture/generated/contract.d.ts +++ b/test/integration/test/ports/prisma/functional/relation-mode-17255-mixed-actions/_fixture/generated/contract.d.ts @@ -42,6 +42,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; @@ -91,6 +95,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; @@ -100,6 +108,14 @@ export type AggregateTypes = { readonly output: 'pg/time-temporal@1'; readonly nullable: true; }; + readonly 'pg/timestamp-date@1': { + readonly output: 'pg/timestamp-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-instant@1': { + readonly output: 'pg/timestamp-instant@1'; + readonly nullable: true; + }; readonly 'pg/timestamp-string@1': { readonly output: 'pg/timestamp-string@1'; readonly nullable: true; @@ -151,6 +167,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; @@ -160,6 +180,14 @@ export type AggregateTypes = { readonly output: 'pg/time-temporal@1'; readonly nullable: true; }; + readonly 'pg/timestamp-date@1': { + readonly output: 'pg/timestamp-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-instant@1': { + readonly output: 'pg/timestamp-instant@1'; + readonly nullable: true; + }; readonly 'pg/timestamp-string@1': { readonly output: 'pg/timestamp-string@1'; readonly nullable: true; @@ -203,6 +231,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; diff --git a/test/integration/test/ports/prisma/functional/relation-mode-17255-same-actions/_fixture/generated/contract.d.ts b/test/integration/test/ports/prisma/functional/relation-mode-17255-same-actions/_fixture/generated/contract.d.ts index 0858344c6c1a..531c1685a072 100644 --- a/test/integration/test/ports/prisma/functional/relation-mode-17255-same-actions/_fixture/generated/contract.d.ts +++ b/test/integration/test/ports/prisma/functional/relation-mode-17255-same-actions/_fixture/generated/contract.d.ts @@ -42,6 +42,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; @@ -91,6 +95,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; @@ -100,6 +108,14 @@ export type AggregateTypes = { readonly output: 'pg/time-temporal@1'; readonly nullable: true; }; + readonly 'pg/timestamp-date@1': { + readonly output: 'pg/timestamp-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-instant@1': { + readonly output: 'pg/timestamp-instant@1'; + readonly nullable: true; + }; readonly 'pg/timestamp-string@1': { readonly output: 'pg/timestamp-string@1'; readonly nullable: true; @@ -151,6 +167,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; @@ -160,6 +180,14 @@ export type AggregateTypes = { readonly output: 'pg/time-temporal@1'; readonly nullable: true; }; + readonly 'pg/timestamp-date@1': { + readonly output: 'pg/timestamp-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-instant@1': { + readonly output: 'pg/timestamp-instant@1'; + readonly nullable: true; + }; readonly 'pg/timestamp-string@1': { readonly output: 'pg/timestamp-string@1'; readonly nullable: true; @@ -203,6 +231,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; diff --git a/test/integration/test/ports/prisma/functional/relation-mode-gh-1-to-1/_fixture/cascade-map/generated/contract.d.ts b/test/integration/test/ports/prisma/functional/relation-mode-gh-1-to-1/_fixture/cascade-map/generated/contract.d.ts index 138f4c7f85a6..99c0e3aed834 100644 --- a/test/integration/test/ports/prisma/functional/relation-mode-gh-1-to-1/_fixture/cascade-map/generated/contract.d.ts +++ b/test/integration/test/ports/prisma/functional/relation-mode-gh-1-to-1/_fixture/cascade-map/generated/contract.d.ts @@ -42,6 +42,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; @@ -91,6 +95,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; @@ -100,6 +108,14 @@ export type AggregateTypes = { readonly output: 'pg/time-temporal@1'; readonly nullable: true; }; + readonly 'pg/timestamp-date@1': { + readonly output: 'pg/timestamp-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-instant@1': { + readonly output: 'pg/timestamp-instant@1'; + readonly nullable: true; + }; readonly 'pg/timestamp-string@1': { readonly output: 'pg/timestamp-string@1'; readonly nullable: true; @@ -151,6 +167,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; @@ -160,6 +180,14 @@ export type AggregateTypes = { readonly output: 'pg/time-temporal@1'; readonly nullable: true; }; + readonly 'pg/timestamp-date@1': { + readonly output: 'pg/timestamp-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-instant@1': { + readonly output: 'pg/timestamp-instant@1'; + readonly nullable: true; + }; readonly 'pg/timestamp-string@1': { readonly output: 'pg/timestamp-string@1'; readonly nullable: true; @@ -203,6 +231,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; diff --git a/test/integration/test/ports/prisma/functional/relation-mode-gh-1-to-1/_fixture/cascade-plain/generated/contract.d.ts b/test/integration/test/ports/prisma/functional/relation-mode-gh-1-to-1/_fixture/cascade-plain/generated/contract.d.ts index df73f0165817..a03b3273edef 100644 --- a/test/integration/test/ports/prisma/functional/relation-mode-gh-1-to-1/_fixture/cascade-plain/generated/contract.d.ts +++ b/test/integration/test/ports/prisma/functional/relation-mode-gh-1-to-1/_fixture/cascade-plain/generated/contract.d.ts @@ -42,6 +42,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; @@ -91,6 +95,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; @@ -100,6 +108,14 @@ export type AggregateTypes = { readonly output: 'pg/time-temporal@1'; readonly nullable: true; }; + readonly 'pg/timestamp-date@1': { + readonly output: 'pg/timestamp-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-instant@1': { + readonly output: 'pg/timestamp-instant@1'; + readonly nullable: true; + }; readonly 'pg/timestamp-string@1': { readonly output: 'pg/timestamp-string@1'; readonly nullable: true; @@ -151,6 +167,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; @@ -160,6 +180,14 @@ export type AggregateTypes = { readonly output: 'pg/time-temporal@1'; readonly nullable: true; }; + readonly 'pg/timestamp-date@1': { + readonly output: 'pg/timestamp-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-instant@1': { + readonly output: 'pg/timestamp-instant@1'; + readonly nullable: true; + }; readonly 'pg/timestamp-string@1': { readonly output: 'pg/timestamp-string@1'; readonly nullable: true; @@ -203,6 +231,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; diff --git a/test/integration/test/ports/prisma/functional/relation-mode-gh-1-to-1/_fixture/noaction-map/generated/contract.d.ts b/test/integration/test/ports/prisma/functional/relation-mode-gh-1-to-1/_fixture/noaction-map/generated/contract.d.ts index 75e7514706de..078cf0fc57b3 100644 --- a/test/integration/test/ports/prisma/functional/relation-mode-gh-1-to-1/_fixture/noaction-map/generated/contract.d.ts +++ b/test/integration/test/ports/prisma/functional/relation-mode-gh-1-to-1/_fixture/noaction-map/generated/contract.d.ts @@ -42,6 +42,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; @@ -91,6 +95,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; @@ -100,6 +108,14 @@ export type AggregateTypes = { readonly output: 'pg/time-temporal@1'; readonly nullable: true; }; + readonly 'pg/timestamp-date@1': { + readonly output: 'pg/timestamp-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-instant@1': { + readonly output: 'pg/timestamp-instant@1'; + readonly nullable: true; + }; readonly 'pg/timestamp-string@1': { readonly output: 'pg/timestamp-string@1'; readonly nullable: true; @@ -151,6 +167,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; @@ -160,6 +180,14 @@ export type AggregateTypes = { readonly output: 'pg/time-temporal@1'; readonly nullable: true; }; + readonly 'pg/timestamp-date@1': { + readonly output: 'pg/timestamp-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-instant@1': { + readonly output: 'pg/timestamp-instant@1'; + readonly nullable: true; + }; readonly 'pg/timestamp-string@1': { readonly output: 'pg/timestamp-string@1'; readonly nullable: true; @@ -203,6 +231,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; diff --git a/test/integration/test/ports/prisma/functional/relation-mode-gh-1-to-1/_fixture/noaction-plain/generated/contract.d.ts b/test/integration/test/ports/prisma/functional/relation-mode-gh-1-to-1/_fixture/noaction-plain/generated/contract.d.ts index cbb89d73881b..65c54d1a5e47 100644 --- a/test/integration/test/ports/prisma/functional/relation-mode-gh-1-to-1/_fixture/noaction-plain/generated/contract.d.ts +++ b/test/integration/test/ports/prisma/functional/relation-mode-gh-1-to-1/_fixture/noaction-plain/generated/contract.d.ts @@ -42,6 +42,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; @@ -91,6 +95,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; @@ -100,6 +108,14 @@ export type AggregateTypes = { readonly output: 'pg/time-temporal@1'; readonly nullable: true; }; + readonly 'pg/timestamp-date@1': { + readonly output: 'pg/timestamp-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-instant@1': { + readonly output: 'pg/timestamp-instant@1'; + readonly nullable: true; + }; readonly 'pg/timestamp-string@1': { readonly output: 'pg/timestamp-string@1'; readonly nullable: true; @@ -151,6 +167,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; @@ -160,6 +180,14 @@ export type AggregateTypes = { readonly output: 'pg/time-temporal@1'; readonly nullable: true; }; + readonly 'pg/timestamp-date@1': { + readonly output: 'pg/timestamp-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-instant@1': { + readonly output: 'pg/timestamp-instant@1'; + readonly nullable: true; + }; readonly 'pg/timestamp-string@1': { readonly output: 'pg/timestamp-string@1'; readonly nullable: true; @@ -203,6 +231,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; diff --git a/test/integration/test/ports/prisma/functional/relation-mode-gh-1-to-1/_fixture/restrict-map/generated/contract.d.ts b/test/integration/test/ports/prisma/functional/relation-mode-gh-1-to-1/_fixture/restrict-map/generated/contract.d.ts index ed8629506426..b2faee52f5c5 100644 --- a/test/integration/test/ports/prisma/functional/relation-mode-gh-1-to-1/_fixture/restrict-map/generated/contract.d.ts +++ b/test/integration/test/ports/prisma/functional/relation-mode-gh-1-to-1/_fixture/restrict-map/generated/contract.d.ts @@ -42,6 +42,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; @@ -91,6 +95,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; @@ -100,6 +108,14 @@ export type AggregateTypes = { readonly output: 'pg/time-temporal@1'; readonly nullable: true; }; + readonly 'pg/timestamp-date@1': { + readonly output: 'pg/timestamp-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-instant@1': { + readonly output: 'pg/timestamp-instant@1'; + readonly nullable: true; + }; readonly 'pg/timestamp-string@1': { readonly output: 'pg/timestamp-string@1'; readonly nullable: true; @@ -151,6 +167,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; @@ -160,6 +180,14 @@ export type AggregateTypes = { readonly output: 'pg/time-temporal@1'; readonly nullable: true; }; + readonly 'pg/timestamp-date@1': { + readonly output: 'pg/timestamp-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-instant@1': { + readonly output: 'pg/timestamp-instant@1'; + readonly nullable: true; + }; readonly 'pg/timestamp-string@1': { readonly output: 'pg/timestamp-string@1'; readonly nullable: true; @@ -203,6 +231,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; diff --git a/test/integration/test/ports/prisma/functional/relation-mode-gh-1-to-1/_fixture/restrict-plain/generated/contract.d.ts b/test/integration/test/ports/prisma/functional/relation-mode-gh-1-to-1/_fixture/restrict-plain/generated/contract.d.ts index 5c964cdeb42d..ace4d9dea95e 100644 --- a/test/integration/test/ports/prisma/functional/relation-mode-gh-1-to-1/_fixture/restrict-plain/generated/contract.d.ts +++ b/test/integration/test/ports/prisma/functional/relation-mode-gh-1-to-1/_fixture/restrict-plain/generated/contract.d.ts @@ -42,6 +42,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; @@ -91,6 +95,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; @@ -100,6 +108,14 @@ export type AggregateTypes = { readonly output: 'pg/time-temporal@1'; readonly nullable: true; }; + readonly 'pg/timestamp-date@1': { + readonly output: 'pg/timestamp-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-instant@1': { + readonly output: 'pg/timestamp-instant@1'; + readonly nullable: true; + }; readonly 'pg/timestamp-string@1': { readonly output: 'pg/timestamp-string@1'; readonly nullable: true; @@ -151,6 +167,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; @@ -160,6 +180,14 @@ export type AggregateTypes = { readonly output: 'pg/time-temporal@1'; readonly nullable: true; }; + readonly 'pg/timestamp-date@1': { + readonly output: 'pg/timestamp-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-instant@1': { + readonly output: 'pg/timestamp-instant@1'; + readonly nullable: true; + }; readonly 'pg/timestamp-string@1': { readonly output: 'pg/timestamp-string@1'; readonly nullable: true; @@ -203,6 +231,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; diff --git a/test/integration/test/ports/prisma/functional/relation-mode-gh-1-to-1/_fixture/setnull-map/generated/contract.d.ts b/test/integration/test/ports/prisma/functional/relation-mode-gh-1-to-1/_fixture/setnull-map/generated/contract.d.ts index 83c9487f03db..385624aeaa1e 100644 --- a/test/integration/test/ports/prisma/functional/relation-mode-gh-1-to-1/_fixture/setnull-map/generated/contract.d.ts +++ b/test/integration/test/ports/prisma/functional/relation-mode-gh-1-to-1/_fixture/setnull-map/generated/contract.d.ts @@ -42,6 +42,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; @@ -91,6 +95,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; @@ -100,6 +108,14 @@ export type AggregateTypes = { readonly output: 'pg/time-temporal@1'; readonly nullable: true; }; + readonly 'pg/timestamp-date@1': { + readonly output: 'pg/timestamp-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-instant@1': { + readonly output: 'pg/timestamp-instant@1'; + readonly nullable: true; + }; readonly 'pg/timestamp-string@1': { readonly output: 'pg/timestamp-string@1'; readonly nullable: true; @@ -151,6 +167,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; @@ -160,6 +180,14 @@ export type AggregateTypes = { readonly output: 'pg/time-temporal@1'; readonly nullable: true; }; + readonly 'pg/timestamp-date@1': { + readonly output: 'pg/timestamp-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-instant@1': { + readonly output: 'pg/timestamp-instant@1'; + readonly nullable: true; + }; readonly 'pg/timestamp-string@1': { readonly output: 'pg/timestamp-string@1'; readonly nullable: true; @@ -203,6 +231,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; diff --git a/test/integration/test/ports/prisma/functional/relation-mode-gh-1-to-1/_fixture/setnull-plain/generated/contract.d.ts b/test/integration/test/ports/prisma/functional/relation-mode-gh-1-to-1/_fixture/setnull-plain/generated/contract.d.ts index 6d6ceb4b0560..e76f025db203 100644 --- a/test/integration/test/ports/prisma/functional/relation-mode-gh-1-to-1/_fixture/setnull-plain/generated/contract.d.ts +++ b/test/integration/test/ports/prisma/functional/relation-mode-gh-1-to-1/_fixture/setnull-plain/generated/contract.d.ts @@ -42,6 +42,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; @@ -91,6 +95,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; @@ -100,6 +108,14 @@ export type AggregateTypes = { readonly output: 'pg/time-temporal@1'; readonly nullable: true; }; + readonly 'pg/timestamp-date@1': { + readonly output: 'pg/timestamp-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-instant@1': { + readonly output: 'pg/timestamp-instant@1'; + readonly nullable: true; + }; readonly 'pg/timestamp-string@1': { readonly output: 'pg/timestamp-string@1'; readonly nullable: true; @@ -151,6 +167,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; @@ -160,6 +180,14 @@ export type AggregateTypes = { readonly output: 'pg/time-temporal@1'; readonly nullable: true; }; + readonly 'pg/timestamp-date@1': { + readonly output: 'pg/timestamp-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-instant@1': { + readonly output: 'pg/timestamp-instant@1'; + readonly nullable: true; + }; readonly 'pg/timestamp-string@1': { readonly output: 'pg/timestamp-string@1'; readonly nullable: true; @@ -203,6 +231,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; diff --git a/test/integration/test/ports/prisma/functional/relation-mode-gh-1-to-n/_fixture/cascade-map/generated/contract.d.ts b/test/integration/test/ports/prisma/functional/relation-mode-gh-1-to-n/_fixture/cascade-map/generated/contract.d.ts index 61bcead75c74..9ab36dcedf89 100644 --- a/test/integration/test/ports/prisma/functional/relation-mode-gh-1-to-n/_fixture/cascade-map/generated/contract.d.ts +++ b/test/integration/test/ports/prisma/functional/relation-mode-gh-1-to-n/_fixture/cascade-map/generated/contract.d.ts @@ -42,6 +42,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; @@ -91,6 +95,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; @@ -100,6 +108,14 @@ export type AggregateTypes = { readonly output: 'pg/time-temporal@1'; readonly nullable: true; }; + readonly 'pg/timestamp-date@1': { + readonly output: 'pg/timestamp-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-instant@1': { + readonly output: 'pg/timestamp-instant@1'; + readonly nullable: true; + }; readonly 'pg/timestamp-string@1': { readonly output: 'pg/timestamp-string@1'; readonly nullable: true; @@ -151,6 +167,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; @@ -160,6 +180,14 @@ export type AggregateTypes = { readonly output: 'pg/time-temporal@1'; readonly nullable: true; }; + readonly 'pg/timestamp-date@1': { + readonly output: 'pg/timestamp-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-instant@1': { + readonly output: 'pg/timestamp-instant@1'; + readonly nullable: true; + }; readonly 'pg/timestamp-string@1': { readonly output: 'pg/timestamp-string@1'; readonly nullable: true; @@ -203,6 +231,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; diff --git a/test/integration/test/ports/prisma/functional/relation-mode-gh-1-to-n/_fixture/cascade-plain/generated/contract.d.ts b/test/integration/test/ports/prisma/functional/relation-mode-gh-1-to-n/_fixture/cascade-plain/generated/contract.d.ts index 559c884fa183..3eb731c6caaf 100644 --- a/test/integration/test/ports/prisma/functional/relation-mode-gh-1-to-n/_fixture/cascade-plain/generated/contract.d.ts +++ b/test/integration/test/ports/prisma/functional/relation-mode-gh-1-to-n/_fixture/cascade-plain/generated/contract.d.ts @@ -42,6 +42,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; @@ -91,6 +95,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; @@ -100,6 +108,14 @@ export type AggregateTypes = { readonly output: 'pg/time-temporal@1'; readonly nullable: true; }; + readonly 'pg/timestamp-date@1': { + readonly output: 'pg/timestamp-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-instant@1': { + readonly output: 'pg/timestamp-instant@1'; + readonly nullable: true; + }; readonly 'pg/timestamp-string@1': { readonly output: 'pg/timestamp-string@1'; readonly nullable: true; @@ -151,6 +167,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; @@ -160,6 +180,14 @@ export type AggregateTypes = { readonly output: 'pg/time-temporal@1'; readonly nullable: true; }; + readonly 'pg/timestamp-date@1': { + readonly output: 'pg/timestamp-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-instant@1': { + readonly output: 'pg/timestamp-instant@1'; + readonly nullable: true; + }; readonly 'pg/timestamp-string@1': { readonly output: 'pg/timestamp-string@1'; readonly nullable: true; @@ -203,6 +231,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; diff --git a/test/integration/test/ports/prisma/functional/relation-mode-gh-1-to-n/_fixture/noaction-map/generated/contract.d.ts b/test/integration/test/ports/prisma/functional/relation-mode-gh-1-to-n/_fixture/noaction-map/generated/contract.d.ts index c76fe9a7e6f7..4956bcf0ba41 100644 --- a/test/integration/test/ports/prisma/functional/relation-mode-gh-1-to-n/_fixture/noaction-map/generated/contract.d.ts +++ b/test/integration/test/ports/prisma/functional/relation-mode-gh-1-to-n/_fixture/noaction-map/generated/contract.d.ts @@ -42,6 +42,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; @@ -91,6 +95,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; @@ -100,6 +108,14 @@ export type AggregateTypes = { readonly output: 'pg/time-temporal@1'; readonly nullable: true; }; + readonly 'pg/timestamp-date@1': { + readonly output: 'pg/timestamp-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-instant@1': { + readonly output: 'pg/timestamp-instant@1'; + readonly nullable: true; + }; readonly 'pg/timestamp-string@1': { readonly output: 'pg/timestamp-string@1'; readonly nullable: true; @@ -151,6 +167,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; @@ -160,6 +180,14 @@ export type AggregateTypes = { readonly output: 'pg/time-temporal@1'; readonly nullable: true; }; + readonly 'pg/timestamp-date@1': { + readonly output: 'pg/timestamp-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-instant@1': { + readonly output: 'pg/timestamp-instant@1'; + readonly nullable: true; + }; readonly 'pg/timestamp-string@1': { readonly output: 'pg/timestamp-string@1'; readonly nullable: true; @@ -203,6 +231,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; diff --git a/test/integration/test/ports/prisma/functional/relation-mode-gh-1-to-n/_fixture/noaction-plain/generated/contract.d.ts b/test/integration/test/ports/prisma/functional/relation-mode-gh-1-to-n/_fixture/noaction-plain/generated/contract.d.ts index 522ec12a381a..9184e173d9b7 100644 --- a/test/integration/test/ports/prisma/functional/relation-mode-gh-1-to-n/_fixture/noaction-plain/generated/contract.d.ts +++ b/test/integration/test/ports/prisma/functional/relation-mode-gh-1-to-n/_fixture/noaction-plain/generated/contract.d.ts @@ -42,6 +42,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; @@ -91,6 +95,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; @@ -100,6 +108,14 @@ export type AggregateTypes = { readonly output: 'pg/time-temporal@1'; readonly nullable: true; }; + readonly 'pg/timestamp-date@1': { + readonly output: 'pg/timestamp-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-instant@1': { + readonly output: 'pg/timestamp-instant@1'; + readonly nullable: true; + }; readonly 'pg/timestamp-string@1': { readonly output: 'pg/timestamp-string@1'; readonly nullable: true; @@ -151,6 +167,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; @@ -160,6 +180,14 @@ export type AggregateTypes = { readonly output: 'pg/time-temporal@1'; readonly nullable: true; }; + readonly 'pg/timestamp-date@1': { + readonly output: 'pg/timestamp-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-instant@1': { + readonly output: 'pg/timestamp-instant@1'; + readonly nullable: true; + }; readonly 'pg/timestamp-string@1': { readonly output: 'pg/timestamp-string@1'; readonly nullable: true; @@ -203,6 +231,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; diff --git a/test/integration/test/ports/prisma/functional/relation-mode-gh-1-to-n/_fixture/restrict-map/generated/contract.d.ts b/test/integration/test/ports/prisma/functional/relation-mode-gh-1-to-n/_fixture/restrict-map/generated/contract.d.ts index 51cf3ca209c9..2816cc23aad5 100644 --- a/test/integration/test/ports/prisma/functional/relation-mode-gh-1-to-n/_fixture/restrict-map/generated/contract.d.ts +++ b/test/integration/test/ports/prisma/functional/relation-mode-gh-1-to-n/_fixture/restrict-map/generated/contract.d.ts @@ -42,6 +42,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; @@ -91,6 +95,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; @@ -100,6 +108,14 @@ export type AggregateTypes = { readonly output: 'pg/time-temporal@1'; readonly nullable: true; }; + readonly 'pg/timestamp-date@1': { + readonly output: 'pg/timestamp-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-instant@1': { + readonly output: 'pg/timestamp-instant@1'; + readonly nullable: true; + }; readonly 'pg/timestamp-string@1': { readonly output: 'pg/timestamp-string@1'; readonly nullable: true; @@ -151,6 +167,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; @@ -160,6 +180,14 @@ export type AggregateTypes = { readonly output: 'pg/time-temporal@1'; readonly nullable: true; }; + readonly 'pg/timestamp-date@1': { + readonly output: 'pg/timestamp-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-instant@1': { + readonly output: 'pg/timestamp-instant@1'; + readonly nullable: true; + }; readonly 'pg/timestamp-string@1': { readonly output: 'pg/timestamp-string@1'; readonly nullable: true; @@ -203,6 +231,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; diff --git a/test/integration/test/ports/prisma/functional/relation-mode-gh-1-to-n/_fixture/restrict-plain/generated/contract.d.ts b/test/integration/test/ports/prisma/functional/relation-mode-gh-1-to-n/_fixture/restrict-plain/generated/contract.d.ts index ffd708b3abd3..961b9213d4b1 100644 --- a/test/integration/test/ports/prisma/functional/relation-mode-gh-1-to-n/_fixture/restrict-plain/generated/contract.d.ts +++ b/test/integration/test/ports/prisma/functional/relation-mode-gh-1-to-n/_fixture/restrict-plain/generated/contract.d.ts @@ -42,6 +42,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; @@ -91,6 +95,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; @@ -100,6 +108,14 @@ export type AggregateTypes = { readonly output: 'pg/time-temporal@1'; readonly nullable: true; }; + readonly 'pg/timestamp-date@1': { + readonly output: 'pg/timestamp-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-instant@1': { + readonly output: 'pg/timestamp-instant@1'; + readonly nullable: true; + }; readonly 'pg/timestamp-string@1': { readonly output: 'pg/timestamp-string@1'; readonly nullable: true; @@ -151,6 +167,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; @@ -160,6 +180,14 @@ export type AggregateTypes = { readonly output: 'pg/time-temporal@1'; readonly nullable: true; }; + readonly 'pg/timestamp-date@1': { + readonly output: 'pg/timestamp-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-instant@1': { + readonly output: 'pg/timestamp-instant@1'; + readonly nullable: true; + }; readonly 'pg/timestamp-string@1': { readonly output: 'pg/timestamp-string@1'; readonly nullable: true; @@ -203,6 +231,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; diff --git a/test/integration/test/ports/prisma/functional/relation-mode-gh-1-to-n/_fixture/setnull-map/generated/contract.d.ts b/test/integration/test/ports/prisma/functional/relation-mode-gh-1-to-n/_fixture/setnull-map/generated/contract.d.ts index f38479f32b90..aea97820b877 100644 --- a/test/integration/test/ports/prisma/functional/relation-mode-gh-1-to-n/_fixture/setnull-map/generated/contract.d.ts +++ b/test/integration/test/ports/prisma/functional/relation-mode-gh-1-to-n/_fixture/setnull-map/generated/contract.d.ts @@ -42,6 +42,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; @@ -91,6 +95,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; @@ -100,6 +108,14 @@ export type AggregateTypes = { readonly output: 'pg/time-temporal@1'; readonly nullable: true; }; + readonly 'pg/timestamp-date@1': { + readonly output: 'pg/timestamp-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-instant@1': { + readonly output: 'pg/timestamp-instant@1'; + readonly nullable: true; + }; readonly 'pg/timestamp-string@1': { readonly output: 'pg/timestamp-string@1'; readonly nullable: true; @@ -151,6 +167,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; @@ -160,6 +180,14 @@ export type AggregateTypes = { readonly output: 'pg/time-temporal@1'; readonly nullable: true; }; + readonly 'pg/timestamp-date@1': { + readonly output: 'pg/timestamp-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-instant@1': { + readonly output: 'pg/timestamp-instant@1'; + readonly nullable: true; + }; readonly 'pg/timestamp-string@1': { readonly output: 'pg/timestamp-string@1'; readonly nullable: true; @@ -203,6 +231,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; diff --git a/test/integration/test/ports/prisma/functional/relation-mode-gh-1-to-n/_fixture/setnull-plain/generated/contract.d.ts b/test/integration/test/ports/prisma/functional/relation-mode-gh-1-to-n/_fixture/setnull-plain/generated/contract.d.ts index 9ee3083bc309..df9d8fca45a4 100644 --- a/test/integration/test/ports/prisma/functional/relation-mode-gh-1-to-n/_fixture/setnull-plain/generated/contract.d.ts +++ b/test/integration/test/ports/prisma/functional/relation-mode-gh-1-to-n/_fixture/setnull-plain/generated/contract.d.ts @@ -42,6 +42,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; @@ -91,6 +95,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; @@ -100,6 +108,14 @@ export type AggregateTypes = { readonly output: 'pg/time-temporal@1'; readonly nullable: true; }; + readonly 'pg/timestamp-date@1': { + readonly output: 'pg/timestamp-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-instant@1': { + readonly output: 'pg/timestamp-instant@1'; + readonly nullable: true; + }; readonly 'pg/timestamp-string@1': { readonly output: 'pg/timestamp-string@1'; readonly nullable: true; @@ -151,6 +167,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; @@ -160,6 +180,14 @@ export type AggregateTypes = { readonly output: 'pg/time-temporal@1'; readonly nullable: true; }; + readonly 'pg/timestamp-date@1': { + readonly output: 'pg/timestamp-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-instant@1': { + readonly output: 'pg/timestamp-instant@1'; + readonly nullable: true; + }; readonly 'pg/timestamp-string@1': { readonly output: 'pg/timestamp-string@1'; readonly nullable: true; @@ -203,6 +231,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; diff --git a/test/integration/test/ports/prisma/functional/relation-mode-gh-m-to-n/_fixture/cascade-nomap/generated/contract.d.ts b/test/integration/test/ports/prisma/functional/relation-mode-gh-m-to-n/_fixture/cascade-nomap/generated/contract.d.ts index 08f1413bc117..02ec8d7fdc5e 100644 --- a/test/integration/test/ports/prisma/functional/relation-mode-gh-m-to-n/_fixture/cascade-nomap/generated/contract.d.ts +++ b/test/integration/test/ports/prisma/functional/relation-mode-gh-m-to-n/_fixture/cascade-nomap/generated/contract.d.ts @@ -42,6 +42,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; @@ -91,6 +95,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; @@ -100,6 +108,14 @@ export type AggregateTypes = { readonly output: 'pg/time-temporal@1'; readonly nullable: true; }; + readonly 'pg/timestamp-date@1': { + readonly output: 'pg/timestamp-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-instant@1': { + readonly output: 'pg/timestamp-instant@1'; + readonly nullable: true; + }; readonly 'pg/timestamp-string@1': { readonly output: 'pg/timestamp-string@1'; readonly nullable: true; @@ -151,6 +167,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; @@ -160,6 +180,14 @@ export type AggregateTypes = { readonly output: 'pg/time-temporal@1'; readonly nullable: true; }; + readonly 'pg/timestamp-date@1': { + readonly output: 'pg/timestamp-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-instant@1': { + readonly output: 'pg/timestamp-instant@1'; + readonly nullable: true; + }; readonly 'pg/timestamp-string@1': { readonly output: 'pg/timestamp-string@1'; readonly nullable: true; @@ -203,6 +231,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; diff --git a/test/integration/test/ports/prisma/functional/relation-mode-gh-m-to-n/_fixture/default-nomap/generated/contract.d.ts b/test/integration/test/ports/prisma/functional/relation-mode-gh-m-to-n/_fixture/default-nomap/generated/contract.d.ts index 0fb7ff132e86..a7d95996d526 100644 --- a/test/integration/test/ports/prisma/functional/relation-mode-gh-m-to-n/_fixture/default-nomap/generated/contract.d.ts +++ b/test/integration/test/ports/prisma/functional/relation-mode-gh-m-to-n/_fixture/default-nomap/generated/contract.d.ts @@ -42,6 +42,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; @@ -91,6 +95,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; @@ -100,6 +108,14 @@ export type AggregateTypes = { readonly output: 'pg/time-temporal@1'; readonly nullable: true; }; + readonly 'pg/timestamp-date@1': { + readonly output: 'pg/timestamp-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-instant@1': { + readonly output: 'pg/timestamp-instant@1'; + readonly nullable: true; + }; readonly 'pg/timestamp-string@1': { readonly output: 'pg/timestamp-string@1'; readonly nullable: true; @@ -151,6 +167,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; @@ -160,6 +180,14 @@ export type AggregateTypes = { readonly output: 'pg/time-temporal@1'; readonly nullable: true; }; + readonly 'pg/timestamp-date@1': { + readonly output: 'pg/timestamp-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-instant@1': { + readonly output: 'pg/timestamp-instant@1'; + readonly nullable: true; + }; readonly 'pg/timestamp-string@1': { readonly output: 'pg/timestamp-string@1'; readonly nullable: true; @@ -203,6 +231,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; diff --git a/test/integration/test/ports/prisma/functional/relation-mode-gh-m-to-n/_fixture/noaction-nomap/generated/contract.d.ts b/test/integration/test/ports/prisma/functional/relation-mode-gh-m-to-n/_fixture/noaction-nomap/generated/contract.d.ts index 0fb7ff132e86..a7d95996d526 100644 --- a/test/integration/test/ports/prisma/functional/relation-mode-gh-m-to-n/_fixture/noaction-nomap/generated/contract.d.ts +++ b/test/integration/test/ports/prisma/functional/relation-mode-gh-m-to-n/_fixture/noaction-nomap/generated/contract.d.ts @@ -42,6 +42,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; @@ -91,6 +95,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; @@ -100,6 +108,14 @@ export type AggregateTypes = { readonly output: 'pg/time-temporal@1'; readonly nullable: true; }; + readonly 'pg/timestamp-date@1': { + readonly output: 'pg/timestamp-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-instant@1': { + readonly output: 'pg/timestamp-instant@1'; + readonly nullable: true; + }; readonly 'pg/timestamp-string@1': { readonly output: 'pg/timestamp-string@1'; readonly nullable: true; @@ -151,6 +167,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; @@ -160,6 +180,14 @@ export type AggregateTypes = { readonly output: 'pg/time-temporal@1'; readonly nullable: true; }; + readonly 'pg/timestamp-date@1': { + readonly output: 'pg/timestamp-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-instant@1': { + readonly output: 'pg/timestamp-instant@1'; + readonly nullable: true; + }; readonly 'pg/timestamp-string@1': { readonly output: 'pg/timestamp-string@1'; readonly nullable: true; @@ -203,6 +231,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; diff --git a/test/integration/test/ports/prisma/functional/relation-mode-gh-m-to-n/_fixture/restrict-nomap/generated/contract.d.ts b/test/integration/test/ports/prisma/functional/relation-mode-gh-m-to-n/_fixture/restrict-nomap/generated/contract.d.ts index c94b4d2e9421..dad3483bacc2 100644 --- a/test/integration/test/ports/prisma/functional/relation-mode-gh-m-to-n/_fixture/restrict-nomap/generated/contract.d.ts +++ b/test/integration/test/ports/prisma/functional/relation-mode-gh-m-to-n/_fixture/restrict-nomap/generated/contract.d.ts @@ -42,6 +42,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; @@ -91,6 +95,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; @@ -100,6 +108,14 @@ export type AggregateTypes = { readonly output: 'pg/time-temporal@1'; readonly nullable: true; }; + readonly 'pg/timestamp-date@1': { + readonly output: 'pg/timestamp-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-instant@1': { + readonly output: 'pg/timestamp-instant@1'; + readonly nullable: true; + }; readonly 'pg/timestamp-string@1': { readonly output: 'pg/timestamp-string@1'; readonly nullable: true; @@ -151,6 +167,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; @@ -160,6 +180,14 @@ export type AggregateTypes = { readonly output: 'pg/time-temporal@1'; readonly nullable: true; }; + readonly 'pg/timestamp-date@1': { + readonly output: 'pg/timestamp-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-instant@1': { + readonly output: 'pg/timestamp-instant@1'; + readonly nullable: true; + }; readonly 'pg/timestamp-string@1': { readonly output: 'pg/timestamp-string@1'; readonly nullable: true; @@ -203,6 +231,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; diff --git a/test/integration/test/psl-infer/marked-types-round-trip.integration.test.ts b/test/integration/test/psl-infer/marked-types-round-trip.integration.test.ts index 727f9c5be36f..1df6f154073a 100644 --- a/test/integration/test/psl-infer/marked-types-round-trip.integration.test.ts +++ b/test/integration/test/psl-infer/marked-types-round-trip.integration.test.ts @@ -80,8 +80,8 @@ const AUTHORED_TYPES = [ 'Bit(8)', 'VarBit', 'VarBit(16)', - 'Interval', - 'Interval(3)', + 'IntervalParts', + 'IntervalParts(3)', ] as const; const AUTHORED_SCHEMA = `model Sample { diff --git a/test/integration/test/psl-print/date-time-types-print.integration.test.ts b/test/integration/test/psl-print/date-time-types-print.integration.test.ts index 7450d0c91422..15c65f9b93a1 100644 --- a/test/integration/test/psl-print/date-time-types-print.integration.test.ts +++ b/test/integration/test/psl-print/date-time-types-print.integration.test.ts @@ -6,10 +6,16 @@ const SCHEMA = `// use prisma-8 model Event { id Int @id instant DateTime - zoned Timestamptz - zoned3 Timestamptz(3) - local Timestamp - local3 Timestamp(3) + zoned TimestamptzTemporal + zoned3 TimestamptzTemporal(3) + local TimestampTemporal + local3 TimestampTemporal(3) + utcDate TimestampJsDate(3) + utcAt TimestampInstant(6) + dayT DateTemporal + clockT TimeTemporal(0) + period IntervalParts + span IntervalString(3) day Date clock Time clock3 Time(3) @@ -33,19 +39,25 @@ function fieldTypes(printed: string): Record { } describe('contract print spells each date and time column', () => { - it('as the type contract infer writes, or the first constructor that reads the column back', async () => { + it('as the type contract infer writes, or else as the explicit type that reads the column back', async () => { const contract = await readPsl(SCHEMA); expect(fieldTypes(printContract(contract).text)).toEqual({ id: 'Int', - instant: 'DateTime', - zoned: 'DateTime', - zoned3: 'Timestamptz(3)', - local: 'Timestamp', - local3: 'Timestamp(3)', - day: 'Date', - clock: 'Time', - clock3: 'Time(3)', + instant: 'TimestamptzTemporal', + zoned: 'TimestamptzTemporal', + zoned3: 'TimestamptzTemporal(3)', + local: 'TimestampTemporal', + local3: 'TimestampTemporal(3)', + utcDate: 'TimestampJsDate(3)', + utcAt: 'TimestampInstant(6)', + dayT: 'DateTemporal', + clockT: 'TimeTemporal(0)', + period: 'IntervalParts', + span: 'IntervalString(3)', + day: 'DateTemporal', + clock: 'TimeTemporal', + clock3: 'TimeTemporal(3)', zonedText: 'TimestamptzString(3)', localText: 'TimestampString(3)', dayText: 'DateString', diff --git a/test/integration/test/psl-print/generated-values-roundtrip.integration.test.ts b/test/integration/test/psl-print/generated-values-roundtrip.integration.test.ts index b277988e4afe..c6e32f32c0ef 100644 --- a/test/integration/test/psl-print/generated-values-roundtrip.integration.test.ts +++ b/test/integration/test/psl-print/generated-values-roundtrip.integration.test.ts @@ -166,8 +166,8 @@ model Event { fieldLines: [ 'id String @id @onCreate(uuid(7))', 'code VarChar(32) @onCreate(nanoid())', - 'seenAt Time(3) @onUpdate(now())', - 'createdAt DateTime @default(now()) @onCreate(now())', + 'seenAt TimeTemporal(3) @onUpdate(now())', + 'createdAt TimestamptzTemporal @default(now()) @onCreate(now())', ], roundTrip: serializedWithoutCapabilities(authored), }); diff --git a/test/integration/test/sql-builder/fixtures/generated-no-pgvector/contract.d.ts b/test/integration/test/sql-builder/fixtures/generated-no-pgvector/contract.d.ts index 80b3d3eda96a..7a2058a0a1fe 100644 --- a/test/integration/test/sql-builder/fixtures/generated-no-pgvector/contract.d.ts +++ b/test/integration/test/sql-builder/fixtures/generated-no-pgvector/contract.d.ts @@ -43,6 +43,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; @@ -92,6 +96,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; @@ -101,6 +109,14 @@ export type AggregateTypes = { readonly output: 'pg/time-temporal@1'; readonly nullable: true; }; + readonly 'pg/timestamp-date@1': { + readonly output: 'pg/timestamp-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-instant@1': { + readonly output: 'pg/timestamp-instant@1'; + readonly nullable: true; + }; readonly 'pg/timestamp-string@1': { readonly output: 'pg/timestamp-string@1'; readonly nullable: true; @@ -152,6 +168,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; @@ -161,6 +181,14 @@ export type AggregateTypes = { readonly output: 'pg/time-temporal@1'; readonly nullable: true; }; + readonly 'pg/timestamp-date@1': { + readonly output: 'pg/timestamp-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-instant@1': { + readonly output: 'pg/timestamp-instant@1'; + readonly nullable: true; + }; readonly 'pg/timestamp-string@1': { readonly output: 'pg/timestamp-string@1'; readonly nullable: true; @@ -204,6 +232,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; diff --git a/test/integration/test/sql-builder/fixtures/generated/contract.d.ts b/test/integration/test/sql-builder/fixtures/generated/contract.d.ts index 937ed7662d5c..b7a660e27d7a 100644 --- a/test/integration/test/sql-builder/fixtures/generated/contract.d.ts +++ b/test/integration/test/sql-builder/fixtures/generated/contract.d.ts @@ -49,6 +49,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; @@ -98,6 +102,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; @@ -107,6 +115,14 @@ export type AggregateTypes = { readonly output: 'pg/time-temporal@1'; readonly nullable: true; }; + readonly 'pg/timestamp-date@1': { + readonly output: 'pg/timestamp-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-instant@1': { + readonly output: 'pg/timestamp-instant@1'; + readonly nullable: true; + }; readonly 'pg/timestamp-string@1': { readonly output: 'pg/timestamp-string@1'; readonly nullable: true; @@ -158,6 +174,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; @@ -167,6 +187,14 @@ export type AggregateTypes = { readonly output: 'pg/time-temporal@1'; readonly nullable: true; }; + readonly 'pg/timestamp-date@1': { + readonly output: 'pg/timestamp-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-instant@1': { + readonly output: 'pg/timestamp-instant@1'; + readonly nullable: true; + }; readonly 'pg/timestamp-string@1': { readonly output: 'pg/timestamp-string@1'; readonly nullable: true; @@ -210,6 +238,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; diff --git a/test/integration/test/sql-orm-client/fixtures/collection-methods/generated/contract.d.ts b/test/integration/test/sql-orm-client/fixtures/collection-methods/generated/contract.d.ts index 98347ff79255..3a9d55796e0e 100644 --- a/test/integration/test/sql-orm-client/fixtures/collection-methods/generated/contract.d.ts +++ b/test/integration/test/sql-orm-client/fixtures/collection-methods/generated/contract.d.ts @@ -48,6 +48,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; @@ -97,6 +101,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; @@ -106,6 +114,14 @@ export type AggregateTypes = { readonly output: 'pg/time-temporal@1'; readonly nullable: true; }; + readonly 'pg/timestamp-date@1': { + readonly output: 'pg/timestamp-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-instant@1': { + readonly output: 'pg/timestamp-instant@1'; + readonly nullable: true; + }; readonly 'pg/timestamp-string@1': { readonly output: 'pg/timestamp-string@1'; readonly nullable: true; @@ -157,6 +173,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; @@ -166,6 +186,14 @@ export type AggregateTypes = { readonly output: 'pg/time-temporal@1'; readonly nullable: true; }; + readonly 'pg/timestamp-date@1': { + readonly output: 'pg/timestamp-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-instant@1': { + readonly output: 'pg/timestamp-instant@1'; + readonly nullable: true; + }; readonly 'pg/timestamp-string@1': { readonly output: 'pg/timestamp-string@1'; readonly nullable: true; @@ -209,6 +237,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; diff --git a/test/integration/test/sql-orm-client/fixtures/execution-defaulted-tags/generated/contract.d.ts b/test/integration/test/sql-orm-client/fixtures/execution-defaulted-tags/generated/contract.d.ts index 97e1d6846948..daee95a0333f 100644 --- a/test/integration/test/sql-orm-client/fixtures/execution-defaulted-tags/generated/contract.d.ts +++ b/test/integration/test/sql-orm-client/fixtures/execution-defaulted-tags/generated/contract.d.ts @@ -43,6 +43,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; @@ -92,6 +96,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; @@ -101,6 +109,14 @@ export type AggregateTypes = { readonly output: 'pg/time-temporal@1'; readonly nullable: true; }; + readonly 'pg/timestamp-date@1': { + readonly output: 'pg/timestamp-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-instant@1': { + readonly output: 'pg/timestamp-instant@1'; + readonly nullable: true; + }; readonly 'pg/timestamp-string@1': { readonly output: 'pg/timestamp-string@1'; readonly nullable: true; @@ -152,6 +168,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; @@ -161,6 +181,14 @@ export type AggregateTypes = { readonly output: 'pg/time-temporal@1'; readonly nullable: true; }; + readonly 'pg/timestamp-date@1': { + readonly output: 'pg/timestamp-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-instant@1': { + readonly output: 'pg/timestamp-instant@1'; + readonly nullable: true; + }; readonly 'pg/timestamp-string@1': { readonly output: 'pg/timestamp-string@1'; readonly nullable: true; @@ -204,6 +232,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; diff --git a/test/integration/test/sql-orm-client/fixtures/fragment-namespace/generated/contract.d.ts b/test/integration/test/sql-orm-client/fixtures/fragment-namespace/generated/contract.d.ts index 522738f53abe..4a0bc8f4834c 100644 --- a/test/integration/test/sql-orm-client/fixtures/fragment-namespace/generated/contract.d.ts +++ b/test/integration/test/sql-orm-client/fixtures/fragment-namespace/generated/contract.d.ts @@ -42,6 +42,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; @@ -91,6 +95,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; @@ -100,6 +108,14 @@ export type AggregateTypes = { readonly output: 'pg/time-temporal@1'; readonly nullable: true; }; + readonly 'pg/timestamp-date@1': { + readonly output: 'pg/timestamp-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-instant@1': { + readonly output: 'pg/timestamp-instant@1'; + readonly nullable: true; + }; readonly 'pg/timestamp-string@1': { readonly output: 'pg/timestamp-string@1'; readonly nullable: true; @@ -151,6 +167,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; @@ -160,6 +180,14 @@ export type AggregateTypes = { readonly output: 'pg/time-temporal@1'; readonly nullable: true; }; + readonly 'pg/timestamp-date@1': { + readonly output: 'pg/timestamp-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-instant@1': { + readonly output: 'pg/timestamp-instant@1'; + readonly nullable: true; + }; readonly 'pg/timestamp-string@1': { readonly output: 'pg/timestamp-string@1'; readonly nullable: true; @@ -203,6 +231,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; diff --git a/test/integration/test/sql-orm-client/fixtures/generated/contract.d.ts b/test/integration/test/sql-orm-client/fixtures/generated/contract.d.ts index e4e7b0d65f35..1f2a81a3b2f8 100644 --- a/test/integration/test/sql-orm-client/fixtures/generated/contract.d.ts +++ b/test/integration/test/sql-orm-client/fixtures/generated/contract.d.ts @@ -50,6 +50,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; @@ -99,6 +103,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; @@ -108,6 +116,14 @@ export type AggregateTypes = { readonly output: 'pg/time-temporal@1'; readonly nullable: true; }; + readonly 'pg/timestamp-date@1': { + readonly output: 'pg/timestamp-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-instant@1': { + readonly output: 'pg/timestamp-instant@1'; + readonly nullable: true; + }; readonly 'pg/timestamp-string@1': { readonly output: 'pg/timestamp-string@1'; readonly nullable: true; @@ -159,6 +175,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; @@ -168,6 +188,14 @@ export type AggregateTypes = { readonly output: 'pg/time-temporal@1'; readonly nullable: true; }; + readonly 'pg/timestamp-date@1': { + readonly output: 'pg/timestamp-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-instant@1': { + readonly output: 'pg/timestamp-instant@1'; + readonly nullable: true; + }; readonly 'pg/timestamp-string@1': { readonly output: 'pg/timestamp-string@1'; readonly nullable: true; @@ -211,6 +239,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; diff --git a/test/integration/test/sql-orm-client/fixtures/integer-representation/generated/contract.d.ts b/test/integration/test/sql-orm-client/fixtures/integer-representation/generated/contract.d.ts index 9002874effb4..c29c79d99878 100644 --- a/test/integration/test/sql-orm-client/fixtures/integer-representation/generated/contract.d.ts +++ b/test/integration/test/sql-orm-client/fixtures/integer-representation/generated/contract.d.ts @@ -42,6 +42,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; @@ -91,6 +95,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; @@ -100,6 +108,14 @@ export type AggregateTypes = { readonly output: 'pg/time-temporal@1'; readonly nullable: true; }; + readonly 'pg/timestamp-date@1': { + readonly output: 'pg/timestamp-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-instant@1': { + readonly output: 'pg/timestamp-instant@1'; + readonly nullable: true; + }; readonly 'pg/timestamp-string@1': { readonly output: 'pg/timestamp-string@1'; readonly nullable: true; @@ -151,6 +167,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; @@ -160,6 +180,14 @@ export type AggregateTypes = { readonly output: 'pg/time-temporal@1'; readonly nullable: true; }; + readonly 'pg/timestamp-date@1': { + readonly output: 'pg/timestamp-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-instant@1': { + readonly output: 'pg/timestamp-instant@1'; + readonly nullable: true; + }; readonly 'pg/timestamp-string@1': { readonly output: 'pg/timestamp-string@1'; readonly nullable: true; @@ -203,6 +231,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; diff --git a/test/integration/test/sql-orm-client/fixtures/junction-namespaces/generated/contract.d.ts b/test/integration/test/sql-orm-client/fixtures/junction-namespaces/generated/contract.d.ts index 73af750f4244..cf321d22b5ac 100644 --- a/test/integration/test/sql-orm-client/fixtures/junction-namespaces/generated/contract.d.ts +++ b/test/integration/test/sql-orm-client/fixtures/junction-namespaces/generated/contract.d.ts @@ -43,6 +43,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; @@ -92,6 +96,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; @@ -101,6 +109,14 @@ export type AggregateTypes = { readonly output: 'pg/time-temporal@1'; readonly nullable: true; }; + readonly 'pg/timestamp-date@1': { + readonly output: 'pg/timestamp-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-instant@1': { + readonly output: 'pg/timestamp-instant@1'; + readonly nullable: true; + }; readonly 'pg/timestamp-string@1': { readonly output: 'pg/timestamp-string@1'; readonly nullable: true; @@ -152,6 +168,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; @@ -161,6 +181,14 @@ export type AggregateTypes = { readonly output: 'pg/time-temporal@1'; readonly nullable: true; }; + readonly 'pg/timestamp-date@1': { + readonly output: 'pg/timestamp-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-instant@1': { + readonly output: 'pg/timestamp-instant@1'; + readonly nullable: true; + }; readonly 'pg/timestamp-string@1': { readonly output: 'pg/timestamp-string@1'; readonly nullable: true; @@ -204,6 +232,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; diff --git a/test/integration/test/sql-orm-client/fixtures/mn-psl/generated/contract.d.ts b/test/integration/test/sql-orm-client/fixtures/mn-psl/generated/contract.d.ts index 93efa01d2b11..24c71ae44ad8 100644 --- a/test/integration/test/sql-orm-client/fixtures/mn-psl/generated/contract.d.ts +++ b/test/integration/test/sql-orm-client/fixtures/mn-psl/generated/contract.d.ts @@ -42,6 +42,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; @@ -91,6 +95,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; @@ -100,6 +108,14 @@ export type AggregateTypes = { readonly output: 'pg/time-temporal@1'; readonly nullable: true; }; + readonly 'pg/timestamp-date@1': { + readonly output: 'pg/timestamp-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-instant@1': { + readonly output: 'pg/timestamp-instant@1'; + readonly nullable: true; + }; readonly 'pg/timestamp-string@1': { readonly output: 'pg/timestamp-string@1'; readonly nullable: true; @@ -151,6 +167,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; @@ -160,6 +180,14 @@ export type AggregateTypes = { readonly output: 'pg/time-temporal@1'; readonly nullable: true; }; + readonly 'pg/timestamp-date@1': { + readonly output: 'pg/timestamp-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-instant@1': { + readonly output: 'pg/timestamp-instant@1'; + readonly nullable: true; + }; readonly 'pg/timestamp-string@1': { readonly output: 'pg/timestamp-string@1'; readonly nullable: true; @@ -203,6 +231,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; diff --git a/test/integration/test/sql-orm-client/fixtures/non-identifier-names/generated/contract.d.ts b/test/integration/test/sql-orm-client/fixtures/non-identifier-names/generated/contract.d.ts index 7918c3eda57a..ddc8a0f51c8b 100644 --- a/test/integration/test/sql-orm-client/fixtures/non-identifier-names/generated/contract.d.ts +++ b/test/integration/test/sql-orm-client/fixtures/non-identifier-names/generated/contract.d.ts @@ -42,6 +42,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; @@ -91,6 +95,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; @@ -100,6 +108,14 @@ export type AggregateTypes = { readonly output: 'pg/time-temporal@1'; readonly nullable: true; }; + readonly 'pg/timestamp-date@1': { + readonly output: 'pg/timestamp-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-instant@1': { + readonly output: 'pg/timestamp-instant@1'; + readonly nullable: true; + }; readonly 'pg/timestamp-string@1': { readonly output: 'pg/timestamp-string@1'; readonly nullable: true; @@ -151,6 +167,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; @@ -160,6 +180,14 @@ export type AggregateTypes = { readonly output: 'pg/time-temporal@1'; readonly nullable: true; }; + readonly 'pg/timestamp-date@1': { + readonly output: 'pg/timestamp-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-instant@1': { + readonly output: 'pg/timestamp-instant@1'; + readonly nullable: true; + }; readonly 'pg/timestamp-string@1': { readonly output: 'pg/timestamp-string@1'; readonly nullable: true; @@ -203,6 +231,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; diff --git a/test/integration/test/sql-orm-client/fixtures/on-update-only/generated/contract.d.ts b/test/integration/test/sql-orm-client/fixtures/on-update-only/generated/contract.d.ts index 78960b7aa687..37b0bd85e647 100644 --- a/test/integration/test/sql-orm-client/fixtures/on-update-only/generated/contract.d.ts +++ b/test/integration/test/sql-orm-client/fixtures/on-update-only/generated/contract.d.ts @@ -43,6 +43,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; @@ -92,6 +96,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; @@ -101,6 +109,14 @@ export type AggregateTypes = { readonly output: 'pg/time-temporal@1'; readonly nullable: true; }; + readonly 'pg/timestamp-date@1': { + readonly output: 'pg/timestamp-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-instant@1': { + readonly output: 'pg/timestamp-instant@1'; + readonly nullable: true; + }; readonly 'pg/timestamp-string@1': { readonly output: 'pg/timestamp-string@1'; readonly nullable: true; @@ -152,6 +168,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; @@ -161,6 +181,14 @@ export type AggregateTypes = { readonly output: 'pg/time-temporal@1'; readonly nullable: true; }; + readonly 'pg/timestamp-date@1': { + readonly output: 'pg/timestamp-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-instant@1': { + readonly output: 'pg/timestamp-instant@1'; + readonly nullable: true; + }; readonly 'pg/timestamp-string@1': { readonly output: 'pg/timestamp-string@1'; readonly nullable: true; @@ -204,6 +232,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; diff --git a/test/integration/test/sql-orm-client/fixtures/polymorphism-namespaces/generated/contract.d.ts b/test/integration/test/sql-orm-client/fixtures/polymorphism-namespaces/generated/contract.d.ts index d77236c61c04..cd940d8aece6 100644 --- a/test/integration/test/sql-orm-client/fixtures/polymorphism-namespaces/generated/contract.d.ts +++ b/test/integration/test/sql-orm-client/fixtures/polymorphism-namespaces/generated/contract.d.ts @@ -42,6 +42,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; @@ -91,6 +95,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; @@ -100,6 +108,14 @@ export type AggregateTypes = { readonly output: 'pg/time-temporal@1'; readonly nullable: true; }; + readonly 'pg/timestamp-date@1': { + readonly output: 'pg/timestamp-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-instant@1': { + readonly output: 'pg/timestamp-instant@1'; + readonly nullable: true; + }; readonly 'pg/timestamp-string@1': { readonly output: 'pg/timestamp-string@1'; readonly nullable: true; @@ -151,6 +167,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; @@ -160,6 +180,14 @@ export type AggregateTypes = { readonly output: 'pg/time-temporal@1'; readonly nullable: true; }; + readonly 'pg/timestamp-date@1': { + readonly output: 'pg/timestamp-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-instant@1': { + readonly output: 'pg/timestamp-instant@1'; + readonly nullable: true; + }; readonly 'pg/timestamp-string@1': { readonly output: 'pg/timestamp-string@1'; readonly nullable: true; @@ -203,6 +231,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; diff --git a/test/integration/test/sql-orm-client/fixtures/polymorphism-self-relation/generated/contract.d.ts b/test/integration/test/sql-orm-client/fixtures/polymorphism-self-relation/generated/contract.d.ts index 22a16e54d56a..f78b0d918171 100644 --- a/test/integration/test/sql-orm-client/fixtures/polymorphism-self-relation/generated/contract.d.ts +++ b/test/integration/test/sql-orm-client/fixtures/polymorphism-self-relation/generated/contract.d.ts @@ -42,6 +42,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; @@ -91,6 +95,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; @@ -100,6 +108,14 @@ export type AggregateTypes = { readonly output: 'pg/time-temporal@1'; readonly nullable: true; }; + readonly 'pg/timestamp-date@1': { + readonly output: 'pg/timestamp-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-instant@1': { + readonly output: 'pg/timestamp-instant@1'; + readonly nullable: true; + }; readonly 'pg/timestamp-string@1': { readonly output: 'pg/timestamp-string@1'; readonly nullable: true; @@ -151,6 +167,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; @@ -160,6 +180,14 @@ export type AggregateTypes = { readonly output: 'pg/time-temporal@1'; readonly nullable: true; }; + readonly 'pg/timestamp-date@1': { + readonly output: 'pg/timestamp-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-instant@1': { + readonly output: 'pg/timestamp-instant@1'; + readonly nullable: true; + }; readonly 'pg/timestamp-string@1': { readonly output: 'pg/timestamp-string@1'; readonly nullable: true; @@ -203,6 +231,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; diff --git a/test/integration/test/sql-orm-client/fixtures/polymorphism/generated/contract.d.ts b/test/integration/test/sql-orm-client/fixtures/polymorphism/generated/contract.d.ts index 90af9e6edd9b..741846a4cec7 100644 --- a/test/integration/test/sql-orm-client/fixtures/polymorphism/generated/contract.d.ts +++ b/test/integration/test/sql-orm-client/fixtures/polymorphism/generated/contract.d.ts @@ -42,6 +42,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; @@ -91,6 +95,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; @@ -100,6 +108,14 @@ export type AggregateTypes = { readonly output: 'pg/time-temporal@1'; readonly nullable: true; }; + readonly 'pg/timestamp-date@1': { + readonly output: 'pg/timestamp-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-instant@1': { + readonly output: 'pg/timestamp-instant@1'; + readonly nullable: true; + }; readonly 'pg/timestamp-string@1': { readonly output: 'pg/timestamp-string@1'; readonly nullable: true; @@ -151,6 +167,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; @@ -160,6 +180,14 @@ export type AggregateTypes = { readonly output: 'pg/time-temporal@1'; readonly nullable: true; }; + readonly 'pg/timestamp-date@1': { + readonly output: 'pg/timestamp-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-instant@1': { + readonly output: 'pg/timestamp-instant@1'; + readonly nullable: true; + }; readonly 'pg/timestamp-string@1': { readonly output: 'pg/timestamp-string@1'; readonly nullable: true; @@ -203,6 +231,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; diff --git a/test/integration/test/sql-orm-client/fixtures/scalar-lists/generated/contract.d.ts b/test/integration/test/sql-orm-client/fixtures/scalar-lists/generated/contract.d.ts index 1d4e5a338ded..42b4486e6950 100644 --- a/test/integration/test/sql-orm-client/fixtures/scalar-lists/generated/contract.d.ts +++ b/test/integration/test/sql-orm-client/fixtures/scalar-lists/generated/contract.d.ts @@ -42,6 +42,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; @@ -91,6 +95,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; @@ -100,6 +108,14 @@ export type AggregateTypes = { readonly output: 'pg/time-temporal@1'; readonly nullable: true; }; + readonly 'pg/timestamp-date@1': { + readonly output: 'pg/timestamp-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-instant@1': { + readonly output: 'pg/timestamp-instant@1'; + readonly nullable: true; + }; readonly 'pg/timestamp-string@1': { readonly output: 'pg/timestamp-string@1'; readonly nullable: true; @@ -151,6 +167,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; @@ -160,6 +180,14 @@ export type AggregateTypes = { readonly output: 'pg/time-temporal@1'; readonly nullable: true; }; + readonly 'pg/timestamp-date@1': { + readonly output: 'pg/timestamp-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-instant@1': { + readonly output: 'pg/timestamp-instant@1'; + readonly nullable: true; + }; readonly 'pg/timestamp-string@1': { readonly output: 'pg/timestamp-string@1'; readonly nullable: true; @@ -203,6 +231,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; diff --git a/test/integration/test/sql-orm-client/fixtures/scope-namespace/generated/contract.d.ts b/test/integration/test/sql-orm-client/fixtures/scope-namespace/generated/contract.d.ts index ffa93ebd92ef..bc89d2cf7208 100644 --- a/test/integration/test/sql-orm-client/fixtures/scope-namespace/generated/contract.d.ts +++ b/test/integration/test/sql-orm-client/fixtures/scope-namespace/generated/contract.d.ts @@ -42,6 +42,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; @@ -91,6 +95,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; @@ -100,6 +108,14 @@ export type AggregateTypes = { readonly output: 'pg/time-temporal@1'; readonly nullable: true; }; + readonly 'pg/timestamp-date@1': { + readonly output: 'pg/timestamp-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-instant@1': { + readonly output: 'pg/timestamp-instant@1'; + readonly nullable: true; + }; readonly 'pg/timestamp-string@1': { readonly output: 'pg/timestamp-string@1'; readonly nullable: true; @@ -151,6 +167,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; @@ -160,6 +180,14 @@ export type AggregateTypes = { readonly output: 'pg/time-temporal@1'; readonly nullable: true; }; + readonly 'pg/timestamp-date@1': { + readonly output: 'pg/timestamp-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-instant@1': { + readonly output: 'pg/timestamp-instant@1'; + readonly nullable: true; + }; readonly 'pg/timestamp-string@1': { readonly output: 'pg/timestamp-string@1'; readonly nullable: true; @@ -203,6 +231,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; diff --git a/test/integration/test/sql-orm-client/fixtures/self-relations/generated/contract.d.ts b/test/integration/test/sql-orm-client/fixtures/self-relations/generated/contract.d.ts index 1611d36bb3ba..2657cb8f7381 100644 --- a/test/integration/test/sql-orm-client/fixtures/self-relations/generated/contract.d.ts +++ b/test/integration/test/sql-orm-client/fixtures/self-relations/generated/contract.d.ts @@ -42,6 +42,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; @@ -91,6 +95,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; @@ -100,6 +108,14 @@ export type AggregateTypes = { readonly output: 'pg/time-temporal@1'; readonly nullable: true; }; + readonly 'pg/timestamp-date@1': { + readonly output: 'pg/timestamp-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-instant@1': { + readonly output: 'pg/timestamp-instant@1'; + readonly nullable: true; + }; readonly 'pg/timestamp-string@1': { readonly output: 'pg/timestamp-string@1'; readonly nullable: true; @@ -151,6 +167,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; @@ -160,6 +180,14 @@ export type AggregateTypes = { readonly output: 'pg/time-temporal@1'; readonly nullable: true; }; + readonly 'pg/timestamp-date@1': { + readonly output: 'pg/timestamp-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-instant@1': { + readonly output: 'pg/timestamp-instant@1'; + readonly nullable: true; + }; readonly 'pg/timestamp-string@1': { readonly output: 'pg/timestamp-string@1'; readonly nullable: true; @@ -203,6 +231,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; diff --git a/test/integration/test/sql-orm-client/fixtures/soft-delete/generated/contract.d.ts b/test/integration/test/sql-orm-client/fixtures/soft-delete/generated/contract.d.ts index 64b6249f01d3..62f3fe8bdef7 100644 --- a/test/integration/test/sql-orm-client/fixtures/soft-delete/generated/contract.d.ts +++ b/test/integration/test/sql-orm-client/fixtures/soft-delete/generated/contract.d.ts @@ -43,6 +43,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; @@ -92,6 +96,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; @@ -101,6 +109,14 @@ export type AggregateTypes = { readonly output: 'pg/time-temporal@1'; readonly nullable: true; }; + readonly 'pg/timestamp-date@1': { + readonly output: 'pg/timestamp-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-instant@1': { + readonly output: 'pg/timestamp-instant@1'; + readonly nullable: true; + }; readonly 'pg/timestamp-string@1': { readonly output: 'pg/timestamp-string@1'; readonly nullable: true; @@ -152,6 +168,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; @@ -161,6 +181,14 @@ export type AggregateTypes = { readonly output: 'pg/time-temporal@1'; readonly nullable: true; }; + readonly 'pg/timestamp-date@1': { + readonly output: 'pg/timestamp-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-instant@1': { + readonly output: 'pg/timestamp-instant@1'; + readonly nullable: true; + }; readonly 'pg/timestamp-string@1': { readonly output: 'pg/timestamp-string@1'; readonly nullable: true; @@ -204,6 +232,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; diff --git a/test/integration/test/sql-orm-client/include-codec-canonical-json.test.ts b/test/integration/test/sql-orm-client/include-codec-canonical-json.test.ts index 457e405300ca..d1a77550a4b7 100644 --- a/test/integration/test/sql-orm-client/include-codec-canonical-json.test.ts +++ b/test/integration/test/sql-orm-client/include-codec-canonical-json.test.ts @@ -2,7 +2,7 @@ import { byteaColumn, int4Column, int8Column, - intervalColumn, + intervalPartsColumn, numericColumn, } from '@internal/adapter-postgres/column-types'; import postgresAdapter from '@internal/adapter-postgres/runtime'; @@ -43,7 +43,7 @@ const Reading = model('Reading', { padded: field.column(numericColumn(10, 2)).optional(), counter: field.column(int8Column).optional(), payload: field.column(byteaColumn).optional(), - elapsed: field.column(intervalColumn()).optional(), + elapsed: field.column(intervalPartsColumn()).optional(), embedding: field.column(vector(3)).optional(), }, }).sql({ table: 'canonical_readings' }); diff --git a/test/integration/test/temporal-defaults/_fixture-aliases/generated/contract.d.ts b/test/integration/test/temporal-defaults/_fixture-aliases/generated/contract.d.ts index c9d5de5e4971..7f140d7dc8a0 100644 --- a/test/integration/test/temporal-defaults/_fixture-aliases/generated/contract.d.ts +++ b/test/integration/test/temporal-defaults/_fixture-aliases/generated/contract.d.ts @@ -43,6 +43,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; @@ -92,6 +96,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; @@ -101,6 +109,14 @@ export type AggregateTypes = { readonly output: 'pg/time-temporal@1'; readonly nullable: true; }; + readonly 'pg/timestamp-date@1': { + readonly output: 'pg/timestamp-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-instant@1': { + readonly output: 'pg/timestamp-instant@1'; + readonly nullable: true; + }; readonly 'pg/timestamp-string@1': { readonly output: 'pg/timestamp-string@1'; readonly nullable: true; @@ -152,6 +168,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; @@ -161,6 +181,14 @@ export type AggregateTypes = { readonly output: 'pg/time-temporal@1'; readonly nullable: true; }; + readonly 'pg/timestamp-date@1': { + readonly output: 'pg/timestamp-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-instant@1': { + readonly output: 'pg/timestamp-instant@1'; + readonly nullable: true; + }; readonly 'pg/timestamp-string@1': { readonly output: 'pg/timestamp-string@1'; readonly nullable: true; @@ -204,6 +232,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; diff --git a/test/integration/test/temporal-defaults/_fixture-attributes/contract.prisma b/test/integration/test/temporal-defaults/_fixture-attributes/contract.prisma index 0905389c7ff3..7ee8ec4e8baa 100644 --- a/test/integration/test/temporal-defaults/_fixture-attributes/contract.prisma +++ b/test/integration/test/temporal-defaults/_fixture-attributes/contract.prisma @@ -14,15 +14,15 @@ model Clock { timetz0 Timetz(0) @onCreate(now()) @onUpdate(now()) timetz3 Timetz(3) @onCreate(now()) @onUpdate(now()) timetz6 Timetz(6) @onCreate(now()) @onUpdate(now()) - timestamp0 Timestamp(0) @onCreate(now()) @onUpdate(now()) - timestamp3 Timestamp(3) @onCreate(now()) @onUpdate(now()) - timestamp6 Timestamp(6) @onCreate(now()) @onUpdate(now()) + timestamp0 TimestampTemporal(0) @onCreate(now()) @onUpdate(now()) + timestamp3 TimestampTemporal(3) @onCreate(now()) @onUpdate(now()) + timestamp6 TimestampTemporal(6) @onCreate(now()) @onUpdate(now()) timestampString0 TimestampString(0) @onCreate(now()) @onUpdate(now()) timestampString3 TimestampString(3) @onCreate(now()) @onUpdate(now()) timestampString6 TimestampString(6) @onCreate(now()) @onUpdate(now()) - timestamptz0 Timestamptz(0) @onCreate(now()) @onUpdate(now()) - timestamptz3 Timestamptz(3) @onCreate(now()) @onUpdate(now()) - timestamptz6 Timestamptz(6) @onCreate(now()) @onUpdate(now()) + timestamptz0 TimestamptzTemporal(0) @onCreate(now()) @onUpdate(now()) + timestamptz3 TimestamptzTemporal(3) @onCreate(now()) @onUpdate(now()) + timestamptz6 TimestamptzTemporal(6) @onCreate(now()) @onUpdate(now()) timestamptzString0 TimestamptzString(0) @onCreate(now()) @onUpdate(now()) timestamptzString3 TimestamptzString(3) @onCreate(now()) @onUpdate(now()) timestamptzString6 TimestamptzString(6) @onCreate(now()) @onUpdate(now()) diff --git a/test/integration/test/temporal-defaults/_fixture-attributes/generated/contract.d.ts b/test/integration/test/temporal-defaults/_fixture-attributes/generated/contract.d.ts index e476373c4fac..186a16dccec4 100644 --- a/test/integration/test/temporal-defaults/_fixture-attributes/generated/contract.d.ts +++ b/test/integration/test/temporal-defaults/_fixture-attributes/generated/contract.d.ts @@ -43,6 +43,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; @@ -92,6 +96,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; @@ -101,6 +109,14 @@ export type AggregateTypes = { readonly output: 'pg/time-temporal@1'; readonly nullable: true; }; + readonly 'pg/timestamp-date@1': { + readonly output: 'pg/timestamp-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-instant@1': { + readonly output: 'pg/timestamp-instant@1'; + readonly nullable: true; + }; readonly 'pg/timestamp-string@1': { readonly output: 'pg/timestamp-string@1'; readonly nullable: true; @@ -152,6 +168,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; @@ -161,6 +181,14 @@ export type AggregateTypes = { readonly output: 'pg/time-temporal@1'; readonly nullable: true; }; + readonly 'pg/timestamp-date@1': { + readonly output: 'pg/timestamp-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-instant@1': { + readonly output: 'pg/timestamp-instant@1'; + readonly nullable: true; + }; readonly 'pg/timestamp-string@1': { readonly output: 'pg/timestamp-string@1'; readonly nullable: true; @@ -204,6 +232,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; diff --git a/test/integration/test/temporal-defaults/_fixture-now/generated/contract.d.ts b/test/integration/test/temporal-defaults/_fixture-now/generated/contract.d.ts index e476373c4fac..186a16dccec4 100644 --- a/test/integration/test/temporal-defaults/_fixture-now/generated/contract.d.ts +++ b/test/integration/test/temporal-defaults/_fixture-now/generated/contract.d.ts @@ -43,6 +43,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; @@ -92,6 +96,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; @@ -101,6 +109,14 @@ export type AggregateTypes = { readonly output: 'pg/time-temporal@1'; readonly nullable: true; }; + readonly 'pg/timestamp-date@1': { + readonly output: 'pg/timestamp-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-instant@1': { + readonly output: 'pg/timestamp-instant@1'; + readonly nullable: true; + }; readonly 'pg/timestamp-string@1': { readonly output: 'pg/timestamp-string@1'; readonly nullable: true; @@ -152,6 +168,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; @@ -161,6 +181,14 @@ export type AggregateTypes = { readonly output: 'pg/time-temporal@1'; readonly nullable: true; }; + readonly 'pg/timestamp-date@1': { + readonly output: 'pg/timestamp-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-instant@1': { + readonly output: 'pg/timestamp-instant@1'; + readonly nullable: true; + }; readonly 'pg/timestamp-string@1': { readonly output: 'pg/timestamp-string@1'; readonly nullable: true; @@ -204,6 +232,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; diff --git a/test/integration/test/temporal-defaults/_fixture-timestamp/generated/contract.d.ts b/test/integration/test/temporal-defaults/_fixture-timestamp/generated/contract.d.ts index b1278162199d..207db1337b24 100644 --- a/test/integration/test/temporal-defaults/_fixture-timestamp/generated/contract.d.ts +++ b/test/integration/test/temporal-defaults/_fixture-timestamp/generated/contract.d.ts @@ -43,6 +43,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; @@ -92,6 +96,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; @@ -101,6 +109,14 @@ export type AggregateTypes = { readonly output: 'pg/time-temporal@1'; readonly nullable: true; }; + readonly 'pg/timestamp-date@1': { + readonly output: 'pg/timestamp-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-instant@1': { + readonly output: 'pg/timestamp-instant@1'; + readonly nullable: true; + }; readonly 'pg/timestamp-string@1': { readonly output: 'pg/timestamp-string@1'; readonly nullable: true; @@ -152,6 +168,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; @@ -161,6 +181,14 @@ export type AggregateTypes = { readonly output: 'pg/time-temporal@1'; readonly nullable: true; }; + readonly 'pg/timestamp-date@1': { + readonly output: 'pg/timestamp-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-instant@1': { + readonly output: 'pg/timestamp-instant@1'; + readonly nullable: true; + }; readonly 'pg/timestamp-string@1': { readonly output: 'pg/timestamp-string@1'; readonly nullable: true; @@ -204,6 +232,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; diff --git a/test/integration/test/temporal-defaults/_fixture/generated/contract.d.ts b/test/integration/test/temporal-defaults/_fixture/generated/contract.d.ts index c9d5de5e4971..7f140d7dc8a0 100644 --- a/test/integration/test/temporal-defaults/_fixture/generated/contract.d.ts +++ b/test/integration/test/temporal-defaults/_fixture/generated/contract.d.ts @@ -43,6 +43,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; @@ -92,6 +96,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; @@ -101,6 +109,14 @@ export type AggregateTypes = { readonly output: 'pg/time-temporal@1'; readonly nullable: true; }; + readonly 'pg/timestamp-date@1': { + readonly output: 'pg/timestamp-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-instant@1': { + readonly output: 'pg/timestamp-instant@1'; + readonly nullable: true; + }; readonly 'pg/timestamp-string@1': { readonly output: 'pg/timestamp-string@1'; readonly nullable: true; @@ -152,6 +168,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; @@ -161,6 +181,14 @@ export type AggregateTypes = { readonly output: 'pg/time-temporal@1'; readonly nullable: true; }; + readonly 'pg/timestamp-date@1': { + readonly output: 'pg/timestamp-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-instant@1': { + readonly output: 'pg/timestamp-instant@1'; + readonly nullable: true; + }; readonly 'pg/timestamp-string@1': { readonly output: 'pg/timestamp-string@1'; readonly nullable: true; @@ -204,6 +232,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; diff --git a/test/integration/test/upgrade-instructions/explicit-date-time-types/detection.test.ts b/test/integration/test/upgrade-instructions/explicit-date-time-types/detection.test.ts new file mode 100644 index 000000000000..c772734a236c --- /dev/null +++ b/test/integration/test/upgrade-instructions/explicit-date-time-types/detection.test.ts @@ -0,0 +1,148 @@ +import { readFileSync } from 'node:fs'; +import { fileURLToPath } from 'node:url'; +import { dirname, join } from 'pathe'; +import { describe, expect, it } from 'vitest'; + +/** The published package an application imports from, kept out of literal import text so the import-root lint reads this file as test data. */ +const published = ['@prisma', 'orm-postgres'].join('/'); +const here = dirname(fileURLToPath(import.meta.url)); +const repoRoot = join(here, '../../../../..'); + +function patternsOf(audience: string, id: string): readonly RegExp[] { + const guide = readFileSync( + join( + repoRoot, + 'upgrade-instructions/pending/explicit-date-time-types', + audience, + 'instructions.md', + ), + 'utf-8', + ); + const block = guide.split(/\n {2}- id: /).find((entry) => entry.startsWith(`${id}\n`)) ?? ''; + return [...block.matchAll(/^ {8}- '(.*)'$/gm)].map( + ([, pattern]) => new RegExp((pattern ?? '').replaceAll("''", "'")), + ); +} + +describe.each(['app', 'extension'])('the %s explicit date and time type detection', (audience) => { + const matches = (id: string, content: string) => + patternsOf(audience, id).some((pattern) => pattern.test(content)); + + it('has a pattern for each change it detects', () => { + for (const id of [ + 'psl-temporal-type-renames', + 'ts-temporal-type-helpers-renamed', + 'interval-with-precision-typed-as-parts', + 'branded-time-types-removed', + ]) { + expect(patternsOf(audience, id).length).toBeGreaterThan(0); + } + }); + + it('matches a PSL field or alias written with a renamed type, and nothing else', () => { + const psl = (line: string) => matches('psl-temporal-type-renames', `model A {\n${line}\n}`); + expect({ + timestamptz: psl(' at Timestamptz(3)'), + timestamp: psl(' local Timestamp?'), + interval: psl(' span Interval[]'), + alias: psl('types {\n Stamp = Timestamp(3)\n}'), + prisma7NativeType: psl(' at DateTime @db.Timestamptz(3)'), + prisma7Timestamp: psl(' at DateTime @db.Timestamp'), + newName: psl(' at TimestamptzTemporal(3)'), + stringType: psl(' at TimestampString(3)'), + tag: psl(' at TimestamptzTemporal @default(timestamptz`2024-01-01T00:00:00Z`)'), + }).toEqual({ + timestamptz: true, + timestamp: true, + interval: true, + alias: true, + prisma7NativeType: false, + prisma7Timestamp: false, + newName: false, + stringType: false, + tag: false, + }); + }); + + it('matches the renamed TypeScript helpers only', () => { + const ts = (content: string) => matches('ts-temporal-type-helpers-renamed', content); + expect({ + helper: ts('field.column(type.Timestamptz(3))'), + interval: ts('field.column(type.Interval())'), + columnHelper: ts('field.column(intervalColumn(6))'), + renamedHelper: ts('field.column(type.TimestamptzTemporal(3))'), + renamedColumnHelper: ts('field.column(intervalPartsColumn(6))'), + }).toEqual({ + helper: true, + interval: true, + columnHelper: true, + renamedHelper: false, + renamedColumnHelper: false, + }); + }); + + it('matches an import of a removed branded type, in either type import form', () => { + const branded = (content: string) => matches('branded-time-types-removed', content); + const interval = (content: string) => + matches('interval-with-precision-typed-as-parts', content); + const snapshotImport = `import type {\n CodecTypes as PgTypes,\n Interval,\n Time,\n Timestamp,\n Timestamptz,\n} from '${published}/target/codec-types';`; + expect({ + snapshot: branded(snapshotImport), + inlineType: branded(`import { type Time } from '${published}/target/codec-types';`), + interval: interval(`import type { Interval } from '${published}/contract-builder';`), + inlineInterval: interval( + `import { field, type Interval } from '${published}/contract-builder';`, + ), + keptName: branded(`import type { TimestampString } from '${published}/target/codec-types';`), + otherPackage: branded("import { Timestamp } from 'bson';"), + }).toEqual({ + snapshot: true, + inlineType: true, + interval: true, + inlineInterval: true, + keptName: false, + otherPackage: false, + }); + }); +}); + +describe.each(['app', 'extension'])('the %s temporal-spec detection', (audience) => { + const matches = (content: string) => + patternsOf(audience, 'temporal-types-from-temporal-spec').some((pattern) => + pattern.test(content), + ); + + it('matches a type test that compares with a global Temporal type, until it imports Temporal itself from temporal-spec', () => { + expect({ + global: matches( + "import { expectTypeOf } from 'vitest';\n\nexpectTypeOf().toEqualTypeOf();", + ), + valueForm: matches('expectTypeOf(row.at).toEqualTypeOf();'), + importsTemporalSpec: matches( + "import type { Temporal } from 'temporal-spec';\n\nexpectTypeOf().toEqualTypeOf();", + ), + importsAfterUse: matches( + "expectTypeOf().toEqualTypeOf();\nimport type { Temporal } from 'temporal-spec';", + ), + noComparison: matches('const at: Temporal.Instant = Temporal.Now.instant();'), + inlineTypeImport: matches( + "import { type Temporal } from 'temporal-spec';\n\nexpectTypeOf().toEqualTypeOf();", + ), + aliasedImport: matches( + "import type { Temporal as TemporalTypes } from 'temporal-spec';\n\nexpectTypeOf().toEqualTypeOf();", + ), + aliasedImportCompared: matches( + "import type { Temporal as TemporalTypes } from 'temporal-spec';\n\nexpectTypeOf().toEqualTypeOf();", + ), + }).toEqual({ + global: true, + valueForm: true, + importsTemporalSpec: false, + importsAfterUse: false, + noComparison: false, + inlineTypeImport: false, + aliasedImport: true, + aliasedImportCompared: false, + }); + }); +}); diff --git a/test/integration/test/value-objects/fixtures/generated/sql-contract.d.ts b/test/integration/test/value-objects/fixtures/generated/sql-contract.d.ts index b7875c82f73d..2e317e716bdc 100644 --- a/test/integration/test/value-objects/fixtures/generated/sql-contract.d.ts +++ b/test/integration/test/value-objects/fixtures/generated/sql-contract.d.ts @@ -6,12 +6,8 @@ import type { Bit, Char, CodecTypes as PgTypes, - Interval, JsonValue, Numeric, - Time, - Timestamp, - Timestamptz, Timetz, VarBit, Varchar, diff --git a/upgrade-instructions/pending/explicit-date-time-types/app/instructions.md b/upgrade-instructions/pending/explicit-date-time-types/app/instructions.md new file mode 100644 index 000000000000..60d4b1d58904 --- /dev/null +++ b/upgrade-instructions/pending/explicit-date-time-types/app/instructions.md @@ -0,0 +1,107 @@ +--- +changes: + - id: psl-temporal-type-renames + summary: | + On PostgreSQL, the PSL types `Timestamptz`, `Timestamp` and `Interval` are renamed to `TimestamptzTemporal`, `TimestampTemporal` and `IntervalParts`, and the old names are errors. Rename each use in your `.prisma` files, keeping its arguments: `Timestamptz(3)` becomes `TimestamptzTemporal(3)`. Each new name reads the same value with the same codec, so the contract and its hashes do not change. `@db.Timestamp` and `@db.Timestamptz` in a Prisma 7 schema are not affected. + detection: + glob: "**/*.prisma" + matches: + - '(?().toEqualTypeOf(); +``` + +If the test file also creates `Temporal` values, import the type under another name, such as `import type { Temporal as TemporalTypes } from 'temporal-spec'`, so the runtime `Temporal` stays in scope. diff --git a/upgrade-instructions/pending/explicit-date-time-types/extension/instructions.md b/upgrade-instructions/pending/explicit-date-time-types/extension/instructions.md new file mode 100644 index 000000000000..37e0b0d5d796 --- /dev/null +++ b/upgrade-instructions/pending/explicit-date-time-types/extension/instructions.md @@ -0,0 +1,107 @@ +--- +changes: + - id: psl-temporal-type-renames + summary: | + On PostgreSQL, the PSL types `Timestamptz`, `Timestamp` and `Interval` are renamed to `TimestamptzTemporal`, `TimestampTemporal` and `IntervalParts`, and the old names are errors. Rename each use in your `.prisma` files, keeping its arguments: `Timestamptz(3)` becomes `TimestamptzTemporal(3)`. Each new name reads the same value with the same codec, so the contract and its hashes do not change. `@db.Timestamp` and `@db.Timestamptz` in a Prisma 7 schema are not affected. + detection: + glob: "**/*.prisma" + matches: + - '(?().toEqualTypeOf(); +``` + +If the test file also creates `Temporal` values, import the type under another name, such as `import type { Temporal as TemporalTypes } from 'temporal-spec'`, so the runtime `Temporal` stays in scope. From 9fc86d9deb60330815f1af7740b80a61b3eab210 Mon Sep 17 00:00:00 2001 From: willbot Date: Fri, 23 Oct 2026 14:42:08 +0200 Subject: [PATCH 079/118] TML-3719: An adopted Prisma 7 schema fills @default(now()) on create in UTC, as Prisma 7 did (#30839) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ## Linked issue Fixes TML-3719. Part of the Convenience date and time types project (TML-3709); stacked on the design PR, prisma/orm#30834. ## Summary ```prisma // A Prisma 7 schema, read by prisma7Schema('./schema.prisma') model Post { id Int @id @default(autoincrement()) createdAt DateTime @default(now()) } ``` On a PostgreSQL server whose `TimeZone` is `Asia/Tokyo`: | Row written by | Before | After | | --- | --- | --- | | `create` through Prisma 8 | the database fills `createdAt` with Tokyo wall-clock time | the client writes the current time in UTC | | an `INSERT` from outside Prisma | the database default | the database default | **The decision: the Prisma 7 schema reader reads `@default(now())` as the database default `now()` plus the client generator `@onCreate(now())`**, the same as Prisma 8's own `@default(now()) @onCreate(now())`. ### Why Prisma 7 handled `@default(now())` in two places. Prisma Client filled the value itself on `create`, in UTC, and the migration also wrote `DEFAULT CURRENT_TIMESTAMP` for writes from outside Prisma. The reader kept only the database default. Prisma 7's `DateTime` is a `timestamp(3)` column, which has no time zone, so the database fills it with the server's local time. On any server not set to UTC, every row Prisma 8 created stored a different time from the one Prisma 7 would have stored. ### What the reader does now - `@default(now())` keeps the database default and adds the `now` generator on create. The generator is chosen by the column's codec through the generator registry, so it writes the value that codec expects whichever codec the reader picks. With `@updatedAt`, the field keeps the database default and both generators. - Two schemas Prisma 7 itself refuses are now refused here too, in the field's own terms: - `now()` on a field that is not a date or time, such as `String @default(now())` or `DateTime @db.Text @default(now())`; - a function default other than `dbgenerated` on a list field, such as `DateTime[] @default(now())`. Before, it loaded with a generator that wrote one value into an array column. ### What changes for an adopted project The column and its database default are unchanged, so the storage hash and the database are unchanged: no migration and no `db sign`. The execution hash changes, because the contract gains a generator, so the project re-emits and commits its contract. `examples/prisma7-adoption` is re-emitted. ADR 275's section on the Prisma 7 reader, the `contract-prisma7` README and the Postgres README describe the new behaviour. ## Testing performed - `@internal/sql-contract-prisma7`: tests, typecheck, lint and coverage. New unit tests cover `DateTime`, an optional field, `@db.Timestamptz(6)`, `@db.Date`, `@db.Time(0)`, `@updatedAt`, the refusals and list fields; each failed before its fix. - Journey `prisma7-dates-without-temporal.e2e.test.ts`: on a session 9 hours ahead of UTC, a create stores UTC and a raw `INSERT` gets the database default. It failed before the fix, with the create 9 hours off. - Integration tests in `test/prisma7-source` and the Prisma 7 print round trip; the `contract-print` and `prisma7-source` journeys; `examples/prisma7-adoption` tests. - `pnpm fixtures:check`, `pnpm check:upgrade-coverage --mode pr`. ## Skill update n/a. The upgrade instruction `upgrade-instructions/pending/prisma7-now-default-on-create/` tells users to re-emit and commit the contract; extension authors need no change. ## Checklist - [x] All commits are signed off. - [x] Scoped to one logical concern. - [x] Tests are updated. - [x] The PR title is in `TML-NNNN: ` form. - [x] The Skill update section is filled in. ## Notes for the reviewer The tools PR, prisma/orm#30858, merges this branch in and changes which codec the reader picks for `DateTime`; the generator follows the codec, so nothing here changes for it. **Alternatives considered:** - **Keep only the database default and document the time zone difference.** Every adopted project on a server not set to UTC keeps a regression from Prisma 7. - **Write `now() AT TIME ZONE 'UTC'` as the database default of a `timestamp` column.** It changes the database default of every existing Prisma 7 column, which shows up as a migration, and it still differs from Prisma 7, whose client filled the value. Agent: thales-11 🤖 Generated with [Claude Code](https://claude.com/claude-code) --------- Signed-off-by: willbot Signed-off-by: Will Madden Co-authored-by: Claude Opus 5.5 --- ...type alias is expanded into each column.md | 2 +- .../generated/prisma8/contract.d.ts | 10 +- .../generated/prisma8/contract.json | 13 ++- .../2-authoring/contract-prisma7/README.md | 4 +- .../contract-prisma7/src/defaults.ts | 104 ++++++++++++++---- .../contract-prisma7/src/interpreter.ts | 1 + .../fixtures/defaults/expected-contract.json | 29 +++++ .../test/list-field-function-defaults.test.ts | 33 ++++++ .../test/now-default-generators.test.ts | 86 +++++++++++++++ .../3-targets/postgres/9-facade/README.md | 2 +- ...prisma7-dates-without-temporal.e2e.test.ts | 74 +++++++++++-- .../app/instructions.md | 32 ++++++ .../extension/instructions.md | 3 + 13 files changed, 355 insertions(+), 38 deletions(-) create mode 100644 packages/2-sql-family/2-authoring/contract-prisma7/test/list-field-function-defaults.test.ts create mode 100644 packages/2-sql-family/2-authoring/contract-prisma7/test/now-default-generators.test.ts create mode 100644 upgrade-instructions/pending/prisma7-now-default-on-create/app/instructions.md create mode 100644 upgrade-instructions/pending/prisma7-now-default-on-create/extension/instructions.md diff --git a/docs/architecture docs/adrs/ADR 275 - Client generators are field attributes, and a type alias is expanded into each column.md b/docs/architecture docs/adrs/ADR 275 - Client generators are field attributes, and a type alias is expanded into each column.md index 121327f5b86a..14c463c50d1c 100644 --- a/docs/architecture docs/adrs/ADR 275 - Client generators are field attributes, and a type alias is expanded into each column.md +++ b/docs/architecture docs/adrs/ADR 275 - Client generators are field attributes, and a type alias is expanded into each column.md @@ -172,7 +172,7 @@ Mongo has no `@default` attribute and no `types {}` block today, its default fun ### The Prisma 7 reader -The Prisma 7 reader builds the contract directly and writes no aliases. It reads `@updatedAt` as `now` resolved for both phases, and reads Prisma 7's client functions in `@default` (`uuid()`, `cuid()`, `nanoid()`, `ulid()`) as `@onCreate`, both through the generator function registry. `@default(now())` stays a database default. The Postgres binding's own `updatedAtGenerator` goes. The contracts it builds from schemas Prisma 7 accepts do not change; since TML-3389 it refuses a client function whose arguments are out of range, fractional or named, and a column too short for what the generator writes. +The Prisma 7 reader builds the contract directly and writes no aliases. It reads `@updatedAt` as `now` resolved for both phases, and reads Prisma 7's client functions in `@default` (`uuid()`, `cuid()`, `nanoid()`, `ulid()`) as `@onCreate`, both through the generator function registry. `@default(now())` becomes both the database default `now()` and the `now` generator on create, resolved from the column's codec, because Prisma 7's client filled the value itself on create and the database default covered writes from outside Prisma. A `now()` default on a column no `now` generator fills is refused, as Prisma 7 refuses it. The Postgres binding has no `updatedAtGenerator` of its own. Apart from the `now` generator that `@default(now())` adds, the contracts it builds from schemas Prisma 7 accepts are the ones it built before this decision. It refuses a client function whose arguments are out of range, fractional or named, and a column too short for what the generator writes. ### `contract print` diff --git a/examples/prisma7-adoption/generated/prisma8/contract.d.ts b/examples/prisma7-adoption/generated/prisma8/contract.d.ts index e21866441001..9e5405fea891 100644 --- a/examples/prisma7-adoption/generated/prisma8/contract.d.ts +++ b/examples/prisma7-adoption/generated/prisma8/contract.d.ts @@ -25,7 +25,7 @@ import type { export type StorageHash = StorageHashBase<'9268d6eef6e976bf77ca87083b15cd8eb247c9bf7fe4d2929f56a23bfa36d9ec'>; export type ExecutionHash = - ExecutionHashBase<'c9a992ceb890a2d2efac06bbccfae061acd6f137268bd6d54afbd4f71a72ec45'>; + ExecutionHashBase<'5d2a944d02782b8bcf53d311c7dbe23d41a990f019100658761ce68af6aa0230'>; export type ProfileHash = ProfileHashBase<'3916f444a8a17ad749191acf9e08dad97d1a327b88c2f1d45d12f240296aa8b2'>; @@ -864,6 +864,14 @@ type ContractBase = Omit< readonly executionHash: ExecutionHash; readonly mutations: { readonly defaults: readonly [ + { + readonly onCreate: { readonly id: 'timestampStringNow'; readonly kind: 'generator' }; + readonly ref: { + readonly entry: 'User'; + readonly field: 'createdAt'; + readonly namespace: 'public'; + }; + }, { readonly onCreate: { readonly id: 'timestampStringNow'; readonly kind: 'generator' }; readonly onUpdate: { readonly id: 'timestampStringNow'; readonly kind: 'generator' }; diff --git a/examples/prisma7-adoption/generated/prisma8/contract.json b/examples/prisma7-adoption/generated/prisma8/contract.json index 26d6f8b9cbee..fa004ad22aff 100644 --- a/examples/prisma7-adoption/generated/prisma8/contract.json +++ b/examples/prisma7-adoption/generated/prisma8/contract.json @@ -657,9 +657,20 @@ "storageHash": "9268d6eef6e976bf77ca87083b15cd8eb247c9bf7fe4d2929f56a23bfa36d9ec" }, "execution": { - "executionHash": "c9a992ceb890a2d2efac06bbccfae061acd6f137268bd6d54afbd4f71a72ec45", + "executionHash": "5d2a944d02782b8bcf53d311c7dbe23d41a990f019100658761ce68af6aa0230", "mutations": { "defaults": [ + { + "onCreate": { + "id": "timestampStringNow", + "kind": "generator" + }, + "ref": { + "entry": "User", + "field": "createdAt", + "namespace": "public" + } + }, { "onCreate": { "id": "timestampStringNow", diff --git a/packages/2-sql-family/2-authoring/contract-prisma7/README.md b/packages/2-sql-family/2-authoring/contract-prisma7/README.md index d7b518477bc9..9cd49166dfa6 100644 --- a/packages/2-sql-family/2-authoring/contract-prisma7/README.md +++ b/packages/2-sql-family/2-authoring/contract-prisma7/README.md @@ -32,7 +32,7 @@ The package itself is target-neutral: everything specific to a database comes fr | `enum` | Native enum type named by `@@map` or the enum name, members in order, each member's value its `@map` or its name; placed in the enum's `@@schema`. | | `@@schema("s")` | Namespace `s`; without it, the target's default namespace. | | Scalars and `@db.*` | The target binding's type map (`typeMap`), for example `DateTime` to `timestamp(3)` and `Json` to `jsonb`; lists are nullable array columns with no derived element check. | -| `@default(...)` | Column defaults through the target's default function registry, literals, list literals, enum members; `uuid`, `ulid`, `nanoid`, `cuid` are client generators on create, resolved for the column's codec through the stack's generator function registry, as Prisma 8 PSL resolves `@onCreate(fn())` (`cuid` maps to `cuid(2)`); `dbgenerated("")` is a raw SQL default and `dbgenerated()` means no column default. | +| `@default(...)` | Column defaults through the target's default function registry, literals, list literals, enum members; `now()` is both the column default and the `now` generator on create, resolved for the column's codec, as Prisma 8 PSL resolves `@default(now()) @onCreate(now())`, because Prisma 7's client filled it too; `uuid`, `ulid`, `nanoid`, `cuid` are client generators on create, resolved for the column's codec through the stack's generator function registry, as Prisma 8 PSL resolves `@onCreate(fn())` (`cuid` maps to `cuid(2)`); `dbgenerated("")` is a raw SQL default and `dbgenerated()` means no column default. On a list field, any function other than `dbgenerated` is `PSL.PRISMA7_UNKNOWN_DEFAULT`; write a list literal instead. | | `@updatedAt` | `now()` resolved for the column's codec through the stack's generator function registry, on create and on update, as Prisma 8 PSL resolves `@onCreate(now()) @onUpdate(now())`; Postgres binds it to every `DateTime` column type, and a `@db.Time` or `@db.Timetz` generator receives the column's precision. Beside `@default(now())`, the column default is kept too. On a field that is not a `DateTime`, on a list, or with a native type that is not a date or time, an error. | | `@id`, `@@id` | Primary key, named by `map` or `{table}_pkey` cut to 63 bytes as Prisma 7 cuts it; the name is stated only where it differs from the target's own (see [Constraint names](#constraint-names)). | | `@unique`, `@@unique`, `@@index` | Indexes named `{table}_{columns}_key` and `{table}_{columns}_idx` cut to 63 bytes as Prisma 7 cuts them, `map` overriding, `type` mapped. | @@ -81,7 +81,7 @@ Explicit relations keep their fields, references, and actions; an omitted `onDel ## Defaults, generators, `@updatedAt`, and indexes -`@default(autoincrement())` and `@default(now())` become column defaults through the target's default function registry (`context.controlMutationDefaults`), as do the ORM-side generators `uuid()`, `uuid(4)`, `uuid(7)`, `ulid()`, `nanoid()`, `nanoid(n)`, `cuid()`, and `cuid(2)`, which become execution generators on create with no column default. `cuid()` maps to `cuid2` because Prisma 7's `cuid()` is cuid v1, which Prisma 8 does not ship; the column type is the same and the ids are opaque, so existing rows keep working beside new ones. Every other call's arguments are bound through the target's signatures, as Prisma 8 PSL binds them: an argument the function does not take (`uuid(5)`, `cuid(1)`, `nanoid(1)`, `nanoid(1000)`, `nanoid(8.5)`) or a named argument (`nanoid(size: 8)`) is `PSL.PRISMA7_UNKNOWN_DEFAULT` at the argument, with the function's message, such as `Field "User.id": uuid: version must be 4 or 7, got 5`. `@default(dbgenerated(""))` becomes a raw SQL default carrying the string as written, without going through the registry; `@default(dbgenerated())` with no argument means the column has no default, on required, optional, and list fields alike. Literals of every scalar, list literals, and enum members (the member's mapped storage value) become literal defaults; a number literal is carried as the value the column codec reads: the number itself when the codec reads a JSON number, otherwise what the codec decodes from the literal exactly as written, so `Decimal` and `BigInt` defaults keep every digit and trailing zero (`"1.50"`, `"9007199254740993"`); a number with a fraction on an `Int` or `BigInt` field, or one the codec reads neither way, is `PSL.PRISMA7_UNKNOWN_DEFAULT`, as Prisma 7 rejects it, and the message says an `Int` or `BigInt` default must be a whole number; `Json` literals are parsed, and `Bytes` and `DateTime` literals are read through the column type's tag, as the current schema language reads them, and stored as values: a `Bytes` default's base64 as the hex body of `bytea`, stored as base64, and a `DateTime` default as the body that keeps what it means on the column: the written date on `date`, the written wall-clock time without its offset on `timestamp` and `time`, the time with its offset on `timetz`, and the written instant on `timestamptz`, stored in UTC (`2024-01-02T01:04:05Z`). A value with more fraction digits than its column holds (three on a plain `DateTime`, which is `timestamp(3)`) is `PSL.PRISMA7_UNKNOWN_DEFAULT`, and the message names the value PostgreSQL stores instead, rounded as PostgreSQL rounds it, to write in its place. Second 60, which PostgreSQL carries into the next minute, is refused with the tag's reason. A `Bytes[]` or `DateTime[]` list default stores each element the same way. `@updatedAt` becomes an ORM-side "now" generator on create and update; the target binding picks the generator from the column's codec and type parameters (`updatedAtGenerator`), and on Postgres every date and time column type has one, each writing UTC text (`timestampStringNow` for a zoneless `timestamp(3)`, `dateStringNow` for `@db.Date`, and so on); the `@db.Time(p)` and `@db.Timetz(p)` generators get `p` as their `precision` param and round down to it, so Postgres never rounds a time up to `24:00:00`. `@updatedAt` on a field that is not a `DateTime`, on a list field, or on a `DateTime` field whose native type is not a date or time (`@db.Text`), is `PSL.PRISMA7_UPDATED_AT_FIELD_INVALID`, as Prisma 7 refuses all three. `@updatedAt` beside `@default(now())` keeps the column default as well, so writes outside the ORM still get a value. A generator or `@updatedAt` on an optional field gives a nullable column: a value the client leaves out is generated, and an explicit `null` is stored as `null`, as in Prisma 7. List columns decline the element-not-null check Prisma 8 would otherwise derive, because Prisma 7 creates none. +`@default(autoincrement())` and `@default(now())` become column defaults through the target's default function registry (`context.controlMutationDefaults`). `@default(now())` also becomes the `now` generator on create, resolved for the column's codec through the generator function registry, as Prisma 8 PSL resolves `@default(now()) @onCreate(now())`: Prisma 7's client filled the value itself and wrote the column default for other writers, so a create through Prisma 8 writes UTC whatever the session time zone, and an `INSERT` from outside Prisma still gets the column default. On a field that is not a `DateTime`, or on a `DateTime` field whose native type is not a date or time (`@db.Text`), it is `PSL.PRISMA7_UNKNOWN_DEFAULT`, as Prisma 7 refuses both. The ORM-side generators `uuid()`, `uuid(4)`, `uuid(7)`, `ulid()`, `nanoid()`, `nanoid(n)`, `cuid()`, and `cuid(2)` become execution generators on create with no column default. `cuid()` maps to `cuid2` because Prisma 7's `cuid()` is cuid v1, which Prisma 8 does not ship; the column type is the same and the ids are opaque, so existing rows keep working beside new ones. Every other call's arguments are bound through the target's signatures, as Prisma 8 PSL binds them: an argument the function does not take (`uuid(5)`, `cuid(1)`, `nanoid(1)`, `nanoid(1000)`, `nanoid(8.5)`) or a named argument (`nanoid(size: 8)`) is `PSL.PRISMA7_UNKNOWN_DEFAULT` at the argument, with the function's message, such as `Field "User.id": uuid: version must be 4 or 7, got 5`. `@default(dbgenerated(""))` becomes a raw SQL default carrying the string as written, without going through the registry; `@default(dbgenerated())` with no argument means the column has no default, on required, optional, and list fields alike. Literals of every scalar, list literals, and enum members (the member's mapped storage value) become literal defaults; a number literal is carried as the value the column codec reads: the number itself when the codec reads a JSON number, otherwise what the codec decodes from the literal exactly as written, so `Decimal` and `BigInt` defaults keep every digit and trailing zero (`"1.50"`, `"9007199254740993"`); a number with a fraction on an `Int` or `BigInt` field, or one the codec reads neither way, is `PSL.PRISMA7_UNKNOWN_DEFAULT`, as Prisma 7 rejects it, and the message says an `Int` or `BigInt` default must be a whole number; `Json` literals are parsed, and `Bytes` and `DateTime` literals are read through the column type's tag, as the current schema language reads them, and stored as values: a `Bytes` default's base64 as the hex body of `bytea`, stored as base64, and a `DateTime` default as the body that keeps what it means on the column: the written date on `date`, the written wall-clock time without its offset on `timestamp` and `time`, the time with its offset on `timetz`, and the written instant on `timestamptz`, stored in UTC (`2024-01-02T01:04:05Z`). A value with more fraction digits than its column holds (three on a plain `DateTime`, which is `timestamp(3)`) is `PSL.PRISMA7_UNKNOWN_DEFAULT`, and the message names the value PostgreSQL stores instead, rounded as PostgreSQL rounds it, to write in its place. Second 60, which PostgreSQL carries into the next minute, is refused with the tag's reason. A `Bytes[]` or `DateTime[]` list default stores each element the same way. `@updatedAt` becomes an ORM-side "now" generator on create and update; the generator function registry picks the generator from the column's codec and type parameters, and on Postgres every date and time column type has one, each writing UTC text (`timestampStringNow` for a zoneless `timestamp(3)`, `dateStringNow` for `@db.Date`, and so on); the `@db.Time(p)` and `@db.Timetz(p)` generators get `p` as their `precision` param and round down to it, so Postgres never rounds a time up to `24:00:00`. `@updatedAt` on a field that is not a `DateTime`, on a list field, or on a `DateTime` field whose native type is not a date or time (`@db.Text`), is `PSL.PRISMA7_UPDATED_AT_FIELD_INVALID`, as Prisma 7 refuses all three. `@updatedAt` beside `@default(now())` keeps the column default as well, so writes outside the ORM still get a value. A generator or `@updatedAt` on an optional field gives a nullable column: a value the client leaves out is generated, and an explicit `null` is stored as `null`, as in Prisma 7. List columns decline the element-not-null check Prisma 8 would otherwise derive, because Prisma 7 creates none. `@unique` and `@@unique` become unique indexes named `{table}_{columns}_key` and `@@index` becomes an index named `{table}_{columns}_idx`, `map` overriding either (`name` on `@@unique` is the client-side name and is ignored). A generated name is cut the way Prisma 7 cuts it to fit PostgreSQL's 63-byte identifier limit: the `{table}_{columns}` part is shortened to 63 bytes minus the suffix, on a character boundary, and the suffix stays whole (`AVeryLongModelNameThatKeepsGoingAndGoingForever_aVeryLongCo_idx`). The same rule cuts an implicit junction's table name (no suffix) and its `_B_index`. `db verify` compares indexes by name, so the contract must carry the name Prisma 7 created. `type: Hash` and the other Prisma 8 index types map through; field arguments such as `sort` and `length`, and `ops`, are `PSL.PRISMA7_INDEX_ARGUMENT_UNSUPPORTED` because Prisma 8 indexes carry none. diff --git a/packages/2-sql-family/2-authoring/contract-prisma7/src/defaults.ts b/packages/2-sql-family/2-authoring/contract-prisma7/src/defaults.ts index bdd05ff454f4..1136b8ed624b 100644 --- a/packages/2-sql-family/2-authoring/contract-prisma7/src/defaults.ts +++ b/packages/2-sql-family/2-authoring/contract-prisma7/src/defaults.ts @@ -37,6 +37,7 @@ import type { AuthoredColumnDefault, AuthoredColumnDefaultLiteralValue, } from '@internal/sql-contract-ts/contract-builder'; +import { InternalError } from '@internal/utils/internal-error'; import { prisma7Diagnostic } from './diagnostics'; import type { Prisma7LiteralDefaultForm } from './target-binding'; @@ -53,6 +54,8 @@ export interface LowerPrisma7DefaultInput { readonly onlyDbgenerated: boolean; readonly codecId: string; readonly typeParams: Readonly> | undefined; + /** The name after `@db.` of the field's native type attribute, if it has one. */ + readonly nativeTypeName: string | undefined; readonly codecLookup: CodecLookupWithDescriptors; readonly literalForm: Prisma7LiteralDefaultForm | undefined; /** Storage value per member name when the field is typed by a Prisma 7 enum. */ @@ -357,6 +360,18 @@ function lowerFunction( span, ); } + if (input.field.list) { + const written = `@default(${printSyntax(expression.syntax).trim()})`; + input.diagnostics.push( + prisma7Diagnostic( + 'PSL.PRISMA7_UNKNOWN_DEFAULT', + `${label}: ${written} cannot fill a list field. Write a list literal, such as @default([]), or remove ${written}.`, + input.sourceId, + span, + ), + ); + return undefined; + } if (entry === undefined) { if (generator === undefined) { return unknown(`function "${fn}()" cannot fill a column of codec "${input.codecId}".`, span); @@ -371,29 +386,8 @@ function lowerFunction( label, ); if (args === undefined) return undefined; - const resolution = input.generatorFunctions.resolve( - { name: fn, args }, - { - codecId: input.codecId, - typeParams: input.typeParams, - length: sqlCodecColumnLength(input.dataTypes, input.codecId, input.typeParams), - }, - ); - if (resolution.ok) return { storage: undefined, onCreate: resolution.generator }; - if (resolution.reason === 'refused') { - const written = - input.attribute.args.find((arg) => arg.kind === 'positional')?.value.trim() ?? `${fn}()`; - input.diagnostics.push( - prisma7Diagnostic( - 'PSL.PRISMA7_GENERATOR_REFUSED', - `${label}: @default(${written}) cannot fill its column: ${resolution.message}`, - input.sourceId, - span, - ), - ); - return undefined; - } - return unknown(`function "${fn}()" cannot fill a column of codec "${input.codecId}".`, span); + const onCreate = resolveGenerator(fn, args, input, label, unknown); + return onCreate === undefined ? undefined : { storage: undefined, onCreate }; } const args = boundArguments( defaultFunctionCall(fn, entry, input.dataTypes), @@ -422,7 +416,69 @@ function lowerFunction( ); return undefined; } - return { storage: lowered.value.defaultValue, onCreate: undefined }; + const storage = lowered.value.defaultValue; + if (fn !== 'now') return { storage, onCreate: undefined }; + if (!input.generatorFunctions.functionsFor(input.codecId).some(({ name }) => name === fn)) { + input.diagnostics.push( + prisma7Diagnostic( + 'PSL.PRISMA7_UNKNOWN_DEFAULT', + `${label}: ${nowOnAColumnThatIsNotADateOrTime(input)}`, + input.sourceId, + span, + ), + ); + return undefined; + } + const onCreate = resolveGenerator(fn, {}, input, label, unknown); + return onCreate === undefined ? undefined : { storage, onCreate }; +} + +/** Why Prisma 7 refuses `@default(now())` on a field whose column is not a date or time. */ +function nowOnAColumnThatIsNotADateOrTime(input: LowerPrisma7DefaultInput): string { + if (input.field.typeName !== 'DateTime') { + return `@default(now()) needs a DateTime field, but the field has type "${input.field.typeName}". Prisma 7 refuses this schema too. Remove @default(now()), or change the field's type to DateTime.`; + } + const nativeType = input.nativeTypeName; + if (nativeType === undefined) { + throw new InternalError( + `The Prisma 7 target binding has no "now" generator for codec "${input.codecId}", which the DateTime scalar maps to.`, + ); + } + return `@default(now()) needs a date or time column, but "@db.${nativeType}" makes it a column that is not a date or time. Prisma 7 refuses this schema too: "Native type ${nativeType} is not compatible with declared field type DateTime". Remove @default(now()), or use a date or time native type such as @db.Timestamp.`; +} + +/** The generator a client function resolves to for the field's column, or `undefined` after reporting why it cannot fill it. */ +function resolveGenerator( + fn: string, + args: Readonly>, + input: LowerPrisma7DefaultInput, + label: string, + unknown: (reason: string, span: PslSpan) => undefined, +): ExecutionMutationDefaultValue | undefined { + const span = input.attribute.span; + const resolution = input.generatorFunctions.resolve( + { name: fn, args }, + { + codecId: input.codecId, + typeParams: input.typeParams, + length: sqlCodecColumnLength(input.dataTypes, input.codecId, input.typeParams), + }, + ); + if (resolution.ok) return resolution.generator; + if (resolution.reason === 'refused') { + const written = + input.attribute.args.find((arg) => arg.kind === 'positional')?.value.trim() ?? `${fn}()`; + input.diagnostics.push( + prisma7Diagnostic( + 'PSL.PRISMA7_GENERATOR_REFUSED', + `${label}: @default(${written}) cannot fill its column: ${resolution.message}`, + input.sourceId, + span, + ), + ); + return undefined; + } + return unknown(`function "${fn}()" cannot fill a column of codec "${input.codecId}".`, span); } /** diff --git a/packages/2-sql-family/2-authoring/contract-prisma7/src/interpreter.ts b/packages/2-sql-family/2-authoring/contract-prisma7/src/interpreter.ts index a694e9493b58..66dd400b897f 100644 --- a/packages/2-sql-family/2-authoring/contract-prisma7/src/interpreter.ts +++ b/packages/2-sql-family/2-authoring/contract-prisma7/src/interpreter.ts @@ -1385,6 +1385,7 @@ function readField(args: ReadFieldArgs): void { onlyDbgenerated: unsupported, codecId: resolved.descriptor.codecId, typeParams: resolved.descriptor.typeParams, + nativeTypeName: nativeType?.name, codecLookup: input.codecLookup, dataTypes: input.dataTypes, literalForm: binding.literalDefaultForm({ diff --git a/packages/2-sql-family/2-authoring/contract-prisma7/test/fixtures/defaults/expected-contract.json b/packages/2-sql-family/2-authoring/contract-prisma7/test/fixtures/defaults/expected-contract.json index ac9dd9ae0dc5..73f82f6c2cbc 100644 --- a/packages/2-sql-family/2-authoring/contract-prisma7/test/fixtures/defaults/expected-contract.json +++ b/packages/2-sql-family/2-authoring/contract-prisma7/test/fixtures/defaults/expected-contract.json @@ -249,6 +249,35 @@ "model": "Defaults" } }, + "execution": { + "executionHash": "f8bf85545419f6c09639ae1eb8943cdf47ce226be55151e46512fdcd7f0fcdc1", + "mutations": { + "defaults": [ + { + "ref": { + "namespace": "public", + "entry": "Defaults", + "field": "createdAt" + }, + "onCreate": { + "kind": "generator", + "id": "timestampStringNow" + } + }, + { + "ref": { + "namespace": "public", + "entry": "Defaults", + "field": "createdAtTz" + }, + "onCreate": { + "kind": "generator", + "id": "timestamptzStringNow" + } + } + ] + } + }, "extensions": {}, "capabilities": {}, "profileHash": "3916f444a8a17ad749191acf9e08dad97d1a327b88c2f1d45d12f240296aa8b2", diff --git a/packages/2-sql-family/2-authoring/contract-prisma7/test/list-field-function-defaults.test.ts b/packages/2-sql-family/2-authoring/contract-prisma7/test/list-field-function-defaults.test.ts new file mode 100644 index 000000000000..cc6467af2512 --- /dev/null +++ b/packages/2-sql-family/2-authoring/contract-prisma7/test/list-field-function-defaults.test.ts @@ -0,0 +1,33 @@ +import { describe, expect, it } from 'vitest'; +import { interpretSchemaTextResult } from './support'; + +function diagnosticsFor(field: string): readonly string[] { + const result = interpretSchemaTextResult(`datasource db { + provider = "postgresql" +} + +model Post { + id Int @id + ${field} +} +`); + return result.ok ? [] : result.failure.diagnostics.map(({ message }) => message); +} + +describe('a function default on a list field', () => { + it.each([ + ['counts Int[] @default(autoincrement())', 'counts', 'autoincrement()'], + ['ids String[] @default(uuid())', 'ids', 'uuid()'], + ['ids String[] @default(uuid(7))', 'ids', 'uuid(7)'], + ['at DateTime[] @default(now())', 'at', 'now()'], + ['days DateTime[] @default(now()) @db.Date', 'days', 'now()'], + ])('is refused: %s', (field, name, call) => { + expect(diagnosticsFor(field)).toEqual([ + `Field "Post.${name}": @default(${call}) cannot fill a list field. Write a list literal, such as @default([]), or remove @default(${call}).`, + ]); + }); + + it('is read for dbgenerated', () => { + expect(diagnosticsFor('labels String[] @default(dbgenerated("\'{}\'::text[]"))')).toEqual([]); + }); +}); diff --git a/packages/2-sql-family/2-authoring/contract-prisma7/test/now-default-generators.test.ts b/packages/2-sql-family/2-authoring/contract-prisma7/test/now-default-generators.test.ts new file mode 100644 index 000000000000..26f10f95ff08 --- /dev/null +++ b/packages/2-sql-family/2-authoring/contract-prisma7/test/now-default-generators.test.ts @@ -0,0 +1,86 @@ +import { describe, expect, it } from 'vitest'; +import { interpretSchemaText, interpretSchemaTextResult, serializedTable } from './support'; + +const now = (id: string, params?: Record) => + params === undefined ? { kind: 'generator', id } : { kind: 'generator', id, params }; +const ref = (field: string) => ({ namespace: 'public', entry: 'Post', field }); +const databaseNow = { kind: 'function', expression: 'now()' }; + +describe('@default(now()) on Postgres', () => { + it('lowers to the database default now() and the now generator on create, resolved by codec', () => { + const contract = interpretSchemaText(`datasource db { + provider = "postgresql" +} + +model Post { + id Int @id + createdAt DateTime @default(now()) + optionalAt DateTime? @default(now()) + zonedAt DateTime @default(now()) @db.Timestamptz(6) + day DateTime @default(now()) @db.Date + clock DateTime @default(now()) @db.Time(0) +} +`); + const { columns } = serializedTable(contract, 'Post'); + expect( + Object.fromEntries( + ['createdAt', 'optionalAt', 'zonedAt', 'day', 'clock'].map((name) => [ + name, + columns[name]?.['default'], + ]), + ), + ).toEqual({ + createdAt: databaseNow, + optionalAt: databaseNow, + zonedAt: databaseNow, + day: databaseNow, + clock: databaseNow, + }); + expect(contract.execution?.mutations.defaults).toEqual([ + { ref: ref('clock'), onCreate: now('timeStringNow', { precision: 0 }) }, + { ref: ref('createdAt'), onCreate: now('timestampStringNow') }, + { ref: ref('day'), onCreate: now('dateStringNow') }, + { ref: ref('optionalAt'), onCreate: now('timestampStringNow') }, + { ref: ref('zonedAt'), onCreate: now('timestamptzStringNow') }, + ]); + }); + + it('keeps the database default and writes now on create and on update with @updatedAt', () => { + const contract = interpretSchemaText(`datasource db { + provider = "postgresql" +} + +model Post { + id Int @id + updatedAt DateTime @default(now()) @updatedAt +} +`); + expect(serializedTable(contract, 'Post').columns['updatedAt']?.['default']).toEqual( + databaseNow, + ); + expect(contract.execution?.mutations.defaults).toEqual([ + { + ref: ref('updatedAt'), + onCreate: now('timestampStringNow'), + onUpdate: now('timestampStringNow'), + }, + ]); + }); + + it('is refused on a field that is not a DateTime, as Prisma 7 refuses it', () => { + const result = interpretSchemaTextResult(`datasource db { + provider = "postgresql" +} + +model Post { + id Int @id + title String @default(now()) + stamp DateTime @default(now()) @db.Text +} +`); + expect(result.ok ? [] : result.failure.diagnostics.map(({ message }) => message)).toEqual([ + 'Field "Post.title": @default(now()) needs a DateTime field, but the field has type "String". Prisma 7 refuses this schema too. Remove @default(now()), or change the field\'s type to DateTime.', + 'Field "Post.stamp": @default(now()) needs a date or time column, but "@db.Text" makes it a column that is not a date or time. Prisma 7 refuses this schema too: "Native type Text is not compatible with declared field type DateTime". Remove @default(now()), or use a date or time native type such as @db.Timestamp.', + ]); + }); +}); diff --git a/packages/3-targets/postgres/9-facade/README.md b/packages/3-targets/postgres/9-facade/README.md index a74a1b5dc9f0..c09ed607df83 100644 --- a/packages/3-targets/postgres/9-facade/README.md +++ b/packages/3-targets/postgres/9-facade/README.md @@ -172,7 +172,7 @@ These changes are not specific to a Prisma 7 schema. They apply to any Postgres - Introspection reads defaults, check constraints, index predicates, and policy expressions in a session pinned to `TimeZone = UTC`, `DateStyle = ISO, MDY`, `IntervalStyle = postgres`, and `bytea_output = hex`, restoring the caller's settings afterwards. The text it reads is therefore the same whatever the server, the role, or the caller set. One consequence: a contract inferred earlier from a server outside UTC, holding a `timestamptz` constant inside check or index text, shows that text once as a difference; the new text is stable from then on. On a server set to `bytea_output = escape`, the pin also rewrites a `bytea` constant inside check, index or policy text, from `'\001'::bytea` to `'\x01'::bytea`, so a contract inferred earlier from such a server shows that text once as a difference too. - Migration planning renders a list literal default with its cast (`ARRAY['1', '-2']::int8[]`), the same rendering the adapter uses for column DDL. -**The ORM's `now` for `timestamp` columns is UTC wall-clock time, whatever the host's time zone.** A database `now()` default uses the session time zone instead, so a column filled by the ORM and a column filled by a database default agree only in a UTC session. Prisma 7 writes UTC into `timestamp(3)`, so a Prisma 7 database stays consistent with what Prisma 8's generator writes. +**The ORM's `now` for `timestamp` columns is UTC wall-clock time, whatever the host's time zone.** A database `now()` default uses the session time zone instead, so a column filled by the ORM and a column filled by a database default agree only in a UTC session. Prisma 7 writes UTC into `timestamp(3)`, so a Prisma 7 database stays consistent with what Prisma 8's generator writes. A Prisma 7 schema read with `prisma7Schema` fills a `@default(now())` column with the ORM's `now` on create, as Prisma 7's client did, and keeps the database default for writes from outside Prisma. ### `@internal/postgres/runtime` diff --git a/test/integration/test/cli-journeys/prisma7-dates-without-temporal.e2e.test.ts b/test/integration/test/cli-journeys/prisma7-dates-without-temporal.e2e.test.ts index 3b14b58375d6..280ef17bc37f 100644 --- a/test/integration/test/cli-journeys/prisma7-dates-without-temporal.e2e.test.ts +++ b/test/integration/test/cli-journeys/prisma7-dates-without-temporal.e2e.test.ts @@ -89,12 +89,18 @@ try { await new Promise((resolve) => setTimeout(resolve, 20)); const updated = await events.where({ id: created.id }).update(writes.update); const readAfterUpdate = await events.where({ id: created.id }).first(); + const [session] = await db.query( + db.raw.sql\`SELECT EXTRACT(TIMEZONE FROM CURRENT_TIMESTAMP)::int AS "utcOffsetSeconds"\` + .returnsRow({ utcOffsetSeconds: 'pg/int4@1' }) + .build(), + ); process.stdout.write( JSON.stringify({ created, readAfterCreate, updated, readAfterUpdate, + sessionUtcOffsetSeconds: session.utcOffsetSeconds, temporalPolyfill: [...loaded].filter((url) => url.includes('temporal-polyfill')), timezoneOffset: new Date().getTimezoneOffset(), }), @@ -104,6 +110,9 @@ try { } `; +const TOKYO_UTC_OFFSET_SECONDS = 9 * 60 * 60; +const TOKYO_UTC_OFFSET_MILLIS = TOKYO_UTC_OFFSET_SECONDS * 1000; + const DATE_COLUMN_CODECS = { createdAt: 'pg/timestamp-string@1', updatedAt: 'pg/timestamp-string@1', @@ -134,6 +143,7 @@ interface AppResult { readonly readAfterCreate: EventRow; readonly updated: EventRow; readonly readAfterUpdate: EventRow; + readonly sessionUtcOffsetSeconds: number; readonly temporalPolyfill: readonly string[]; readonly timezoneOffset: number; } @@ -152,14 +162,12 @@ function expectNow(timestampText: string, before: number, after: number): void { expect(millis, timestampText).toBeLessThanOrEqual(after + 1_000); } -async function runApp(ctx: JourneyContext, connectionString: string, writes: unknown) { - const sessionUtcOffset = await withClient(connectionString, async (client) => { - const { rows } = await client.query<{ seconds: number }>( - 'SELECT EXTRACT(TIMEZONE FROM CURRENT_TIMESTAMP)::int AS seconds', - ); - return rows[0]?.seconds; - }); - expect(sessionUtcOffset, 'createdAt is CURRENT_TIMESTAMP in the session time zone').toBe(0); +async function runApp( + ctx: JourneyContext, + connectionString: string, + writes: unknown, + expectedSessionUtcOffsetSeconds = 0, +) { const script = join(ctx.testDir, 'app.mjs'); writeFileSync(script, APP_SCRIPT, 'utf-8'); const before = Date.now(); @@ -176,6 +184,10 @@ async function runApp(ctx: JourneyContext, connectionString: string, writes: unk const result: AppResult = JSON.parse(run.stdout); expect(result.temporalPolyfill).toEqual([]); expect(result.timezoneOffset, 'the child runs at UTC+3').toBe(-180); + expect( + result.sessionUtcOffsetSeconds, + "the application's database session time zone, which a database now() default uses", + ).toBe(expectedSessionUtcOffsetSeconds); return { result, before, after }; } @@ -297,5 +309,51 @@ withTempDir(({ createTempDir }) => { timeouts.spinUpPpgDev * 2, ); }); + + describe('through prisma7Schema(...), on a session in Asia/Tokyo', () => { + const db = useDevDatabase({ + onReady: (cs) => withClient(cs, (client) => client.query(PRISMA7_SQL)), + }); + + it( + 'stores the current UTC time on a create, and a raw INSERT still gets the database default', + async () => { + const ctx = setupPrisma7Project(createTempDir, db.connectionString); + const emit = await runContractEmit(ctx); + expect(emit.exitCode, `contract emit\n${output(emit)}`).toBe(0); + const sign = await runDbSign(ctx); + expect(sign.exitCode, `db sign\n${output(sign)}`).toBe(0); + await withClient(db.connectionString, (client) => + client.query("SET TIME ZONE 'Asia/Tokyo'"), + ); + + const { result, before, after } = await runApp( + ctx, + db.connectionString, + { create: { title: 'a', ...WRITTEN }, update: { title: 'b' } }, + TOKYO_UTC_OFFSET_SECONDS, + ); + expectNow(result.created.createdAt, before, after); + expect(result.readAfterCreate).toEqual(result.created); + + const rawBefore = Date.now(); + const raw = await withClient(db.connectionString, (client) => + client.query<{ createdAt: string }>( + `INSERT INTO "Event" ("title", "updatedAt", "happensAt", "day", "opensAt") + VALUES ('raw', now(), now(), current_date, current_time) + RETURNING "createdAt"::text AS "createdAt"`, + ), + ); + const rawAfter = Date.now(); + const rawCreatedAt = raw.rows[0]?.createdAt ?? ''; + expectNow( + rawCreatedAt, + rawBefore + TOKYO_UTC_OFFSET_MILLIS, + rawAfter + TOKYO_UTC_OFFSET_MILLIS, + ); + }, + timeouts.spinUpPpgDev * 2, + ); + }); }); }); diff --git a/upgrade-instructions/pending/prisma7-now-default-on-create/app/instructions.md b/upgrade-instructions/pending/prisma7-now-default-on-create/app/instructions.md new file mode 100644 index 000000000000..9ae68850d25d --- /dev/null +++ b/upgrade-instructions/pending/prisma7-now-default-on-create/app/instructions.md @@ -0,0 +1,32 @@ +--- +changes: + - id: prisma7-schema-now-default-writes-on-create + summary: | + A contract from `prisma7Schema(...)` now reads Prisma 7's `@default(now())` on a date or time field as both the database default `now()` and the client generator `now` on create, as Prisma 8 PSL reads `@default(now()) @onCreate(now())`. A create through Prisma 8 now writes the current UTC time, as Prisma 7's client did, instead of leaving the column to the database's clock in the session time zone. Re-emit the contract with `prisma contract emit` and commit it. Only the execution hash changes, so the database needs no migration and no `db sign`. + detection: + glob: "**/prisma.config.{ts,mts,cts,js,mjs}" + matches: + - '\bprisma7Schema\s*\(' +--- + +# A Prisma 7 `@default(now())` is written by the client on create + +## `prisma7-schema-now-default-writes-on-create` + +Prisma 7's client filled a `@default(now())` field itself on create, with the current time in UTC, and also wrote `now()` as the column default for inserts from outside Prisma. A contract from `prisma7Schema(...)` used to keep only the column default, so a create through Prisma 8 got the database's clock. On a `timestamp` column, that is the wall-clock time of the session's time zone, which differs from what Prisma 7 wrote unless the session runs in UTC. + +The contract now keeps the column default and adds the `now` generator on create, chosen for the column's type: + +```prisma +// Prisma 7 schema, read by prisma7Schema('prisma/schema.prisma') +model Post { + id Int @id @default(autoincrement()) + createdAt DateTime @default(now()) +} +``` + +reads as the Prisma 8 PSL field `createdAt TimestampString(3) @default(now()) @onCreate(now())`. A create that supplies `createdAt` still stores the supplied value. An `INSERT` from outside Prisma still gets the column default. + +For each project whose `prisma.config.ts` uses `prisma7Schema(...)`, run `prisma contract emit` again and commit the new `contract.json` and `contract.d.ts`. If the schema has a `@default(now())` field, the generator's id is in the execution section of `contract.json`, so the execution hash changes. The storage hash does not change, and the database marker holds only the storage and profile hashes, so there is no migration to plan and no need to run `prisma db sign`. + +If the schema has no `@default(now())` field, the contract does not change and there is nothing to do. diff --git a/upgrade-instructions/pending/prisma7-now-default-on-create/extension/instructions.md b/upgrade-instructions/pending/prisma7-now-default-on-create/extension/instructions.md new file mode 100644 index 000000000000..8e1cc0ebe668 --- /dev/null +++ b/upgrade-instructions/pending/prisma7-now-default-on-create/extension/instructions.md @@ -0,0 +1,3 @@ +--- +changes: [] +--- From 86b11b603d16c7f5d1a32a73e29392deb809c91e Mon Sep 17 00:00:00 2001 From: willbot Date: Fri, 23 Oct 2026 14:54:34 +0200 Subject: [PATCH 080/118] Convenience type options are as: and storage:, and their TypeScript helpers take one object PostgreSQL's DateTime, Date, Time and Duration name their options as: (the application value: jsDate, temporal, string, parts) and storage: (the database type), instead of value: and column:. "Value" already means a data type's stored value, and "column" is a SQL word, while a convenience name must mean the same on every target. PSL, the Prisma 7 reader, the language server, docs, skills and the pending upgrade instruction use the new names, and a refused combination reads "cannot be X with as: Y". type.DateTime(...) and the other type-choice helpers take one object, such as type.DateTime({ as: 'string', precision: 6 }), typed with one overload per row of the type's table. A call that is not an object, or names an unknown key, is refused. Co-Authored-By: Claude Opus 5.5 Signed-off-by: willbot Signed-off-by: Will Madden --- docs/Serverless Deployment Guide.md | 2 +- ...type alias is expanded into each column.md | 2 +- docs/reference/error-reference.md | 4 +- docs/reference/scalar-types.md | 16 +-- gotchas.md | 2 +- .../1-core/framework-components/README.md | 2 +- .../src/exports/authoring.ts | 1 + .../src/shared/authoring-type-choice.ts | 47 ++++++- .../test/authoring-type-choice.test.ts | 52 ++++++-- .../test/type-choice-assembly.test.ts | 48 ++++--- .../test/authoring-arguments.test.ts | 4 +- .../test/authoring-arguments.value.test.ts | 8 +- .../test/binder-contributed-types.test.ts | 24 ++-- .../test/completion-provider.test.ts | 6 +- .../language-server/test/hover.test.ts | 16 +-- .../test/interpreter.type-choices.test.ts | 16 +-- .../test/type-choice-binding.test.ts | 2 +- .../test/interpreter.type-choices.test.ts | 52 ++++---- .../2-authoring/contract-ts/README.md | 2 +- .../src/authoring-helper-runtime.ts | 6 +- .../src/composed-authoring-helpers.ts | 121 +++++++++--------- .../test/helpers/type-choice-packs.ts | 19 +-- .../test/type-choice-helpers.test-d.ts | 51 ++++++-- .../test/type-choice-helpers.test.ts | 56 +++++++- .../1-target/src/core/type-constructors.ts | 58 +++++---- .../test/convenience-type-helpers.test.ts | 2 +- .../temporal-field-types.test-d.ts | 94 ++++++++------ skills/prisma-8/references/contract.md | 4 +- skills/prisma-8/references/debug.md | 2 +- .../parity/ts-psl-parity.real-packs.test.ts | 26 ++-- ...te-time-types-without-temporal.e2e.test.ts | 12 +- .../convenience-types.integration.test.ts | 41 +++--- .../app/instructions.md | 6 +- 33 files changed, 490 insertions(+), 314 deletions(-) diff --git a/docs/Serverless Deployment Guide.md b/docs/Serverless Deployment Guide.md index babd8406fdc7..0a8d8c21a69c 100644 --- a/docs/Serverless Deployment Guide.md +++ b/docs/Serverless Deployment Guide.md @@ -110,7 +110,7 @@ This goes in `.env`, not `.dev.vars`. `.dev.vars` is for runtime worker secrets; A serverless client at module scope; a connection from `connect` in each request; SQL, ORM and transactions on that connection; cursor streaming. The full file is `examples/prisma-8-cloudflare-worker/src/worker.ts`. -The samples use extensionless relative imports and `./contract.d`, which need `moduleResolution: "bundler"` in `tsconfig.json`; Worker projects built with wrangler use that setting, and so does the example. On a runtime without a built-in `Temporal`, which today includes Cloudflare Workers at the example's compatibility date and Node.js 24, reading a column whose type reads `Temporal` values, such as `TimestamptzTemporal` or `DateTime(value: temporal)`, needs a Temporal polyfill: add `temporal-polyfill` and import `temporal-polyfill/full/global` before the first query, as the example does at the top of `worker.ts` and `seed.ts`. +The samples use extensionless relative imports and `./contract.d`, which need `moduleResolution: "bundler"` in `tsconfig.json`; Worker projects built with wrangler use that setting, and so does the example. On a runtime without a built-in `Temporal`, which today includes Cloudflare Workers at the example's compatibility date and Node.js 24, reading a column whose type reads `Temporal` values, such as `TimestamptzTemporal` or `DateTime(as: temporal)`, needs a Temporal polyfill: add `temporal-polyfill` and import `temporal-polyfill/full/global` before the first query, as the example does at the top of `worker.ts` and `seed.ts`. #### Module scope diff --git a/docs/architecture docs/adrs/ADR 275 - Client generators are field attributes, and a type alias is expanded into each column.md b/docs/architecture docs/adrs/ADR 275 - Client generators are field attributes, and a type alias is expanded into each column.md index 4ad2527a0eb3..47ff4b3a4c03 100644 --- a/docs/architecture docs/adrs/ADR 275 - Client generators are field attributes, and a type alias is expanded into each column.md +++ b/docs/architecture docs/adrs/ADR 275 - Client generators are field attributes, and a type alias is expanded into each column.md @@ -5,7 +5,7 @@ **Builds on:** [ADR 158 — Execution mutation defaults](ADR%20158%20-%20Execution%20mutation%20defaults.md), [ADR 241 — Scalar types use the authoring type-constructor channel](ADR%20241%20-%20Scalar%20types%20use%20the%20authoring%20type-constructor%20channel.md), [ADR 269 — PSL mixins](ADR%20269%20-%20PSL%20mixins.md) **Supersedes:** the field-preset half of [ADR 170 — Pack-provided type constructors and field presets](ADR%20170%20-%20Pack-provided%20type%20constructors%20and%20field%20presets.md); [ADR 246 — Option arguments and select templates for authoring helpers](ADR%20246%20-%20Option%20arguments%20and%20select%20templates%20for%20authoring%20helpers.md), except its two omission rules **Amends:** [ADR 104](ADR%20104%20-%20PSL%20extension%20namespacing%20%26%20syntax.md), [ADR 158](ADR%20158%20-%20Execution%20mutation%20defaults.md), [ADR 169 — Declared applicability for mutation default generators](ADR%20169%20-%20Declared%20applicability%20for%20mutation%20default%20generators.md), [ADR 208 — Higher-order codecs for parameterized types](ADR%20208%20-%20Higher-order%20codecs%20for%20parameterized%20types.md), [ADR 212 — AST-bound codec resolution](ADR%20212%20-%20AST-bound%20codec%20resolution.md), [ADR 231](ADR%20231%20-%20Declarative%20attribute%20specifications.md), [ADR 241](ADR%20241%20-%20Scalar%20types%20use%20the%20authoring%20type-constructor%20channel.md), [ADR 249](ADR%20249%20-%20Central%20attribute-spec%20registry.md), [ADR 254](ADR%20254%20-%20Data%20types%20and%20casts.md) -**Amended by [ADR 279](ADR%20279%20-%20Convenience%20date%20and%20time%20types%20choose%20an%20explicit%20type%20from%20their%20arguments.md) (2026-10-22):** the contributed `temporal.*` aliases are built on the convenience type `DateTime`, and their `String` and `JsDate` variants are not needed, because `now()` picks its generator from the field's final codec and a user writes `DateTime(value: string) @onCreate(now())` for another kind of value. +**Amended by [ADR 279](ADR%20279%20-%20Convenience%20date%20and%20time%20types%20choose%20an%20explicit%20type%20from%20their%20arguments.md) (2026-10-22):** the contributed `temporal.*` aliases are built on the convenience type `DateTime`, and their `String` and `JsDate` variants are not needed, because `now()` picks its generator from the field's final codec and a user writes `DateTime(as: string) @onCreate(now())` for another kind of value. **Amended 2026-10-21:** the TypeScript `types` key is renamed `databaseTypes` and registers database types only; a contributed alias is used as `field.column(type.temporal.UpdatedAt())`; a contributed alias carries no database default; Mongo's contributed aliases are on `Date`; the Mongo TypeScript builder gets no contributed aliases yet. Each change is marked "Amended 2026-10-21" below. --- diff --git a/docs/reference/error-reference.md b/docs/reference/error-reference.md index 04fd59fdccd8..2f9c43702906 100644 --- a/docs/reference/error-reference.md +++ b/docs/reference/error-reference.md @@ -243,7 +243,7 @@ The migration-file CLI (`prisma migration`) received a flag it does not recognis ### CONTRACT.ARGUMENT_INVALID -A builder or helper on the contract-authoring surface is called with a bad argument: a composed authoring helper receives too many arguments or a malformed trailing options object, `field.sql({ id })` / `field.sql({ unique })` is used without a matching inline `.id(...)` / `.unique(...)` declaration, `model("Name", ...)` is called without a model definition, a nanoid ID generator is given a size outside 2–255, a TypeScript `type.*` type constructor receives an argument its data type's parameter schema refuses (meta: `helperPath`, `argumentIndex`; see `CONTRACT.TYPE_PARAMS_INVALID` for how PSL and the contract build report the same bound), or an authored index combines its cross-field parameters invalidly (fields and an expression together or neither, an expression without `name:`/`map:`, or `map:` combined with `name:`). Also raised by the Mongo TypeScript builder when two variants of a polymorphic base declare the same discriminator value (meta: `modelName`, `value`, `variants`, `reason: 'duplicate-discriminator-value'`). Also raised by the Mongo TypeScript builder for `.mongo({ field })` on a field of a value object, whose fields are stored under their own names (meta: `valueObjectName`, `fieldName`, `reason: 'value-object-field-stored-name'`). Also raised by the Mongo TypeScript builder for a model field stored as `__proto__`, which JavaScript reads as the prototype of an object rather than as a key (meta: `modelName`, `fieldName`, `reason: 'stored-name-unsupported'`); the Mongo PSL interpreter reports the same case at the field as `PSL_STORED_NAME_UNSUPPORTED`, and reading `contract.json` back refuses a field or embed relation stored as `__proto__`. Also raised when a contract targets SQLite and declares an expression or partial index: SQLite's namespace construction rejects `expression:`/`where:` because the target does not support them. Also raised when a column with a literal default has type parameters that its codec does not accept beyond its data type's parameters, which the build checks first with `CONTRACT.TYPE_PARAMS_INVALID` (meta: `modelName`, `fieldName`, `codecId`, `reason: 'type-params-invalid'`; the codec's error is the `cause`). Also raised by a SQL contract build for a relation's `index: ""` that names nothing on its table, matches more than one index, unique constraint or primary key of its table, by the `name` or `map` the source gave it or an index's stored name, names an object whose first columns are not the foreign key's columns, or names an index of a type whose body is rendered from its options rather than its columns, such as Postgres's `fullText` (meta: `reason: 'foreign-key-index-unresolved'`, `namespaceId`, `tableName`, `columns`, `index`); a PSL source reports it as `PSL_INVALID_RELATION_ATTRIBUTE` at the relation. Also raised when two identical indexes of one table are both named with `name:`, whose wire names the planner could not tell apart (meta: `tableName`, `indexes`). Also raised by `requireSqlExpression` when a TypeScript builder field that takes raw SQL (`index` `where` or `expression`, `check` `expression`, `fullTextIndex` `where`, a policy's `using` or `withCheck`) holds something other than a `sql` value at run time, which only JavaScript that is not type-checked can pass (an object that carries the `sql` value marker but whose `text` is not a string counts as something else): ``Index "post_user_active" where must be a sql`...` value.`` (meta: `what`, which names the object and the field: `Index "" where`, `Index "" expression`, `Check "" expression`, `Full-text index "" where`, `Policy "" using`, `Policy "" withCheck`; an index or check with no `name` or `map` is named by its model, as in `Index on "" where`, and a full-text index with neither by its fields in order, as in `Full-text index on fields "", "" where`). Also raised by `sqlTextOf` when a migration-file function that takes a SQL fragment gets something that is neither a string nor a `sql` value: ``createIndex "post_user_active" where must be a string or a sql`...` value.`` (meta: `what`, which names the function, the object and the argument: `fn expression`, `checkExpression "" expression`, `addCheckConstraint "" expression`, `createIndex "" expression`, `createIndex "" where`, `createRlsPolicy "" using`, `createRlsPolicy "" withCheck`, `alterColumnType "

"."" using`, `addColumnWithTemporaryDefault "
"."" temporaryDefault`, and on SQLite `addColumn "
"."" default`, `recreateTable "
"."" default` and `recreateTable "
" copyExpressions.`); this one is raised when the migration file runs, not while the contract is built. Otherwise raised while authoring/building the contract, before emit. Payload: varies per site. +A builder or helper on the contract-authoring surface is called with a bad argument: a composed authoring helper receives too many arguments or a malformed trailing options object, `field.sql({ id })` / `field.sql({ unique })` is used without a matching inline `.id(...)` / `.unique(...)` declaration, `model("Name", ...)` is called without a model definition, a nanoid ID generator is given a size outside 2–255, a TypeScript `type.*` type constructor receives an argument its data type's parameter schema refuses (meta: `helperPath`, `argumentIndex`; see `CONTRACT.TYPE_PARAMS_INVALID` for how PSL and the contract build report the same bound), or an authored index combines its cross-field parameters invalidly (fields and an expression together or neither, an expression without `name:`/`map:`, or `map:` combined with `name:`). A TypeScript `type.*` helper of a type choice, such as PostgreSQL's `type.DateTime`, takes its arguments as one object keyed by name, and raises it when given anything else (` takes its arguments as one object with the keys , such as ({
"."" using`, `addColumnWithTemporaryDefault "
"."" temporaryDefault`, and on SQLite `addColumn "
"."" default`, `recreateTable "
"."" default` and `recreateTable "
" copyExpressions.`); this one is raised when the migration file runs, not while the contract is built. Otherwise raised while authoring/building the contract, before emit. Payload: varies per site. ### CONTRACT.AGGREGATE_DESCRIPTOR_AMBIGUOUS @@ -543,7 +543,7 @@ Namespaces are declared (contract-level list or a model-level `namespace`) on a A composed pack's contribution is malformed or collides with another contribution; this is the extension-author-facing bucket. Covers: entity types colliding with reserved helper keys, duplicate entity kinds or index-type registrations, a registered entity kind with no `lowerEntityHandles` lowering, an invalid `indexTypes` shape, entries-slot collisions between a model attribute and a block entry kind, a model attribute that lowers to a malformed index, bad authoring-helper paths, a codec registered with an entity-ref arg but no `columnFromEntity` hook, and print-time contribution mismatches (a block keyword with no PSL block descriptor, or a descriptor whose discriminator disagrees with the block's kind). Raised during contract authoring/lowering and PSL printing. -A contributed type choice (`kind: 'typeChoice'`, such as PostgreSQL's `DateTime`) is refused when the stack is assembled, by the control stack and by the TypeScript builders, when its table cannot be resolved: `Type choice "" contributed by "" chooses "", which is not a type constructor any component registers beside it.`, a row whose combination or an option default the option does not list, two rows for one combination (`… has two rows for value: ; each combination chooses one type.`), a combination of defaults with no row (`… has no row for its defaults value: , so it cannot be written bare.`), a parameter default for an argument it does not pass on or that the chosen constructor does not take (`… gives a default for "" to "", which takes no argument of that name.`), or a passed-on argument of another data type than the chosen constructor takes. Payload: `packId`, `contribution`. +A contributed type choice (`kind: 'typeChoice'`, such as PostgreSQL's `DateTime`) is refused when the stack is assembled, by the control stack and by the TypeScript builders, when its table cannot be resolved: `Type choice "" contributed by "" chooses "", which is not a type constructor any component registers beside it.`, a row whose combination or an option default the option does not list, two rows for one combination (`… has two rows for as: ; each combination chooses one type.`), a combination of defaults with no row (`… has no row for its defaults as: , so it cannot be written bare.`), a parameter default for an argument it does not pass on or that the chosen constructor does not take (`… gives a default for "" to "", which takes no argument of that name.`), or a passed-on argument of another data type than the chosen constructor takes. Payload: `packId`, `contribution`. A contributed type alias (`kind: 'typeAlias'`, such as `temporal.CreatedAt`) is refused when the stack is assembled, by the control stack and by the TypeScript builders, so PSL and TypeScript refuse the same packs. Its type params may not reference an argument, because a type alias takes none: `Invalid authoring type alias "" contributed by descriptor "". output.type.typeParams. references an argument, but a type alias takes none. Write the value itself.` Each generator call must resolve for the alias's codec: `Type alias "" calls now() on create, which does not apply to its codec "".`, `…, which its codec "" refuses: `, or `…, but no component declares the generator function "".` Payload: `packId`, `contribution`, `reason`, `keyword`, `paramName`, `codecId`. diff --git a/docs/reference/scalar-types.md b/docs/reference/scalar-types.md index 66298c60a45e..7b7df47c0ed1 100644 --- a/docs/reference/scalar-types.md +++ b/docs/reference/scalar-types.md @@ -66,25 +66,25 @@ Two limitations to know about: These are the current names. The Postgres and SQLite rename project will change several of them to the target's own type names and update this table. -The date and time types have two layers ([ADR 279](../architecture%20docs/adrs/ADR%20279%20-%20Convenience%20date%20and%20time%20types%20choose%20an%20explicit%20type%20from%20their%20arguments.md)). Each explicit type in the table below names one column type and one kind of value. The convenience types `DateTime`, `Date`, `Time` and `Duration` are named for what a field means, and each becomes one explicit type, chosen by its optional `value` and `column` arguments; `precision` is passed on to the explicit type. The contract stores only the explicit type, so `DateTime(value: temporal)` and `TimestamptzTemporal` emit the same contract. +The date and time types have two layers ([ADR 279](../architecture%20docs/adrs/ADR%20279%20-%20Convenience%20date%20and%20time%20types%20choose%20an%20explicit%20type%20from%20their%20arguments.md)). Each explicit type in the table below names one column type and one kind of value. The convenience types `DateTime`, `Date`, `Time` and `Duration` are named for what a field means, and each becomes one explicit type, chosen by its optional `as` and `storage` arguments: `as` is the value the application reads, and `storage` is the database type that stores it; `precision` is passed on to the explicit type. The contract stores only the explicit type, so `DateTime(as: temporal)` and `TimestamptzTemporal` emit the same contract. -| Convenience type | `value` | `column` | Becomes | +| Convenience type | `as` | `storage` | Becomes | | --- | --- | --- | --- | -| `DateTime(value?, column?, precision?)` | `jsDate` (default) | `timestamptz` (default) | `TimestamptzJsDate`, precision 3 unless written | +| `DateTime(as?, storage?, precision?)` | `jsDate` (default) | `timestamptz` (default) | `TimestamptzJsDate`, precision 3 unless written | | | `temporal` | `timestamptz` | `TimestamptzTemporal` | | | `string` | `timestamptz` | `TimestamptzString` | | | `jsDate` | `timestamp` | `TimestampJsDate`, precision 3 unless written | | | `temporal` | `timestamp` | `TimestampInstant` | | | `string` | `timestamp` | `TimestampString` | -| `Date(value?, column?)` | `string` (default) | `date` (default) | `DateString` | +| `Date(as?, storage?)` | `string` (default) | `date` (default) | `DateString` | | | `temporal` | `date` | `DateTemporal` | -| `Time(value?, column?, precision?)` | `string` (default) | `time` (default) | `TimeString` | +| `Time(as?, storage?, precision?)` | `string` (default) | `time` (default) | `TimeString` | | | `temporal` | `time` | `TimeTemporal` | | | `string` | `timetz` | `Timetz` | -| `Duration(value?, column?, precision?)` | `string` (default) | `interval` (default) | `IntervalString` | +| `Duration(as?, storage?, precision?)` | `string` (default) | `interval` (default) | `IntervalString` | | | `parts` | `interval` | `IntervalParts` | -The arguments are written as bare names, by name or in this order: `DateTime(value: string, precision: 6)`, `DateTime(temporal, timestamp)`. A combination the table does not list, such as `Time(value: temporal, column: timetz)` or `Date(value: jsDate)`, is refused at the argument that makes it invalid, naming the values it accepts; so is a `precision` the chosen type does not take. No default needs a global `Temporal`. In a TypeScript contract the helpers take the same arguments in the same order, `type.DateTime('string', undefined, 6)`, and are typed as the explicit type they choose. `contract print` and `contract infer` write the explicit type. +The arguments are written as bare names, by name or in this order: `DateTime(as: string, precision: 6)`, `DateTime(temporal, timestamp)`. A combination the table does not list, such as `Time(as: temporal, storage: timetz)` or `Date(as: jsDate)`, is refused at the argument that makes it invalid, naming the values it accepts; so is a `precision` the chosen type does not take. No default needs a global `Temporal`. In a TypeScript contract the helpers take the same arguments as one object, `type.DateTime({ as: 'string', precision: 6 })`, and are typed as the explicit type they choose; a combination the table does not list, or an argument the chosen type does not take, does not compile. `contract print` and `contract infer` write the explicit type. | PSL name | TS helper | Codec id | Storage type | Application type | | --- | --- | --- | --- | --- | @@ -127,7 +127,7 @@ The arguments are written as bare names, by name or in this order: `DateTime(val A literal default of a date, time, interval or bytes type is written with the tag of its type, as in `@default(timestamptz`2024-01-01T00:00:00Z`)` or `@default(bytea`\x0102`)`; a quoted string is refused with a message that shows the tag. It is stored in the one form its data type stores, whichever codec the column uses and however the default was written, and a default with more fraction digits or more scale than its column holds is refused rather than rounded; [ADR 254](../architecture%20docs/adrs/ADR%20254%20-%20Data%20types%20and%20casts.md#date-and-time-types) states each form. -The tools that write a schema for you use the text date and time types. In a PSL schema, `prisma orm init` writes `createdAt TimestamptzString @default(now())` and `updatedAt temporal.updatedAtString()`. In a TypeScript contract, it writes `field.temporal.createdAtString()` and `field.temporal.updatedAtString()`. All of these are `TimestamptzString` columns. `prisma contract infer` and `prisma7Schema(...)` write `TimestampString(p)`, `TimestamptzString(p)`, `DateString` and `TimeString(p)`. Five types read and write `Temporal` values: `TimestamptzTemporal`, `TimestampTemporal`, `TimestampInstant`, `DateTemporal` and `TimeTemporal`, which the convenience types choose with `value: temporal`. Their TypeScript types come from `temporal-spec`, so a project needs no global `Temporal` declarations to type them. An application that uses them needs a global `Temporal`: on a runtime without one, such as Node.js 24, install it, for example with `import 'temporal-polyfill/full/global'`. +The tools that write a schema for you use the text date and time types. In a PSL schema, `prisma orm init` writes `createdAt TimestamptzString @default(now())` and `updatedAt temporal.updatedAtString()`. In a TypeScript contract, it writes `field.temporal.createdAtString()` and `field.temporal.updatedAtString()`. All of these are `TimestamptzString` columns. `prisma contract infer` and `prisma7Schema(...)` write `TimestampString(p)`, `TimestamptzString(p)`, `DateString` and `TimeString(p)`. Five types read and write `Temporal` values: `TimestamptzTemporal`, `TimestampTemporal`, `TimestampInstant`, `DateTemporal` and `TimeTemporal`, which the convenience types choose with `as: temporal`. Their TypeScript types come from `temporal-spec`, so a project needs no global `Temporal` declarations to type them. An application that uses them needs a global `Temporal`: on a runtime without one, such as Node.js 24, install it, for example with `import 'temporal-polyfill/full/global'`. ## SQLite diff --git a/gotchas.md b/gotchas.md index 9d97eac3145f..d490bf62e458 100644 --- a/gotchas.md +++ b/gotchas.md @@ -132,7 +132,7 @@ The same command with `--from 20260707T1005_init` (a migration directory name) s **Workaround.** `import 'temporal-polyfill/full/global'` before the client is created (the example did so in `src/db.ts` before the fix), or author the column with the `*String` presets to receive PostgreSQL's text. -**Resolved.** `prisma7Schema(...)` and `contract infer` now write `TimestampString(3)`, `TimestamptzString(p)`, `DateString` and `TimeString(p)` for these columns, so they read as PostgreSQL's text, `@updatedAt` writes UTC text, and the application needs no `Temporal`. A column written by hand as `TimestamptzTemporal(p)`, `TimestampTemporal(p)`, `DateTemporal` or `TimeTemporal(p)`, or as a convenience type with `value: temporal`, still reads as a `Temporal` value and still needs one; the convenience types `DateTime`, `Date` and `Time` need none at their defaults. +**Resolved.** `prisma7Schema(...)` and `contract infer` now write `TimestampString(3)`, `TimestamptzString(p)`, `DateString` and `TimeString(p)` for these columns, so they read as PostgreSQL's text, `@updatedAt` writes UTC text, and the application needs no `Temporal`. A column written by hand as `TimestamptzTemporal(p)`, `TimestampTemporal(p)`, `DateTemporal` or `TimeTemporal(p)`, or as a convenience type with `as: temporal`, still reads as a `Temporal` value and still needs one; the convenience types `DateTime`, `Date` and `Time` need none at their defaults. **Reproduction.** 1. `cd examples/prisma7-adoption && pnpm db:start`, then `pnpm v7:migrate && pnpm emit && pnpm sign && pnpm seed`. diff --git a/packages/1-framework/1-core/framework-components/README.md b/packages/1-framework/1-core/framework-components/README.md index 9a668f311464..2e6311f4eae7 100644 --- a/packages/1-framework/1-core/framework-components/README.md +++ b/packages/1-framework/1-core/framework-components/README.md @@ -7,7 +7,7 @@ Framework component types, authoring logic, control stack assembly, and emission ## What this package provides - **Component types** (`./components`): Base descriptor and instance interfaces for framework components (family, target, adapter, driver, extension), pack refs, and type renderer system -- **Authoring types** (`./authoring`): Declarative authoring contribution types, template resolution, and validation for type constructors and field presets. A type choice (`kind: 'typeChoice'`) chooses a type constructor of its namespace from its option arguments: `resolveAuthoringTypeChoice` resolves a call, `instantiateAuthoringTypeCall` instantiates a constructor or choice by name, and `assertTypeChoicesValid` refuses an unresolvable table at assembly (ADR 279). `findAuthoringTypeConstructorCall(namespace, output)` is the inverse of `instantiateAuthoringTypeConstructor`: the first call to a constructor in a namespace that produces a given codec, native type and type parameters. `assembleDataTypeSupport(components, dataTypeOfCodec)` builds the data types and authoring entries every contract source reads values with, refusing duplicates and value arguments whose data type is unregistered or unwritten; the control stack and the TypeScript builder both call it. `readWrittenValueIntoCodec` reads a written value, a list included, into a codec reference by the cast rule, which every family's enum members and `@@base` values go through; `applyDiscriminatorRule` decides whether a field can be a discriminator and which of its variants' values it accepts, and `describeDiscriminatorRefusal` words its refusals for PSL +- **Authoring types** (`./authoring`): Declarative authoring contribution types, template resolution, and validation for type constructors and field presets. A type choice (`kind: 'typeChoice'`) chooses a type constructor of its namespace from its option arguments: `resolveAuthoringTypeChoice` resolves a call, `typeChoiceArgumentsFromObject` reads the one object a TypeScript helper call writes into the choice's argument positions, `instantiateAuthoringTypeCall` instantiates a constructor or choice by name, and `assertTypeChoicesValid` refuses an unresolvable table at assembly (ADR 279). `findAuthoringTypeConstructorCall(namespace, output)` is the inverse of `instantiateAuthoringTypeConstructor`: the first call to a constructor in a namespace that produces a given codec, native type and type parameters. `assembleDataTypeSupport(components, dataTypeOfCodec)` builds the data types and authoring entries every contract source reads values with, refusing duplicates and value arguments whose data type is unregistered or unwritten; the control stack and the TypeScript builder both call it. `readWrittenValueIntoCodec` reads a written value, a list included, into a codec reference by the cast rule, which every family's enum members and `@@base` values go through; `applyDiscriminatorRule` decides whether a field can be a discriminator and which of its variants' values it accepts, and `describeDiscriminatorRefusal` words its refusals for PSL - **Codec base interface** (`./codec`): The cross-family `Codec` base type that SQL `Codec` and Mongo `MongoCodec` extend, plus the helpers targets declare data types and authoring entries with: shared casts, the number classifier, JSON text reading and printing, numeral text, and the date and time stored-form readers - **Control stack** (`./control`): Assembly functions that combine component descriptors into a unified `ControlStack` with derived state (codec imports, renderers, authoring contributions) - **Emission SPI** (`./emission`): Types for the emission pipeline — `TargetFamilyHook`, `GenerateContractTypesOptions`, `TypeRenderEntry`, `TypeRenderer`, `ParameterizedCodecDescriptor`, and related types diff --git a/packages/1-framework/1-core/framework-components/src/exports/authoring.ts b/packages/1-framework/1-core/framework-components/src/exports/authoring.ts index a215d8e5ac10..763700daca98 100644 --- a/packages/1-framework/1-core/framework-components/src/exports/authoring.ts +++ b/packages/1-framework/1-core/framework-components/src/exports/authoring.ts @@ -10,6 +10,7 @@ export { assertTypeChoicesValid, isAuthoringTypeChoiceOption, resolveAuthoringTypeChoice, + typeChoiceArgumentsFromObject, typeChoiceCombinationText, typeConstructorLookup, } from '../shared/authoring-type-choice'; diff --git a/packages/1-framework/1-core/framework-components/src/shared/authoring-type-choice.ts b/packages/1-framework/1-core/framework-components/src/shared/authoring-type-choice.ts index 74b74c745fab..488c186b5c20 100644 --- a/packages/1-framework/1-core/framework-components/src/shared/authoring-type-choice.ts +++ b/packages/1-framework/1-core/framework-components/src/shared/authoring-type-choice.ts @@ -62,13 +62,54 @@ function rowMatches( return names.every((name) => row.when[name] === chosen.get(name)); } -/** How a combination of option values is written, as in `value: date, column: narrow`. */ +/** How a combination of option values is written, as in `as: string, storage: timestamp`. */ export function typeChoiceCombinationText(row: AuthoringTypeChoiceRow): string { return Object.entries(row.when) .map(([name, value]) => `${name}: ${value}`) .join(', '); } +function listedNames(names: readonly string[]): string { + return names.length < 2 + ? names.join('') + : `${names.slice(0, -1).join(', ')} and ${names[names.length - 1]}`; +} + +function isArgumentObject(value: unknown): value is Readonly> { + return typeof value === 'object' && value !== null && !Array.isArray(value); +} + +/** + * The values of a type choice call that writes its arguments as one object, by the choice's argument positions, as {@link resolveAuthoringTypeChoice} reads them. `written` is the call's arguments: none, or one object keyed by argument name. Anything else, and a key the choice has no argument for, is refused with `CONTRACT.ARGUMENT_INVALID`. + */ +export function typeChoiceArgumentsFromObject( + helperPath: string, + descriptor: AuthoringTypeChoiceDescriptor, + written: readonly unknown[], +): readonly unknown[] { + const names = descriptor.args.map((argument) => argument.name); + const [first] = written; + const object = written.length === 0 ? {} : first; + if (written.length > 1 || !isArgumentObject(object)) { + const example = descriptor.args.find(isAuthoringTypeChoiceOption); + const sample = example === undefined ? '' : `{ ${example.name}: '${example.default}' }`; + throw runtimeError( + 'CONTRACT.ARGUMENT_INVALID', + `${helperPath} takes its arguments as one object with the keys ${listedNames(names)}, such as ${helperPath}(${sample}).`, + { helperPath }, + ); + } + const unknown = Object.keys(object).find((key) => !names.includes(key)); + if (unknown !== undefined) { + throw runtimeError( + 'CONTRACT.ARGUMENT_INVALID', + `${helperPath} has no argument "${unknown}"; its arguments are ${listedNames(names)}.`, + { helperPath }, + ); + } + return names.map((name) => object[name]); +} + /** * The type constructor a call of the type choice `descriptor` chooses, and the arguments it passes on. `args` are the call's values by the choice's argument positions; an option left out takes its default. A value an option does not list, a combination with no row, and a passed-on argument the chosen constructor does not take are refused with `CONTRACT.ARGUMENT_INVALID`, naming the argument by its index. */ @@ -105,10 +146,10 @@ export function resolveAuthoringTypeChoice(input: { fitting.some((row) => row.when[argument.name] === candidate), ); if (!accepted.includes(String(value))) { - const others = decided.map((name) => `${name} is ${chosen.get(name)}`).join(' and '); + const others = decided.map((name) => `${name}: ${chosen.get(name)}`).join(', '); refuse( index, - `${label(index)} cannot be ${String(value)} when ${others}; write one of: ${accepted.join(', ')}.`, + `${label(index)} cannot be ${String(value)} with ${others}; write one of: ${accepted.join(', ')}.`, ); } chosen.set(argument.name, String(value)); diff --git a/packages/1-framework/1-core/framework-components/test/authoring-type-choice.test.ts b/packages/1-framework/1-core/framework-components/test/authoring-type-choice.test.ts index 591fcb5700bb..f2efe0c978fe 100644 --- a/packages/1-framework/1-core/framework-components/test/authoring-type-choice.test.ts +++ b/packages/1-framework/1-core/framework-components/test/authoring-type-choice.test.ts @@ -2,6 +2,7 @@ import { type } from 'arktype'; import { describe, expect, it } from 'vitest'; import { resolveAuthoringTypeChoice, + typeChoiceArgumentsFromObject, typeConstructorLookup, } from '../src/shared/authoring-type-choice'; import { @@ -23,10 +24,10 @@ const namespace = { Moment: { kind: 'typeChoice', args: [ - { kind: 'option', name: 'value', values: ['date', 'text'], default: 'date', optional: true }, + { kind: 'option', name: 'as', values: ['date', 'text'], default: 'date', optional: true }, { kind: 'option', - name: 'column', + name: 'storage', values: ['wide', 'narrow'], default: 'wide', optional: true, @@ -34,9 +35,9 @@ const namespace = { precision, ], choices: [ - { when: { value: 'date', column: 'wide' }, type: 'WideDate', defaults: { precision: 3 } }, - { when: { value: 'text', column: 'wide' }, type: 'WideText' }, - { when: { value: 'date', column: 'narrow' }, type: 'NarrowDate' }, + { when: { as: 'date', storage: 'wide' }, type: 'WideDate', defaults: { precision: 3 } }, + { when: { as: 'text', storage: 'wide' }, type: 'WideText' }, + { when: { as: 'date', storage: 'narrow' }, type: 'NarrowDate' }, ], }, WideDate: { @@ -96,15 +97,14 @@ describe('resolveAuthoringTypeChoice', () => { it('refuses a value the option does not list, at that argument, naming its values', () => { expect(refusal(['bogus'])).toEqual({ - message: 'Argument "value" of Moment is bogus; write one of: date, text.', + message: 'Argument "as" of Moment is bogus; write one of: date, text.', details: { helperPath: 'Moment', argumentIndex: 0 }, }); }); it('refuses an unlisted combination at the argument that makes it invalid, naming what it accepts', () => { expect(refusal(['text', 'narrow'])).toEqual({ - message: - 'Argument "column" of Moment cannot be narrow when value is text; write one of: wide.', + message: 'Argument "storage" of Moment cannot be narrow with as: text; write one of: wide.', details: { helperPath: 'Moment', argumentIndex: 1 }, }); }); @@ -112,12 +112,46 @@ describe('resolveAuthoringTypeChoice', () => { it('refuses a passed-on argument the chosen type does not take', () => { expect(refusal([undefined, 'narrow', 6])).toEqual({ message: - 'Argument "precision" of Moment does not apply to NarrowDate, the type that value: date, column: narrow chooses.', + 'Argument "precision" of Moment does not apply to NarrowDate, the type that as: date, storage: narrow chooses.', details: { helperPath: 'Moment', argumentIndex: 2 }, }); }); }); +describe('typeChoiceArgumentsFromObject', () => { + const fromObject = (written: readonly unknown[]) => + typeChoiceArgumentsFromObject('Moment', namespace.Moment, written); + + it('gives the values by the argument positions of the choice, undefined where left out', () => { + expect({ + none: fromObject([]), + empty: fromObject([{}]), + some: fromObject([{ precision: 6, as: 'text' }]), + }).toEqual({ + none: [undefined, undefined, undefined], + empty: [undefined, undefined, undefined], + some: ['text', undefined, 6], + }); + }); + + it.each([ + ['positional arguments', ['text', 'wide']], + ['a string', ['text']], + ['a list', [['text']]], + ['null', [null]], + ])('refuses %s in place of one object', (_, written) => { + expect(() => fromObject(written)).toThrow( + "Moment takes its arguments as one object with the keys as, storage and precision, such as Moment({ as: 'date' }).", + ); + }); + + it('refuses a key the choice has no argument for', () => { + expect(() => fromObject([{ as: 'text', value: 'text' }])).toThrow( + 'Moment has no argument "value"; its arguments are as, storage and precision.', + ); + }); +}); + describe('a type choice in the type namespace', () => { it('instantiates to the column its chosen constructor makes', () => { expect(instantiateAuthoringTypeCall(namespace, 'Moment', ['text', undefined, 6])).toEqual({ diff --git a/packages/1-framework/1-core/framework-components/test/type-choice-assembly.test.ts b/packages/1-framework/1-core/framework-components/test/type-choice-assembly.test.ts index ef0ecd070aee..ce1bd6b78cd8 100644 --- a/packages/1-framework/1-core/framework-components/test/type-choice-assembly.test.ts +++ b/packages/1-framework/1-core/framework-components/test/type-choice-assembly.test.ts @@ -14,9 +14,9 @@ const precision = { optional: true, } as const; -const valueOption = { +const asOption = { kind: 'option', - name: 'value', + name: 'as', values: ['date', 'text'], default: 'date', optional: true, @@ -25,10 +25,10 @@ const valueOption = { const choice = (overrides: Partial = {}) => ({ kind: 'typeChoice', - args: [valueOption, precision], + args: [asOption, precision], choices: [ - { when: { value: 'date' }, type: 'WideDate', defaults: { precision: 3 } }, - { when: { value: 'text' }, type: 'WideText' }, + { when: { as: 'date' }, type: 'WideDate', defaults: { precision: 3 } }, + { when: { as: 'text' }, type: 'WideText' }, ], ...overrides, }) satisfies AuthoringTypeChoiceDescriptor; @@ -65,14 +65,14 @@ describe('assertTypeChoicesValid', () => { }); it('refuses a row naming a constructor no component registers, naming the pack and the name', () => { - expect(check(choice({ choices: [{ when: { value: 'date' }, type: 'Missing' }] }))).toThrow( + expect(check(choice({ choices: [{ when: { as: 'date' }, type: 'Missing' }] }))).toThrow( 'Type choice "Moment" contributed by "x-pack" chooses "Missing", which is not a type constructor any component registers beside it.', ); }); it('refuses a default no row matches', () => { - expect(check(choice({ choices: [{ when: { value: 'text' }, type: 'WideText' }] }))).toThrow( - 'Type choice "Moment" contributed by "x-pack" has no row for its defaults value: date, so it cannot be written bare.', + expect(check(choice({ choices: [{ when: { as: 'text' }, type: 'WideText' }] }))).toThrow( + 'Type choice "Moment" contributed by "x-pack" has no row for its defaults as: date, so it cannot be written bare.', ); }); @@ -80,13 +80,11 @@ describe('assertTypeChoicesValid', () => { expect( check( choice({ - args: [ - { kind: 'option', name: 'value', values: ['text'], default: 'date', optional: true }, - ], + args: [{ kind: 'option', name: 'as', values: ['text'], default: 'date', optional: true }], }), ), ).toThrow( - 'Type choice "Moment" contributed by "x-pack" defaults option "value" to date, which it does not list.', + 'Type choice "Moment" contributed by "x-pack" defaults option "as" to date, which it does not list.', ); }); @@ -95,13 +93,13 @@ describe('assertTypeChoicesValid', () => { check( choice({ choices: [ - { when: { value: 'date' }, type: 'WideDate' }, - { when: { value: 'json' }, type: 'WideText' }, + { when: { as: 'date' }, type: 'WideDate' }, + { when: { as: 'json' }, type: 'WideText' }, ], }), ), ).toThrow( - 'Type choice "Moment" contributed by "x-pack" has a row for value: json, which does not give every option one of its values.', + 'Type choice "Moment" contributed by "x-pack" has a row for as: json, which does not give every option one of its values.', ); }); @@ -110,8 +108,8 @@ describe('assertTypeChoicesValid', () => { check( choice({ choices: [ - { when: { value: 'date' }, type: 'WideDate', defaults: { scale: 2 } }, - { when: { value: 'text' }, type: 'WideText' }, + { when: { as: 'date' }, type: 'WideDate', defaults: { scale: 2 } }, + { when: { as: 'text' }, type: 'WideText' }, ], }), ), @@ -125,14 +123,14 @@ describe('assertTypeChoicesValid', () => { check( choice({ choices: [ - { when: { value: 'date' }, type: 'WideDate' }, - { when: { value: 'text' }, type: 'WideText' }, - { when: { value: 'date' }, type: 'WideText' }, + { when: { as: 'date' }, type: 'WideDate' }, + { when: { as: 'text' }, type: 'WideText' }, + { when: { as: 'date' }, type: 'WideText' }, ], }), ), ).toThrow( - 'Type choice "Moment" contributed by "x-pack" has two rows for value: date; each combination chooses one type.', + 'Type choice "Moment" contributed by "x-pack" has two rows for as: date; each combination chooses one type.', ); }); @@ -141,8 +139,8 @@ describe('assertTypeChoicesValid', () => { check( choice({ choices: [ - { when: { value: 'date' }, type: 'WideDate' }, - { when: { value: 'text' }, type: 'WideText', defaults: { precision: 3 } }, + { when: { as: 'date' }, type: 'WideDate' }, + { when: { as: 'text' }, type: 'WideText', defaults: { precision: 3 } }, ], }), ), @@ -152,7 +150,7 @@ describe('assertTypeChoicesValid', () => { }); it('refuses a passed-on argument of another data type than the chosen constructor takes', () => { - expect(check(choice({ args: [valueOption, { ...precision, dataType: 'test/text' }] }))).toThrow( + expect(check(choice({ args: [asOption, { ...precision, dataType: 'test/text' }] }))).toThrow( 'Type choice "Moment" contributed by "x-pack" passes "precision" as a value of data type "test/text" to "WideDate", which takes "test/int".', ); }); @@ -167,7 +165,7 @@ describe('createControlStack', () => { id: 'tgt', version: '0.0.1', authoring: { - type: { Moment: choice({ choices: [{ when: { value: 'date' }, type: 'Missing' }] }) }, + type: { Moment: choice({ choices: [{ when: { as: 'date' }, type: 'Missing' }] }) }, }, }, extensions: [], diff --git a/packages/1-framework/2-authoring/psl-parser/test/authoring-arguments.test.ts b/packages/1-framework/2-authoring/psl-parser/test/authoring-arguments.test.ts index 6e39fac4ef7a..80f09edcc026 100644 --- a/packages/1-framework/2-authoring/psl-parser/test/authoring-arguments.test.ts +++ b/packages/1-framework/2-authoring/psl-parser/test/authoring-arguments.test.ts @@ -127,10 +127,10 @@ describe('mapPslHelperArgs argument kinds', () => { it('names the values a named option takes when a name is quoted in its place', () => { const { diagnostics } = callMap( [positional('"string"')], - [{ kind: 'option', name: 'value', values: ['string', 'temporal'] }], + [{ kind: 'option', name: 'as', values: ['string', 'temporal'] }], ); expect(diagnostics[0]?.message).toMatch( - / argument #1 is "string", but argument "value" takes one of these names, written without quotes: string, temporal\.$/, + / argument #1 is "string", but argument "as" takes one of these names, written without quotes: string, temporal\.$/, ); expect(diagnostics[0]?.message).not.toMatch(/cannot parse/); }); diff --git a/packages/1-framework/2-authoring/psl-parser/test/authoring-arguments.value.test.ts b/packages/1-framework/2-authoring/psl-parser/test/authoring-arguments.value.test.ts index 249db8773530..170e33ccc41c 100644 --- a/packages/1-framework/2-authoring/psl-parser/test/authoring-arguments.value.test.ts +++ b/packages/1-framework/2-authoring/psl-parser/test/authoring-arguments.value.test.ts @@ -202,7 +202,7 @@ describe('mapPslHelperArgs on a value argument with no expression', () => { describe('a number written where an option is expected', () => { const option = { kind: 'option', - name: 'value', + name: 'as', values: ['string', 'temporal'], } as const; const message = (refusal: { diagnostics: ContractSourceDiagnostic[] }) => @@ -210,20 +210,20 @@ describe('a number written where an option is expected', () => { it('says to write the one value argument that takes a number by name', () => { expect(message(callWith('3', [option, { ...length, name: 'precision' }]))).toBe( - 'Field "Model.field" constructor "Sized" argument #1 is 3, but argument "value" takes one of these names, written without quotes: string, temporal. Write precision by name, as in precision: 3.', + 'Field "Model.field" constructor "Sized" argument #1 is 3, but argument "as" takes one of these names, written without quotes: string, temporal. Write precision by name, as in precision: 3.', ); }); it('says only which names the option takes when no value argument takes a number', () => { const label = { kind: 'value', dataType: 'pg/text', name: 'label', optional: true } as const; expect(message(callWith('3', [option, label]))).toBe( - 'Field "Model.field" constructor "Sized" argument #1 is 3, but argument "value" takes one of these names, written without quotes: string, temporal.', + 'Field "Model.field" constructor "Sized" argument #1 is 3, but argument "as" takes one of these names, written without quotes: string, temporal.', ); }); it('says only which names the option takes when two value arguments take a number', () => { expect(message(callWith('3', [option, length, { ...length, name: 'scale' }]))).toBe( - 'Field "Model.field" constructor "Sized" argument #1 is 3, but argument "value" takes one of these names, written without quotes: string, temporal.', + 'Field "Model.field" constructor "Sized" argument #1 is 3, but argument "as" takes one of these names, written without quotes: string, temporal.', ); }); }); diff --git a/packages/1-framework/2-authoring/psl-parser/test/binder-contributed-types.test.ts b/packages/1-framework/2-authoring/psl-parser/test/binder-contributed-types.test.ts index 93358fe39e8b..fc4be3dab42e 100644 --- a/packages/1-framework/2-authoring/psl-parser/test/binder-contributed-types.test.ts +++ b/packages/1-framework/2-authoring/psl-parser/test/binder-contributed-types.test.ts @@ -71,13 +71,19 @@ const narrowDate: AuthoringTypeConstructorDescriptor = { const moment: AuthoringTypeChoiceDescriptor = { kind: 'typeChoice', args: [ - { kind: 'option', name: 'value', values: ['date', 'text'], default: 'date', optional: true }, - { kind: 'option', name: 'column', values: ['wide', 'narrow'], default: 'wide', optional: true }, + { kind: 'option', name: 'as', values: ['date', 'text'], default: 'date', optional: true }, + { + kind: 'option', + name: 'storage', + values: ['wide', 'narrow'], + default: 'wide', + optional: true, + }, ], choices: [ - { when: { value: 'date', column: 'wide' }, type: 'WideDate' }, - { when: { value: 'text', column: 'wide' }, type: 'WideText' }, - { when: { value: 'date', column: 'narrow' }, type: 'NarrowDate' }, + { when: { as: 'date', storage: 'wide' }, type: 'WideDate' }, + { when: { as: 'text', storage: 'wide' }, type: 'WideText' }, + { when: { as: 'date', storage: 'narrow' }, type: 'NarrowDate' }, ], }; @@ -227,16 +233,16 @@ describe('createBinder — contributed types', () => { const { resolve, binder, symbolTable } = bindProject( [ 'types {', - ' Stamp = Moment(value: text)', + ' Stamp = Moment(as: text)', '}', 'model Post {', ' bare Moment', - ' named Moment(column: narrow)', + ' named Moment(storage: narrow)', ' positional Moment(text)', ' aliased Stamp', - ' refused Moment(value: text, column: narrow)', + ' refused Moment(as: text, storage: narrow)', ' unknownNamed Moment(bogus: narrow)', - ' twice Moment(text, value: date)', + ' twice Moment(text, as: date)', '}', ].join('\n'), ); diff --git a/packages/1-framework/3-tooling/language-server/test/completion-provider.test.ts b/packages/1-framework/3-tooling/language-server/test/completion-provider.test.ts index a1572e749963..8e4efc291657 100644 --- a/packages/1-framework/3-tooling/language-server/test/completion-provider.test.ts +++ b/packages/1-framework/3-tooling/language-server/test/completion-provider.test.ts @@ -713,15 +713,15 @@ namespace other { model Hidden { id Int } }`, args: [ { kind: 'option', - name: 'value', + name: 'as', values: ['date', 'text'], default: 'date', optional: true, }, ], choices: [ - { when: { value: 'date' }, type: 'Stamp' }, - { when: { value: 'text' }, type: 'Stamp' }, + { when: { as: 'date' }, type: 'Stamp' }, + { when: { as: 'text' }, type: 'Stamp' }, ], }, Stamp: { kind: 'typeConstructor', output: { codecId: 'fixture/value' } }, diff --git a/packages/1-framework/3-tooling/language-server/test/hover.test.ts b/packages/1-framework/3-tooling/language-server/test/hover.test.ts index 41db28daaa51..892fd8db6e90 100644 --- a/packages/1-framework/3-tooling/language-server/test/hover.test.ts +++ b/packages/1-framework/3-tooling/language-server/test/hover.test.ts @@ -157,14 +157,14 @@ const authoringContributions = assembleAuthoringContributions([ args: [ { kind: 'option', - name: 'value', + name: 'as', values: ['date', 'text'], default: 'date', optional: true, }, { kind: 'option', - name: 'column', + name: 'storage', values: ['wide', 'narrow'], default: 'wide', optional: true, @@ -173,12 +173,12 @@ const authoringContributions = assembleAuthoringContributions([ ], choices: [ { - when: { value: 'date', column: 'wide' }, + when: { as: 'date', storage: 'wide' }, type: 'Stamp', defaults: { precision: 3 }, }, - { when: { value: 'text', column: 'wide' }, type: 'Text' }, - { when: { value: 'date', column: 'narrow' }, type: 'Flag' }, + { when: { as: 'text', storage: 'wide' }, type: 'Text' }, + { when: { as: 'date', storage: 'narrow' }, type: 'Flag' }, ], }, Stamp: { @@ -602,17 +602,17 @@ describe('providePslHover', () => { }); it('shows a type choice with its choices as written and the explicit type each becomes', () => { - const result = hover('model Product {\n at pg.Mo|ment(value: text)\n}'); + const result = hover('model Product {\n at pg.Mo|ment(as: text)\n}'); expect(result?.contents).toEqual({ kind: 'markdown', value: [ '```prisma', - 'pg.Moment(value?: date | text, column?: wide | narrow, precision?: a number)', + 'pg.Moment(as?: date | text, storage?: wide | narrow, precision?: a number)', '```', '', 'A moment in time.', '', - '| value | column | Becomes |', + '| as | storage | Becomes |', '| --- | --- | --- |', '| date (default) | wide (default) | `Stamp(precision: 3)` |', '| text | wide (default) | `Text` |', diff --git a/packages/2-mongo-family/2-authoring/contract-psl/test/interpreter.type-choices.test.ts b/packages/2-mongo-family/2-authoring/contract-psl/test/interpreter.type-choices.test.ts index 678480e65be4..da9334f9d234 100644 --- a/packages/2-mongo-family/2-authoring/contract-psl/test/interpreter.type-choices.test.ts +++ b/packages/2-mongo-family/2-authoring/contract-psl/test/interpreter.type-choices.test.ts @@ -17,12 +17,12 @@ const type = { Moment: { kind: 'typeChoice', args: [ - { kind: 'option', name: 'value', values: ['date', 'text'], default: 'date', optional: true }, + { kind: 'option', name: 'as', values: ['date', 'text'], default: 'date', optional: true }, label, ], choices: [ - { when: { value: 'date' }, type: 'Stamp' }, - { when: { value: 'text' }, type: 'Text' }, + { when: { as: 'date' }, type: 'Stamp' }, + { when: { as: 'text' }, type: 'Text' }, ], }, } as const satisfies AuthoringTypeNamespace; @@ -58,7 +58,7 @@ function diagnostics(schema: string) { describe('a MongoDB field typed by a type choice', () => { it('gets the type of the constructor its call chooses, bare, named and positional', () => { expect( - fieldTypes(model('bare Moment', 'named Moment(value: text)', 'positional Moment(text, "x")')), + fieldTypes(model('bare Moment', 'named Moment(as: text)', 'positional Moment(text, "x")')), ).toEqual({ id: { kind: 'scalar', codecId: 'mongo/objectId@1' }, bare: { kind: 'scalar', codecId: 'mongo/date@1' }, @@ -68,7 +68,7 @@ describe('a MongoDB field typed by a type choice', () => { }); it('gets the type its alias’s call chooses', () => { - expect(fieldTypes(`types {\n Note = Moment(value: text)\n}\n${model('note Note')}`)).toEqual({ + expect(fieldTypes(`types {\n Note = Moment(as: text)\n}\n${model('note Note')}`)).toEqual({ id: { kind: 'scalar', codecId: 'mongo/objectId@1' }, note: { kind: 'scalar', codecId: 'mongo/string@1' }, }); @@ -82,13 +82,13 @@ describe('a MongoDB field typed by a type choice', () => { ], [ 'a value the option does not list', - 'Moment(value: json)', - 'Field "Doc.at" type "Moment": Argument "value" of Moment is json; write one of: date, text.', + 'Moment(as: json)', + 'Field "Doc.at" type "Moment": Argument "as" of Moment is json; write one of: date, text.', ], [ 'an argument the chosen type does not take', 'Moment(label: "x")', - 'Field "Doc.at" type "Moment": Argument "label" of Moment does not apply to Stamp, the type that value: date chooses.', + 'Field "Doc.at" type "Moment": Argument "label" of Moment does not apply to Stamp, the type that as: date chooses.', ], ])('refuses %s at the argument', (_case, written, message) => { expect(diagnostics(model(`at ${written}`))).toEqual([ diff --git a/packages/2-sql-family/2-authoring/contract-prisma7/test/type-choice-binding.test.ts b/packages/2-sql-family/2-authoring/contract-prisma7/test/type-choice-binding.test.ts index e2ade2594cef..b7cad8fc148f 100644 --- a/packages/2-sql-family/2-authoring/contract-prisma7/test/type-choice-binding.test.ts +++ b/packages/2-sql-family/2-authoring/contract-prisma7/test/type-choice-binding.test.ts @@ -45,7 +45,7 @@ describe('a Prisma 7 type the binding maps to a type choice', () => { expect(() => interpretSchemaTextResult(SCHEMA, bindingWithDateTime(['jsDate', 'timetz'])), ).toThrow( - 'The Prisma 7 target binding maps Field "Event.at" to type choice "DateTime" with values it refuses: Argument "column" of DateTime is timetz; write one of: timestamptz, timestamp.', + 'The Prisma 7 target binding maps Field "Event.at" to type choice "DateTime" with values it refuses: Argument "storage" of DateTime is timetz; write one of: timestamptz, timestamp.', ); }); }); diff --git a/packages/2-sql-family/2-authoring/contract-psl/test/interpreter.type-choices.test.ts b/packages/2-sql-family/2-authoring/contract-psl/test/interpreter.type-choices.test.ts index 9cb39bc7b033..1be01b6e7f39 100644 --- a/packages/2-sql-family/2-authoring/contract-psl/test/interpreter.type-choices.test.ts +++ b/packages/2-sql-family/2-authoring/contract-psl/test/interpreter.type-choices.test.ts @@ -35,22 +35,22 @@ const types = { args: [ { kind: 'option', - name: 'value', + name: 'as', values: ['temporal', 'string'], default: 'temporal', optional: true, }, - { kind: 'option', name: 'column', values: ['tz', 'local'], default: 'tz', optional: true }, + { kind: 'option', name: 'storage', values: ['tz', 'local'], default: 'tz', optional: true }, precision, ], choices: [ { - when: { value: 'temporal', column: 'tz' }, + when: { as: 'temporal', storage: 'tz' }, type: 'TimestamptzTemporal', defaults: { precision: 3 }, }, - { when: { value: 'temporal', column: 'local' }, type: 'TimestampTemporal' }, - { when: { value: 'string', column: 'local' }, type: 'TimestampString' }, + { when: { as: 'temporal', storage: 'local' }, type: 'TimestampTemporal' }, + { when: { as: 'string', storage: 'local' }, type: 'TimestampString' }, ], }, Day: { @@ -58,7 +58,7 @@ const types = { args: [ { kind: 'option', - name: 'value', + name: 'as', values: ['string', 'temporal'], default: 'string', optional: true, @@ -66,8 +66,8 @@ const types = { precision, ], choices: [ - { when: { value: 'string' }, type: 'DateString' }, - { when: { value: 'temporal' }, type: 'DateTemporal' }, + { when: { as: 'string' }, type: 'DateString' }, + { when: { as: 'temporal' }, type: 'DateTemporal' }, ], }, Label: { @@ -75,15 +75,15 @@ const types = { args: [ { kind: 'option', - name: 'value', + name: 'as', values: ['text', 'number'], default: 'text', optional: true, }, ], choices: [ - { when: { value: 'text' }, type: 'String' }, - { when: { value: 'number' }, type: 'Int' }, + { when: { as: 'text' }, type: 'String' }, + { when: { as: 'number' }, type: 'Int' }, ], }, } satisfies AuthoringTypeNamespace; @@ -132,9 +132,9 @@ describe('a type choice in a field type', () => { lowered( model( 'bare Moment', - 'named Moment(value: string, column: local)', + 'named Moment(as: string, storage: local)', 'positional Moment(temporal, local, 6)', - 'day Day(value: temporal)', + 'day Day(as: temporal)', 'list Moment[]', 'maybe Moment?', ), @@ -160,24 +160,24 @@ describe('a type choice in a field type', () => { }); it('refuses an unlisted combination at the argument that makes it invalid', () => { - const line = 'at Moment(value: string, column: tz)'; - const start = ' '.length + line.indexOf('column'); + const line = 'at Moment(as: string, storage: tz)'; + const start = ' '.length + line.indexOf('storage'); expect(diagnostics(model(line))).toEqual([ { code: 'PSL_INVALID_ATTRIBUTE_ARGUMENT', message: - 'Field "T.at" type "Moment": Argument "column" of Moment cannot be tz when value is string; write one of: local.', - columns: [start + 1, start + 1 + 'column: tz'.length], + 'Field "T.at" type "Moment": Argument "storage" of Moment cannot be tz with as: string; write one of: local.', + columns: [start + 1, start + 1 + 'storage: tz'.length], }, ]); }); it('refuses a value the option does not list, naming the values it does', () => { - expect(diagnostics(model('at Day(value: jsDate)'))).toMatchObject([ + expect(diagnostics(model('at Day(as: jsDate)'))).toMatchObject([ { code: 'PSL_INVALID_ATTRIBUTE_ARGUMENT', message: - 'Field "T.at" type "Day": Argument "value" of Day is jsDate; write one of: string, temporal.', + 'Field "T.at" type "Day": Argument "as" of Day is jsDate; write one of: string, temporal.', }, ]); }); @@ -187,13 +187,13 @@ describe('a type choice in a field type', () => { { code: 'PSL_INVALID_ATTRIBUTE_ARGUMENT', message: - 'Field "T.at" type "Day": Argument "precision" of Day does not apply to DateString, the type that value: string chooses.', + 'Field "T.at" type "Day": Argument "precision" of Day does not apply to DateString, the type that as: string chooses.', }, ]); }); it('refuses a precision the chosen type’s codec refuses, naming the argument of the choice', () => { - const line = 'at Moment(value: temporal, column: local, precision: 7)'; + const line = 'at Moment(as: temporal, storage: local, precision: 7)'; const start = ' '.length + line.indexOf('precision'); expect(diagnostics(model(line))).toEqual([ { @@ -210,7 +210,7 @@ describe('a type choice behind a types {} alias', () => { it('emits what the type it chooses emits', () => { expect( lowered(`types { - Stamp = Moment(value: string, column: local) + Stamp = Moment(as: string, storage: local) Plain = Moment } ${model('stamp Stamp', 'plain Plain')}`), @@ -223,7 +223,7 @@ describe('attributes on a field typed by a type choice', () => { expect(lowered(model('label Label @default("a")'))).toEqual( lowered(model('label String @default("a")')), ); - expect(diagnostics(model('label Label(value: number) @default("a")'))).toMatchObject([ + expect(diagnostics(model('label Label(as: number) @default("a")'))).toMatchObject([ { code: 'PSL_VALUE_TYPE_INCOMPATIBLE' }, ]); }); @@ -231,7 +231,7 @@ describe('attributes on a field typed by a type choice', () => { it('admit a @default by the codec the alias’s call chooses', () => { expect( diagnostics(`types { - Count = Label(value: number) + Count = Label(as: number) } ${model('count Count @default("a")')}`), ).toMatchObject([{ code: 'PSL_VALUE_TYPE_INCOMPATIBLE' }]); @@ -242,7 +242,7 @@ ${model('count Count @default("a")')}`), lowered( model( 'a Moment @onCreate(now()) @onUpdate(now())', - 'b Moment(value: string, column: local) @onCreate(now())', + 'b Moment(as: string, storage: local) @onCreate(now())', 'c Day @onCreate(now())', ), ), @@ -260,7 +260,7 @@ ${model('count Count @default("a")')}`), it('resolve generators through an alias written as a type choice', () => { expect( lowered(`types { - Stamp = Moment(value: string, column: local) @onCreate(now()) + Stamp = Moment(as: string, storage: local) @onCreate(now()) } ${model('stamp Stamp')}`), ).toEqual(lowered(model('stamp TimestampString @onCreate(now())'))); diff --git a/packages/2-sql-family/2-authoring/contract-ts/README.md b/packages/2-sql-family/2-authoring/contract-ts/README.md index 0714d6f79497..884f2c349c69 100644 --- a/packages/2-sql-family/2-authoring/contract-ts/README.md +++ b/packages/2-sql-family/2-authoring/contract-ts/README.md @@ -146,7 +146,7 @@ Anything else inside `${…}` is a type error, and `CONTRACT.SQL_EXPRESSION_INTE Pack-provided helper presets are available through the callback overload. This is the surface that exposes `field.id.*`, `field.text()`, `field.temporal.createdAt()`, `field.temporal.updatedAt()`, the `type.*` helpers, and extension helpers such as `type.pgvector.Vector(...)`. -`type.*` has a helper for every PSL type constructor of the stack, such as `type.VarChar(64)`, `type.TimestampString(3)` and `type.String()`, plus the type aliases the packs contribute, such as `type.temporal.CreatedAt()` and `type.temporal.UpdatedAt()`. Pass the result to `field.column(...)`. A type alias is a constant: field builders are immutable, so one constant serves every field and model that uses it, and each column stores the alias's codec and params inline. A constant may carry a default and generators, and a field may add what the constant does not set: +`type.*` has a helper for every PSL type constructor of the stack, such as `type.VarChar(64)`, `type.TimestampString(3)` and `type.String()`, plus the type aliases the packs contribute, such as `type.temporal.CreatedAt()` and `type.temporal.UpdatedAt()`. A type choice, such as PostgreSQL's `DateTime`, takes its arguments as one object, `type.DateTime({ as: 'string', precision: 6 })`, and is typed as the explicit type its arguments choose. Pass the result to `field.column(...)`. A type alias is a constant: field builders are immutable, so one constant serves every field and model that uses it, and each column stores the alias's codec and params inline. A constant may carry a default and generators, and a field may add what the constant does not set: ```typescript const UpdatedAt = field.column(type.TimestampString(3)).onCreate(gen.now()).onUpdate(gen.now()); diff --git a/packages/2-sql-family/2-authoring/contract-ts/src/authoring-helper-runtime.ts b/packages/2-sql-family/2-authoring/contract-ts/src/authoring-helper-runtime.ts index c87e89152cb6..b116062e3bd5 100644 --- a/packages/2-sql-family/2-authoring/contract-ts/src/authoring-helper-runtime.ts +++ b/packages/2-sql-family/2-authoring/contract-ts/src/authoring-helper-runtime.ts @@ -16,6 +16,7 @@ import { isAuthoringTypeConstructorDescriptor, readWrittenValueAs, resolveAuthoringTypeChoice, + typeChoiceArgumentsFromObject, typeConstructorLookup, validateAuthoringHelperArguments, validateAuthoringTypeChoiceParams, @@ -146,11 +147,12 @@ export function createTypeHelpersFromNamespace( const helperPath = currentPath.join('.'); const constructorNamed = typeConstructorLookup(namespace); helpers[key] = (...written: readonly unknown[]): AuthoredStorageTypeInstance => { - validateAuthoringHelperArguments(helperPath, value.args, written); + const args = typeChoiceArgumentsFromObject(helperPath, value, written); + validateAuthoringHelperArguments(helperPath, value.args, args); const chosen = resolveAuthoringTypeChoice({ helperPath, descriptor: value, - args: readValueArguments(helperPath, value.args, written, lookups.dataTypes), + args: readValueArguments(helperPath, value.args, args, lookups.dataTypes), constructorNamed, }); const output = instantiateAuthoringTypeConstructor(chosen.descriptor, chosen.args); diff --git a/packages/2-sql-family/2-authoring/contract-ts/src/composed-authoring-helpers.ts b/packages/2-sql-family/2-authoring/contract-ts/src/composed-authoring-helpers.ts index 47edb136aec6..5d35d95c4e10 100644 --- a/packages/2-sql-family/2-authoring/contract-ts/src/composed-authoring-helpers.ts +++ b/packages/2-sql-family/2-authoring/contract-ts/src/composed-authoring-helpers.ts @@ -118,24 +118,6 @@ type TypeHelperFunction< ) => StorageTypeFromDescriptor> : () => StorageTypeFromDescriptor; -/** The value a call writes for each argument of a type choice, by the argument's name; `undefined` for one it leaves out. */ -type WrittenChoiceArguments< - Args extends readonly AuthoringTypeChoiceArgument[], - Params extends readonly unknown[], -> = Args extends readonly [ - infer Head extends AuthoringTypeChoiceArgument, - ...infer Tail extends readonly AuthoringTypeChoiceArgument[], -] - ? { - readonly [Name in Head['name']]: Params extends readonly [infer Written, ...unknown[]] - ? Written - : undefined; - } & WrittenChoiceArguments< - Tail, - Params extends readonly [unknown, ...infer Rest] ? Rest : readonly [] - > - : Record; - type RowDefault = Row extends { readonly defaults: infer Defaults; } @@ -147,7 +129,7 @@ type RowDefault = Row e /** The arguments a type choice passes on to the constructor its row chooses, by that constructor's argument positions: the written value, or the row's default. */ type PassedOnArguments< Parameters extends readonly AuthoringArgumentDescriptor[], - Written extends Record, + Written extends object, Row extends AuthoringTypeChoiceRow, > = { readonly [Index in keyof Parameters]: Parameters[Index] extends { @@ -163,7 +145,7 @@ type PassedOnArguments< type ChosenStorageType< Chosen, - Written extends Record, + Written extends object, Row extends AuthoringTypeChoiceRow, ValueTypes extends DataTypeArgumentTypes, > = Chosen extends AuthoringTypeConstructorDescriptor @@ -185,73 +167,84 @@ type ParameterNames = Chosen extends { : never : never; -type RowArgument< - Head extends AuthoringTypeChoiceArgument, +type RowValue< Row extends AuthoringTypeChoiceRow, - Chosen, - ValueTypes extends DataTypeArgumentTypes, -> = Head extends { readonly kind: 'option'; readonly default: infer Default } - ? Head['name'] extends keyof Row['when'] - ? Row['when'][Head['name']] | (Row['when'][Head['name']] extends Default ? undefined : never) - : never - : Head['name'] extends ParameterNames - ? ArgTypeFromDescriptor | undefined - : undefined; + Name extends string, +> = Name extends keyof Row['when'] ? Row['when'][Name] : never; -/** Whether a call must write `Head` to choose `Row`: an option whose value in the row is not its default. */ -type RowRequires< - Head extends AuthoringTypeChoiceArgument, +/** Whether `Row` gives the option `Argument` its default, so a call choosing the row may leave it out. */ +type RowTakesDefault< + Argument extends AuthoringTypeChoiceArgument, Row extends AuthoringTypeChoiceRow, -> = Head extends { readonly kind: 'option'; readonly default: infer Default } - ? Head['name'] extends keyof Row['when'] - ? Row['when'][Head['name']] extends Default - ? false - : true +> = Argument extends { readonly default: infer Default } + ? RowValue extends Default + ? true : false - : false; + : true; -type RowArguments< +/** The options a call choosing `Row` must write: those whose value in the row is not their default. */ +type RequiredRowOptions< + Args extends readonly AuthoringTypeChoiceArgument[], + Row extends AuthoringTypeChoiceRow, +> = { + readonly [Argument in Args[number] as Argument extends { readonly kind: 'option' } + ? RowTakesDefault extends true + ? never + : Argument['name'] + : never]: RowValue; +}; + +/** What else a call choosing `Row` may write: an option the row gives its default, and an argument the chosen constructor takes. */ +type OptionalRowArguments< Args extends readonly AuthoringTypeChoiceArgument[], Row extends AuthoringTypeChoiceRow, Chosen, ValueTypes extends DataTypeArgumentTypes, > = { - readonly [Index in keyof Args]: RowArgument; + readonly [Argument in Args[number] as Argument extends { readonly kind: 'option' } + ? RowTakesDefault extends true + ? Argument['name'] + : never + : Argument['name'] extends ParameterNames + ? Argument['name'] + : never]?: + | (Argument extends { readonly kind: 'option' } + ? RowValue + : ArgTypeFromDescriptor) + | undefined; +}; + +/** `Params`, with every key `Allowed` does not name typed `never`, so a call writing such a key does not compile. */ +type OnlyKeys = Params & { + readonly [Key in Exclude]: never; }; -type RowRequirements< +type RowArguments< Args extends readonly AuthoringTypeChoiceArgument[], Row extends AuthoringTypeChoiceRow, -> = { readonly [Index in keyof Args]: RowRequires }; - -/** The arguments as a tuple whose trailing elements, after the last one a call must write, are optional. */ -type TrailingOptional< - Elements extends readonly unknown[], - Requirements extends readonly boolean[], -> = Requirements extends readonly [...infer RestRequirements extends readonly boolean[], infer Last] - ? Last extends true - ? Elements - : Elements extends readonly [...infer RestElements, infer LastElement] - ? readonly [...TrailingOptional, LastElement?] - : readonly [] - : readonly []; - -/** What a call choosing `Row` may write for each argument: the row's value for an option, left out only when that is the option's default, and a value for an argument the chosen constructor takes. */ -type RowParameters< + Chosen, + ValueTypes extends DataTypeArgumentTypes, +> = RequiredRowOptions & OptionalRowArguments; + +type RowObjectHelperFunction< Args extends readonly AuthoringTypeChoiceArgument[], Row extends AuthoringTypeChoiceRow, Chosen, ValueTypes extends DataTypeArgumentTypes, -> = TrailingOptional, RowRequirements>; +> = >( + args: OnlyKeys>, +) => ChosenStorageType; +/** A call choosing `Row`: with no arguments too when the row gives every option its default. */ type RowHelperFunction< Args extends readonly AuthoringTypeChoiceArgument[], Row extends AuthoringTypeChoiceRow, Chosen, ValueTypes extends DataTypeArgumentTypes, -> = >( - ...args: Params -) => ChosenStorageType, Row, ValueTypes>; +> = keyof RequiredRowOptions extends never + ? (() => ChosenStorageType, Row, ValueTypes>) & + RowObjectHelperFunction + : RowObjectHelperFunction; type RowOverloads< Args extends readonly AuthoringTypeChoiceArgument[], @@ -271,7 +264,7 @@ type RowOverloads< RowOverloads : unknown; -/** A type choice's helper: one overload per row, so a call is typed by the row its option values choose, and a combination with no row does not compile. */ +/** A type choice's helper, which takes its arguments as one object keyed by name: one overload per row, so a call is typed by the row its option values choose, and a combination with no row, an unknown key, or an argument the chosen type does not take does not compile. */ type TypeChoiceHelperFunction< Descriptor extends AuthoringTypeChoiceDescriptor, Namespace, diff --git a/packages/2-sql-family/2-authoring/contract-ts/test/helpers/type-choice-packs.ts b/packages/2-sql-family/2-authoring/contract-ts/test/helpers/type-choice-packs.ts index fee661734e9f..16cb5659796f 100644 --- a/packages/2-sql-family/2-authoring/contract-ts/test/helpers/type-choice-packs.ts +++ b/packages/2-sql-family/2-authoring/contract-ts/test/helpers/type-choice-packs.ts @@ -48,14 +48,14 @@ export const targetPack = { args: [ { kind: 'option', - name: 'value', - values: ['temporal', 'string'], + name: 'as', + values: ['temporal', 'string', 'date'], default: 'temporal', optional: true, }, { kind: 'option', - name: 'column', + name: 'storage', values: ['tz', 'local'], default: 'tz', optional: true, @@ -64,12 +64,13 @@ export const targetPack = { ], choices: [ { - when: { value: 'temporal', column: 'tz' }, + when: { as: 'temporal', storage: 'tz' }, type: 'TimestamptzTemporal', defaults: { precision: 3 }, }, - { when: { value: 'temporal', column: 'local' }, type: 'TimestampTemporal' }, - { when: { value: 'string', column: 'local' }, type: 'TimestampString' }, + { when: { as: 'temporal', storage: 'local' }, type: 'TimestampTemporal' }, + { when: { as: 'string', storage: 'local' }, type: 'TimestampString' }, + { when: { as: 'date', storage: 'local' }, type: 'DateString' }, ], }, Day: { @@ -77,15 +78,15 @@ export const targetPack = { args: [ { kind: 'option', - name: 'value', + name: 'as', values: ['string', 'temporal'], default: 'string', optional: true, }, ], choices: [ - { when: { value: 'string' }, type: 'DateString' }, - { when: { value: 'temporal' }, type: 'DateTemporal' }, + { when: { as: 'string' }, type: 'DateString' }, + { when: { as: 'temporal' }, type: 'DateTemporal' }, ], }, }, diff --git a/packages/2-sql-family/2-authoring/contract-ts/test/type-choice-helpers.test-d.ts b/packages/2-sql-family/2-authoring/contract-ts/test/type-choice-helpers.test-d.ts index 3c1c7ca2ae2b..333b038897f8 100644 --- a/packages/2-sql-family/2-authoring/contract-ts/test/type-choice-helpers.test-d.ts +++ b/packages/2-sql-family/2-authoring/contract-ts/test/type-choice-helpers.test-d.ts @@ -10,30 +10,61 @@ const { type } = createComposedAuthoringHelpers({ }); test('a bare call resolves to the default row, with its parameter defaults', () => { + expectTypeOf(type.Moment()).not.toBeAny(); expectTypeOf(type.Moment()).toEqualTypeOf(type.TimestamptzTemporal(3)); + expectTypeOf(type.Moment({})).toEqualTypeOf(type.TimestamptzTemporal(3)); expectTypeOf(type.Moment().codecId).toEqualTypeOf<'pg/timestamptz-temporal@1'>(); }); test('a written precision replaces the row default', () => { - expectTypeOf(type.Moment('temporal', 'tz', 6)).toEqualTypeOf(type.TimestamptzTemporal(6)); + expectTypeOf(type.Moment({ precision: 6 })).toEqualTypeOf(type.TimestamptzTemporal(6)); + expectTypeOf(type.Moment({ as: 'temporal', storage: 'tz', precision: 6 })).toEqualTypeOf( + type.TimestamptzTemporal(6), + ); }); test('each row resolves to the type it names', () => { - expectTypeOf(type.Moment('temporal', 'local')).toEqualTypeOf(type.TimestampTemporal()); - expectTypeOf(type.Moment('temporal', 'local', 6)).toEqualTypeOf(type.TimestampTemporal(6)); - expectTypeOf(type.Moment('string', 'local')).toEqualTypeOf(type.TimestampString()); + expectTypeOf(type.Moment({ storage: 'local' })).toEqualTypeOf(type.TimestampTemporal()); + expectTypeOf(type.Moment({ storage: 'local', precision: 6 })).toEqualTypeOf( + type.TimestampTemporal(6), + ); + expectTypeOf(type.Moment({ as: 'string', storage: 'local' })).toEqualTypeOf( + type.TimestampString(), + ); + expectTypeOf(type.Moment({ as: 'date', storage: 'local' })).toEqualTypeOf(type.DateString()); expectTypeOf(type.Day()).toEqualTypeOf(type.DateString()); - expectTypeOf(type.Day('temporal')).toEqualTypeOf(type.DateTemporal()); + expectTypeOf(type.Day({ as: 'temporal' })).toEqualTypeOf(type.DateTemporal()); }); test('an option argument takes only the values it lists', () => { - // @ts-expect-error Moment's value is temporal or string - type.Moment('jsDate'); + // @ts-expect-error Moment's as is temporal, string or date + type.Moment({ as: 'jsDate' }); }); test('an unlisted combination does not compile', () => { - // @ts-expect-error value: string chooses no type with the default column tz - type.Moment('string'); + // @ts-expect-error as: string chooses no type with the default storage tz + type.Moment({ as: 'string' }); +}); + +test('an argument the chosen type does not take does not compile', () => { // @ts-expect-error DateString takes no precision - type.Day('string', 3); + type.Moment({ as: 'date', storage: 'local', precision: 3 }); + // @ts-expect-error Day passes on no precision + type.Day({ precision: 3 }); +}); + +test('a key the type choice has no argument for does not compile', () => { + // @ts-expect-error Moment has no argument value + type.Moment({ value: 'string' }); + // @ts-expect-error Moment has no argument bogus + type.Moment({ as: 'temporal', bogus: 1 }); + // @ts-expect-error Moment has no argument bogus + type.Moment({ storage: 'local', bogus: 1 }); +}); + +test('positional arguments do not compile', () => { + // @ts-expect-error a type choice takes one object + type.Moment('temporal'); + // @ts-expect-error a type choice takes one object + type.Moment({}, {}); }); diff --git a/packages/2-sql-family/2-authoring/contract-ts/test/type-choice-helpers.test.ts b/packages/2-sql-family/2-authoring/contract-ts/test/type-choice-helpers.test.ts index b417587036ac..789ebcb7954a 100644 --- a/packages/2-sql-family/2-authoring/contract-ts/test/type-choice-helpers.test.ts +++ b/packages/2-sql-family/2-authoring/contract-ts/test/type-choice-helpers.test.ts @@ -9,18 +9,26 @@ const { type } = createComposedAuthoringHelpers({ target: targetPack, }); +const untyped = type.Moment as (...args: readonly unknown[]) => unknown; + describe('a type.* helper of a type choice', () => { it('gives what the helper of the type it chooses gives, for each row', () => { expect({ bare: type.Moment(), - local: type.Moment('temporal', 'local', 6), - string: type.Moment('string', 'local'), + empty: type.Moment({}), + precision: type.Moment({ precision: 6 }), + local: type.Moment({ storage: 'local', precision: 6 }), + string: type.Moment({ as: 'string', storage: 'local' }), + date: type.Moment({ as: 'date', storage: 'local' }), day: type.Day(), - temporalDay: type.Day('temporal'), + temporalDay: type.Day({ as: 'temporal' }), }).toEqual({ bare: type.TimestamptzTemporal(3), + empty: type.TimestamptzTemporal(3), + precision: type.TimestamptzTemporal(6), local: type.TimestampTemporal(6), string: type.TimestampString(), + date: type.DateString(), day: type.DateString(), temporalDay: type.DateTemporal(), }); @@ -34,10 +42,44 @@ describe('a type.* helper of a type choice', () => { }); }); + it('reads an argument written as undefined as left out', () => { + expect(type.Moment({ as: undefined, precision: undefined })).toEqual( + type.TimestamptzTemporal(3), + ); + }); + it('refuses an unlisted combination at the argument that makes it invalid', () => { - const untyped = type.Moment as (...args: readonly unknown[]) => unknown; - expect(() => untyped('string')).toThrow( - 'Argument "column" of Moment cannot be tz when value is string; write one of: local.', + expect(() => untyped({ as: 'string' })).toThrow( + 'Argument "storage" of Moment cannot be tz with as: string; write one of: local.', + ); + }); + + it('refuses a value the option does not list', () => { + expect(() => untyped({ as: 'jsDate' })).toThrow( + 'Argument "as" of Moment must be one of "temporal", "string", "date"; received "jsDate"', + ); + }); + + it('refuses an argument the chosen type does not take', () => { + expect(() => untyped({ as: 'date', storage: 'local', precision: 3 })).toThrow( + 'Argument "precision" of Moment does not apply to DateString, the type that as: date, storage: local chooses.', + ); + }); + + it.each([ + ['positional arguments', ['string', 'local']], + ['a string', ['string']], + ['a list', [['string']]], + ['null', [null]], + ])('refuses %s in place of one object', (_, args) => { + expect(() => untyped(...args)).toThrow( + "Moment takes its arguments as one object with the keys as, storage and precision, such as Moment({ as: 'temporal' }).", + ); + }); + + it('refuses an argument the type choice does not have', () => { + expect(() => untyped({ value: 'string' })).toThrow( + 'Moment has no argument "value"; its arguments are as, storage and precision.', ); }); }); @@ -50,7 +92,7 @@ describe('composing helpers for a pack with a type choice', () => { type: { Moment: { ...targetPack.authoring.type.Moment, - choices: [{ when: { value: 'temporal', column: 'tz' }, type: 'Missing' }], + choices: [{ when: { as: 'temporal', storage: 'tz' }, type: 'Missing' }], }, }, }, diff --git a/packages/3-targets/postgres/1-target/src/core/type-constructors.ts b/packages/3-targets/postgres/1-target/src/core/type-constructors.ts index 0bcab774b183..6da52c607271 100644 --- a/packages/3-targets/postgres/1-target/src/core/type-constructors.ts +++ b/packages/3-targets/postgres/1-target/src/core/type-constructors.ts @@ -296,24 +296,24 @@ const precision = { } as const; /** - * The convenience date and time types. Each is a type choice: its `value` and `column` arguments choose the explicit type it becomes, and `precision` is passed on to that type. ADR 279. + * The convenience date and time types. Each is a type choice: its `as` and `storage` arguments choose the explicit type it becomes, and `precision` is passed on to that type. ADR 279. */ export const postgresConvenienceDateTimeTypes = { DateTime: { kind: 'typeChoice', documentation: - 'A moment in time. `value` is what the application reads: jsDate (a JavaScript Date), temporal (Temporal.Instant) or string. `column` is timestamptz or timestamp, a timestamp read and written as UTC.', + 'A moment in time. `as` is what the application reads: jsDate (a JavaScript Date), temporal (Temporal.Instant) or string. `storage` is timestamptz or timestamp, a timestamp read and written as UTC.', args: [ { kind: 'option', - name: 'value', + name: 'as', values: ['jsDate', 'temporal', 'string'], default: 'jsDate', optional: true, }, { kind: 'option', - name: 'column', + name: 'storage', values: ['timestamptz', 'timestamp'], default: 'timestamptz', optional: true, @@ -322,55 +322,55 @@ export const postgresConvenienceDateTimeTypes = { ], choices: [ { - when: { value: 'jsDate', column: 'timestamptz' }, + when: { as: 'jsDate', storage: 'timestamptz' }, type: 'TimestamptzJsDate', defaults: { precision: 3 }, }, - { when: { value: 'temporal', column: 'timestamptz' }, type: 'TimestamptzTemporal' }, - { when: { value: 'string', column: 'timestamptz' }, type: 'TimestamptzString' }, + { when: { as: 'temporal', storage: 'timestamptz' }, type: 'TimestamptzTemporal' }, + { when: { as: 'string', storage: 'timestamptz' }, type: 'TimestamptzString' }, { - when: { value: 'jsDate', column: 'timestamp' }, + when: { as: 'jsDate', storage: 'timestamp' }, type: 'TimestampJsDate', defaults: { precision: 3 }, }, - { when: { value: 'temporal', column: 'timestamp' }, type: 'TimestampInstant' }, - { when: { value: 'string', column: 'timestamp' }, type: 'TimestampString' }, + { when: { as: 'temporal', storage: 'timestamp' }, type: 'TimestampInstant' }, + { when: { as: 'string', storage: 'timestamp' }, type: 'TimestampString' }, ], }, Date: { kind: 'typeChoice', documentation: - 'A calendar date. `value` is what the application reads: string, such as "2024-02-29", or temporal (Temporal.PlainDate).', + 'A calendar date. `as` is what the application reads: string, such as "2024-02-29", or temporal (Temporal.PlainDate).', args: [ { kind: 'option', - name: 'value', + name: 'as', values: ['string', 'temporal'], default: 'string', optional: true, }, - { kind: 'option', name: 'column', values: ['date'], default: 'date', optional: true }, + { kind: 'option', name: 'storage', values: ['date'], default: 'date', optional: true }, ], choices: [ - { when: { value: 'string', column: 'date' }, type: 'DateString' }, - { when: { value: 'temporal', column: 'date' }, type: 'DateTemporal' }, + { when: { as: 'string', storage: 'date' }, type: 'DateString' }, + { when: { as: 'temporal', storage: 'date' }, type: 'DateTemporal' }, ], }, Time: { kind: 'typeChoice', documentation: - 'A time of day. `value` is what the application reads: string, such as "09:30:00", or temporal (Temporal.PlainTime). `column` is time, or timetz with value string.', + 'A time of day. `as` is what the application reads: string, such as "09:30:00", or temporal (Temporal.PlainTime). `storage` is time, or timetz with as: string.', args: [ { kind: 'option', - name: 'value', + name: 'as', values: ['string', 'temporal'], default: 'string', optional: true, }, { kind: 'option', - name: 'column', + name: 'storage', values: ['time', 'timetz'], default: 'time', optional: true, @@ -378,29 +378,35 @@ export const postgresConvenienceDateTimeTypes = { precision, ], choices: [ - { when: { value: 'string', column: 'time' }, type: 'TimeString' }, - { when: { value: 'temporal', column: 'time' }, type: 'TimeTemporal' }, - { when: { value: 'string', column: 'timetz' }, type: 'Timetz' }, + { when: { as: 'string', storage: 'time' }, type: 'TimeString' }, + { when: { as: 'temporal', storage: 'time' }, type: 'TimeTemporal' }, + { when: { as: 'string', storage: 'timetz' }, type: 'Timetz' }, ], }, Duration: { kind: 'typeChoice', documentation: - 'A length of time. `value` is what the application reads: string, an ISO 8601 duration such as "P1M", or parts, the object { months, days, micros }.', + 'A length of time. `as` is what the application reads: string, an ISO 8601 duration such as "P1M", or parts, the object { months, days, micros }.', args: [ { kind: 'option', - name: 'value', + name: 'as', values: ['string', 'parts'], default: 'string', optional: true, }, - { kind: 'option', name: 'column', values: ['interval'], default: 'interval', optional: true }, + { + kind: 'option', + name: 'storage', + values: ['interval'], + default: 'interval', + optional: true, + }, precision, ], choices: [ - { when: { value: 'string', column: 'interval' }, type: 'IntervalString' }, - { when: { value: 'parts', column: 'interval' }, type: 'IntervalParts' }, + { when: { as: 'string', storage: 'interval' }, type: 'IntervalString' }, + { when: { as: 'parts', storage: 'interval' }, type: 'IntervalParts' }, ], }, } as const satisfies AuthoringTypeNamespace; diff --git a/packages/3-targets/postgres/9-facade/test/convenience-type-helpers.test.ts b/packages/3-targets/postgres/9-facade/test/convenience-type-helpers.test.ts index fd7effedfd15..af3c2018c44f 100644 --- a/packages/3-targets/postgres/9-facade/test/convenience-type-helpers.test.ts +++ b/packages/3-targets/postgres/9-facade/test/convenience-type-helpers.test.ts @@ -9,7 +9,7 @@ describe('a convenience type helper', () => { Order: model('Order', { fields: { id: field.id.uuidv4String(), - at: field.column(type.DateTime(undefined, undefined, 7)), + at: field.column(type.DateTime({ precision: 7 })), }, }), }, diff --git a/packages/3-targets/postgres/9-facade/test/no-temporal-global/temporal-field-types.test-d.ts b/packages/3-targets/postgres/9-facade/test/no-temporal-global/temporal-field-types.test-d.ts index c59dd2a5c2d3..f84c6b524b7b 100644 --- a/packages/3-targets/postgres/9-facade/test/no-temporal-global/temporal-field-types.test-d.ts +++ b/packages/3-targets/postgres/9-facade/test/no-temporal-global/temporal-field-types.test-d.ts @@ -58,17 +58,17 @@ const convenience = defineContract({}, ({ type, field, model }) => ({ fields: { id: field.id.uuidv4String(), paidAt: field.column(type.DateTime()), - settledAt: field.column(type.DateTime('temporal')), - textAt: field.column(type.DateTime('string', undefined, 6)), - legacyAt: field.column(type.DateTime(undefined, 'timestamp')), - utcAt: field.column(type.DateTime('temporal', 'timestamp')), + settledAt: field.column(type.DateTime({ as: 'temporal' })), + textAt: field.column(type.DateTime({ as: 'string', precision: 6 })), + legacyAt: field.column(type.DateTime({ storage: 'timestamp' })), + utcAt: field.column(type.DateTime({ as: 'temporal', storage: 'timestamp' })), shipBy: field.column(type.Date()), - shipOn: field.column(type.Date('temporal')), - opensAt: field.column(type.Time(undefined, undefined, 0)), - opensAtT: field.column(type.Time('temporal')), - localAt: field.column(type.Time(undefined, 'timetz')), + shipOn: field.column(type.Date({ as: 'temporal' })), + opensAt: field.column(type.Time({ precision: 0 })), + opensAtT: field.column(type.Time({ as: 'temporal' })), + localAt: field.column(type.Time({ storage: 'timetz' })), period: field.column(type.Duration()), - parts: field.column(type.Duration('parts')), + parts: field.column(type.Duration({ as: 'parts' })), textAtExplicit: field.column(type.TimestamptzString(6)), opensAtExplicit: field.column(type.TimeString(0)), localAtExplicit: field.column(type.Timetz()), @@ -103,9 +103,15 @@ test('a convenience type helper refuses a combination with no explicit type', () fields: { id: field.id.uuidv4String(), // @ts-expect-error Temporal.PlainTime cannot hold a timetz offset - at: field.column(type.Time('temporal', 'timetz')), + at: field.column(type.Time({ as: 'temporal', storage: 'timetz' })), // @ts-expect-error a calendar date is never a JavaScript Date - day: field.column(type.Date('jsDate')), + day: field.column(type.Date({ as: 'jsDate' })), + // @ts-expect-error a calendar date has no precision + dayAt: field.column(type.Date({ precision: 3 })), + // @ts-expect-error the options are as and storage + stamp: field.column(type.DateTime({ value: 'string' })), + // @ts-expect-error a precision is written by name + opens: field.column(type.Time(3)), }, }), }, @@ -115,40 +121,56 @@ test('a convenience type helper refuses a combination with no explicit type', () test('each convenience type call has the type of the explicit type helper it chooses', () => { defineContract({}, ({ type }) => { expectTypeOf(type.DateTime()).toEqualTypeOf(type.TimestamptzJsDate(3)); - expectTypeOf(type.DateTime(undefined, undefined, 6)).toEqualTypeOf(type.TimestamptzJsDate(6)); - expectTypeOf(type.DateTime('jsDate', 'timestamptz')).toEqualTypeOf(type.TimestamptzJsDate(3)); - expectTypeOf(type.DateTime('temporal')).toEqualTypeOf(type.TimestamptzTemporal()); - expectTypeOf(type.DateTime('temporal', undefined, 3)).toEqualTypeOf( + expectTypeOf(type.DateTime({ precision: 6 })).toEqualTypeOf(type.TimestamptzJsDate(6)); + expectTypeOf(type.DateTime({ as: 'jsDate', storage: 'timestamptz' })).toEqualTypeOf( + type.TimestamptzJsDate(3), + ); + expectTypeOf(type.DateTime({ as: 'temporal' })).toEqualTypeOf(type.TimestamptzTemporal()); + expectTypeOf(type.DateTime({ as: 'temporal', precision: 3 })).toEqualTypeOf( type.TimestamptzTemporal(3), ); - expectTypeOf(type.DateTime('string')).toEqualTypeOf(type.TimestamptzString()); - expectTypeOf(type.DateTime('string', 'timestamptz', 6)).toEqualTypeOf( - type.TimestamptzString(6), + expectTypeOf(type.DateTime({ as: 'string' })).toEqualTypeOf(type.TimestamptzString()); + expectTypeOf( + type.DateTime({ as: 'string', storage: 'timestamptz', precision: 6 }), + ).toEqualTypeOf(type.TimestamptzString(6)); + expectTypeOf(type.DateTime({ storage: 'timestamp' })).toEqualTypeOf(type.TimestampJsDate(3)); + expectTypeOf(type.DateTime({ as: 'jsDate', storage: 'timestamp', precision: 0 })).toEqualTypeOf( + type.TimestampJsDate(0), + ); + expectTypeOf(type.DateTime({ as: 'temporal', storage: 'timestamp' })).toEqualTypeOf( + type.TimestampInstant(), + ); + expectTypeOf( + type.DateTime({ as: 'temporal', storage: 'timestamp', precision: 6 }), + ).toEqualTypeOf(type.TimestampInstant(6)); + expectTypeOf(type.DateTime({ as: 'string', storage: 'timestamp' })).toEqualTypeOf( + type.TimestampString(), + ); + expectTypeOf(type.DateTime({ as: 'string', storage: 'timestamp', precision: 3 })).toEqualTypeOf( + type.TimestampString(3), ); - expectTypeOf(type.DateTime(undefined, 'timestamp')).toEqualTypeOf(type.TimestampJsDate(3)); - expectTypeOf(type.DateTime('jsDate', 'timestamp', 0)).toEqualTypeOf(type.TimestampJsDate(0)); - expectTypeOf(type.DateTime('temporal', 'timestamp')).toEqualTypeOf(type.TimestampInstant()); - expectTypeOf(type.DateTime('temporal', 'timestamp', 6)).toEqualTypeOf(type.TimestampInstant(6)); - expectTypeOf(type.DateTime('string', 'timestamp')).toEqualTypeOf(type.TimestampString()); - expectTypeOf(type.DateTime('string', 'timestamp', 3)).toEqualTypeOf(type.TimestampString(3)); expectTypeOf(type.Date()).toEqualTypeOf(type.DateString()); - expectTypeOf(type.Date('string', 'date')).toEqualTypeOf(type.DateString()); - expectTypeOf(type.Date('temporal')).toEqualTypeOf(type.DateTemporal()); + expectTypeOf(type.Date({ as: 'string', storage: 'date' })).toEqualTypeOf(type.DateString()); + expectTypeOf(type.Date({ as: 'temporal' })).toEqualTypeOf(type.DateTemporal()); expectTypeOf(type.Time()).toEqualTypeOf(type.TimeString()); - expectTypeOf(type.Time(undefined, undefined, 0)).toEqualTypeOf(type.TimeString(0)); - expectTypeOf(type.Time('temporal')).toEqualTypeOf(type.TimeTemporal()); - expectTypeOf(type.Time('temporal', 'time', 3)).toEqualTypeOf(type.TimeTemporal(3)); - expectTypeOf(type.Time('string', 'timetz')).toEqualTypeOf(type.Timetz()); - expectTypeOf(type.Time(undefined, 'timetz', 2)).toEqualTypeOf(type.Timetz(2)); + expectTypeOf(type.Time({ precision: 0 })).toEqualTypeOf(type.TimeString(0)); + expectTypeOf(type.Time({ as: 'temporal' })).toEqualTypeOf(type.TimeTemporal()); + expectTypeOf(type.Time({ as: 'temporal', storage: 'time', precision: 3 })).toEqualTypeOf( + type.TimeTemporal(3), + ); + expectTypeOf(type.Time({ as: 'string', storage: 'timetz' })).toEqualTypeOf(type.Timetz()); + expectTypeOf(type.Time({ storage: 'timetz', precision: 2 })).toEqualTypeOf(type.Timetz(2)); expectTypeOf(type.Duration()).toEqualTypeOf(type.IntervalString()); - expectTypeOf(type.Duration(undefined, undefined, 3)).toEqualTypeOf(type.IntervalString(3)); - expectTypeOf(type.Duration('parts')).toEqualTypeOf(type.IntervalParts()); - expectTypeOf(type.Duration('parts', 'interval', 6)).toEqualTypeOf(type.IntervalParts(6)); + expectTypeOf(type.Duration({ precision: 3 })).toEqualTypeOf(type.IntervalString(3)); + expectTypeOf(type.Duration({ as: 'parts' })).toEqualTypeOf(type.IntervalParts()); + expectTypeOf(type.Duration({ as: 'parts', storage: 'interval', precision: 6 })).toEqualTypeOf( + type.IntervalParts(6), + ); const widened: number = 4; - expectTypeOf(type.DateTime('string', undefined, widened)).toEqualTypeOf( + expectTypeOf(type.DateTime({ as: 'string', precision: widened })).toEqualTypeOf( type.TimestamptzString(widened), ); - expectTypeOf(type.DateTime(undefined, undefined, widened)).not.toEqualTypeOf( + expectTypeOf(type.DateTime({ precision: widened })).not.toEqualTypeOf( type.TimestamptzString(widened), ); return { models: {} }; diff --git a/skills/prisma-8/references/contract.md b/skills/prisma-8/references/contract.md index 4a8d02fe7926..727a79944e90 100644 --- a/skills/prisma-8/references/contract.md +++ b/skills/prisma-8/references/contract.md @@ -117,9 +117,9 @@ model Post { Then run `pnpm prisma contract emit` (or rely on the Vite plugin — see `references/build.md`). Specify cascade behaviour explicitly with `onDelete` / `onUpdate`; the default is `Restrict`. -**Date and time types.** On PostgreSQL, write a field with a convenience type named for what it means: `DateTime` (a moment in time, a `timestamptz(3)` read as a JavaScript `Date`), `Date` (a calendar date, read as a string such as `"2024-02-29"`), `Time` (a time of day, read as a string) or `Duration` (an ISO 8601 duration string such as `"P1M"`). None needs `Temporal`. Optional arguments choose another explicit type: `value` (`jsDate`, `temporal` or `string` for `DateTime`; `string` or `temporal` for `Date` and `Time`; `string` or `parts` for `Duration`), `column` (`timestamptz` or `timestamp` for `DateTime`; `time` or, with `value: string`, `timetz` for `Time`), and `precision`: `DateTime(value: temporal)`, `DateTime(column: timestamp)`, `Time(precision: 0)`. The contract stores only the explicit type, and `contract print` writes it; the full table is [Scalar types](https://github.com/prisma/orm/blob/main/docs/reference/scalar-types.md). `Time(3)` is refused, because the first argument is `value`; write `Time(precision: 3)`. +**Date and time types.** On PostgreSQL, write a field with a convenience type named for what it means: `DateTime` (a moment in time, a `timestamptz(3)` read as a JavaScript `Date`), `Date` (a calendar date, read as a string such as `"2024-02-29"`), `Time` (a time of day, read as a string) or `Duration` (an ISO 8601 duration string such as `"P1M"`). None needs `Temporal`. Optional arguments choose another explicit type: `as`, the value the application reads (`jsDate`, `temporal` or `string` for `DateTime`; `string` or `temporal` for `Date` and `Time`; `string` or `parts` for `Duration`), `storage`, the database type that stores it (`timestamptz` or `timestamp` for `DateTime`; `time` or, with `as: string`, `timetz` for `Time`), and `precision`: `DateTime(as: temporal)`, `DateTime(storage: timestamp)`, `Time(precision: 0)`. The contract stores only the explicit type, and `contract print` writes it; the full table is [Scalar types](https://github.com/prisma/orm/blob/main/docs/reference/scalar-types.md). `Time(3)` is refused, because the first argument is `as`; write `Time(precision: 3)`. In a TypeScript contract the helpers take one object: `type.DateTime({ as: 'string', precision: 6 })`, `type.Time({ storage: 'timetz' })`. -**Temporal columns.** On PostgreSQL, `DateTemporal`, `TimestampTemporal(p)`, `TimestampInstant(p)`, `TimestamptzTemporal(p)` and `TimeTemporal(p)` (and a convenience type with `value: temporal`) read and write `Temporal` values (`Temporal.PlainDate`, `PlainDateTime`, `Instant` for both instant types, `PlainTime`), never JavaScript `Date`. They need a global `Temporal` at query time: Node.js 26.8.2 and later ship `globalThis.Temporal`; 26.8.1 and earlier — including every 22 and 24 release — do not, and the first read or write of such a column throws `RUNTIME.TEMPORAL_UNAVAILABLE`. On those runtimes either `import 'temporal-polyfill/full/global'` before the first query (add `temporal-polyfill` as a dependency) or author the column as `DateString` / `TimestampString(p)` / `TimestamptzString(p)` / `TimeString(p)`, which carry text and need no `Temporal`, or as `TimestamptzJsDate(p)` / `TimestampJsDate(p)`, which read a JavaScript `Date`. `IntervalParts(p)` reads `{ months, days, micros }` and `IntervalString(p)` an ISO 8601 duration such as `P1Y2M`. `prisma contract emit`, `prisma db init` and the other commands need no global `Temporal`: the Postgres target loads `temporal-polyfill` as a fallback `Temporal` for the control plane, and sets no global. `temporal-polyfill` is a required peer dependency of `@prisma/orm-postgres`. npm, pnpm and bun install it automatically. With Yarn, add `temporal-polyfill` (`^1.0.4`) to the project's dependencies, or the commands fail because Node.js cannot find the package. That fallback is held once per process. An application that loads control-plane code in its own process, such as server code under `vite dev` with the Prisma Vite plugin or a script that calls the control client, decodes dates without its own `Temporal`, and then fails in production. An application that uses the Temporal codecs must load its own `Temporal`. A TypeScript contract file that constructs a `Temporal` value must load an implementation too. +**Temporal columns.** On PostgreSQL, `DateTemporal`, `TimestampTemporal(p)`, `TimestampInstant(p)`, `TimestamptzTemporal(p)` and `TimeTemporal(p)` (and a convenience type with `as: temporal`) read and write `Temporal` values (`Temporal.PlainDate`, `PlainDateTime`, `Instant` for both instant types, `PlainTime`), never JavaScript `Date`. They need a global `Temporal` at query time: Node.js 26.8.2 and later ship `globalThis.Temporal`; 26.8.1 and earlier — including every 22 and 24 release — do not, and the first read or write of such a column throws `RUNTIME.TEMPORAL_UNAVAILABLE`. On those runtimes either `import 'temporal-polyfill/full/global'` before the first query (add `temporal-polyfill` as a dependency) or author the column as `DateString` / `TimestampString(p)` / `TimestamptzString(p)` / `TimeString(p)`, which carry text and need no `Temporal`, or as `TimestamptzJsDate(p)` / `TimestampJsDate(p)`, which read a JavaScript `Date`. `IntervalParts(p)` reads `{ months, days, micros }` and `IntervalString(p)` an ISO 8601 duration such as `P1Y2M`. `prisma contract emit`, `prisma db init` and the other commands need no global `Temporal`: the Postgres target loads `temporal-polyfill` as a fallback `Temporal` for the control plane, and sets no global. `temporal-polyfill` is a required peer dependency of `@prisma/orm-postgres`. npm, pnpm and bun install it automatically. With Yarn, add `temporal-polyfill` (`^1.0.4`) to the project's dependencies, or the commands fail because Node.js cannot find the package. That fallback is held once per process. An application that loads control-plane code in its own process, such as server code under `vite dev` with the Prisma Vite plugin or a script that calls the control client, decodes dates without its own `Temporal`, and then fails in production. An application that uses the Temporal codecs must load its own `Temporal`. A TypeScript contract file that constructs a `Temporal` value must load an implementation too. `@@index` also accepts `expression:` (instead of a fields list), `where:` (partial-index predicate), `unique:`, `type:`/`options:` (target-registered access method), and `name:` xor `map:`. Raw SQL in `expression:` and `where:` is a `sql` literal; a quoted string is refused with `PSL_VALUE_TYPE_INCOMPATIBLE`, whose message gives the rewrite: diff --git a/skills/prisma-8/references/debug.md b/skills/prisma-8/references/debug.md index a20f6250020f..a65c5a1f0707 100644 --- a/skills/prisma-8/references/debug.md +++ b/skills/prisma-8/references/debug.md @@ -110,7 +110,7 @@ The single source of truth: read the envelope, find the row by `code`, follow th | `BUDGET.ROWS_EXCEEDED` / `BUDGET.TIME_EXCEEDED` | Runtime, when the `budgets` middleware is active | Tune `budgets({ maxRows, maxLatencyMs, ... })` or rewrite the query. See `references/runtime.md`. | | `LINT.SELECT_STAR` / `LINT.NO_LIMIT` / `LINT.DELETE_WITHOUT_WHERE` / `LINT.UPDATE_WITHOUT_WHERE` / `LINT.READ_ONLY_MUTATION` | Runtime, when the `lints` middleware is active | Fix the query (add a `WHERE` / `LIMIT` / explicit columns), or relax the lint config. See `references/runtime.md`. | | `PLAN.HASH_MISMATCH` | Runtime, executing a precompiled plan | The contract the plan was built against does not match the runtime contract. Re-emit, rebuild, redeploy. | -| `RUNTIME.TEMPORAL_UNAVAILABLE` | Runtime, first read or write touching a Temporal-backed column (`DateTemporal`, `TimestampTemporal(p)`, `TimestampInstant(p)`, `TimestamptzTemporal(p)`, `TimeTemporal(p)`, and `DateTime`, `Date` or `Time` with `value: temporal`) or a `temporal.updatedAt()` clock | No global `Temporal`. Node.js 26.8.2 and later ship `globalThis.Temporal`; 26.8.1 and earlier — including every 22 and 24 — do not. Either `import 'temporal-polyfill/full/global'` before the first query, or write the field as a convenience type at its default (`DateTime`, `Date`, `Time`), which reads a JavaScript `Date` or a string. | +| `RUNTIME.TEMPORAL_UNAVAILABLE` | Runtime, first read or write touching a Temporal-backed column (`DateTemporal`, `TimestampTemporal(p)`, `TimestampInstant(p)`, `TimestamptzTemporal(p)`, `TimeTemporal(p)`, and `DateTime`, `Date` or `Time` with `as: temporal`) or a `temporal.updatedAt()` clock | No global `Temporal`. Node.js 26.8.2 and later ship `globalThis.Temporal`; 26.8.1 and earlier — including every 22 and 24 — do not. Either `import 'temporal-polyfill/full/global'` before the first query, or write the field as a convenience type at its default (`DateTime`, `Date`, `Time`), which reads a JavaScript `Date` or a string. | | `ORM.RELATION_MUTATION_UNSUPPORTED` | ORM nested `create` / `connect` on an N:M relation whose junction has required payload columns | Write the junction table directly or use the SQL builder (`meta.junction`). | | `RUNTIME.ABORTED` (`details.phase` = `encode\|decode\|stream\|beforeEncode\|onRow`) | Runtime, when an `AbortSignal` fires mid-execute | Cancellation, not a bug; surface to the caller. | | `SqlQueryError` (no dotted code) | Raw-SQL paths surfacing a driver error | Inspect `sqlState` + `constraint` + `table` + `column`. Postgres `23505` = unique violation, `23503` = foreign-key violation, etc. Fix the data or the schema. | diff --git a/test/integration/test/authoring/parity/ts-psl-parity.real-packs.test.ts b/test/integration/test/authoring/parity/ts-psl-parity.real-packs.test.ts index 1509144e62ce..5b8d8a9e3ec9 100644 --- a/test/integration/test/authoring/parity/ts-psl-parity.real-packs.test.ts +++ b/test/integration/test/authoring/parity/ts-psl-parity.real-packs.test.ts @@ -362,15 +362,17 @@ ${calls.map((call) => ` ${call.column} ${pslFieldType(call)}`).join('\n')} fields: { id: field.column(int4Column).id(), paid: field.column(type.DateTime()), - settled: field.column(type.DateTime('temporal')), - legacy: field.column(type.DateTime('jsDate', 'timestamp', 6)), - text: field.column(type.DateTime('string', 'timestamp')), + settled: field.column(type.DateTime({ as: 'temporal' })), + legacy: field.column( + type.DateTime({ as: 'jsDate', storage: 'timestamp', precision: 6 }), + ), + text: field.column(type.DateTime({ as: 'string', storage: 'timestamp' })), ship: field.column(type.Date()), - shipT: field.column(type.Date('temporal')), - opens: field.column(type.Time(undefined, undefined, 0)), - local: field.column(type.Time('string', 'timetz')), - period: field.column(type.Duration(undefined, undefined, 3)), - parts: field.column(type.Duration('parts')), + shipT: field.column(type.Date({ as: 'temporal' })), + opens: field.column(type.Time({ precision: 0 })), + local: field.column(type.Time({ as: 'string', storage: 'timetz' })), + period: field.column(type.Duration({ precision: 3 })), + parts: field.column(type.Duration({ as: 'parts' })), }, }).sql({ table: 'Sample' }), }, @@ -380,15 +382,15 @@ ${calls.map((call) => ` ${call.column} ${pslFieldType(call)}`).join('\n')} const interpreted = interpretWithRealPacks(`model Sample { id Int @id paid DateTime - settled DateTime(value: temporal) + settled DateTime(as: temporal) legacy DateTime(jsDate, timestamp, 6) - text DateTime(value: string, column: timestamp) + text DateTime(as: string, storage: timestamp) ship Date shipT Date(temporal) opens Time(precision: 0) - local Time(column: timetz) + local Time(storage: timetz) period Duration(precision: 3) - parts Duration(value: parts) + parts Duration(as: parts) } `); diff --git a/test/integration/test/cli-journeys/convenience-date-time-types-without-temporal.e2e.test.ts b/test/integration/test/cli-journeys/convenience-date-time-types-without-temporal.e2e.test.ts index cccdd133e74e..823f213dd761 100644 --- a/test/integration/test/cli-journeys/convenience-date-time-types-without-temporal.e2e.test.ts +++ b/test/integration/test/cli-journeys/convenience-date-time-types-without-temporal.e2e.test.ts @@ -1,5 +1,5 @@ /** - * Journey: a schema written with the convenience date and time types `DateTime`, `Date`, `Time` and `Duration`, at their defaults and with the choices that need no `Temporal`, emits, creates its table, and is written and read by an application that runs with no global `Temporal`. Each field reads the kind of value its convenience type chooses: a JavaScript `Date` for `DateTime`, a string for `Date`, `Time` and `Duration`, and the `{ months, days, micros }` object for `Duration(value: parts)`. The client generates `updatedAt` with `now()` in that process. + * Journey: a schema written with the convenience date and time types `DateTime`, `Date`, `Time` and `Duration`, at their defaults and with the choices that need no `Temporal`, emits, creates its table, and is written and read by an application that runs with no global `Temporal`. Each field reads the kind of value its convenience type chooses: a JavaScript `Date` for `DateTime`, a string for `Date`, `Time` and `Duration`, and the `{ months, days, micros }` object for `Duration(as: parts)`. The client generates `updatedAt` with `now()` in that process. */ import { writeFileSync } from 'node:fs'; import { join } from 'pathe'; @@ -26,14 +26,14 @@ const SCHEMA = `// use prisma-8 model Order { id Int @id paidAt DateTime - legacyAt DateTime(column: timestamp) - textAt DateTime(value: string, precision: 6) + legacyAt DateTime(storage: timestamp) + textAt DateTime(as: string, precision: 6) shipBy Date opensAt Time(precision: 0) - localAt Time(column: timetz) + localAt Time(storage: timetz) period Duration - parts Duration(value: parts) - settledAt DateTime(value: temporal)? + parts Duration(as: parts) + settledAt DateTime(as: temporal)? updatedAt DateTime @onCreate(now()) @onUpdate(now()) @@map("order") diff --git a/test/integration/test/convenience-date-time-types/convenience-types.integration.test.ts b/test/integration/test/convenience-date-time-types/convenience-types.integration.test.ts index 3d39d50d9435..43dbf7acead0 100644 --- a/test/integration/test/convenience-date-time-types/convenience-types.integration.test.ts +++ b/test/integration/test/convenience-date-time-types/convenience-types.integration.test.ts @@ -14,23 +14,23 @@ const model = (...fields: readonly string[]) => const rows: readonly (readonly [convenience: string, explicit: string])[] = [ ['DateTime', 'TimestamptzJsDate(3)'], - ['DateTime(value: temporal)', 'TimestamptzTemporal'], - ['DateTime(value: string)', 'TimestamptzString'], - ['DateTime(column: timestamp)', 'TimestampJsDate(3)'], - ['DateTime(value: temporal, column: timestamp)', 'TimestampInstant'], - ['DateTime(value: string, column: timestamp)', 'TimestampString'], - ['DateTime(value: string, precision: 6)', 'TimestamptzString(6)'], + ['DateTime(as: temporal)', 'TimestamptzTemporal'], + ['DateTime(as: string)', 'TimestamptzString'], + ['DateTime(storage: timestamp)', 'TimestampJsDate(3)'], + ['DateTime(as: temporal, storage: timestamp)', 'TimestampInstant'], + ['DateTime(as: string, storage: timestamp)', 'TimestampString'], + ['DateTime(as: string, precision: 6)', 'TimestamptzString(6)'], ['DateTime(precision: 6)', 'TimestamptzJsDate(6)'], ['DateTime(temporal, timestamptz, 3)', 'TimestamptzTemporal(3)'], ['Date', 'DateString'], - ['Date(value: temporal)', 'DateTemporal'], + ['Date(as: temporal)', 'DateTemporal'], ['Time', 'TimeString'], ['Time(precision: 0)', 'TimeString(0)'], - ['Time(value: temporal)', 'TimeTemporal'], - ['Time(column: timetz)', 'Timetz'], + ['Time(as: temporal)', 'TimeTemporal'], + ['Time(storage: timetz)', 'Timetz'], ['Duration', 'IntervalString'], ['Duration(precision: 3)', 'IntervalString(3)'], - ['Duration(value: parts)', 'IntervalParts'], + ['Duration(as: parts)', 'IntervalParts'], ['DateTime[]', 'TimestamptzJsDate(3)[]'], ['DateTime?', 'TimestamptzJsDate(3)?'], ]; @@ -50,7 +50,7 @@ describe('a convenience date and time type', () => { 'updatedAt DateTime @onCreate(now()) @onUpdate(now())', 'day Date @onCreate(now())', 'clock Time(precision: 0) @onCreate(now())', - 'instant DateTime(value: temporal) @onCreate(now())', + 'instant DateTime(as: temporal) @onCreate(now())', ), ), ).toEqual( @@ -70,7 +70,7 @@ describe('a convenience date and time type', () => { const convenience = `// use prisma-8 types { - Stamp = DateTime(column: timestamp) @default(timestamp\`2024-01-01 00:00:00\`) + Stamp = DateTime(storage: timestamp) @default(timestamp\`2024-01-01 00:00:00\`) } model Order { @@ -91,22 +91,19 @@ model Order { it('refuses a default of another date and time type, naming the column’s', async () => { await expect( - readPsl(model('at DateTime(column: timestamp) @default(timestamptz`2024-01-01T00:00:00Z`)')), + readPsl(model('at DateTime(storage: timestamp) @default(timestamptz`2024-01-01T00:00:00Z`)')), ).rejects.toThrow(/PSL_VALUE_TYPE_INCOMPATIBLE.*Expected timestamp`\.\.\.`/); }); it.each([ [ - 'Time(value: temporal, column: timetz)', - 'Argument \\"column\\" of Time cannot be timetz when value is temporal; write one of: time.', + 'Time(as: temporal, storage: timetz)', + 'Argument \\"storage\\" of Time cannot be timetz with as: temporal; write one of: time.', ], + ['Date(as: jsDate)', 'Argument \\"as\\" of Date is jsDate; write one of: string, temporal.'], [ - 'Date(value: jsDate)', - 'Argument \\"value\\" of Date is jsDate; write one of: string, temporal.', - ], - [ - 'Duration(value: parts, column: date)', - 'Argument \\"column\\" of Duration is date; write one of: interval.', + 'Duration(as: parts, storage: date)', + 'Argument \\"storage\\" of Duration is date; write one of: interval.', ], ])('refuses %s at the argument, naming the accepted choices', async (written, message) => { await expect(readPsl(model(`at ${written}`))).rejects.toThrow(message); @@ -114,7 +111,7 @@ model Order { it('refuses a precision written in the place of the value option, saying to write it by name', async () => { await expect(readPsl(model('at Time(3)'))).rejects.toThrow( - 'Field \\"Order.at\\" type \\"Time\\" argument #1 is 3, but argument \\"value\\" takes one of these names, written without quotes: string, temporal. Write precision by name, as in precision: 3.', + 'Field \\"Order.at\\" type \\"Time\\" argument #1 is 3, but argument \\"as\\" takes one of these names, written without quotes: string, temporal. Write precision by name, as in precision: 3.', ); }); diff --git a/upgrade-instructions/pending/convenience-date-time-types/app/instructions.md b/upgrade-instructions/pending/convenience-date-time-types/app/instructions.md index 88fc7912faf9..2c26d5cc131e 100644 --- a/upgrade-instructions/pending/convenience-date-time-types/app/instructions.md +++ b/upgrade-instructions/pending/convenience-date-time-types/app/instructions.md @@ -9,7 +9,7 @@ changes: - '(?` with the hash it gives, which writes a migration with no operations, then `prisma db migrate`. - With `prisma db update`: run it. It applies no operations and records the new contract on the database. -- **`DateTime`** makes the column `timestamptz(3)`, and a schema that wrote `DateTime` before had a `timestamptz` column, which holds microseconds. Lowering the precision rounds stored values to milliseconds, so both commands ask before they change it. Run `prisma migration plan --delete .` and then `prisma db migrate`, or `prisma db update --delete .`, which accepts the rounding. A JavaScript `Date` holds milliseconds only, so to keep microseconds, keep the `Temporal.Instant` field: write `DateTime(value: temporal)`, which is exactly `TimestamptzTemporal`. +- **`DateTime`** makes the column `timestamptz(3)`, and a schema that wrote `DateTime` before had a `timestamptz` column, which holds microseconds. Lowering the precision rounds stored values to milliseconds, so both commands ask before they change it. Run `prisma migration plan --delete .` and then `prisma db migrate`, or `prisma db update --delete .`, which accepts the rounding. A JavaScript `Date` holds milliseconds only, so to keep microseconds, keep the `Temporal.Instant` field: write `DateTime(as: temporal)`, which is exactly `TimestamptzTemporal`. ## `ts-date-time-helpers-are-convenience-types` From cd49fcfaca6a80a18a399907c0a4e959f96fa3af Mon Sep 17 00:00:00 2001 From: willbot Date: Fri, 23 Oct 2026 15:43:13 +0200 Subject: [PATCH 081/118] TML-3418: PostgreSQL's string date and time types read ISO 8601 on every server (#30851) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ## Linked issue Fixes TML-3418. Part of the Convenience date and time types project (TML-3709); stacked on the design PR, prisma/orm#30834. ## Summary A `TimestamptzString` column holding the instant `2024-01-01T00:00:00Z`: | Read | Before | After | | --- | --- | --- | | on a server in UTC | `2024-01-01 00:00:00+00` | `2024-01-01T00:00:00Z` | | on a server set to `Europe/Berlin` | `2024-01-01 01:00:00+01` | `2024-01-01T00:00:00Z` | | holding 44 BC | `0044-03-15 12:00:00+00 BC` | `-000043-03-15T12:00:00Z` | | inside an `include` | the same display text | the same ISO text | **The decision: PostgreSQL's string date and time types read ISO 8601, the same on every server, and the PostgreSQL target requires `DateStyle=ISO` on every connection, which the driver ensures.** ### Why The string types returned PostgreSQL's display text, whose shape depends on the server's `TimeZone` and `DateStyle` settings. The same row read differently on two servers, and a value written by the application did not read back as written. The convenience types `Date`, `Time` and `Duration` (prisma/orm#30847) default to these string types, so they have to be predictable. ### The codecs read the form the contract stores `TimestamptzString`, `TimestampString`, `DateString`, `TimeString` and `Timetz` now turn PostgreSQL's text into the ISO 8601 form the contract already stores for these types (ADR 254, "Date and time types"). A `timestamptz` is converted to UTC; `timetz` keeps its offset; BC years and years past 9999 use ISO's signed years; `infinity` stays `infinity`. A value written in that form reads back unchanged. Codec ids and contracts do not change. The `now()` generators for these types write the same form, and the timestamp ones round down to the column's precision, as the time ones already did. That adds a `precision` parameter to those generators, so a contract using them gets a new execution hash; the upgrade instruction says to re-emit. ### The target declares `DateStyle=ISO`, and the driver ensures it PostgreSQL prints a date or timestamp in the session's `DateStyle`, so ISO text needs `DateStyle=ISO`. The PostgreSQL target states this as data: ```ts sessionSettings: { DateStyle: 'ISO' } ``` The execution and control stacks hand the target's settings to the driver (`create(options, sessionSettings)`), and the driver makes every connection it hands out satisfy them: - **A connection Prisma opens**, from a URL, the facades, the CLI or Supabase, starts with the settings as startup options. That costs nothing per query, and `RESET ALL` keeps them. - **A `pg.Pool` or `pg.Client` the application passes in** is already connected, so the driver runs `set_config` on each connection before first use, and again after a connection held for a transaction is released, because the holder may have reset the session. Supabase's `RESET ALL` at the end of a role scope is that case. The codecs never set anything. If a connection still prints another style, for example because a statement set a different `DateStyle` or a connection pooler dropped the startup option, every date and time codec fails with `RUNTIME.DATE_STYLE_NOT_ISO` instead of misreading the value. The error reference gives the way out: pass your own `pg.Pool` behind a pooler that refuses startup options, or set `DateStyle` on the role or database. ADR 159 records the new rule: a target declares session settings, and the driver ensures them. ## Testing performed - A test walks every registered date and timestamp codec and checks each refuses a non-ISO `DateStyle`; removing the check from one codec makes it fail. - Real sessions that start in `German` DateStyle: a supplied pool and a supplied client read ISO before first use and after an exclusive connection is released; Supabase reads ISO across two role scopes with `German` set in between; a setting the server rejects refuses and evicts the connection. The release tests fail with the re-apply switched off. - The startup-option path against a recording server, including `PGOPTIONS` changed after the pool exists. - Package tests: target-postgres, driver-postgres, adapter-postgres, framework-components, contract-psl, contract-prisma7, family-sql, the facade and Supabase; the codec testkit's conformance and date-time-text files. Integration: infer round trip, enum round trip, temporal defaults, defaults read by codec, include codecs. - `pnpm build`, `pnpm typecheck`, `pnpm fixtures:check`, `pnpm lint:deps`, `pnpm lint:ratchets`, `pnpm check:error-reference`, `pnpm lint:docs`, `pnpm check:upgrade-coverage --mode pr`, and lint for every touched package. Coverage is above each package's threshold. ## Skill update `skills/prisma-8` references describe ISO values and the `DateStyle` error. Upgrade instructions: an `app` instruction (string values are ISO; re-emit for the generator precision; don't set a non-ISO `DateStyle`) and an `extension` instruction (a driver ensures the session settings its target declares, and a wrapped driver descriptor passes them on). ## Checklist - [x] All commits are signed off. - [x] Scoped to one logical concern. - [x] Tests are updated. - [x] The PR title is in `TML-NNNN: ` form. - [x] The Skill update section is filled in. ## Notes for the reviewer - **Hosted poolers are not tested.** Supavisor, Neon's pooler, Hyperdrive, Prisma Postgres and PgBouncer need real services. PgBouncer tracks `DateStyle` by default, but an older one rejects the `options` startup parameter unless configured to ignore it. Where a pooler refuses or drops the option, the failure is a connection error or `RUNTIME.DATE_STYLE_NOT_ISO`, never a misread value. - An enum may now use the string timestamp codecs, because a query returns the same text the contract stores. - One journey in prisma/orm#30847 asserts the old display text for two values; whichever of the two PRs merges second updates it. - **Merging with the explicit types PR, prisma/orm#30844.** Both change `decodeDate` in `date-codecs.ts`. Keep `requireIsoDateStyle` inside the shared `decodeDate`, so `TimestampJsDate` gets the check too. `date-style-every-codec.test.ts` reads every date and timestamp codec from the registry and fails for any codec that skips the check. **Alternatives considered:** - **The codecs set `DateStyle`, or the runtime sends `SET` before each query.** A codec has no connection, and a statement per query costs a round trip on every query. - **A startup option on every connection, including supplied ones.** A supplied pool is already connected; only a statement can reach it. - **Re-apply the setting after every transaction on connections Prisma opens too.** A statement per transaction for every user, to undo a statement the application ran itself. - **Parse every `DateStyle`.** The `SQL` and `German` styles are ambiguous for some dates, and the parser would be large. Agent: thales-11 🤖 Generated with [Claude Code](https://claude.com/claude-code) ## Summary by CodeRabbit * **New Features** * PostgreSQL date and time string values now use canonical ISO 8601 forms, including UTC timestamps and normalized offsets. Expanded years and supported edge values are handled consistently. * Drivers apply required PostgreSQL session settings, including `DateStyle=ISO`, across managed and supplied connections. * Timestamp `now()` defaults now honor column precision. * Timestamp and time string codecs can now be used in enums. * **Bug Fixes** * Reads using a non-ISO PostgreSQL `DateStyle` now report a specific error with guidance. * Supabase and serverless PostgreSQL connections now receive the required session settings. * **Upgrade Notes** * Review date and time string parsing, comparisons, display, and literal values for the ISO 8601 format change. --------- Signed-off-by: willbot Signed-off-by: Will Madden Co-authored-by: Claude Opus 5.5 --- architecture.config.json | 6 + ... 159 - Driver Terminology and Lifecycle.md | 20 +- .../adrs/ADR 254 - Data types and casts.md | 8 +- docs/reference/codec-authoring-guide.md | 4 +- docs/reference/error-reference.md | 8 +- docs/reference/scalar-types.md | 14 +- .../generated/prisma8/contract.d.ts | 20 +- .../generated/prisma8/contract.json | 17 +- gotchas.md | 4 +- .../src/control/control-descriptors.ts | 3 + .../src/control/control-stack.ts | 17 +- .../src/execution/execution-descriptors.ts | 4 +- .../src/execution/execution-stack.ts | 22 +- .../src/exports/components.ts | 1 + .../framework-components/src/shared/codec.ts | 7 +- .../src/shared/data-type.ts | 4 +- .../src/shared/framework-components.ts | 6 + .../test/codec-impl-values.test.ts | 52 ++++ .../test/session-settings.test.ts | 95 +++++++ .../fixtures/defaults/expected-contract.json | 12 +- .../expected-contract.json | 12 +- .../expected-contract.json | 12 +- .../updated-at/expected-contract.json | 22 +- .../test/now-default-generators.test.ts | 10 +- .../test/updated-at-generators.test.ts | 10 +- .../test/interpreter.alias-expansion.test.ts | 4 +- .../supabase/src/runtime/supabase.ts | 20 +- .../test/date-style-startup-option.test.ts | 42 +++ .../test/session-settings.integration.test.ts | 75 ++++++ .../postgres/1-target/src/core/authoring.ts | 2 + .../postgres/1-target/src/core/codecs.ts | 16 +- .../postgres/1-target/src/core/date-codecs.ts | 6 +- .../src/core/date-time-now-generators.ts | 14 +- .../1-target/src/core/date-time-wire-text.ts | 56 ++++ .../src/core/descriptor-meta-runtime.ts | 1 + .../postgres/1-target/src/core/errors.ts | 28 +- .../1-target/src/core/now-generators.ts | 6 +- .../1-target/src/core/postgres-year-text.ts | 4 +- .../src/core/temporal-codec-helpers.ts | 7 + .../src/core/temporal-string-codecs.ts | 49 ++-- .../1-target/src/core/type-constructors.ts | 15 +- .../test/authoring-field-presets.test.ts | 6 +- .../test/codec-decode-json-forms.test.ts | 12 +- .../test/codec-render-value-literal.test.ts | 6 +- .../1-target/test/codecs-class.test.ts | 6 +- .../postgres/1-target/test/codecs.test.ts | 4 +- .../1-target/test/date-codecs.test.ts | 14 + .../test/date-style-every-codec.test.ts | 34 +++ .../date-time-codecs.canonical-form.test.ts | 2 +- .../test/date-time-text-wording.test.ts | 59 +++++ .../1-target/test/descriptor-meta.test.ts | 8 + .../1-target/test/enum-eligibility.test.ts | 8 +- .../1-target/test/generator-functions.test.ts | 4 +- .../1-target/test/now-generators.test.ts | 57 +++- .../1-target/test/postgres-year-text.test.ts | 1 + .../1-target/test/temporal-codecs.test.ts | 28 +- .../test/temporal-string-codecs.test.ts | 250 ++++++++++++------ .../test/text-timestamp-codec-encode.test.ts | 6 +- .../2-adapter/src/exports/column-types.ts | 2 +- ...mporal-codec-roundtrip.integration.test.ts | 6 +- ...string-codec-roundtrip.integration.test.ts | 65 ++--- .../3-targets/postgres/3-driver/README.md | 3 + .../3-targets/postgres/3-driver/package.json | 1 + .../postgres/3-driver/src/exports/control.ts | 14 +- .../postgres/3-driver/src/exports/runtime.ts | 16 +- .../postgres/3-driver/src/postgres-driver.ts | 78 ++++-- .../postgres/3-driver/src/session-settings.ts | 119 +++++++++ .../postgres/3-driver/test/control.test.ts | 12 +- ...river.session-settings.integration.test.ts | 101 +++++++ .../test/driver.startup-options.test.ts | 82 ++++++ .../3-driver/test/session-settings.test-d.ts | 12 + .../3-driver/test/session-settings.test.ts | 197 ++++++++++++++ .../test/codec-conformance/cases.ts | 34 ++- .../test/date-time-text.integration.test.ts | 155 ++++++++--- .../src/runtime/postgres-serverless.ts | 14 +- .../postgres/9-facade/src/runtime/postgres.ts | 22 +- .../values-as-postgres-prints.test.ts | 26 +- .../test/date-style-startup-option.test.ts | 48 ++++ .../9-facade/test/postgres-serverless.test.ts | 13 +- .../@prisma/orm-target-postgres/package.json | 1 + pnpm-lock.yaml | 6 + skills/prisma-8/references/contract.md | 4 +- skills/prisma-8/references/debug.md | 2 +- test/e2e/framework/test/dml.test.ts | 2 +- test/e2e/framework/test/fixtures/contract.ts | 2 +- ...defaults-read-by-codec.integration.test.ts | 31 +-- ...prisma7-dates-without-temporal.e2e.test.ts | 14 +- .../fixtures/postgres/generated/contract.d.ts | 14 +- .../fixtures/postgres/generated/contract.json | 12 +- .../enum-round-trip.postgres.test.ts | 1 + .../contract-date-time-defaults.ts | 10 +- ...nfer-roundtrip-runtime.integration.test.ts | 56 ++-- .../_fixture-attributes/contract.prisma | 2 + .../generated/contract.d.ts | 144 +++++++++- .../generated/contract.json | 148 ++++++++++- .../_fixture-now/contract.prisma | 2 + .../_fixture-now/generated/contract.d.ts | 144 +++++++++- .../_fixture-now/generated/contract.json | 148 ++++++++++- ...ow-generators-readback.integration.test.ts | 20 +- test/utils/src/exports/index.ts | 1 + test/utils/src/startup-parameters.ts | 75 ++++++ .../app/instructions.md | 58 ++++ .../extension/instructions.md | 38 +++ 103 files changed, 2661 insertions(+), 534 deletions(-) create mode 100644 packages/1-framework/1-core/framework-components/test/session-settings.test.ts create mode 100644 packages/3-extensions/supabase/test/date-style-startup-option.test.ts create mode 100644 packages/3-extensions/supabase/test/session-settings.integration.test.ts create mode 100644 packages/3-targets/postgres/1-target/src/core/date-time-wire-text.ts create mode 100644 packages/3-targets/postgres/1-target/test/date-style-every-codec.test.ts create mode 100644 packages/3-targets/postgres/1-target/test/date-time-text-wording.test.ts create mode 100644 packages/3-targets/postgres/3-driver/src/session-settings.ts create mode 100644 packages/3-targets/postgres/3-driver/test/driver.session-settings.integration.test.ts create mode 100644 packages/3-targets/postgres/3-driver/test/driver.startup-options.test.ts create mode 100644 packages/3-targets/postgres/3-driver/test/session-settings.test-d.ts create mode 100644 packages/3-targets/postgres/3-driver/test/session-settings.test.ts create mode 100644 packages/3-targets/postgres/9-facade/test/date-style-startup-option.test.ts create mode 100644 test/utils/src/startup-parameters.ts create mode 100644 upgrade-instructions/pending/iso-string-date-time-values/app/instructions.md create mode 100644 upgrade-instructions/pending/iso-string-date-time-values/extension/instructions.md diff --git a/architecture.config.json b/architecture.config.json index 56898ddb14d0..2592380c13af 100644 --- a/architecture.config.json +++ b/architecture.config.json @@ -684,6 +684,12 @@ "layer": "drivers", "plane": "shared" }, + { + "glob": "packages/3-targets/postgres/3-driver/src/session-settings.ts", + "domain": "targets", + "layer": "drivers", + "plane": "shared" + }, { "glob": "packages/3-targets/postgres/3-driver/src/driver-error.ts", "domain": "targets", diff --git a/docs/architecture docs/adrs/ADR 159 - Driver Terminology and Lifecycle.md b/docs/architecture docs/adrs/ADR 159 - Driver Terminology and Lifecycle.md index aa71a45fd625..950f628880e2 100644 --- a/docs/architecture docs/adrs/ADR 159 - Driver Terminology and Lifecycle.md +++ b/docs/architecture docs/adrs/ADR 159 - Driver Terminology and Lifecycle.md @@ -92,18 +92,32 @@ sequenceDiagram - `SqlDriver.connect(binding: TBinding)`: - binding is **driver-determined** - after connect, driver is executable (and may lazily allocate resources as needed) +- `RuntimeDriverDescriptor.create(options?, sessionSettings?)`: + - `sessionSettings` come from the stack's target; the driver ensures them on every connection it hands out - `ExecutionStackInstance.driver`: - stack instantiation includes the driver instance so the stack is “complete” and deterministic +### A target's session settings + +A target may need every database session to have certain settings. PostgreSQL's date and time codecs read ISO 8601 text, so they need `DateStyle=ISO`. The target states this as data, `sessionSettings` on its descriptor: + +```ts +sessionSettings: { DateStyle: 'ISO' } +``` + +The stack hands the settings to the driver. `createExecutionStack` calls `RuntimeDriverDescriptor.create(options, sessionSettings)`, and `createControlStack` passes them in the control driver's connect options. The driver ensures every connection it hands to the runtime has them, whatever its binding. How it does so is the driver's choice. The PostgreSQL driver sends them as startup options on connections it opens. On a pool or client the user supplies, it applies them with a statement before first use, and again after an exclusively held connection is released, because the holder may have reset the session. + +A codec never sets a session setting. If a connection does not have the setting, for example because a pooler dropped it, the codec refuses the value it cannot read and names the setting. + ### How to pass driver create options when stack instantiation calls `create()` with no args -Because `instantiateExecutionStack(...)` calls `descriptor.create()` with no arguments, the supported pattern is to **curry options into the descriptor** (i.e. wrap `create()`): +Because `instantiateExecutionStack(...)` calls `descriptor.create()` with no driver options, the supported pattern is to **curry options into the descriptor** (i.e. wrap `create()`). The wrapper passes on the session settings the stack hands it: ```ts const driverWithCursor = { ...postgresDriverDescriptor, - create() { - return postgresDriverDescriptor.create({ cursor }); + create(_options, sessionSettings) { + return postgresDriverDescriptor.create({ cursor }, sessionSettings); }, }; ``` diff --git a/docs/architecture docs/adrs/ADR 254 - Data types and casts.md b/docs/architecture docs/adrs/ADR 254 - Data types and casts.md index 200a86baccc6..c2894196ee1a 100644 --- a/docs/architecture docs/adrs/ADR 254 - Data types and casts.md +++ b/docs/architecture docs/adrs/ADR 254 - Data types and casts.md @@ -107,7 +107,7 @@ Codecs of one type differ only in the application value. `pg/int8@1` holds a `bi A limit of the type itself is the type's, as "Values" describes. A codec checks only parameters it keeps for itself: `arktype/json@1` validates the document against its schema in `fromDataTypeValue` and `fromWire`. -An enum compares values with its members: `db.enums` holds each member as `fromDataTypeValue` gives it, which equals what a query returns for it. So every enum authoring surface, in TypeScript and PSL and in both families, refuses a codec whose descriptor does not declare the `equality` trait. A codec that declares it but whose values cannot be compared as members sets `enumRefusal` on its descriptor, naming the reason and what to use instead. On Postgres these are the text timestamp codecs and `pg/timetz@1`, whose values a query reads as the text PostgreSQL prints rather than the ISO 8601 the contract stores; `pg/bytea@1`, whose base64 stored form PostgreSQL reads as the bytes of that text; `pg/jsonb@1` and `arktype/json@1`, because a CHECK lists a string member as text PostgreSQL reads as JSON and a number member as a number it cannot compare with `jsonb`; `pg/enum@1`, which types a native enum's column, so the reason says to declare a native enum; `pg/vector@1`, whose members are arrays no CHECK can list; and `pg/geometry@1`, which is refused until a test can prove a shape reads back as its member. A codec without the trait may set `enumRefusal` to replace the generic reason with a specific one: `pg/json@1` says its type has no equality operator and to use a text enum, and `pg/tsquery@1` says PostgreSQL normalises query text. `pg/tsquery@1` declares no `equality` although PostgreSQL has `tsquery = tsquery`, because comparing queries means nothing to an application and no contract can write a `tsquery` column. Every surface reads both rules through `enumRefusalOf`. Every surface checks `enumRefusalOf` first, so a codec's own reason wins. Two more checks then refuse an enum that `enumRefusalOf` accepts, because the family's storage check cannot enforce its members: the SQL family's CHECK builder refuses a member whose stored form is neither a string nor a finite number, such as a `pg/bool@1` or `pg/text-array@1` member, and on MongoDB, both TypeScript and PSL refuse an enum on a managed collection whose codec stores a BSON type that a collection validator cannot list, such as `long`, `date` or `objectId`. A test per target puts every registered codec in exactly one of these groups or proves that its member reads back. +An enum compares values with its members: `db.enums` holds each member as `fromDataTypeValue` gives it, which equals what a query returns for it. So every enum authoring surface, in TypeScript and PSL and in both families, refuses a codec whose descriptor does not declare the `equality` trait. A codec that declares it but whose values cannot be compared as members sets `enumRefusal` on its descriptor, naming the reason and what to use instead. On Postgres these are `pg/bytea@1`, whose base64 stored form PostgreSQL reads as the bytes of that text; `pg/jsonb@1` and `arktype/json@1`, because a CHECK lists a string member as text PostgreSQL reads as JSON and a number member as a number it cannot compare with `jsonb`; `pg/enum@1`, which types a native enum's column, so the reason says to declare a native enum; `pg/vector@1`, whose members are arrays no CHECK can list; and `pg/geometry@1`, which is refused until a test can prove a shape reads back as its member. A codec without the trait may set `enumRefusal` to replace the generic reason with a specific one: `pg/json@1` says its type has no equality operator and to use a text enum, and `pg/tsquery@1` says PostgreSQL normalises query text. `pg/tsquery@1` declares no `equality` although PostgreSQL has `tsquery = tsquery`, because comparing queries means nothing to an application and no contract can write a `tsquery` column. Every surface reads both rules through `enumRefusalOf`. Every surface checks `enumRefusalOf` first, so a codec's own reason wins. Two more checks then refuse an enum that `enumRefusalOf` accepts, because the family's storage check cannot enforce its members: the SQL family's CHECK builder refuses a member whose stored form is neither a string nor a finite number, such as a `pg/bool@1` or `pg/text-array@1` member, and on MongoDB, both TypeScript and PSL refuse an enum on a managed collection whose codec stores a BSON type that a collection validator cannot list, such as `long`, `date` or `objectId`. A test per target puts every registered codec in exactly one of these groups or proves that its member reads back. An enum member written in PSL is a written value, read by the cast rule as a default is: its syntax gives it a data type, the enum codec's data type takes it directly or through a cast, and the codec checks it. A quoted member is text, so a member of a type with no cast from text is written in that type's own syntax: `Half = 0.5` on `pg/numeric@1`, `` Launch = date`2024-01-01` `` on `pg/date-temporal@1`, and `Nan = NaN` on a float codec. A bare member is its name, as text. A member written as a list, such as `Up = [0, 1]` on a vector codec, is read through the list cast of the codec's data type. Both families read members this way, as "Enum members and discriminator values" describes, so the same member gets the same code and wording on SQL and on MongoDB. A member written in TypeScript is an application value, which the codec writes as any value it is given. @@ -350,7 +350,11 @@ The earliest value of each Postgres type is the earliest PostgreSQL holds, 4714- Each tag reads ISO 8601 with a four-digit or signed six-digit year and a `T` or a space between date and time. A Postgres tag also reads the text PostgreSQL prints: a ` BC` suffix, a year of five or six digits, `infinity` and `-infinity`, and for `interval` the text PostgreSQL prints under `IntervalStyle = postgres`. `parse` uses no `Temporal` and no JavaScript `Date`, so a default reads the same on every runtime. It refuses an offset on a type that holds none, a missing offset on a type that needs one, a date on a time type, a time on `pg/date`, more fraction digits than the type holds, a date or time that does not exist, and a value outside the type's range, and each refusal names what is wrong and shows text the type takes. -PostgreSQL reads no signed year, so the Postgres codecs' `toWire` writes a year after 9999 without its sign and leading zeros (`10000-01-01`) and a year at or before 0000 with a ` BC` suffix (`0044-03-15 BC`). The `Temporal` and `Date` codecs' `fromWire` reads both into their value. A text codec's `fromWire` keeps PostgreSQL's year text as it arrives, ` BC` included, because its application value is that text, and its `toWire` converts the stored ISO year into it. One pair of functions in the Postgres target, `isoYearText` and `postgresYearText`, reads and writes PostgreSQL's year text, and the codecs and the tags share it. +PostgreSQL reads no signed year, so the Postgres codecs' `toWire` writes a year after 9999 without its sign and leading zeros (`10000-01-01`) and a year at or before 0000 with a ` BC` suffix (`0044-03-15 BC`). The `Temporal` and `Date` codecs' `fromWire` reads both into their value. One pair of functions in the Postgres target, `isoYearText` and `postgresYearText`, reads and writes PostgreSQL's year text, and the codecs and the tags share it. + +A text codec's application value is the stored form of its data type, the same on every server: `pg/timestamptz-string@1` reads `2024-01-01T00:00:00Z` whether the session prints `2024-01-01 00:00:00+00` or `2024-01-01 01:00:00+01`. Its `fromWire` reads PostgreSQL's text into the stored form, converting a ` BC` or long year to the ISO year and any offset of an instant to UTC, so the session's `TimeZone` never reaches the application, and it reads a row and a JSON row alike. A value PostgreSQL holds beyond the stored form's range reads in the same ISO shape rather than being refused, because the text codecs exist to carry the values the other codecs cannot: `24:00:00` for `time` and `timetz`, a year after +275760 in six digits, and a date's seven-digit year with all its digits, as in `+5874897-12-31`. Its `toDataTypeValue` takes only the stored form, so a default or an enum member is written as a query reads it back, and its `toWire` sends the value in PostgreSQL's year text, which PostgreSQL reads in any form ISO 8601 allows. + +PostgreSQL prints dates and timestamps in the session's `DateStyle`, and only `ISO` prints text these codecs read. The PostgreSQL target declares `DateStyle=ISO` as a session setting on its descriptor (`sessionSettings`), and the stack passes it to every driver it creates. The PostgreSQL driver ensures it on every connection it uses; how is the driver's own detail. It starts each connection it opens with `options=-c DateStyle=ISO`, after any options the URL or `PGOPTIONS` gives; a startup option sets the session's default, so a `RESET ALL`, which the Supabase client runs when it releases a role scope, keeps it. On a `pg.Pool` or `pg.Client` the application supplies, it sets `DateStyle=ISO` with a statement on each connection before first use, and again after a connection held for exclusive use is released, since its holder may have reset the session. Every date and time codec that reads a date or timestamp, the text, `Temporal` and `Date` codecs alike, refuses one printed in another style with `RUNTIME.DATE_STYLE_NOT_ISO`, which says the driver could not ensure the setting, rather than read it wrong. `time` and `timetz` print the same text in every style. No `TimeZone` setting is needed. ## Assembly diff --git a/docs/reference/codec-authoring-guide.md b/docs/reference/codec-authoring-guide.md index 65e4359cb398..f2337549b860 100644 --- a/docs/reference/codec-authoring-guide.md +++ b/docs/reference/codec-authoring-guide.md @@ -52,11 +52,11 @@ One limit remains: **float codecs need `extra_float_digits >= 1`.** `pg/float4@1 A value in a contract, a literal default or an enum member, converts to the value a query returns for it: `fromDataTypeValue` of the stored value equals what `fromWire` gives for the same value in a row, and `db.enums` compares with that value ([ADR 254](../architecture%20docs/adrs/ADR%20254%20-%20Data%20types%20and%20casts.md)). The stored form need not be the text the database prints: `pg/bytea` stores base64 where PostgreSQL prints hex, and the timestamp types store ISO 8601. Where the database normalises how a value is written, the stored form is the database's spelling, which the data type writes and its `read` requires: `pg/uuid` stores lower case, the integer types and `pg/numeric` drop leading zeros and the minus sign on zero, a `numeric(10,2)` value has two fraction digits, and `pg/inet` drops `/32` or `/128` on a host address and writes IPv6 in lower case with PostgreSQL's zero compression. -An enum compares values with its members, so every enum authoring surface (`enumType` in `defineContract` and the PSL enum block, in both families) refuses a codec whose descriptor does not declare the `equality` trait. A codec that declares it, but whose values read back can never equal a member as the contract stores it, sets `enumRefusal` on its descriptor: one or two sentences saying why, ending with what to use instead. A codec without equality may set it too, to replace the generic reason. `enumRefusalOf` from `@internal/framework-components/codec` reads both rules, and every surface refuses through it. On Postgres, `pg/timestamp-string@1` and `pg/timestamptz-string@1` set it, because they read PostgreSQL's own text while the contract stores ISO 8601; `pg/bytea@1`, because its CHECK would compare a value with the bytes of the stored base64 text; `pg/tsquery@1`, because PostgreSQL normalises the query text; `pg/json@1`, because its type has no equality operator; `pg/jsonb@1` and `arktype/json@1`, because a CHECK cannot compare their stored members with `jsonb`; `pg/timetz@1`, because it reads PostgreSQL's own text while the contract stores ISO 8601; `pg/enum@1`, which types a native enum's column; `pg/vector@1`, whose members no CHECK can list; and `pg/geometry@1`, until a test proves a shape reads back as its member. Each names a text enum, or for `pg/enum@1` a native enum, as what to use instead. +An enum compares values with its members, so every enum authoring surface (`enumType` in `defineContract` and the PSL enum block, in both families) refuses a codec whose descriptor does not declare the `equality` trait. A codec that declares it, but whose values read back can never equal a member as the contract stores it, sets `enumRefusal` on its descriptor: one or two sentences saying why, ending with what to use instead. A codec without equality may set it too, to replace the generic reason. `enumRefusalOf` from `@internal/framework-components/codec` reads both rules, and every surface refuses through it. On Postgres, `pg/bytea@1` sets it, because its CHECK would compare a value with the bytes of the stored base64 text; `pg/tsquery@1`, because PostgreSQL normalises the query text; `pg/json@1`, because its type has no equality operator; `pg/jsonb@1` and `arktype/json@1`, because a CHECK cannot compare their stored members with `jsonb`; `pg/enum@1`, which types a native enum's column; `pg/vector@1`, whose members no CHECK can list; and `pg/geometry@1`, until a test proves a shape reads back as its member. Each names a text enum, or for `pg/enum@1` a native enum, as what to use instead. The consumer-facing [`BigInt`, `BigIntNumber`, and `UnboundedInt` representation choices](./integer-representation-types.md), are documented separately from this contributor guide. -The PostgreSQL temporal codecs pair a `Temporal`-valued codec with a raw-text one for each native type — `pg/timestamptz-temporal@1` reads a `Temporal.Instant`, `pg/timestamptz-string@1` reads PostgreSQL's own text, and the contract names which one a column uses. A type may have more codecs: `timestamptz` and `timestamp` also have a `Date` codec, `timestamp` a second `Temporal` codec, `pg/timestamp-instant@1`, that reads the stored date and time as a UTC `Temporal.Instant`, and `interval` a codec that reads an ISO 8601 duration string. A codec of one type differs from its siblings only in the application value, so a new one is a codec, never a new data type. A text codec's `fromWire` keeps PostgreSQL's year text, ` BC` included, and its `toWire` converts the stored ISO year into it. +The PostgreSQL temporal codecs pair a `Temporal`-valued codec with a text one for each native type — `pg/timestamptz-temporal@1` reads a `Temporal.Instant`, `pg/timestamptz-string@1` reads ISO 8601 text, and the contract names which one a column uses. A type may have more codecs: `timestamptz` and `timestamp` also have a `Date` codec, `timestamp` a second `Temporal` codec, `pg/timestamp-instant@1`, that reads the stored date and time as a UTC `Temporal.Instant`, and `interval` a codec that reads an ISO 8601 duration string. A codec of one type differs from its siblings only in the application value, so a new one is a codec, never a new data type. A text codec's application value is its data type's stored form: its `fromWire` reads the text PostgreSQL prints under `DateStyle=ISO` into that form, converting a `timestamptz` offset to UTC and a ` BC` or long year to the signed ISO year, and refuses text printed under another `DateStyle` with `RUNTIME.DATE_STYLE_NOT_ISO`, as every Postgres codec that reads a date or timestamp does. Its `toDataTypeValue` takes only the stored form, and its `toWire` converts the stored ISO year into PostgreSQL's year text. A value beyond the stored form's range, such as `24:00:00` or a seven-digit year, reads in the same ISO shape. ## Three case studies diff --git a/docs/reference/error-reference.md b/docs/reference/error-reference.md index 3dc27f229994..7af163fc660e 100644 --- a/docs/reference/error-reference.md +++ b/docs/reference/error-reference.md @@ -413,7 +413,7 @@ An enum declaration is malformed. Raised while authoring a contract. Payload: `e - `duplicate-member-value`: two members have the same value by SameValueZero, the equality the handle's `has`, `nameOf` and `ordinalOf` use; `members` names both. Values that differ there, such as `1` and `'1'` or two distinct objects, are compared again by the contract build, as their codec stores them. - The SQL contract builder (`defineContract` from the Postgres and SQLite packages): - `key-name-mismatch`: the key in `defineContract({ enums })` differs from the `enumType` name; - - `codec-not-for-enums`: an enum cannot use the enum's codec. A codec without the `equality` trait is refused, because no value can be compared with a member, and so is a codec whose descriptor gives its own reason in `enumRefusal`. On Postgres these are `pg/timestamp-string@1` and `pg/timestamptz-string@1`, which a query reads as the text Postgres prints, such as `2024-01-02 03:04:05`, while the contract stores ISO 8601, such as `2024-01-02T03:04:05` (use `pg/timestamp-temporal@1` or `pg/timestamptz-temporal@1`); `pg/bytea@1`, whose stored base64 text Postgres would read as the bytes of that text (no enum can use a bytea codec; use a text enum); `pg/tsquery@1`, whose text Postgres normalises, printing `a & b` as `'a' & 'b'` (use a text enum); `pg/json@1`, whose type has no equality operator, so no CHECK can compare a value with the members (use a text enum); `pg/jsonb@1`, and `arktype/json@1`, which stores `jsonb`, because a CHECK lists a string member as SQL text, which PostgreSQL reads as the JSON text of a document, and a number member as a number, which PostgreSQL cannot compare with `jsonb` (use a text enum); `pg/timetz@1`, which a query reads as the text PostgreSQL prints, such as `12:34:56+02`, while the contract stores ISO 8601, such as `12:34:56+02:00` (use a text enum); `pg/enum@1`, which types the column of a native enum, whose values the database enforces with an enum type of its own (declare a native enum: `native_enum` in PSL, or `nativeEnum` in TypeScript); `pg/vector@1`, whose members are arrays of numbers, which no CHECK can list (use a text enum); and `pg/geometry@1`, which is not supported yet, because no test proves that a shape reads back as its member (use a text enum that names each shape). The message is `enumType(""): an enum cannot use the codec . `, and the reason ends with what to use instead. A PSL enum block typed by such a codec, in either family, is reported as `PSL_EXTENSION_INVALID_VALUE` at its `@@type`, with the same reason; + - `codec-not-for-enums`: an enum cannot use the enum's codec. A codec without the `equality` trait is refused, because no value can be compared with a member, and so is a codec whose descriptor gives its own reason in `enumRefusal`. On Postgres these are `pg/bytea@1`, whose stored base64 text Postgres would read as the bytes of that text (no enum can use a bytea codec; use a text enum); `pg/tsquery@1`, whose text Postgres normalises, printing `a & b` as `'a' & 'b'` (use a text enum); `pg/json@1`, whose type has no equality operator, so no CHECK can compare a value with the members (use a text enum); `pg/jsonb@1`, and `arktype/json@1`, which stores `jsonb`, because a CHECK lists a string member as SQL text, which PostgreSQL reads as the JSON text of a document, and a number member as a number, which PostgreSQL cannot compare with `jsonb` (use a text enum); `pg/enum@1`, which types the column of a native enum, whose values the database enforces with an enum type of its own (declare a native enum: `native_enum` in PSL, or `nativeEnum` in TypeScript); `pg/vector@1`, whose members are arrays of numbers, which no CHECK can list (use a text enum); and `pg/geometry@1`, which is not supported yet, because no test proves that a shape reads back as its member (use a text enum that names each shape). The message is `enumType(""): an enum cannot use the codec . `, and the reason ends with what to use instead. A PSL enum block typed by such a codec, in either family, is reported as `PSL_EXTENSION_INVALID_VALUE` at its `@@type`, with the same reason; - `codec-refused-member`: the enum's codec refuses a member's value, either when writing it or when reading back what it wrote, such as a `pg/char@1` member longer than one character, since the enum's column is `character`. The message is `enumType("") member "" has a value its codec refuses: `; give the member a value the codec takes; - `member-not-stored-as-written`: the column's codec reads a member back as a different value from the one written. The check compares through the stored value: the codec's `toDataTypeValue` gives the member's stored value, and `fromDataTypeValue` of that value must equal the member. An upper-case or braced uuid is refused because `pg/uuid@1` stores lower case, a `pg/numeric@1` member with a leading zero or a minus sign on zero because Postgres prints `01.5` as `1.5` and `-0` as `0`, and a `pg/inet@1` member such as `10.0.0.1/32` or `::FFFF:10.0.0.1` because Postgres prints `10.0.0.1` and `::ffff:10.0.0.1`. A numeric enum's CHECK constraint, on a scalar or a list column, compares values as numbers, so the column also takes a value equal to a member but written with another scale, such as `0.50` for the member `0.5`, which reads back as `0.50` and which `db.enums` finds as the member `0.5`, because the enum compares values by its data type's equality. Only members with a literal type (strings, numbers, booleans, bigints, and arrays and plain objects of them) are checked, and the check is only as exact as the stored value: where it is not the text Postgres returns, a member Postgres would print differently is accepted; - `duplicate-member-value`: two members that the codec stores as the same value; `members` names both. Stored values are compared by their JSON form, so two objects with the same entries in a different order are the same value. The message is `enumType(""): members "" and "" both store . Member values must be unique as their codec stores them.` For a data type whose equal values have more than one stored form (`pg/numeric`, `pg/interval`, `mongo/decimal128`), stored values are compared by the type's equality instead, and two members stored in different forms of one value are refused with both forms named: `enumType(""): members "" and "" store "0.5" and "0.50", which the database holds equal. Member values must be unique as their data type compares them.` The PSL readers report the same two cases as `PSL_ENUM_DUPLICATE_MEMBER_VALUE`, at the later member: `enum "": members "" and "" both store `, or `enum "": members "" and "" store "P1D" and "PT24H", which the database holds equal`; @@ -1230,6 +1230,10 @@ At SQL context construction, the contract's target family (e.g. `mongo`) does no At SQL context construction, the contract's target (e.g. `sqlite`) does not match the runtime stack's target descriptor (e.g. `postgres`): the contract and the adapter/driver stack disagree about the database target. Payload: `actual`, `expected`. +### RUNTIME.DATE_STYLE_NOT_ISO + +A PostgreSQL date and time codec that reads a date or timestamp (`pg/date-string@1`, `pg/date-temporal@1`, `pg/timestamp-string@1`, `pg/timestamp-temporal@1`, `pg/timestamptz-string@1`, `pg/timestamptz-temporal@1`, `pg/timestamptz-date@1`) read a value PostgreSQL printed under a `DateStyle` other than `ISO`, such as `02.01.2026` under `German`: the driver could not ensure the `DateStyle=ISO` the PostgreSQL target requires. The driver starts each connection it opens with `DateStyle=ISO` as a startup option, and sets it with a statement on a connection of a `pg.Pool` or `pg.Client` the application supplies before first use and after an exclusive use ends. So a statement in the application set a `DateStyle` other than `ISO` on a connection Prisma uses, or a connection pooler dropped the startup option (PgBouncer does when `ignore_startup_parameters` lists `options`) or runs each statement on another server connection. Do not set a `DateStyle` other than `ISO` on a connection Prisma uses. Behind a pooler that refuses startup options, pass Prisma your own `pg.Pool`; behind one that moves statements between server connections, set `DateStyle` to `ISO` on the role or database. `time` and `timetz` print the same text in every `DateStyle`. On a flat read the envelope surfaces as it is; on an `.include()` read it is the `cause` of a `RUNTIME.DECODE_FAILED`. Payload: `codecId`, `received` (the text, cut to 100 characters). + ### RUNTIME.DDL_UNSUPPORTED `lower()` was asked to lower DDL on a surface that cannot do it: the runtime adapter (DDL lowering is a control-plane concern), or the synchronous control lowering path (DDL default literals require async codec encoding; use `lowerToExecuteRequest()`). Raised by the Postgres and SQLite adapters. Payload: `surface`. @@ -1374,7 +1378,7 @@ It is raised on **reads**, because the check is the first thing a Temporal codec This error never occurs in the control plane. The CLI commands, `node migration.ts`, the Vite plugin, the language server and the programmatic control client all load `prisma.config.ts` or the control descriptors, and so load the target's control entry. The target's control entry (`@internal/target-postgres/control`) sets the fallback `Temporal`, from `temporal-polyfill`, when it is loaded. It does not set `globalThis.Temporal`. `temporal-polyfill` is a required peer dependency of `@prisma/orm-postgres` and `@prisma/orm-target-postgres`. npm, pnpm and bun install it automatically; a project that uses Yarn adds it to its own dependencies. When it is not installed, loading the control entry fails because Node.js cannot find the package `temporal-polyfill`, not with this error. The fallback is held once per process, and every Postgres codec in the process uses it. An application process that loads no control-plane code and has no global `Temporal` gets `RUNTIME.TEMPORAL_UNAVAILABLE`. An application that loads control-plane code in its own process, such as server code under `vite dev` with the Prisma Vite plugin or a script that calls the control client, decodes dates without its own `Temporal`, and then fails in production. An application that uses the Temporal codecs must load its own `Temporal`. -In the application, install a global implementation before any query runs (`import 'temporal-polyfill/full/global'`), or author the column with its `*String` type (`DateString`, `TimestampString(p)`, `TimestamptzString(p)`, `TimeString(p)`) to read and write PostgreSQL's own text, which needs no Temporal at all. +In the application, install a global implementation before any query runs (`import 'temporal-polyfill/full/global'`), or author the column with its `*String` type (`DateString`, `TimestampString(p)`, `TimestamptzString(p)`, `TimeString(p)`) to read and write ISO 8601 text, which needs no Temporal at all. ### RUNTIME.TRANSACTION_ALREADY_OPEN diff --git a/docs/reference/scalar-types.md b/docs/reference/scalar-types.md index 0bee42f30f4f..df6077f4bdfb 100644 --- a/docs/reference/scalar-types.md +++ b/docs/reference/scalar-types.md @@ -82,16 +82,16 @@ These are the current names. The Postgres and SQLite rename project will change | `Decimal`, `Numeric(p?, s?)` | `field.decimal()` | `pg/numeric@1` | `numeric` | `string` (decimal text) | | `DateTime`, `TimestamptzTemporal(p?)` | `field.dateTime()` | `pg/timestamptz-temporal@1` | `timestamptz` | `Temporal.Instant` | | `TimestamptzJsDate(p?)` | — | `pg/timestamptz-date@1` | `timestamptz` | `Date` | -| `TimestamptzString(p?)` | — | `pg/timestamptz-string@1` | `timestamptz` | `string` | +| `TimestamptzString(p?)` | — | `pg/timestamptz-string@1` | `timestamptz` | `string` (ISO 8601) | | `TimestampTemporal(p?)` | — | `pg/timestamp-temporal@1` | `timestamp` | `Temporal.PlainDateTime` | -| `TimestampString(p?)` | — | `pg/timestamp-string@1` | `timestamp` | `string` | +| `TimestampString(p?)` | — | `pg/timestamp-string@1` | `timestamp` | `string` (ISO 8601) | | `TimestampJsDate(p?)` | — | `pg/timestamp-date@1` | `timestamp` | `Date`, the stored date and time read as UTC | | `TimestampInstant(p?)` | — | `pg/timestamp-instant@1` | `timestamp` | `Temporal.Instant`, the stored date and time read as UTC | | `Date`, `DateTemporal` | — | `pg/date-temporal@1` | `date` | `Temporal.PlainDate` | -| `DateString` | — | `pg/date-string@1` | `date` | `string` | +| `DateString` | — | `pg/date-string@1` | `date` | `string` (ISO 8601) | | `Time(p?)`, `TimeTemporal(p?)` | — | `pg/time-temporal@1` | `time` | `Temporal.PlainTime` | -| `TimeString(p?)` | — | `pg/time-string@1` | `time` | `string` | -| `Timetz(p?)` | — | `pg/timetz@1` | `timetz` | `string` | +| `TimeString(p?)` | — | `pg/time-string@1` | `time` | `string` (ISO 8601) | +| `Timetz(p?)` | — | `pg/timetz@1` | `timetz` | `string` (ISO 8601) | | `Uuid` | `field.uuidNative()` | `pg/uuid@1` | `uuid` | `string` | | `Inet` | — | `pg/inet@1` | `inet` | `string` | | `IntervalParts(p?)` | — | `pg/interval@1` | `interval` | `{ months: number; days: number; micros: bigint }` | @@ -109,6 +109,10 @@ A literal default of a date, time, interval or bytes type is written with the ta The tools that write a schema for you use the text date and time types. In a PSL schema, `prisma orm init` writes `createdAt TimestamptzString @default(now())` and `updatedAt temporal.updatedAtString()`. In a TypeScript contract, it writes `field.temporal.createdAtString()` and `field.temporal.updatedAtString()`. All of these are `TimestamptzString` columns. `prisma contract infer` and `prisma7Schema(...)` write `TimestampString(p)`, `TimestamptzString(p)`, `DateString` and `TimeString(p)`. Five types read and write `Temporal` values: `TimestamptzTemporal` (also written `DateTime`), `TimestampTemporal`, `TimestampInstant`, `DateTemporal` (also written `Date`) and `TimeTemporal` (also written `Time`). Their TypeScript types come from `temporal-spec`, so a project needs no global `Temporal` declarations to type them. An application that uses them needs a global `Temporal`: on a runtime without one, such as Node.js 24, install it, for example with `import 'temporal-polyfill/full/global'`. +A text date or time type reads and writes ISO 8601, the form its data type stores: `TimestamptzString` reads `2024-01-01T00:00:00Z`, the instant in UTC, whatever the session's time zone; `TimestampString` reads `2024-01-01T12:34:56.5`; `DateString` reads `2024-01-01`; `TimeString` reads `12:34:56`; and `Timetz` reads `12:34:56+02:00`, or `12:34:56Z` for a zero offset. A year before 1 or after 9999 is a sign and six digits, as in `-000043-03-15` for 44 BC. `infinity` and `-infinity` read as those words. A value PostgreSQL holds beyond the range of the `Temporal` and `Date` types reads in the same shape: the time `24:00:00`, and a date with a seven-digit year, such as `+5874897-12-31`. A value written in another form PostgreSQL reads is stored as the value it names and reads back in ISO 8601: `2024-01-01 01:00:00+01` written to a `TimestamptzString` column reads back as `2024-01-01T00:00:00Z`; a literal default or enum member must be written in the stored form. + +PostgreSQL prints a date or timestamp in the connection's `DateStyle`. The PostgreSQL target requires `DateStyle=ISO` on every session, and the PostgreSQL driver ensures it on every connection it uses: it starts each connection it opens with `DateStyle=ISO` as a startup option, after any `options` the URL or `PGOPTIONS` gives, which a `RESET ALL` keeps; on a `pg.Pool` or `pg.Client` you pass to Prisma, it sets `DateStyle=ISO` on each connection before first use, and again after a connection held for a transaction, an ORM operation or a Supabase role scope is released, one statement each time. When the driver could not ensure it, because a statement set a `DateStyle` other than `ISO` on a connection Prisma uses or a connection pooler dropped the startup option, every date and time codec that reads a date or timestamp, text, `Temporal` or `Date`, fails with `RUNTIME.DATE_STYLE_NOT_ISO`. Behind a connection pooler that refuses startup options, pass Prisma your own `pg.Pool`, which the driver sets with a statement instead; behind one that runs each statement on another server connection, also set `DateStyle=ISO` on the role or database (`ALTER ROLE SET DateStyle = 'ISO'`). + ## SQLite These are the current names; the Postgres and SQLite rename project will update this table. SQLite has no scalar TS helpers; use `field.column(...)`. diff --git a/examples/prisma7-adoption/generated/prisma8/contract.d.ts b/examples/prisma7-adoption/generated/prisma8/contract.d.ts index 9e5405fea891..7282c2ae09be 100644 --- a/examples/prisma7-adoption/generated/prisma8/contract.d.ts +++ b/examples/prisma7-adoption/generated/prisma8/contract.d.ts @@ -25,7 +25,7 @@ import type { export type StorageHash = StorageHashBase<'9268d6eef6e976bf77ca87083b15cd8eb247c9bf7fe4d2929f56a23bfa36d9ec'>; export type ExecutionHash = - ExecutionHashBase<'5d2a944d02782b8bcf53d311c7dbe23d41a990f019100658761ce68af6aa0230'>; + ExecutionHashBase<'8552d69a879d0f7c4482b9b7a2e9932db9ea33b3421ed778e87bf04b17076b10'>; export type ProfileHash = ProfileHashBase<'3916f444a8a17ad749191acf9e08dad97d1a327b88c2f1d45d12f240296aa8b2'>; @@ -865,7 +865,11 @@ type ContractBase = Omit< readonly mutations: { readonly defaults: readonly [ { - readonly onCreate: { readonly id: 'timestampStringNow'; readonly kind: 'generator' }; + readonly onCreate: { + readonly id: 'timestampStringNow'; + readonly kind: 'generator'; + readonly params: { readonly precision: 3 }; + }; readonly ref: { readonly entry: 'User'; readonly field: 'createdAt'; @@ -873,8 +877,16 @@ type ContractBase = Omit< }; }, { - readonly onCreate: { readonly id: 'timestampStringNow'; readonly kind: 'generator' }; - readonly onUpdate: { readonly id: 'timestampStringNow'; readonly kind: 'generator' }; + readonly onCreate: { + readonly id: 'timestampStringNow'; + readonly kind: 'generator'; + readonly params: { readonly precision: 3 }; + }; + readonly onUpdate: { + readonly id: 'timestampStringNow'; + readonly kind: 'generator'; + readonly params: { readonly precision: 3 }; + }; readonly ref: { readonly entry: 'User'; readonly field: 'updatedAt'; diff --git a/examples/prisma7-adoption/generated/prisma8/contract.json b/examples/prisma7-adoption/generated/prisma8/contract.json index fa004ad22aff..479758e006e3 100644 --- a/examples/prisma7-adoption/generated/prisma8/contract.json +++ b/examples/prisma7-adoption/generated/prisma8/contract.json @@ -657,13 +657,16 @@ "storageHash": "9268d6eef6e976bf77ca87083b15cd8eb247c9bf7fe4d2929f56a23bfa36d9ec" }, "execution": { - "executionHash": "5d2a944d02782b8bcf53d311c7dbe23d41a990f019100658761ce68af6aa0230", + "executionHash": "8552d69a879d0f7c4482b9b7a2e9932db9ea33b3421ed778e87bf04b17076b10", "mutations": { "defaults": [ { "onCreate": { "id": "timestampStringNow", - "kind": "generator" + "kind": "generator", + "params": { + "precision": 3 + } }, "ref": { "entry": "User", @@ -674,11 +677,17 @@ { "onCreate": { "id": "timestampStringNow", - "kind": "generator" + "kind": "generator", + "params": { + "precision": 3 + } }, "onUpdate": { "id": "timestampStringNow", - "kind": "generator" + "kind": "generator", + "params": { + "precision": 3 + } }, "ref": { "entry": "User", diff --git a/gotchas.md b/gotchas.md index 3fdcb747ee0e..c6096dee358b 100644 --- a/gotchas.md +++ b/gotchas.md @@ -130,9 +130,9 @@ The same command with `--from 20260707T1005_init` (a migration directory name) s **Cause.** Prisma 8's temporal codecs return `Temporal.PlainDateTime` (`timestamp`) and `Temporal.Instant` (`timestamptz`); nothing in the client installs a polyfill. A Prisma 7 user expects a `Date`. -**Workaround.** `import 'temporal-polyfill/full/global'` before the client is created (the example did so in `src/db.ts` before the fix), or author the column with the `*String` presets to receive PostgreSQL's text. +**Workaround.** `import 'temporal-polyfill/full/global'` before the client is created (the example did so in `src/db.ts` before the fix), or author the column with the `*String` presets to receive ISO 8601 text. -**Resolved.** `prisma7Schema(...)` and `contract infer` now write `TimestampString(3)`, `TimestamptzString(p)`, `DateString` and `TimeString(p)` for these columns, so they read as PostgreSQL's text, `@updatedAt` writes UTC text, and the application needs no `Temporal`. A column written by hand as `DateTime`, `TimestampTemporal(p)`, `Date` or `Time(p)` still reads as a `Temporal` value and still needs one. +**Resolved.** `prisma7Schema(...)` and `contract infer` now write `TimestampString(3)`, `TimestamptzString(p)`, `DateString` and `TimeString(p)` for these columns, so they read as ISO 8601 text, `@updatedAt` writes UTC text, and the application needs no `Temporal`. A column written by hand as `DateTime`, `TimestampTemporal(p)`, `Date` or `Time(p)` still reads as a `Temporal` value and still needs one. **Reproduction.** 1. `cd examples/prisma7-adoption && pnpm db:start`, then `pnpm v7:migrate && pnpm emit && pnpm sign && pnpm seed`. diff --git a/packages/1-framework/1-core/framework-components/src/control/control-descriptors.ts b/packages/1-framework/1-core/framework-components/src/control/control-descriptors.ts index 6f3419f0fa1c..6f1872830584 100644 --- a/packages/1-framework/1-core/framework-components/src/control/control-descriptors.ts +++ b/packages/1-framework/1-core/framework-components/src/control/control-descriptors.ts @@ -4,6 +4,7 @@ import type { DriverDescriptor, ExtensionDescriptor, FamilyDescriptor, + SessionSettings, TargetDescriptor, } from '../shared/framework-components'; import type { GeneratorFunctionField } from '../shared/generator-functions'; @@ -100,6 +101,8 @@ export interface ControlDriverConnectOptions { readonly existingDatabaseOnly?: boolean; /** Give up connecting after this many milliseconds, where the driver connects over a network. */ readonly connectTimeoutMs?: number; + /** Session settings the stack's target declares, which the driver ensures on its connection. */ + readonly sessionSettings?: SessionSettings; } export interface ControlExtensionDescriptor< diff --git a/packages/1-framework/1-core/framework-components/src/control/control-stack.ts b/packages/1-framework/1-core/framework-components/src/control/control-stack.ts index d8b37607089b..7acb0c3c59c6 100644 --- a/packages/1-framework/1-core/framework-components/src/control/control-stack.ts +++ b/packages/1-framework/1-core/framework-components/src/control/control-stack.ts @@ -40,7 +40,7 @@ import { mergeAuthoringAttributeSpecs, mergeAuthoringNamespaces, } from '../shared/framework-authoring'; -import type { ComponentMetadata } from '../shared/framework-components'; +import type { ComponentMetadata, SessionSettings } from '../shared/framework-components'; import { assembleGeneratorFunctions, assembleGeneratorRegistrations, @@ -715,6 +715,19 @@ export function buildExtensionLoadOrder( return result; } +/** The driver descriptor, connecting each driver with the session settings the target declares. */ +function withSessionSettings( + driver: ControlDriverDescriptor | undefined, + sessionSettings: SessionSettings | undefined, +): ControlDriverDescriptor | undefined { + if (driver === undefined || sessionSettings === undefined) return driver; + const create = driver.create.bind(driver); + return { + ...driver, + create: (connection, options) => create(connection, { ...options, sessionSettings }), + }; +} + export function createControlStack( input: CreateControlStackInput, ): ControlStack { @@ -757,7 +770,7 @@ export function createControlStack, > extends DriverDescriptor { - create(options?: TCreateOptions): TDriverInstance; + /** Creates an unbound driver. `sessionSettings` are the settings the stack's target declares, which the driver ensures on every connection it hands out. */ + create(options?: TCreateOptions, sessionSettings?: SessionSettings): TDriverInstance; } export interface RuntimeExtensionDescriptor< diff --git a/packages/1-framework/1-core/framework-components/src/execution/execution-stack.ts b/packages/1-framework/1-core/framework-components/src/execution/execution-stack.ts index 3c9237566ba6..fd32510307e6 100644 --- a/packages/1-framework/1-core/framework-components/src/execution/execution-stack.ts +++ b/packages/1-framework/1-core/framework-components/src/execution/execution-stack.ts @@ -1,3 +1,4 @@ +import type { SessionSettings } from '../shared/framework-components'; import type { RuntimeAdapterDescriptor, RuntimeDriverDescriptor, @@ -105,11 +106,30 @@ export function createExecutionStack< return { target: input.target, adapter: input.adapter, - driver: input.driver, + driver: withSessionSettings< + TFamilyId, + TTargetId, + TDriverInstance, + TDriverDescriptor | undefined + >(input.driver, input.target.sessionSettings), extensions: input.extensions ?? [], }; } +/** The driver descriptor, creating each driver with the session settings the target declares. */ +function withSessionSettings< + TFamilyId extends string, + TTargetId extends string, + TDriverInstance extends RuntimeDriverInstance, + TDriverDescriptor extends + | RuntimeDriverDescriptor + | undefined, +>(driver: TDriverDescriptor, sessionSettings: SessionSettings | undefined): TDriverDescriptor { + if (driver === undefined || sessionSettings === undefined) return driver; + const create = driver.create.bind(driver); + return { ...driver, create: (options?: unknown) => create(options, sessionSettings) }; +} + export function instantiateExecutionStack< TFamilyId extends string, TTargetId extends string, diff --git a/packages/1-framework/1-core/framework-components/src/exports/components.ts b/packages/1-framework/1-core/framework-components/src/exports/components.ts index e7b0ab2b268b..64c49fd6bbd8 100644 --- a/packages/1-framework/1-core/framework-components/src/exports/components.ts +++ b/packages/1-framework/1-core/framework-components/src/exports/components.ts @@ -43,6 +43,7 @@ export type { FamilyInstance, FamilyPackRef, PackRefBase, + SessionSettings, TargetBoundComponentDescriptor, TargetDescriptor, TargetInstance, diff --git a/packages/1-framework/1-core/framework-components/src/shared/codec.ts b/packages/1-framework/1-core/framework-components/src/shared/codec.ts index 7c0124016c9a..f6ed3180d61e 100644 --- a/packages/1-framework/1-core/framework-components/src/shared/codec.ts +++ b/packages/1-framework/1-core/framework-components/src/shared/codec.ts @@ -77,7 +77,7 @@ function namedDataType(descriptor: object): unknown { /** * Abstract base class for concrete codec implementations. * - * A codec is built with its descriptor, the data type it converts values of and the column's parameters of that type. The constructor makes `fromDataTypeValue` refuse a value of another data type before the subclass's conversion runs. `fromDataTypeValue` and `toDataTypeValue` convert between a value of the type and the application value, and `toDataTypeValue` finishes with {@link CodecImpl.dataTypeValueOf}, which constructs the value through the type. `fromWire` and `toWire` read and write what the driver exchanges, asynchronously. + * A codec is built with its descriptor, the data type it converts values of and the column's parameters of that type. The constructor makes `fromDataTypeValue` refuse a value of another data type before the subclass's conversion runs. `fromDataTypeValue` and `toDataTypeValue` convert between a value of the type and the application value, and `toDataTypeValue` finishes with {@link CodecImpl.dataTypeValueOf}, which constructs the value through the type and writes any spelling the type reads in its stored spelling, or, for a codec whose application value is the stored form itself, with {@link CodecImpl.storedDataTypeValueOf}, which takes only the stored spelling. `fromWire` and `toWire` read and write what the driver exchanges, asynchronously. */ export abstract class CodecImpl< Id extends string = string, @@ -119,6 +119,11 @@ export abstract class CodecImpl< return this.dataType.valueFrom(json, this.dataTypeParams); } + /** The value of the codec's data type that `json` stores, refusing JSON in any spelling but the stored one: for a codec whose application value is the stored form, so a value it is given reads back as given. */ + protected storedDataTypeValueOf(json: JsonValue): DataTypeValue { + return this.dataType.fromContract(json, this.dataTypeParams); + } + abstract fromDataTypeValue(value: DataTypeValue): TInput; abstract toDataTypeValue(input: TInput): DataTypeValue; abstract fromWire(wire: TWire, ctx: CodecCallContext): Promise; diff --git a/packages/1-framework/1-core/framework-components/src/shared/data-type.ts b/packages/1-framework/1-core/framework-components/src/shared/data-type.ts index 23608c63d503..fc179d78ceba 100644 --- a/packages/1-framework/1-core/framework-components/src/shared/data-type.ts +++ b/packages/1-framework/1-core/framework-components/src/shared/data-type.ts @@ -88,9 +88,9 @@ export interface DataType { /** Keyed by the id of the type each cast takes values of. */ readonly casts: Readonly>; readonly listCast?: ListCast; - /** The value `json` stores under `params`. It refuses JSON the type does not store in that form, a value the parameters exclude, and JSON in a spelling other than the one the parameters give. */ + /** The value `json` stores under `params`. It refuses JSON the type does not store in that form, a value the parameters exclude, and JSON in a spelling other than the one the parameters give. A codec whose application value is the stored form builds its values here too. */ fromContract(json: JsonValue, params: DataTypeParams): DataTypeValue; - /** A value of the type built from `json`: read under `params`, which refuse a value they exclude, and written in the spelling they give. Every producer of a value that is not reading `contract.json` builds it here: a codec, a written default, a cast. */ + /** A value of the type built from `json`: read under `params`, which refuse a value they exclude, and written in the spelling they give. Every producer of a value that is not reading `contract.json` builds it here: a written default, a cast, and a codec, except a codec whose application value is the stored form, which builds it with `fromContract` so that only the stored spelling is taken. */ valueFrom(json: J, params: DataTypeParams): DataTypeValue; /** The JSON `contract.json` stores for `value`. */ toContract(value: DataTypeValue): JsonValue; diff --git a/packages/1-framework/1-core/framework-components/src/shared/framework-components.ts b/packages/1-framework/1-core/framework-components/src/shared/framework-components.ts index 41bad9f5f10d..a8caeff6b377 100644 --- a/packages/1-framework/1-core/framework-components/src/shared/framework-components.ts +++ b/packages/1-framework/1-core/framework-components/src/shared/framework-components.ts @@ -212,6 +212,9 @@ export interface FamilyDescriptor extends ComponentDes * postgres.targetId // 'postgres' * ``` */ +/** Session settings by name, each with the value a database session must hold. */ +export type SessionSettings = Readonly>; + export interface TargetDescriptor extends ComponentDescriptor<'target'> { /** The family this target belongs to */ @@ -222,6 +225,9 @@ export interface TargetDescriptor { ); }); }); + +const upperText = dataType('demo/upper', { + read: (json) => json, + spell: (json) => (typeof json === 'string' ? json.toUpperCase() : json), +}); + +class SpellingCodec extends CodecImpl<'demo/upper@1', readonly [], string, string> { + constructor( + descriptor: CodecDescriptorImpl, + private readonly storedOnly: boolean, + ) { + super(descriptor, upperText); + } + fromDataTypeValue(value: DataTypeValue): string { + return value.value; + } + toDataTypeValue(input: string): DataTypeValue { + return this.storedOnly ? this.storedDataTypeValueOf(input) : this.dataTypeValueOf(input); + } + async fromWire(wire: string): Promise { + return wire; + } + async toWire(input: string): Promise { + return input; + } +} + +class SpellingDescriptor extends CodecDescriptorImpl { + override readonly dataType = upperText.id; + override readonly codecId = 'demo/upper@1'; + override readonly traits = [] as const; + override readonly paramsSchema = undefined; + override factory(): (ctx: CodecInstanceContext) => SpellingCodec { + return () => new SpellingCodec(this, false); + } +} + +describe('the two ways a codec builds a value of its data type', () => { + const descriptor = new SpellingDescriptor(); + + it('writes any spelling the type reads in its stored spelling, through dataTypeValueOf', () => { + expect(new SpellingCodec(descriptor, false).toDataTypeValue('low').value).toBe('LOW'); + }); + + it('takes only the stored spelling, through storedDataTypeValueOf, for a codec whose application value is the stored form', () => { + const codec = new SpellingCodec(descriptor, true); + expect(codec.toDataTypeValue('LOW').value).toBe('LOW'); + expect(() => codec.toDataTypeValue('low')).toThrow( + expect.objectContaining({ code: 'RUNTIME.DECODE_FAILED' }), + ); + }); +}); diff --git a/packages/1-framework/1-core/framework-components/test/session-settings.test.ts b/packages/1-framework/1-core/framework-components/test/session-settings.test.ts new file mode 100644 index 000000000000..ea916a5cfd7d --- /dev/null +++ b/packages/1-framework/1-core/framework-components/test/session-settings.test.ts @@ -0,0 +1,95 @@ +import { describe, expect, it } from 'vitest'; +import type { ControlDriverConnectOptions } from '../src/control/control-descriptors'; +import { type CreateControlStackInput, createControlStack } from '../src/control/control-stack'; +import type { + RuntimeAdapterDescriptor, + RuntimeDriverDescriptor, + RuntimeTargetDescriptor, +} from '../src/execution/execution-descriptors'; +import { createExecutionStack, instantiateExecutionStack } from '../src/execution/execution-stack'; +import type { SessionSettings } from '../src/shared/framework-components'; + +const SETTINGS: SessionSettings = { ReadStyle: 'plain' }; + +function runtimeTarget(sessionSettings?: SessionSettings): RuntimeTargetDescriptor<'sql', 'demo'> { + return { + kind: 'target', + id: 'demo', + familyId: 'sql', + targetId: 'demo', + version: '0.0.1', + ...(sessionSettings === undefined ? {} : { sessionSettings }), + create: () => ({ familyId: 'sql', targetId: 'demo' }), + }; +} + +const adapter: RuntimeAdapterDescriptor<'sql', 'demo'> = { + kind: 'adapter', + id: 'demo-adapter', + familyId: 'sql', + targetId: 'demo', + version: '0.0.1', + create: () => ({ familyId: 'sql', targetId: 'demo' }), +}; + +function recordingRuntimeDriver() { + const calls: unknown[][] = []; + const driver: RuntimeDriverDescriptor<'sql', 'demo', { readonly batch: number }> = { + kind: 'driver', + id: 'demo-driver', + familyId: 'sql', + targetId: 'demo', + version: '0.0.1', + create: (...args: unknown[]) => { + calls.push(args); + return { familyId: 'sql', targetId: 'demo' }; + }, + }; + return { driver, calls }; +} + +describe('the session settings a target declares', () => { + it('reach every runtime driver the stack creates, with the options the caller gives', () => { + const { driver, calls } = recordingRuntimeDriver(); + const stack = createExecutionStack({ target: runtimeTarget(SETTINGS), adapter, driver }); + + stack.driver?.create({ batch: 5 }); + instantiateExecutionStack(stack); + + expect(calls).toEqual([ + [{ batch: 5 }, SETTINGS], + [undefined, SETTINGS], + ]); + }); + + it('leave the runtime driver as it is when the target declares none', () => { + const { driver } = recordingRuntimeDriver(); + expect(createExecutionStack({ target: runtimeTarget(), adapter, driver }).driver).toBe(driver); + }); + + it('reach every control driver the stack creates, beside the connect options the caller gives', async () => { + const calls: unknown[][] = []; + const input = { + family: { kind: 'family', id: 'sql', version: '0.0.1' }, + target: { kind: 'target', id: 'demo', version: '0.0.1', sessionSettings: SETTINGS }, + driver: { + kind: 'driver', + id: 'demo-driver', + familyId: 'sql', + targetId: 'demo', + version: '0.0.1', + create: async (url: string, options?: ControlDriverConnectOptions) => { + calls.push([url, options]); + return {}; + }, + }, + } as unknown as CreateControlStackInput<'sql', 'demo'>; + + const stack = createControlStack(input); + await stack.driver?.create('postgres://example', { connectTimeoutMs: 5 }); + + expect(calls).toEqual([ + ['postgres://example', { connectTimeoutMs: 5, sessionSettings: SETTINGS }], + ]); + }); +}); diff --git a/packages/2-sql-family/2-authoring/contract-prisma7/test/fixtures/defaults/expected-contract.json b/packages/2-sql-family/2-authoring/contract-prisma7/test/fixtures/defaults/expected-contract.json index 73f82f6c2cbc..e2adb69e8b51 100644 --- a/packages/2-sql-family/2-authoring/contract-prisma7/test/fixtures/defaults/expected-contract.json +++ b/packages/2-sql-family/2-authoring/contract-prisma7/test/fixtures/defaults/expected-contract.json @@ -250,7 +250,7 @@ } }, "execution": { - "executionHash": "f8bf85545419f6c09639ae1eb8943cdf47ce226be55151e46512fdcd7f0fcdc1", + "executionHash": "34724d7f5b296e0eee40a5a82495ef0ff3eb4bcb7428d1261cf475fb29be215b", "mutations": { "defaults": [ { @@ -261,7 +261,10 @@ }, "onCreate": { "kind": "generator", - "id": "timestampStringNow" + "id": "timestampStringNow", + "params": { + "precision": 3 + } } }, { @@ -272,7 +275,10 @@ }, "onCreate": { "kind": "generator", - "id": "timestamptzStringNow" + "id": "timestamptzStringNow", + "params": { + "precision": 6 + } } } ] diff --git a/packages/2-sql-family/2-authoring/contract-prisma7/test/fixtures/updated-at-optional/expected-contract.json b/packages/2-sql-family/2-authoring/contract-prisma7/test/fixtures/updated-at-optional/expected-contract.json index 94f43cc66ec1..1fd89ad2fb00 100644 --- a/packages/2-sql-family/2-authoring/contract-prisma7/test/fixtures/updated-at-optional/expected-contract.json +++ b/packages/2-sql-family/2-authoring/contract-prisma7/test/fixtures/updated-at-optional/expected-contract.json @@ -52,7 +52,7 @@ } }, "execution": { - "executionHash": "db900cc51b4881b6d70e26bbca09f945f2de39bb36d6f47c18f72245245a71a8", + "executionHash": "fd49086f64d04b8fbdb1a5168eae87b6272974b85c774c2ba52bc13665040d49", "mutations": { "defaults": [ { @@ -63,11 +63,17 @@ }, "onCreate": { "kind": "generator", - "id": "timestampStringNow" + "id": "timestampStringNow", + "params": { + "precision": 3 + } }, "onUpdate": { "kind": "generator", - "id": "timestampStringNow" + "id": "timestampStringNow", + "params": { + "precision": 3 + } } } ] diff --git a/packages/2-sql-family/2-authoring/contract-prisma7/test/fixtures/updated-at-with-default/expected-contract.json b/packages/2-sql-family/2-authoring/contract-prisma7/test/fixtures/updated-at-with-default/expected-contract.json index b6b0bf640335..8fecb848c6e0 100644 --- a/packages/2-sql-family/2-authoring/contract-prisma7/test/fixtures/updated-at-with-default/expected-contract.json +++ b/packages/2-sql-family/2-authoring/contract-prisma7/test/fixtures/updated-at-with-default/expected-contract.json @@ -52,7 +52,7 @@ } }, "execution": { - "executionHash": "a1ccd99824fa880275f16fa3f98ea2be9cd5db712de26a1da3e5b2d332cfe6d8", + "executionHash": "7a7d1cf6cde9c00b1c57375b140d9c74dd354caedbbb7c0e201c8bd51cfc9e1e", "mutations": { "defaults": [ { @@ -63,11 +63,17 @@ }, "onCreate": { "kind": "generator", - "id": "timestampStringNow" + "id": "timestampStringNow", + "params": { + "precision": 3 + } }, "onUpdate": { "kind": "generator", - "id": "timestampStringNow" + "id": "timestampStringNow", + "params": { + "precision": 3 + } } } ] diff --git a/packages/2-sql-family/2-authoring/contract-prisma7/test/fixtures/updated-at/expected-contract.json b/packages/2-sql-family/2-authoring/contract-prisma7/test/fixtures/updated-at/expected-contract.json index e7bd02111bf2..dedc0f97de6b 100644 --- a/packages/2-sql-family/2-authoring/contract-prisma7/test/fixtures/updated-at/expected-contract.json +++ b/packages/2-sql-family/2-authoring/contract-prisma7/test/fixtures/updated-at/expected-contract.json @@ -66,7 +66,7 @@ } }, "execution": { - "executionHash": "901dc5392c955173f23872f8945af11b6e29a21e54613ee9b821f7fc69f43c82", + "executionHash": "6648fa9bcd0cacf082bbb524662c5e16474259ce08470b5ee52cf6826b1b88a8", "mutations": { "defaults": [ { @@ -77,11 +77,17 @@ }, "onCreate": { "kind": "generator", - "id": "timestampStringNow" + "id": "timestampStringNow", + "params": { + "precision": 3 + } }, "onUpdate": { "kind": "generator", - "id": "timestampStringNow" + "id": "timestampStringNow", + "params": { + "precision": 3 + } } }, { @@ -92,11 +98,17 @@ }, "onCreate": { "kind": "generator", - "id": "timestamptzStringNow" + "id": "timestamptzStringNow", + "params": { + "precision": 6 + } }, "onUpdate": { "kind": "generator", - "id": "timestamptzStringNow" + "id": "timestamptzStringNow", + "params": { + "precision": 6 + } } } ] diff --git a/packages/2-sql-family/2-authoring/contract-prisma7/test/now-default-generators.test.ts b/packages/2-sql-family/2-authoring/contract-prisma7/test/now-default-generators.test.ts index 26f10f95ff08..abb34d951948 100644 --- a/packages/2-sql-family/2-authoring/contract-prisma7/test/now-default-generators.test.ts +++ b/packages/2-sql-family/2-authoring/contract-prisma7/test/now-default-generators.test.ts @@ -38,10 +38,10 @@ model Post { }); expect(contract.execution?.mutations.defaults).toEqual([ { ref: ref('clock'), onCreate: now('timeStringNow', { precision: 0 }) }, - { ref: ref('createdAt'), onCreate: now('timestampStringNow') }, + { ref: ref('createdAt'), onCreate: now('timestampStringNow', { precision: 3 }) }, { ref: ref('day'), onCreate: now('dateStringNow') }, - { ref: ref('optionalAt'), onCreate: now('timestampStringNow') }, - { ref: ref('zonedAt'), onCreate: now('timestamptzStringNow') }, + { ref: ref('optionalAt'), onCreate: now('timestampStringNow', { precision: 3 }) }, + { ref: ref('zonedAt'), onCreate: now('timestamptzStringNow', { precision: 6 }) }, ]); }); @@ -61,8 +61,8 @@ model Post { expect(contract.execution?.mutations.defaults).toEqual([ { ref: ref('updatedAt'), - onCreate: now('timestampStringNow'), - onUpdate: now('timestampStringNow'), + onCreate: now('timestampStringNow', { precision: 3 }), + onUpdate: now('timestampStringNow', { precision: 3 }), }, ]); }); diff --git a/packages/2-sql-family/2-authoring/contract-prisma7/test/updated-at-generators.test.ts b/packages/2-sql-family/2-authoring/contract-prisma7/test/updated-at-generators.test.ts index e5d49d720931..453ead0dd568 100644 --- a/packages/2-sql-family/2-authoring/contract-prisma7/test/updated-at-generators.test.ts +++ b/packages/2-sql-family/2-authoring/contract-prisma7/test/updated-at-generators.test.ts @@ -38,7 +38,7 @@ describe('@updatedAt on Postgres', () => { ).toMatchObject({ ok: true, generator: { kind: 'generator' } }); }); - it('lowers to now() on create and on update, with the precision of a time column', () => { + it('lowers to now() on create and on update, with the precision of a time or timestamp column', () => { const contract = interpretSchemaText(`datasource db { provider = "postgresql" } @@ -61,13 +61,13 @@ model Clock { }, { ref: ref('stampedAt'), - onCreate: now('timestampStringNow'), - onUpdate: now('timestampStringNow'), + onCreate: now('timestampStringNow', { precision: 0 }), + onUpdate: now('timestampStringNow', { precision: 0 }), }, { ref: ref('updatedAt'), - onCreate: now('timestampStringNow'), - onUpdate: now('timestampStringNow'), + onCreate: now('timestampStringNow', { precision: 3 }), + onUpdate: now('timestampStringNow', { precision: 3 }), }, ]); }); diff --git a/packages/2-sql-family/2-authoring/contract-psl/test/interpreter.alias-expansion.test.ts b/packages/2-sql-family/2-authoring/contract-psl/test/interpreter.alias-expansion.test.ts index 0f3a42fc3885..f2ab1262a5d8 100644 --- a/packages/2-sql-family/2-authoring/contract-psl/test/interpreter.alias-expansion.test.ts +++ b/packages/2-sql-family/2-authoring/contract-psl/test/interpreter.alias-expansion.test.ts @@ -108,8 +108,8 @@ model Post { { ref: ref('id'), onCreate: { kind: 'generator', id: 'uuidv4' } }, { ref: ref('updatedAt'), - onCreate: { kind: 'generator', id: 'timestampStringNow' }, - onUpdate: { kind: 'generator', id: 'timestampStringNow' }, + onCreate: { kind: 'generator', id: 'timestampStringNow', params: { precision: 3 } }, + onUpdate: { kind: 'generator', id: 'timestampStringNow', params: { precision: 3 } }, }, ], }); diff --git a/packages/3-extensions/supabase/src/runtime/supabase.ts b/packages/3-extensions/supabase/src/runtime/supabase.ts index ad76b83cd259..324e1c23a6d3 100644 --- a/packages/3-extensions/supabase/src/runtime/supabase.ts +++ b/packages/3-extensions/supabase/src/runtime/supabase.ts @@ -1,7 +1,10 @@ import postgresAdapter from '@internal/adapter-postgres/runtime'; import type { NamespacedEnums } from '@internal/contract/enum-accessor'; import type { Contract } from '@internal/contract/types'; -import postgresDriver, { suppressIdleConnectionErrors } from '@internal/driver-postgres/runtime'; +import postgresDriver, { + createPgPool, + suppressIdleConnectionErrors, +} from '@internal/driver-postgres/runtime'; import type { AsyncIterableResult, RuntimeExecuteOptions, @@ -55,7 +58,7 @@ import { ifDefined } from '@internal/utils/defined'; import { InternalError } from '@internal/utils/internal-error'; import { structuredError } from '@internal/utils/structured-error'; import { createRemoteJWKSet, decodeProtectedHeader, type JWTVerifyResult, jwtVerify } from 'jose'; -import { Pool } from 'pg'; +import type { Pool } from 'pg'; import extensionContractJson from '../contract/contract.json' with { type: 'json' }; import { isSupabaseRole, SUPABASE_JWT_ROLE_CLAIM, SupabaseRole } from '../contract/roles'; import { supabaseRuntimeDescriptor } from './descriptor'; @@ -280,11 +283,14 @@ function toPool>( if (typeof options.url === 'string') { return { pool: suppressIdleConnectionErrors( - new Pool({ - connectionString: options.url, - connectionTimeoutMillis: options.poolOptions?.connectionTimeoutMillis ?? 20_000, - idleTimeoutMillis: options.poolOptions?.idleTimeoutMillis ?? 30_000, - }), + createPgPool( + { + connectionString: options.url, + connectionTimeoutMillis: options.poolOptions?.connectionTimeoutMillis ?? 20_000, + idleTimeoutMillis: options.poolOptions?.idleTimeoutMillis ?? 30_000, + }, + postgresTarget.sessionSettings ?? {}, + ), ), owned: true, }; diff --git a/packages/3-extensions/supabase/test/date-style-startup-option.test.ts b/packages/3-extensions/supabase/test/date-style-startup-option.test.ts new file mode 100644 index 000000000000..7e4d984289d0 --- /dev/null +++ b/packages/3-extensions/supabase/test/date-style-startup-option.test.ts @@ -0,0 +1,42 @@ +import { contractFromJson } from '@internal/postgres/static'; +import { + type StartupParameterRecorder, + startStartupParameterRecorder, + timeouts, +} from '@repo/test-utils'; +import { afterEach, beforeEach, describe, expect, it } from 'vitest'; +import supabase from '../src/runtime/supabase'; +import type { Contract } from './fixtures/example-app/contract'; +import contractJson from './fixtures/example-app/contract.json' with { type: 'json' }; + +const contract = contractFromJson(contractJson); + +describe('a connection the Supabase client opens from a URL', () => { + let recorder: StartupParameterRecorder; + + beforeEach(async () => { + recorder = await startStartupParameterRecorder(); + }); + + afterEach(async () => { + await recorder.close(); + }); + + it( + 'sends DateStyle=ISO in the startup message of a connection it opens', + async () => { + const db = await supabase({ + contract, + url: recorder.url, + jwtSecret: 'fixture-jwt-signing-input-not-a-real-credential', + verifyMarker: false, + }); + const anon = db.asAnon(); + await anon.execute(anon.raw.sql`SELECT 1`.affectedCount().build()).catch(() => undefined); + await db.close().catch(() => undefined); + + expect((await recorder.next())['options']).toBe('-c DateStyle=ISO'); + }, + timeouts.databaseOperation, + ); +}); diff --git a/packages/3-extensions/supabase/test/session-settings.integration.test.ts b/packages/3-extensions/supabase/test/session-settings.integration.test.ts new file mode 100644 index 000000000000..c530d7cb23af --- /dev/null +++ b/packages/3-extensions/supabase/test/session-settings.integration.test.ts @@ -0,0 +1,75 @@ +import { contractFromJson } from '@internal/postgres/static'; +import { createDevDatabase, timeouts, withClient } from '@repo/test-utils'; +import pg from 'pg'; +import { afterEach, beforeEach, describe, expect, it } from 'vitest'; +import { supabase } from '../src/exports/runtime'; +import type { Contract } from './fixtures/example-app/contract'; +import contractJson from './fixtures/example-app/contract.json' with { type: 'json' }; +import { fixtureJwt } from './fixtures/example-app/db'; +import { setUpSupabaseMockSchema } from './fixtures/supabase-reference/set-up-mock-schema'; + +const contract = contractFromJson(contractJson); + +async function grantDevDatabaseSchema(connectionString: string): Promise { + await withClient(connectionString, async (client) => { + await client.query( + 'GRANT USAGE ON SCHEMA _prisma_dev_wal TO anon, authenticated, service_role', + ); + await client.query( + 'GRANT ALL ON ALL TABLES IN SCHEMA _prisma_dev_wal TO anon, authenticated, service_role', + ); + await client.query( + 'GRANT ALL ON ALL SEQUENCES IN SCHEMA _prisma_dev_wal TO anon, authenticated, service_role', + ); + await client.query( + 'GRANT EXECUTE ON FUNCTION _prisma_dev_wal.capture_event() TO anon, authenticated, service_role', + ); + }); +} + +describe('the session settings of a Supabase client on a pool the application supplies', () => { + let database: Awaited>; + + beforeEach(async () => { + database = await createDevDatabase(); + await withClient(database.connectionString, setUpSupabaseMockSchema); + await grantDevDatabaseSchema(database.connectionString); + }, timeouts.spinUpPpgDev); + + afterEach(async () => { + await database?.close(); + }, timeouts.spinUpPpgDev); + + it( + 'hold again for the next role scope after the RESET ALL that ends one, whatever the session went back to', + async () => { + const pool = new pg.Pool({ connectionString: database.connectionString, max: 1 }); + const db = await supabase({ contract, pg: pool, jwtSecret: fixtureJwt, verifyMarker: false }); + const service = db.asServiceRole(); + const dateStyle = async () => { + const rows = await service.query( + service.raw.sql`SELECT current_setting('DateStyle') AS style` + .returnsRow({ style: 'pg/text@1' }) + .build(), + ); + return rows[0]?.style; + }; + + try { + await pool.query("SET DateStyle TO 'German, DMY'"); + const first = await dateStyle(); + await pool.query("SET DateStyle TO 'German, DMY'"); + const second = await dateStyle(); + + expect({ first, second }).toEqual({ + first: expect.stringMatching(/^ISO/), + second: expect.stringMatching(/^ISO/), + }); + } finally { + await db.close(); + await pool.end(); + } + }, + timeouts.spinUpPpgDev, + ); +}); diff --git a/packages/3-targets/postgres/1-target/src/core/authoring.ts b/packages/3-targets/postgres/1-target/src/core/authoring.ts index 975174d90686..fbb48d1b900d 100644 --- a/packages/3-targets/postgres/1-target/src/core/authoring.ts +++ b/packages/3-targets/postgres/1-target/src/core/authoring.ts @@ -956,11 +956,13 @@ export const postgresAuthoringFieldPresets = { sizeType: PG_SIZE_TYPE, codecId: PG_TIMESTAMP_STRING_CODEC_ID, generatorId: postgresNowGeneratorIds[PG_TIMESTAMP_STRING_CODEC_ID], + nowTakesPrecision: postgresNowGeneratorTakesPrecision[PG_TIMESTAMP_STRING_CODEC_ID], }), timestamptzString: /* @__PURE__ */ temporalCodecPresetWithPrecision({ sizeType: PG_SIZE_TYPE, codecId: PG_TIMESTAMPTZ_STRING_CODEC_ID, generatorId: postgresNowGeneratorIds[PG_TIMESTAMPTZ_STRING_CODEC_ID], + nowTakesPrecision: postgresNowGeneratorTakesPrecision[PG_TIMESTAMPTZ_STRING_CODEC_ID], }), date: /* @__PURE__ */ temporalCodecPreset({ codecId: PG_DATE_TEMPORAL_CODEC_ID, diff --git a/packages/3-targets/postgres/1-target/src/core/codecs.ts b/packages/3-targets/postgres/1-target/src/core/codecs.ts index 76b22c30f281..19384ba30946 100644 --- a/packages/3-targets/postgres/1-target/src/core/codecs.ts +++ b/packages/3-targets/postgres/1-target/src/core/codecs.ts @@ -136,6 +136,7 @@ import { pgVarchar, } from './data-types'; import { pgTimestampDateDescriptor, pgTimestamptzDateDescriptor } from './date-codecs'; +import { type DateTimeWireReading, storedTextFromWire } from './date-time-wire-text'; import { postgresError } from './errors'; import { pgIntervalStringDescriptor } from './interval-string-codec'; import { parsePostgresListText } from './list-decoder'; @@ -934,6 +935,12 @@ export const pgUnboundedIntColumn = () => pgUnboundedIntColumn satisfies ColumnHelperFor; pgUnboundedIntColumn satisfies ColumnHelperForStrict; +const TIMETZ_READING: DateTimeWireReading = { + shape: 'timeWithOffset', + holdsInfinity: false, + maxOffsetHours: 15, +}; + export class PgTimetzCodec extends CodecImpl< typeof PG_TIMETZ_CODEC_ID, readonly ['equality', 'order'], @@ -944,13 +951,13 @@ export class PgTimetzCodec extends CodecImpl< return value; } async fromWire(wire: string, _ctx: CodecCallContext): Promise { - return wire; + return storedTextFromWire(this.id, TIMETZ_READING, wire); } fromDataTypeValue(value: DataTypeValue): string { return value.value; } toDataTypeValue(input: string): DataTypeValue { - return this.dataTypeValueOf(input); + return this.storedDataTypeValueOf(input); } } @@ -961,8 +968,9 @@ export class PgTimetzDescriptor extends PostgresCodecDescriptor override readonly dataType = pgTimetz.id; override readonly codecId = PG_TIMETZ_CODEC_ID; override readonly traits = ['equality', 'order'] as const; - override readonly enumRefusal = - 'A query reads each value as the text PostgreSQL prints, such as "12:34:56+02", while the contract stores it in ISO 8601, such as "12:34:56+02:00", so a value read back need not equal the member written. No enum can use a timetz codec; use a text enum instead.'; + override renderValueLiteral(value: JsonValue): string | undefined { + return renderTsLiteral(value); + } override readonly paramsSchema = pgPrecisionParams satisfies StandardSchemaV1; override factory(params: PrecisionParams): (ctx: CodecInstanceContext) => PgTimetzCodec { return () => new PgTimetzCodec(this, pgTimetz, params ?? {}); diff --git a/packages/3-targets/postgres/1-target/src/core/date-codecs.ts b/packages/3-targets/postgres/1-target/src/core/date-codecs.ts index 0317c589213f..8690b596b2dc 100644 --- a/packages/3-targets/postgres/1-target/src/core/date-codecs.ts +++ b/packages/3-targets/postgres/1-target/src/core/date-codecs.ts @@ -26,6 +26,7 @@ import { pgTimestamptz, pgTimestamptzStoredText, } from './data-types'; +import { requireIsoDateStyle } from './date-time-wire-text'; import { EARLIEST_POSTGRES_TIMESTAMP_MILLISECONDS, utcTimestampText, @@ -89,7 +90,10 @@ function storedInstant(identity: DateCodecIdentity, text: string): string | unde } function decodeDate(identity: DateCodecIdentity, wire: unknown): Date { - const instant = typeof wire === 'string' ? storedInstant(identity, wire) : undefined; + const instant = + typeof wire === 'string' + ? storedInstant(identity, requireIsoDateStyle(identity.codecId, wire)) + : undefined; const date = instant === undefined ? undefined : new Date(instant); if (date === undefined || !isRepresentable(date)) throw invalidDate(identity); return date; diff --git a/packages/3-targets/postgres/1-target/src/core/date-time-now-generators.ts b/packages/3-targets/postgres/1-target/src/core/date-time-now-generators.ts index 87b1aaa79da8..8e96cbc723d4 100644 --- a/packages/3-targets/postgres/1-target/src/core/date-time-now-generators.ts +++ b/packages/3-targets/postgres/1-target/src/core/date-time-now-generators.ts @@ -3,7 +3,7 @@ import type { Temporal } from 'temporal-spec'; import { requireTemporal } from './require-temporal'; /** - * The Postgres "now" generators, one per date and time codec except `pg/timestamptz-date@1`, which uses the framework's `timestampNow`. All read the clock through `Date.now()` and write UTC. The time generators round down to their `precision` param. The string generators are named after the codec they serve and write the text PostgreSQL prints in a UTC session, with milliseconds and no trailing zeros in the fraction. + * The Postgres "now" generators, one per date and time codec except `pg/timestamptz-date@1`, which uses the framework's `timestampNow`. All read the clock through `Date.now()` and write UTC. The time generators and the string timestamp generators round down to their `precision` param, so the column stores the value as written. The string generators are named after the codec they serve and write the codec's stored form (ADR 254, "Date and time types") in UTC, with milliseconds and no trailing zeros in the fraction. */ export const INSTANT_NOW_GENERATOR_ID = 'instantNow' as const; export const PLAIN_DATE_TIME_NOW_GENERATOR_ID = 'plainDateTimeNow' as const; @@ -15,7 +15,7 @@ export const DATE_STRING_NOW_GENERATOR_ID = 'dateStringNow' as const; export const TIME_STRING_NOW_GENERATOR_ID = 'timeStringNow' as const; export const TIMETZ_NOW_GENERATOR_ID = 'timetzNow' as const; -const UTC_OFFSET = '+00'; +const UTC_OFFSET = 'Z'; const MAX_TIME_PRECISION = 6; type GeneratorParams = Readonly> | undefined; @@ -77,13 +77,13 @@ export function plainTimeNow(params?: GeneratorParams): Temporal.PlainTime { ); } -export function timestampStringNow(): string { - const { date, time } = utcNow(); - return `${date} ${time}`; +export function timestampStringNow(params?: GeneratorParams): string { + const { date, time } = utcNow(fractionDigitsOf(params)); + return `${date}T${time}`; } -export function timestamptzStringNow(): string { - return `${timestampStringNow()}${UTC_OFFSET}`; +export function timestamptzStringNow(params?: GeneratorParams): string { + return `${timestampStringNow(params)}${UTC_OFFSET}`; } export function dateStringNow(): string { diff --git a/packages/3-targets/postgres/1-target/src/core/date-time-wire-text.ts b/packages/3-targets/postgres/1-target/src/core/date-time-wire-text.ts new file mode 100644 index 000000000000..1f3006b1353c --- /dev/null +++ b/packages/3-targets/postgres/1-target/src/core/date-time-wire-text.ts @@ -0,0 +1,56 @@ +import { type DateTimeShape, dateTimeStoredText } from '@internal/framework-components/codec'; +import { ifDefined } from '@internal/utils/defined'; +import { errorDateStyleNotIso } from './errors'; +import { isoYearText } from './postgres-year-text'; + +export interface DateTimeWireReading { + readonly shape: DateTimeShape; + readonly holdsInfinity: boolean; + readonly maxOffsetHours?: number; +} + +const INFINITIES: ReadonlySet = new Set(['infinity', '-infinity']); +const ISO_DATE_PREFIX = /^[+-]?\d{4,}-\d{2}-\d{2}/; +const LONG_YEAR_DATE = /^(\d{7,})(-\d{2}-\d{2})$/; +const END_OF_DAY = '24:00:00'; +const MIDNIGHT = '00:00:00'; + +/** `wire`, a date or timestamp PostgreSQL printed or its ISO 8601 stored form, unless it printed it under a DateStyle other than ISO, which is refused. `infinity` and `-infinity` print the same in every style. */ +export function requireIsoDateStyle(codecId: string, wire: string): string { + if (INFINITIES.has(wire) || ISO_DATE_PREFIX.test(wire)) return wire; + throw errorDateStyleNotIso(codecId, wire); +} + +/** + * The stored form (ADR 254, "Date and time types") of the text PostgreSQL prints for a date or time type under DateStyle ISO, with the UTC offset of an instant converted to UTC. A value PostgreSQL holds beyond the stored form's range is read as ISO text of the same shape: a year of seven digits keeps them all, as in `+5874897-12-31`, and the end of the day stays `24:00:00`. Text PostgreSQL prints under another DateStyle is refused. + */ +export function storedTextFromWire( + codecId: string, + reading: DateTimeWireReading, + wire: string, +): string { + if (reading.holdsInfinity && INFINITIES.has(wire)) return wire; + const hasDate = reading.shape !== 'time' && reading.shape !== 'timeWithOffset'; + if (hasDate) requireIsoDateStyle(codecId, wire); + + const longYear = reading.shape === 'date' ? LONG_YEAR_DATE.exec(wire) : null; + if (longYear !== null) return `+${longYear[1]}${longYear[2]}`; + + if (!hasDate && wire.startsWith(END_OF_DAY)) { + const read = storedText(codecId, reading, `${MIDNIGHT}${wire.slice(END_OF_DAY.length)}`); + return `${END_OF_DAY}${read.slice(MIDNIGHT.length)}`; + } + return storedText(codecId, reading, wire); +} + +function storedText(codecId: string, reading: DateTimeWireReading, wire: string): string { + return dateTimeStoredText( + isoYearText(wire), + { + shape: reading.shape, + ownerId: codecId, + ...ifDefined('maxOffsetHours', reading.maxOffsetHours), + }, + wire, + ); +} diff --git a/packages/3-targets/postgres/1-target/src/core/descriptor-meta-runtime.ts b/packages/3-targets/postgres/1-target/src/core/descriptor-meta-runtime.ts index 5da73b56fd24..ffc886b45e36 100644 --- a/packages/3-targets/postgres/1-target/src/core/descriptor-meta-runtime.ts +++ b/packages/3-targets/postgres/1-target/src/core/descriptor-meta-runtime.ts @@ -23,6 +23,7 @@ const postgresTargetDescriptorMetaRuntimeBase = { version: '0.0.1', capabilities: {}, dataTypes: postgresDataTypes, + sessionSettings: { DateStyle: 'ISO' }, } as const; export const postgresTargetDescriptorMetaRuntime: typeof postgresTargetDescriptorMetaRuntimeBase & { diff --git a/packages/3-targets/postgres/1-target/src/core/errors.ts b/packages/3-targets/postgres/1-target/src/core/errors.ts index fc596dac93b2..3a22a9ecc2f8 100644 --- a/packages/3-targets/postgres/1-target/src/core/errors.ts +++ b/packages/3-targets/postgres/1-target/src/core/errors.ts @@ -22,6 +22,7 @@ export type PostgresTargetErrorCode = | 'MIGRATION.SQL_EXPRESSION_INVALID' | 'MIGRATION.TARGET_MISMATCH' | 'RUNTIME.ARGUMENT_INVALID' + | 'RUNTIME.DATE_STYLE_NOT_ISO' | 'RUNTIME.DECODE_FAILED' | 'RUNTIME.ENCODE_FAILED' | 'RUNTIME.TEMPORAL_UNAVAILABLE' @@ -75,7 +76,7 @@ export function errorTemporalUnavailable(codecId: string, operation: string): St `Codec '${codecId}' cannot ${operation} a value because this runtime has no global Temporal implementation.`, { why: 'Temporal-backed codecs read and write their values through the global Temporal API, which this runtime does not provide.', - fix: "Run on a runtime with Temporal available, install a Temporal polyfill before creating the client, or author the column with its *String type to read and write PostgreSQL's own text instead.", + fix: 'Run on a runtime with Temporal available, install a Temporal polyfill before creating the client, or author the column with its *String type to read and write ISO 8601 text instead.', meta: { codecId, operation }, }, ); @@ -87,7 +88,7 @@ export function errorTemporalUnavailableForDefault(generatorId: string): Structu `Mutation default generator '${generatorId}' cannot produce a value because this runtime has no global Temporal implementation.`, { why: 'The generator answers a Temporal-backed column, so the value it produces is a Temporal.Instant, which this runtime cannot construct.', - fix: "Run on a runtime with Temporal available, install a Temporal polyfill before creating the client, or author the column with `temporal.createdAtString()` / `temporal.updatedAtString()` to store PostgreSQL's own text instead.", + fix: 'Run on a runtime with Temporal available, install a Temporal polyfill before creating the client, or author the column with `temporal.createdAtString()` / `temporal.updatedAtString()` to store ISO 8601 text instead.', meta: { generatorId }, }, ); @@ -105,10 +106,10 @@ export function errorTemporalUnrepresentable(options: { const direction = operation === 'decode' ? 'PostgreSQL returned' : 'the application supplied'; return postgresError( operation === 'decode' ? 'RUNTIME.DECODE_FAILED' : 'RUNTIME.ENCODE_FAILED', - `Codec '${codecId}' cannot represent the value ${direction}: ${JSON.stringify(value)} — ${detail}. Use ${stringType} to carry this column as PostgreSQL's own text.`, + `Codec '${codecId}' cannot represent the value ${direction}: ${JSON.stringify(value)} — ${detail}. Use ${stringType} to carry this column as ISO 8601 text.`, { why: `${detail}. Temporal is the authoritative parser and range check for this codec, so a value it cannot express has no representation on this column.`, - fix: `Author the column as ${stringType} to read and write PostgreSQL's own text for it, which preserves every value the database accepts.`, + fix: `Author the column as ${stringType} to read and write it as ISO 8601 text, which holds every value the database accepts.`, meta: { codecId, operation, value, stringType }, ...(options.cause === undefined ? {} : { cause: options.cause }), }, @@ -126,20 +127,33 @@ export function errorTemporalWrongType( `Codec '${codecId}' encodes a ${temporalTag}, but received ${received}.`, { why: "The codec serializes through the Temporal value's own toString(). A different type reaches PostgreSQL in a spelling it does not accept, and the resulting error names the column rather than the codec.", - fix: `Pass a ${temporalTag}, or author the column with its ${stringType} type to write PostgreSQL text directly.`, + fix: `Pass a ${temporalTag}, or author the column with its ${stringType} type to write ISO 8601 text.`, meta: { codecId, temporalTag, stringType, received }, }, ); } +/** A date or time codec read text PostgreSQL printed under a DateStyle other than ISO, which no codec reads. */ +export function errorDateStyleNotIso(codecId: string, received: string): StructuredError { + return postgresError( + 'RUNTIME.DATE_STYLE_NOT_ISO', + `Codec '${codecId}' read ${JSON.stringify(received)}, which PostgreSQL prints only when the connection's DateStyle is not ISO: the driver could not ensure the DateStyle=ISO the PostgreSQL target requires. Prisma reads dates and times only in DateStyle ISO.`, + { + why: 'PostgreSQL prints a date or timestamp in the DateStyle of the connection. The PostgreSQL driver ensures DateStyle=ISO on every connection it uses: it starts each connection it opens with it, and sets it on a connection the application supplies before first use. A statement that sets DateStyle on a connection undoes that, and so does a connection pooler between Prisma and PostgreSQL that drops the startup option or runs each statement on another server connection.', + fix: "Do not change DateStyle on a connection Prisma uses. Behind a connection pooler, set DateStyle on the role or database (ALTER ROLE SET DateStyle = 'ISO'), so every server connection holds it.", + meta: { codecId, received: received.slice(0, 100) }, + }, + ); +} + /** A string timestamp codec received something other than text, such as a `Date` from a contract emitted before its generator wrote text. */ export function errorTimestampTextWrongType(codecId: string, received: string): StructuredError { return postgresError( 'RUNTIME.ENCODE_FAILED', - `Codec '${codecId}' writes PostgreSQL's text for the value, but received ${received}.`, + `Codec '${codecId}' writes ISO 8601 text, but received ${received}.`, { why: "The codec sends its value to PostgreSQL unchanged. A Date would be sent in the process's local time zone, and a timestamp column without a time zone would store the wrong wall-clock time.", - fix: 'Pass the timestamp as text, such as "2024-01-02 03:04:05.678". If the value came from a generated default, emit the contract again: a contract emitted by an earlier version pairs this column with timestampNow, which generates a Date.', + fix: 'Pass the timestamp as ISO 8601 text, such as "2024-01-02T03:04:05.678". If the value came from a generated default, emit the contract again: a contract emitted by an earlier version pairs this column with timestampNow, which generates a Date.', meta: { codecId, received }, }, ); diff --git a/packages/3-targets/postgres/1-target/src/core/now-generators.ts b/packages/3-targets/postgres/1-target/src/core/now-generators.ts index 964215258c15..db8f02261cc8 100644 --- a/packages/3-targets/postgres/1-target/src/core/now-generators.ts +++ b/packages/3-targets/postgres/1-target/src/core/now-generators.ts @@ -51,14 +51,16 @@ export function postgresNowGeneratorIdFor(codecId: string): string | undefined { return nowGeneratorIdByCodecId.get(codecId); } -/** The codecs whose "now" generator takes the column's precision, so that it rounds down where PostgreSQL would round a time up to `24:00:00`. */ +/** The codecs whose "now" generator takes the column's precision and rounds down to it, so PostgreSQL stores the value as generated rather than rounding it up, which for a time can reach `24:00:00`. */ export const postgresNowGeneratorTakesPrecision = { [PG_TIME_TEMPORAL_CODEC_ID]: true, [PG_TIME_STRING_CODEC_ID]: true, [PG_TIMETZ_CODEC_ID]: true, + [PG_TIMESTAMP_STRING_CODEC_ID]: true, + [PG_TIMESTAMPTZ_STRING_CODEC_ID]: true, } as const; -/** The `now` generator function bound to each Postgres date and time codec, with the column's precision on the three time codecs. */ +/** The `now` generator function bound to each Postgres date and time codec, with the column's precision on the codecs that take it. */ export const postgresNowGeneratorFunction: GeneratorFunctionContribution = { name: 'now', bindings: Object.entries(postgresNowGeneratorIds).map(([codecId, generatorId]) => diff --git a/packages/3-targets/postgres/1-target/src/core/postgres-year-text.ts b/packages/3-targets/postgres/1-target/src/core/postgres-year-text.ts index 8f53e1bbe49e..9371ed76ab58 100644 --- a/packages/3-targets/postgres/1-target/src/core/postgres-year-text.ts +++ b/packages/3-targets/postgres/1-target/src/core/postgres-year-text.ts @@ -3,7 +3,7 @@ */ const POSTGRES_YEAR = /^(\d{4,6})(-.*?)( BC)?$/; -const ISO_YEAR = /^([+-]\d{6}|\d{4})(-.*)$/; +const ISO_YEAR = /^([+-]\d{6,}|\d{4})(-.*)$/; const BC_SUFFIX = ' BC'; /** ISO 8601 text, with a four-digit or signed six-digit year, from text PostgreSQL prints. Text with no year, ISO text and `0000 BC`, which does not exist, stay as they are. */ @@ -19,7 +19,7 @@ export function isoYearText(text: string): string { return digits.length > 4 ? `${isoYear(year)}${rest}` : text; } -/** The text PostgreSQL reads for ISO 8601 text with a four-digit or signed six-digit year. Text with no year stays as it is. */ +/** The text PostgreSQL reads for ISO 8601 text with a four-digit year or a signed year of six or more digits. Text with no year stays as it is. */ export function postgresYearText(text: string): string { const match = ISO_YEAR.exec(text); if (match === null) return text; diff --git a/packages/3-targets/postgres/1-target/src/core/temporal-codec-helpers.ts b/packages/3-targets/postgres/1-target/src/core/temporal-codec-helpers.ts index e82e7903b291..bcd3555830c0 100644 --- a/packages/3-targets/postgres/1-target/src/core/temporal-codec-helpers.ts +++ b/packages/3-targets/postgres/1-target/src/core/temporal-codec-helpers.ts @@ -8,6 +8,7 @@ import { PG_TIMESTAMPTZ_TEMPORAL_CODEC_ID, } from './codec-ids'; import { pgTimestampStoredText } from './data-types'; +import { requireIsoDateStyle } from './date-time-wire-text'; import { errorTemporalNonIsoCalendar, errorTemporalUnrepresentable, @@ -18,6 +19,9 @@ import { requireTemporal, type TemporalImplementation } from './require-temporal const POSTGRES_TEMPORAL_SENTINELS: ReadonlySet = new Set(['infinity', '-infinity']); +/** The Temporal types whose text PostgreSQL prints the same in every DateStyle, because it holds no date. Every other Temporal codec reads a date, so its text is refused unless PostgreSQL printed it under DateStyle ISO. */ +const TEMPORAL_TAGS_WITHOUT_A_DATE: ReadonlySet = new Set(['Temporal.PlainTime']); + interface TemporalCodecIdentity { readonly codecId: string; readonly stringType: string; @@ -30,6 +34,9 @@ function decodeTemporalText( parse: (temporal: TemporalImplementation, text: string) => T, ): T { const temporal = requireTemporal({ codecId: identity.codecId, operation: 'decode' }); + if (!TEMPORAL_TAGS_WITHOUT_A_DATE.has(identity.temporalTag)) { + requireIsoDateStyle(identity.codecId, wire); + } if (POSTGRES_TEMPORAL_SENTINELS.has(wire)) { throw errorTemporalUnrepresentable({ ...identity, diff --git a/packages/3-targets/postgres/1-target/src/core/temporal-string-codecs.ts b/packages/3-targets/postgres/1-target/src/core/temporal-string-codecs.ts index 44d625f11a98..648d87e1b34b 100644 --- a/packages/3-targets/postgres/1-target/src/core/temporal-string-codecs.ts +++ b/packages/3-targets/postgres/1-target/src/core/temporal-string-codecs.ts @@ -20,9 +20,15 @@ import { PG_TIMESTAMPTZ_STRING_CODEC_ID, } from './codec-ids'; import { pgDate, pgPrecisionParams, pgTime, pgTimestamp, pgTimestamptz } from './data-types'; +import { type DateTimeWireReading, storedTextFromWire } from './date-time-wire-text'; import { errorTimestampTextWrongType } from './errors'; import { postgresYearText } from './postgres-year-text'; +const DATE_READING: DateTimeWireReading = { shape: 'date', holdsInfinity: true }; +const TIMESTAMP_READING: DateTimeWireReading = { shape: 'dateTime', holdsInfinity: true }; +const TIMESTAMPTZ_READING: DateTimeWireReading = { shape: 'instant', holdsInfinity: true }; +const TIME_READING: DateTimeWireReading = { shape: 'time', holdsInfinity: false }; + export class PgDateStringCodec extends CodecImpl< typeof PG_DATE_STRING_CODEC_ID, readonly ['equality', 'order'], @@ -33,13 +39,13 @@ export class PgDateStringCodec extends CodecImpl< return postgresYearText(value); } async fromWire(wire: string, _ctx: CodecCallContext): Promise { - return wire; + return storedTextFromWire(this.id, DATE_READING, wire); } fromDataTypeValue(value: DataTypeValue): string { return value.value; } toDataTypeValue(input: string): DataTypeValue { - return this.dataTypeValueOf(input); + return this.storedDataTypeValueOf(input); } } @@ -88,25 +94,16 @@ export class PgTimestampStringCodec extends CodecImpl< return postgresYearText(requireText(this.id, value)); } async fromWire(wire: string, _ctx: CodecCallContext): Promise { - return wire; + return storedTextFromWire(this.id, TIMESTAMP_READING, wire); } fromDataTypeValue(value: DataTypeValue): string { return value.value; } toDataTypeValue(input: string): DataTypeValue { - return this.dataTypeValueOf(input); + return this.storedDataTypeValueOf(input); } } -/** Why an enum cannot use a string timestamp codec, given how PostgreSQL prints a value and how the contract stores it. */ -const stringTimestampEnumRefusal = ( - printed: string, - stored: string, - printing: string, - instead: string, -): string => - `A query reads each value as the text PostgreSQL prints${printing}, such as "${printed}", while the contract stores it in ISO 8601, such as "${stored}", so no value read back equals a member. Use ${instead}, whose members are Temporal values.`; - export class PgTimestampStringDescriptor extends PostgresCodecDescriptor { protected override jsonProjection(expression: ProjectionExpr): ProjectionExpr { return CastExpr.as(expression, 'text'); @@ -114,12 +111,9 @@ export class PgTimestampStringDescriptor extends PostgresCodecDescriptor; override factory(params: PrecisionParams): (ctx: CodecInstanceContext) => PgTimestampStringCodec { return () => new PgTimestampStringCodec(this, pgTimestamp, params ?? {}); @@ -144,13 +138,13 @@ export class PgTimestamptzStringCodec extends CodecImpl< return postgresYearText(requireText(this.id, value)); } async fromWire(wire: string, _ctx: CodecCallContext): Promise { - return wire; + return storedTextFromWire(this.id, TIMESTAMPTZ_READING, wire); } fromDataTypeValue(value: DataTypeValue): string { return value.value; } toDataTypeValue(input: string): DataTypeValue { - return this.dataTypeValueOf(input); + return this.storedDataTypeValueOf(input); } } @@ -161,12 +155,9 @@ export class PgTimestamptzStringDescriptor extends PostgresCodecDescriptor; override factory( params: PrecisionParams, @@ -197,13 +188,13 @@ export class PgTimeStringCodec extends CodecImpl< return value; } async fromWire(wire: string, _ctx: CodecCallContext): Promise { - return wire; + return storedTextFromWire(this.id, TIME_READING, wire); } fromDataTypeValue(value: DataTypeValue): string { return value.value; } toDataTypeValue(input: string): DataTypeValue { - return this.dataTypeValueOf(input); + return this.storedDataTypeValueOf(input); } } diff --git a/packages/3-targets/postgres/1-target/src/core/type-constructors.ts b/packages/3-targets/postgres/1-target/src/core/type-constructors.ts index 0cda1602c106..9b897ad8499d 100644 --- a/packages/3-targets/postgres/1-target/src/core/type-constructors.ts +++ b/packages/3-targets/postgres/1-target/src/core/type-constructors.ts @@ -155,7 +155,8 @@ export const postgresNativeAuthoringTypes = { Timetz: { kind: 'typeConstructor', inferred: true, - documentation: 'A time of day with a UTC offset stored as PostgreSQL timetz.', + documentation: + 'A time of day with a UTC offset stored as PostgreSQL timetz and represented as ISO 8601 text.', args: [{ kind: 'value', dataType: PG_SIZE_TYPE, name: 'precision', optional: true }], output: { codecId: 'pg/timetz@1', @@ -197,18 +198,18 @@ export const postgresNativeAuthoringTypes = { output: { codecId: 'pg/date-temporal@1' }, }, // The representation-explicit spellings. Same columns, same precision, same native types — the - // only difference is that a read hands back PostgreSQL's own text instead of a `Temporal.*`, so a - // value Temporal cannot express still round-trips. + // only difference is that a read hands back ISO 8601 text instead of a `Temporal.*`, so a value + // Temporal cannot express still round-trips. DateString: { kind: 'typeConstructor', inferred: true, - documentation: 'A PostgreSQL date represented as database text rather than Temporal.PlainDate.', + documentation: 'A PostgreSQL date represented as ISO 8601 text rather than Temporal.PlainDate.', output: { codecId: 'pg/date-string@1' }, }, TimestampString: { kind: 'typeConstructor', inferred: true, - documentation: 'A timestamp without a time zone represented as PostgreSQL text.', + documentation: 'A timestamp without a time zone represented as ISO 8601 text.', args: [{ kind: 'value', dataType: PG_SIZE_TYPE, name: 'precision', optional: true }], output: { codecId: 'pg/timestamp-string@1', @@ -248,7 +249,7 @@ export const postgresNativeAuthoringTypes = { TimestamptzString: { kind: 'typeConstructor', inferred: true, - documentation: 'A timestamp with time zone represented as PostgreSQL text.', + documentation: 'A timestamp with time zone represented as ISO 8601 text, the instant in UTC.', args: [{ kind: 'value', dataType: PG_SIZE_TYPE, name: 'precision', optional: true }], output: { codecId: 'pg/timestamptz-string@1', @@ -258,7 +259,7 @@ export const postgresNativeAuthoringTypes = { TimeString: { kind: 'typeConstructor', inferred: true, - documentation: 'A time of day without a time zone represented as PostgreSQL text.', + documentation: 'A time of day without a time zone represented as ISO 8601 text.', args: [{ kind: 'value', dataType: PG_SIZE_TYPE, name: 'precision', optional: true }], output: { codecId: 'pg/time-string@1', diff --git a/packages/3-targets/postgres/1-target/test/authoring-field-presets.test.ts b/packages/3-targets/postgres/1-target/test/authoring-field-presets.test.ts index 605b2c9cb532..a3bd07bf63c6 100644 --- a/packages/3-targets/postgres/1-target/test/authoring-field-presets.test.ts +++ b/packages/3-targets/postgres/1-target/test/authoring-field-presets.test.ts @@ -131,11 +131,13 @@ describe('postgres temporal per-codec presets', () => { sizeType: 'pg/int4', codecId: 'pg/timestamp-string@1', generatorId: 'timestampStringNow', + nowTakesPrecision: true, }), timestamptzString: temporalCodecPresetWithPrecision({ sizeType: 'pg/int4', codecId: 'pg/timestamptz-string@1', generatorId: 'timestamptzStringNow', + nowTakesPrecision: true, }), date: temporalCodecPreset({ codecId: 'pg/date-temporal@1', generatorId: 'plainDateNow' }), dateString: temporalCodecPreset({ @@ -166,8 +168,6 @@ describe('postgres temporal per-codec presets', () => { it.each([ ['timestamp', 'plainDateTimeNow'], ['timestamptz', 'instantNow'], - ['timestampString', 'timestampStringNow'], - ['timestamptzString', 'timestamptzStringNow'], ] as const)( 'temporal.%s answers `now` with the %s generator, the representation its codec encodes', (helper, generatorId) => { @@ -190,6 +190,8 @@ describe('postgres temporal per-codec presets', () => { ['time', 'plainTimeNow'], ['timeString', 'timeStringNow'], ['timetz', 'timetzNow'], + ['timestampString', 'timestampStringNow'], + ['timestamptzString', 'timestamptzStringNow'], ] as const)( 'temporal.%s answers `now` with the %s generator, given the precision so it rounds down', (helper, generatorId) => { diff --git a/packages/3-targets/postgres/1-target/test/codec-decode-json-forms.test.ts b/packages/3-targets/postgres/1-target/test/codec-decode-json-forms.test.ts index fb6250fbbf2e..5150748f7d99 100644 --- a/packages/3-targets/postgres/1-target/test/codec-decode-json-forms.test.ts +++ b/packages/3-targets/postgres/1-target/test/codec-decode-json-forms.test.ts @@ -441,7 +441,7 @@ describe('the length and scale checks on a long run of padding', () => { }); }); -describe('the date and time codecs that carry PostgreSQL text read what they write', () => { +describe('the date and time codecs that carry text store only the stored form', () => { it.each([ ['pg/date-string@1', pgDateStringDescriptor.factory()(ctx), '0044-03-15 BC', '-000043-03-15'], [ @@ -458,8 +458,14 @@ describe('the date and time codecs that carry PostgreSQL text read what they wri ], ['pg/time-string@1', pgTimeStringDescriptor.factory({})(ctx), '12:34', '12:34:00'], ['pg/timetz@1', pgTimetzDescriptor.factory({})(ctx), '12:34:56+02', '12:34:56+02:00'], - ])('%s stores %s as %s and reads that text back unchanged', (_id, codec, value, stored) => { - const json = toContractJson(codec, value); + ])('%s refuses %s and stores %s as written', (_id, codec, other, stored) => { + expect(() => toContractJson(codec, other)).toThrow( + expect.objectContaining({ + code: 'RUNTIME.DECODE_FAILED', + message: expect.stringContaining(stored), + }), + ); + const json = toContractJson(codec, stored); expect({ json, read: fromContractJson(codec, json) }).toEqual({ json: stored, read: stored }); }); }); diff --git a/packages/3-targets/postgres/1-target/test/codec-render-value-literal.test.ts b/packages/3-targets/postgres/1-target/test/codec-render-value-literal.test.ts index eb081cb2c37d..ffe4310ce313 100644 --- a/packages/3-targets/postgres/1-target/test/codec-render-value-literal.test.ts +++ b/packages/3-targets/postgres/1-target/test/codec-render-value-literal.test.ts @@ -192,13 +192,13 @@ describe('the literal each registered codec renders', () => { 'pg/timestamptz-temporal@1': undefined, 'pg/time-temporal@1': undefined, 'pg/date-string@1': '"2024-01-01"', - 'pg/timestamp-string@1': undefined, - 'pg/timestamptz-string@1': undefined, + 'pg/timestamp-string@1': '"2024-01-01T00:00:00"', + 'pg/timestamptz-string@1': '"2024-01-01T00:00:00Z"', 'pg/timestamptz-date@1': undefined, 'pg/timestamp-date@1': undefined, 'pg/timestamp-instant@1': undefined, 'pg/time-string@1': '"12:34:56"', - 'pg/timetz@1': undefined, + 'pg/timetz@1': '"12:34:56+02:00"', 'pg/bool@1': 'true', 'pg/bit@1': '"1"', 'pg/varbit@1': '"101"', diff --git a/packages/3-targets/postgres/1-target/test/codecs-class.test.ts b/packages/3-targets/postgres/1-target/test/codecs-class.test.ts index bcc4b4e1eced..f1ebdbba61cb 100644 --- a/packages/3-targets/postgres/1-target/test/codecs-class.test.ts +++ b/packages/3-targets/postgres/1-target/test/codecs-class.test.ts @@ -113,9 +113,9 @@ describe('codecs-class', () => { describe('pg/timetz@1', () => { const codec = pgTimetzDescriptor.factory({})(instanceCtx); - it('round-trips strings verbatim', async () => { - expect(await codec.toWire('10:30:00+00', callCtx)).toBe('10:30:00+00'); - expect(await codec.fromWire('10:30:00+00', callCtx)).toBe('10:30:00+00'); + it('writes its stored form and reads the text PostgreSQL prints as that form', async () => { + expect(await codec.toWire('10:30:00Z', callCtx)).toBe('10:30:00Z'); + expect(await codec.fromWire('10:30:00+00', callCtx)).toBe('10:30:00Z'); }); }); diff --git a/packages/3-targets/postgres/1-target/test/codecs.test.ts b/packages/3-targets/postgres/1-target/test/codecs.test.ts index a671c1d58ffa..2b65e363f4b7 100644 --- a/packages/3-targets/postgres/1-target/test/codecs.test.ts +++ b/packages/3-targets/postgres/1-target/test/codecs.test.ts @@ -178,8 +178,8 @@ describe('adapter-postgres codecs', () => { expect(await timetzCodec.toWire('12:34:56+02', {})).toBe('12:34:56+02'); }); - it('decodes string as-is', async () => { - expect(await timetzCodec.fromWire('23:59:59-05', {})).toBe('23:59:59-05'); + it('decodes the text PostgreSQL prints to its stored form', async () => { + expect(await timetzCodec.fromWire('23:59:59-05', {})).toBe('23:59:59-05:00'); }); }); diff --git a/packages/3-targets/postgres/1-target/test/date-codecs.test.ts b/packages/3-targets/postgres/1-target/test/date-codecs.test.ts index 4e8af097beca..0a91dce4ee0e 100644 --- a/packages/3-targets/postgres/1-target/test/date-codecs.test.ts +++ b/packages/3-targets/postgres/1-target/test/date-codecs.test.ts @@ -6,6 +6,20 @@ import { fromContractJson, toContractJson } from './contract-json'; const codec = pgTimestamptzDateDescriptor.factory({})({ name: 'test' }); describe('pg/timestamptz-date@1', () => { + it.each([ + '02.01.2026 03:04:05.123456 UTC', + '01/02/2026 03:04:05.123456 UTC', + 'Fri Jan 02 03:04:05.123456 2026 UTC', + ])( + 'refuses %s, which PostgreSQL prints only under another DateStyle, naming the setting', + async (wire) => { + await expect(codec.fromWire(wire, {})).rejects.toMatchObject({ + code: 'RUNTIME.DATE_STYLE_NOT_ISO', + meta: { codecId: 'pg/timestamptz-date@1', received: wire }, + }); + }, + ); + it.each([ ['2026-01-02 03:04:05.123456+00', '2026-01-02T03:04:05.123Z'], ['2026-01-02T03:04:05.123Z', '2026-01-02T03:04:05.123Z'], diff --git a/packages/3-targets/postgres/1-target/test/date-style-every-codec.test.ts b/packages/3-targets/postgres/1-target/test/date-style-every-codec.test.ts new file mode 100644 index 000000000000..751b933131ba --- /dev/null +++ b/packages/3-targets/postgres/1-target/test/date-style-every-codec.test.ts @@ -0,0 +1,34 @@ +import { describe, expect, it } from 'vitest'; +import { pgDate, pgTimestamp, pgTimestamptz } from '../src/core/data-types'; +import { postgresCodecDescriptorRegistry } from '../src/core/registry'; + +/** Text PostgreSQL prints for a value of each type under DateStyle German. */ +const GERMAN_TEXT: Readonly> = { + [pgDate.id]: '02.01.2026', + [pgTimestamp.id]: '02.01.2026 03:04:05.123456', + [pgTimestamptz.id]: '02.01.2026 03:04:05.123456 UTC', +}; + +const dateBearingCodecs = [...postgresCodecDescriptorRegistry.values()].filter((descriptor) => + Object.hasOwn(GERMAN_TEXT, descriptor.dataType), +); + +describe('every Postgres codec of a date, timestamp or timestamptz column', () => { + it('is found in the registry', () => { + expect(new Set(dateBearingCodecs.map(({ dataType }) => dataType))).toEqual( + new Set(Object.keys(GERMAN_TEXT)), + ); + }); + + it.each(dateBearingCodecs.map((descriptor) => ({ codecId: descriptor.codecId, descriptor })))( + '$codecId refuses text PostgreSQL prints under DateStyle German, naming the setting', + async ({ codecId, descriptor }) => { + const codec = descriptor.factory(undefined)({ name: 'date-style' }); + const wire = GERMAN_TEXT[descriptor.dataType]; + await expect(codec.fromWire(wire, {})).rejects.toMatchObject({ + code: 'RUNTIME.DATE_STYLE_NOT_ISO', + meta: { codecId, received: wire }, + }); + }, + ); +}); diff --git a/packages/3-targets/postgres/1-target/test/date-time-codecs.canonical-form.test.ts b/packages/3-targets/postgres/1-target/test/date-time-codecs.canonical-form.test.ts index b8acc6f83e4c..ca219698867d 100644 --- a/packages/3-targets/postgres/1-target/test/date-time-codecs.canonical-form.test.ts +++ b/packages/3-targets/postgres/1-target/test/date-time-codecs.canonical-form.test.ts @@ -48,7 +48,7 @@ const someCodecsHold: Readonly> = { 'pg/date': ['-infinity'], }; -/** The codecs that hold `infinity` and `-infinity`: those whose value is PostgreSQL's own text. */ +/** The codecs that hold `infinity` and `-infinity`: the text codecs. */ const holdsInfinity: ReadonlySet = new Set([ 'pg/date-string@1', 'pg/timestamp-string@1', diff --git a/packages/3-targets/postgres/1-target/test/date-time-text-wording.test.ts b/packages/3-targets/postgres/1-target/test/date-time-text-wording.test.ts new file mode 100644 index 000000000000..e9ba15f10df6 --- /dev/null +++ b/packages/3-targets/postgres/1-target/test/date-time-text-wording.test.ts @@ -0,0 +1,59 @@ +import { describe, expect, it } from 'vitest'; +import { + errorDateStyleNotIso, + errorTemporalUnavailable, + errorTemporalUnavailableForDefault, + errorTemporalUnrepresentable, + errorTemporalWrongType, +} from '../src/core/errors'; +import { postgresNativeAuthoringTypes } from '../src/core/type-constructors'; + +function wordingOf(error: { message: string; why?: string; fix?: string }): string { + return [error.message, error.why ?? '', error.fix ?? ''].join(' '); +} + +describe('the wording that sends a user to a text date or time type', () => { + it.each([ + ['the missing Temporal error', errorTemporalUnavailable('pg/date-temporal@1', 'decode')], + ['the missing Temporal error of a generator', errorTemporalUnavailableForDefault('instantNow')], + [ + 'the unrepresentable value error', + errorTemporalUnrepresentable({ + codecId: 'pg/time-temporal@1', + operation: 'decode', + stringType: 'TimeString(p)', + value: '24:00:00', + detail: 'hour 24 is out of range', + }), + ], + [ + 'the wrong value type error', + errorTemporalWrongType('pg/date-temporal@1', 'Temporal.PlainDate', 'DateString', 'a string'), + ], + ])('says %s reads ISO 8601 text', (_name, error) => { + const wording = wordingOf(error); + expect({ + iso: wording.includes('ISO 8601'), + postgresText: /PostgreSQL('s own)? text/.test(wording), + }).toEqual({ + iso: true, + postgresText: false, + }); + }); + + it('says the driver could not ensure DateStyle=ISO, names a pooler as one cause, and gives no advice to set startup options', () => { + const error = errorDateStyleNotIso('pg/date-string@1', '02.01.2026'); + expect({ + couldNotEnsure: error.message.includes('the driver could not ensure'), + pooler: (error.why ?? '').includes('pooler'), + startupOptionAdvice: (error.fix ?? '').includes('options:'), + }).toEqual({ couldNotEnsure: true, pooler: true, startupOptionAdvice: false }); + }); + + it.each(['DateString', 'TimeString', 'TimestampString', 'TimestamptzString', 'Timetz'] as const)( + 'documents %s as ISO 8601 text', + (name) => { + expect(postgresNativeAuthoringTypes[name].documentation).toContain('ISO 8601'); + }, + ); +}); diff --git a/packages/3-targets/postgres/1-target/test/descriptor-meta.test.ts b/packages/3-targets/postgres/1-target/test/descriptor-meta.test.ts index 2d88e5e0a425..c821d2ac6098 100644 --- a/packages/3-targets/postgres/1-target/test/descriptor-meta.test.ts +++ b/packages/3-targets/postgres/1-target/test/descriptor-meta.test.ts @@ -1,6 +1,7 @@ import { describe, expect, it } from 'vitest'; import { postgresRenderCheckExpressions } from '../src/core/check-expressions'; import { postgresTargetDescriptorMeta } from '../src/core/descriptor-meta'; +import postgresRuntimeTargetDescriptor from '../src/exports/runtime'; describe('postgresTargetDescriptorMeta', () => { it('declares namespace support, so emitted model names keep their namespace segment', () => { @@ -19,4 +20,11 @@ describe('postgresTargetDescriptorMeta', () => { postgresRenderCheckExpressions, ); }); + + it('declares, on the control and runtime target, that every session prints dates in DateStyle ISO, which its date and time codecs read', () => { + expect([ + postgresTargetDescriptorMeta.sessionSettings, + postgresRuntimeTargetDescriptor.sessionSettings, + ]).toEqual([{ DateStyle: 'ISO' }, { DateStyle: 'ISO' }]); + }); }); diff --git a/packages/3-targets/postgres/1-target/test/enum-eligibility.test.ts b/packages/3-targets/postgres/1-target/test/enum-eligibility.test.ts index 804fe53f9559..acddccc487eb 100644 --- a/packages/3-targets/postgres/1-target/test/enum-eligibility.test.ts +++ b/packages/3-targets/postgres/1-target/test/enum-eligibility.test.ts @@ -3,7 +3,7 @@ import { describe, expect, it } from 'vitest'; import { codecDescriptors } from '../src/core/codecs'; describe('the Postgres codecs an enum cannot use', () => { - it('are the native enum codec, the string timestamps, timetz, bytea, tsquery, json, jsonb and raw, each refused with its reason and what to use instead', () => { + it('are the native enum codec, bytea, tsquery, json, jsonb and raw, each refused with its reason and what to use instead', () => { const refusals = Object.fromEntries( codecDescriptors.flatMap((descriptor) => { const refusal = enumRefusalOf(descriptor); @@ -14,12 +14,6 @@ describe('the Postgres codecs an enum cannot use', () => { expect(refusals).toEqual({ 'pg/enum@1': 'A pg/enum codec types the column of a native enum, whose values the database enforces with an enum type of its own. Declare a native enum instead: native_enum in PSL, or nativeEnum in TypeScript.', - 'pg/timestamp-string@1': - 'A query reads each value as the text PostgreSQL prints, such as "2024-01-02 03:04:05", while the contract stores it in ISO 8601, such as "2024-01-02T03:04:05", so no value read back equals a member. Use pg/timestamp-temporal@1, whose members are Temporal values.', - 'pg/timestamptz-string@1': - 'A query reads each value as the text PostgreSQL prints in the session\'s time zone, such as "2024-01-02 03:04:05+00", while the contract stores it in ISO 8601, such as "2024-01-02T03:04:05Z", so no value read back equals a member. Use pg/timestamptz-temporal@1, whose members are Temporal values.', - 'pg/timetz@1': - 'A query reads each value as the text PostgreSQL prints, such as "12:34:56+02", while the contract stores it in ISO 8601, such as "12:34:56+02:00", so a value read back need not equal the member written. No enum can use a timetz codec; use a text enum instead.', 'pg/bytea@1': 'The contract stores a bytea value as base64 text, which PostgreSQL reads as the bytes of that text, so no CHECK can compare a value with the members. No enum can use a bytea codec; use a text enum instead.', 'pg/tsquery@1': diff --git a/packages/3-targets/postgres/1-target/test/generator-functions.test.ts b/packages/3-targets/postgres/1-target/test/generator-functions.test.ts index 3a809de01207..7d7e5105c6b6 100644 --- a/packages/3-targets/postgres/1-target/test/generator-functions.test.ts +++ b/packages/3-targets/postgres/1-target/test/generator-functions.test.ts @@ -13,8 +13,6 @@ describe('the Postgres generator functions', () => { it.each([ ['pg/timestamptz-temporal@1', 'instantNow'], ['pg/timestamp-temporal@1', 'plainDateTimeNow'], - ['pg/timestamptz-string@1', 'timestamptzStringNow'], - ['pg/timestamp-string@1', 'timestampStringNow'], ['pg/timestamptz-date@1', 'timestampNow'], ['pg/timestamp-date@1', 'timestampNow'], ['pg/timestamp-instant@1', 'instantNow'], @@ -31,6 +29,8 @@ describe('the Postgres generator functions', () => { ['pg/time-temporal@1', 'plainTimeNow'], ['pg/time-string@1', 'timeStringNow'], ['pg/timetz@1', 'timetzNow'], + ['pg/timestamptz-string@1', 'timestamptzStringNow'], + ['pg/timestamp-string@1', 'timestampStringNow'], ])("resolve now() on %s to %s with the column's precision", (codecId, generatorId) => { expect({ withPrecision: resolve('now', codecId, { precision: 3 }), diff --git a/packages/3-targets/postgres/1-target/test/now-generators.test.ts b/packages/3-targets/postgres/1-target/test/now-generators.test.ts index 114c1585ff6c..711f69184fa5 100644 --- a/packages/3-targets/postgres/1-target/test/now-generators.test.ts +++ b/packages/3-targets/postgres/1-target/test/now-generators.test.ts @@ -14,7 +14,11 @@ import { timestamptzStringNow, timetzNow, } from '../src/core/date-time-now-generators'; -import { postgresNowGeneratorIdFor, postgresNowGeneratorIds } from '../src/core/now-generators'; +import { + postgresNowGeneratorIdFor, + postgresNowGeneratorIds, + postgresNowGeneratorTakesPrecision, +} from '../src/core/now-generators'; import { postgresCodecRegistry } from '../src/core/registry'; import { fromContractJson, toContractJson } from './contract-json'; @@ -65,6 +69,18 @@ describe('the "now" generator for each codec', () => { }, ); + it.each([ + 'pg/date-string@1', + 'pg/time-string@1', + 'pg/timetz@1', + 'pg/timestamp-string@1', + 'pg/timestamptz-string@1', + ])('generates a value already in the stored form of %s', (codecId) => { + const generatorId = postgresNowGeneratorIdFor(codecId) ?? ''; + const value = generate[generatorId]?.(); + expect(toContractJson(codecFor(codecId), value)).toBe(value); + }); + it('gives every date and time codec a generator', () => { expect( DATE_AND_TIME_CODEC_IDS.filter((codecId) => postgresNowGeneratorIdFor(codecId) === undefined), @@ -117,7 +133,7 @@ describe('the text "now" generators', () => { vi.useRealTimers(); }); - it('write the text PostgreSQL prints in a UTC session', () => { + it('write the stored form of the current UTC time, which reads back as written', () => { vi.useFakeTimers({ now: new Date('2024-01-02T03:04:05.678Z') }); expect([ timestampStringNow(), @@ -126,18 +142,41 @@ describe('the text "now" generators', () => { timeStringNow(), timetzNow(), ]).toEqual([ - '2024-01-02 03:04:05.678', - '2024-01-02 03:04:05.678+00', + '2024-01-02T03:04:05.678', + '2024-01-02T03:04:05.678Z', '2024-01-02', '03:04:05.678', - '03:04:05.678+00', + '03:04:05.678Z', ]); }); it.each([ - ['2024-01-02T03:04:05.670Z', '2024-01-02 03:04:05.67'], - ['2024-01-02T03:04:05.600Z', '2024-01-02 03:04:05.6'], - ['2024-01-02T03:04:00.000Z', '2024-01-02 03:04:00'], + [0, '2024-01-02T23:59:59'], + [1, '2024-01-02T23:59:59.9'], + [2, '2024-01-02T23:59:59.99'], + [6, '2024-01-02T23:59:59.999'], + ])( + 'round the timestamp down to precision %s, so the column stores what was written', + (precision, text) => { + vi.useFakeTimers({ now: new Date('2024-01-02T23:59:59.999Z') }); + expect([timestampStringNow({ precision }), timestamptzStringNow({ precision })]).toEqual([ + text, + `${text}Z`, + ]); + }, + ); + + it('pass the column precision to the timestamp generators', () => { + expect(postgresNowGeneratorTakesPrecision).toMatchObject({ + 'pg/timestamp-string@1': true, + 'pg/timestamptz-string@1': true, + }); + }); + + it.each([ + ['2024-01-02T03:04:05.670Z', '2024-01-02T03:04:05.67'], + ['2024-01-02T03:04:05.600Z', '2024-01-02T03:04:05.6'], + ['2024-01-02T03:04:00.000Z', '2024-01-02T03:04:00'], ])('remove the trailing zeros of the fraction of %s', (now, text) => { vi.useFakeTimers({ now: new Date(now) }); expect(timestampStringNow()).toBe(text); @@ -161,7 +200,7 @@ describe('the time "now" generators', () => { const params = precision === undefined ? undefined : { precision }; expect([timeStringNow(params), timetzNow(params), plainTimeNow(params).toString()]).toEqual([ time, - `${time}+00`, + `${time}Z`, time, ]); }); diff --git a/packages/3-targets/postgres/1-target/test/postgres-year-text.test.ts b/packages/3-targets/postgres/1-target/test/postgres-year-text.test.ts index c614e231c6ce..449f32fd52bd 100644 --- a/packages/3-targets/postgres/1-target/test/postgres-year-text.test.ts +++ b/packages/3-targets/postgres/1-target/test/postgres-year-text.test.ts @@ -10,6 +10,7 @@ describe('postgresYearText', () => { ['9999-12-31', '9999-12-31'], ['+010000-01-01', '10000-01-01'], ['+275760-09-13', '275760-09-13'], + ['+5874897-12-31', '5874897-12-31'], ['-000043-03-15T12:00:00', '0044-03-15T12:00:00 BC'], ['-000043-03-15T12:00:00.000Z', '0044-03-15T12:00:00.000Z BC'], ['+010000-01-01T00:00:00Z', '10000-01-01T00:00:00Z'], diff --git a/packages/3-targets/postgres/1-target/test/temporal-codecs.test.ts b/packages/3-targets/postgres/1-target/test/temporal-codecs.test.ts index 224878645dbb..b8d0c0476edb 100644 --- a/packages/3-targets/postgres/1-target/test/temporal-codecs.test.ts +++ b/packages/3-targets/postgres/1-target/test/temporal-codecs.test.ts @@ -156,20 +156,6 @@ describe('Temporal-backed temporal codecs', () => { ['timestamp infinity', timestampCodec, 'infinity', 'TimestampString(p)'], ['timestamptz infinity', timestamptzCodec, 'infinity', 'TimestamptzString(p)'], ['timestamptz -infinity', timestamptzCodec, '-infinity', 'TimestamptzString(p)'], - ['German DateStyle date', dateCodec, '02.01.2026', 'DateString'], - [ - 'German DateStyle timestamptz', - timestamptzCodec, - '02.01.2026 03:04:05.123456 UTC', - 'TimestamptzString(p)', - ], - ['SQL DateStyle date', dateCodec, '02/01/2026', 'DateString'], - [ - 'Postgres DateStyle timestamp', - timestampCodec, - 'Fri 02 Jan 03:04:05.123456 2026', - 'TimestampString(p)', - ], [ 'hour 24, which PostgreSQL allows and Temporal does not', timeCodec, @@ -190,6 +176,20 @@ describe('Temporal-backed temporal codecs', () => { }, ); + it.each([ + ['German', '02.01.2026', dateCodec], + ['German', '02.01.2026 03:04:05.123456 UTC', timestamptzCodec], + ['SQL', '01/02/2026', dateCodec], + ['SQL', '01/02/2026 03:04:05.123456', timestampCodec], + ['Postgres', 'Fri Jan 02 03:04:05.123456 2026', timestampCodec], + ['Postgres', 'Fri Jan 02 03:04:05.123456 2026 UTC', timestamptzCodec], + ] as const)('refuses %s DateStyle text %s, naming the setting', async (_style, wire, codec) => { + await expect(codec.fromWire(wire, callCtx)).rejects.toMatchObject({ + code: 'RUNTIME.DATE_STYLE_NOT_ISO', + meta: { codecId: codec.id, received: wire }, + }); + }); + it.each([ ['date', dateCodec, 'infinity'], ['date', dateCodec, '-infinity'], diff --git a/packages/3-targets/postgres/1-target/test/temporal-string-codecs.test.ts b/packages/3-targets/postgres/1-target/test/temporal-string-codecs.test.ts index 9b5a3a52d622..310034f725fe 100644 --- a/packages/3-targets/postgres/1-target/test/temporal-string-codecs.test.ts +++ b/packages/3-targets/postgres/1-target/test/temporal-string-codecs.test.ts @@ -5,7 +5,9 @@ import { PG_TIME_STRING_CODEC_ID, PG_TIMESTAMP_STRING_CODEC_ID, PG_TIMESTAMPTZ_STRING_CODEC_ID, + PG_TIMETZ_CODEC_ID, } from '../src/core/codec-ids'; +import { pgTimetzDescriptor } from '../src/core/codecs'; import { pgDateStringDescriptor, pgTimeStringDescriptor, @@ -17,148 +19,228 @@ import { fromContractJson, toContractJson } from './contract-json'; const instanceCtx = { name: '' }; const callCtx = {}; -const UNREPRESENTABLE_VALUES = [ - 'infinity', - '-infinity', - '0044-03-15 BC', - '12026-01-02 03:04:05', - '2026-01-02 03:04:05.123456+00', - '24:00:00', - '02.01.2026', - '01/02/2026 03:04:05.123456 CET', - 'Thu Jan 02 03:04:05.123456 2026 PST', -] as const; - const OTHER_DATE_STYLES = [ '02.01.2026', '01/02/2026 03:04:05.123456 CET', 'Thu Jan 02 03:04:05.123456 2026 PST', ] as const; -const CODECS = [ +interface StringCodecCase { + readonly id: string; + readonly descriptor: + | typeof pgDateStringDescriptor + | typeof pgTimestampStringDescriptor + | typeof pgTimestamptzStringDescriptor + | typeof pgTimeStringDescriptor + | typeof pgTimetzDescriptor; + /** Text PostgreSQL prints under DateStyle ISO, and the stored form the codec reads it as. */ + readonly read: readonly (readonly [wire: string, stored: string])[]; + /** A stored value, and the text the codec sends PostgreSQL for it. */ + readonly written: readonly (readonly [stored: string, wire: string])[]; + /** Text PostgreSQL reads that is not the stored form, which `toDataTypeValue` refuses. */ + readonly notStored: readonly string[]; + /** Whether PostgreSQL prints the type under the session's DateStyle. */ + readonly printedUnderDateStyle: boolean; + /** Whether a JSON row carries the value through a text cast, rather than as the JSON PostgreSQL writes for it. */ + readonly projectsThroughText: boolean; +} + +const CODECS: readonly StringCodecCase[] = [ { id: PG_DATE_STRING_CODEC_ID, descriptor: pgDateStringDescriptor, - standardJson: [ + read: [ ['2026-01-02', '2026-01-02'], ['0044-03-15 BC', '-000043-03-15'], + ['4713-11-24 BC', '-004712-11-24'], + ['12026-01-02', '+012026-01-02'], + ['5874897-12-31', '+5874897-12-31'], + ['infinity', 'infinity'], + ['-infinity', '-infinity'], + ], + written: [ + ['2026-01-02', '2026-01-02'], + ['-000043-03-15', '0044-03-15 BC'], + ['+012026-01-02', '12026-01-02'], ['infinity', 'infinity'], ], - refusedJson: ['02.01.2026', '2026-01-02 03:04:05'], + notStored: ['0044-03-15 BC', '12026-01-02'], + printedUnderDateStyle: true, + projectsThroughText: true, }, { id: PG_TIMESTAMP_STRING_CODEC_ID, descriptor: pgTimestampStringDescriptor, - standardJson: [ + read: [ ['2026-01-02 03:04:05.123456', '2026-01-02T03:04:05.123456'], - ['12026-01-02 03:04:05', '+012026-01-02T03:04:05'], + ['2026-01-02 03:04:05', '2026-01-02T03:04:05'], + ['0044-03-15 12:00:00 BC', '-000043-03-15T12:00:00'], + ['294276-12-31 23:59:59.999999', '+294276-12-31T23:59:59.999999'], ['-infinity', '-infinity'], ], - refusedJson: ['01/02/2026 03:04:05.123456 CET', '2026-01-02 03:04:05.123456+00'], + written: [ + ['2026-01-02T03:04:05.123456', '2026-01-02T03:04:05.123456'], + ['-000043-03-15T12:00:00', '0044-03-15T12:00:00 BC'], + ['+012026-01-02T03:04:05', '12026-01-02T03:04:05'], + ], + notStored: ['2026-01-02 03:04:05.123456', '2024-01-01T12:34:56.50'], + printedUnderDateStyle: true, + projectsThroughText: true, }, { id: PG_TIMESTAMPTZ_STRING_CODEC_ID, descriptor: pgTimestamptzStringDescriptor, - standardJson: [ + read: [ ['2026-01-02 03:04:05.123456+00', '2026-01-02T03:04:05.123456Z'], - ['0044-03-15 00:00:00+00 BC', '-000043-03-15T00:00:00Z'], + ['2026-01-02 12:04:05.123456+09', '2026-01-02T03:04:05.123456Z'], + ['2024-01-01 01:00:00+01', '2024-01-01T00:00:00Z'], + ['2025-12-31 20:30:00-03:30', '2026-01-01T00:00:00Z'], + ['1800-01-01 00:17:30+00:17:30', '1800-01-01T00:00:00Z'], + ['0044-03-15 12:00:00+00 BC', '-000043-03-15T12:00:00Z'], + ['294276-12-31 23:59:59+00', '+294276-12-31T23:59:59Z'], ['infinity', 'infinity'], ], - refusedJson: ['Thu Jan 02 03:04:05.123456 2026 PST', '12026-01-02 03:04:05'], + written: [ + ['2026-01-02T03:04:05.123456Z', '2026-01-02T03:04:05.123456Z'], + ['-000043-03-15T12:00:00Z', '0044-03-15T12:00:00Z BC'], + ], + notStored: ['2026-01-02 03:04:05.123456+00', '2024-01-01T01:00:00+01:00'], + printedUnderDateStyle: true, + projectsThroughText: true, }, { id: PG_TIME_STRING_CODEC_ID, descriptor: pgTimeStringDescriptor, - standardJson: [['03:04:05.123000', '03:04:05.123']], - refusedJson: ['24:00:00', 'infinity'], + read: [ + ['03:04:05.123456', '03:04:05.123456'], + ['00:00:00', '00:00:00'], + ['24:00:00', '24:00:00'], + ], + written: [['03:04:05.5', '03:04:05.5']], + notStored: ['12:34', '03:04:05.50'], + printedUnderDateStyle: false, + projectsThroughText: true, }, -] as const; - -async function withoutTemporalGlobal(body: () => Promise): Promise { - const had = Object.hasOwn(globalThis, 'Temporal'); - const original = Reflect.get(globalThis, 'Temporal'); - Reflect.deleteProperty(globalThis, 'Temporal'); - try { - return await body(); - } finally { - if (had) { - Reflect.set(globalThis, 'Temporal', original); - } - } -} - -describe('representation-explicit temporal string codecs', () => { - for (const { id, descriptor, standardJson, refusedJson } of CODECS) { + { + id: PG_TIMETZ_CODEC_ID, + descriptor: pgTimetzDescriptor, + read: [ + ['12:34:56+02', '12:34:56+02:00'], + ['03:04:05+00', '03:04:05Z'], + ['12:00:00+01:30:15', '12:00:00+01:30:15'], + ['12:00:00-15:59', '12:00:00-15:59'], + ['12:34:56.123456+05:45', '12:34:56.123456+05:45'], + ['24:00:00+00', '24:00:00Z'], + ['24:00:00-02', '24:00:00-02:00'], + ], + written: [['12:34:56+02:00', '12:34:56+02:00']], + notStored: ['12:34:56+02', '03:04:05+00'], + printedUnderDateStyle: false, + projectsThroughText: false, + }, +]; + +describe('the string date and time codecs', () => { + for (const { + id, + descriptor, + read, + written, + notStored, + printedUnderDateStyle, + projectsThroughText, + } of CODECS) { describe(id, () => { const codec = descriptor.factory({})(instanceCtx); - it.each(UNREPRESENTABLE_VALUES)('forwards %s unchanged on the wire', async (value) => { - expect({ - encoded: await codec.toWire(value, callCtx), - decoded: await codec.fromWire(value, callCtx), - }).toEqual({ encoded: value, decoded: value }); - }); + it.each(read.map(([wire, stored]) => ({ wire, stored })))( + 'reads the text PostgreSQL prints, $wire, as its stored form $stored', + async ({ wire, stored }) => { + expect(await codec.fromWire(wire, callCtx)).toBe(stored); + }, + ); - it.each(OTHER_DATE_STYLES)( - 'refuses to read %s from JSON, which only a session in another DateStyle writes', - (value) => { - expect(() => fromContractJson(codec, value)).toThrow( - expect.objectContaining({ code: 'RUNTIME.DECODE_FAILED' }), - ); + it.each(written.map(([stored, wire]) => ({ stored, wire })))( + 'writes $stored as $wire, which PostgreSQL reads', + async ({ stored, wire }) => { + expect(await codec.toWire(stored, callCtx)).toBe(wire); }, ); - it.each(standardJson.map(([value, json]) => ({ value, json })))( - 'writes $value to JSON in canonical form $json', - ({ value, json }) => { - expect(toContractJson(codec, value)).toBe(json); + it.each(written.map(([stored]) => stored))( + 'stores %s in the contract as it is and reads it back unchanged', + (stored) => { + const json = toContractJson(codec, stored); + expect({ json, read: fromContractJson(codec, json) }).toEqual({ + json: stored, + read: stored, + }); }, ); - it.each(refusedJson)('refuses to write %s to JSON, as its data type does', (value) => { + it.each(notStored)('refuses to store %s, which is not its stored form', (value) => { expect(() => toContractJson(codec, value)).toThrow( expect.objectContaining({ code: 'RUNTIME.DECODE_FAILED' }), ); }); - it('projects to JSON through a text cast, so a nested read matches a flat one', () => { + if (printedUnderDateStyle) { + it.each(OTHER_DATE_STYLES)( + 'refuses %s, which PostgreSQL prints only under another DateStyle, naming the setting', + async (wire) => { + await expect(codec.fromWire(wire, callCtx)).rejects.toMatchObject({ + code: 'RUNTIME.DATE_STYLE_NOT_ISO', + message: expect.stringContaining('the driver could not ensure'), + fix: expect.stringContaining('DateStyle'), + meta: { codecId: id, received: wire }, + }); + }, + ); + } + + it.each(OTHER_DATE_STYLES)( + 'refuses to read %s from JSON, which only a session in another DateStyle writes', + (value) => { + expect(() => fromContractJson(codec, value)).toThrow( + expect.objectContaining({ code: 'RUNTIME.DECODE_FAILED' }), + ); + }, + ); + + it('projects to JSON as text or as the JSON PostgreSQL writes, both of which fromWire reads', () => { const expression = ColumnRef.of('moments', 'value'); expect(descriptor.projectJson(expression, { codecId: id })).toEqual( - CastExpr.as(expression, 'text'), + projectsThroughText ? CastExpr.as(expression, 'text') : expression, ); }); }); } - it('encodes and decodes with no global Temporal available', async () => { - const standIn = { note: 'stands in for a host Temporal implementation' }; - const hadHostTemporal = Object.hasOwn(globalThis, 'Temporal'); - const hostTemporal = Reflect.get(globalThis, 'Temporal'); - Reflect.set(globalThis, 'Temporal', standIn); - const seenDuring: unknown[] = []; + it('writes a date it reads with a seven-digit year, which is beyond the stored range, back as PostgreSQL reads it', async () => { + const codec = pgDateStringDescriptor.factory()(instanceCtx); + const read = await codec.fromWire('5874897-12-31', callCtx); - try { - const results = await withoutTemporalGlobal(async () => { - const decoded: string[] = []; - for (const { descriptor } of CODECS) { - const codec = descriptor.factory({})(instanceCtx); - const encoded = await codec.toWire('infinity', callCtx); - seenDuring.push(Reflect.get(globalThis, 'Temporal')); - decoded.push(await codec.fromWire(encoded, callCtx)); - } - return decoded; - }); + expect({ read, written: await codec.toWire(read, callCtx) }).toEqual({ + read: '+5874897-12-31', + written: '5874897-12-31', + }); + }); - expect(seenDuring).toEqual([undefined, undefined, undefined, undefined]); - expect(results).toEqual(['infinity', 'infinity', 'infinity', 'infinity']); - expect(Reflect.get(globalThis, 'Temporal')).toBe(standIn); - } finally { - if (hadHostTemporal) { - Reflect.set(globalThis, 'Temporal', hostTemporal); - } else { - Reflect.deleteProperty(globalThis, 'Temporal'); + it('reads and writes with no global Temporal available', async () => { + const had = Object.hasOwn(globalThis, 'Temporal'); + const original = Reflect.get(globalThis, 'Temporal'); + Reflect.deleteProperty(globalThis, 'Temporal'); + try { + const results: string[] = []; + for (const { descriptor, read } of CODECS) { + const codec = descriptor.factory({})(instanceCtx); + const [wire = ''] = read[1] ?? []; + results.push(await codec.fromWire(await codec.toWire(wire, callCtx), callCtx)); } + expect(results).toEqual(CODECS.map(({ read }) => read[1]?.[1])); + } finally { + if (had) Reflect.set(globalThis, 'Temporal', original); } }); diff --git a/packages/3-targets/postgres/1-target/test/text-timestamp-codec-encode.test.ts b/packages/3-targets/postgres/1-target/test/text-timestamp-codec-encode.test.ts index 20d0e60d9c6d..3ccf171181e8 100644 --- a/packages/3-targets/postgres/1-target/test/text-timestamp-codec-encode.test.ts +++ b/packages/3-targets/postgres/1-target/test/text-timestamp-codec-encode.test.ts @@ -7,8 +7,8 @@ import { const wrongTyped = (value: unknown): never => value as never; describe.each([ - { descriptor: pgTimestampStringDescriptor, text: '2024-01-02 03:04:05.678' }, - { descriptor: pgTimestamptzStringDescriptor, text: '2024-01-02 03:04:05.678+00' }, + { descriptor: pgTimestampStringDescriptor, text: '2024-01-02T03:04:05.678' }, + { descriptor: pgTimestamptzStringDescriptor, text: '2024-01-02T03:04:05.678Z' }, ])('$descriptor.codecId encode', ({ descriptor, text }) => { const codec = descriptor.factory({})({ name: '' }); const codecId = descriptor.codecId; @@ -24,7 +24,7 @@ describe.each([ ])('refuses %s', async (received, value) => { await expect(codec.toWire(wrongTyped(value), {})).rejects.toMatchObject({ code: 'RUNTIME.ENCODE_FAILED', - message: `Codec '${codecId}' writes PostgreSQL's text for the value, but received ${received}.`, + message: `Codec '${codecId}' writes ISO 8601 text, but received ${received}.`, meta: { codecId, received }, }); }); diff --git a/packages/3-targets/postgres/2-adapter/src/exports/column-types.ts b/packages/3-targets/postgres/2-adapter/src/exports/column-types.ts index 16ecfdafa58e..afa4676c0aed 100644 --- a/packages/3-targets/postgres/2-adapter/src/exports/column-types.ts +++ b/packages/3-targets/postgres/2-adapter/src/exports/column-types.ts @@ -132,7 +132,7 @@ export function numericColumn( /** * Representation-explicit temporal descriptors share storage types but select different application - * values: `Temporal.*`, PostgreSQL text, or a JavaScript `Date` for `timestamptz`. + * values: `Temporal.*`, ISO 8601 text, or a JavaScript `Date` for `timestamptz`. */ export const dateTemporalColumn = { codecId: PG_DATE_TEMPORAL_CODEC_ID, diff --git a/packages/3-targets/postgres/2-adapter/test/temporal-codec-roundtrip.integration.test.ts b/packages/3-targets/postgres/2-adapter/test/temporal-codec-roundtrip.integration.test.ts index d7d0963458a9..73f5cb24fa02 100644 --- a/packages/3-targets/postgres/2-adapter/test/temporal-codec-roundtrip.integration.test.ts +++ b/packages/3-targets/postgres/2-adapter/test/temporal-codec-roundtrip.integration.test.ts @@ -303,14 +303,14 @@ describe('Temporal codecs round-trip through PostgreSQL', () => { }); }); - it('rejects a non-ISO DateStyle rendering rather than guessing at it', { + it('refuses a non-ISO DateStyle rendering, naming the setting, rather than guessing at it', { timeout: timeouts.spinUpPpgDev, }, async () => { await session!.execute({ sql: "SET DateStyle TO 'German, DMY'" }); try { await expect(readRaw(1)).rejects.toMatchObject({ - code: 'RUNTIME.DECODE_FAILED', - meta: { value: '02.01.2026', stringType: 'DateString' }, + code: 'RUNTIME.DATE_STYLE_NOT_ISO', + meta: { codecId: 'pg/date-temporal@1', received: '02.01.2026' }, }); } finally { await session!.execute({ sql: "SET DateStyle TO 'ISO, MDY'" }); diff --git a/packages/3-targets/postgres/2-adapter/test/temporal-string-codec-roundtrip.integration.test.ts b/packages/3-targets/postgres/2-adapter/test/temporal-string-codec-roundtrip.integration.test.ts index fc82d7b88962..724b18f99b5a 100644 --- a/packages/3-targets/postgres/2-adapter/test/temporal-string-codec-roundtrip.integration.test.ts +++ b/packages/3-targets/postgres/2-adapter/test/temporal-string-codec-roundtrip.integration.test.ts @@ -136,7 +136,7 @@ function buildSelectByIdAst(id: number): SelectAst { ); } -describe('temporal string codecs round-trip PostgreSQL text', () => { +describe('the string date and time codecs read ISO 8601 from PostgreSQL', () => { let database: Awaited> | undefined; let runtime: Runtime | undefined; let session: ReturnType | undefined; @@ -208,21 +208,21 @@ describe('temporal string codecs round-trip PostgreSQL text', () => { expect('Temporal' in globalThis).toBe(false); }); - it('preserves microsecond precision that a millisecond-resolution value would truncate', { + it('reads back microsecond precision that a millisecond-resolution value would truncate, in ISO 8601', { timeout: timeouts.spinUpPpgDev, }, async () => { await insert({ id: 1, d: '2026-01-02', - ts: '2026-01-02 03:04:05.123456', - tstz: '2026-01-02 03:04:05.123456+00', + ts: '2026-01-02T03:04:05.123456', + tstz: '2026-01-02T03:04:05.123456Z', t: '03:04:05.123456', }); expect(await read(1)).toEqual({ d: '2026-01-02', - ts: '2026-01-02 03:04:05.123456', - tstz: '2026-01-02 03:04:05.123456+00', + ts: '2026-01-02T03:04:05.123456', + tstz: '2026-01-02T03:04:05.123456Z', t: '03:04:05.123456', }); }); @@ -242,59 +242,50 @@ describe('temporal string codecs round-trip PostgreSQL text', () => { }); }); - it('round-trips a BC date and an expanded year, which fall outside the representable range', { + it('round-trips a year before Christ and an expanded year in their ISO 8601 form', { timeout: timeouts.spinUpPpgDev, }, async () => { - await insert({ + const beforeChrist = { id: 4, - d: '0044-03-15 BC', - ts: '0044-03-15 12:00:00 BC', - tstz: '0044-03-15 12:00:00+00 BC', + d: '-000043-03-15', + ts: '-000043-03-15T12:00:00', + tstz: '-000043-03-15T12:00:00Z', t: null, - }); - await insert({ + }; + const expanded = { id: 5, - d: '12026-01-02', - ts: '12026-01-02 03:04:05', - tstz: '12026-01-02 03:04:05+00', + d: '+012026-01-02', + ts: '+012026-01-02T03:04:05', + tstz: '+012026-01-02T03:04:05Z', t: null, - }); + }; + await insert(beforeChrist); + await insert(expanded); - expect(await read(4)).toEqual({ - d: '0044-03-15 BC', - ts: '0044-03-15 12:00:00 BC', - tstz: '0044-03-15 12:00:00+00 BC', - t: null, - }); - expect(await read(5)).toEqual({ - d: '12026-01-02', - ts: '12026-01-02 03:04:05', - tstz: '12026-01-02 03:04:05+00', - t: null, - }); + const { id: _4, ...beforeChristRow } = beforeChrist; + const { id: _5, ...expandedRow } = expanded; + expect([await read(4), await read(5)]).toEqual([beforeChristRow, expandedRow]); }); - it('surfaces the session TimeZone in the rendering instead of pinning the read to UTC', { + it('reads a timestamptz as the same UTC instant whatever the session TimeZone', { timeout: timeouts.spinUpPpgDev, }, async () => { await session!.execute({ sql: "SET TimeZone TO 'Asia/Tokyo'" }); try { - expect((await read(1)).tstz).toBe('2026-01-02 12:04:05.123456+09'); + expect((await read(1)).tstz).toBe('2026-01-02T03:04:05.123456Z'); } finally { await session!.execute({ sql: "SET TimeZone TO 'UTC'" }); } }); - it('surfaces a non-default DateStyle in the rendering instead of normalising it away', { + it('refuses to read a date the session prints under another DateStyle, naming the setting', { timeout: timeouts.spinUpPpgDev, }, async () => { await session!.execute({ sql: "SET DateStyle TO 'German, DMY'" }); try { - expect(await read(1)).toEqual({ - d: '02.01.2026', - ts: '02.01.2026 03:04:05.123456', - tstz: '02.01.2026 03:04:05.123456 UTC', - t: '03:04:05.123456', + await expect(read(1)).rejects.toMatchObject({ + code: 'RUNTIME.DATE_STYLE_NOT_ISO', + meta: { codecId: 'pg/date-string@1', received: '02.01.2026' }, }); } finally { await session!.execute({ sql: "SET DateStyle TO 'ISO, MDY'" }); diff --git a/packages/3-targets/postgres/3-driver/README.md b/packages/3-targets/postgres/3-driver/README.md index 036fa5c2bb8e..97a37fb66e3c 100644 --- a/packages/3-targets/postgres/3-driver/README.md +++ b/packages/3-targets/postgres/3-driver/README.md @@ -115,12 +115,15 @@ Binding variants: - `{ kind: 'pgPool', pool }`: Use an existing pg Pool - `{ kind: 'pgClient', client }`: Use an existing pg Client (direct connection) +The driver ensures the session settings the stack's target declares on every connection it uses; the PostgreSQL target declares `DateStyle=ISO`, which every date and time codec that reads a date or timestamp needs. A pool or client made by `createPgPool` or `createPgClient`, as a `url` binding's pool is, starts each session with the settings as startup options, after any `options` the URL or `PGOPTIONS` gives, and reads its connection string, `PGOPTIONS` and certificate files when it opens each connection, as pg does. On any other pool or client, the driver sets the settings with one statement on each connection before first use, and again after a connection from `acquireConnection()` is released. + With `cursor: { disabled: true }` (or buffered cursor fallback), driver-level pool queries return their connection after fetching, before yielding buffered rows for decoding or consumption. Cursor streams retain the connection until completion or iterator cleanup. Queries on caller-owned connections and transactions never release the caller's lease. ## Exports - `./runtime`: Runtime entry point for driver implementation - Default: `postgresRuntimeDriverDescriptor` — use `create()` for unbound driver, then `connect(binding)` + - `createPgPool(config, sessionSettings)` and `createPgClient(config, sessionSettings)`: a pg pool or client whose sessions start with the session settings - Types: `PostgresBinding`, `PostgresDriverCreateOptions`, `QueryResult` - `./control`: Control plane entry point for driver descriptors - Default export: `DriverDescriptor` for use in `prisma.config.ts` diff --git a/packages/3-targets/postgres/3-driver/package.json b/packages/3-targets/postgres/3-driver/package.json index 96c89a4cf3aa..c6467b6b13d7 100644 --- a/packages/3-targets/postgres/3-driver/package.json +++ b/packages/3-targets/postgres/3-driver/package.json @@ -27,6 +27,7 @@ "@types/pg": "catalog:", "arktype": "^2.2.2", "pg": "catalog:", + "pg-connection-string": "^2.14.0", "pg-cursor": "^2.21.0" }, "devDependencies": { diff --git a/packages/3-targets/postgres/3-driver/src/exports/control.ts b/packages/3-targets/postgres/3-driver/src/exports/control.ts index 454451042125..f390f83e0ffd 100644 --- a/packages/3-targets/postgres/3-driver/src/exports/control.ts +++ b/packages/3-targets/postgres/3-driver/src/exports/control.ts @@ -9,10 +9,11 @@ import { blindCast } from '@internal/utils/casts'; import { ifDefined } from '@internal/utils/defined'; import { redactDatabaseUrl } from '@internal/utils/redact-db-url'; import { suppressIdleConnectionErrors } from '@internal/utils/suppress-idle-connection-errors'; -import { Client } from 'pg'; +import type { Client } from 'pg'; import { postgresDriverDescriptorMeta } from '../core/descriptor-meta'; import { normalizePgError } from '../normalize-error'; import { controlTextTypes } from '../server-text-types'; +import { createPgClient } from '../session-settings'; export class PostgresControlDriver implements SqlControlDriverInstance<'postgres'> { readonly familyId = 'sql' as const; @@ -62,10 +63,13 @@ const postgresDriverDescriptor: ControlDriverDescriptor<'sql', 'postgres', Postg options?: ControlDriverConnectOptions, ): Promise { const client = suppressIdleConnectionErrors( - new Client({ - connectionString: url, - ...ifDefined('connectionTimeoutMillis', options?.connectTimeoutMs), - }), + createPgClient( + { + connectionString: url, + ...ifDefined('connectionTimeoutMillis', options?.connectTimeoutMs), + }, + options?.sessionSettings ?? {}, + ), ); try { await client.connect(); diff --git a/packages/3-targets/postgres/3-driver/src/exports/runtime.ts b/packages/3-targets/postgres/3-driver/src/exports/runtime.ts index b947d99b213f..2a5e89c46eaa 100644 --- a/packages/3-targets/postgres/3-driver/src/exports/runtime.ts +++ b/packages/3-targets/postgres/3-driver/src/exports/runtime.ts @@ -1,3 +1,4 @@ +import type { SessionSettings } from '@internal/framework-components/components'; import type { RuntimeDriverDescriptor, RuntimeDriverInstance, @@ -9,6 +10,7 @@ import type { SqlExplainResult, SqlStatementStats, } from '@internal/sql-relational-core/ast'; +import { ifDefined } from '@internal/utils/defined'; import { postgresDriverDescriptorMeta } from '../core/descriptor-meta'; import { driverError } from '../driver-error'; import { @@ -56,10 +58,12 @@ class PostgresUnboundDriverImpl implements PostgresRuntimeDriver { #disconnectedState: DisconnectedState = 'unbound'; #cursorOpts: PostgresDriverCreateOptions['cursor']; #preparedStatements: PostgresDriverCreateOptions['preparedStatements']; + #sessionSettings: PostgresDriverCreateOptions['sessionSettings']; constructor(options?: PostgresDriverCreateOptions) { this.#cursorOpts = options?.cursor; this.#preparedStatements = options?.preparedStatements; + this.#sessionSettings = options?.sessionSettings; } get state(): 'unbound' | 'connected' | 'closed' { @@ -89,6 +93,7 @@ class PostgresUnboundDriverImpl implements PostgresRuntimeDriver { } this.#delegate = createBoundDriverFromBinding(binding, this.#cursorOpts, { preparedStatements: this.#preparedStatements, + sessionSettings: this.#sessionSettings, }); this.#disconnectedState = 'unbound'; } @@ -174,8 +179,14 @@ const postgresRuntimeDriverDescriptor: RuntimeDriverDescriptor< PostgresRuntimeDriver > = { ...postgresDriverDescriptorMeta, - create(options?: PostgresDriverCreateOptions): PostgresRuntimeDriver { - return new PostgresUnboundDriverImpl(options); + create( + options?: PostgresDriverCreateOptions, + sessionSettings?: SessionSettings, + ): PostgresRuntimeDriver { + return new PostgresUnboundDriverImpl({ + ...options, + ...ifDefined('sessionSettings', sessionSettings), + }); }, }; @@ -186,3 +197,4 @@ export type { PostgresDriverCreateOptions, QueryResult, } from '../postgres-driver'; +export { createPgClient, createPgPool } from '../session-settings'; diff --git a/packages/3-targets/postgres/3-driver/src/postgres-driver.ts b/packages/3-targets/postgres/3-driver/src/postgres-driver.ts index a1e25130e1d7..712815e3c260 100644 --- a/packages/3-targets/postgres/3-driver/src/postgres-driver.ts +++ b/packages/3-targets/postgres/3-driver/src/postgres-driver.ts @@ -1,3 +1,4 @@ +import type { SessionSettings } from '@internal/framework-components/components'; import type { PreparedStatementHandle, SqlConnection, @@ -20,13 +21,13 @@ import type { QueryConfig, QueryResultRow, } from 'pg'; -import { Pool } from 'pg'; import Cursor from 'pg-cursor'; import { callbackToPromise } from './callback-to-promise'; import { type DriverRuntimeError, driverError } from './driver-error'; import { NamedCursor } from './named-cursor'; import { isAlreadyConnectedError, isPostgresError, normalizePgError } from './normalize-error'; import { serverTextTypes } from './server-text-types'; +import { createPgPool, SessionSettingsGuard } from './session-settings'; export type QueryResult = PgQueryResult; @@ -63,6 +64,8 @@ interface PostgresDriverOptions { * transaction mode). */ readonly preparedStatements?: boolean | undefined; + /** The session settings the stack's target declares, which the driver ensures on every connection it uses. */ + readonly sessionSettings?: SessionSettings | undefined; } export type PostgresDriverCreateOptions = Omit; @@ -631,11 +634,13 @@ class PostgresPoolDriverImpl implements SqlDriver { private readonly pool: PoolType; + readonly #sessionSettings: SessionSettingsGuard; #closed = false; constructor(options: PostgresDriverOptions & { connect: { pool: PoolType } }) { super(buildConnectionOptions(options)); this.pool = options.connect.pool; + this.#sessionSettings = new SessionSettingsGuard(options.sessionSettings ?? {}, this.pool); } get state(): SqlDriverState { @@ -648,7 +653,9 @@ class PostgresPoolDriverImpl async acquireConnection(): Promise { const client = await this.acquireClient(); - return new PostgresConnectionImpl(client, this.options); + return new PostgresConnectionImpl(client, this.options, () => + this.#sessionSettings.forget(client), + ); } async close(): Promise { @@ -664,7 +671,14 @@ class PostgresPoolDriverImpl // out, so a connection error during a held connection, transaction, or // cursor emits on the client itself; without a listener that kills the // process. - return suppressIdleConnectionErrors(await this.pool.connect()); + const client = suppressIdleConnectionErrors(await this.pool.connect()); + try { + await ensureSessionSettings(this.#sessionSettings, client); + } catch (error) { + client.release(true); + throw normalizePgError(error); + } + return client; } async releaseClient(client: PoolClient): Promise { @@ -677,6 +691,7 @@ class PostgresDirectDriverImpl implements SqlDriver { private readonly directClient: Client; + readonly #sessionSettings: SessionSettingsGuard; readonly #connectionMutex = new AsyncMutex(); #closed = false; #connected = false; @@ -689,6 +704,10 @@ class PostgresDirectDriverImpl constructor(options: PostgresDriverOptions & { connect: { client: Client } }) { super(buildConnectionOptions(options)); this.directClient = options.connect.client; + this.#sessionSettings = new SessionSettingsGuard( + options.sessionSettings ?? {}, + this.directClient, + ); // A pg.Client cannot be reused after its connection dies, so mark the // driver closed and let acquireClient reject with a clear error instead // of handing out the dead client. @@ -717,7 +736,10 @@ class PostgresDirectDriverImpl return new PostgresConnectionImpl( client, this.options, - releaseLease, + () => { + this.#sessionSettings.forget(client); + releaseLease(); + }, // A direct driver has a single underlying socket, so a destroyed // connection means the driver itself is no longer usable. Tear down // the driver while still holding the lease so that any @@ -774,12 +796,17 @@ class PostgresDirectDriverImpl 'Postgres connection lost or closed. Create a new client to reconnect.', ); } - if (this.#connected) { - return this.directClient; - } + if (!this.#connected) await this.#connectDirectClient(); + await ensureSessionSettings(this.#sessionSettings, this.directClient).catch( + rethrowNormalizedError, + ); + return this.directClient; + } + + async #connectDirectClient(): Promise { if (this.#connectPromise !== undefined) { await this.#connectPromise; - return this.directClient; + return; } this.#connectPromise = (async () => { @@ -795,8 +822,6 @@ class PostgresDirectDriverImpl this.#connected = true; })(); await this.#connectPromise; - - return this.directClient; } async releaseClient(_client: Client): Promise {} @@ -805,22 +830,27 @@ class PostgresDirectDriverImpl export function createBoundDriverFromBinding( binding: PostgresBinding, cursorOpts: PostgresDriverCreateOptions['cursor'], - extraOpts?: Pick, + extraOpts?: Pick, ): SqlDriver { const preparedStatements = extraOpts?.preparedStatements; + const sessionSettings = extraOpts?.sessionSettings; switch (binding.kind) { case 'url': { const pool = suppressIdleConnectionErrors( - new Pool({ - connectionString: binding.url, - connectionTimeoutMillis: 20_000, - idleTimeoutMillis: 30_000, - }), + createPgPool( + { + connectionString: binding.url, + connectionTimeoutMillis: 20_000, + idleTimeoutMillis: 30_000, + }, + sessionSettings ?? {}, + ), ); return new PostgresPoolDriverImpl({ connect: { pool }, cursor: cursorOpts, preparedStatements, + sessionSettings, }); } case 'pgPool': @@ -828,16 +858,32 @@ export function createBoundDriverFromBinding( connect: { pool: suppressIdleConnectionErrors(binding.pool) }, cursor: cursorOpts, preparedStatements, + sessionSettings, }); case 'pgClient': return new PostgresDirectDriverImpl({ connect: { client: suppressIdleConnectionErrors(binding.client) }, cursor: cursorOpts, preparedStatements, + sessionSettings, }); } } +/** Ensures the session settings on a client the driver is about to use, holding the client's query lock while it does. */ +async function ensureSessionSettings( + guard: SessionSettingsGuard, + client: ClientBase, +): Promise { + if (!guard.needs(client)) return; + const releaseLock = await acquireClientQueryLock(client); + try { + await guard.ensure(client); + } finally { + releaseLock(); + } +} + function readCursor(cursor: Cursor, size: number): Promise { return callbackToPromise((cb) => { cursor.read(size, (err, rows) => cb(err, rows)); diff --git a/packages/3-targets/postgres/3-driver/src/session-settings.ts b/packages/3-targets/postgres/3-driver/src/session-settings.ts new file mode 100644 index 000000000000..3b0059ebb942 --- /dev/null +++ b/packages/3-targets/postgres/3-driver/src/session-settings.ts @@ -0,0 +1,119 @@ +import type { SessionSettings } from '@internal/framework-components/components'; +import { Client, type ClientBase, type ClientConfig, Pool, type PoolConfig } from 'pg'; +import { parse } from 'pg-connection-string'; + +/** The settings pg's `ConnectionParameters` reads from a parsed connection string, except `binary`, which a pg client also applies to every query when it is in the client's own settings. Any other query parameter is not a connection setting, and pg ignores it in a connection string. */ +const CONNECTION_SETTINGS = [ + 'user', + 'password', + 'host', + 'port', + 'database', + 'options', + 'ssl', + 'sslnegotiation', + 'client_encoding', + 'replication', + 'application_name', + 'fallback_application_name', + 'statement_timeout', + 'lock_timeout', + 'idle_in_transaction_session_timeout', + 'query_timeout', +] as const; + +function startupOptions(sessionSettings: SessionSettings): string { + return Object.entries(sessionSettings) + .map(([name, value]) => `-c ${name}=${value.replace(/[\\ ]/g, '\\$&')}`) + .join(' '); +} + +/** + * The pg settings of a connection that starts with `sessionSettings`: the connection settings `config` gives, its connection string read as pg reads it, and each session setting as a startup option after the options the connection string gives, or else after `PGOPTIONS`, which is where pg reads them from. A startup option sets the session's default, so `RESET ALL` keeps it. + */ +export function pgConnectionConfig( + config: ClientConfig, + sessionSettings: SessionSettings, +): ClientConfig { + const { connectionString, ...rest } = config; + const merged: ClientConfig = { ...rest }; + if (connectionString !== undefined) { + const parsed = parse(connectionString); + for (const setting of CONNECTION_SETTINGS) { + const value: unknown = Reflect.get(parsed, setting); + if (value !== undefined) Object.assign(merged, { [setting]: value }); + } + } + const startup = startupOptions(sessionSettings); + if (startup === '') return merged; + const given = merged.options || process.env['PGOPTIONS']; + merged.options = given ? `${given} ${startup}` : startup; + return merged; +} + +/** The pools and clients made by `createPgPool` and `createPgClient`, with the settings their sessions start with. */ +const startupSettingsOf = new WeakMap(); + +/** A pg client class whose every client starts its session with `sessionSettings`. It reads its connection string, and `PGOPTIONS`, when each client is made, as pg does. */ +function clientClassStartingWith(sessionSettings: SessionSettings): typeof Client { + return class SessionSettingsClient extends Client { + constructor(config?: ClientConfig) { + super(pgConnectionConfig({ ...config }, sessionSettings)); + } + }; +} + +/** A pg pool whose connections start their sessions with `sessionSettings`. */ +export function createPgPool(config: PoolConfig, sessionSettings: SessionSettings): Pool { + const pool = new Pool({ ...config, Client: clientClassStartingWith(sessionSettings) }); + startupSettingsOf.set(pool, sessionSettings); + return pool; +} + +/** A pg client whose session starts with `sessionSettings`. */ +export function createPgClient(config: ClientConfig, sessionSettings: SessionSettings): Client { + const SessionSettingsClient = clientClassStartingWith(sessionSettings); + const client = new SessionSettingsClient(config); + startupSettingsOf.set(client, sessionSettings); + return client; +} + +function startsWith(poolOrClient: object, sessionSettings: SessionSettings): boolean { + const atStartup = startupSettingsOf.get(poolOrClient); + return ( + atStartup !== undefined && + Object.entries(sessionSettings).every(([name, value]) => atStartup[name] === value) + ); +} + +/** + * Ensures the session settings a target declares on each pg client a driver uses from `poolOrClient`. A pool or client made by `createPgPool` or `createPgClient` with the settings needs nothing, because each session starts with them and `RESET ALL` keeps a startup setting. On any other, each client is set once, and again after it was handed out for exclusive use, whose holder may have changed or reset its session. + */ +export class SessionSettingsGuard { + readonly #entries: readonly (readonly [string, string])[]; + readonly #ensured = new WeakSet(); + + constructor(sessionSettings: SessionSettings, poolOrClient: object) { + this.#entries = startsWith(poolOrClient, sessionSettings) + ? [] + : Object.entries(sessionSettings); + } + + needs(client: ClientBase): boolean { + return this.#entries.length > 0 && !this.#ensured.has(client); + } + + /** Sets the settings on a client that {@link SessionSettingsGuard.needs} them. */ + async ensure(client: ClientBase): Promise { + const calls = this.#entries.map( + (_, index) => `set_config($${index * 2 + 1}, $${index * 2 + 2}, false)`, + ); + await client.query(`SELECT ${calls.join(', ')}`, this.#entries.flat()); + this.#ensured.add(client); + } + + /** Marks a client returned from exclusive use, so its settings are ensured again before the driver next uses it. */ + forget(client: ClientBase): void { + this.#ensured.delete(client); + } +} diff --git a/packages/3-targets/postgres/3-driver/test/control.test.ts b/packages/3-targets/postgres/3-driver/test/control.test.ts index 14dde24eca0c..b8c52a081151 100644 --- a/packages/3-targets/postgres/3-driver/test/control.test.ts +++ b/packages/3-targets/postgres/3-driver/test/control.test.ts @@ -32,7 +32,7 @@ describe('@internal/driver-postgres control', () => { vi.doUnmock('pg'); }); - it('passes a connect timeout to the client only when asked for one', async () => { + it('passes a connect timeout to the client only when asked for one, and the session settings as startup options', async () => { const configs: unknown[] = []; const mockClient = { connect: vi.fn(async () => {}), end: vi.fn(async () => {}), on: vi.fn() }; vi.doMock('pg', () => ({ @@ -44,12 +44,16 @@ describe('@internal/driver-postgres control', () => { vi.resetModules(); const { default: descriptor } = await import('../src/exports/control'); - await descriptor.create('postgres://localhost/test', { connectTimeoutMs: 3000 }); + await descriptor.create('postgres://localhost/test', { + connectTimeoutMs: 3000, + sessionSettings: { DateStyle: 'ISO' }, + }); await descriptor.create('postgres://localhost/test'); + const connection = { host: 'localhost', database: 'test', user: '', password: '', port: '' }; expect(configs).toEqual([ - { connectionString: 'postgres://localhost/test', connectionTimeoutMillis: 3000 }, - { connectionString: 'postgres://localhost/test' }, + { ...connection, connectionTimeoutMillis: 3000, options: '-c DateStyle=ISO' }, + connection, ]); vi.doUnmock('pg'); }); diff --git a/packages/3-targets/postgres/3-driver/test/driver.session-settings.integration.test.ts b/packages/3-targets/postgres/3-driver/test/driver.session-settings.integration.test.ts new file mode 100644 index 000000000000..4efb896a5cc0 --- /dev/null +++ b/packages/3-targets/postgres/3-driver/test/driver.session-settings.integration.test.ts @@ -0,0 +1,101 @@ +import { createDevDatabase, timeouts } from '@repo/test-utils'; +import pg from 'pg'; +import { afterEach, describe, expect, it } from 'vitest'; +import { createBoundDriverFromBinding } from '../src/postgres-driver'; +import { queryRows } from './sql-queryable-test-utils'; + +const SESSION_SETTINGS = { DateStyle: 'ISO' } as const; +const GERMAN = "SET DateStyle TO 'German, DMY'"; + +async function dateStyleOf( + queryable: Parameters[0], +): Promise { + const rows = await queryRows<{ DateStyle: string }>(queryable, 'SHOW DateStyle'); + return rows[0]?.DateStyle; +} + +describe('the session settings on a connection the application supplies', () => { + const cleanups: (() => Promise)[] = []; + + afterEach(async () => { + for (const cleanup of cleanups.splice(0).reverse()) await cleanup().catch(() => undefined); + }); + + async function devDatabaseUrl(): Promise { + const database = await createDevDatabase(); + cleanups.push(() => database.close()); + return database.connectionString; + } + + it( + 'are applied to a pooled connection whose session holds another value, before the driver uses it', + async () => { + const pool = new pg.Pool({ connectionString: await devDatabaseUrl(), max: 1 }); + await pool.query(GERMAN); + const driver = createBoundDriverFromBinding({ kind: 'pgPool', pool }, undefined, { + sessionSettings: SESSION_SETTINGS, + }); + cleanups.push(() => driver.close()); + + expect(await dateStyleOf(driver)).toMatch(/^ISO/); + }, + timeouts.spinUpPpgDev, + ); + + it( + 'are applied again after a connection handed out for exclusive use is released', + async () => { + const pool = new pg.Pool({ connectionString: await devDatabaseUrl(), max: 1 }); + const driver = createBoundDriverFromBinding({ kind: 'pgPool', pool }, undefined, { + sessionSettings: SESSION_SETTINGS, + }); + cleanups.push(() => driver.close()); + + const connection = await driver.acquireConnection(); + await connection.execute({ sql: GERMAN }); + await connection.release(); + + expect(await dateStyleOf(driver)).toMatch(/^ISO/); + }, + timeouts.spinUpPpgDev, + ); + + it( + 'are applied to a client whose session holds another value, before the driver uses it, and again after an exclusive connection is released', + async () => { + const client = new pg.Client({ connectionString: await devDatabaseUrl() }); + await client.connect(); + await client.query(GERMAN); + const driver = createBoundDriverFromBinding({ kind: 'pgClient', client }, undefined, { + sessionSettings: SESSION_SETTINGS, + }); + cleanups.push(() => driver.close()); + + const first = await dateStyleOf(driver); + const connection = await driver.acquireConnection(); + await connection.execute({ sql: GERMAN }); + await connection.release(); + + expect({ first, afterRelease: await dateStyleOf(driver) }).toEqual({ + first: expect.stringMatching(/^ISO/), + afterRelease: expect.stringMatching(/^ISO/), + }); + }, + timeouts.spinUpPpgDev, + ); + + it( + 'refuse a connection whose settings the server rejects, and evict it from the pool', + async () => { + const pool = new pg.Pool({ connectionString: await devDatabaseUrl(), max: 1 }); + const driver = createBoundDriverFromBinding({ kind: 'pgPool', pool }, undefined, { + sessionSettings: { no_such_setting: 'x' }, + }); + cleanups.push(() => driver.close()); + + await expect(driver.acquireConnection()).rejects.toThrow(/no_such_setting/); + expect(pool.totalCount).toBe(0); + }, + timeouts.spinUpPpgDev, + ); +}); diff --git a/packages/3-targets/postgres/3-driver/test/driver.startup-options.test.ts b/packages/3-targets/postgres/3-driver/test/driver.startup-options.test.ts new file mode 100644 index 000000000000..ace0b64f6146 --- /dev/null +++ b/packages/3-targets/postgres/3-driver/test/driver.startup-options.test.ts @@ -0,0 +1,82 @@ +import { type StartupParameterRecorder, startStartupParameterRecorder } from '@repo/test-utils'; +import pg from 'pg'; +import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest'; +import postgresControlDriverDescriptor from '../src/exports/control'; +import postgresRuntimeDriverDescriptor from '../src/exports/runtime'; +import { createBoundDriverFromBinding } from '../src/postgres-driver'; +import { createPgPool } from '../src/session-settings'; +import { queryRows } from './sql-queryable-test-utils'; + +const SESSION_SETTINGS = { DateStyle: 'ISO' } as const; + +const URL_OPTIONS = 'options=-c%20search_path%3Dreports%20-c%20DateStyle%3DGerman'; + +describe('the startup options of a connection', () => { + let recorder: StartupParameterRecorder; + + beforeEach(async () => { + recorder = await startStartupParameterRecorder(); + }); + + afterEach(async () => { + await recorder.close(); + }); + + it('set the session settings, after the options the URL gives, on a runtime connection Prisma opens', async () => { + const driver = createBoundDriverFromBinding( + { kind: 'url', url: `${recorder.url}?${URL_OPTIONS}` }, + undefined, + { sessionSettings: SESSION_SETTINGS }, + ); + await queryRows(driver, 'select 1').catch(() => undefined); + await driver.close().catch(() => undefined); + + expect((await recorder.next())['options']).toBe( + '-c search_path=reports -c DateStyle=German -c DateStyle=ISO', + ); + }); + + it('set the session settings a runtime driver is created with on the connections it opens', async () => { + const driver = postgresRuntimeDriverDescriptor.create(undefined, SESSION_SETTINGS); + await driver.connect({ kind: 'url', url: recorder.url }); + await queryRows(driver, 'select 1').catch(() => undefined); + await driver.close().catch(() => undefined); + + expect((await recorder.next())['options']).toBe('-c DateStyle=ISO'); + }); + + it('set the session settings on a control connection Prisma opens', async () => { + await postgresControlDriverDescriptor + .create(recorder.url, { sessionSettings: SESSION_SETTINGS }) + .catch(() => undefined); + + expect((await recorder.next())['options']).toBe('-c DateStyle=ISO'); + }); + + it('are left as they are on a client the application supplies', async () => { + const driver = createBoundDriverFromBinding( + { + kind: 'pgClient', + client: new pg.Client({ connectionString: `${recorder.url}?${URL_OPTIONS}` }), + }, + undefined, + { sessionSettings: SESSION_SETTINGS }, + ); + await queryRows(driver, 'select 1').catch(() => undefined); + await driver.close().catch(() => undefined); + + expect((await recorder.next())['options']).toBe('-c search_path=reports -c DateStyle=German'); + }); + + it('read PGOPTIONS when a pool opens each connection, not once when the pool is made', async () => { + const pool = createPgPool({ connectionString: recorder.url }, SESSION_SETTINGS); + vi.stubEnv('PGOPTIONS', '-c search_path=later'); + try { + await pool.connect().catch(() => undefined); + expect((await recorder.next())['options']).toBe('-c search_path=later -c DateStyle=ISO'); + } finally { + vi.unstubAllEnvs(); + await pool.end().catch(() => undefined); + } + }); +}); diff --git a/packages/3-targets/postgres/3-driver/test/session-settings.test-d.ts b/packages/3-targets/postgres/3-driver/test/session-settings.test-d.ts new file mode 100644 index 000000000000..e2350ca28b32 --- /dev/null +++ b/packages/3-targets/postgres/3-driver/test/session-settings.test-d.ts @@ -0,0 +1,12 @@ +import { expectTypeOf, test } from 'vitest'; +import { createPgClient, createPgPool, pgConnectionConfig } from '../src/session-settings'; + +test('a pg pool, client or connection config cannot be made without the session settings', () => { + // @ts-expect-error the session settings the target declares are required + createPgPool({ connectionString: 'postgres://db.example.com/app' }); + // @ts-expect-error the session settings the target declares are required + createPgClient({ connectionString: 'postgres://db.example.com/app' }); + // @ts-expect-error the session settings the target declares are required + pgConnectionConfig({ connectionString: 'postgres://db.example.com/app' }); + expectTypeOf(createPgPool).parameter(1).toEqualTypeOf>>(); +}); diff --git a/packages/3-targets/postgres/3-driver/test/session-settings.test.ts b/packages/3-targets/postgres/3-driver/test/session-settings.test.ts new file mode 100644 index 000000000000..82ed8b46b6e2 --- /dev/null +++ b/packages/3-targets/postgres/3-driver/test/session-settings.test.ts @@ -0,0 +1,197 @@ +import { mkdtempSync, writeFileSync } from 'node:fs'; +import { tmpdir } from 'node:os'; +import pg from 'pg'; +import { afterEach, describe, expect, it, vi } from 'vitest'; +import { pgConnectionConfig } from '../src/session-settings'; + +const SETTINGS = { DateStyle: 'ISO' } as const; + +function fromUrl(url: string) { + return pgConnectionConfig({ connectionString: url }, SETTINGS); +} + +const CONNECTION_FIELDS = [ + 'user', + 'password', + 'database', + 'port', + 'host', + 'binary', + 'options', + 'ssl', + 'sslnegotiation', + 'client_encoding', + 'replication', + 'isDomainSocket', + 'application_name', + 'fallback_application_name', + 'statement_timeout', + 'lock_timeout', + 'idle_in_transaction_session_timeout', + 'query_timeout', + 'connect_timeout', + 'keepalives', + 'keepalives_idle', +] as const; + +/** What a pg client connects with, apart from its startup options. */ +function connectsWith(client: pg.Client): Record { + const parameters: unknown = Reflect.get(client, 'connectionParameters'); + return Object.fromEntries( + CONNECTION_FIELDS.filter((field) => field !== 'options').map((field) => [ + field, + Reflect.get(Object(parameters), field), + ]), + ); +} + +const certificate = `${mkdtempSync(`${tmpdir()}/iso-date-style-`)}/c.pem`; +writeFileSync(certificate, 'not a real certificate'); + +const URLS = [ + 'postgres://u:p@localhost:5432/db', + 'postgresql://u:p%40ss%2Fw%3Ard@db.example.com/db', + 'postgres://u:p@localhost/db?sslmode=require', + `postgres://u:p@localhost/db?sslmode=verify-full&sslrootcert=${certificate}&sslcert=${certificate}&sslkey=${certificate}`, + 'postgres://u:p@localhost/db?sslmode=disable', + 'postgres://u:p@localhost/db?sslmode=no-verify', + 'postgres://u:p@localhost/db?ssl=true', + 'postgres://u:p@localhost/db?ssl=no-verify', + 'postgres://u:p@localhost/db?uselibpqcompat=true&sslmode=require', + 'postgres://u:p@localhost/db?options=-c%20search_path%3Dfoo', + 'postgres://u:p@localhost/db?application_name=myapp&connect_timeout=7&statement_timeout=1000&keepalives=1', + 'postgres://u:p@/db?host=/var/run/postgresql', + 'postgres://u:p@%2Fvar%2Frun%2Fpostgresql/db', + 'socket://u:p@/var/run/postgresql?db=db&encoding=utf8', + '/var/run/postgresql db', + 'postgres://u:p@[::1]:5433/db', + 'postgres://u:p@[2001:db8::1]/db', + 'postgres://u@localhost/db', + 'postgres://localhost', + 'postgres://u:p@localhost/db?schema=public&connection_limit=5', + 'postgres://u:p@localhost/db?replication=database', + 'postgres://u:p@localhost/db?client_encoding=LATIN1&fallback_application_name=x', + 'postgres://u:p@localhost/db?sslnegotiation=direct&sslmode=require', + 'postgres://u:p@localhost/db?port=6000', + 'postgres://u:p@localhost/db?statement_timeout=1000&query_timeout=2000&lock_timeout=3000&idle_in_transaction_session_timeout=4000', +]; + +describe('a pg client built from the settings of a URL', () => { + afterEach(() => { + vi.unstubAllEnvs(); + }); + + it.each(URLS)('connects as pg does from the connection string %s', (url) => { + expect(connectsWith(new pg.Client(fromUrl(url)))).toEqual( + connectsWith(new pg.Client({ connectionString: url })), + ); + }); + + it.each(URLS)( + 'connects as pg does from the connection string %s when PGSSLMODE and PGAPPNAME are set', + (url) => { + vi.stubEnv('PGSSLMODE', 'require'); + vi.stubEnv('PGAPPNAME', 'envapp'); + expect(connectsWith(new pg.Client(fromUrl(url)))).toEqual( + connectsWith(new pg.Client({ connectionString: url })), + ); + }, + ); + + it('keeps TLS on without certificate checks for ssl=no-verify', () => { + const client = new pg.Client(fromUrl('postgres://u:p@localhost/db?ssl=no-verify')); + expect(Reflect.get(client, 'ssl')).toEqual({ rejectUnauthorized: false }); + }); + + it.each(['true', 'false'])('reads text rows when the URL says binary=%s', (binary) => { + const client = new pg.Client(fromUrl(`postgres://u:p@localhost/db?binary=${binary}`)); + expect(Reflect.get(client, 'binary')).toBe(false); + }); +}); + +describe('the connection settings of a connection Prisma opens', () => { + it('send each session setting as a startup option, escaping spaces and backslashes', () => { + expect( + pgConnectionConfig( + { connectionString: 'postgres://db.example.com/app' }, + { DateStyle: 'ISO', search_path: 'my schema', application_name: 'a\\b' }, + ).options, + ).toBe('-c DateStyle=ISO -c search_path=my\\ schema -c application_name=a\\\\b'); + }); + + it('add the session settings to a connection given without a connection string', () => { + expect(pgConnectionConfig({ host: 'db.example.com', database: 'app' }, SETTINGS)).toEqual({ + host: 'db.example.com', + database: 'app', + options: '-c DateStyle=ISO', + }); + }); + + it('keep the settings given beside the connection string', () => { + expect( + pgConnectionConfig( + { connectionString: 'postgres://db.example.com/app', connectionTimeoutMillis: 5 }, + SETTINGS, + ).connectionTimeoutMillis, + ).toBe(5); + }); + + it('add no startup options when the target declares no settings', () => { + expect( + pgConnectionConfig({ connectionString: 'postgres://db.example.com/app' }, {}).options, + ).toBeUndefined(); + }); + + it('set DateStyle=ISO as a startup option', () => { + expect( + fromUrl('postgres://someone:secret@db.example.com:6543/app?application_name=reports').options, + ).toBe('-c DateStyle=ISO'); + }); + + it('add DateStyle=ISO after the startup options the URL gives, so it wins over a DateStyle there', () => { + expect( + fromUrl( + 'postgres://db.example.com/app?options=-c%20search_path%3Dreports%20-c%20DateStyle%3DGerman', + ).options, + ).toBe('-c search_path=reports -c DateStyle=German -c DateStyle=ISO'); + }); + + describe('with PGOPTIONS set', () => { + afterEach(() => { + vi.unstubAllEnvs(); + }); + + it('add DateStyle=ISO after PGOPTIONS when the URL gives no options, as pg reads PGOPTIONS then', () => { + vi.stubEnv('PGOPTIONS', '-c search_path=reports'); + + expect(fromUrl('postgres://db.example.com/app').options).toBe( + '-c search_path=reports -c DateStyle=ISO', + ); + }); + + it('add DateStyle=ISO after the URL options, which pg reads in place of PGOPTIONS', () => { + vi.stubEnv('PGOPTIONS', '-c search_path=reports'); + + expect(fromUrl('postgres://db.example.com/app?options=-c%20work_mem%3D64MB').options).toBe( + '-c work_mem=64MB -c DateStyle=ISO', + ); + }); + }); + + it('pass on only the connection settings pg reads from a URL, so a query parameter cannot set up the pool or the socket', () => { + const config = fromUrl( + 'postgres://db.example.com/app?max=3&keepAlive=false&idleTimeoutMillis=1', + ); + const pool = new pg.Pool(config); + + expect({ + keys: ['max', 'keepAlive', 'idleTimeoutMillis'].filter((key) => Object.hasOwn(config, key)), + max: pool.options.max, + idleTimeoutMillis: pool.options.idleTimeoutMillis, + }).toEqual({ + keys: [], + max: new pg.Pool().options.max, + idleTimeoutMillis: new pg.Pool().options.idleTimeoutMillis, + }); + }); +}); diff --git a/packages/3-targets/postgres/4-codec-testkit/test/codec-conformance/cases.ts b/packages/3-targets/postgres/4-codec-testkit/test/codec-conformance/cases.ts index 68d42c0e1ffd..1a7cec423024 100644 --- a/packages/3-targets/postgres/4-codec-testkit/test/codec-conformance/cases.ts +++ b/packages/3-targets/postgres/4-codec-testkit/test/codec-conformance/cases.ts @@ -328,21 +328,20 @@ export const postgresConformanceCases: readonly PostgresCodecConformanceCase[] = value: undefined, nullValue: true, }, - // The `*-string` codecs' application value is PostgreSQL's own rendering, so each case is written - // the way the server writes it — space separator, two-digit offset, microseconds. That is now - // what the projection returns too, which is the whole point of the text cast: one spelling, - // whether the column is read flat or built into a JSON document. + // The `*-string` codecs' application value is the ISO 8601 stored form of the value, which they + // read from the text PostgreSQL prints whether the column is read flat or built into a JSON + // document, in any session time zone. { codecId: 'pg/date-string@1', label: 'calendar date', value: '2026-01-02' }, { codecId: 'pg/timestamp-string@1', label: 'microsecond precision', - value: '2026-01-02 03:04:05.123456', + value: '2026-01-02T03:04:05.123456', typeParams: { precision: 6 }, }, { codecId: 'pg/timestamptz-string@1', label: 'microsecond precision at UTC', - value: '2026-01-02 03:04:05.123456+00', + value: '2026-01-02T03:04:05.123456Z', typeParams: { precision: 6 }, }, { @@ -351,35 +350,42 @@ export const postgresConformanceCases: readonly PostgresCodecConformanceCase[] = value: '03:04:05.123456', typeParams: { precision: 6 }, }, - { codecId: 'pg/timetz@1', label: 'time of day at UTC', value: '03:04:05+00' }, + { codecId: 'pg/timetz@1', label: 'time of day at UTC', value: '03:04:05Z' }, { codecId: 'pg/timetz@1', label: 'time of day at UTC under a hostile session', - value: '03:04:05+00', + value: '03:04:05Z', setupSql: HOSTILE_TEMPORAL_SESSION, }, - // Text PostgreSQL writes with ` BC`, with an offset that has seconds, and to a precision below six digits, read back - // as PostgreSQL wrote it. - { codecId: 'pg/date-string@1', label: 'a year before Christ', value: '0044-03-15 BC' }, + // Values PostgreSQL prints with ` BC`, with an offset that has seconds, and to a precision below six digits, read + // back in their ISO 8601 form. + { codecId: 'pg/date-string@1', label: 'a year before Christ', value: '-000043-03-15' }, { codecId: 'pg/timestamp-string@1', label: 'a year before Christ', - value: '0044-03-15 12:00:00 BC', + value: '-000043-03-15T12:00:00', typeParams: { precision: 6 }, }, { codecId: 'pg/timestamptz-string@1', label: 'a year before Christ at UTC', - value: '0044-03-15 12:00:00+00 BC', + value: '-000043-03-15T12:00:00Z', typeParams: { precision: 6 }, }, { codecId: 'pg/timestamptz-string@1', label: 'an offset with seconds, in the local mean time of Amsterdam', - value: '1800-01-01 00:17:30+00:17:30', + value: '1800-01-01T00:00:00Z', typeParams: { precision: 6 }, setupSql: ["SET TimeZone = 'Europe/Amsterdam'"], }, + { + codecId: 'pg/timestamptz-string@1', + label: 'the same instant in a session in Tokyo', + value: '2026-01-02T03:04:05.123456Z', + typeParams: { precision: 6 }, + setupSql: ["SET TimeZone = 'Asia/Tokyo'"], + }, { codecId: 'pg/time-string@1', label: 'millisecond precision', diff --git a/packages/3-targets/postgres/4-codec-testkit/test/date-time-text.integration.test.ts b/packages/3-targets/postgres/4-codec-testkit/test/date-time-text.integration.test.ts index 66fb37958f40..265b9a5378e8 100644 --- a/packages/3-targets/postgres/4-codec-testkit/test/date-time-text.integration.test.ts +++ b/packages/3-targets/postgres/4-codec-testkit/test/date-time-text.integration.test.ts @@ -9,62 +9,83 @@ const ZONES = ['UTC', 'Europe/Amsterdam', 'Asia/Kolkata', 'America/St_Johns'] as interface DateTimeType { readonly codecId: string; readonly type: string; - /** Values at the edges of what the type holds. */ - readonly values: readonly string[]; - /** A value with six digits after the decimal point, read at each precision from 0 to 6. */ - readonly precise?: string; + /** Values at the edges of what the type holds, each with the stored form the codec reads it as in every time zone. */ + readonly values: readonly (readonly [value: string, stored: string])[]; + /** A value with six digits after the decimal point, read at each precision from 0 to 6, and its stored form for a fraction. */ + readonly precise?: { + readonly value: string; + readonly stored: (fraction: string) => string; + }; } +/** The fraction PostgreSQL keeps of .123456 at each precision from 0 to 6, without trailing zeros. */ +const FRACTIONS = ['', '.1', '.12', '.123', '.1235', '.12346', '.123456'] as const; + const TYPES: readonly DateTimeType[] = [ { codecId: 'pg/date-string@1', type: 'date', values: [ - '2024-01-01', - '0044-03-15 BC', - '4713-11-24 BC', - '0001-01-01 BC', - '10000-01-01', - '5874897-12-31', - 'infinity', - '-infinity', + ['2024-01-01', '2024-01-01'], + ['0044-03-15 BC', '-000043-03-15'], + ['4713-11-24 BC', '-004712-11-24'], + ['0001-01-01 BC', '0000-01-01'], + ['10000-01-01', '+010000-01-01'], + ['5874897-12-31', '+5874897-12-31'], + ['infinity', 'infinity'], + ['-infinity', '-infinity'], ], }, { codecId: 'pg/time-string@1', type: 'time', - values: ['00:00:00', '23:59:59.999999', '24:00:00'], - precise: '12:34:56.123456', + values: [ + ['00:00:00', '00:00:00'], + ['23:59:59.999999', '23:59:59.999999'], + ['24:00:00', '24:00:00'], + ], + precise: { value: '12:34:56.123456', stored: (fraction) => `12:34:56${fraction}` }, }, { codecId: 'pg/timetz@1', type: 'timetz', - values: ['12:00:00+01:30:15', '12:00:00-15:59', '12:00:00+15:59:59', '24:00:00+00'], - precise: '12:34:56.123456+02', + values: [ + ['12:00:00+01:30:15', '12:00:00+01:30:15'], + ['12:00:00-15:59', '12:00:00-15:59'], + ['12:00:00+15:59:59', '12:00:00+15:59:59'], + ['24:00:00+00', '24:00:00Z'], + ], + precise: { value: '12:34:56.123456+02', stored: (fraction) => `12:34:56${fraction}+02:00` }, }, { codecId: 'pg/timestamp-string@1', type: 'timestamp', values: [ - '0044-03-15 12:00:00 BC', - '4713-11-24 00:00:00 BC', - '294276-12-31 23:59:59', - 'infinity', - '-infinity', + ['0044-03-15 12:00:00 BC', '-000043-03-15T12:00:00'], + ['4713-11-24 00:00:00 BC', '-004712-11-24T00:00:00'], + ['294276-12-31 23:59:59', '+294276-12-31T23:59:59'], + ['infinity', 'infinity'], + ['-infinity', '-infinity'], ], - precise: '2024-01-01 12:34:56.123456', + precise: { + value: '2024-01-01 12:34:56.123456', + stored: (fraction) => `2024-01-01T12:34:56${fraction}`, + }, }, { codecId: 'pg/timestamptz-string@1', type: 'timestamptz', values: [ - '1800-01-01 00:00:00+00', - '0044-03-15 12:00:00+00 BC', - '294276-12-31 23:59:59+00', - 'infinity', - '-infinity', + ['1800-01-01 00:00:00+00', '1800-01-01T00:00:00Z'], + ['0044-03-15 12:00:00+00 BC', '-000043-03-15T12:00:00Z'], + ['294276-12-31 23:59:59+00', '+294276-12-31T23:59:59Z'], + ['infinity', 'infinity'], + ['-infinity', '-infinity'], ], - precise: '2024-01-01 12:34:56.123456+00', + precise: { + value: '2024-01-01 12:34:56.123456+00', + stored: (fraction) => `2024-01-01T12:34:56${fraction}Z`, + }, }, ]; @@ -84,17 +105,21 @@ function codecFor(codecId: string) { }); } -function typesAtEachPrecision(entry: DateTimeType): readonly (readonly [string, string])[] { - const edges = entry.values.map((value) => [entry.type, value] as const); +function typesAtEachPrecision( + entry: DateTimeType, +): readonly (readonly [type: string, value: string, stored: string])[] { + const edges = entry.values.map(([value, stored]) => [entry.type, value, stored] as const); if (entry.precise === undefined) return edges; - const precise = entry.precise; + const { value, stored } = entry.precise; return [ ...edges, - ...[0, 1, 2, 3, 4, 5, 6].map((precision) => [`${entry.type}(${precision})`, precise] as const), + ...FRACTIONS.map( + (fraction, precision) => [`${entry.type}(${precision})`, value, stored(fraction)] as const, + ), ]; } -describe('the date and time codecs that carry PostgreSQL text', { concurrent: false }, () => { +describe('the date and time codecs that carry text', { concurrent: false }, () => { let database: Awaited> | undefined; let driver: Awaited> | undefined; @@ -109,7 +134,7 @@ describe('the date and time codecs that carry PostgreSQL text', { concurrent: fa }, timeouts.spinUpPpgDev); it( - 'read the text PostgreSQL writes for each value, in each time zone and at each precision, unchanged', + 'read the text PostgreSQL writes for each value, in each time zone and at each precision, as its ISO 8601 stored form', async () => { const unread: unknown[] = []; let read = 0; @@ -117,7 +142,7 @@ describe('the date and time codecs that carry PostgreSQL text', { concurrent: fa await driver!.query(`SET TimeZone = '${zone}'`); for (const entry of TYPES) { const codec = codecFor(entry.codecId); - for (const [type, value] of typesAtEachPrecision(entry)) { + for (const [type, value, stored] of typesAtEachPrecision(entry)) { const result = await driver!.query<{ text: string }>( `select $1::${type}::text as text`, [value], @@ -125,11 +150,11 @@ describe('the date and time codecs that carry PostgreSQL text', { concurrent: fa const text = result.rows[0]!.text; try { const decoded = await codec.fromWire(text, {}); - if (decoded === text) { + if (decoded === stored) { read += 1; continue; } - unread.push({ zone, type, value, text, decoded }); + unread.push({ zone, type, value, text, stored, decoded }); } catch (error) { unread.push({ zone, type, value, text, error: String(error) }); } @@ -142,6 +167,62 @@ describe('the date and time codecs that carry PostgreSQL text', { concurrent: fa timeouts.databaseOperation, ); + it( + 'write each value they read back in a form PostgreSQL reads as the same value', + async () => { + const changed: unknown[] = []; + for (const entry of TYPES) { + const codec = codecFor(entry.codecId); + for (const [, stored] of entry.values) { + const wire = await codec.toWire(stored, {}); + const result = await driver!.query<{ text: string }>( + `select $1::${entry.type}::text as text`, + [wire], + ); + const read = await codec.fromWire(result.rows[0]!.text, {}); + if (read !== stored) changed.push({ type: entry.type, stored, wire, read }); + } + } + expect(changed).toEqual([]); + }, + timeouts.databaseOperation, + ); + + it( + 'refuse, with every codec, a date or timestamp the session prints under another DateStyle, naming the setting', + async () => { + const refusals = []; + await driver!.query("SET DateStyle = 'German, DMY'"); + try { + for (const [codecId, type] of [ + ['pg/date-string@1', 'date'], + ['pg/date-temporal@1', 'date'], + ['pg/timestamp-string@1', 'timestamp'], + ['pg/timestamp-temporal@1', 'timestamp'], + ['pg/timestamptz-string@1', 'timestamptz'], + ['pg/timestamptz-temporal@1', 'timestamptz'], + ['pg/timestamptz-date@1', 'timestamptz'], + ] as const) { + const result = await driver!.query<{ text: string }>( + `select '2024-01-02 03:04:05'::${type}::text as text`, + ); + refusals.push( + await codecFor(codecId) + .fromWire(result.rows[0]!.text, {}) + .then( + () => 'read', + (error: { code?: string }) => error.code, + ), + ); + } + } finally { + await driver!.query('RESET DateStyle'); + } + expect(refusals).toEqual(Array(7).fill('RUNTIME.DATE_STYLE_NOT_ISO')); + }, + timeouts.databaseOperation, + ); + it( 'refuse text PostgreSQL refuses for their type', async () => { diff --git a/packages/3-targets/postgres/9-facade/src/runtime/postgres-serverless.ts b/packages/3-targets/postgres/9-facade/src/runtime/postgres-serverless.ts index e6f3860128d1..43e3068f172d 100644 --- a/packages/3-targets/postgres/9-facade/src/runtime/postgres-serverless.ts +++ b/packages/3-targets/postgres/9-facade/src/runtime/postgres-serverless.ts @@ -1,6 +1,9 @@ import postgresAdapter from '@internal/adapter-postgres/runtime'; import type { Contract } from '@internal/contract/types'; -import postgresDriver, { suppressIdleConnectionErrors } from '@internal/driver-postgres/runtime'; +import postgresDriver, { + createPgClient, + suppressIdleConnectionErrors, +} from '@internal/driver-postgres/runtime'; import type { SqlStorage } from '@internal/sql-contract/types'; import type { CollectionClasses, NoCollectionClasses } from '@internal/sql-orm-client'; import type { Runtime } from '@internal/sql-runtime'; @@ -8,7 +11,6 @@ import { createExecutionContext, createSqlExecutionStack } from '@internal/sql-r import postgresTarget from '@internal/target-postgres/runtime'; import { InternalError } from '@internal/utils/internal-error'; import { redactDatabaseUrl } from '@internal/utils/redact-db-url'; -import { Client } from 'pg'; import { postgresError } from '../errors'; import { validatePostgresContract } from '../static/contract-from-json'; import { buildPostgresStaticContext } from '../static/postgres-static'; @@ -203,10 +205,10 @@ export default function postgresServerless< }); const pgClient = suppressIdleConnectionErrors( - new Client({ - connectionString: url, - connectionTimeoutMillis: DEFAULT_CONNECT_TIMEOUT_MILLIS, - }), + createPgClient( + { connectionString: url, connectionTimeoutMillis: DEFAULT_CONNECT_TIMEOUT_MILLIS }, + stack.target.sessionSettings ?? {}, + ), ); await driver.connect({ kind: 'pgClient', client: pgClient }); diff --git a/packages/3-targets/postgres/9-facade/src/runtime/postgres.ts b/packages/3-targets/postgres/9-facade/src/runtime/postgres.ts index 3ed490a596b8..4b9b10f8489d 100644 --- a/packages/3-targets/postgres/9-facade/src/runtime/postgres.ts +++ b/packages/3-targets/postgres/9-facade/src/runtime/postgres.ts @@ -1,13 +1,16 @@ import postgresAdapter from '@internal/adapter-postgres/runtime'; import type { Contract } from '@internal/contract/types'; -import postgresDriver, { suppressIdleConnectionErrors } from '@internal/driver-postgres/runtime'; +import postgresDriver, { + createPgPool, + suppressIdleConnectionErrors, +} from '@internal/driver-postgres/runtime'; import type { SqlStorage } from '@internal/sql-contract/types'; import type { CollectionClasses, NoCollectionClasses } from '@internal/sql-orm-client'; import type { Runtime } from '@internal/sql-runtime'; import { createExecutionContext, createSqlExecutionStack } from '@internal/sql-runtime'; import postgresTarget from '@internal/target-postgres/runtime'; import { InternalError } from '@internal/utils/internal-error'; -import { type Client, Pool } from 'pg'; +import type { Client, Pool } from 'pg'; import { postgresError } from '../errors'; import { validatePostgresContract } from '../static/contract-from-json'; import { buildPostgresStaticContext } from '../static/postgres-static'; @@ -77,12 +80,15 @@ function toRuntimeBinding< return { kind: 'pgPool', pool: suppressIdleConnectionErrors( - new Pool({ - connectionString: binding.url, - connectionTimeoutMillis: - options.poolOptions?.connectionTimeoutMillis ?? DEFAULT_CONNECT_TIMEOUT_MILLIS, - idleTimeoutMillis: options.poolOptions?.idleTimeoutMillis ?? 30_000, - }), + createPgPool( + { + connectionString: binding.url, + connectionTimeoutMillis: + options.poolOptions?.connectionTimeoutMillis ?? DEFAULT_CONNECT_TIMEOUT_MILLIS, + idleTimeoutMillis: options.poolOptions?.idleTimeoutMillis ?? 30_000, + }, + postgresTarget.sessionSettings ?? {}, + ), ), } as const; } diff --git a/packages/3-targets/postgres/9-facade/test/contract-builder/values-as-postgres-prints.test.ts b/packages/3-targets/postgres/9-facade/test/contract-builder/values-as-postgres-prints.test.ts index b328f5b6f541..8766a1a1a087 100644 --- a/packages/3-targets/postgres/9-facade/test/contract-builder/values-as-postgres-prints.test.ts +++ b/packages/3-targets/postgres/9-facade/test/contract-builder/values-as-postgres-prints.test.ts @@ -89,6 +89,22 @@ describe('values in a Postgres contract decode to the value a query returns', () }, ); + it.each([ + [ + 'pg/timestamp-string@1', + enumType('Stamp', pgTimestampString, member('A', '2024-01-02T03:04:05')), + ], + [ + 'pg/timestamptz-string@1', + enumType('Stamp', pgTimestamptzString, member('A', '2024-01-02T03:04:05Z')), + ], + ])( + 'accepts an enum over %s, whose values a query reads in the ISO 8601 form members are written in', + (_codecId, handle) => { + expect(() => contractWithEnum(handle)).not.toThrow(); + }, + ); + it('refuses an inet enum member that is not an address', () => { expect(() => contractWithEnum(enumType('Host', pgInet, member('A', 'not an address')))).toThrow( expect.objectContaining({ @@ -100,16 +116,6 @@ describe('values in a Postgres contract decode to the value a query returns', () }); it.each([ - [ - 'pg/timestamp-string@1', - enumType('Stamp', pgTimestampString, member('A', '2024-01-02T03:04:05')), - 'A query reads each value as the text PostgreSQL prints, such as "2024-01-02 03:04:05", while the contract stores it in ISO 8601, such as "2024-01-02T03:04:05", so no value read back equals a member. Use pg/timestamp-temporal@1, whose members are Temporal values.', - ], - [ - 'pg/timestamptz-string@1', - enumType('Stamp', pgTimestamptzString, member('A', '2024-01-02T03:04:05Z')), - `A query reads each value as the text PostgreSQL prints in the session's time zone, such as "2024-01-02 03:04:05+00", while the contract stores it in ISO 8601, such as "2024-01-02T03:04:05Z", so no value read back equals a member. Use pg/timestamptz-temporal@1, whose members are Temporal values.`, - ], [ 'pg/json@1', enumType('Stamp', pgJson, member('A', 'low')), diff --git a/packages/3-targets/postgres/9-facade/test/date-style-startup-option.test.ts b/packages/3-targets/postgres/9-facade/test/date-style-startup-option.test.ts new file mode 100644 index 000000000000..3731e660a645 --- /dev/null +++ b/packages/3-targets/postgres/9-facade/test/date-style-startup-option.test.ts @@ -0,0 +1,48 @@ +import type { SqlStorage } from '@internal/sql-contract/types'; +import { + createContract, + type StartupParameterRecorder, + startStartupParameterRecorder, + timeouts, +} from '@repo/test-utils'; +import { afterEach, beforeEach, describe, expect, it } from 'vitest'; +import postgres from '../src/runtime/postgres'; +import postgresServerless from '../src/runtime/postgres-serverless'; + +const contract = createContract(); + +describe('a connection the client opens from a URL', () => { + let recorder: StartupParameterRecorder; + + beforeEach(async () => { + recorder = await startStartupParameterRecorder(); + }); + + afterEach(async () => { + await recorder.close(); + }); + + it( + 'sets DateStyle=ISO as a startup option', + async () => { + const db = postgres({ contract, url: recorder.url, verifyMarker: false }); + await db.execute(db.raw.sql`SELECT 1`.affectedCount().build()).catch(() => undefined); + await db.close().catch(() => undefined); + + expect((await recorder.next())['options']).toBe('-c DateStyle=ISO'); + }, + timeouts.databaseOperation, + ); + + it( + 'sets DateStyle=ISO as a startup option on a serverless connection', + async () => { + await postgresServerless({ contract }) + .connect({ url: recorder.url }) + .catch(() => undefined); + + expect((await recorder.next())['options']).toBe('-c DateStyle=ISO'); + }, + timeouts.databaseOperation, + ); +}); diff --git a/packages/3-targets/postgres/9-facade/test/postgres-serverless.test.ts b/packages/3-targets/postgres/9-facade/test/postgres-serverless.test.ts index 6078d8daf729..76afcc9d4af8 100644 --- a/packages/3-targets/postgres/9-facade/test/postgres-serverless.test.ts +++ b/packages/3-targets/postgres/9-facade/test/postgres-serverless.test.ts @@ -10,7 +10,7 @@ import fixtureContractJson from './fixtures/generated/contract.json' with { type type QueryMock = ReturnType; interface RecordedClient { - readonly connectionString: string | undefined; + readonly database: string | undefined; readonly connectionTimeoutMillis: number | undefined; readonly connect: QueryMock; readonly query: QueryMock; @@ -44,10 +44,10 @@ vi.mock('pg', () => { connect = vi.fn(() => recorded.connectImpl()); query = vi.fn().mockResolvedValue({ rows: [], rowCount: 0 }); end = vi.fn(() => recorded.endImpl()); - readonly connectionString: string | undefined; + readonly database: string | undefined; readonly connectionTimeoutMillis: number | undefined; - constructor(config?: { connectionString?: string; connectionTimeoutMillis?: number }) { - this.connectionString = config?.connectionString; + constructor(config?: { database?: string; connectionTimeoutMillis?: number }) { + this.database = config?.database; this.connectionTimeoutMillis = config?.connectionTimeoutMillis; recorded.clients.push(this); } @@ -203,10 +203,7 @@ describe('postgresServerless connect()', () => { const first = await serverless.connect({ url }); const second = await serverless.connect({ url: 'postgres://localhost:5432/other' }); - expect(recorded.clients.map((client) => client.connectionString)).toEqual([ - url, - 'postgres://localhost:5432/other', - ]); + expect(recorded.clients.map((client) => client.database)).toEqual(['db', 'other']); expect(first).not.toBe(second); expect(first.runtime()).not.toBe(second.runtime()); diff --git a/packages/9-public/@prisma/orm-target-postgres/package.json b/packages/9-public/@prisma/orm-target-postgres/package.json index 382b44f8eef6..f8bb71effca0 100644 --- a/packages/9-public/@prisma/orm-target-postgres/package.json +++ b/packages/9-public/@prisma/orm-target-postgres/package.json @@ -24,6 +24,7 @@ "arktype": "^2.2.2", "pathe": "^2.0.3", "pg": "catalog:", + "pg-connection-string": "^2.14.0", "pg-cursor": "^2.21.0", "postgres-array": "2.0.0", "temporal-spec": "1.0.1" diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 47583738a745..fbf403aad731 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -4323,6 +4323,9 @@ importers: pg: specifier: 'catalog:' version: 8.22.0 + pg-connection-string: + specifier: ^2.14.0 + version: 2.14.0 pg-cursor: specifier: ^2.21.0 version: 2.21.0(pg@8.22.0) @@ -5499,6 +5502,9 @@ importers: pg: specifier: 'catalog:' version: 8.22.0 + pg-connection-string: + specifier: ^2.14.0 + version: 2.14.0 pg-cursor: specifier: ^2.21.0 version: 2.21.0(pg@8.22.0) diff --git a/skills/prisma-8/references/contract.md b/skills/prisma-8/references/contract.md index 129ff86b6e32..6f64607bcbca 100644 --- a/skills/prisma-8/references/contract.md +++ b/skills/prisma-8/references/contract.md @@ -117,7 +117,7 @@ model Post { Then run `pnpm prisma contract emit` (or rely on the Vite plugin — see `references/build.md`). Specify cascade behaviour explicitly with `onDelete` / `onUpdate`; the default is `Restrict`. -**Temporal columns.** On PostgreSQL, `DateTemporal`, `TimestampTemporal(p)`, `TimestampInstant(p)`, `TimestamptzTemporal(p)` and `TimeTemporal(p)` (and `Date`, `DateTime` and `Time`, which today mean the same as `DateTemporal`, `TimestamptzTemporal` and `TimeTemporal`) read and write `Temporal` values (`Temporal.PlainDate`, `PlainDateTime`, `Instant` for both instant types, `PlainTime`), never JavaScript `Date`. They need a global `Temporal` at query time: Node.js 26.8.2 and later ship `globalThis.Temporal`; 26.8.1 and earlier — including every 22 and 24 release — do not, and the first read or write of such a column throws `RUNTIME.TEMPORAL_UNAVAILABLE`. On those runtimes either `import 'temporal-polyfill/full/global'` before the first query (add `temporal-polyfill` as a dependency) or author the column as `DateString` / `TimestampString(p)` / `TimestamptzString(p)` / `TimeString(p)`, which carry text and need no `Temporal`, or as `TimestamptzJsDate(p)` / `TimestampJsDate(p)`, which read a JavaScript `Date`. `IntervalParts(p)` reads `{ months, days, micros }` and `IntervalString(p)` an ISO 8601 duration such as `P1Y2M`. `prisma contract emit`, `prisma db init` and the other commands need no global `Temporal`: the Postgres target loads `temporal-polyfill` as a fallback `Temporal` for the control plane, and sets no global. `temporal-polyfill` is a required peer dependency of `@prisma/orm-postgres`. npm, pnpm and bun install it automatically. With Yarn, add `temporal-polyfill` (`^1.0.4`) to the project's dependencies, or the commands fail because Node.js cannot find the package. That fallback is held once per process. An application that loads control-plane code in its own process, such as server code under `vite dev` with the Prisma Vite plugin or a script that calls the control client, decodes dates without its own `Temporal`, and then fails in production. An application that uses the Temporal codecs must load its own `Temporal`. A TypeScript contract file that constructs a `Temporal` value must load an implementation too. +**Temporal columns.** On PostgreSQL, `DateTemporal`, `TimestampTemporal(p)`, `TimestampInstant(p)`, `TimestamptzTemporal(p)` and `TimeTemporal(p)` (and `Date`, `DateTime` and `Time`, which today mean the same as `DateTemporal`, `TimestamptzTemporal` and `TimeTemporal`) read and write `Temporal` values (`Temporal.PlainDate`, `PlainDateTime`, `Instant` for both instant types, `PlainTime`), never JavaScript `Date`. They need a global `Temporal` at query time: Node.js 26.8.2 and later ship `globalThis.Temporal`; 26.8.1 and earlier — including every 22 and 24 release — do not, and the first read or write of such a column throws `RUNTIME.TEMPORAL_UNAVAILABLE`. On those runtimes either `import 'temporal-polyfill/full/global'` before the first query (add `temporal-polyfill` as a dependency) or author the column as `DateString` / `TimestampString(p)` / `TimestamptzString(p)` / `TimeString(p)`, which carry ISO 8601 text, the same on every server (`2024-01-01T00:00:00Z` for a `timestamptz`, the instant in UTC), and need no `Temporal`, or as `TimestamptzJsDate(p)` / `TimestampJsDate(p)`, which read a JavaScript `Date`. `IntervalParts(p)` reads `{ months, days, micros }` and `IntervalString(p)` an ISO 8601 duration such as `P1Y2M`. `prisma contract emit`, `prisma db init` and the other commands need no global `Temporal`: the Postgres target loads `temporal-polyfill` as a fallback `Temporal` for the control plane, and sets no global. `temporal-polyfill` is a required peer dependency of `@prisma/orm-postgres`. npm, pnpm and bun install it automatically. With Yarn, add `temporal-polyfill` (`^1.0.4`) to the project's dependencies, or the commands fail because Node.js cannot find the package. That fallback is held once per process. An application that loads control-plane code in its own process, such as server code under `vite dev` with the Prisma Vite plugin or a script that calls the control client, decodes dates without its own `Temporal`, and then fails in production. An application that uses the Temporal codecs must load its own `Temporal`. A TypeScript contract file that constructs a `Temporal` value must load an implementation too. `@@index` also accepts `expression:` (instead of a fields list), `where:` (partial-index predicate), `unique:`, `type:`/`options:` (target-registered access method), and `name:` xor `map:`. Raw SQL in `expression:` and `where:` is a `sql` literal; a quoted string is refused with `PSL_VALUE_TYPE_INCOMPATIBLE`, whose message gives the rewrite: @@ -339,7 +339,7 @@ model User { **Reading members at runtime (`db.enums`).** `db.enums..` on Postgres, and `db.enums.` on SQLite and Mongo, holds each member as a query returns it: a `pg/int8@1` member written `Low = "1"` is `1n`, a date or timestamp member is the `Date` or Temporal value its codec reads, and a float member written `"NaN"` is `NaN`. So `db.enums.public.Level.members.Low === row.level` holds wherever `===` applies. `has(value)`, `nameOf(value)` and `ordinalOf(value)` find a value equal to a member: a string, number or bigint must be the member itself, and an object such as a `Date` must be of the member's kind and stored as the member is, so a date equal to a member matches although it is a different object. Text a codec would normalize, such as an upper-case uuid, is no member. Each read of a mutable member, such as a `Date` or a `Uint8Array`, returns a fresh copy, so changing it does not change the enum; a Temporal member is immutable and is the same value on every read. An enum's members are decoded when the enum is first read, so a client builds without `Temporal` even when its contract has a Temporal enum. `contract.d.ts` types each member as that same value. The rule behind this: the value a codec reads from the contract's stored form equals the value a query returns for it. The stored form itself can differ from the text the database prints, such as ISO 8601 for a timestamp. -**Codecs an enum cannot use.** An enum compares values with its members, so a codec whose values cannot be compared for equality, or whose values read back never equal a member as the contract stores it, is refused, in TypeScript with `CONTRACT.ENUM_INVALID` and in PSL with `PSL_EXTENSION_INVALID_VALUE` at the `@@type`. The message says why and what to use instead. On Postgres: `pg/timestamp-string@1` and `pg/timestamptz-string@1` (use `pg/timestamp-temporal@1` or `pg/timestamptz-temporal@1`), `pg/json@1` (use `pg/jsonb@1`), and `pg/bytea@1` and `pg/tsquery@1`, for which no enum is possible (use a text enum). +**Codecs an enum cannot use.** An enum compares values with its members, so a codec whose values cannot be compared for equality, or whose values read back never equal a member as the contract stores it, is refused, in TypeScript with `CONTRACT.ENUM_INVALID` and in PSL with `PSL_EXTENSION_INVALID_VALUE` at the `@@type`. The message says why and what to use instead. On Postgres: `pg/json@1`, `pg/jsonb@1`, `pg/bytea@1` and `pg/tsquery@1` (use a text enum). The text date and time codecs read their values in ISO 8601, so an enum can use them. **Mongo enums.** A Mongo contract's collection validator lists each enum's members in their stored JSON forms, so an enum needs a codec whose BSON type JSON holds: string, int, double, bool, object or array, with finite numbers for doubles. PSL and TypeScript contracts derive the same validator. An enum over `mongo/int64@1`, `mongo/date@1` or `mongo/objectId@1` is refused: with `PSL_EXTENSION_INVALID_VALUE` in PSL, and with `CONTRACT.ENUM_INVALID` by `defineContract`. diff --git a/skills/prisma-8/references/debug.md b/skills/prisma-8/references/debug.md index e1cd66468bf8..e5d8480553fa 100644 --- a/skills/prisma-8/references/debug.md +++ b/skills/prisma-8/references/debug.md @@ -110,7 +110,7 @@ The single source of truth: read the envelope, find the row by `code`, follow th | `BUDGET.ROWS_EXCEEDED` / `BUDGET.TIME_EXCEEDED` | Runtime, when the `budgets` middleware is active | Tune `budgets({ maxRows, maxLatencyMs, ... })` or rewrite the query. See `references/runtime.md`. | | `LINT.SELECT_STAR` / `LINT.NO_LIMIT` / `LINT.DELETE_WITHOUT_WHERE` / `LINT.UPDATE_WITHOUT_WHERE` / `LINT.READ_ONLY_MUTATION` | Runtime, when the `lints` middleware is active | Fix the query (add a `WHERE` / `LIMIT` / explicit columns), or relax the lint config. See `references/runtime.md`. | | `PLAN.HASH_MISMATCH` | Runtime, executing a precompiled plan | The contract the plan was built against does not match the runtime contract. Re-emit, rebuild, redeploy. | -| `RUNTIME.TEMPORAL_UNAVAILABLE` | Runtime, first read or write touching a Temporal-backed column (`DateTemporal`, `TimestampTemporal(p)`, `TimestampInstant(p)`, `TimestamptzTemporal(p)`, `TimeTemporal(p)`, and `Date`, `DateTime` and `Time`) or a `temporal.updatedAt()` clock | No global `Temporal`. Node.js 26.8.2 and later ship `globalThis.Temporal`; 26.8.1 and earlier — including every 22 and 24 — do not. Either `import 'temporal-polyfill/full/global'` before the first query, or author the column as `DateString` / `TimestampString(p)` / `TimestamptzString(p)` / `TimeString(p)` to read PostgreSQL's own text. | +| `RUNTIME.TEMPORAL_UNAVAILABLE` | Runtime, first read or write touching a Temporal-backed column (`DateTemporal`, `TimestampTemporal(p)`, `TimestampInstant(p)`, `TimestamptzTemporal(p)`, `TimeTemporal(p)`, and `Date`, `DateTime` and `Time`) or a `temporal.updatedAt()` clock | No global `Temporal`. Node.js 26.8.2 and later ship `globalThis.Temporal`; 26.8.1 and earlier — including every 22 and 24 — do not. Either `import 'temporal-polyfill/full/global'` before the first query, or author the column as `DateString` / `TimestampString(p)` / `TimestamptzString(p)` / `TimeString(p)` to read ISO 8601 text. | | `ORM.RELATION_MUTATION_UNSUPPORTED` | ORM nested `create` / `connect` on an N:M relation whose junction has required payload columns | Write the junction table directly or use the SQL builder (`meta.junction`). | | `RUNTIME.ABORTED` (`details.phase` = `encode\|decode\|stream\|beforeEncode\|onRow`) | Runtime, when an `AbortSignal` fires mid-execute | Cancellation, not a bug; surface to the caller. | | `SqlQueryError` (no dotted code) | Raw-SQL paths surfacing a driver error | Inspect `sqlState` + `constraint` + `table` + `column`. Postgres `23505` = unique violation, `23503` = foreign-key violation, etc. Fix the data or the schema. | diff --git a/test/e2e/framework/test/dml.test.ts b/test/e2e/framework/test/dml.test.ts index 2a44be2c6941..05a94b8bce4e 100644 --- a/test/e2e/framework/test/dml.test.ts +++ b/test/e2e/framework/test/dml.test.ts @@ -89,7 +89,7 @@ describe('DML E2E Tests - UUIDv7 client-generated IDs', { timeout: 30000 }, () = id: expect.stringMatching(UUIDV7_REGEX), name: 'uuidv7-test-event', created_at: expect.any(Temporal.Instant), - scheduled_at: '2024-01-15 10:30:00+00', + scheduled_at: '2024-01-15T10:30:00Z', }); }); }); diff --git a/test/e2e/framework/test/fixtures/contract.ts b/test/e2e/framework/test/fixtures/contract.ts index 9e39e96f901c..f87134e59e3b 100644 --- a/test/e2e/framework/test/fixtures/contract.ts +++ b/test/e2e/framework/test/fixtures/contract.ts @@ -116,7 +116,7 @@ export const contract = defineContract( name: field.column(textColumn), scheduledAt: field .column(timestamptzStringColumn) - .default({ kind: 'literal', value: '2024-01-15 10:30:00+00' }) + .default({ kind: 'literal', value: '2024-01-15T10:30:00Z' }) .column('scheduled_at'), createdAt: field.column(timestamptzTemporalColumn).default(now()).column('created_at'), }, diff --git a/test/integration/test/authoring/psl-defaults-read-by-codec.integration.test.ts b/test/integration/test/authoring/psl-defaults-read-by-codec.integration.test.ts index 1f18e2e2f002..9dde7525aaa1 100644 --- a/test/integration/test/authoring/psl-defaults-read-by-codec.integration.test.ts +++ b/test/integration/test/authoring/psl-defaults-read-by-codec.integration.test.ts @@ -273,18 +273,6 @@ model Task { 'PSL_VALUE_TYPE_INCOMPATIBLE', 'enum "Priority" member "Low": Expected a number that pg/int4 can hold; got pg/numeric', ], - [ - 'a string timestamp codec, which an enum cannot use', - ' @@type("pg/timestamp-string@1")\n Low = "2024-01-02T03:04:05"', - 'PSL_EXTENSION_INVALID_VALUE', - 'enum "Priority" cannot use the codec "pg/timestamp-string@1". A query reads each value as the text PostgreSQL prints, such as "2024-01-02 03:04:05", while the contract stores it in ISO 8601, such as "2024-01-02T03:04:05", so no value read back equals a member. Use pg/timestamp-temporal@1, whose members are Temporal values.', - ], - [ - 'a timestamptz string codec, which an enum cannot use', - ' @@type("pg/timestamptz-string@1")\n Low = "2024-01-02T03:04:05Z"', - 'PSL_EXTENSION_INVALID_VALUE', - `enum "Priority" cannot use the codec "pg/timestamptz-string@1". A query reads each value as the text PostgreSQL prints in the session's time zone, such as "2024-01-02 03:04:05+00", while the contract stores it in ISO 8601, such as "2024-01-02T03:04:05Z", so no value read back equals a member. Use pg/timestamptz-temporal@1, whose members are Temporal values.`, - ], [ 'a bytea codec, which an enum cannot use', ' @@type("pg/bytea@1")\n Low = "AQI="', @@ -309,17 +297,24 @@ model Task { 'PSL_EXTENSION_INVALID_VALUE', 'enum "Priority" cannot use the codec "pg/jsonb@1". A CHECK lists a string member as SQL text, which PostgreSQL reads as the JSON text of a document rather than as a string, and a number member as a number, which PostgreSQL cannot compare with jsonb, so no CHECK can compare a value with the members. No enum can use a jsonb codec; use a text enum instead.', ], - [ - 'a timetz codec, which an enum cannot use', - ' @@type("pg/timetz@1")\n Low = "12:34:56+02:00"', - 'PSL_EXTENSION_INVALID_VALUE', - 'enum "Priority" cannot use the codec "pg/timetz@1". A query reads each value as the text PostgreSQL prints, such as "12:34:56+02", while the contract stores it in ISO 8601, such as "12:34:56+02:00", so a value read back need not equal the member written. No enum can use a timetz codec; use a text enum instead.', - ], ])('refuses %s', async (_name, members, code, message) => { expect(await diagnosticsOf(enumOf(members))).toEqual([{ code, message }]); }); }); + describe('an enum member of a string date or time codec', () => { + it.each([ + ['pg/timestamp-string@1', 'timestamp`2024-01-02 03:04:05`', '2024-01-02T03:04:05'], + ['pg/timestamptz-string@1', 'timestamptz`2024-01-02 04:04:05+01`', '2024-01-02T03:04:05Z'], + ['pg/timetz@1', 'timetz`12:34:56+02`', '12:34:56+02:00'], + ])( + 'is accepted by %s and %s is stored in its ISO 8601 form %s', + async (codecId, member, stored) => { + expect(await storedEnumValues(codecId, [`A = ${member}`])).toEqual({ values: [stored] }); + }, + ); + }); + describe('an enum member written as a number literal, read from its source text', () => { it('stores a pg/int8@1 member past 2^53 with every digit, so the integer below it is not a duplicate', async () => { expect( diff --git a/test/integration/test/cli-journeys/prisma7-dates-without-temporal.e2e.test.ts b/test/integration/test/cli-journeys/prisma7-dates-without-temporal.e2e.test.ts index 280ef17bc37f..91f48629d210 100644 --- a/test/integration/test/cli-journeys/prisma7-dates-without-temporal.e2e.test.ts +++ b/test/integration/test/cli-journeys/prisma7-dates-without-temporal.e2e.test.ts @@ -121,9 +121,9 @@ const DATE_COLUMN_CODECS = { opensAt: 'pg/time-string@1', }; -/** The written values, each in the text PostgreSQL prints for it, so a read returns it unchanged. */ +/** The written values, each in its ISO 8601 stored form, so a read returns it unchanged. */ const WRITTEN = { - happensAt: '2024-01-15 10:00:00.123456+00', + happensAt: '2024-01-15T10:00:00.123456Z', day: '2024-01-15', opensAt: '08:30:15.25', }; @@ -153,7 +153,7 @@ function output(run: EngineCommandResult): string { } function utcMillis(timestampText: string): number { - return new Date(`${timestampText.replace(' ', 'T')}Z`).getTime(); + return new Date(`${timestampText}Z`).getTime(); } function expectNow(timestampText: string, before: number, after: number): void { @@ -241,15 +241,15 @@ withTempDir(({ createTempDir }) => { expect(sign.exitCode, `db sign\n${output(sign)}`).toBe(0); const { result, before, after } = await runApp(ctx, db.connectionString, { - create: { title: 'a', updatedAt: '2024-01-15 09:00:00.5', ...WRITTEN }, - update: { title: 'b', updatedAt: '2024-01-15 11:00:00.25' }, + create: { title: 'a', updatedAt: '2024-01-15T09:00:00.5', ...WRITTEN }, + update: { title: 'b', updatedAt: '2024-01-15T11:00:00.25' }, }); expect(result.created).toEqual({ id: result.created.id, title: 'a', createdAt: result.created.createdAt, - updatedAt: '2024-01-15 09:00:00.5', + updatedAt: '2024-01-15T09:00:00.5', ...WRITTEN, }); expectNow(result.created.createdAt, before, after); @@ -257,7 +257,7 @@ withTempDir(({ createTempDir }) => { expect(result.updated).toEqual({ ...result.created, title: 'b', - updatedAt: '2024-01-15 11:00:00.25', + updatedAt: '2024-01-15T11:00:00.25', }); expect(result.readAfterUpdate).toEqual(result.updated); }, diff --git a/test/integration/test/contract-parity/fixtures/postgres/generated/contract.d.ts b/test/integration/test/contract-parity/fixtures/postgres/generated/contract.d.ts index 278e9edeeaa5..c9f5bae1c1b6 100644 --- a/test/integration/test/contract-parity/fixtures/postgres/generated/contract.d.ts +++ b/test/integration/test/contract-parity/fixtures/postgres/generated/contract.d.ts @@ -26,7 +26,7 @@ import type { export type StorageHash = StorageHashBase<'4e5aa929c76c9181dd640f59692e6743a0626b5ab9023a02e5960ec96509c225'>; export type ExecutionHash = - ExecutionHashBase<'4c8b52aa880ed951466684d8a101a1fc3992151da635fe969fe2926dbd8964eb'>; + ExecutionHashBase<'3212cd023cdce9916f261da80be0ba8357cd4263dde560c3b8164673d80e9d4b'>; export type ProfileHash = ProfileHashBase<'3916f444a8a17ad749191acf9e08dad97d1a327b88c2f1d45d12f240296aa8b2'>; @@ -2250,8 +2250,16 @@ type ContractBase = Omit< }; }, { - readonly onCreate: { readonly id: 'timestamptzStringNow'; readonly kind: 'generator' }; - readonly onUpdate: { readonly id: 'timestamptzStringNow'; readonly kind: 'generator' }; + readonly onCreate: { + readonly id: 'timestamptzStringNow'; + readonly kind: 'generator'; + readonly params: { readonly precision: 3 }; + }; + readonly onUpdate: { + readonly id: 'timestamptzStringNow'; + readonly kind: 'generator'; + readonly params: { readonly precision: 3 }; + }; readonly ref: { readonly entry: 'users'; readonly field: 'timestamptzString'; diff --git a/test/integration/test/contract-parity/fixtures/postgres/generated/contract.json b/test/integration/test/contract-parity/fixtures/postgres/generated/contract.json index 151008ad4ecf..230eecb4ff9a 100644 --- a/test/integration/test/contract-parity/fixtures/postgres/generated/contract.json +++ b/test/integration/test/contract-parity/fixtures/postgres/generated/contract.json @@ -2127,7 +2127,7 @@ "storageHash": "4e5aa929c76c9181dd640f59692e6743a0626b5ab9023a02e5960ec96509c225" }, "execution": { - "executionHash": "4c8b52aa880ed951466684d8a101a1fc3992151da635fe969fe2926dbd8964eb", + "executionHash": "3212cd023cdce9916f261da80be0ba8357cd4263dde560c3b8164673d80e9d4b", "mutations": { "defaults": [ { @@ -2356,11 +2356,17 @@ { "onCreate": { "id": "timestamptzStringNow", - "kind": "generator" + "kind": "generator", + "params": { + "precision": 3 + } }, "onUpdate": { "id": "timestamptzStringNow", - "kind": "generator" + "kind": "generator", + "params": { + "precision": 3 + } }, "ref": { "entry": "users", diff --git a/test/integration/test/enum-members/enum-round-trip.postgres.test.ts b/test/integration/test/enum-members/enum-round-trip.postgres.test.ts index 0e90bd21d0d1..84c726f3a519 100644 --- a/test/integration/test/enum-members/enum-round-trip.postgres.test.ts +++ b/test/integration/test/enum-members/enum-round-trip.postgres.test.ts @@ -51,6 +51,7 @@ const storedMembers: Readonly> = { 'pg/timestamp': '2024-01-02T03:04:05.5', 'pg/timestamptz': '2024-01-02T03:04:05.5Z', 'pg/time': '03:04:05.5', + 'pg/timetz': '03:04:05.5+02:00', 'pg/bool': true, 'pg/bit': '1', 'pg/varbit': '101', diff --git a/test/integration/test/fixtures/cli/cli-e2e-test-app/fixtures/cli-journeys/contract-date-time-defaults.ts b/test/integration/test/fixtures/cli/cli-e2e-test-app/fixtures/cli-journeys/contract-date-time-defaults.ts index ce27258a6b8c..e566db38b866 100644 --- a/test/integration/test/fixtures/cli/cli-e2e-test-app/fixtures/cli-journeys/contract-date-time-defaults.ts +++ b/test/integration/test/fixtures/cli/cli-e2e-test-app/fixtures/cli-journeys/contract-date-time-defaults.ts @@ -16,11 +16,11 @@ export const contract = defineContract({ fields: { id: field.column(int4Column).id(), jsDate: field.column(timestamptzJsDateColumn).default(new Date('2024-01-01T00:00:00.000Z')), - instantText: field.column(timestamptzStringColumn).default('2024-01-01 00:00:00+00'), - localText: field.column(timestampStringColumn).default('2024-01-01 12:34:56.500'), - dayText: field.column(dateStringColumn).default('0044-03-15 BC'), - clockText: field.column(timeStringColumn()).default('12:34:56.500'), - zoned: field.column(timetzColumn()).default('12:34:56+02'), + instantText: field.column(timestamptzStringColumn).default('2024-01-01T00:00:00Z'), + localText: field.column(timestampStringColumn).default('2024-01-01T12:34:56.5'), + dayText: field.column(dateStringColumn).default('-000043-03-15'), + clockText: field.column(timeStringColumn()).default('12:34:56.5'), + zoned: field.column(timetzColumn()).default('12:34:56+02:00'), span: field .column(intervalPartsColumn()) .default({ months: 14, days: 3, micros: 14706500000n }), diff --git a/test/integration/test/infer-roundtrip-runtime.integration.test.ts b/test/integration/test/infer-roundtrip-runtime.integration.test.ts index 2d42031a99ff..a3ffb5cd9817 100644 --- a/test/integration/test/infer-roundtrip-runtime.integration.test.ts +++ b/test/integration/test/infer-roundtrip-runtime.integration.test.ts @@ -163,7 +163,7 @@ withTempDir(({ createTempDir }) => { ); }); - describe('Runtime: inferred date and time columns decode as PostgreSQL text in both top-level select and include()', () => { + describe('Runtime: inferred date and time columns decode as ISO 8601 in both top-level select and include()', () => { let database: Awaited>; beforeAll(async () => { @@ -193,14 +193,14 @@ withTempDir(({ createTempDir }) => { }, timeouts.spinUpPpgDev); it( - 'top-level select and include() both decode the date and time columns', + 'top-level select and include() both decode the date and time columns, in any session time zone, and refuse another DateStyle', async () => { const ctx = await inferAndEmit(database.connectionString, createTempDir); const contract = readEmittedContract(ctx); const stored = { notedOn: '2024-01-15', - loggedAt: '2024-01-15 10:00:00.5', - seenAt: '2024-01-15 10:00:00+00', + loggedAt: '2024-01-15T10:00:00.5', + seenAt: '2024-01-15T10:00:00Z', opensAt: '08:30:00', }; @@ -231,26 +231,40 @@ withTempDir(({ createTempDir }) => { const records = new Collection({ runtime, context }, 'Record', { namespaceId: 'public', }); - const rows = await records - .select('id', 'notedOn', 'loggedAt', 'seenAt', 'opensAt') - .all(); - expect(rows).toEqual([{ id: 1, ...stored }]); - const owners = new Collection({ runtime, context }, 'Owner', { namespaceId: 'public', }); - const includeResult = await owners - .select('id') - // `contract` is a dynamically-introspected `Contract` - // with no literal domain shape (it doesn't exist until this test - // runs infer + emit), so `include`'s relation-name type inference - // has nothing to key off; test files are exempt from the - // no-bare-casts rule. - .include('records' as never, (record) => - record.select('notedOn', 'loggedAt', 'seenAt', 'opensAt'), - ) - .all(); - expect(includeResult).toEqual([{ id: 1, records: [stored] }]); + const flat = () => + records.select('id', 'notedOn', 'loggedAt', 'seenAt', 'opensAt').all(); + const included = () => + owners + .select('id') + // `contract` is a dynamically-introspected `Contract` + // with no literal domain shape (it doesn't exist until this test + // runs infer + emit), so `include`'s relation-name type inference + // has nothing to key off; test files are exempt from the + // no-bare-casts rule. + .include('records' as never, (record) => + record.select('notedOn', 'loggedAt', 'seenAt', 'opensAt'), + ) + .all(); + const expected = { + flat: [{ id: 1, ...stored }], + included: [{ id: 1, records: [stored] }], + }; + + expect({ flat: await flat(), included: await included() }).toEqual(expected); + + await client.query("SET TimeZone = 'Asia/Tokyo'"); + expect({ flat: await flat(), included: await included() }).toEqual(expected); + + await client.query("SET DateStyle = 'German, DMY'"); + const refusal = { code: 'RUNTIME.DATE_STYLE_NOT_ISO' }; + await expect(flat()).rejects.toMatchObject(refusal); + await expect(included()).rejects.toMatchObject({ + code: 'RUNTIME.DECODE_FAILED', + cause: refusal, + }); } finally { await runtime.close(); } diff --git a/test/integration/test/temporal-defaults/_fixture-attributes/contract.prisma b/test/integration/test/temporal-defaults/_fixture-attributes/contract.prisma index 7ee8ec4e8baa..2b67697fa903 100644 --- a/test/integration/test/temporal-defaults/_fixture-attributes/contract.prisma +++ b/test/integration/test/temporal-defaults/_fixture-attributes/contract.prisma @@ -24,6 +24,8 @@ model Clock { timestamptz3 TimestamptzTemporal(3) @onCreate(now()) @onUpdate(now()) timestamptz6 TimestamptzTemporal(6) @onCreate(now()) @onUpdate(now()) timestamptzString0 TimestamptzString(0) @onCreate(now()) @onUpdate(now()) + timestamptzString1 TimestamptzString(1) @onCreate(now()) @onUpdate(now()) + timestamptzString2 TimestamptzString(2) @onCreate(now()) @onUpdate(now()) timestamptzString3 TimestamptzString(3) @onCreate(now()) @onUpdate(now()) timestamptzString6 TimestamptzString(6) @onCreate(now()) @onUpdate(now()) timestamptzJsDate0 TimestamptzJsDate(0) @onCreate(now()) @onUpdate(now()) diff --git a/test/integration/test/temporal-defaults/_fixture-attributes/generated/contract.d.ts b/test/integration/test/temporal-defaults/_fixture-attributes/generated/contract.d.ts index 186a16dccec4..3b6b863b22c5 100644 --- a/test/integration/test/temporal-defaults/_fixture-attributes/generated/contract.d.ts +++ b/test/integration/test/temporal-defaults/_fixture-attributes/generated/contract.d.ts @@ -23,9 +23,9 @@ import type { } from '@internal/contract/types'; export type StorageHash = - StorageHashBase<'cedcd9ab983b876215346a9eca6de6c41fe9fd966787f634bd330256c1e2508c'>; + StorageHashBase<'e912a0ac1c484ed92ae8cb217638a889ad671c1f4a9bfb3e469ebfb14916c6dd'>; export type ExecutionHash = - ExecutionHashBase<'c3a3387d2ba8c172f3b31c9a79cedb29984f1d49e22fd98046b9d41f1fb09e76'>; + ExecutionHashBase<'51be7e21b1611b41897bee6131dc8d5bfc6abd6380ca0bd6ba5a86d1c2f479c4'>; export type ProfileHash = ProfileHashBase<'3916f444a8a17ad749191acf9e08dad97d1a327b88c2f1d45d12f240296aa8b2'>; @@ -326,6 +326,8 @@ export namespace Models { timestamptzJsDate3: FieldOutputTypes['public']['Clock']['timestamptzJsDate3']; timestamptzJsDate6: FieldOutputTypes['public']['Clock']['timestamptzJsDate6']; timestamptzString0: FieldOutputTypes['public']['Clock']['timestamptzString0']; + timestamptzString1: FieldOutputTypes['public']['Clock']['timestamptzString1']; + timestamptzString2: FieldOutputTypes['public']['Clock']['timestamptzString2']; timestamptzString3: FieldOutputTypes['public']['Clock']['timestamptzString3']; timestamptzString6: FieldOutputTypes['public']['Clock']['timestamptzString6']; timetz0: FieldOutputTypes['public']['Clock']['timetz0']; @@ -518,6 +520,20 @@ type ContractBase = Omit< readonly typeParams: { readonly precision: 0 }; readonly many: false; }; + readonly timestamptzString1: { + readonly dataType: 'pg/timestamptz'; + readonly codecId: 'pg/timestamptz-string@1'; + readonly nullable: false; + readonly typeParams: { readonly precision: 1 }; + readonly many: false; + }; + readonly timestamptzString2: { + readonly dataType: 'pg/timestamptz'; + readonly codecId: 'pg/timestamptz-string@1'; + readonly nullable: false; + readonly typeParams: { readonly precision: 2 }; + readonly many: false; + }; readonly timestamptzString3: { readonly dataType: 'pg/timestamptz'; readonly codecId: 'pg/timestamptz-string@1'; @@ -746,6 +762,22 @@ type ContractBase = Omit< readonly typeParams: { readonly precision: 0 }; }; }; + readonly timestamptzString1: { + readonly nullable: false; + readonly type: { + readonly kind: 'scalar'; + readonly codecId: 'pg/timestamptz-string@1'; + readonly typeParams: { readonly precision: 1 }; + }; + }; + readonly timestamptzString2: { + readonly nullable: false; + readonly type: { + readonly kind: 'scalar'; + readonly codecId: 'pg/timestamptz-string@1'; + readonly typeParams: { readonly precision: 2 }; + }; + }; readonly timestamptzString3: { readonly nullable: false; readonly type: { @@ -815,6 +847,8 @@ type ContractBase = Omit< readonly timestamptzJsDate3: { readonly column: 'timestamptzJsDate3' }; readonly timestamptzJsDate6: { readonly column: 'timestamptzJsDate6' }; readonly timestamptzString0: { readonly column: 'timestamptzString0' }; + readonly timestamptzString1: { readonly column: 'timestamptzString1' }; + readonly timestamptzString2: { readonly column: 'timestamptzString2' }; readonly timestamptzString3: { readonly column: 'timestamptzString3' }; readonly timestamptzString6: { readonly column: 'timestamptzString6' }; readonly timetz0: { readonly column: 'timetz0' }; @@ -1007,8 +1041,16 @@ type ContractBase = Omit< }; }, { - readonly onCreate: { readonly id: 'timestampStringNow'; readonly kind: 'generator' }; - readonly onUpdate: { readonly id: 'timestampStringNow'; readonly kind: 'generator' }; + readonly onCreate: { + readonly id: 'timestampStringNow'; + readonly kind: 'generator'; + readonly params: { readonly precision: 0 }; + }; + readonly onUpdate: { + readonly id: 'timestampStringNow'; + readonly kind: 'generator'; + readonly params: { readonly precision: 0 }; + }; readonly ref: { readonly entry: 'clock'; readonly field: 'timestampString0'; @@ -1016,8 +1058,16 @@ type ContractBase = Omit< }; }, { - readonly onCreate: { readonly id: 'timestampStringNow'; readonly kind: 'generator' }; - readonly onUpdate: { readonly id: 'timestampStringNow'; readonly kind: 'generator' }; + readonly onCreate: { + readonly id: 'timestampStringNow'; + readonly kind: 'generator'; + readonly params: { readonly precision: 3 }; + }; + readonly onUpdate: { + readonly id: 'timestampStringNow'; + readonly kind: 'generator'; + readonly params: { readonly precision: 3 }; + }; readonly ref: { readonly entry: 'clock'; readonly field: 'timestampString3'; @@ -1025,8 +1075,16 @@ type ContractBase = Omit< }; }, { - readonly onCreate: { readonly id: 'timestampStringNow'; readonly kind: 'generator' }; - readonly onUpdate: { readonly id: 'timestampStringNow'; readonly kind: 'generator' }; + readonly onCreate: { + readonly id: 'timestampStringNow'; + readonly kind: 'generator'; + readonly params: { readonly precision: 6 }; + }; + readonly onUpdate: { + readonly id: 'timestampStringNow'; + readonly kind: 'generator'; + readonly params: { readonly precision: 6 }; + }; readonly ref: { readonly entry: 'clock'; readonly field: 'timestampString6'; @@ -1088,8 +1146,16 @@ type ContractBase = Omit< }; }, { - readonly onCreate: { readonly id: 'timestamptzStringNow'; readonly kind: 'generator' }; - readonly onUpdate: { readonly id: 'timestamptzStringNow'; readonly kind: 'generator' }; + readonly onCreate: { + readonly id: 'timestamptzStringNow'; + readonly kind: 'generator'; + readonly params: { readonly precision: 0 }; + }; + readonly onUpdate: { + readonly id: 'timestamptzStringNow'; + readonly kind: 'generator'; + readonly params: { readonly precision: 0 }; + }; readonly ref: { readonly entry: 'clock'; readonly field: 'timestamptzString0'; @@ -1097,8 +1163,50 @@ type ContractBase = Omit< }; }, { - readonly onCreate: { readonly id: 'timestamptzStringNow'; readonly kind: 'generator' }; - readonly onUpdate: { readonly id: 'timestamptzStringNow'; readonly kind: 'generator' }; + readonly onCreate: { + readonly id: 'timestamptzStringNow'; + readonly kind: 'generator'; + readonly params: { readonly precision: 1 }; + }; + readonly onUpdate: { + readonly id: 'timestamptzStringNow'; + readonly kind: 'generator'; + readonly params: { readonly precision: 1 }; + }; + readonly ref: { + readonly entry: 'clock'; + readonly field: 'timestamptzString1'; + readonly namespace: 'public'; + }; + }, + { + readonly onCreate: { + readonly id: 'timestamptzStringNow'; + readonly kind: 'generator'; + readonly params: { readonly precision: 2 }; + }; + readonly onUpdate: { + readonly id: 'timestamptzStringNow'; + readonly kind: 'generator'; + readonly params: { readonly precision: 2 }; + }; + readonly ref: { + readonly entry: 'clock'; + readonly field: 'timestamptzString2'; + readonly namespace: 'public'; + }; + }, + { + readonly onCreate: { + readonly id: 'timestamptzStringNow'; + readonly kind: 'generator'; + readonly params: { readonly precision: 3 }; + }; + readonly onUpdate: { + readonly id: 'timestamptzStringNow'; + readonly kind: 'generator'; + readonly params: { readonly precision: 3 }; + }; readonly ref: { readonly entry: 'clock'; readonly field: 'timestamptzString3'; @@ -1106,8 +1214,16 @@ type ContractBase = Omit< }; }, { - readonly onCreate: { readonly id: 'timestamptzStringNow'; readonly kind: 'generator' }; - readonly onUpdate: { readonly id: 'timestamptzStringNow'; readonly kind: 'generator' }; + readonly onCreate: { + readonly id: 'timestamptzStringNow'; + readonly kind: 'generator'; + readonly params: { readonly precision: 6 }; + }; + readonly onUpdate: { + readonly id: 'timestamptzStringNow'; + readonly kind: 'generator'; + readonly params: { readonly precision: 6 }; + }; readonly ref: { readonly entry: 'clock'; readonly field: 'timestamptzString6'; diff --git a/test/integration/test/temporal-defaults/_fixture-attributes/generated/contract.json b/test/integration/test/temporal-defaults/_fixture-attributes/generated/contract.json index e4d0f1cba832..b857e938d057 100644 --- a/test/integration/test/temporal-defaults/_fixture-attributes/generated/contract.json +++ b/test/integration/test/temporal-defaults/_fixture-attributes/generated/contract.json @@ -233,6 +233,26 @@ } } }, + "timestamptzString1": { + "nullable": false, + "type": { + "codecId": "pg/timestamptz-string@1", + "kind": "scalar", + "typeParams": { + "precision": 1 + } + } + }, + "timestamptzString2": { + "nullable": false, + "type": { + "codecId": "pg/timestamptz-string@1", + "kind": "scalar", + "typeParams": { + "precision": 2 + } + } + }, "timestamptzString3": { "nullable": false, "type": { @@ -356,6 +376,12 @@ "timestamptzString0": { "column": "timestamptzString0" }, + "timestamptzString1": { + "column": "timestamptzString1" + }, + "timestamptzString2": { + "column": "timestamptzString2" + }, "timestamptzString3": { "column": "timestamptzString3" }, @@ -559,6 +585,22 @@ "precision": 0 } }, + "timestamptzString1": { + "codecId": "pg/timestamptz-string@1", + "dataType": "pg/timestamptz", + "nullable": false, + "typeParams": { + "precision": 1 + } + }, + "timestamptzString2": { + "codecId": "pg/timestamptz-string@1", + "dataType": "pg/timestamptz", + "nullable": false, + "typeParams": { + "precision": 2 + } + }, "timestamptzString3": { "codecId": "pg/timestamptz-string@1", "dataType": "pg/timestamptz", @@ -615,10 +657,10 @@ "kind": "postgres-schema" } }, - "storageHash": "cedcd9ab983b876215346a9eca6de6c41fe9fd966787f634bd330256c1e2508c" + "storageHash": "e912a0ac1c484ed92ae8cb217638a889ad671c1f4a9bfb3e469ebfb14916c6dd" }, "execution": { - "executionHash": "c3a3387d2ba8c172f3b31c9a79cedb29984f1d49e22fd98046b9d41f1fb09e76", + "executionHash": "51be7e21b1611b41897bee6131dc8d5bfc6abd6380ca0bd6ba5a86d1c2f479c4", "mutations": { "defaults": [ { @@ -825,11 +867,17 @@ { "onCreate": { "id": "timestampStringNow", - "kind": "generator" + "kind": "generator", + "params": { + "precision": 0 + } }, "onUpdate": { "id": "timestampStringNow", - "kind": "generator" + "kind": "generator", + "params": { + "precision": 0 + } }, "ref": { "entry": "clock", @@ -840,11 +888,17 @@ { "onCreate": { "id": "timestampStringNow", - "kind": "generator" + "kind": "generator", + "params": { + "precision": 3 + } }, "onUpdate": { "id": "timestampStringNow", - "kind": "generator" + "kind": "generator", + "params": { + "precision": 3 + } }, "ref": { "entry": "clock", @@ -855,11 +909,17 @@ { "onCreate": { "id": "timestampStringNow", - "kind": "generator" + "kind": "generator", + "params": { + "precision": 6 + } }, "onUpdate": { "id": "timestampStringNow", - "kind": "generator" + "kind": "generator", + "params": { + "precision": 6 + } }, "ref": { "entry": "clock", @@ -960,11 +1020,17 @@ { "onCreate": { "id": "timestamptzStringNow", - "kind": "generator" + "kind": "generator", + "params": { + "precision": 0 + } }, "onUpdate": { "id": "timestamptzStringNow", - "kind": "generator" + "kind": "generator", + "params": { + "precision": 0 + } }, "ref": { "entry": "clock", @@ -975,11 +1041,59 @@ { "onCreate": { "id": "timestamptzStringNow", - "kind": "generator" + "kind": "generator", + "params": { + "precision": 1 + } }, "onUpdate": { "id": "timestamptzStringNow", - "kind": "generator" + "kind": "generator", + "params": { + "precision": 1 + } + }, + "ref": { + "entry": "clock", + "field": "timestamptzString1", + "namespace": "public" + } + }, + { + "onCreate": { + "id": "timestamptzStringNow", + "kind": "generator", + "params": { + "precision": 2 + } + }, + "onUpdate": { + "id": "timestamptzStringNow", + "kind": "generator", + "params": { + "precision": 2 + } + }, + "ref": { + "entry": "clock", + "field": "timestamptzString2", + "namespace": "public" + } + }, + { + "onCreate": { + "id": "timestamptzStringNow", + "kind": "generator", + "params": { + "precision": 3 + } + }, + "onUpdate": { + "id": "timestamptzStringNow", + "kind": "generator", + "params": { + "precision": 3 + } }, "ref": { "entry": "clock", @@ -990,11 +1104,17 @@ { "onCreate": { "id": "timestamptzStringNow", - "kind": "generator" + "kind": "generator", + "params": { + "precision": 6 + } }, "onUpdate": { "id": "timestamptzStringNow", - "kind": "generator" + "kind": "generator", + "params": { + "precision": 6 + } }, "ref": { "entry": "clock", diff --git a/test/integration/test/temporal-defaults/_fixture-now/contract.prisma b/test/integration/test/temporal-defaults/_fixture-now/contract.prisma index e2175b486719..9391d3576d3d 100644 --- a/test/integration/test/temporal-defaults/_fixture-now/contract.prisma +++ b/test/integration/test/temporal-defaults/_fixture-now/contract.prisma @@ -24,6 +24,8 @@ model Clock { timestamptz3 temporal.timestamptz(3, onCreate: now, onUpdate: now) timestamptz6 temporal.timestamptz(6, onCreate: now, onUpdate: now) timestamptzString0 temporal.timestamptzString(0, onCreate: now, onUpdate: now) + timestamptzString1 temporal.timestamptzString(1, onCreate: now, onUpdate: now) + timestamptzString2 temporal.timestamptzString(2, onCreate: now, onUpdate: now) timestamptzString3 temporal.timestamptzString(3, onCreate: now, onUpdate: now) timestamptzString6 temporal.timestamptzString(6, onCreate: now, onUpdate: now) timestamptzJsDate0 temporal.timestamptzJsDate(0, onCreate: now, onUpdate: now) diff --git a/test/integration/test/temporal-defaults/_fixture-now/generated/contract.d.ts b/test/integration/test/temporal-defaults/_fixture-now/generated/contract.d.ts index 186a16dccec4..3b6b863b22c5 100644 --- a/test/integration/test/temporal-defaults/_fixture-now/generated/contract.d.ts +++ b/test/integration/test/temporal-defaults/_fixture-now/generated/contract.d.ts @@ -23,9 +23,9 @@ import type { } from '@internal/contract/types'; export type StorageHash = - StorageHashBase<'cedcd9ab983b876215346a9eca6de6c41fe9fd966787f634bd330256c1e2508c'>; + StorageHashBase<'e912a0ac1c484ed92ae8cb217638a889ad671c1f4a9bfb3e469ebfb14916c6dd'>; export type ExecutionHash = - ExecutionHashBase<'c3a3387d2ba8c172f3b31c9a79cedb29984f1d49e22fd98046b9d41f1fb09e76'>; + ExecutionHashBase<'51be7e21b1611b41897bee6131dc8d5bfc6abd6380ca0bd6ba5a86d1c2f479c4'>; export type ProfileHash = ProfileHashBase<'3916f444a8a17ad749191acf9e08dad97d1a327b88c2f1d45d12f240296aa8b2'>; @@ -326,6 +326,8 @@ export namespace Models { timestamptzJsDate3: FieldOutputTypes['public']['Clock']['timestamptzJsDate3']; timestamptzJsDate6: FieldOutputTypes['public']['Clock']['timestamptzJsDate6']; timestamptzString0: FieldOutputTypes['public']['Clock']['timestamptzString0']; + timestamptzString1: FieldOutputTypes['public']['Clock']['timestamptzString1']; + timestamptzString2: FieldOutputTypes['public']['Clock']['timestamptzString2']; timestamptzString3: FieldOutputTypes['public']['Clock']['timestamptzString3']; timestamptzString6: FieldOutputTypes['public']['Clock']['timestamptzString6']; timetz0: FieldOutputTypes['public']['Clock']['timetz0']; @@ -518,6 +520,20 @@ type ContractBase = Omit< readonly typeParams: { readonly precision: 0 }; readonly many: false; }; + readonly timestamptzString1: { + readonly dataType: 'pg/timestamptz'; + readonly codecId: 'pg/timestamptz-string@1'; + readonly nullable: false; + readonly typeParams: { readonly precision: 1 }; + readonly many: false; + }; + readonly timestamptzString2: { + readonly dataType: 'pg/timestamptz'; + readonly codecId: 'pg/timestamptz-string@1'; + readonly nullable: false; + readonly typeParams: { readonly precision: 2 }; + readonly many: false; + }; readonly timestamptzString3: { readonly dataType: 'pg/timestamptz'; readonly codecId: 'pg/timestamptz-string@1'; @@ -746,6 +762,22 @@ type ContractBase = Omit< readonly typeParams: { readonly precision: 0 }; }; }; + readonly timestamptzString1: { + readonly nullable: false; + readonly type: { + readonly kind: 'scalar'; + readonly codecId: 'pg/timestamptz-string@1'; + readonly typeParams: { readonly precision: 1 }; + }; + }; + readonly timestamptzString2: { + readonly nullable: false; + readonly type: { + readonly kind: 'scalar'; + readonly codecId: 'pg/timestamptz-string@1'; + readonly typeParams: { readonly precision: 2 }; + }; + }; readonly timestamptzString3: { readonly nullable: false; readonly type: { @@ -815,6 +847,8 @@ type ContractBase = Omit< readonly timestamptzJsDate3: { readonly column: 'timestamptzJsDate3' }; readonly timestamptzJsDate6: { readonly column: 'timestamptzJsDate6' }; readonly timestamptzString0: { readonly column: 'timestamptzString0' }; + readonly timestamptzString1: { readonly column: 'timestamptzString1' }; + readonly timestamptzString2: { readonly column: 'timestamptzString2' }; readonly timestamptzString3: { readonly column: 'timestamptzString3' }; readonly timestamptzString6: { readonly column: 'timestamptzString6' }; readonly timetz0: { readonly column: 'timetz0' }; @@ -1007,8 +1041,16 @@ type ContractBase = Omit< }; }, { - readonly onCreate: { readonly id: 'timestampStringNow'; readonly kind: 'generator' }; - readonly onUpdate: { readonly id: 'timestampStringNow'; readonly kind: 'generator' }; + readonly onCreate: { + readonly id: 'timestampStringNow'; + readonly kind: 'generator'; + readonly params: { readonly precision: 0 }; + }; + readonly onUpdate: { + readonly id: 'timestampStringNow'; + readonly kind: 'generator'; + readonly params: { readonly precision: 0 }; + }; readonly ref: { readonly entry: 'clock'; readonly field: 'timestampString0'; @@ -1016,8 +1058,16 @@ type ContractBase = Omit< }; }, { - readonly onCreate: { readonly id: 'timestampStringNow'; readonly kind: 'generator' }; - readonly onUpdate: { readonly id: 'timestampStringNow'; readonly kind: 'generator' }; + readonly onCreate: { + readonly id: 'timestampStringNow'; + readonly kind: 'generator'; + readonly params: { readonly precision: 3 }; + }; + readonly onUpdate: { + readonly id: 'timestampStringNow'; + readonly kind: 'generator'; + readonly params: { readonly precision: 3 }; + }; readonly ref: { readonly entry: 'clock'; readonly field: 'timestampString3'; @@ -1025,8 +1075,16 @@ type ContractBase = Omit< }; }, { - readonly onCreate: { readonly id: 'timestampStringNow'; readonly kind: 'generator' }; - readonly onUpdate: { readonly id: 'timestampStringNow'; readonly kind: 'generator' }; + readonly onCreate: { + readonly id: 'timestampStringNow'; + readonly kind: 'generator'; + readonly params: { readonly precision: 6 }; + }; + readonly onUpdate: { + readonly id: 'timestampStringNow'; + readonly kind: 'generator'; + readonly params: { readonly precision: 6 }; + }; readonly ref: { readonly entry: 'clock'; readonly field: 'timestampString6'; @@ -1088,8 +1146,16 @@ type ContractBase = Omit< }; }, { - readonly onCreate: { readonly id: 'timestamptzStringNow'; readonly kind: 'generator' }; - readonly onUpdate: { readonly id: 'timestamptzStringNow'; readonly kind: 'generator' }; + readonly onCreate: { + readonly id: 'timestamptzStringNow'; + readonly kind: 'generator'; + readonly params: { readonly precision: 0 }; + }; + readonly onUpdate: { + readonly id: 'timestamptzStringNow'; + readonly kind: 'generator'; + readonly params: { readonly precision: 0 }; + }; readonly ref: { readonly entry: 'clock'; readonly field: 'timestamptzString0'; @@ -1097,8 +1163,50 @@ type ContractBase = Omit< }; }, { - readonly onCreate: { readonly id: 'timestamptzStringNow'; readonly kind: 'generator' }; - readonly onUpdate: { readonly id: 'timestamptzStringNow'; readonly kind: 'generator' }; + readonly onCreate: { + readonly id: 'timestamptzStringNow'; + readonly kind: 'generator'; + readonly params: { readonly precision: 1 }; + }; + readonly onUpdate: { + readonly id: 'timestamptzStringNow'; + readonly kind: 'generator'; + readonly params: { readonly precision: 1 }; + }; + readonly ref: { + readonly entry: 'clock'; + readonly field: 'timestamptzString1'; + readonly namespace: 'public'; + }; + }, + { + readonly onCreate: { + readonly id: 'timestamptzStringNow'; + readonly kind: 'generator'; + readonly params: { readonly precision: 2 }; + }; + readonly onUpdate: { + readonly id: 'timestamptzStringNow'; + readonly kind: 'generator'; + readonly params: { readonly precision: 2 }; + }; + readonly ref: { + readonly entry: 'clock'; + readonly field: 'timestamptzString2'; + readonly namespace: 'public'; + }; + }, + { + readonly onCreate: { + readonly id: 'timestamptzStringNow'; + readonly kind: 'generator'; + readonly params: { readonly precision: 3 }; + }; + readonly onUpdate: { + readonly id: 'timestamptzStringNow'; + readonly kind: 'generator'; + readonly params: { readonly precision: 3 }; + }; readonly ref: { readonly entry: 'clock'; readonly field: 'timestamptzString3'; @@ -1106,8 +1214,16 @@ type ContractBase = Omit< }; }, { - readonly onCreate: { readonly id: 'timestamptzStringNow'; readonly kind: 'generator' }; - readonly onUpdate: { readonly id: 'timestamptzStringNow'; readonly kind: 'generator' }; + readonly onCreate: { + readonly id: 'timestamptzStringNow'; + readonly kind: 'generator'; + readonly params: { readonly precision: 6 }; + }; + readonly onUpdate: { + readonly id: 'timestamptzStringNow'; + readonly kind: 'generator'; + readonly params: { readonly precision: 6 }; + }; readonly ref: { readonly entry: 'clock'; readonly field: 'timestamptzString6'; diff --git a/test/integration/test/temporal-defaults/_fixture-now/generated/contract.json b/test/integration/test/temporal-defaults/_fixture-now/generated/contract.json index e4d0f1cba832..b857e938d057 100644 --- a/test/integration/test/temporal-defaults/_fixture-now/generated/contract.json +++ b/test/integration/test/temporal-defaults/_fixture-now/generated/contract.json @@ -233,6 +233,26 @@ } } }, + "timestamptzString1": { + "nullable": false, + "type": { + "codecId": "pg/timestamptz-string@1", + "kind": "scalar", + "typeParams": { + "precision": 1 + } + } + }, + "timestamptzString2": { + "nullable": false, + "type": { + "codecId": "pg/timestamptz-string@1", + "kind": "scalar", + "typeParams": { + "precision": 2 + } + } + }, "timestamptzString3": { "nullable": false, "type": { @@ -356,6 +376,12 @@ "timestamptzString0": { "column": "timestamptzString0" }, + "timestamptzString1": { + "column": "timestamptzString1" + }, + "timestamptzString2": { + "column": "timestamptzString2" + }, "timestamptzString3": { "column": "timestamptzString3" }, @@ -559,6 +585,22 @@ "precision": 0 } }, + "timestamptzString1": { + "codecId": "pg/timestamptz-string@1", + "dataType": "pg/timestamptz", + "nullable": false, + "typeParams": { + "precision": 1 + } + }, + "timestamptzString2": { + "codecId": "pg/timestamptz-string@1", + "dataType": "pg/timestamptz", + "nullable": false, + "typeParams": { + "precision": 2 + } + }, "timestamptzString3": { "codecId": "pg/timestamptz-string@1", "dataType": "pg/timestamptz", @@ -615,10 +657,10 @@ "kind": "postgres-schema" } }, - "storageHash": "cedcd9ab983b876215346a9eca6de6c41fe9fd966787f634bd330256c1e2508c" + "storageHash": "e912a0ac1c484ed92ae8cb217638a889ad671c1f4a9bfb3e469ebfb14916c6dd" }, "execution": { - "executionHash": "c3a3387d2ba8c172f3b31c9a79cedb29984f1d49e22fd98046b9d41f1fb09e76", + "executionHash": "51be7e21b1611b41897bee6131dc8d5bfc6abd6380ca0bd6ba5a86d1c2f479c4", "mutations": { "defaults": [ { @@ -825,11 +867,17 @@ { "onCreate": { "id": "timestampStringNow", - "kind": "generator" + "kind": "generator", + "params": { + "precision": 0 + } }, "onUpdate": { "id": "timestampStringNow", - "kind": "generator" + "kind": "generator", + "params": { + "precision": 0 + } }, "ref": { "entry": "clock", @@ -840,11 +888,17 @@ { "onCreate": { "id": "timestampStringNow", - "kind": "generator" + "kind": "generator", + "params": { + "precision": 3 + } }, "onUpdate": { "id": "timestampStringNow", - "kind": "generator" + "kind": "generator", + "params": { + "precision": 3 + } }, "ref": { "entry": "clock", @@ -855,11 +909,17 @@ { "onCreate": { "id": "timestampStringNow", - "kind": "generator" + "kind": "generator", + "params": { + "precision": 6 + } }, "onUpdate": { "id": "timestampStringNow", - "kind": "generator" + "kind": "generator", + "params": { + "precision": 6 + } }, "ref": { "entry": "clock", @@ -960,11 +1020,17 @@ { "onCreate": { "id": "timestamptzStringNow", - "kind": "generator" + "kind": "generator", + "params": { + "precision": 0 + } }, "onUpdate": { "id": "timestamptzStringNow", - "kind": "generator" + "kind": "generator", + "params": { + "precision": 0 + } }, "ref": { "entry": "clock", @@ -975,11 +1041,59 @@ { "onCreate": { "id": "timestamptzStringNow", - "kind": "generator" + "kind": "generator", + "params": { + "precision": 1 + } }, "onUpdate": { "id": "timestamptzStringNow", - "kind": "generator" + "kind": "generator", + "params": { + "precision": 1 + } + }, + "ref": { + "entry": "clock", + "field": "timestamptzString1", + "namespace": "public" + } + }, + { + "onCreate": { + "id": "timestamptzStringNow", + "kind": "generator", + "params": { + "precision": 2 + } + }, + "onUpdate": { + "id": "timestamptzStringNow", + "kind": "generator", + "params": { + "precision": 2 + } + }, + "ref": { + "entry": "clock", + "field": "timestamptzString2", + "namespace": "public" + } + }, + { + "onCreate": { + "id": "timestamptzStringNow", + "kind": "generator", + "params": { + "precision": 3 + } + }, + "onUpdate": { + "id": "timestamptzStringNow", + "kind": "generator", + "params": { + "precision": 3 + } }, "ref": { "entry": "clock", @@ -990,11 +1104,17 @@ { "onCreate": { "id": "timestamptzStringNow", - "kind": "generator" + "kind": "generator", + "params": { + "precision": 6 + } }, "onUpdate": { "id": "timestamptzStringNow", - "kind": "generator" + "kind": "generator", + "params": { + "precision": 6 + } }, "ref": { "entry": "clock", diff --git a/test/integration/test/temporal-defaults/now-generators-readback.integration.test.ts b/test/integration/test/temporal-defaults/now-generators-readback.integration.test.ts index 4b85c874ce79..afa3488a35f4 100644 --- a/test/integration/test/temporal-defaults/now-generators-readback.integration.test.ts +++ b/test/integration/test/temporal-defaults/now-generators-readback.integration.test.ts @@ -25,21 +25,23 @@ const expectedAtLastMillisecond = { timeString0: '23:59:59', timeString3: '23:59:59.999', timeString6: '23:59:59.999', - timetz0: '23:59:59+00', - timetz3: '23:59:59.999+00', - timetz6: '23:59:59.999+00', + timetz0: '23:59:59Z', + timetz3: '23:59:59.999Z', + timetz6: '23:59:59.999Z', timestamp0: '2024-01-03T00:00:00', timestamp3: '2024-01-02T23:59:59.999', timestamp6: '2024-01-02T23:59:59.999', - timestampString0: '2024-01-03 00:00:00', - timestampString3: '2024-01-02 23:59:59.999', - timestampString6: '2024-01-02 23:59:59.999', + timestampString0: '2024-01-02T23:59:59', + timestampString3: '2024-01-02T23:59:59.999', + timestampString6: '2024-01-02T23:59:59.999', timestamptz0: '2024-01-03T00:00:00Z', timestamptz3: '2024-01-02T23:59:59.999Z', timestamptz6: '2024-01-02T23:59:59.999Z', - timestamptzString0: '2024-01-03 00:00:00+00', - timestamptzString3: '2024-01-02 23:59:59.999+00', - timestamptzString6: '2024-01-02 23:59:59.999+00', + timestamptzString0: '2024-01-02T23:59:59Z', + timestamptzString1: '2024-01-02T23:59:59.9Z', + timestamptzString2: '2024-01-02T23:59:59.99Z', + timestamptzString3: '2024-01-02T23:59:59.999Z', + timestamptzString6: '2024-01-02T23:59:59.999Z', timestamptzJsDate0: '2024-01-03T00:00:00.000Z', timestamptzJsDate3: '2024-01-02T23:59:59.999Z', timestamptzJsDate6: '2024-01-02T23:59:59.999Z', diff --git a/test/utils/src/exports/index.ts b/test/utils/src/exports/index.ts index 962141fe37c4..b5d50765fda8 100644 --- a/test/utils/src/exports/index.ts +++ b/test/utils/src/exports/index.ts @@ -7,6 +7,7 @@ export * from '../column-descriptors'; export * from '../contract-factories'; export * from '../lowered-params'; export * from '../operation-descriptors'; +export * from '../startup-parameters'; export * from '../timeouts'; function normalizeConnectionString(raw: string): string { diff --git a/test/utils/src/startup-parameters.ts b/test/utils/src/startup-parameters.ts new file mode 100644 index 000000000000..b1b5df863710 --- /dev/null +++ b/test/utils/src/startup-parameters.ts @@ -0,0 +1,75 @@ +import { createServer, type Socket } from 'node:net'; + +const SSL_REQUEST_CODE = 80_877_103; +const SSL_REFUSED = 'N'; + +export type StartupParameters = Readonly>; + +/** A server that records the parameters of each PostgreSQL startup message it receives, then drops the connection. */ +export interface StartupParameterRecorder { + /** A URL that points a PostgreSQL client at the recorder. */ + readonly url: string; + /** The parameters of the next startup message the recorder receives. */ + next(): Promise; + close(): Promise; +} + +function parametersOf(message: Buffer): StartupParameters { + const fields = message.subarray(8).toString('utf8').split('\0'); + const parameters: Record = {}; + for (let index = 0; index + 1 < fields.length && fields[index] !== ''; index += 2) { + parameters[fields[index] ?? ''] = fields[index + 1] ?? ''; + } + return parameters; +} + +export async function startStartupParameterRecorder(): Promise { + const received: StartupParameters[] = []; + const waiting: ((parameters: StartupParameters) => void)[] = []; + const record = (parameters: StartupParameters) => { + const waiter = waiting.shift(); + if (waiter === undefined) received.push(parameters); + else waiter(parameters); + }; + + const sockets = new Set(); + const server = createServer((socket) => { + sockets.add(socket); + socket.on('close', () => sockets.delete(socket)); + socket.on('error', () => {}); + let buffered = Buffer.alloc(0); + socket.on('data', (chunk) => { + buffered = Buffer.concat([buffered, chunk]); + while (buffered.length >= 8 && buffered.length >= buffered.readInt32BE(0)) { + const length = buffered.readInt32BE(0); + const message = buffered.subarray(0, length); + buffered = buffered.subarray(length); + if (message.readInt32BE(4) === SSL_REQUEST_CODE) { + socket.write(SSL_REFUSED); + continue; + } + record(parametersOf(message)); + socket.destroy(); + return; + } + }); + }); + + await new Promise((resolve) => server.listen(0, '127.0.0.1', resolve)); + const address = server.address(); + const port = typeof address === 'object' && address !== null ? address.port : 0; + + return { + url: `postgres://recorder@127.0.0.1:${port}/recorder`, + next: () => { + const parameters = received.shift(); + if (parameters !== undefined) return Promise.resolve(parameters); + return new Promise((resolve) => waiting.push(resolve)); + }, + close: () => + new Promise((resolve) => { + for (const socket of sockets) socket.destroy(); + server.close(() => resolve()); + }), + }; +} diff --git a/upgrade-instructions/pending/iso-string-date-time-values/app/instructions.md b/upgrade-instructions/pending/iso-string-date-time-values/app/instructions.md new file mode 100644 index 000000000000..0686a13ae0b7 --- /dev/null +++ b/upgrade-instructions/pending/iso-string-date-time-values/app/instructions.md @@ -0,0 +1,58 @@ +--- +changes: + - id: iso-string-date-time-values + summary: | + The PostgreSQL text date and time types (`TimestamptzString`, `TimestampString`, `DateString`, `TimeString`, `Timetz`) read and write ISO 8601, the form the contract stores, instead of the text PostgreSQL prints. A `timestamptz` reads as the instant in UTC whatever the session's time zone, as in `2024-01-01T00:00:00Z`. A literal default or enum member on these types must be written in that form. + detection: + glob: "**/*.{prisma,ts,tsx,mts,cts,js,mjs,cjs}" + matches: + - '(? SET DateStyle = 'ISO'` (or set it on the database). diff --git a/upgrade-instructions/pending/iso-string-date-time-values/extension/instructions.md b/upgrade-instructions/pending/iso-string-date-time-values/extension/instructions.md new file mode 100644 index 000000000000..8e4a1ae98f3e --- /dev/null +++ b/upgrade-instructions/pending/iso-string-date-time-values/extension/instructions.md @@ -0,0 +1,38 @@ +--- +changes: + - id: driver-ensures-target-session-settings + summary: | + A target descriptor may declare `sessionSettings`, the settings its codecs need on every database session; the PostgreSQL target declares `DateStyle=ISO`. A stack passes them to each driver it creates: a runtime driver descriptor's `create(options, sessionSettings)` receives them as a second argument, and a control driver's connect options carry them as `sessionSettings`. A driver for a target that declares them must ensure them on every connection it hands out. A wrapper around a driver descriptor's `create` passes `sessionSettings` on. + detection: + glob: "**/*.{ts,tsx,mts,cts}" + matches: + - '(? // options.sessionSettings +``` + +A driver for a target that declares settings applies them to every connection it hands out, before the first statement, and again whenever a connection's session may have been reset. A driver for a target that declares none needs no change. Code that wraps a driver descriptor to curry its options passes the second argument on, or the driver never receives the settings: + +```ts +const driverWithCursor = { + ...postgresDriverDescriptor, + create(_options, sessionSettings) { + return postgresDriverDescriptor.create({ cursor }, sessionSettings); + }, +}; +``` + +A codec whose application value is the stored form of its data type builds its values with the new `CodecImpl.storedDataTypeValueOf`, which takes only the stored spelling, instead of `dataTypeValueOf`. From 9850ad9b417663bb9be71bc5d46c97e30d559c8a Mon Sep 17 00:00:00 2001 From: willbot Date: Fri, 23 Oct 2026 16:09:39 +0200 Subject: [PATCH 082/118] Links to ADR 279 use its new file name ADR 279 was renamed to "Convenience types choose an explicit type from their arguments". ADR 254, 257 and 275, the codec authoring guide and the scalar types reference now link to the new file. Signed-off-by: willbot Signed-off-by: Will Madden Co-Authored-By: Claude Opus 5.5 --- docs/architecture docs/adrs/ADR 254 - Data types and casts.md | 2 +- ... Scalar types are named after the target on every surface.md | 2 +- ...attributes, and a type alias is expanded into each column.md | 2 +- docs/reference/codec-authoring-guide.md | 2 +- docs/reference/scalar-types.md | 2 +- 5 files changed, 5 insertions(+), 5 deletions(-) diff --git a/docs/architecture docs/adrs/ADR 254 - Data types and casts.md b/docs/architecture docs/adrs/ADR 254 - Data types and casts.md index 35762723a9f0..0dd3258c9cdf 100644 --- a/docs/architecture docs/adrs/ADR 254 - Data types and casts.md +++ b/docs/architecture docs/adrs/ADR 254 - Data types and casts.md @@ -1,6 +1,6 @@ # ADR 254 — Data types and casts -Status: **Accepted.** Implemented for the Postgres, SQLite and MongoDB targets and their extensions. Amended 2026-10-20: a type-constructor argument that is a value names a data type and is admitted by the cast rule (see "Columns and type constructors"). Amended 2026-10-22: MongoDB values are written and cast as SQL values are, and enum members and discriminator values are read by one framework function in both families (see "Enum members and discriminator values"). Amended by [ADR 279](ADR%20279%20-%20Convenience%20date%20and%20time%20types%20choose%20an%20explicit%20type%20from%20their%20arguments.md) on 2026-10-22: a type constructor still names one codec, and a second kind of type declaration, a type choice, resolves to a type constructor from its arguments, so a column always stores the codec and parameters of a type constructor. +Status: **Accepted.** Implemented for the Postgres, SQLite and MongoDB targets and their extensions. Amended 2026-10-20: a type-constructor argument that is a value names a data type and is admitted by the cast rule (see "Columns and type constructors"). Amended 2026-10-22: MongoDB values are written and cast as SQL values are, and enum members and discriminator values are read by one framework function in both families (see "Enum members and discriminator values"). Amended by [ADR 279](ADR%20279%20-%20Convenience%20types%20choose%20an%20explicit%20type%20from%20their%20arguments.md) on 2026-10-22: a type constructor still names one codec, and a second kind of type declaration, a type choice, resolves to a type constructor from its arguments, so a column always stores the codec and parameters of a type constructor. ## At a glance diff --git a/docs/architecture docs/adrs/ADR 257 - Scalar types are named after the target on every surface.md b/docs/architecture docs/adrs/ADR 257 - Scalar types are named after the target on every surface.md index 0069d41ce0ed..590aceeda2f6 100644 --- a/docs/architecture docs/adrs/ADR 257 - Scalar types are named after the target on every surface.md +++ b/docs/architecture docs/adrs/ADR 257 - Scalar types are named after the target on every surface.md @@ -2,7 +2,7 @@ Status: **Accepted** -**Amended by [ADR 279](ADR%20279%20-%20Convenience%20date%20and%20time%20types%20choose%20an%20explicit%20type%20from%20their%20arguments.md) (2026-10-22):** where a stored type has several codecs, every one of them carries a suffix naming its kind of value, not only the second (`TimestamptzTemporal`, `TimestamptzJsDate`, `TimestamptzString`). The unsuffixed name of the concept, such as `DateTime`, `Date`, `Time` and `Duration` on PostgreSQL, belongs to a convenience type, a type choice that becomes one of the suffixed types. +**Amended by [ADR 279](ADR%20279%20-%20Convenience%20types%20choose%20an%20explicit%20type%20from%20their%20arguments.md) (2026-10-22):** where a stored type has several codecs, every one of them carries a suffix naming its kind of value, not only the second (`TimestamptzTemporal`, `TimestamptzJsDate`, `TimestamptzString`). The unsuffixed name of the concept, such as `DateTime`, `Date`, `Time` and `Duration` on PostgreSQL, belongs to a convenience type, a type choice that becomes one of the suffixed types. ## Decision diff --git a/docs/architecture docs/adrs/ADR 275 - Client generators are field attributes, and a type alias is expanded into each column.md b/docs/architecture docs/adrs/ADR 275 - Client generators are field attributes, and a type alias is expanded into each column.md index f4b9f7b2b01f..3d3bbc9bbdc0 100644 --- a/docs/architecture docs/adrs/ADR 275 - Client generators are field attributes, and a type alias is expanded into each column.md +++ b/docs/architecture docs/adrs/ADR 275 - Client generators are field attributes, and a type alias is expanded into each column.md @@ -5,7 +5,7 @@ **Builds on:** [ADR 158 — Execution mutation defaults](ADR%20158%20-%20Execution%20mutation%20defaults.md), [ADR 241 — Scalar types use the authoring type-constructor channel](ADR%20241%20-%20Scalar%20types%20use%20the%20authoring%20type-constructor%20channel.md), [ADR 269 — PSL mixins](ADR%20269%20-%20PSL%20mixins.md) **Supersedes:** the field-preset half of [ADR 170 — Pack-provided type constructors and field presets](ADR%20170%20-%20Pack-provided%20type%20constructors%20and%20field%20presets.md); [ADR 246 — Option arguments and select templates for authoring helpers](ADR%20246%20-%20Option%20arguments%20and%20select%20templates%20for%20authoring%20helpers.md), except its two omission rules **Amends:** [ADR 104](ADR%20104%20-%20PSL%20extension%20namespacing%20%26%20syntax.md), [ADR 158](ADR%20158%20-%20Execution%20mutation%20defaults.md), [ADR 169 — Declared applicability for mutation default generators](ADR%20169%20-%20Declared%20applicability%20for%20mutation%20default%20generators.md), [ADR 208 — Higher-order codecs for parameterized types](ADR%20208%20-%20Higher-order%20codecs%20for%20parameterized%20types.md), [ADR 212 — AST-bound codec resolution](ADR%20212%20-%20AST-bound%20codec%20resolution.md), [ADR 231](ADR%20231%20-%20Declarative%20attribute%20specifications.md), [ADR 241](ADR%20241%20-%20Scalar%20types%20use%20the%20authoring%20type-constructor%20channel.md), [ADR 249](ADR%20249%20-%20Central%20attribute-spec%20registry.md), [ADR 254](ADR%20254%20-%20Data%20types%20and%20casts.md) -**Amended by [ADR 279](ADR%20279%20-%20Convenience%20date%20and%20time%20types%20choose%20an%20explicit%20type%20from%20their%20arguments.md) (2026-10-22):** the contributed `temporal.*` aliases are built on the convenience type `DateTime`, and their `String` and `JsDate` variants are not needed, because `now()` picks its generator from the field's final codec and a user writes `DateTime(as: string) @onCreate(now())` for another kind of value. +**Amended by [ADR 279](ADR%20279%20-%20Convenience%20types%20choose%20an%20explicit%20type%20from%20their%20arguments.md) (2026-10-22):** the contributed `temporal.*` aliases are built on the convenience type `DateTime`, and their `String` and `JsDate` variants are not needed, because `now()` picks its generator from the field's final codec and a user writes `DateTime(as: string) @onCreate(now())` for another kind of value. **Amended 2026-10-21:** the TypeScript `types` key is renamed `databaseTypes` and registers database types only; a contributed alias is used as `field.column(type.temporal.UpdatedAt())`; a contributed alias carries no database default; Mongo's contributed aliases are on `Date`; the Mongo TypeScript builder gets no contributed aliases yet. Each change is marked "Amended 2026-10-21" below. --- diff --git a/docs/reference/codec-authoring-guide.md b/docs/reference/codec-authoring-guide.md index 49df59e1187f..da24bfd5a7da 100644 --- a/docs/reference/codec-authoring-guide.md +++ b/docs/reference/codec-authoring-guide.md @@ -591,7 +591,7 @@ A codec with parameters of its own sets `paramsSchema` to `dataType.params.and(o A type constructor maps its arguments onto the parameters and names only the codec; the data type follows from the codec. Every argument of a type constructor is a value argument, `{ kind: 'value', dataType }`, and assembly refuses any other kind with `CONTRACT.PACK_CONTRIBUTION_INVALID`; field presets keep their own argument kinds until they are removed. A value argument is a value of the named data type, read and cast as an attribute argument is (ADR 254), so `Vector(1536)` reads `1536` as a `pg/int2` and casts it to `pg/int4`, and `Vector("1536")` is refused. On Postgres every length, precision, scale and SRID is a `pg/int4`, the type of a typmod. A value argument carries no bounds, because the parameter's schema checks the value it gives. Assembly refuses a value argument whose data type no component registers (`CONTRACT.DATA_TYPE_UNREGISTERED`) or that no contract source can write (`CONTRACT.DATA_TYPE_NOT_WRITABLE`). In the TypeScript builder, the pack that registers a data type declares the TypeScript value an argument of it takes as `__dataTypeArguments` in its type, such as `{ 'pg/int4': { argument: number; parameter: number } }`: `argument` is what an author passes, and `parameter` is the type parameter the data type's reader gives for it, and `contract print` writes each argument as a literal of its data type. `inferred: true` marks the constructor written for a column of the data type when no schema names one: `contract infer` and the Prisma 7 reader write it. `contract print` writes it when it produces the column's codec, and otherwise the first constructor in the stack that does, so a `timestamptz` column with the `Temporal` codec prints `TimestamptzTemporal`. It is called with the fewest arguments that give the column's parameters in normal form, so a `numeric(10,0)` column is written `Numeric(10)`. The Prisma 7 reader builds a contract rather than printing PSL, so it passes every parameter the Prisma 7 column gives. At most one constructor per data type carries the mark. A data type with no marked constructor, such as `pg/tsquery`, is still recognised, but `contract infer` cannot write a column of it and fails with `CONTRACT.INFER_COLUMN_TYPE_UNCLAIMED`. -A type choice (`kind: 'typeChoice'`) is a second kind of type declaration, for a type named after what a field means rather than a column type ([ADR 279](../architecture%20docs/adrs/ADR%20279%20-%20Convenience%20date%20and%20time%20types%20choose%20an%20explicit%20type%20from%20their%20arguments.md)). It names no codec. It declares option arguments, `{ kind: 'option', name, values, default, optional: true }`, written as bare names, and value arguments it passes on by name, such as `precision`; and `choices`, a table whose rows map one value of each option to the name of a type constructor at the same level of the namespace, with `defaults` for passed-on arguments the call leaves out. A call resolves to its row's constructor, called with the passed-on arguments, so the contract records only that constructor's codec and parameters. A combination with no row, and a passed-on argument the chosen constructor does not take, are refused at the argument. Every argument is optional, so a type choice is also written bare, as its default row. Assembly refuses a table that names no registered constructor, a value or default an option does not list, defaults with no row, and a passed-on argument of another data type than its constructor's. A type choice is never marked `inferred`, and `contract print` and `contract infer` write the constructor. In the TypeScript builder its `type.*` helper takes the same arguments in the same order and is typed as one overload per row. +A type choice (`kind: 'typeChoice'`) is a second kind of type declaration, for a type named after what a field means rather than a column type ([ADR 279](../architecture%20docs/adrs/ADR%20279%20-%20Convenience%20types%20choose%20an%20explicit%20type%20from%20their%20arguments.md)). It names no codec. It declares option arguments, `{ kind: 'option', name, values, default, optional: true }`, written as bare names, and value arguments it passes on by name, such as `precision`; and `choices`, a table whose rows map one value of each option to the name of a type constructor at the same level of the namespace, with `defaults` for passed-on arguments the call leaves out. A call resolves to its row's constructor, called with the passed-on arguments, so the contract records only that constructor's codec and parameters. A combination with no row, and a passed-on argument the chosen constructor does not take, are refused at the argument. Every argument is optional, so a type choice is also written bare, as its default row. Assembly refuses a table that names no registered constructor, a value or default an option does not list, defaults with no row, and a passed-on argument of another data type than its constructor's. A type choice is never marked `inferred`, and `contract print` and `contract infer` write the constructor. In the TypeScript builder its `type.*` helper takes the same arguments in the same order and is typed as one overload per row. ```ts import type { AuthoringTypeNamespace } from '@internal/framework-components/authoring'; diff --git a/docs/reference/scalar-types.md b/docs/reference/scalar-types.md index 78487ed9e3a3..f3bcba485bcb 100644 --- a/docs/reference/scalar-types.md +++ b/docs/reference/scalar-types.md @@ -66,7 +66,7 @@ Two limitations to know about: These are the current names. The Postgres and SQLite rename project will change several of them to the target's own type names and update this table. -The date and time types have two layers ([ADR 279](../architecture%20docs/adrs/ADR%20279%20-%20Convenience%20date%20and%20time%20types%20choose%20an%20explicit%20type%20from%20their%20arguments.md)). Each explicit type in the table below names one column type and one kind of value. The convenience types `DateTime`, `Date`, `Time` and `Duration` are named for what a field means, and each becomes one explicit type, chosen by its optional `as` and `storage` arguments: `as` is the value the application reads, and `storage` is the database type that stores it; `precision` is passed on to the explicit type. The contract stores only the explicit type, so `DateTime(as: temporal)` and `TimestamptzTemporal` emit the same contract. +The date and time types have two layers ([ADR 279](../architecture%20docs/adrs/ADR%20279%20-%20Convenience%20types%20choose%20an%20explicit%20type%20from%20their%20arguments.md)). Each explicit type in the table below names one column type and one kind of value. The convenience types `DateTime`, `Date`, `Time` and `Duration` are named for what a field means, and each becomes one explicit type, chosen by its optional `as` and `storage` arguments: `as` is the value the application reads, and `storage` is the database type that stores it; `precision` is passed on to the explicit type. The contract stores only the explicit type, so `DateTime(as: temporal)` and `TimestamptzTemporal` emit the same contract. | Convenience type | `as` | `storage` | Becomes | | --- | --- | --- | --- | From f4e5c188c643606ef7bc959bd9ef533c076054f8 Mon Sep 17 00:00:00 2001 From: willbot Date: Fri, 23 Oct 2026 16:11:56 +0200 Subject: [PATCH 083/118] The codec authoring guide says a type choice's TypeScript helper takes one object Signed-off-by: willbot Signed-off-by: Will Madden Co-Authored-By: Claude Opus 5.5 --- docs/reference/codec-authoring-guide.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/reference/codec-authoring-guide.md b/docs/reference/codec-authoring-guide.md index da24bfd5a7da..f0445e6cde3b 100644 --- a/docs/reference/codec-authoring-guide.md +++ b/docs/reference/codec-authoring-guide.md @@ -591,7 +591,7 @@ A codec with parameters of its own sets `paramsSchema` to `dataType.params.and(o A type constructor maps its arguments onto the parameters and names only the codec; the data type follows from the codec. Every argument of a type constructor is a value argument, `{ kind: 'value', dataType }`, and assembly refuses any other kind with `CONTRACT.PACK_CONTRIBUTION_INVALID`; field presets keep their own argument kinds until they are removed. A value argument is a value of the named data type, read and cast as an attribute argument is (ADR 254), so `Vector(1536)` reads `1536` as a `pg/int2` and casts it to `pg/int4`, and `Vector("1536")` is refused. On Postgres every length, precision, scale and SRID is a `pg/int4`, the type of a typmod. A value argument carries no bounds, because the parameter's schema checks the value it gives. Assembly refuses a value argument whose data type no component registers (`CONTRACT.DATA_TYPE_UNREGISTERED`) or that no contract source can write (`CONTRACT.DATA_TYPE_NOT_WRITABLE`). In the TypeScript builder, the pack that registers a data type declares the TypeScript value an argument of it takes as `__dataTypeArguments` in its type, such as `{ 'pg/int4': { argument: number; parameter: number } }`: `argument` is what an author passes, and `parameter` is the type parameter the data type's reader gives for it, and `contract print` writes each argument as a literal of its data type. `inferred: true` marks the constructor written for a column of the data type when no schema names one: `contract infer` and the Prisma 7 reader write it. `contract print` writes it when it produces the column's codec, and otherwise the first constructor in the stack that does, so a `timestamptz` column with the `Temporal` codec prints `TimestamptzTemporal`. It is called with the fewest arguments that give the column's parameters in normal form, so a `numeric(10,0)` column is written `Numeric(10)`. The Prisma 7 reader builds a contract rather than printing PSL, so it passes every parameter the Prisma 7 column gives. At most one constructor per data type carries the mark. A data type with no marked constructor, such as `pg/tsquery`, is still recognised, but `contract infer` cannot write a column of it and fails with `CONTRACT.INFER_COLUMN_TYPE_UNCLAIMED`. -A type choice (`kind: 'typeChoice'`) is a second kind of type declaration, for a type named after what a field means rather than a column type ([ADR 279](../architecture%20docs/adrs/ADR%20279%20-%20Convenience%20types%20choose%20an%20explicit%20type%20from%20their%20arguments.md)). It names no codec. It declares option arguments, `{ kind: 'option', name, values, default, optional: true }`, written as bare names, and value arguments it passes on by name, such as `precision`; and `choices`, a table whose rows map one value of each option to the name of a type constructor at the same level of the namespace, with `defaults` for passed-on arguments the call leaves out. A call resolves to its row's constructor, called with the passed-on arguments, so the contract records only that constructor's codec and parameters. A combination with no row, and a passed-on argument the chosen constructor does not take, are refused at the argument. Every argument is optional, so a type choice is also written bare, as its default row. Assembly refuses a table that names no registered constructor, a value or default an option does not list, defaults with no row, and a passed-on argument of another data type than its constructor's. A type choice is never marked `inferred`, and `contract print` and `contract infer` write the constructor. In the TypeScript builder its `type.*` helper takes the same arguments in the same order and is typed as one overload per row. +A type choice (`kind: 'typeChoice'`) is a second kind of type declaration, for a type named after what a field means rather than a column type ([ADR 279](../architecture%20docs/adrs/ADR%20279%20-%20Convenience%20types%20choose%20an%20explicit%20type%20from%20their%20arguments.md)). It names no codec. It declares option arguments, `{ kind: 'option', name, values, default, optional: true }`, written as bare names, and value arguments it passes on by name, such as `precision`; and `choices`, a table whose rows map one value of each option to the name of a type constructor at the same level of the namespace, with `defaults` for passed-on arguments the call leaves out. A call resolves to its row's constructor, called with the passed-on arguments, so the contract records only that constructor's codec and parameters. A combination with no row, and a passed-on argument the chosen constructor does not take, are refused at the argument. Every argument is optional, so a type choice is also written bare, as its default row. Assembly refuses a table that names no registered constructor, a value or default an option does not list, defaults with no row, and a passed-on argument of another data type than its constructor's. A type choice is never marked `inferred`, and `contract print` and `contract infer` write the constructor. In the TypeScript builder its `type.*` helper takes the same arguments as one object keyed by name, such as `type.DateTime({ as: 'string', precision: 6 })`, and is typed as one overload per row. ```ts import type { AuthoringTypeNamespace } from '@internal/framework-components/authoring'; From 0b7a09b897bcbf261854559e11c80455d9c83a01 Mon Sep 17 00:00:00 2001 From: willbot Date: Fri, 23 Oct 2026 16:19:32 +0200 Subject: [PATCH 084/118] The convenience types journey writes and reads ISO 8601 text The string codecs now read ISO 8601 on every server and take only that form, so the journey writes and expects 2024-02-29T10:00:00.123456Z for the timestamptz text column and 09:30:00+02:00 for the timetz column. Signed-off-by: willbot Signed-off-by: Will Madden Co-Authored-By: Claude Opus 5.5 --- ...nvenience-date-time-types-without-temporal.e2e.test.ts | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/test/integration/test/cli-journeys/convenience-date-time-types-without-temporal.e2e.test.ts b/test/integration/test/cli-journeys/convenience-date-time-types-without-temporal.e2e.test.ts index 823f213dd761..b3169ca39a8b 100644 --- a/test/integration/test/cli-journeys/convenience-date-time-types-without-temporal.e2e.test.ts +++ b/test/integration/test/cli-journeys/convenience-date-time-types-without-temporal.e2e.test.ts @@ -69,10 +69,10 @@ try { id: 1, paidAt: new Date('2024-02-29T10:00:00.123Z'), legacyAt: new Date('2024-02-29T10:00:00.5Z'), - textAt: '2024-02-29 10:00:00.123456+00', + textAt: '2024-02-29T10:00:00.123456Z', shipBy: '2024-02-29', opensAt: '09:30:00', - localAt: '09:30:00+02', + localAt: '09:30:00+02:00', period: 'P1M', parts: { months: 1, days: 2, micros: 3n }, settledAt: null, @@ -140,10 +140,10 @@ withTempDir(({ createTempDir }) => { id: { kind: 'number', value: 1 }, paidAt: { kind: 'Date', value: '2024-02-29T10:00:00.123Z' }, legacyAt: { kind: 'Date', value: '2024-02-29T10:00:00.500Z' }, - textAt: { kind: 'string', value: '2024-02-29 10:00:00.123456+00' }, + textAt: { kind: 'string', value: '2024-02-29T10:00:00.123456Z' }, shipBy: { kind: 'string', value: '2024-02-29' }, opensAt: { kind: 'string', value: '09:30:00' }, - localAt: { kind: 'string', value: '09:30:00+02' }, + localAt: { kind: 'string', value: '09:30:00+02:00' }, period: { kind: 'string', value: 'P1M' }, parts: { kind: 'object', value: { months: '1', days: '2', micros: '3' } }, settledAt: { kind: 'null', value: null }, From 510d1a07001e90cea86b0c7ea8f52ce7c3f462d9 Mon Sep 17 00:00:00 2001 From: willbot Date: Fri, 23 Oct 2026 17:08:45 +0200 Subject: [PATCH 085/118] TML-3713: contract infer, contract print, the Prisma 7 reader and orm init write the convenience date and time types (#30858) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ## Linked issue Fixes TML-3713. Part of the Convenience date and time types project (TML-3709); stacked on the convenience types PR, prisma/orm#30847. It also merges in the Prisma 7 `now()` defaults PR, prisma/orm#30839, so the diff shows that change too; review it there. ## Summary `contract infer` on this table: ```sql CREATE TABLE event ( at timestamptz, paid_at timestamptz(3), legacy timestamp(3), day date, opens time(0), local_at timetz, span interval ); ``` writes: ```prisma model Event { at DateTime(precision: 6) // TimestamptzJsDate(6) paidAt DateTime // TimestamptzJsDate(3) legacy DateTime(column: timestamp) // TimestampJsDate(3) day Date // DateString opens Time(precision: 0) // TimeString(0) localAt Time(column: timetz) // Timetz span Duration // IntervalString } ``` **The decision: the tools that write a schema for you (`contract infer`, `contract print`, the Prisma 7 schema reader and `prisma orm init`) write the convenience date and time types, choosing the convenience default wherever it fits the column.** The schema above emits, `db verify` finds nothing, and the planner plans nothing. ADR 279 ("What the tools write") is the design. ### Why The convenience types (prisma/orm#30847) only help if the schemas users start from use them. A schema infer writes should read like one a user would write, and the default path should be the one users see first. ### Infer and print write the shortest convenience call For each column, infer picks the explicit type the convenience default gives, then writes the shortest convenience call that becomes it. It writes the explicit type only when no convenience call does, for example a `timestamp` read as `Temporal.PlainDateTime` (`TimestampTemporal`). `contract print` writes the same forms; it compares parameters exactly, so it reproduces a contract byte for byte. One framework helper does this for both, beside the type-choice resolver: given an explicit constructor call, it finds the type-choice row that names it, builds the arguments by name, drops those equal to the defaults, and re-resolves to prove the call gives the same codec and parameters. ### A column with no precision compares equal to precision 6 PostgreSQL stores microseconds either way, so the date and time data types now treat an omitted precision as 6 when columns are compared (their normal form). That is what lets infer write `DateTime(precision: 6)` for a bare `timestamptz` and still verify clean. Two things keep the precision as written: - the check the planner runs after `ADD COLUMN` or `ALTER COLUMN TYPE` compares PostgreSQL's `format_type` with the type as written, because PostgreSQL prints a bare `timestamptz` as `timestamp with time zone`, not `timestamp(6) with time zone`. A SQL data type may declare `catalogParams` for this; MySQL's `datetime` declares none. - `db verify` messages show a bare column as `timestamptz`. ADR 254's sentence on that check now says the catalog text is for the type as written. No stored contract, hash or committed plan changes. ### The Prisma 7 reader, the aliases and `orm init` - The Prisma 7 reader picks the same types: `DateTime` becomes `TimestampJsDate(3)` (printed `DateTime(column: timestamp)`), and `@db.Timestamptz(p)`, `@db.Date`, `@db.Time(p)` and `@db.Timetz(p)` the matching convenience forms. An adopted project reads `Date` values where it read strings. - `temporal.CreatedAt` and `temporal.UpdatedAt` are built on `TimestamptzJsDate(3)`, exactly what bare `DateTime` gives; their `String` and `JsDate` variants are removed. - `prisma orm init` writes `createdAt DateTime @default(now()) @onCreate(now())` and `updatedAt temporal.UpdatedAt`, in PSL and TypeScript. ### A `Date` cannot hold digits below a millisecond A JavaScript `Date` holds milliseconds. ADR 254 already says a codec refuses a stored value its application value cannot hold exactly; the `Date` codecs dropped the extra digits instead. Now: - The Postgres `timestamptz` and `timestamp` `Date` codecs and MySQL's `datetime` `Date` codec refuse, in `fromDataTypeValue`, a stored value with digits below a millisecond, naming the types that hold it. So `@default(timestamptz`2024-01-01T01:00:00.123456Z`)` on a `DateTime` field is refused at authoring. Reading and writing rows is unchanged: a row with microseconds still reads as a `Date` at the millisecond. - `contract infer` prints a reported default as a literal only when the codec reads it exactly, as it already does for JSON numbers; a microsecond default is printed as its raw SQL expression, which keeps every digit in DDL. - The Prisma 7 reader writes no SQL. For a default the codec refuses, single or list, it asks the target binding (`refusedLiteralDefault`); the Postgres binding writes `'2024-01-02T03:04:05.123456'::timestamp(6)` or `ARRAY[...]::timestamp(6)[]`. ADR 252 lists the new binding member. - On a column read as a `Date`, `db verify` reads both defaults as the application reads them, so a difference below a millisecond is not reported. ADR 254 and `scalar-types.md` say so. The TypeScript builder cannot write such a default at all: `.default()` on a `Date` field takes a `Date`. ### The framework vocabulary limit goes from 237 to 238 The lint counts target words such as `column` in `packages/1-framework`. The PostgreSQL TypeScript starter that `orm init` writes lives in the CLI package and now contains `field.column(type.DateTime())`; the template is one string, so the occurrence can't be shared with an existing one. The same templates are already counted for the same reason. ## Testing performed - `contract infer` on the table above writes exactly that schema; it emits, `db verify --strict` finds nothing, the planner plans nothing. - A microsecond default: infer prints the raw expression; a database migrated from it holds `.123456` (checked in `information_schema` and an inserted row); the Prisma 7 `tsMicro` and list `ts6l` fixtures migrate and verify clean; signed years write SQL PostgreSQL accepts. A PSL `.123456` default on `DateTime` is refused; `.123` is accepted. - Postcheck text against PGlite: a bare `timestamptz` checks as `timestamp with time zone`, `timestamptz(6)` as `timestamp(6) with time zone`. - Package tests with coverage, typecheck and lint: framework-components, sql-contract, family-sql, contract-psl, contract-prisma7, target-postgres, adapter-postgres, target-mysql, CLI, postgres facade. - Integration: 2804 tests across psl-infer, psl-print, temporal-defaults, authoring parity, prisma7-source, convenience-date-time-types, date-time-defaults, init-templates, infer round trip, db-introspect, upgrade-instructions. Journeys: 79 tests in 27 files. `examples/prisma7-adoption` tests. - `pnpm fixtures:check`, `pnpm lint:deps`, `pnpm lint:ratchets`, `pnpm lint:docs`, `pnpm check:upgrade-coverage --mode pr`. ## Skill update `skills/prisma-8` (`contract.md`, the infer part of `quickstart.md`) describes what infer writes. Upgrade instructions in `upgrade-instructions/pending/tools-write-convenience-types/app/`: re-emit and `db sign` adopted projects and read `Date` where strings were read; rewrite the removed alias variants; write a sub-millisecond default on a `Date` field with a string or Temporal type or a `sql` expression. The Prisma 7 `now()` defaults instruction now names `DateTime(column: timestamp)`. ## Checklist - [x] All commits are signed off. - [x] Scoped to one logical concern. - [x] Tests are updated. - [x] The PR title is in `TML-NNNN: ` form. - [x] The Skill update section is filled in. ## Notes for the reviewer - Ten Prisma 7 fixtures changed codec and hash; `examples/prisma7-adoption` has a re-emitted contract and a new signed snapshot, and its code reads a `Date`. - A contract emitted before this change with a microsecond literal on a `Date` column fails when a migration is planned, until it is re-authored. **Alternatives considered:** - **Infer writes the string types for microsecond columns, which lose nothing.** Nothing is lost, but the schema differs from what a user would write; ADR 279 records the choice of the default path. - **Store an omitted precision as 6 in the contract.** It would change existing contracts and hashes; comparing in normal form changes none. - **A data type reads the database's text of its defaults.** ADR 254 rejected it: that text is what a row arrives in, and the codec reads it. Refusing in the codec follows the rule ADR 254 already states. - **Compare a timestamp default with the full value the column holds in `db verify`.** It needs a per-type reader beside the codec, and its first version compared one side at full precision and the other through the codec. Agent: thales-11 🤖 Generated with [Claude Code](https://claude.com/claude-code) --------- Signed-off-by: willbot Signed-off-by: Will Madden Co-authored-by: Claude Opus 5.5 --- ...a version's schema is a contract source.md | 2 +- .../adrs/ADR 254 - Data types and casts.md | 8 +- ...type alias is expanded into each column.md | 4 +- docs/reference/error-reference.md | 6 +- docs/reference/scalar-types.md | 17 +- examples/prisma7-adoption/README.md | 2 +- .../generated/prisma8/contract.d.ts | 18 +- .../generated/prisma8/contract.json | 18 +- .../migrations/app/refs/db.json | 2 +- .../contract.d.ts | 227 +++++++------- .../contract.json | 1 + .../contract.json | 1 - examples/prisma7-adoption/src/main.ts | 2 +- .../prisma7-adoption/test/adoption.test.ts | 8 +- gotchas.md | 2 +- .../src/exports/authoring.ts | 5 + .../src/shared/authoring-type-choice-call.ts | 129 ++++++++ .../shared/authoring-type-constructor-call.ts | 8 +- .../src/shared/framework-authoring.ts | 2 +- .../test/authoring-type-choice-call.test.ts | 164 ++++++++++ .../commands/init/templates/code-templates.ts | 28 +- .../init/__snapshots__/templates.test.ts.snap | 28 +- .../cli/test/commands/init/templates.test.ts | 2 +- .../1-core/contract/src/exports/data-type.ts | 1 + .../1-core/contract/src/sql-data-type.ts | 28 +- .../contract/test/sql-data-type-fixtures.ts | 26 ++ .../test/sql-data-type.render.test.ts | 22 ++ .../sql-data-type.resolve-written.test.ts | 17 +- .../2-authoring/contract-prisma7/README.md | 6 +- .../contract-prisma7/src/defaults.ts | 43 ++- .../contract-prisma7/src/interpreter.ts | 6 + .../contract-prisma7/src/target-binding.ts | 3 + .../contract-prisma7/test/defaults.test.ts | 2 +- .../datetime-defaults/expected-contract.json | 34 +-- .../fixtures/defaults/expected-contract.json | 20 +- .../expected-contract.json | 10 +- .../list-defaults/expected-contract.json | 22 +- .../expected-contract.json | 14 +- .../expected-contract.json | 10 +- .../fixtures/scalars/expected-contract.json | 14 +- .../expected-contract.json | 12 +- .../expected-contract.json | 12 +- .../updated-at/expected-contract.json | 20 +- .../test/now-default-generators.test.ts | 10 +- .../test/sub-millisecond-defaults.test.ts | 32 ++ .../test/updated-at-generators.test.ts | 8 +- .../2-authoring/contract-psl/README.md | 2 +- .../contract-psl/src/data-type-default.ts | 83 +++-- .../2-authoring/contract-ts/README.md | 2 +- packages/2-sql-family/9-family/README.md | 2 +- .../core/migrations/contract-to-schema-ir.ts | 5 +- .../src/core/psl-build/column-types.ts | 76 ++++- .../psl-build/inferred-type-constructors.ts | 73 +++++ .../core/timestamp-now-runtime-generator.ts | 16 +- .../9-family/src/exports/psl-build.ts | 5 + .../test/contract-to-schema-ir.test.ts | 29 ++ .../inferred-type-constructors.test.ts | 147 ++++++++- .../timestamp-now-runtime-generator.test.ts | 29 ++ .../mysql/1-target/src/core/codecs.ts | 8 + .../mysql/1-target/test/codecs.test.ts | 12 + .../3-targets/postgres/1-target/README.md | 6 +- .../postgres/1-target/src/core/authoring.ts | 23 +- .../postgres/1-target/src/core/data-types.ts | 18 ++ .../postgres/1-target/src/core/date-codecs.ts | 18 +- .../src/core/date-time-now-generators.ts | 2 +- .../postgres/1-target/src/core/errors.ts | 9 +- .../src/core/migrations/planner-sql-checks.ts | 22 +- .../1-target/src/core/now-generators.ts | 4 +- .../1-target/src/core/prisma7-binding.ts | 37 +++ .../src/core/psl-build/psl-literals.ts | 10 + .../src/core/psl-infer/infer-model-blocks.ts | 12 +- .../src/core/psl-infer/infer-psl-contract.ts | 1 + .../src/core/psl-infer/infer-read-defaults.ts | 51 +++- .../junction-relation-field-names.ts | 2 +- .../src/core/psl-print/column-types.ts | 33 +- .../1-target/src/core/type-constructors.ts | 9 +- .../test/authoring-field-presets.test.ts | 1 + .../1-target/test/data-type-texts.test.ts | 20 +- .../test/date-codecs.sub-millisecond.test.ts | 40 +++ .../test/date-time-text-wording.test.ts | 9 + ...ting-column-types-without-temporal.test.ts | 1 + .../1-target/test/generator-functions.test.ts | 4 +- .../test/migrations/alter-type-check.test.ts | 16 +- .../migrations/column-type-comparison.test.ts | 23 ++ .../1-target/test/now-generators.test.ts | 2 + .../prisma7-binding.refused-defaults.test.ts | 34 +++ .../1-target/test/prisma7-type-map.test.ts | 6 +- .../marked-type-constructors.test.ts | 4 +- .../1-target/test/psl-infer/fixtures.ts | 3 +- .../test/psl-infer/infer-column-types.test.ts | 28 +- .../test/psl-infer/infer-parse-emit.test.ts | 72 +---- .../psl-infer/inferred-psl.round-trip.test.ts | 7 - .../inferred-psl/inferred-psl.core.test.ts | 2 +- .../inferred-psl.data-type-defaults.test.ts | 64 +++- .../inferred-psl.defaults-and-types.test.ts | 24 +- .../inferred-psl.literal-defaults.test.ts | 26 +- .../test/psl-print/convenience-types.test.ts | 52 ++++ .../test/psl-print/generated-values.test.ts | 6 +- .../test/temporal-unavailable.test.ts | 8 +- .../1-target/test/type-constructors.test.ts | 61 +--- ...-time-column-postcheck.integration.test.ts | 72 +++++ .../contract-builder/type-helpers.test-d.ts | 4 +- projects/datetime-convenience-types/plan.md | 4 + scripts/framework-vocabulary-thresholds.json | 2 +- skills/prisma-8/references/contract.md | 2 +- skills/prisma-8/references/quickstart.md | 2 +- .../temporal-type-aliases.real-packs.test.ts | 56 ++-- ...lane-commands-without-temporal.e2e.test.ts | 8 +- ...te-time-default-canonical-form.e2e.test.ts | 16 +- ...-fidelity.brace-array-defaults.e2e.test.ts | 2 +- ...trip-fidelity.prisma7-defaults.e2e.test.ts | 60 ++-- ...prisma7-dates-without-temporal.e2e.test.ts | 45 ++- .../sub-millisecond-defaults.test.ts | 62 ++++ ...nfer-roundtrip-runtime.integration.test.ts | 6 +- ...fused-defaults-migrate.integration.test.ts | 171 +++++++++++ ...onvenience-types-infer.integration.test.ts | 189 ++++++++++++ ...arked-types-round-trip.integration.test.ts | 12 +- .../date-time-types-print.integration.test.ts | 40 +-- ...rated-values-roundtrip.integration.test.ts | 4 +- ...onvenience-types-print.integration.test.ts | 77 +++++ .../_fixture-aliases/contract.prisma | 10 +- .../_fixture-aliases/generated/contract.d.ts | 79 ++--- .../_fixture-aliases/generated/contract.json | 109 +++---- .../_fixture-attributes/contract.prisma | 6 + .../generated/contract.d.ts | 244 ++++++++++++++- .../generated/contract.json | 286 +++++++++++++++++- .../_fixture-now/contract.prisma | 6 + .../_fixture-now/generated/contract.d.ts | 244 ++++++++++++++- .../_fixture-now/generated/contract.json | 286 +++++++++++++++++- ...ow-generators-readback.integration.test.ts | 8 +- .../type-aliases-parity.test.ts | 29 +- .../detection.test.ts | 92 ++++++ .../app/instructions.md | 4 +- .../app/instructions.md | 108 +++++++ 134 files changed, 3761 insertions(+), 899 deletions(-) rename examples/prisma7-adoption/migrations/snapshots/{cee288aa57be618663c6c6118ece25e6ae2049027a7337e8e04c1742faedfff3 => 97963644c7d9359d5e31879ed1c6bfdc39b666d0fd1d0f152525b055d3c574c3}/contract.d.ts (85%) create mode 100644 examples/prisma7-adoption/migrations/snapshots/97963644c7d9359d5e31879ed1c6bfdc39b666d0fd1d0f152525b055d3c574c3/contract.json delete mode 100644 examples/prisma7-adoption/migrations/snapshots/cee288aa57be618663c6c6118ece25e6ae2049027a7337e8e04c1742faedfff3/contract.json create mode 100644 packages/1-framework/1-core/framework-components/src/shared/authoring-type-choice-call.ts create mode 100644 packages/1-framework/1-core/framework-components/test/authoring-type-choice-call.test.ts create mode 100644 packages/2-sql-family/2-authoring/contract-prisma7/test/sub-millisecond-defaults.test.ts create mode 100644 packages/2-sql-family/9-family/test/timestamp-now-runtime-generator.test.ts create mode 100644 packages/3-targets/postgres/1-target/test/date-codecs.sub-millisecond.test.ts create mode 100644 packages/3-targets/postgres/1-target/test/prisma7-binding.refused-defaults.test.ts create mode 100644 packages/3-targets/postgres/1-target/test/psl-print/convenience-types.test.ts create mode 100644 packages/3-targets/postgres/2-adapter/test/migrations/add-date-time-column-postcheck.integration.test.ts create mode 100644 test/integration/test/convenience-date-time-types/sub-millisecond-defaults.test.ts create mode 100644 test/integration/test/prisma7-source/refused-defaults-migrate.integration.test.ts create mode 100644 test/integration/test/psl-infer/convenience-types-infer.integration.test.ts create mode 100644 test/integration/test/psl-print/prisma7-convenience-types-print.integration.test.ts create mode 100644 test/integration/test/upgrade-instructions/tools-write-convenience-types/detection.test.ts create mode 100644 upgrade-instructions/pending/tools-write-convenience-types/app/instructions.md diff --git a/docs/architecture docs/adrs/ADR 252 - An earlier Prisma version's schema is a contract source.md b/docs/architecture docs/adrs/ADR 252 - An earlier Prisma version's schema is a contract source.md index dbc122aa3821..3956b8c9617a 100644 --- a/docs/architecture docs/adrs/ADR 252 - An earlier Prisma version's schema is a contract source.md +++ b/docs/architecture docs/adrs/ADR 252 - An earlier Prisma version's schema is a contract source.md @@ -112,7 +112,7 @@ flowchart LR **Rules of the language live in the family authoring package.** `@internal/sql-contract-prisma7` holds everything that is true of the Prisma 7 language for the SQL family: blocks and attributes, relation pairing, junction tables, defaults, and the diagnostics. It knows nothing about a particular database and depends on no Prisma 7 package. -**Facts about the database come from the target through one binding.** `Prisma7TargetBinding` is the complete list of what a target must answer: the target pack and namespace factory, the datasource providers it accepts, the type map of what the earlier version creates for each scalar and native-type attribute, the native enum entity kind, the index types, the identifier byte limit, the junction relation field names, the "now" generator for each column codec, and how a literal default is read for a column whose codec does not take the written value. The Postgres target exports one instance. +**Facts about the database come from the target through one binding.** `Prisma7TargetBinding` is the complete list of what a target must answer: the target pack and namespace factory, the datasource providers it accepts, the type map of what the earlier version creates for each scalar and native-type attribute, the native enum entity kind, the index types, the identifier byte limit, the junction relation field names, the "now" generator for each column codec, how a literal default is read for a column whose codec does not take the written value, and the SQL expression of a default whose value the column's type holds but its codec cannot hold exactly. The Postgres target exports one instance. **The facade publishes one function** that wires the two together: diff --git a/docs/architecture docs/adrs/ADR 254 - Data types and casts.md b/docs/architecture docs/adrs/ADR 254 - Data types and casts.md index 0dd3258c9cdf..df71ad3c6d26 100644 --- a/docs/architecture docs/adrs/ADR 254 - Data types and casts.md +++ b/docs/architecture docs/adrs/ADR 254 - Data types and casts.md @@ -103,7 +103,7 @@ A codec has four methods: A codec is built with its descriptor, its data type and the column's parameters of that type, and `toDataTypeValue` ends with `dataTypeValueOf(json)`, which calls `DataType.valueFrom` with those parameters. A Mongo codec is the same: its `toDataTypeValue` returns a `DataTypeValue`. The wire value is what the driver exchanges with the database, and it is not always text: a Postgres `bytea` codec writes a `Uint8Array`, a SQLite integer codec reads a number, a Mongo codec writes a BSON value. `fromWire` and `toWire` take the per-call context of [ADR 207](ADR%20207%20-%20Codec%20call%20context%20per-query%20AbortSignal%20and%20column%20metadata.md) and are asynchronous as [ADR 204](ADR%20204%20-%20Single-Path%20Async%20Codec%20Runtime.md) describes. A codec never reads `contract.json` and never sees PSL text. -Codecs of one type differ only in the application value. `pg/int8@1` holds a `bigint` and `pg/int8number@1` a `number`; `pg/timestamptz-date@1`, `pg/timestamptz-temporal@1` and `pg/timestamptz-string@1` hold a `Date`, a `Temporal.Instant` and text. A codec refuses in `fromDataTypeValue` a value its application value cannot hold exactly: `pg/int8number@1` refuses digit text past 2^53, and the `Temporal` and `Date` codecs refuse `infinity`, which only the text codecs hold. A limit one target's storage has belongs to that target's codec: SQLite cannot store `NaN`, so `sqlite/real@1`, and `sql/float@1` as SQLite adapts it, refuse it in `toWire` and in both value methods, and the SQLite driver refuses a `NaN` parameter no codec encoded, such as one in raw SQL. +Codecs of one type differ only in the application value. `pg/int8@1` holds a `bigint` and `pg/int8number@1` a `number`; `pg/timestamptz-date@1`, `pg/timestamptz-temporal@1` and `pg/timestamptz-string@1` hold a `Date`, a `Temporal.Instant` and text. A codec refuses in `fromDataTypeValue` a value its application value cannot hold exactly: `pg/int8number@1` refuses digit text past 2^53, and the `Temporal` and `Date` codecs refuse `infinity`, which only the text codecs hold. The `Date` codecs also refuse digits below a millisecond, which the text and `Temporal` codecs hold. A limit one target's storage has belongs to that target's codec: SQLite cannot store `NaN`, so `sqlite/real@1`, and `sql/float@1` as SQLite adapts it, refuse it in `toWire` and in both value methods, and the SQLite driver refuses a `NaN` parameter no codec encoded, such as one in raw SQL. A limit of the type itself is the type's, as "Values" describes. A codec checks only parameters it keeps for itself: `arktype/json@1` validates the document against its schema in `fromDataTypeValue` and `fromWire`. @@ -229,9 +229,9 @@ Every consumer of a stored default reads it through the column's data type and c - **Loading a contract.** When the CLI loads a contract, the SQL family's `deserializeContract` reads every literal default through its column's type and refuses a value the type does not hold, with `CONTRACT.VALIDATION_FAILED` naming the column. No later step meets an invalid stored value. - **Writing a default into DDL.** The planner hands the column's value to its codec, `fromDataTypeValue` then `toWire`, and the adapter writes the wire value as a SQL literal of the column's type. The `DEFAULT` clause therefore holds the text the codec writes for every row: on SQLite a default and a row the application wrote for the same instant are one text, and on Postgres a year before 1 is written with ` BC`. The codec's methods are asynchronous, so this runs where DDL is lowered, which is asynchronous too. - **Reading a default the database reports.** Postgres reports a default as a literal with a cast, `'2024-01-01 00:00:00+00'::timestamp with time zone`; SQLite reports the literal its `DEFAULT` clause holds. The target takes the literal's body out of that text, which is what its driver would hand `fromWire` for a row of the column, and introspection reads it with the column's codec, `fromWire` then `toDataTypeValue`. Introspection is asynchronous and receives the contract, so this runs there, and the tree it builds holds the value ([ADR 235](ADR%20235%20-%20The%20schema%20differ%20walks%20two%20derived%20schema%20IRs.md): each side of a diff is built in the form it is compared in). A default the database holds as an expression, such as `now()`, is compared as expression text. A contract default written as a `sql` expression that holds only a literal, such as `'hello'::bytea`, has its literal read the same way, so the two sides compare as values; text is compared only where no codec can read a literal, and then case and spaces inside quotes count. A literal the codec cannot read, such as a value written outside Prisma that the application value cannot hold, is reported as a difference naming the column and the codec's message. -- **Comparing defaults.** `db verify` and the planner compare two values of the column's type for equality. A `Date` default is a value of its column's type like any other: the database's default is read through the column's codec, and the comparison never goes through `toISOString()`. When no database is in reach, both sides come from contracts and no codec is needed. +- **Comparing defaults.** `db verify` and the planner compare two values of the column's type for equality. A `Date` default is a value of its column's type like any other: the database's default is read through the column's codec, and the comparison never goes through `toISOString()`. On a column whose codec reads a `Date`, both sides are read as the application reads them, so a difference below a millisecond is not reported. When no database is in reach, both sides come from contracts and no codec is needed. - **Rows the database returns as JSON.** An `include` or an aggregated child row set comes back as JSON. Each column enters it through its codec's JSON projection ([codec authoring guide](../../reference/codec-authoring-guide.md#rows-the-database-returns-as-json)), and the runtime reads it with `fromWire`, the same method that reads an ordinary row. A projection puts into the JSON a value `fromWire` reads exactly, giving the same application value as an ordinary row. On Postgres a type whose value would enter the JSON in another form than its row's text is cast to text, `CAST(x AS text)`, including `json`, `jsonb` and vectors: a document embedded as JSON would come back already parsed, so `fromWire` would parse the string document `"42"` a second time, and a vector widened to double precision would read `0.10000000149011612` where a row reads `0.1`. A type whose JSON value `fromWire` already reads, such as a number, a boolean, or text like a uuid or an enum label, enters as the column itself, and its `fromWire` reads both forms; `pg/char` enters with its padding removed. The runtime has one way to read what the database returns, and the stored form never has to match the database's spelling. -- **Inferring a contract.** `contract infer` has no contract, so it reads a reported default with the codec of the type constructor it prints for the column, `fromWire` of the literal's body then `toDataTypeValue`, and an enum column's through `pg/text@1`. It prints the value as "Printing" describes. +- **Inferring a contract.** `contract infer` has no contract, so it reads a reported default with the codec of the type constructor it prints for the column, `fromWire` of the literal's body then `toDataTypeValue`, and an enum column's through `pg/text@1`. It prints a reported literal only when the codec reads it exactly, as it does for a JSON number the codec would round, and otherwise prints the raw expression. It prints the value as "Printing" describes. ## How a data type names its database type @@ -300,7 +300,7 @@ SQLite stores text, integers, reals and blobs, so its target declares `sqlite/te A type is stored as its own database type only when the database can record it. Declaring a SQLite column `DATETIME` or `JSON` would let introspection tell the types apart, but `JSON` gives the column numeric affinity, and SQLite would then store the document `"42"` as the integer 42. -**Comparing column types.** Two columns have the same type when they are stored as the same data type, with equal parameters in its normal form, and are both lists or both not. The rule is the same whether one column comes from the database or both come from contracts, as when the migration planner compares two contracts or the contract validator checks a many-to-many join table; without the stack's data types, the validator compares parameters as written. The database side's type is the one that claims what the database reports: by its texts, or for an enum by the kind of the type, read from the catalog with its schema and name, so the result does not depend on the search path. A column whose reported type no data type in the stack claims never equals another, and its mismatch names the reported type: `The database column is "point", which no data type in the configured stack declares.`, or for a SQLite column declared with no type, `The database column has no declared type.` After `ALTER COLUMN TYPE`, the Postgres planner checks the new type the same way: a type with texts by comparing `format_type` with its catalog text, an enum by identity, `atttypid = to_regtype(...)`, or the element type's `typarray` for a list. A column in the unbound namespace whose enum type lives outside `public` does not compare equal; this is a known limit. +**Comparing column types.** Two columns have the same type when they are stored as the same data type, with equal parameters in its normal form, and are both lists or both not. The rule is the same whether one column comes from the database or both come from contracts, as when the migration planner compares two contracts or the contract validator checks a many-to-many join table; without the stack's data types, the validator compares parameters as written. The database side's type is the one that claims what the database reports: by its texts, or for an enum by the kind of the type, read from the catalog with its schema and name, so the result does not depend on the search path. A column whose reported type no data type in the stack claims never equals another, and its mismatch names the reported type: `The database column is "point", which no data type in the configured stack declares.`, or for a SQLite column declared with no type, `The database column has no declared type.` After `ALTER COLUMN TYPE`, the Postgres planner checks the new type the same way: a type with texts by comparing `format_type` with its catalog text for the type as written (a bare `timestamptz` is `timestamp with time zone`, though its normal form counts the omitted precision as 6), an enum by identity, `atttypid = to_regtype(...)`, or the element type's `typarray` for a list. A column in the unbound namespace whose enum type lives outside `public` does not compare equal; this is a known limit. ## Columns and type constructors diff --git a/docs/architecture docs/adrs/ADR 275 - Client generators are field attributes, and a type alias is expanded into each column.md b/docs/architecture docs/adrs/ADR 275 - Client generators are field attributes, and a type alias is expanded into each column.md index 3d3bbc9bbdc0..83da45596a56 100644 --- a/docs/architecture docs/adrs/ADR 275 - Client generators are field attributes, and a type alias is expanded into each column.md +++ b/docs/architecture docs/adrs/ADR 275 - Client generators are field attributes, and a type alias is expanded into each column.md @@ -78,6 +78,8 @@ For Postgres, `now` maps each date and time codec to its generator. That map exi | `pg/date-temporal@1`, `pg/date-string@1` | `plainDateNow`, `dateStringNow` | none | | `pg/time-temporal@1`, `pg/time-string@1`, `pg/timetz@1` | `plainTimeNow`, `timeStringNow`, `timetzNow` | `precision` | +(Amended 2026-10-23: the string and `Date` timestamp codecs, `pg/timestamptz-string@1`, `pg/timestamp-string@1`, `pg/timestamptz-date@1` and `pg/timestamp-date@1`, also pass `precision`, and their generators round down to it, so the client holds the value PostgreSQL stores instead of one PostgreSQL rounds up.) + SQLite maps `sqlite/datetime@1` to `timestampNow`. Mongo maps `mongo/date@1` to `timestampNow`. `uuid`, `cuid`, `ulid`, `nanoid` and `ksuid` (new; a `ksuid` generator exists but no function names it) map every text-like codec of the target to their generators. The lists must be complete. Today the applicability lists name only `pg/text@1`, `sql/char@1` and `pg/uuid@1`, so `uuid()` on a SQLite `String` (`sqlite/text@1`) or a Postgres `VarChar` (`sql/varchar@1`) is refused; that is a defect this registry fixes. @@ -125,7 +127,7 @@ What this changes elsewhere: A pack's `type` namespace gains a type alias descriptor (new). Its output names a type constructor with fixed arguments and a generator function for each phase. The PSL interpreter and the TypeScript builder expand it exactly like a user alias. (Amended 2026-10-21: a contributed alias carries a type and generators only, and no database default. No contributed alias needs one, and a default would be a SQL concept on a framework descriptor that Mongo could not use. A database default can be added to the descriptor later, when a pack needs one, with its own check at assembly. Assembly checks the alias's fixed type parameters against its codec's parameter schema, and resolves its generator calls for the field length those parameters give, so a broken pack is refused with `CONTRACT.PACK_CONTRIBUTION_INVALID` before any schema is read.) -The Postgres target contributes `temporal.CreatedAt` and `temporal.UpdatedAt` on `Timestamptz`, with `String` and `JsDate` variants on the string and `Date` codecs (all new names). SQLite contributes `temporal.CreatedAt` and `temporal.UpdatedAt` on `DateTime`; Mongo the same on its `DateTime`. (Amended 2026-10-21: Mongo's aliases are on `Date`, because Mongo's `DateTime` constructor is deprecated. Both name the codec `mongo/date@1`, so the stored column is the same. The Postgres variants are named `temporal.CreatedAtString`, `temporal.UpdatedAtString`, `temporal.CreatedAtJsDate` and `temporal.UpdatedAtJsDate`.) Contributed aliases take no parameters, so a column with a precision is written with the attributes: +The Postgres target contributes `temporal.CreatedAt` and `temporal.UpdatedAt` on `Timestamptz`, with `String` and `JsDate` variants on the string and `Date` codecs (all new names). SQLite contributes `temporal.CreatedAt` and `temporal.UpdatedAt` on `DateTime`; Mongo the same on its `DateTime`. (Amended 2026-10-21: Mongo's aliases are on `Date`, because Mongo's `DateTime` constructor is deprecated. Both name the codec `mongo/date@1`, so the stored column is the same. The Postgres variants are named `temporal.CreatedAtString`, `temporal.UpdatedAtString`, `temporal.CreatedAtJsDate` and `temporal.UpdatedAtJsDate`.) (Amended by ADR 279: the Postgres aliases are built on the convenience type `DateTime`, and the variants are removed.) Contributed aliases take no parameters, so a column with a precision is written with the attributes: ```prisma model Event { diff --git a/docs/reference/error-reference.md b/docs/reference/error-reference.md index 24c148653289..1db9649075af 100644 --- a/docs/reference/error-reference.md +++ b/docs/reference/error-reference.md @@ -1375,8 +1375,8 @@ A raw-SQL tagged template interpolated a JS value whose type cannot be auto-infe A value that only a `Temporal` implementation can produce or read was needed in a process that has neither a global `Temporal` nor the fallback `Temporal` of the Postgres target. Two paths raise it, and they carry different metadata: -- A Temporal-backed codec (`pg/date-temporal@1`, `pg/timestamp-temporal@1`, `pg/timestamptz-temporal@1`, `pg/time-temporal@1`) decoding a value. Payload: `codecId`, `operation` (`'decode'`). Encoding a `Temporal` value needs no implementation and does not raise this error. -- The `instantNow`, `plainDateTimeNow`, `plainDateNow` or `plainTimeNow` mutation-default generator producing a value, for `temporal.createdAt()`, `temporal.updatedAt()`, or for a `temporal.timestamptz(…)`, `timestamp(…)`, `date(…)` or `time(…)` preset given an `onCreate`/`onUpdate` of `'now'`. No codec is involved. Payload: `generatorId`. +- A Temporal-backed codec (`pg/date-temporal@1`, `pg/time-temporal@1`, `pg/timestamp-temporal@1`, `pg/timestamptz-temporal@1`, `pg/timestamp-instant@1`) decoding a value. These are the codecs of `DateTemporal`, `TimeTemporal`, `TimestampTemporal`, `TimestamptzTemporal` and `TimestampInstant`, which the convenience types choose with `as: temporal`. Payload: `codecId`, `operation` (`'decode'`). Encoding a `Temporal` value needs no implementation and does not raise this error. +- The `instantNow`, `plainDateTimeNow`, `plainDateNow` or `plainTimeNow` mutation-default generator producing a value, for `@onCreate(now())` or `@onUpdate(now())` on a column with one of those codecs, for `temporal.createdAt()` and `temporal.updatedAt()`, or for a `temporal.timestamptz(…)`, `timestamp(…)`, `date(…)` or `time(…)` preset given an `onCreate`/`onUpdate` of `'now'`. No codec is involved. Payload: `generatorId`. The check is lazy: registering the target, validating a contract, building a runtime or a client, resolving a descriptor and constructing a codec instance all succeed without `Temporal`. Only producing or interpreting a value fails, including the first read of a `db.enums` enum whose codec is a Temporal codec. @@ -1384,7 +1384,7 @@ It is raised on **reads**, because the check is the first thing a Temporal codec This error never occurs in the control plane. The CLI commands, `node migration.ts`, the Vite plugin, the language server and the programmatic control client all load `prisma.config.ts` or the control descriptors, and so load the target's control entry. The target's control entry (`@internal/target-postgres/control`) sets the fallback `Temporal`, from `temporal-polyfill`, when it is loaded. It does not set `globalThis.Temporal`. `temporal-polyfill` is a required peer dependency of `@prisma/orm-postgres` and `@prisma/orm-target-postgres`. npm, pnpm and bun install it automatically; a project that uses Yarn adds it to its own dependencies. When it is not installed, loading the control entry fails because Node.js cannot find the package `temporal-polyfill`, not with this error. The fallback is held once per process, and every Postgres codec in the process uses it. An application process that loads no control-plane code and has no global `Temporal` gets `RUNTIME.TEMPORAL_UNAVAILABLE`. An application that loads control-plane code in its own process, such as server code under `vite dev` with the Prisma Vite plugin or a script that calls the control client, decodes dates without its own `Temporal`, and then fails in production. An application that uses the Temporal codecs must load its own `Temporal`. -In the application, install a global implementation before any query runs (`import 'temporal-polyfill/full/global'`), or author the column with its `*String` type (`DateString`, `TimestampString(p)`, `TimestamptzString(p)`, `TimeString(p)`) to read and write ISO 8601 text, which needs no Temporal at all. +In the application, install a global implementation before any query runs (`import 'temporal-polyfill/full/global'`), or write the column as a convenience type at its default, which needs no Temporal at all: `DateTime` reads a JavaScript `Date`, and `Date`, `Time` and `Duration` read ISO 8601 strings. `temporal.CreatedAt` and `temporal.UpdatedAt` are `DateTime` and need none either. ### RUNTIME.TRANSACTION_ALREADY_OPEN diff --git a/docs/reference/scalar-types.md b/docs/reference/scalar-types.md index f3bcba485bcb..0a5fcf73ae32 100644 --- a/docs/reference/scalar-types.md +++ b/docs/reference/scalar-types.md @@ -84,7 +84,7 @@ The date and time types have two layers ([ADR 279](../architecture%20docs/adrs/A | `Duration(as?, storage?, precision?)` | `string` (default) | `interval` (default) | `IntervalString` | | | `parts` | `interval` | `IntervalParts` | -The arguments are written as bare names, by name or in this order: `DateTime(as: string, precision: 6)`, `DateTime(temporal, timestamp)`. A combination the table does not list, such as `Time(as: temporal, storage: timetz)` or `Date(as: jsDate)`, is refused at the argument that makes it invalid, naming the values it accepts; so is a `precision` the chosen type does not take. No default needs a global `Temporal`. In a TypeScript contract the helpers take the same arguments as one object, `type.DateTime({ as: 'string', precision: 6 })`, and are typed as the explicit type they choose; a combination the table does not list, or an argument the chosen type does not take, does not compile. `contract print` and `contract infer` write the explicit type. +The arguments are written as bare names, by name or in this order: `DateTime(as: string, precision: 6)`, `DateTime(temporal, timestamp)`. A combination the table does not list, such as `Time(as: temporal, storage: timetz)` or `Date(as: jsDate)`, is refused at the argument that makes it invalid, naming the values it accepts; so is a `precision` the chosen type does not take. No default needs a global `Temporal`. In a TypeScript contract the helpers take the same arguments as one object, `type.DateTime({ as: 'string', precision: 6 })`, and are typed as the explicit type they choose; a combination the table does not list, or an argument the chosen type does not take, does not compile. | PSL name | TS helper | Codec id | Storage type | Application type | | --- | --- | --- | --- | --- | @@ -123,11 +123,20 @@ The arguments are written as bare names, by name or in this order: `DateTime(as: | `Bytes` | `field.bytes()` | `pg/bytea@1` | `bytea` | `Uint8Array` | | `pg.enum(Name)` | — | `pg/enum@1` | the native enum type | `string` | -`TimestampJsDate` and `TimestampInstant` read the date and time a `timestamp` column stores as UTC, and write a value's UTC date and time with no offset, so a value the application writes reads back as the same instant whatever the time zone of the server, the session or the application. A database default is different: PostgreSQL fills a `timestamp` column's `@default(now())` in the session's time zone, so on a server whose `TimeZone` is not UTC that value reads back shifted by the zone's offset. `@onCreate(now())` writes the client's time in UTC and reads back unshifted. A `Date` holds milliseconds, so on a `TimestampJsDate` column with a precision above 3 a read drops the extra digits, and an equality filter with a value read back misses a row stored with microseconds. Use `TimestampInstant` or `TimestampString` to keep them. They refuse `infinity`, `-infinity` and a value after `+275760-09-13T00:00:00`, which no `Date` or `Temporal.Instant` holds, and name `TimestampString`, which holds them. `IntervalString` reads every `IntervalStyle` and writes an ISO 8601 duration; it refuses any other text on write. +`TimestampJsDate` and `TimestampInstant` read the date and time a `timestamp` column stores as UTC, and write a value's UTC date and time with no offset, so a value the application writes reads back as the same instant whatever the time zone of the server, the session or the application. A database default is different: PostgreSQL fills a `timestamp` column's `@default(now())` in the session's time zone, so on a server whose `TimeZone` is not UTC that value reads back shifted by the zone's offset. `@onCreate(now())` writes the client's time in UTC and reads back unshifted. On a column read as a `Date` with a precision below 3, `now()` is rounded down to that precision, so the client holds the value the column stores. A `Date` holds milliseconds, so on a `TimestampJsDate` column with a precision above 3 a read drops the extra digits, and an equality filter with a value read back misses a row stored with microseconds. Use `TimestampInstant` or `TimestampString` to keep them. They refuse `infinity`, `-infinity` and a value after `+275760-09-13T00:00:00`, which no `Date` or `Temporal.Instant` holds, and name `TimestampString`, which holds them. `IntervalString` reads every `IntervalStyle` and writes an ISO 8601 duration; it refuses any other text on write. A literal default of a date, time, interval or bytes type is written with the tag of its type, as in `@default(timestamptz`2024-01-01T00:00:00Z`)` or `@default(bytea`\x0102`)`; a quoted string is refused with a message that shows the tag. It is stored in the one form its data type stores, whichever codec the column uses and however the default was written, and a default with more fraction digits or more scale than its column holds is refused rather than rounded; [ADR 254](../architecture%20docs/adrs/ADR%20254%20-%20Data%20types%20and%20casts.md#date-and-time-types) states each form. -The tools that write a schema for you use the text date and time types. In a PSL schema, `prisma orm init` writes `createdAt TimestamptzString @default(now())` and `updatedAt temporal.updatedAtString()`. In a TypeScript contract, it writes `field.temporal.createdAtString()` and `field.temporal.updatedAtString()`. All of these are `TimestamptzString` columns. `prisma contract infer` and `prisma7Schema(...)` write `TimestampString(p)`, `TimestamptzString(p)`, `DateString` and `TimeString(p)`. Five types read and write `Temporal` values: `TimestamptzTemporal`, `TimestampTemporal`, `TimestampInstant`, `DateTemporal` and `TimeTemporal`, which the convenience types choose with `as: temporal`. Their TypeScript types come from `temporal-spec`, so a project needs no global `Temporal` declarations to type them. An application that uses them needs a global `Temporal`: on a runtime without one, such as Node.js 24, install it, for example with `import 'temporal-polyfill/full/global'`. +The tools that write a schema for you write the convenience types, with only the arguments that differ from the defaults, and the explicit type only where no convenience call becomes it, such as `TimestampTemporal` for a `timestamp` read as `Temporal.PlainDateTime`. + +- `prisma orm init` writes `createdAt DateTime @default(now()) @onCreate(now())` and `updatedAt temporal.UpdatedAt` in a PSL schema, and `field.column(type.DateTime()).default(now()).onCreate(gen.now())` and `field.column(type.temporal.UpdatedAt())` in a TypeScript contract. +- `prisma contract infer` takes the default path for each column: a `timestamptz(3)` column is `DateTime`, a `timestamp(3)` column `DateTime(storage: timestamp)`, a `date` column `Date`, a `time(0)` column `Time(precision: 0)`, a `timetz` column `Time(storage: timetz)` and an `interval` column `Duration`. A column with no declared precision holds microseconds, as precision 6 does, so a `timestamptz` column is `DateTime(precision: 6)`; `db verify` and the migration planner count an omitted precision on a date or time column as 6. A `Date` holds milliseconds, so a timestamp column with more digits reads with them dropped, and a timestamp default a `Date` cannot hold, such as one with microseconds or `infinity`, is written as its raw `sql` expression. To keep the digits, change the field to `DateTime(as: temporal)` or `DateTime(as: string)`. +- `prisma contract print` writes each column as the convenience call that becomes its explicit type exactly, such as `DateTime(as: temporal)` for `TimestamptzTemporal`, and otherwise the explicit type. +- `prisma7Schema(...)` reads Prisma 7's `DateTime` as `TimestampJsDate(3)`, a `timestamp(3)` column read as a `Date` as Prisma 7 read it, printed `DateTime(storage: timestamp)`, and `@db.Timestamptz(p)` as `TimestamptzJsDate(p)`. + +On a column read as a `Date` (`TimestamptzJsDate`, `TimestampJsDate`, and `DateTime` at its default), a default with digits below a millisecond is refused: write it with a string or `Temporal` type, or as a `sql` expression. A TypeScript contract cannot write one, because its `.default()` takes a `Date`. `db verify` compares a default on such a column as the application reads it, so it does not see a difference smaller than a millisecond. + +`temporal.CreatedAt` is `DateTime @onCreate(now())` and `temporal.UpdatedAt` is `DateTime @onCreate(now()) @onUpdate(now())`. Five types read and write `Temporal` values: `TimestamptzTemporal`, `TimestampTemporal`, `TimestampInstant`, `DateTemporal` and `TimeTemporal`, which the convenience types choose with `as: temporal`. Their TypeScript types come from `temporal-spec`, so a project needs no global `Temporal` declarations to type them. An application that uses them needs a global `Temporal`: on a runtime without one, such as Node.js 24, install it, for example with `import 'temporal-polyfill/full/global'`. A text date or time type reads and writes ISO 8601, the form its data type stores: `TimestamptzString` reads `2024-01-01T00:00:00Z`, the instant in UTC, whatever the session's time zone; `TimestampString` reads `2024-01-01T12:34:56.5`; `DateString` reads `2024-01-01`; `TimeString` reads `12:34:56`; and `Timetz` reads `12:34:56+02:00`, or `12:34:56Z` for a zero offset. A year before 1 or after 9999 is a sign and six digits, as in `-000043-03-15` for 44 BC. `infinity` and `-infinity` read as those words. A value PostgreSQL holds beyond the range of the `Temporal` and `Date` types reads in the same shape: the time `24:00:00`, and a date with a seven-digit year, such as `+5874897-12-31`. A value written in another form PostgreSQL reads is stored as the value it names and reads back in ISO 8601: `2024-01-01 01:00:00+01` written to a `TimestamptzString` column reads back as `2024-01-01T00:00:00Z`; a literal default or enum member must be written in the stored form. @@ -172,4 +181,4 @@ The PSL name on each target for a concept, and the Prisma 6/7 name a migrating s A Prisma 6 MongoDB schema read with `prisma6Schema` may keep its native types. Each native type Prisma 6.19 accepts gives the field the MongoDB type of what Prisma 6 stores: `Int @db.Int` is `Int32`, `Int @db.Long` is `Int64Number` like a plain `Int`, `BigInt @db.Long` is `Int64`, `Bytes @db.ObjectId` is `ObjectId`, and `@db.String`, `@db.Bool`, `@db.Double`, `@db.Date`, `@db.BinData` and `@db.Json` are the same type as the plain field. `DateTime @db.Timestamp` stores a BSON timestamp, which no MongoDB scalar type holds, so `prisma6Schema` refuses it. -A Prisma 7 PostgreSQL schema read with `prisma7Schema`, or a database Prisma 7 created and `contract infer` reads, gets `TimestampString(3)` for `DateTime`, and `TimestampString(p)`, `TimestamptzString(p)`, `DateString` and `TimeString(p)` for `@db.Timestamp`, `@db.Timestamptz`, `@db.Date` and `@db.Time`. The PostgreSQL cell above is what a new Prisma 8 schema writes for the concept. +A Prisma 7 PostgreSQL schema read with `prisma7Schema`, or a database Prisma 7 created and `contract infer` reads, gets `TimestampJsDate(3)` for `DateTime`, written `DateTime(storage: timestamp)`, and `TimestampJsDate(p)`, `TimestamptzJsDate(p)`, `DateString`, `TimeString(p)` and `Timetz(p)` for `@db.Timestamp`, `@db.Timestamptz`, `@db.Date`, `@db.Time` and `@db.Timetz`, so a `DateTime` field still reads a JavaScript `Date`. The PostgreSQL cell above is what a new Prisma 8 schema writes for the concept. diff --git a/examples/prisma7-adoption/README.md b/examples/prisma7-adoption/README.md index 0d06cd746504..2ee95bc0782c 100644 --- a/examples/prisma7-adoption/README.md +++ b/examples/prisma7-adoption/README.md @@ -110,7 +110,7 @@ A Prisma 7 schema that uses `@ignore`, `@@ignore` or `Unsupported("...")` fields - The guide's `prisma7.config.ts` sets `datasource.url` to `process.env["DATABASE_URL"]`, which is `string | undefined`; under `exactOptionalPropertyTypes` that does not type-check, so this example adds the `datasource` block only when the variable is set. `prisma7 generate` runs without a database either way. - Prisma 7 rejects `url` inside the `datasource` block; the URL lives only in `prisma7.config.ts` (Prisma 7) and `prisma.config.ts` (Prisma 8), both reading the same `DATABASE_URL` from `.env`. - Where Prisma 7 named a primary key or foreign key differently from Prisma 8, the contract states Prisma 7's name: a `map` on `@id`, `@@id` or `@relation`, a junction's primary key such as `_PostToTag_AB_pkey`, and a foreign key name Prisma 7 cut to 63 bytes. `db verify` does not compare these names, but migrations drop and rename constraints by them. -- Prisma 8 reads a `DateTime` column as the text PostgreSQL prints, such as `2026-09-14 10:00:00.123` (UTC, as Prisma 7 wrote it), not as a JavaScript `Date`. `@db.Timestamptz`, `@db.Date` and `@db.Time` columns read as text too, so the application needs no `Temporal`. +- Prisma 8 reads a `DateTime` column as a JavaScript `Date`, as Prisma 7 did: `prisma7Schema` reads it as `DateTime(storage: timestamp)`, a `timestamp(3)` column whose value is stored as UTC. A `@db.Timestamptz` column also reads as a `Date`; `@db.Date` and `@db.Time` columns read as ISO 8601 strings, such as `2026-09-14` and `10:00:00`. The application needs no `Temporal`. - `pnpm sign` creates `migrations/` (a snapshot of the signed contract and the `db` ref). It is Prisma 8's record of what was signed and is committed here; in phase 4 the first `migration plan` builds the baseline from it. - The Prisma 8 CLI prints JSON only with `--json`. Without it, a pipe, a file or an agent receives the command's tab-separated data lines on stdout, and the human output goes to stderr. diff --git a/examples/prisma7-adoption/generated/prisma8/contract.d.ts b/examples/prisma7-adoption/generated/prisma8/contract.d.ts index 7282c2ae09be..cbb02d20fa4f 100644 --- a/examples/prisma7-adoption/generated/prisma8/contract.d.ts +++ b/examples/prisma7-adoption/generated/prisma8/contract.d.ts @@ -23,9 +23,9 @@ import type { } from '@prisma/orm-postgres/contract/types'; export type StorageHash = - StorageHashBase<'9268d6eef6e976bf77ca87083b15cd8eb247c9bf7fe4d2929f56a23bfa36d9ec'>; + StorageHashBase<'97963644c7d9359d5e31879ed1c6bfdc39b666d0fd1d0f152525b055d3c574c3'>; export type ExecutionHash = - ExecutionHashBase<'8552d69a879d0f7c4482b9b7a2e9932db9ea33b3421ed778e87bf04b17076b10'>; + ExecutionHashBase<'9a5883a46a0f8f232598bc988951650620bebba27bb6d25c01513cb5e3fd94b9'>; export type ProfileHash = ProfileHashBase<'3916f444a8a17ad749191acf9e08dad97d1a327b88c2f1d45d12f240296aa8b2'>; @@ -536,7 +536,7 @@ type ContractBase = Omit< columns: { readonly createdAt: { readonly dataType: 'pg/timestamp'; - readonly codecId: 'pg/timestamp-string@1'; + readonly codecId: 'pg/timestamp-date@1'; readonly nullable: false; readonly default: { readonly kind: 'function'; readonly expression: 'now()' }; readonly typeParams: { readonly precision: 3 }; @@ -577,7 +577,7 @@ type ContractBase = Omit< }; readonly updatedAt: { readonly dataType: 'pg/timestamp'; - readonly codecId: 'pg/timestamp-string@1'; + readonly codecId: 'pg/timestamp-date@1'; readonly nullable: false; readonly typeParams: { readonly precision: 3 }; readonly many: false; @@ -772,7 +772,7 @@ type ContractBase = Omit< readonly nullable: false; readonly type: { readonly kind: 'scalar'; - readonly codecId: 'pg/timestamp-string@1'; + readonly codecId: 'pg/timestamp-date@1'; readonly typeParams: { readonly precision: 3 }; }; }; @@ -800,7 +800,7 @@ type ContractBase = Omit< readonly nullable: false; readonly type: { readonly kind: 'scalar'; - readonly codecId: 'pg/timestamp-string@1'; + readonly codecId: 'pg/timestamp-date@1'; readonly typeParams: { readonly precision: 3 }; }; }; @@ -866,7 +866,7 @@ type ContractBase = Omit< readonly defaults: readonly [ { readonly onCreate: { - readonly id: 'timestampStringNow'; + readonly id: 'timestampNow'; readonly kind: 'generator'; readonly params: { readonly precision: 3 }; }; @@ -878,12 +878,12 @@ type ContractBase = Omit< }, { readonly onCreate: { - readonly id: 'timestampStringNow'; + readonly id: 'timestampNow'; readonly kind: 'generator'; readonly params: { readonly precision: 3 }; }; readonly onUpdate: { - readonly id: 'timestampStringNow'; + readonly id: 'timestampNow'; readonly kind: 'generator'; readonly params: { readonly precision: 3 }; }; diff --git a/examples/prisma7-adoption/generated/prisma8/contract.json b/examples/prisma7-adoption/generated/prisma8/contract.json index 479758e006e3..3799b1f0b9fa 100644 --- a/examples/prisma7-adoption/generated/prisma8/contract.json +++ b/examples/prisma7-adoption/generated/prisma8/contract.json @@ -270,7 +270,7 @@ "createdAt": { "nullable": false, "type": { - "codecId": "pg/timestamp-string@1", + "codecId": "pg/timestamp-date@1", "kind": "scalar", "typeParams": { "precision": 3 @@ -311,7 +311,7 @@ "updatedAt": { "nullable": false, "type": { - "codecId": "pg/timestamp-string@1", + "codecId": "pg/timestamp-date@1", "kind": "scalar", "typeParams": { "precision": 3 @@ -490,7 +490,7 @@ "User": { "columns": { "createdAt": { - "codecId": "pg/timestamp-string@1", + "codecId": "pg/timestamp-date@1", "dataType": "pg/timestamp", "default": { "expression": "now()", @@ -539,7 +539,7 @@ } }, "updatedAt": { - "codecId": "pg/timestamp-string@1", + "codecId": "pg/timestamp-date@1", "dataType": "pg/timestamp", "nullable": false, "typeParams": { @@ -654,15 +654,15 @@ "kind": "postgres-schema" } }, - "storageHash": "9268d6eef6e976bf77ca87083b15cd8eb247c9bf7fe4d2929f56a23bfa36d9ec" + "storageHash": "97963644c7d9359d5e31879ed1c6bfdc39b666d0fd1d0f152525b055d3c574c3" }, "execution": { - "executionHash": "8552d69a879d0f7c4482b9b7a2e9932db9ea33b3421ed778e87bf04b17076b10", + "executionHash": "9a5883a46a0f8f232598bc988951650620bebba27bb6d25c01513cb5e3fd94b9", "mutations": { "defaults": [ { "onCreate": { - "id": "timestampStringNow", + "id": "timestampNow", "kind": "generator", "params": { "precision": 3 @@ -676,14 +676,14 @@ }, { "onCreate": { - "id": "timestampStringNow", + "id": "timestampNow", "kind": "generator", "params": { "precision": 3 } }, "onUpdate": { - "id": "timestampStringNow", + "id": "timestampNow", "kind": "generator", "params": { "precision": 3 diff --git a/examples/prisma7-adoption/migrations/app/refs/db.json b/examples/prisma7-adoption/migrations/app/refs/db.json index e6cba861b2b1..fce4f374d9d3 100644 --- a/examples/prisma7-adoption/migrations/app/refs/db.json +++ b/examples/prisma7-adoption/migrations/app/refs/db.json @@ -1,4 +1,4 @@ { - "hash": "cee288aa57be618663c6c6118ece25e6ae2049027a7337e8e04c1742faedfff3", + "hash": "97963644c7d9359d5e31879ed1c6bfdc39b666d0fd1d0f152525b055d3c574c3", "invariants": [] } diff --git a/examples/prisma7-adoption/migrations/snapshots/cee288aa57be618663c6c6118ece25e6ae2049027a7337e8e04c1742faedfff3/contract.d.ts b/examples/prisma7-adoption/migrations/snapshots/97963644c7d9359d5e31879ed1c6bfdc39b666d0fd1d0f152525b055d3c574c3/contract.d.ts similarity index 85% rename from examples/prisma7-adoption/migrations/snapshots/cee288aa57be618663c6c6118ece25e6ae2049027a7337e8e04c1742faedfff3/contract.d.ts rename to examples/prisma7-adoption/migrations/snapshots/97963644c7d9359d5e31879ed1c6bfdc39b666d0fd1d0f152525b055d3c574c3/contract.d.ts index 21be02e58726..7841e9099d4b 100644 --- a/examples/prisma7-adoption/migrations/snapshots/cee288aa57be618663c6c6118ece25e6ae2049027a7337e8e04c1742faedfff3/contract.d.ts +++ b/examples/prisma7-adoption/migrations/snapshots/97963644c7d9359d5e31879ed1c6bfdc39b666d0fd1d0f152525b055d3c574c3/contract.d.ts @@ -3,11 +3,12 @@ // To regenerate, run: prisma contract emit import type { CodecTypes as PgTypes, - TimestampString, + ParameterizedOutputTypesForTypeParams as PgParameterizedOutputTypes, } from '@prisma/orm-postgres/target/codec-types'; import type { QueryOperationTypes as PgTargetQueryOps } from '@prisma/orm-postgres/target/operation-types'; import type { + ColumnTypesFromStorage, ContractWithTypeMaps, RelationKeys, TypeMaps as TypeMapsType, @@ -15,15 +16,16 @@ import type { import type { Contract as ContractType, ExecutionHashBase, + FieldTypesFromDomain, NamespaceId, ProfileHashBase, StorageHashBase, } from '@prisma/orm-postgres/contract/types'; export type StorageHash = - StorageHashBase<'cee288aa57be618663c6c6118ece25e6ae2049027a7337e8e04c1742faedfff3'>; + StorageHashBase<'97963644c7d9359d5e31879ed1c6bfdc39b666d0fd1d0f152525b055d3c574c3'>; export type ExecutionHash = - ExecutionHashBase<'76ee8948067fff39e1b787eafdf2c9319ebe5e0f5029f71536b4f58b92d7790e'>; + ExecutionHashBase<'380511653781da301aa7612ce64f0ac7b74bedf70c783143bab5ae56d5295037'>; export type ProfileHash = ProfileHashBase<'3916f444a8a17ad749191acf9e08dad97d1a327b88c2f1d45d12f240296aa8b2'>; @@ -41,6 +43,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/float8@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; @@ -90,6 +96,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; @@ -99,6 +109,14 @@ export type AggregateTypes = { readonly output: 'pg/time-temporal@1'; readonly nullable: true; }; + readonly 'pg/timestamp-date@1': { + readonly output: 'pg/timestamp-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-instant@1': { + readonly output: 'pg/timestamp-instant@1'; + readonly nullable: true; + }; readonly 'pg/timestamp-string@1': { readonly output: 'pg/timestamp-string@1'; readonly nullable: true; @@ -150,6 +168,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true }; @@ -159,6 +181,14 @@ export type AggregateTypes = { readonly output: 'pg/time-temporal@1'; readonly nullable: true; }; + readonly 'pg/timestamp-date@1': { + readonly output: 'pg/timestamp-date@1'; + readonly nullable: true; + }; + readonly 'pg/timestamp-instant@1': { + readonly output: 'pg/timestamp-instant@1'; + readonly nullable: true; + }; readonly 'pg/timestamp-string@1': { readonly output: 'pg/timestamp-string@1'; readonly nullable: true; @@ -202,6 +232,10 @@ export type AggregateTypes = { readonly 'pg/int4@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; readonly 'pg/int8@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true }; + readonly 'pg/interval-string@1': { + readonly output: 'pg/interval-string@1'; + readonly nullable: true; + }; readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true }; readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true }; @@ -234,152 +268,85 @@ type DefaultLiteralValue = CodecId extends keyo ? Encoded : CodecTypes[CodecId]['json'] : Encoded; +type ParameterizedOutputTypeFunctions = PgParameterizedOutputTypes; -export type FieldOutputTypes = { - readonly public: { - readonly Post: { - readonly authorId: CodecTypes['pg/int4@1']['output']; - readonly content: CodecTypes['pg/text@1']['output'] | null; - readonly id: CodecTypes['pg/int4@1']['output']; - readonly published: CodecTypes['pg/bool@1']['output']; - readonly title: CodecTypes['pg/text@1']['output']; - readonly viewCount: CodecTypes['pg/int4@1']['output']; - }; - readonly PostToTag: { - readonly A: CodecTypes['pg/int4@1']['output']; - readonly B: CodecTypes['pg/int4@1']['output']; - }; - readonly Tag: { - readonly id: CodecTypes['pg/int4@1']['output']; - readonly name: CodecTypes['pg/text@1']['output']; - }; - readonly User: { - readonly createdAt: TimestampString<3>; - readonly email: CodecTypes['pg/text@1']['output']; - readonly id: CodecTypes['pg/int4@1']['output']; - readonly name: CodecTypes['pg/text@1']['output'] | null; - readonly role: 'USER' | 'ADMIN'; - readonly updatedAt: TimestampString<3>; - }; - }; -}; -export type FieldInputTypes = { +type FieldTypeOverrides = { readonly public: { - readonly Post: { - readonly authorId: CodecTypes['pg/int4@1']['input']; - readonly content: CodecTypes['pg/text@1']['input'] | null; - readonly id: CodecTypes['pg/int4@1']['input']; - readonly published: CodecTypes['pg/bool@1']['input']; - readonly title: CodecTypes['pg/text@1']['input']; - readonly viewCount: CodecTypes['pg/int4@1']['input']; - }; - readonly PostToTag: { - readonly A: CodecTypes['pg/int4@1']['input']; - readonly B: CodecTypes['pg/int4@1']['input']; - }; - readonly Tag: { - readonly id: CodecTypes['pg/int4@1']['input']; - readonly name: CodecTypes['pg/text@1']['input']; - }; - readonly User: { - readonly createdAt: CodecTypes['pg/timestamp-string@1']['input']; - readonly email: CodecTypes['pg/text@1']['input']; - readonly id: CodecTypes['pg/int4@1']['input']; - readonly name: CodecTypes['pg/text@1']['input'] | null; - readonly role: 'USER' | 'ADMIN'; - readonly updatedAt: CodecTypes['pg/timestamp-string@1']['input']; - }; - }; -}; -export type StorageColumnTypes = { - readonly public: { - readonly _PostToTag: { - readonly A: CodecTypes['pg/int4@1']['output']; - readonly B: CodecTypes['pg/int4@1']['output']; - }; - readonly Post: { - readonly authorId: CodecTypes['pg/int4@1']['output']; - readonly content: CodecTypes['pg/text@1']['output'] | null; - readonly id: CodecTypes['pg/int4@1']['output']; - readonly published: CodecTypes['pg/bool@1']['output']; - readonly title: CodecTypes['pg/text@1']['output']; - readonly viewCount: CodecTypes['pg/int4@1']['output']; - }; - readonly Tag: { - readonly id: CodecTypes['pg/int4@1']['output']; - readonly name: CodecTypes['pg/text@1']['output']; - }; - readonly User: { - readonly createdAt: TimestampString<3>; - readonly email: CodecTypes['pg/text@1']['output']; - readonly id: CodecTypes['pg/int4@1']['output']; - readonly name: CodecTypes['pg/text@1']['output'] | null; - readonly role: 'USER' | 'ADMIN'; - readonly updatedAt: TimestampString<3>; + readonly models: { + readonly User: { + readonly role: { readonly output: 'USER' | 'ADMIN'; readonly input: 'USER' | 'ADMIN' }; + }; }; }; }; -export type StorageColumnInputTypes = { +export type FieldOutputTypes = FieldTypesFromDomain< + ContractBase['domain']['namespaces'], + CodecTypes, + ParameterizedOutputTypeFunctions, + FieldTypeOverrides, + 'output' +>; +export type FieldInputTypes = FieldTypesFromDomain< + ContractBase['domain']['namespaces'], + CodecTypes, + ParameterizedOutputTypeFunctions, + FieldTypeOverrides, + 'input' +>; +type ColumnTypeOverrides = { readonly public: { - readonly _PostToTag: { - readonly A: CodecTypes['pg/int4@1']['input']; - readonly B: CodecTypes['pg/int4@1']['input']; - }; - readonly Post: { - readonly authorId: CodecTypes['pg/int4@1']['input']; - readonly content: CodecTypes['pg/text@1']['input'] | null; - readonly id: CodecTypes['pg/int4@1']['input']; - readonly published: CodecTypes['pg/bool@1']['input']; - readonly title: CodecTypes['pg/text@1']['input']; - readonly viewCount: CodecTypes['pg/int4@1']['input']; - }; - readonly Tag: { - readonly id: CodecTypes['pg/int4@1']['input']; - readonly name: CodecTypes['pg/text@1']['input']; - }; readonly User: { - readonly createdAt: CodecTypes['pg/timestamp-string@1']['input']; - readonly email: CodecTypes['pg/text@1']['input']; - readonly id: CodecTypes['pg/int4@1']['input']; - readonly name: CodecTypes['pg/text@1']['input'] | null; - readonly role: 'USER' | 'ADMIN'; - readonly updatedAt: CodecTypes['pg/timestamp-string@1']['input']; + readonly role: { readonly output: 'USER' | 'ADMIN'; readonly input: 'USER' | 'ADMIN' }; }; }; }; +export type StorageColumnTypes = ColumnTypesFromStorage< + ContractBase['storage'], + CodecTypes, + ParameterizedOutputTypeFunctions, + ColumnTypeOverrides, + 'output' +>; +export type StorageColumnInputTypes = ColumnTypesFromStorage< + ContractBase['storage'], + CodecTypes, + ParameterizedOutputTypeFunctions, + ColumnTypeOverrides, + 'input' +>; export namespace Models { export type public_Post = { - authorId: CodecTypes['pg/int4@1']['output']; - content: CodecTypes['pg/text@1']['output'] | null; - id: CodecTypes['pg/int4@1']['output']; - published: CodecTypes['pg/bool@1']['output']; - title: CodecTypes['pg/text@1']['output']; - viewCount: CodecTypes['pg/int4@1']['output']; + authorId: FieldOutputTypes['public']['Post']['authorId']; + content: FieldOutputTypes['public']['Post']['content']; + id: FieldOutputTypes['public']['Post']['id']; + published: FieldOutputTypes['public']['Post']['published']; + title: FieldOutputTypes['public']['Post']['title']; + viewCount: FieldOutputTypes['public']['Post']['viewCount']; author: public_User; tags: public_Tag[]; readonly [RelationKeys]?: 'author' | 'tags'; }; export type public_PostToTag = { - A: CodecTypes['pg/int4@1']['output']; - B: CodecTypes['pg/int4@1']['output']; + A: FieldOutputTypes['public']['PostToTag']['A']; + B: FieldOutputTypes['public']['PostToTag']['B']; post: public_Post; tag: public_Tag; readonly [RelationKeys]?: 'post' | 'tag'; }; export type public_Tag = { - id: CodecTypes['pg/int4@1']['output']; - name: CodecTypes['pg/text@1']['output']; + id: FieldOutputTypes['public']['Tag']['id']; + name: FieldOutputTypes['public']['Tag']['name']; posts: public_Post[]; readonly [RelationKeys]?: 'posts'; }; export type public_User = { - createdAt: TimestampString<3>; - email: CodecTypes['pg/text@1']['output']; - id: CodecTypes['pg/int4@1']['output']; - name: CodecTypes['pg/text@1']['output'] | null; - role: 'USER' | 'ADMIN'; - updatedAt: TimestampString<3>; + createdAt: FieldOutputTypes['public']['User']['createdAt']; + email: FieldOutputTypes['public']['User']['email']; + id: FieldOutputTypes['public']['User']['id']; + name: FieldOutputTypes['public']['User']['name']; + role: FieldOutputTypes['public']['User']['role']; + updatedAt: FieldOutputTypes['public']['User']['updatedAt']; posts: public_Post[]; readonly [RelationKeys]?: 'posts'; }; @@ -569,7 +536,7 @@ type ContractBase = Omit< columns: { readonly createdAt: { readonly dataType: 'pg/timestamp'; - readonly codecId: 'pg/timestamp-string@1'; + readonly codecId: 'pg/timestamp-date@1'; readonly nullable: false; readonly default: { readonly kind: 'function'; readonly expression: 'now()' }; readonly typeParams: { readonly precision: 3 }; @@ -610,7 +577,7 @@ type ContractBase = Omit< }; readonly updatedAt: { readonly dataType: 'pg/timestamp'; - readonly codecId: 'pg/timestamp-string@1'; + readonly codecId: 'pg/timestamp-date@1'; readonly nullable: false; readonly typeParams: { readonly precision: 3 }; readonly many: false; @@ -805,7 +772,7 @@ type ContractBase = Omit< readonly nullable: false; readonly type: { readonly kind: 'scalar'; - readonly codecId: 'pg/timestamp-string@1'; + readonly codecId: 'pg/timestamp-date@1'; readonly typeParams: { readonly precision: 3 }; }; }; @@ -833,7 +800,7 @@ type ContractBase = Omit< readonly nullable: false; readonly type: { readonly kind: 'scalar'; - readonly codecId: 'pg/timestamp-string@1'; + readonly codecId: 'pg/timestamp-date@1'; readonly typeParams: { readonly precision: 3 }; }; }; @@ -897,6 +864,14 @@ type ContractBase = Omit< readonly executionHash: ExecutionHash; readonly mutations: { readonly defaults: readonly [ + { + readonly onCreate: { readonly id: 'timestampNow'; readonly kind: 'generator' }; + readonly ref: { + readonly entry: 'User'; + readonly field: 'createdAt'; + readonly namespace: 'public'; + }; + }, { readonly onCreate: { readonly id: 'timestampNow'; readonly kind: 'generator' }; readonly onUpdate: { readonly id: 'timestampNow'; readonly kind: 'generator' }; diff --git a/examples/prisma7-adoption/migrations/snapshots/97963644c7d9359d5e31879ed1c6bfdc39b666d0fd1d0f152525b055d3c574c3/contract.json b/examples/prisma7-adoption/migrations/snapshots/97963644c7d9359d5e31879ed1c6bfdc39b666d0fd1d0f152525b055d3c574c3/contract.json new file mode 100644 index 000000000000..f0914e7639c8 --- /dev/null +++ b/examples/prisma7-adoption/migrations/snapshots/97963644c7d9359d5e31879ed1c6bfdc39b666d0fd1d0f152525b055d3c574c3/contract.json @@ -0,0 +1 @@ +{"_generated":{"message":"This file is automatically generated by \"prisma contract emit\".","regenerate":"To regenerate, run: prisma contract emit","warning":"⚠️ GENERATED FILE - DO NOT EDIT"},"capabilities":{"postgres":{"distinctOn":true,"forKeyShare":true,"forNoKeyUpdate":true,"jsonAgg":true,"lateral":true,"limit":true,"orderBy":true,"returning":true},"sql":{"checkConstraint":true,"defaultInInsert":true,"enums":true,"forShare":true,"forUpdate":true,"insertOnConflictSkip":true,"insertOnConflictWithoutTarget":true,"lateral":true,"lockNowait":true,"lockOf":true,"lockSkipLocked":true,"returning":true,"scalarList":true}},"domain":{"namespaces":{"public":{"models":{"Post":{"fields":{"authorId":{"nullable":false,"type":{"codecId":"pg/int4@1","kind":"scalar"}},"content":{"nullable":true,"type":{"codecId":"pg/text@1","kind":"scalar"}},"id":{"nullable":false,"type":{"codecId":"pg/int4@1","kind":"scalar"}},"published":{"nullable":false,"type":{"codecId":"pg/bool@1","kind":"scalar"}},"title":{"nullable":false,"type":{"codecId":"pg/text@1","kind":"scalar"}},"viewCount":{"nullable":false,"type":{"codecId":"pg/int4@1","kind":"scalar"}}},"relations":{"author":{"cardinality":"N:1","nullable":false,"on":{"localFields":["authorId"],"targetFields":["id"]},"to":{"model":"User","namespace":"public"}},"tags":{"cardinality":"N:M","on":{"localFields":["id"],"targetFields":["A"]},"through":{"childColumns":["B"],"namespaceId":"public","parentColumns":["A"],"table":"_PostToTag","targetColumns":["id"]},"to":{"model":"Tag","namespace":"public"}}},"storage":{"fields":{"authorId":{"column":"authorId"},"content":{"column":"content"},"id":{"column":"id"},"published":{"column":"published"},"title":{"column":"title"},"viewCount":{"column":"viewCount"}},"namespaceId":"public","table":"Post"}},"PostToTag":{"fields":{"A":{"nullable":false,"type":{"codecId":"pg/int4@1","kind":"scalar"}},"B":{"nullable":false,"type":{"codecId":"pg/int4@1","kind":"scalar"}}},"relations":{"post":{"cardinality":"N:1","nullable":false,"on":{"localFields":["A"],"targetFields":["id"]},"to":{"model":"Post","namespace":"public"}},"tag":{"cardinality":"N:1","nullable":false,"on":{"localFields":["B"],"targetFields":["id"]},"to":{"model":"Tag","namespace":"public"}}},"storage":{"fields":{"A":{"column":"A"},"B":{"column":"B"}},"namespaceId":"public","table":"_PostToTag"}},"Tag":{"fields":{"id":{"nullable":false,"type":{"codecId":"pg/int4@1","kind":"scalar"}},"name":{"nullable":false,"type":{"codecId":"pg/text@1","kind":"scalar"}}},"relations":{"posts":{"cardinality":"N:M","on":{"localFields":["id"],"targetFields":["B"]},"through":{"childColumns":["A"],"namespaceId":"public","parentColumns":["B"],"table":"_PostToTag","targetColumns":["id"]},"to":{"model":"Post","namespace":"public"}}},"storage":{"fields":{"id":{"column":"id"},"name":{"column":"name"}},"namespaceId":"public","table":"Tag"}},"User":{"fields":{"createdAt":{"nullable":false,"type":{"codecId":"pg/timestamp-date@1","kind":"scalar","typeParams":{"precision":3}}},"email":{"nullable":false,"type":{"codecId":"pg/text@1","kind":"scalar"}},"id":{"nullable":false,"type":{"codecId":"pg/int4@1","kind":"scalar"}},"name":{"nullable":true,"type":{"codecId":"pg/text@1","kind":"scalar"}},"role":{"nullable":false,"type":{"codecId":"pg/enum@1","kind":"scalar","typeParams":{"typeName":"Role"}}},"updatedAt":{"nullable":false,"type":{"codecId":"pg/timestamp-date@1","kind":"scalar","typeParams":{"precision":3}}}},"relations":{"posts":{"cardinality":"1:N","on":{"localFields":["id"],"targetFields":["authorId"]},"to":{"model":"Post","namespace":"public"}}},"storage":{"fields":{"createdAt":{"column":"createdAt"},"email":{"column":"email"},"id":{"column":"id"},"name":{"column":"name"},"role":{"column":"role"},"updatedAt":{"column":"updatedAt"}},"namespaceId":"public","table":"User"}}}}}},"execution":{"executionHash":"380511653781da301aa7612ce64f0ac7b74bedf70c783143bab5ae56d5295037","mutations":{"defaults":[{"onCreate":{"id":"timestampNow","kind":"generator"},"ref":{"entry":"User","field":"createdAt","namespace":"public"}},{"onCreate":{"id":"timestampNow","kind":"generator"},"onUpdate":{"id":"timestampNow","kind":"generator"},"ref":{"entry":"User","field":"updatedAt","namespace":"public"}}]}},"extensions":{},"meta":{},"profileHash":"3916f444a8a17ad749191acf9e08dad97d1a327b88c2f1d45d12f240296aa8b2","roots":{"Post":{"model":"Post","namespace":"public"},"Tag":{"model":"Tag","namespace":"public"},"User":{"model":"User","namespace":"public"},"_PostToTag":{"model":"PostToTag","namespace":"public"}},"schemaVersion":"1","storage":{"namespaces":{"public":{"entries":{"native_enum":{"Role":{"kind":"postgres-enum","members":["USER","ADMIN"],"typeName":"Role"}},"table":{"Post":{"columns":{"authorId":{"codecId":"pg/int4@1","dataType":"pg/int4","nullable":false},"content":{"codecId":"pg/text@1","dataType":"pg/text","nullable":true},"id":{"codecId":"pg/int4@1","dataType":"pg/int4","default":{"expression":"autoincrement()","kind":"function"},"nullable":false},"published":{"codecId":"pg/bool@1","dataType":"pg/bool","default":{"kind":"literal","value":false},"nullable":false},"title":{"codecId":"pg/text@1","dataType":"pg/text","nullable":false},"viewCount":{"codecId":"pg/int4@1","dataType":"pg/int4","default":{"kind":"literal","value":0},"nullable":false}},"foreignKeys":[{"onDelete":"restrict","onUpdate":"cascade","source":{"columns":["authorId"],"namespaceId":"public","tableName":"Post"},"target":{"columns":["id"],"namespaceId":"public","tableName":"User"}}],"indexes":[],"primaryKey":{"columns":["id"]},"uniques":[]},"Tag":{"columns":{"id":{"codecId":"pg/int4@1","dataType":"pg/int4","default":{"expression":"autoincrement()","kind":"function"},"nullable":false},"name":{"codecId":"pg/text@1","dataType":"pg/text","nullable":false}},"foreignKeys":[],"indexes":[{"columns":["name"],"name":"Tag_name_key","unique":true}],"primaryKey":{"columns":["id"]},"uniques":[]},"User":{"columns":{"createdAt":{"codecId":"pg/timestamp-date@1","dataType":"pg/timestamp","default":{"expression":"now()","kind":"function"},"nullable":false,"typeParams":{"precision":3}},"email":{"codecId":"pg/text@1","dataType":"pg/text","nullable":false},"id":{"codecId":"pg/int4@1","dataType":"pg/int4","default":{"expression":"autoincrement()","kind":"function"},"nullable":false},"name":{"codecId":"pg/text@1","dataType":"pg/text","nullable":true},"role":{"codecId":"pg/enum@1","dataType":"pg/enum","default":{"kind":"literal","value":"USER"},"nullable":false,"typeParams":{"typeName":"Role"},"valueSet":{"entityKind":"valueSet","entityName":"Role","namespaceId":"public","plane":"storage"}},"updatedAt":{"codecId":"pg/timestamp-date@1","dataType":"pg/timestamp","nullable":false,"typeParams":{"precision":3}}},"foreignKeys":[],"indexes":[{"columns":["email"],"name":"User_email_key","unique":true}],"primaryKey":{"columns":["id"]},"uniques":[]},"_PostToTag":{"columns":{"A":{"codecId":"pg/int4@1","dataType":"pg/int4","nullable":false},"B":{"codecId":"pg/int4@1","dataType":"pg/int4","nullable":false}},"foreignKeys":[{"index":{"primaryKey":true},"onDelete":"cascade","onUpdate":"cascade","source":{"columns":["A"],"namespaceId":"public","tableName":"_PostToTag"},"target":{"columns":["id"],"namespaceId":"public","tableName":"Post"}},{"index":{"name":"_PostToTag_B_index"},"onDelete":"cascade","onUpdate":"cascade","source":{"columns":["B"],"namespaceId":"public","tableName":"_PostToTag"},"target":{"columns":["id"],"namespaceId":"public","tableName":"Tag"}}],"indexes":[{"columns":["B"],"name":"_PostToTag_B_index","unique":false}],"primaryKey":{"columns":["A","B"],"name":"_PostToTag_AB_pkey"},"uniques":[]}},"valueSet":{"Role":{"kind":"valueSet","values":["USER","ADMIN"]}}},"id":"public","kind":"postgres-schema"}},"storageHash":"97963644c7d9359d5e31879ed1c6bfdc39b666d0fd1d0f152525b055d3c574c3"},"target":"postgres","targetFamily":"sql"} diff --git a/examples/prisma7-adoption/migrations/snapshots/cee288aa57be618663c6c6118ece25e6ae2049027a7337e8e04c1742faedfff3/contract.json b/examples/prisma7-adoption/migrations/snapshots/cee288aa57be618663c6c6118ece25e6ae2049027a7337e8e04c1742faedfff3/contract.json deleted file mode 100644 index 2b53b07db2ba..000000000000 --- a/examples/prisma7-adoption/migrations/snapshots/cee288aa57be618663c6c6118ece25e6ae2049027a7337e8e04c1742faedfff3/contract.json +++ /dev/null @@ -1 +0,0 @@ -{"_generated":{"message":"This file is automatically generated by \"prisma contract emit\".","regenerate":"To regenerate, run: prisma contract emit","warning":"⚠️ GENERATED FILE - DO NOT EDIT"},"capabilities":{"postgres":{"distinctOn":true,"forKeyShare":true,"forNoKeyUpdate":true,"jsonAgg":true,"lateral":true,"limit":true,"orderBy":true,"returning":true},"sql":{"checkConstraint":true,"defaultInInsert":true,"enums":true,"forShare":true,"forUpdate":true,"insertOnConflictSkip":true,"insertOnConflictWithoutTarget":true,"lateral":true,"lockNowait":true,"lockOf":true,"lockSkipLocked":true,"returning":true,"scalarList":true}},"domain":{"namespaces":{"public":{"models":{"Post":{"fields":{"authorId":{"nullable":false,"type":{"codecId":"pg/int4@1","kind":"scalar"}},"content":{"nullable":true,"type":{"codecId":"pg/text@1","kind":"scalar"}},"id":{"nullable":false,"type":{"codecId":"pg/int4@1","kind":"scalar"}},"published":{"nullable":false,"type":{"codecId":"pg/bool@1","kind":"scalar"}},"title":{"nullable":false,"type":{"codecId":"pg/text@1","kind":"scalar"}},"viewCount":{"nullable":false,"type":{"codecId":"pg/int4@1","kind":"scalar"}}},"relations":{"author":{"cardinality":"N:1","nullable":false,"on":{"localFields":["authorId"],"targetFields":["id"]},"to":{"model":"User","namespace":"public"}},"tags":{"cardinality":"N:M","on":{"localFields":["id"],"targetFields":["A"]},"through":{"childColumns":["B"],"namespaceId":"public","parentColumns":["A"],"table":"_PostToTag","targetColumns":["id"]},"to":{"model":"Tag","namespace":"public"}}},"storage":{"fields":{"authorId":{"column":"authorId"},"content":{"column":"content"},"id":{"column":"id"},"published":{"column":"published"},"title":{"column":"title"},"viewCount":{"column":"viewCount"}},"namespaceId":"public","table":"Post"}},"PostToTag":{"fields":{"A":{"nullable":false,"type":{"codecId":"pg/int4@1","kind":"scalar"}},"B":{"nullable":false,"type":{"codecId":"pg/int4@1","kind":"scalar"}}},"relations":{"post":{"cardinality":"N:1","nullable":false,"on":{"localFields":["A"],"targetFields":["id"]},"to":{"model":"Post","namespace":"public"}},"tag":{"cardinality":"N:1","nullable":false,"on":{"localFields":["B"],"targetFields":["id"]},"to":{"model":"Tag","namespace":"public"}}},"storage":{"fields":{"A":{"column":"A"},"B":{"column":"B"}},"namespaceId":"public","table":"_PostToTag"}},"Tag":{"fields":{"id":{"nullable":false,"type":{"codecId":"pg/int4@1","kind":"scalar"}},"name":{"nullable":false,"type":{"codecId":"pg/text@1","kind":"scalar"}}},"relations":{"posts":{"cardinality":"N:M","on":{"localFields":["id"],"targetFields":["B"]},"through":{"childColumns":["A"],"namespaceId":"public","parentColumns":["B"],"table":"_PostToTag","targetColumns":["id"]},"to":{"model":"Post","namespace":"public"}}},"storage":{"fields":{"id":{"column":"id"},"name":{"column":"name"}},"namespaceId":"public","table":"Tag"}},"User":{"fields":{"createdAt":{"nullable":false,"type":{"codecId":"pg/timestamp-string@1","kind":"scalar","typeParams":{"precision":3}}},"email":{"nullable":false,"type":{"codecId":"pg/text@1","kind":"scalar"}},"id":{"nullable":false,"type":{"codecId":"pg/int4@1","kind":"scalar"}},"name":{"nullable":true,"type":{"codecId":"pg/text@1","kind":"scalar"}},"role":{"nullable":false,"type":{"codecId":"pg/enum@1","kind":"scalar","typeParams":{"typeName":"Role"}}},"updatedAt":{"nullable":false,"type":{"codecId":"pg/timestamp-string@1","kind":"scalar","typeParams":{"precision":3}}}},"relations":{"posts":{"cardinality":"1:N","on":{"localFields":["id"],"targetFields":["authorId"]},"to":{"model":"Post","namespace":"public"}}},"storage":{"fields":{"createdAt":{"column":"createdAt"},"email":{"column":"email"},"id":{"column":"id"},"name":{"column":"name"},"role":{"column":"role"},"updatedAt":{"column":"updatedAt"}},"namespaceId":"public","table":"User"}}}}}},"execution":{"executionHash":"76ee8948067fff39e1b787eafdf2c9319ebe5e0f5029f71536b4f58b92d7790e","mutations":{"defaults":[{"onCreate":{"id":"timestampNow","kind":"generator"},"onUpdate":{"id":"timestampNow","kind":"generator"},"ref":{"entry":"User","field":"updatedAt","namespace":"public"}}]}},"extensions":{},"meta":{},"profileHash":"3916f444a8a17ad749191acf9e08dad97d1a327b88c2f1d45d12f240296aa8b2","roots":{"Post":{"model":"Post","namespace":"public"},"Tag":{"model":"Tag","namespace":"public"},"User":{"model":"User","namespace":"public"},"_PostToTag":{"model":"PostToTag","namespace":"public"}},"schemaVersion":"1","storage":{"namespaces":{"public":{"entries":{"native_enum":{"Role":{"kind":"postgres-enum","members":["USER","ADMIN"],"typeName":"Role"}},"table":{"Post":{"columns":{"authorId":{"codecId":"pg/int4@1","dataType":"pg/int4","nullable":false},"content":{"codecId":"pg/text@1","dataType":"pg/text","nullable":true},"id":{"codecId":"pg/int4@1","dataType":"pg/int4","default":{"expression":"autoincrement()","kind":"function"},"nullable":false},"published":{"codecId":"pg/bool@1","dataType":"pg/bool","default":{"kind":"literal","value":false},"nullable":false},"title":{"codecId":"pg/text@1","dataType":"pg/text","nullable":false},"viewCount":{"codecId":"pg/int4@1","dataType":"pg/int4","default":{"kind":"literal","value":0},"nullable":false}},"foreignKeys":[{"onDelete":"restrict","onUpdate":"cascade","source":{"columns":["authorId"],"namespaceId":"public","tableName":"Post"},"target":{"columns":["id"],"namespaceId":"public","tableName":"User"}}],"indexes":[],"primaryKey":{"columns":["id"]},"uniques":[]},"Tag":{"columns":{"id":{"codecId":"pg/int4@1","dataType":"pg/int4","default":{"expression":"autoincrement()","kind":"function"},"nullable":false},"name":{"codecId":"pg/text@1","dataType":"pg/text","nullable":false}},"foreignKeys":[],"indexes":[{"columns":["name"],"name":"Tag_name_key","unique":true}],"primaryKey":{"columns":["id"]},"uniques":[]},"User":{"columns":{"createdAt":{"codecId":"pg/timestamp-string@1","dataType":"pg/timestamp","default":{"expression":"now()","kind":"function"},"nullable":false,"typeParams":{"precision":3}},"email":{"codecId":"pg/text@1","dataType":"pg/text","nullable":false},"id":{"codecId":"pg/int4@1","dataType":"pg/int4","default":{"expression":"autoincrement()","kind":"function"},"nullable":false},"name":{"codecId":"pg/text@1","dataType":"pg/text","nullable":true},"role":{"codecId":"pg/enum@1","dataType":"pg/enum","default":{"kind":"literal","value":"USER"},"nullable":false,"typeParams":{"typeName":"Role"},"valueSet":{"entityKind":"valueSet","entityName":"Role","namespaceId":"public","plane":"storage"}},"updatedAt":{"codecId":"pg/timestamp-string@1","dataType":"pg/timestamp","nullable":false,"typeParams":{"precision":3}}},"foreignKeys":[],"indexes":[{"columns":["email"],"name":"User_email_key","unique":true}],"primaryKey":{"columns":["id"]},"uniques":[]},"_PostToTag":{"columns":{"A":{"codecId":"pg/int4@1","dataType":"pg/int4","nullable":false},"B":{"codecId":"pg/int4@1","dataType":"pg/int4","nullable":false}},"foreignKeys":[{"onDelete":"cascade","onUpdate":"cascade","source":{"columns":["A"],"namespaceId":"public","tableName":"_PostToTag"},"target":{"columns":["id"],"namespaceId":"public","tableName":"Post"}},{"onDelete":"cascade","onUpdate":"cascade","source":{"columns":["B"],"namespaceId":"public","tableName":"_PostToTag"},"target":{"columns":["id"],"namespaceId":"public","tableName":"Tag"}}],"indexes":[{"columns":["B"],"name":"_PostToTag_B_index","unique":false}],"primaryKey":{"columns":["A","B"],"name":"_PostToTag_AB_pkey"},"uniques":[]}},"valueSet":{"Role":{"kind":"valueSet","values":["USER","ADMIN"]}}},"id":"public","kind":"postgres-schema"}},"storageHash":"cee288aa57be618663c6c6118ece25e6ae2049027a7337e8e04c1742faedfff3"},"target":"postgres","targetFamily":"sql"} diff --git a/examples/prisma7-adoption/src/main.ts b/examples/prisma7-adoption/src/main.ts index 07b122ad282e..f26d6e9c9c40 100644 --- a/examples/prisma7-adoption/src/main.ts +++ b/examples/prisma7-adoption/src/main.ts @@ -40,7 +40,7 @@ const renamed = await db.orm.public.User.where({ id: alice.id }).update({ name: `Alice (renamed by Prisma 8 at ${new Date().toISOString()})`, }); console.log( - `updatedAt advanced: ${alice.updatedAt.replace(' ', 'T')} -> ${renamed?.updatedAt.replace(' ', 'T')}`, + `updatedAt advanced: ${alice.updatedAt.toISOString()} -> ${renamed?.updatedAt.toISOString()}`, ); await prisma.$disconnect(); diff --git a/examples/prisma7-adoption/test/adoption.test.ts b/examples/prisma7-adoption/test/adoption.test.ts index 1b0ebd10dce0..434e3ee10225 100644 --- a/examples/prisma7-adoption/test/adoption.test.ts +++ b/examples/prisma7-adoption/test/adoption.test.ts @@ -56,8 +56,12 @@ describe('adopting Prisma 8 beside Prisma 7', () => { expect(prisma8Read).toMatch(/Created post \d+ through Prisma 8, tagged orm/); const advanced = /updatedAt advanced: (\S+) -> (\S+)/.exec(prisma8Read); expect(advanced, prisma8Read).not.toBeNull(); - const [, before, after] = advanced ?? []; - expect(new Date(`${after}Z`).getTime()).toBeGreaterThan(new Date(`${before}Z`).getTime()); + const [, before = '', after = ''] = advanced ?? []; + expect({ before, after }).toEqual({ + before: new Date(before).toISOString(), + after: new Date(after).toISOString(), + }); + expect(new Date(after).getTime()).toBeGreaterThan(new Date(before).getTime()); expect(await tsx('src/v7-read.ts')).toContain('Written through Prisma 8'); writeFileSync(join(dir, 'prisma/schema.prisma'), FINAL_SCHEMA); diff --git a/gotchas.md b/gotchas.md index f7a6972ebca9..08df8b2e87f3 100644 --- a/gotchas.md +++ b/gotchas.md @@ -132,7 +132,7 @@ The same command with `--from 20260707T1005_init` (a migration directory name) s **Workaround.** `import 'temporal-polyfill/full/global'` before the client is created (the example did so in `src/db.ts` before the fix), or author the column with the `*String` presets to receive ISO 8601 text. -**Resolved.** `prisma7Schema(...)` and `contract infer` now write `TimestampString(3)`, `TimestamptzString(p)`, `DateString` and `TimeString(p)` for these columns, so they read as ISO 8601 text, `@updatedAt` writes UTC text, and the application needs no `Temporal`. A column written by hand as `TimestamptzTemporal(p)`, `TimestampTemporal(p)`, `DateTemporal` or `TimeTemporal(p)`, or as a convenience type with `as: temporal`, still reads as a `Temporal` value and still needs one; the convenience types `DateTime`, `Date` and `Time` need none at their defaults. +**Resolved.** `prisma7Schema(...)` and `contract infer` now write the convenience types' defaults for these columns: `DateTime(storage: timestamp)` (`TimestampJsDate(3)`) and `DateTime(precision: p)` (`TimestamptzJsDate(p)`), which read a JavaScript `Date` as Prisma 7 did, and `Date`, `Time(precision: p)` and `Time(storage: timetz)`, which read ISO strings. `@updatedAt` writes a `Date`, and the application needs no `Temporal`. A column written by hand as `TimestamptzTemporal(p)`, `TimestampTemporal(p)`, `DateTemporal` or `TimeTemporal(p)`, or as a convenience type with `as: temporal`, still reads as a `Temporal` value and still needs one; the convenience types `DateTime`, `Date` and `Time` need none at their defaults. **Reproduction.** 1. `cd examples/prisma7-adoption && pnpm db:start`, then `pnpm v7:migrate && pnpm emit && pnpm sign && pnpm seed`. diff --git a/packages/1-framework/1-core/framework-components/src/exports/authoring.ts b/packages/1-framework/1-core/framework-components/src/exports/authoring.ts index 763700daca98..89e9aab8c056 100644 --- a/packages/1-framework/1-core/framework-components/src/exports/authoring.ts +++ b/packages/1-framework/1-core/framework-components/src/exports/authoring.ts @@ -14,6 +14,11 @@ export { typeChoiceCombinationText, typeConstructorLookup, } from '../shared/authoring-type-choice'; +export type { + AuthoringTypeChoiceCall, + AuthoringTypeChoiceCallArgument, +} from '../shared/authoring-type-choice-call'; +export { findAuthoringTypeChoiceCall } from '../shared/authoring-type-choice-call'; export type { AuthoringTypeConstructorCall, AuthoringTypeConstructorOutput, diff --git a/packages/1-framework/1-core/framework-components/src/shared/authoring-type-choice-call.ts b/packages/1-framework/1-core/framework-components/src/shared/authoring-type-choice-call.ts new file mode 100644 index 000000000000..a205eacc8432 --- /dev/null +++ b/packages/1-framework/1-core/framework-components/src/shared/authoring-type-choice-call.ts @@ -0,0 +1,129 @@ +import { isInternalError } from '@internal/utils/internal-error'; +import { + type AuthoringTypeChoiceDescriptor, + isAuthoringTypeChoiceOption, + resolveAuthoringTypeChoice, + typeConstructorLookup, +} from './authoring-type-choice'; +import { + type AuthoringTypeConstructorCall, + type AuthoringTypeConstructorOutput, + sameAuthoringTypeConstructorOutput, +} from './authoring-type-constructor-call'; +import { + type AuthoringTypeNamespace, + type AuthoringTypeNamespaceMember, + instantiateAuthoringTypeCall, + instantiateAuthoringTypeConstructor, + isAuthoringTypeChoiceDescriptor, +} from './framework-authoring'; + +/** One argument of a type choice call, written by name. */ +export interface AuthoringTypeChoiceCallArgument { + readonly name: string; + readonly value: unknown; +} + +/** A type choice, by its path in an authoring namespace, and the arguments of one call to it in the choice's argument order; an argument left out takes its default. */ +export interface AuthoringTypeChoiceCall { + readonly path: readonly string[]; + readonly args: readonly AuthoringTypeChoiceCallArgument[]; + /** The type constructor call the type choice call becomes. */ + readonly becomes: AuthoringTypeConstructorCall; +} + +function namespaceLevel( + namespace: AuthoringTypeNamespace, + path: readonly string[], +): AuthoringTypeNamespace | undefined { + let level: AuthoringTypeNamespace | undefined = namespace; + for (const segment of path) { + const member: AuthoringTypeNamespaceMember | undefined = + level !== undefined && Object.hasOwn(level, segment) ? level[segment] : undefined; + level = member !== undefined && !('kind' in member) ? member : undefined; + } + return level; +} + +function firstChoiceNaming( + level: AuthoringTypeNamespace, + constructorName: string, +): + | { + readonly name: string; + readonly descriptor: AuthoringTypeChoiceDescriptor; + readonly when: Readonly>; + } + | undefined { + for (const [name, member] of Object.entries(level)) { + if (!isAuthoringTypeChoiceDescriptor(member)) continue; + const row = member.choices.find((candidate) => candidate.type === constructorName); + if (row !== undefined) return { name, descriptor: member, when: row.when }; + } + return undefined; +} + +function outputOf(produce: () => AuthoringTypeConstructorOutput) { + try { + return produce(); + } catch (error) { + if (isInternalError(error)) throw error; + return undefined; + } +} + +/** + * The call to a type choice that becomes the type constructor call `call`: the first type choice, in namespace order at the constructor's level, with a row naming the constructor. Its options come from that row and its passed-on arguments from the call, and every argument whose omission still gives the same output is left out. `sameOutput` decides what counts as the same output; by default the codec and type parameters must be exactly equal. `undefined` when no type choice names the constructor or no call to it gives the same output. + */ +export function findAuthoringTypeChoiceCall(input: { + readonly namespace: AuthoringTypeNamespace; + readonly call: AuthoringTypeConstructorCall; + readonly sameOutput?: ( + produced: AuthoringTypeConstructorOutput, + wanted: AuthoringTypeConstructorOutput, + ) => boolean; +}): AuthoringTypeChoiceCall | undefined { + const { call } = input; + const sameOutput = input.sameOutput ?? sameAuthoringTypeConstructorOutput; + const prefix = call.path.slice(0, -1); + const constructorName = call.path.at(-1); + const level = namespaceLevel(input.namespace, prefix); + if (level === undefined || constructorName === undefined) return undefined; + const explicit = typeConstructorLookup(level)(constructorName); + const choice = explicit && firstChoiceNaming(level, constructorName); + const wanted = + explicit && outputOf(() => instantiateAuthoringTypeConstructor(explicit, call.args)); + if (explicit === undefined || choice === undefined || wanted === undefined) return undefined; + + const passedValue = (name: string) => + call.args[(explicit.args ?? []).findIndex((parameter) => parameter.name === name)]; + const reproduces = (args: readonly unknown[]) => { + const produced = outputOf(() => instantiateAuthoringTypeCall(level, choice.name, args)); + return produced !== undefined && sameOutput(produced, wanted); + }; + + const args: unknown[] = choice.descriptor.args.map((argument) => + isAuthoringTypeChoiceOption(argument) ? choice.when[argument.name] : passedValue(argument.name), + ); + if (!reproduces(args)) return undefined; + for (const index of args.keys()) { + const without = args.map((value, at) => (at === index ? undefined : value)); + if (args[index] !== undefined && reproduces(without)) args[index] = undefined; + } + + const chosen = resolveAuthoringTypeChoice({ + helperPath: choice.name, + descriptor: choice.descriptor, + args, + constructorNamed: typeConstructorLookup(level), + }); + const chosenArgs = [...chosen.args]; + while (chosenArgs.length > 0 && chosenArgs.at(-1) === undefined) chosenArgs.pop(); + return { + path: [...prefix, choice.name], + args: choice.descriptor.args.flatMap((argument, index) => + args[index] === undefined ? [] : [{ name: argument.name, value: args[index] }], + ), + becomes: { path: [...prefix, chosen.type], args: chosenArgs }, + }; +} diff --git a/packages/1-framework/1-core/framework-components/src/shared/authoring-type-constructor-call.ts b/packages/1-framework/1-core/framework-components/src/shared/authoring-type-constructor-call.ts index 0b4a15327081..17ec14eb90a8 100644 --- a/packages/1-framework/1-core/framework-components/src/shared/authoring-type-constructor-call.ts +++ b/packages/1-framework/1-core/framework-components/src/shared/authoring-type-constructor-call.ts @@ -34,7 +34,8 @@ export function* authoringTypeConstructors( } } -function produces( +/** Whether `produced` is exactly `wanted`: the same codec and the same type parameters, an absent set of parameters counting as an empty one. */ +export function sameAuthoringTypeConstructorOutput( produced: AuthoringTypeConstructorOutput, wanted: AuthoringTypeConstructorOutput, ): boolean { @@ -69,7 +70,10 @@ export function authoringTypeConstructorArguments( const requiredArgs = (descriptor.args ?? []).filter((arg) => arg.optional !== true).length; if (args.length < requiredArgs || Array.from(args).includes(undefined)) return undefined; try { - return produces(instantiateAuthoringTypeConstructor(descriptor, args), output) + return sameAuthoringTypeConstructorOutput( + instantiateAuthoringTypeConstructor(descriptor, args), + output, + ) ? args : undefined; } catch { diff --git a/packages/1-framework/1-core/framework-components/src/shared/framework-authoring.ts b/packages/1-framework/1-core/framework-components/src/shared/framework-authoring.ts index 00a3223696d7..24db87103d0d 100644 --- a/packages/1-framework/1-core/framework-components/src/shared/framework-authoring.ts +++ b/packages/1-framework/1-core/framework-components/src/shared/framework-authoring.ts @@ -144,7 +144,7 @@ export interface AuthoringTypeConstructorDescriptor { readonly entityRefArg?: AuthoringTypeConstructorEntityRef; /** Present when this name is kept only as an alias of `replacement` and will be removed; it resolves as before, and a source may warn. */ readonly deprecated?: { readonly replacement: string }; - /** Marks the constructor written for a column of its codec's data type when no schema names one: `contract infer` writes it, and the Prisma 7 reader will. `contract print` writes it when it produces the column's codec, and otherwise the first constructor in the stack that does, so a `timestamptz` column with the `Temporal` codec prints `DateTime`. At most one per data type. */ + /** Marks the constructor written for a column of its codec's data type when no schema names one: `contract infer` and the reader of an earlier Prisma version's schema pick it. `contract print` picks it when it produces the column's codec, and otherwise the first constructor in the stack that does; either writes a type choice that becomes it instead, where one does. At most one per data type. */ readonly inferred?: true; } diff --git a/packages/1-framework/1-core/framework-components/test/authoring-type-choice-call.test.ts b/packages/1-framework/1-core/framework-components/test/authoring-type-choice-call.test.ts new file mode 100644 index 000000000000..572c7b97700b --- /dev/null +++ b/packages/1-framework/1-core/framework-components/test/authoring-type-choice-call.test.ts @@ -0,0 +1,164 @@ +import { describe, expect, it } from 'vitest'; +import { findAuthoringTypeChoiceCall } from '../src/shared/authoring-type-choice-call'; +import type { AuthoringTypeConstructorOutput } from '../src/shared/authoring-type-constructor-call'; +import type { AuthoringTypeNamespace } from '../src/shared/framework-authoring'; + +const precision = { + kind: 'value', + dataType: 'test/int', + name: 'precision', + optional: true, +} as const; + +const constructors = { + WideDate: { + kind: 'typeConstructor', + args: [precision], + output: { codecId: 'test/wide-date@1', typeParams: { precision: { kind: 'arg', index: 0 } } }, + }, + WideText: { + kind: 'typeConstructor', + args: [precision], + output: { codecId: 'test/wide-text@1', typeParams: { precision: { kind: 'arg', index: 0 } } }, + }, + NarrowDate: { + kind: 'typeConstructor', + output: { codecId: 'test/narrow-date@1' }, + }, + WideTemporal: { + kind: 'typeConstructor', + args: [precision], + output: { + codecId: 'test/wide-temporal@1', + typeParams: { precision: { kind: 'arg', index: 0 } }, + }, + }, +} as const satisfies AuthoringTypeNamespace; + +const moment = { + kind: 'typeChoice', + args: [ + { kind: 'option', name: 'value', values: ['date', 'text'], default: 'date', optional: true }, + { + kind: 'option', + name: 'column', + values: ['wide', 'narrow'], + default: 'wide', + optional: true, + }, + precision, + ], + choices: [ + { when: { value: 'date', column: 'wide' }, type: 'WideDate', defaults: { precision: 3 } }, + { when: { value: 'text', column: 'wide' }, type: 'WideText' }, + { when: { value: 'date', column: 'narrow' }, type: 'NarrowDate' }, + ], +} as const; + +const namespace = { ...constructors, Moment: moment } as const satisfies AuthoringTypeNamespace; + +const find = ( + path: readonly string[], + args: readonly unknown[], + types: AuthoringTypeNamespace = namespace, +) => findAuthoringTypeChoiceCall({ namespace: types, call: { path, args } }); + +describe('findAuthoringTypeChoiceCall', () => { + it('writes the default row bare when its parameter is the row default', () => { + expect(find(['WideDate'], [3])).toEqual({ + path: ['Moment'], + args: [], + becomes: { path: ['WideDate'], args: [3] }, + }); + }); + + it('writes only the arguments that differ from the defaults, by name', () => { + expect(find(['WideText'], [2])).toEqual({ + path: ['Moment'], + args: [ + { name: 'value', value: 'text' }, + { name: 'precision', value: 2 }, + ], + becomes: { path: ['WideText'], args: [2] }, + }); + }); + + it('writes a parameter that differs from the row default', () => { + expect(find(['WideDate'], [6])).toEqual({ + path: ['Moment'], + args: [{ name: 'precision', value: 6 }], + becomes: { path: ['WideDate'], args: [6] }, + }); + }); + + it('writes the option of a row other than the default', () => { + expect(find(['NarrowDate'], [])).toEqual({ + path: ['Moment'], + args: [{ name: 'column', value: 'narrow' }], + becomes: { path: ['NarrowDate'], args: [] }, + }); + }); + + it('finds nothing for a constructor no row names', () => { + expect(find(['WideTemporal'], [3])).toBeUndefined(); + }); + + it('finds nothing when no call reproduces the parameters, as a row default would fill an omitted one', () => { + expect(find(['WideDate'], [])).toBeUndefined(); + }); + + it('finds nothing for a name that is not a type constructor', () => { + expect(find(['Moment'], [])).toBeUndefined(); + }); + + it('drops an argument whose omission the comparison counts as the same output', () => { + const omittedPrecisionIsSix = ( + produced: AuthoringTypeConstructorOutput, + wanted: AuthoringTypeConstructorOutput, + ) => + produced.codecId === wanted.codecId && + (produced.typeParams?.['precision'] ?? 6) === (wanted.typeParams?.['precision'] ?? 6); + expect( + findAuthoringTypeChoiceCall({ + namespace, + call: { path: ['WideText'], args: [6] }, + sameOutput: omittedPrecisionIsSix, + }), + ).toEqual({ + path: ['Moment'], + args: [{ name: 'value', value: 'text' }], + becomes: { path: ['WideText'], args: [] }, + }); + expect( + findAuthoringTypeChoiceCall({ + namespace, + call: { path: ['WideDate'], args: [6] }, + sameOutput: omittedPrecisionIsSix, + }), + ).toEqual({ + path: ['Moment'], + args: [{ name: 'precision', value: 6 }], + becomes: { path: ['WideDate'], args: [6] }, + }); + }); + + it('takes the first type choice in namespace order that names the constructor', () => { + const second = { + ...moment, + choices: [{ when: { value: 'text', column: 'wide' }, type: 'WideDate' }], + }; + expect(find(['WideDate'], [3], { ...constructors, Moment: moment, Other: second })).toEqual({ + path: ['Moment'], + args: [], + becomes: { path: ['WideDate'], args: [3] }, + }); + }); + + it('writes a type choice of the same sub-namespace as the constructor', () => { + expect(find(['pg', 'NarrowDate'], [], { pg: namespace })).toEqual({ + path: ['pg', 'Moment'], + args: [{ name: 'column', value: 'narrow' }], + becomes: { path: ['pg', 'NarrowDate'], args: [] }, + }); + }); +}); diff --git a/packages/1-framework/3-tooling/cli/src/commands/init/templates/code-templates.ts b/packages/1-framework/3-tooling/cli/src/commands/init/templates/code-templates.ts index 0f07aadc9f66..5a5191b5c477 100644 --- a/packages/1-framework/3-tooling/cli/src/commands/init/templates/code-templates.ts +++ b/packages/1-framework/3-tooling/cli/src/commands/init/templates/code-templates.ts @@ -181,23 +181,23 @@ function starterSchemaPslPostgres(): string { return `// use prisma-8 model User { - id Int @id @default(autoincrement()) - email String @unique + id Int @id @default(autoincrement()) + email String @unique username String? name String? posts Post[] - createdAt TimestamptzString @default(now()) - updatedAt temporal.updatedAtString() + createdAt DateTime @default(now()) @onCreate(now()) + updatedAt temporal.UpdatedAt } model Post { - id Int @id @default(autoincrement()) + id Int @id @default(autoincrement()) title String content String? - author User @relation(fields: [authorId], references: [id]) + author User @relation(fields: [authorId], references: [id]) authorId Int - createdAt TimestamptzString @default(now()) - updatedAt temporal.updatedAtString() + createdAt DateTime @default(now()) @onCreate(now()) + updatedAt temporal.UpdatedAt } `; } @@ -226,17 +226,17 @@ model Post { } function starterSchemaTsPostgres(builder: string): string { - return `import { defineContract } from '${builder}'; + return `import { defineContract, now } from '${builder}'; -export const contract = defineContract({}, ({ field, model, rel }) => { +export const contract = defineContract({}, ({ field, model, rel, type, gen }) => { const User = model('User', { fields: { id: field.id.uuidv7String(), email: field.text().unique(), username: field.text().optional(), name: field.text().optional(), - createdAt: field.temporal.createdAtString(), - updatedAt: field.temporal.updatedAtString(), + createdAt: field.column(type.DateTime()).default(now()).onCreate(gen.now()), + updatedAt: field.column(type.temporal.UpdatedAt()), }, }); @@ -246,8 +246,8 @@ export const contract = defineContract({}, ({ field, model, rel }) => { title: field.text(), content: field.text().optional(), authorId: field.uuidString(), - createdAt: field.temporal.createdAtString(), - updatedAt: field.temporal.updatedAtString(), + createdAt: field.column(type.DateTime()).default(now()).onCreate(gen.now()), + updatedAt: field.column(type.temporal.UpdatedAt()), }, }); diff --git a/packages/1-framework/3-tooling/cli/test/commands/init/__snapshots__/templates.test.ts.snap b/packages/1-framework/3-tooling/cli/test/commands/init/__snapshots__/templates.test.ts.snap index 93c4829219a5..fe93a7003ac7 100644 --- a/packages/1-framework/3-tooling/cli/test/commands/init/__snapshots__/templates.test.ts.snap +++ b/packages/1-framework/3-tooling/cli/test/commands/init/__snapshots__/templates.test.ts.snap @@ -623,23 +623,23 @@ exports[`templates > per-cell snapshots (FR5.4) > postgres + psl > starterSchema "// use prisma-8 model User { - id Int @id @default(autoincrement()) - email String @unique + id Int @id @default(autoincrement()) + email String @unique username String? name String? posts Post[] - createdAt TimestamptzString @default(now()) - updatedAt temporal.updatedAtString() + createdAt DateTime @default(now()) @onCreate(now()) + updatedAt temporal.UpdatedAt } model Post { - id Int @id @default(autoincrement()) + id Int @id @default(autoincrement()) title String content String? - author User @relation(fields: [authorId], references: [id]) + author User @relation(fields: [authorId], references: [id]) authorId Int - createdAt TimestamptzString @default(now()) - updatedAt temporal.updatedAtString() + createdAt DateTime @default(now()) @onCreate(now()) + updatedAt temporal.UpdatedAt } " `; @@ -798,17 +798,17 @@ If this project lives inside a pnpm workspace, a few things are worth knowing: `; exports[`templates > per-cell snapshots (FR5.4) > postgres + typescript > starterSchema is stable 1`] = ` -"import { defineContract } from '@internal/postgres/contract-builder'; +"import { defineContract, now } from '@internal/postgres/contract-builder'; -export const contract = defineContract({}, ({ field, model, rel }) => { +export const contract = defineContract({}, ({ field, model, rel, type, gen }) => { const User = model('User', { fields: { id: field.id.uuidv7String(), email: field.text().unique(), username: field.text().optional(), name: field.text().optional(), - createdAt: field.temporal.createdAtString(), - updatedAt: field.temporal.updatedAtString(), + createdAt: field.column(type.DateTime()).default(now()).onCreate(gen.now()), + updatedAt: field.column(type.temporal.UpdatedAt()), }, }); @@ -818,8 +818,8 @@ export const contract = defineContract({}, ({ field, model, rel }) => { title: field.text(), content: field.text().optional(), authorId: field.uuidString(), - createdAt: field.temporal.createdAtString(), - updatedAt: field.temporal.updatedAtString(), + createdAt: field.column(type.DateTime()).default(now()).onCreate(gen.now()), + updatedAt: field.column(type.temporal.UpdatedAt()), }, }); diff --git a/packages/1-framework/3-tooling/cli/test/commands/init/templates.test.ts b/packages/1-framework/3-tooling/cli/test/commands/init/templates.test.ts index 9339e22287a7..9871d6c8f417 100644 --- a/packages/1-framework/3-tooling/cli/test/commands/init/templates.test.ts +++ b/packages/1-framework/3-tooling/cli/test/commands/init/templates.test.ts @@ -114,7 +114,7 @@ describe('templates', () => { // body. The shared signature is the callback destructure itself. for (const schema of [pg, mongo]) { expect(schema).toMatch( - /defineContract\(\s*\{\s*\},\s*\(\{ field, model, rel \}\) => [({]/, + /defineContract\(\s*\{\s*\},\s*\(\{ field, model, rel(, type, gen)? \}\) => [({]/, ); expect(schema).not.toMatch(/\{ family:/); expect(schema).toContain('models: {'); diff --git a/packages/2-sql-family/1-core/contract/src/exports/data-type.ts b/packages/2-sql-family/1-core/contract/src/exports/data-type.ts index 2f0a478bdeb8..0ac630b7a9d6 100644 --- a/packages/2-sql-family/1-core/contract/src/exports/data-type.ts +++ b/packages/2-sql-family/1-core/contract/src/exports/data-type.ts @@ -1,5 +1,6 @@ export type { ResolvedSqlType, + ResolvedWrittenSqlType, SqlColumnTypeFields, SqlDataType, SqlDataTypeCollision, diff --git a/packages/2-sql-family/1-core/contract/src/sql-data-type.ts b/packages/2-sql-family/1-core/contract/src/sql-data-type.ts index 8905e209dd7a..836c636cb462 100644 --- a/packages/2-sql-family/1-core/contract/src/sql-data-type.ts +++ b/packages/2-sql-family/1-core/contract/src/sql-data-type.ts @@ -53,6 +53,8 @@ export interface SqlDataTypeSpec /** The id of the type a column of this type is stored as, with the same parameters. A type stored as another declares no texts. */ readonly storedAs?: DataTypeId; readonly normalize?: (params: Params) => Params; + /** The parameters the database catalog prints for parameters as written, when they are not their normal form: PostgreSQL prints no precision for a bare `timestamptz`, though its normal form has precision 6. */ + readonly catalogParams?: (params: Params) => Params; /** The type's name from its parameters, for a type that claims a kind or whose name is its catalog text. */ readonly render?: (params: Params) => string; readonly fromReported?: (reported: ReportedSqlType) => Params; @@ -70,6 +72,8 @@ export interface SqlDataTypeFacts readonly storedAs: DataTypeId | undefined; /** The normal form of `params`; parameters the type's schema refuses are returned as written. */ readonly normalize: (params: Params) => Params; + /** The parameters the catalog prints for `params` as written: the declared {@link SqlDataTypeSpec.catalogParams}, or else the normal form. */ + readonly catalogParams: (params: Params) => Params; readonly render: ((params: Params) => string) | undefined; readonly fromReported: ((reported: ReportedSqlType) => Params) | undefined; readonly nameIsCatalogText: boolean; @@ -86,6 +90,11 @@ export interface ResolvedSqlType { readonly typeParams: SqlTypeParams; } +/** A type a migration writes, read as {@link ResolvedSqlType}, with its parameters also as the text writes them, before the normal form. */ +export interface ResolvedWrittenSqlType extends ResolvedSqlType { + readonly writtenTypeParams: SqlTypeParams; +} + const PLACEHOLDER = /\{([A-Za-z][A-Za-z0-9]*)\}/g; const LITERAL_CHARACTERS = /^[a-z0-9_ (),.]*$/; const REGEXP_SPECIAL = /[.*+?^${}()|[\]\\]/g; @@ -269,6 +278,12 @@ export function sqlDataType( checkMarks(declared.id, texts); checkKindClaim(declared.id, spec); checkStoredAs(declared.id, spec); + const declaredCatalogParams = spec.catalogParams; + const catalogParams = + declaredCatalogParams === undefined + ? normalize + : (params: Params): Params => + isParamsOf(spec.params, params) ? declaredCatalogParams(params) : params; const type: SqlDataType = { ...declared, ...ifDefined('params', spec.params), @@ -277,6 +292,7 @@ export function sqlDataType( claimsKind: spec.claimsKind, storedAs: spec.storedAs, normalize, + catalogParams, render: spec.render, fromReported: spec.fromReported, nameIsCatalogText: spec.nameIsCatalogText === true, @@ -556,7 +572,7 @@ export function renderSqlTypeName( return substitute(type, pickText(type, 'written', keys), params); } -/** The text the database catalog prints for `type` with `params`, in their normal form. */ +/** The text the database catalog prints for `type` with `params` as written, from the parameters its catalog prints for them ({@link SqlDataTypeFacts.catalogParams}). */ export function renderSqlCatalogText( type: SqlDataType, params: SqlTypeParams, @@ -568,8 +584,8 @@ export function renderSqlCatalogText( ); } if (type.sql.render !== undefined) return type.sql.render(validated); - const normalized = type.sql.normalize(validated); - return substitute(type, pickText(type, 'catalog', presentKeys(normalized)), normalized); + const printed = type.sql.catalogParams(validated); + return substitute(type, pickText(type, 'catalog', presentKeys(printed)), printed); } function prepareReportedText(text: string): string { @@ -695,21 +711,21 @@ function paramsMatching( /** * Every data type in `dataTypes` that reads `text` as a migration may write it, with its parameters - * in normal form: any of a type's texts counts, whether written, printed by the catalog or only + * in normal form and as written: any of a type's texts counts, whether written, printed by the catalog or only * claimed. Empty when no type reads it or its parameters fail the type's schema. A type that claims * a kind has no texts, so it never reads one. */ export function resolveWrittenSqlTypes( text: string, dataTypes: readonly DataType[], -): readonly ResolvedSqlType[] { +): readonly ResolvedWrittenSqlType[] { const prepared = prepareReportedText(text); return dataTypes.filter(isSqlDataType).flatMap((type) => { for (const candidate of type.sql.texts) { const params = paramsMatching(type, candidate, prepared); if (params === undefined) continue; const resolved = resolvedWith(type, params); - if (resolved !== undefined) return [resolved]; + if (resolved !== undefined) return [{ ...resolved, writtenTypeParams: params }]; } return []; }); diff --git a/packages/2-sql-family/1-core/contract/test/sql-data-type-fixtures.ts b/packages/2-sql-family/1-core/contract/test/sql-data-type-fixtures.ts index 4f4b510d563a..0b3ead000caa 100644 --- a/packages/2-sql-family/1-core/contract/test/sql-data-type-fixtures.ts +++ b/packages/2-sql-family/1-core/contract/test/sql-data-type-fixtures.ts @@ -62,6 +62,32 @@ export const timestamp = sqlDataType('t/timestamp', { ], }); +/** Like the Postgres timestamp types: an omitted precision is the precision 6 the database stores either way, though its catalog prints the precision as written. */ +export const timestamptz = sqlDataType('t/timestamptz', { + read: (json) => json, + params: type({ 'precision?': 'number.integer >= 0 & number.integer <= 6' }), + texts: [ + { text: 'timestamptz', written: true }, + { text: 'timestamptz({precision})', written: true }, + { text: 'timestamp with time zone', catalog: true }, + { text: 'timestamp({precision}) with time zone', catalog: true }, + ], + normalize: (params) => (params.precision === undefined ? { ...params, precision: 6 } : params), + catalogParams: (params) => params, +}); + +/** Like MySQL's datetime: precision 0 is the normal form of no precision, and the catalog prints neither. */ +export const datetime = sqlDataType('t/datetime', { + read: (json) => json, + params: type({ 'precision?': 'number.integer >= 0 & number.integer <= 6' }), + texts: [ + { text: 'datetime', written: true, catalog: true }, + { text: 'datetime({precision})', written: true, catalog: true }, + ], + normalize: ({ precision, ...rest }) => + precision === undefined || precision === 0 ? rest : { ...rest, precision }, +}); + /** Like the SQLite character types: the length is written, then forgotten. */ export const character = sqlDataType('t/character', { read: (json) => json, diff --git a/packages/2-sql-family/1-core/contract/test/sql-data-type.render.test.ts b/packages/2-sql-family/1-core/contract/test/sql-data-type.render.test.ts index cae5c5fe84ee..035288efcd36 100644 --- a/packages/2-sql-family/1-core/contract/test/sql-data-type.render.test.ts +++ b/packages/2-sql-family/1-core/contract/test/sql-data-type.render.test.ts @@ -11,12 +11,14 @@ import { import { char, character, + datetime, enumType, geometry, int4, numeric, textArray, timestamp, + timestamptz, vector, } from './sql-data-type-fixtures'; @@ -149,6 +151,26 @@ describe('renderSqlCatalogText', () => { expect(renderSqlCatalogText(timestamp, {})).toBe('timestamp without time zone'); }); + it('prints the normal form of the parameters for a type that declares no catalog parameters', () => { + expect([ + renderSqlCatalogText(datetime, { precision: 0 }), + renderSqlCatalogText(datetime, {}), + renderSqlCatalogText(datetime, { precision: 3 }), + ]).toEqual(['datetime', 'datetime', 'datetime(3)']); + }); + + it('prints the parameters a type declares its catalog prints for those written', () => { + expect([ + renderSqlCatalogText(timestamptz, {}), + renderSqlCatalogText(timestamptz, { precision: 6 }), + renderSqlCatalogText(timestamptz, { precision: 3 }), + ]).toEqual([ + 'timestamp with time zone', + 'timestamp(6) with time zone', + 'timestamp(3) with time zone', + ]); + }); + it('prints the display when the text has one', () => { expect(renderSqlCatalogText(geometry, { srid: 4326 })).toBe('geometry(Geometry,4326)'); }); diff --git a/packages/2-sql-family/1-core/contract/test/sql-data-type.resolve-written.test.ts b/packages/2-sql-family/1-core/contract/test/sql-data-type.resolve-written.test.ts index bf43e66fade8..5b701c1838f4 100644 --- a/packages/2-sql-family/1-core/contract/test/sql-data-type.resolve-written.test.ts +++ b/packages/2-sql-family/1-core/contract/test/sql-data-type.resolve-written.test.ts @@ -1,6 +1,6 @@ import { describe, expect, it } from 'vitest'; import { resolveWrittenSqlTypes as resolveWritten } from '../src/sql-data-type'; -import { char, character, int4, numeric, timestamp } from './sql-data-type-fixtures'; +import { char, character, int4, numeric, timestamp, timestamptz } from './sql-data-type-fixtures'; /** resolveWrittenSqlTypes, with each resolved data type named by its id for the expectations. */ const resolveWrittenSqlTypes = (...args: Parameters) => @@ -36,6 +36,21 @@ describe('resolveWrittenSqlTypes', () => { ]); }); + it('keeps the parameters as the text writes them beside their normal form', () => { + expect( + ['timestamptz', 'timestamptz(6)', 'numeric(10)'].map((text) => + resolveWritten(text, [timestamptz, numeric]).map(({ typeParams, writtenTypeParams }) => ({ + typeParams, + writtenTypeParams, + })), + ), + ).toEqual([ + [{ typeParams: { precision: 6 }, writtenTypeParams: {} }], + [{ typeParams: { precision: 6 }, writtenTypeParams: { precision: 6 } }], + [{ typeParams: { precision: 10, scale: 0 }, writtenTypeParams: { precision: 10 } }], + ]); + }); + it('lists every type that reads the text, and none for a text no type reads or parameters it refuses', () => { expect([ resolveWrittenSqlTypes('character', [char, character]).map(({ dataType }) => dataType), diff --git a/packages/2-sql-family/2-authoring/contract-prisma7/README.md b/packages/2-sql-family/2-authoring/contract-prisma7/README.md index 9cd49166dfa6..4b1c65e1e32c 100644 --- a/packages/2-sql-family/2-authoring/contract-prisma7/README.md +++ b/packages/2-sql-family/2-authoring/contract-prisma7/README.md @@ -31,9 +31,9 @@ The package itself is target-neutral: everything specific to a database comes fr | `model` | Model named verbatim; table is `@@map` or the name; column is `@map` or the field name. | | `enum` | Native enum type named by `@@map` or the enum name, members in order, each member's value its `@map` or its name; placed in the enum's `@@schema`. | | `@@schema("s")` | Namespace `s`; without it, the target's default namespace. | -| Scalars and `@db.*` | The target binding's type map (`typeMap`), for example `DateTime` to `timestamp(3)` and `Json` to `jsonb`; lists are nullable array columns with no derived element check. | +| Scalars and `@db.*` | The target binding's type map (`typeMap`), for example `DateTime` to `timestamp(3)` and `Json` to `jsonb`; on Postgres a `DateTime` reads a JavaScript `Date`, as Prisma 7 read it, and `@db.Date` and `@db.Time` read strings. Lists are nullable array columns with no derived element check. | | `@default(...)` | Column defaults through the target's default function registry, literals, list literals, enum members; `now()` is both the column default and the `now` generator on create, resolved for the column's codec, as Prisma 8 PSL resolves `@default(now()) @onCreate(now())`, because Prisma 7's client filled it too; `uuid`, `ulid`, `nanoid`, `cuid` are client generators on create, resolved for the column's codec through the stack's generator function registry, as Prisma 8 PSL resolves `@onCreate(fn())` (`cuid` maps to `cuid(2)`); `dbgenerated("")` is a raw SQL default and `dbgenerated()` means no column default. On a list field, any function other than `dbgenerated` is `PSL.PRISMA7_UNKNOWN_DEFAULT`; write a list literal instead. | -| `@updatedAt` | `now()` resolved for the column's codec through the stack's generator function registry, on create and on update, as Prisma 8 PSL resolves `@onCreate(now()) @onUpdate(now())`; Postgres binds it to every `DateTime` column type, and a `@db.Time` or `@db.Timetz` generator receives the column's precision. Beside `@default(now())`, the column default is kept too. On a field that is not a `DateTime`, on a list, or with a native type that is not a date or time, an error. | +| `@updatedAt` | `now()` resolved for the column's codec through the stack's generator function registry, on create and on update, as Prisma 8 PSL resolves `@onCreate(now()) @onUpdate(now())`; Postgres binds it to every `DateTime` column type, and a timestamp, `@db.Time` or `@db.Timetz` generator receives the column's precision. Beside `@default(now())`, the column default is kept too. On a field that is not a `DateTime`, on a list, or with a native type that is not a date or time, an error. | | `@id`, `@@id` | Primary key, named by `map` or `{table}_pkey` cut to 63 bytes as Prisma 7 cuts it; the name is stated only where it differs from the target's own (see [Constraint names](#constraint-names)). | | `@unique`, `@@unique`, `@@index` | Indexes named `{table}_{columns}_key` and `{table}_{columns}_idx` cut to 63 bytes as Prisma 7 cuts them, `map` overriding, `type` mapped. | | Explicit relations | Foreign keys with `onDelete` `restrict` (any foreign key field required) or `setNull` (every field optional) and `onUpdate` `cascade` unless given, named by `map` or `{table}_{columns}_fkey` cut as Prisma 7 cuts it; paired through `@internal/sql-contract-psl/resolution`. | @@ -81,7 +81,7 @@ Explicit relations keep their fields, references, and actions; an omitted `onDel ## Defaults, generators, `@updatedAt`, and indexes -`@default(autoincrement())` and `@default(now())` become column defaults through the target's default function registry (`context.controlMutationDefaults`). `@default(now())` also becomes the `now` generator on create, resolved for the column's codec through the generator function registry, as Prisma 8 PSL resolves `@default(now()) @onCreate(now())`: Prisma 7's client filled the value itself and wrote the column default for other writers, so a create through Prisma 8 writes UTC whatever the session time zone, and an `INSERT` from outside Prisma still gets the column default. On a field that is not a `DateTime`, or on a `DateTime` field whose native type is not a date or time (`@db.Text`), it is `PSL.PRISMA7_UNKNOWN_DEFAULT`, as Prisma 7 refuses both. The ORM-side generators `uuid()`, `uuid(4)`, `uuid(7)`, `ulid()`, `nanoid()`, `nanoid(n)`, `cuid()`, and `cuid(2)` become execution generators on create with no column default. `cuid()` maps to `cuid2` because Prisma 7's `cuid()` is cuid v1, which Prisma 8 does not ship; the column type is the same and the ids are opaque, so existing rows keep working beside new ones. Every other call's arguments are bound through the target's signatures, as Prisma 8 PSL binds them: an argument the function does not take (`uuid(5)`, `cuid(1)`, `nanoid(1)`, `nanoid(1000)`, `nanoid(8.5)`) or a named argument (`nanoid(size: 8)`) is `PSL.PRISMA7_UNKNOWN_DEFAULT` at the argument, with the function's message, such as `Field "User.id": uuid: version must be 4 or 7, got 5`. `@default(dbgenerated(""))` becomes a raw SQL default carrying the string as written, without going through the registry; `@default(dbgenerated())` with no argument means the column has no default, on required, optional, and list fields alike. Literals of every scalar, list literals, and enum members (the member's mapped storage value) become literal defaults; a number literal is carried as the value the column codec reads: the number itself when the codec reads a JSON number, otherwise what the codec decodes from the literal exactly as written, so `Decimal` and `BigInt` defaults keep every digit and trailing zero (`"1.50"`, `"9007199254740993"`); a number with a fraction on an `Int` or `BigInt` field, or one the codec reads neither way, is `PSL.PRISMA7_UNKNOWN_DEFAULT`, as Prisma 7 rejects it, and the message says an `Int` or `BigInt` default must be a whole number; `Json` literals are parsed, and `Bytes` and `DateTime` literals are read through the column type's tag, as the current schema language reads them, and stored as values: a `Bytes` default's base64 as the hex body of `bytea`, stored as base64, and a `DateTime` default as the body that keeps what it means on the column: the written date on `date`, the written wall-clock time without its offset on `timestamp` and `time`, the time with its offset on `timetz`, and the written instant on `timestamptz`, stored in UTC (`2024-01-02T01:04:05Z`). A value with more fraction digits than its column holds (three on a plain `DateTime`, which is `timestamp(3)`) is `PSL.PRISMA7_UNKNOWN_DEFAULT`, and the message names the value PostgreSQL stores instead, rounded as PostgreSQL rounds it, to write in its place. Second 60, which PostgreSQL carries into the next minute, is refused with the tag's reason. A `Bytes[]` or `DateTime[]` list default stores each element the same way. `@updatedAt` becomes an ORM-side "now" generator on create and update; the generator function registry picks the generator from the column's codec and type parameters, and on Postgres every date and time column type has one, each writing UTC text (`timestampStringNow` for a zoneless `timestamp(3)`, `dateStringNow` for `@db.Date`, and so on); the `@db.Time(p)` and `@db.Timetz(p)` generators get `p` as their `precision` param and round down to it, so Postgres never rounds a time up to `24:00:00`. `@updatedAt` on a field that is not a `DateTime`, on a list field, or on a `DateTime` field whose native type is not a date or time (`@db.Text`), is `PSL.PRISMA7_UPDATED_AT_FIELD_INVALID`, as Prisma 7 refuses all three. `@updatedAt` beside `@default(now())` keeps the column default as well, so writes outside the ORM still get a value. A generator or `@updatedAt` on an optional field gives a nullable column: a value the client leaves out is generated, and an explicit `null` is stored as `null`, as in Prisma 7. List columns decline the element-not-null check Prisma 8 would otherwise derive, because Prisma 7 creates none. +`@default(autoincrement())` and `@default(now())` become column defaults through the target's default function registry (`context.controlMutationDefaults`). `@default(now())` also becomes the `now` generator on create, resolved for the column's codec through the generator function registry, as Prisma 8 PSL resolves `@default(now()) @onCreate(now())`: Prisma 7's client filled the value itself and wrote the column default for other writers, so a create through Prisma 8 writes UTC whatever the session time zone, and an `INSERT` from outside Prisma still gets the column default. On a field that is not a `DateTime`, or on a `DateTime` field whose native type is not a date or time (`@db.Text`), it is `PSL.PRISMA7_UNKNOWN_DEFAULT`, as Prisma 7 refuses both. The ORM-side generators `uuid()`, `uuid(4)`, `uuid(7)`, `ulid()`, `nanoid()`, `nanoid(n)`, `cuid()`, and `cuid(2)` become execution generators on create with no column default. `cuid()` maps to `cuid2` because Prisma 7's `cuid()` is cuid v1, which Prisma 8 does not ship; the column type is the same and the ids are opaque, so existing rows keep working beside new ones. Every other call's arguments are bound through the target's signatures, as Prisma 8 PSL binds them: an argument the function does not take (`uuid(5)`, `cuid(1)`, `nanoid(1)`, `nanoid(1000)`, `nanoid(8.5)`) or a named argument (`nanoid(size: 8)`) is `PSL.PRISMA7_UNKNOWN_DEFAULT` at the argument, with the function's message, such as `Field "User.id": uuid: version must be 4 or 7, got 5`. `@default(dbgenerated(""))` becomes a raw SQL default carrying the string as written, without going through the registry; `@default(dbgenerated())` with no argument means the column has no default, on required, optional, and list fields alike. Literals of every scalar, list literals, and enum members (the member's mapped storage value) become literal defaults; a number literal is carried as the value the column codec reads: the number itself when the codec reads a JSON number, otherwise what the codec decodes from the literal exactly as written, so `Decimal` and `BigInt` defaults keep every digit and trailing zero (`"1.50"`, `"9007199254740993"`); a number with a fraction on an `Int` or `BigInt` field, or one the codec reads neither way, is `PSL.PRISMA7_UNKNOWN_DEFAULT`, as Prisma 7 rejects it, and the message says an `Int` or `BigInt` default must be a whole number; `Json` literals are parsed, and `Bytes` and `DateTime` literals are read through the column type's tag, as the current schema language reads them, and stored as values: a `Bytes` default's base64 as the hex body of `bytea`, stored as base64, and a `DateTime` default as the body that keeps what it means on the column: the written date on `date`, the written wall-clock time without its offset on `timestamp` and `time`, the time with its offset on `timetz`, and the written instant on `timestamptz`, stored in UTC (`2024-01-02T01:04:05Z`). A value with more fraction digits than its column holds (three on a plain `DateTime`, which is `timestamp(3)`) is `PSL.PRISMA7_UNKNOWN_DEFAULT`, and the message names the value PostgreSQL stores instead, rounded as PostgreSQL rounds it, to write in its place. Second 60, which PostgreSQL carries into the next minute, is refused with the tag's reason. A `Bytes[]` or `DateTime[]` list default stores each element the same way. A value the column's type holds but its codec cannot hold exactly, such as a `DateTime` default with digits below a millisecond on a column read as a JavaScript `Date`, is kept as the SQL expression the target binding writes for it (`refusedLiteralDefault`), for the whole default, list or not. `@updatedAt` becomes an ORM-side "now" generator on create and update; the generator function registry picks the generator from the column's codec and type parameters, and on Postgres every date and time column type has one, each writing the current UTC time as the column's codec reads it (`timestampNow`, a `Date`, for a plain `DateTime` and a `@db.Timestamptz`, `dateStringNow` for `@db.Date`, and so on); the timestamp and `@db.Time(p)` and `@db.Timetz(p)` generators get the column's precision as their `precision` param and round down to it, so the client holds the value Postgres stores, which never rounds a `@db.Timestamp(0)` value up to the next second or a time up to `24:00:00`. `@updatedAt` on a field that is not a `DateTime`, on a list field, or on a `DateTime` field whose native type is not a date or time (`@db.Text`), is `PSL.PRISMA7_UPDATED_AT_FIELD_INVALID`, as Prisma 7 refuses all three. `@updatedAt` beside `@default(now())` keeps the column default as well, so writes outside the ORM still get a value. A generator or `@updatedAt` on an optional field gives a nullable column: a value the client leaves out is generated, and an explicit `null` is stored as `null`, as in Prisma 7. List columns decline the element-not-null check Prisma 8 would otherwise derive, because Prisma 7 creates none. `@unique` and `@@unique` become unique indexes named `{table}_{columns}_key` and `@@index` becomes an index named `{table}_{columns}_idx`, `map` overriding either (`name` on `@@unique` is the client-side name and is ignored). A generated name is cut the way Prisma 7 cuts it to fit PostgreSQL's 63-byte identifier limit: the `{table}_{columns}` part is shortened to 63 bytes minus the suffix, on a character boundary, and the suffix stays whole (`AVeryLongModelNameThatKeepsGoingAndGoingForever_aVeryLongCo_idx`). The same rule cuts an implicit junction's table name (no suffix) and its `_B_index`. `db verify` compares indexes by name, so the contract must carry the name Prisma 7 created. `type: Hash` and the other Prisma 8 index types map through; field arguments such as `sort` and `length`, and `ops`, are `PSL.PRISMA7_INDEX_ARGUMENT_UNSUPPORTED` because Prisma 8 indexes carry none. diff --git a/packages/2-sql-family/2-authoring/contract-prisma7/src/defaults.ts b/packages/2-sql-family/2-authoring/contract-prisma7/src/defaults.ts index 1136b8ed624b..f1bc49d30132 100644 --- a/packages/2-sql-family/2-authoring/contract-prisma7/src/defaults.ts +++ b/packages/2-sql-family/2-authoring/contract-prisma7/src/defaults.ts @@ -58,6 +58,8 @@ export interface LowerPrisma7DefaultInput { readonly nativeTypeName: string | undefined; readonly codecLookup: CodecLookupWithDescriptors; readonly literalForm: Prisma7LiteralDefaultForm | undefined; + /** The SQL expression of a default whose value the column's codec cannot hold exactly; see `Prisma7TargetBinding.refusedLiteralDefault`. */ + readonly refusedLiteralDefault: (stored: JsonValue) => string; /** Storage value per member name when the field is typed by a Prisma 7 enum. */ readonly enumMembers: ReadonlyMap | undefined; readonly controlMutationDefaults: ControlMutationDefaults; @@ -113,24 +115,39 @@ export function lowerPrisma7Default( return lowerFunction(call, expression, input, label, unknown); } - const scalar = scalarValue(expression, input, unknown); - if (scalar === undefined) return undefined; - return { storage: { kind: 'literal', value: scalar, inStoredForm: true }, onCreate: undefined }; + const storage = scalarValue(expression, input, unknown); + return storage === undefined ? undefined : { storage, onCreate: undefined }; } /** - * The stored value of a written default: an enum member resolves through the enum's members, and + * The stored default of a written value: an enum member resolves through the enum's members, and * every other value is read by the authoring entry for its syntax, cast into the column's data type - * and validated by the column's codec — the same path the current schema language takes. + * and validated by the column's codec — the same path the current schema language takes. A value + * the column's type holds but its codec cannot hold exactly, such as a timestamp with digits below + * a millisecond on a column read as a JavaScript `Date`, is kept as the SQL expression the target + * writes for it. */ function scalarValue( expression: ExpressionAst, input: LowerPrisma7DefaultInput, unknown: (reason: string, span: PslSpan) => undefined, -): AuthoredColumnDefaultLiteralValue | undefined { +): AuthoredColumnDefault | undefined { + const literal = scalarLiteral(expression, input, unknown); + if (literal === undefined || 'expression' in literal) return literal; + return { kind: 'literal', value: literal.value, inStoredForm: true }; +} + +function scalarLiteral( + expression: ExpressionAst, + input: LowerPrisma7DefaultInput, + unknown: (reason: string, span: PslSpan) => undefined, +): + | { readonly value: AuthoredColumnDefaultLiteralValue } + | { readonly kind: 'function'; readonly expression: string } + | undefined { const span = input.attribute.span; const enumValue = enumMemberValue(expression, input); - if (enumValue !== undefined) return enumValue; + if (enumValue !== undefined) return { value: enumValue }; const array = ArrayLiteralAst.cast(expression.syntax); const elements = array === undefined ? undefined : [...array.elements()]; @@ -143,7 +160,7 @@ function scalarValue( } values.push(value); } - return values; + return { value: values }; } const finer = finerThanColumn(elements ?? [expression], elements !== undefined, input); @@ -153,7 +170,7 @@ function scalarValue( if (written === undefined) return unreadableValue(expression, input, unknown); const jsonNull = jsonNullDefault(written, expression, input); - if (jsonNull !== undefined) return jsonNull; + if (jsonNull !== undefined) return { value: jsonNull }; const read = readDataTypeDefault({ written, @@ -163,7 +180,12 @@ function scalarValue( dataTypes: input.dataTypes, fieldPath: `${input.modelName}.${input.field.name}`, }); - return read.ok ? read.value : unknown(refusalReason(read.refusal), span); + if (read.ok) return { value: read.value }; + const { refusal } = read; + if (refusal.kind === 'refused-by-codec') { + return { kind: 'function', expression: input.refusedLiteralDefault(refusal.stored) }; + } + return unknown(refusalReason(refusal), span); } /** @@ -325,6 +347,7 @@ function refusalReason(refusal: DefaultRefusal): string { case 'no-element-cast': return `holds a ${refusal.valueType} value${at}, which the list cast of ${refusal.receivingType} does not take; it takes ${refusal.elementTypes.join(', ')}.`; case 'refused-by-column': + case 'refused-by-codec': return `holds a value${at} that ${refusal.codecId} does not read: ${refusal.message}`; } } diff --git a/packages/2-sql-family/2-authoring/contract-prisma7/src/interpreter.ts b/packages/2-sql-family/2-authoring/contract-prisma7/src/interpreter.ts index f027d29fbf77..6ca95344072d 100644 --- a/packages/2-sql-family/2-authoring/contract-prisma7/src/interpreter.ts +++ b/packages/2-sql-family/2-authoring/contract-prisma7/src/interpreter.ts @@ -1425,6 +1425,12 @@ function readField(args: ReadFieldArgs): void { dataType: sqlDataTypeOfCodec(resolved.descriptor.codecId, typeLookups).id, typeParams: resolved.descriptor.typeParams, }), + refusedLiteralDefault: (stored) => + binding.refusedLiteralDefault(stored, { + codecId: resolved.descriptor.codecId, + dataType: sqlDataTypeOfCodec(resolved.descriptor.codecId, typeLookups).id, + typeParams: resolved.descriptor.typeParams, + }), enumMembers: enumDeclaration === undefined ? undefined diff --git a/packages/2-sql-family/2-authoring/contract-prisma7/src/target-binding.ts b/packages/2-sql-family/2-authoring/contract-prisma7/src/target-binding.ts index d9bfd9baa182..7cbd34dd20b7 100644 --- a/packages/2-sql-family/2-authoring/contract-prisma7/src/target-binding.ts +++ b/packages/2-sql-family/2-authoring/contract-prisma7/src/target-binding.ts @@ -1,3 +1,4 @@ +import type { JsonValue } from '@internal/contract/types'; import type { TargetPackRef } from '@internal/framework-components/components'; import type { SqlNamespaceBase, SqlNamespaceInput } from '@internal/sql-contract/types'; import type { Prisma7TypeMap } from './native-types'; @@ -62,4 +63,6 @@ export interface Prisma7TargetBinding { ) => readonly [string, string]; /** How a literal `@default` on the column is read; `undefined` when the codec takes the written value. */ readonly literalDefaultForm: (column: Prisma7ColumnType) => Prisma7LiteralDefaultForm | undefined; + /** The SQL expression of a default whose value the column's type holds but its codec cannot hold exactly, such as a timestamp with digits below a millisecond read as a JavaScript `Date`: `stored` is the value, or a list column's elements, as the type stores them. */ + readonly refusedLiteralDefault: (stored: JsonValue, column: Prisma7ColumnType) => string; } diff --git a/packages/2-sql-family/2-authoring/contract-prisma7/test/defaults.test.ts b/packages/2-sql-family/2-authoring/contract-prisma7/test/defaults.test.ts index 4a430dbfbcc9..675ada33da52 100644 --- a/packages/2-sql-family/2-authoring/contract-prisma7/test/defaults.test.ts +++ b/packages/2-sql-family/2-authoring/contract-prisma7/test/defaults.test.ts @@ -52,7 +52,7 @@ describe('Bytes[] and DateTime[] list defaults', () => { datel: { kind: 'literal', value: ['2024-01-02'] }, timel: { kind: 'literal', value: ['12:34:56'] }, timetzl: { kind: 'literal', value: ['12:34:56Z'] }, - ts6l: { kind: 'literal', value: ['2024-01-02T03:04:05.123456'] }, + ts6l: { kind: 'function', expression: "ARRAY['2024-01-02T03:04:05.123456']::timestamp(6)[]" }, }); }); }); diff --git a/packages/2-sql-family/2-authoring/contract-prisma7/test/fixtures/datetime-defaults/expected-contract.json b/packages/2-sql-family/2-authoring/contract-prisma7/test/fixtures/datetime-defaults/expected-contract.json index ff60d35016e6..b0ce8c179493 100644 --- a/packages/2-sql-family/2-authoring/contract-prisma7/test/fixtures/datetime-defaults/expected-contract.json +++ b/packages/2-sql-family/2-authoring/contract-prisma7/test/fixtures/datetime-defaults/expected-contract.json @@ -75,7 +75,7 @@ "tsZ": { "type": { "kind": "scalar", - "codecId": "pg/timestamp-string@1", + "codecId": "pg/timestamp-date@1", "typeParams": { "precision": 3 } @@ -86,7 +86,7 @@ "tsMs": { "type": { "kind": "scalar", - "codecId": "pg/timestamp-string@1", + "codecId": "pg/timestamp-date@1", "typeParams": { "precision": 3 } @@ -97,7 +97,7 @@ "tsPlus": { "type": { "kind": "scalar", - "codecId": "pg/timestamp-string@1", + "codecId": "pg/timestamp-date@1", "typeParams": { "precision": 3 } @@ -108,7 +108,7 @@ "tsMinus": { "type": { "kind": "scalar", - "codecId": "pg/timestamp-string@1", + "codecId": "pg/timestamp-date@1", "typeParams": { "precision": 3 } @@ -119,7 +119,7 @@ "tsMicro": { "type": { "kind": "scalar", - "codecId": "pg/timestamp-string@1", + "codecId": "pg/timestamp-date@1", "typeParams": { "precision": 6 } @@ -130,7 +130,7 @@ "tzZ": { "type": { "kind": "scalar", - "codecId": "pg/timestamptz-string@1", + "codecId": "pg/timestamptz-date@1", "typeParams": { "precision": 6 } @@ -141,7 +141,7 @@ "tzPlus": { "type": { "kind": "scalar", - "codecId": "pg/timestamptz-string@1", + "codecId": "pg/timestamptz-date@1", "typeParams": { "precision": 6 } @@ -254,7 +254,7 @@ "profileHash": "3916f444a8a17ad749191acf9e08dad97d1a327b88c2f1d45d12f240296aa8b2", "meta": {}, "storage": { - "storageHash": "feda26d6f6cfb7e3e2fd75cf177cc4e5c1e7ff60deff04d5ab208cf114426e02", + "storageHash": "5772a67257453bdad9ff5ce4e2b5098608b3719f70f3479194fe2bfd6b446050", "namespaces": { "public": { "id": "public", @@ -271,7 +271,7 @@ }, "tsZ": { "dataType": "pg/timestamp", - "codecId": "pg/timestamp-string@1", + "codecId": "pg/timestamp-date@1", "nullable": false, "many": false, "typeParams": { @@ -284,7 +284,7 @@ }, "tsMs": { "dataType": "pg/timestamp", - "codecId": "pg/timestamp-string@1", + "codecId": "pg/timestamp-date@1", "nullable": false, "many": false, "typeParams": { @@ -297,7 +297,7 @@ }, "tsPlus": { "dataType": "pg/timestamp", - "codecId": "pg/timestamp-string@1", + "codecId": "pg/timestamp-date@1", "nullable": false, "many": false, "typeParams": { @@ -310,7 +310,7 @@ }, "tsMinus": { "dataType": "pg/timestamp", - "codecId": "pg/timestamp-string@1", + "codecId": "pg/timestamp-date@1", "nullable": false, "many": false, "typeParams": { @@ -323,20 +323,20 @@ }, "tsMicro": { "dataType": "pg/timestamp", - "codecId": "pg/timestamp-string@1", + "codecId": "pg/timestamp-date@1", "nullable": false, "many": false, "typeParams": { "precision": 6 }, "default": { - "kind": "literal", - "value": "2024-01-02T03:04:05.123456" + "kind": "function", + "expression": "'2024-01-02T03:04:05.123456'::timestamp(6)" } }, "tzZ": { "dataType": "pg/timestamptz", - "codecId": "pg/timestamptz-string@1", + "codecId": "pg/timestamptz-date@1", "nullable": false, "many": false, "typeParams": { @@ -349,7 +349,7 @@ }, "tzPlus": { "dataType": "pg/timestamptz", - "codecId": "pg/timestamptz-string@1", + "codecId": "pg/timestamptz-date@1", "nullable": false, "many": false, "typeParams": { diff --git a/packages/2-sql-family/2-authoring/contract-prisma7/test/fixtures/defaults/expected-contract.json b/packages/2-sql-family/2-authoring/contract-prisma7/test/fixtures/defaults/expected-contract.json index e2adb69e8b51..e6d66c9c6c0d 100644 --- a/packages/2-sql-family/2-authoring/contract-prisma7/test/fixtures/defaults/expected-contract.json +++ b/packages/2-sql-family/2-authoring/contract-prisma7/test/fixtures/defaults/expected-contract.json @@ -86,7 +86,7 @@ "createdAt": { "type": { "kind": "scalar", - "codecId": "pg/timestamp-string@1", + "codecId": "pg/timestamp-date@1", "typeParams": { "precision": 3 } @@ -97,7 +97,7 @@ "createdAtTz": { "type": { "kind": "scalar", - "codecId": "pg/timestamptz-string@1", + "codecId": "pg/timestamptz-date@1", "typeParams": { "precision": 6 } @@ -168,7 +168,7 @@ "dateTimeLiteral": { "type": { "kind": "scalar", - "codecId": "pg/timestamp-string@1", + "codecId": "pg/timestamp-date@1", "typeParams": { "precision": 3 } @@ -250,7 +250,7 @@ } }, "execution": { - "executionHash": "34724d7f5b296e0eee40a5a82495ef0ff3eb4bcb7428d1261cf475fb29be215b", + "executionHash": "16f44213037f9551e1ff3ab95c43a8ff95c77b0332ce526f45df70bea0c4c416", "mutations": { "defaults": [ { @@ -261,7 +261,7 @@ }, "onCreate": { "kind": "generator", - "id": "timestampStringNow", + "id": "timestampNow", "params": { "precision": 3 } @@ -275,7 +275,7 @@ }, "onCreate": { "kind": "generator", - "id": "timestamptzStringNow", + "id": "timestampNow", "params": { "precision": 6 } @@ -289,7 +289,7 @@ "profileHash": "3916f444a8a17ad749191acf9e08dad97d1a327b88c2f1d45d12f240296aa8b2", "meta": {}, "storage": { - "storageHash": "df40eca2aa7086780f4e2e2bb1ad623cc60b7c5253a32cd076be318e8ae69c27", + "storageHash": "3866a5e224af9199d6b8ae227ff30109a72405c2d23a1209458c2d3f63596324", "namespaces": { "public": { "id": "public", @@ -320,7 +320,7 @@ }, "createdAt": { "dataType": "pg/timestamp", - "codecId": "pg/timestamp-string@1", + "codecId": "pg/timestamp-date@1", "nullable": false, "many": false, "typeParams": { @@ -333,7 +333,7 @@ }, "createdAtTz": { "dataType": "pg/timestamptz", - "codecId": "pg/timestamptz-string@1", + "codecId": "pg/timestamptz-date@1", "nullable": false, "many": false, "typeParams": { @@ -420,7 +420,7 @@ }, "dateTimeLiteral": { "dataType": "pg/timestamp", - "codecId": "pg/timestamp-string@1", + "codecId": "pg/timestamp-date@1", "nullable": false, "many": false, "typeParams": { diff --git a/packages/2-sql-family/2-authoring/contract-prisma7/test/fixtures/ignored-field-defaults/expected-contract.json b/packages/2-sql-family/2-authoring/contract-prisma7/test/fixtures/ignored-field-defaults/expected-contract.json index 2d84eee37b3f..84ef0dd4289e 100644 --- a/packages/2-sql-family/2-authoring/contract-prisma7/test/fixtures/ignored-field-defaults/expected-contract.json +++ b/packages/2-sql-family/2-authoring/contract-prisma7/test/fixtures/ignored-field-defaults/expected-contract.json @@ -42,7 +42,7 @@ "profileHash": "3916f444a8a17ad749191acf9e08dad97d1a327b88c2f1d45d12f240296aa8b2", "meta": {}, "storage": { - "storageHash": "8e7bd87d3410fa8c14b9861bc31aaa1f572c78a30e045d315c3c436fd5b90f94", + "storageHash": "5e7f6b214527aec54879d1123e2e50bf622b4750aa0e5b3d4a6f7803691436f3", "namespaces": { "public": { "id": "public", @@ -69,7 +69,7 @@ }, "createdAt": { "dataType": "pg/timestamp", - "codecId": "pg/timestamp-string@1", + "codecId": "pg/timestamp-date@1", "nullable": false, "many": false, "typeParams": { @@ -94,7 +94,7 @@ }, "touchedAt": { "dataType": "pg/timestamp", - "codecId": "pg/timestamp-string@1", + "codecId": "pg/timestamp-date@1", "nullable": false, "many": false, "typeParams": { @@ -103,7 +103,7 @@ }, "seenAt": { "dataType": "pg/timestamp", - "codecId": "pg/timestamp-string@1", + "codecId": "pg/timestamp-date@1", "nullable": true, "many": false, "typeParams": { @@ -126,7 +126,7 @@ }, "stamp": { "dataType": "pg/timestamp", - "codecId": "pg/timestamp-string@1", + "codecId": "pg/timestamp-date@1", "nullable": false, "many": false, "typeParams": { diff --git a/packages/2-sql-family/2-authoring/contract-prisma7/test/fixtures/list-defaults/expected-contract.json b/packages/2-sql-family/2-authoring/contract-prisma7/test/fixtures/list-defaults/expected-contract.json index 665612d88e19..d17a2f057bda 100644 --- a/packages/2-sql-family/2-authoring/contract-prisma7/test/fixtures/list-defaults/expected-contract.json +++ b/packages/2-sql-family/2-authoring/contract-prisma7/test/fixtures/list-defaults/expected-contract.json @@ -74,7 +74,7 @@ "dl": { "type": { "kind": "scalar", - "codecId": "pg/timestamp-string@1", + "codecId": "pg/timestamp-date@1", "typeParams": { "precision": 3 } @@ -87,7 +87,7 @@ "dlEmpty": { "type": { "kind": "scalar", - "codecId": "pg/timestamp-string@1", + "codecId": "pg/timestamp-date@1", "typeParams": { "precision": 3 } @@ -100,7 +100,7 @@ "tzl": { "type": { "kind": "scalar", - "codecId": "pg/timestamptz-string@1", + "codecId": "pg/timestamptz-date@1", "typeParams": { "precision": 6 } @@ -149,7 +149,7 @@ "ts6l": { "type": { "kind": "scalar", - "codecId": "pg/timestamp-string@1", + "codecId": "pg/timestamp-date@1", "typeParams": { "precision": 6 } @@ -177,7 +177,7 @@ "profileHash": "3916f444a8a17ad749191acf9e08dad97d1a327b88c2f1d45d12f240296aa8b2", "meta": {}, "storage": { - "storageHash": "f1cdd79e91d6fdd6e05ca7cc6078d30664a13a3fec4c10cad4947bb2dc2616b4", + "storageHash": "5eb58269a5271620ca1beb81f4e851671e4f6c10dedb97290eb185fac02f6f70", "namespaces": { "public": { "id": "public", @@ -218,7 +218,7 @@ }, "dl": { "dataType": "pg/timestamp", - "codecId": "pg/timestamp-string@1", + "codecId": "pg/timestamp-date@1", "nullable": true, "many": { "elementNullable": true @@ -233,7 +233,7 @@ }, "dlEmpty": { "dataType": "pg/timestamp", - "codecId": "pg/timestamp-string@1", + "codecId": "pg/timestamp-date@1", "nullable": true, "many": { "elementNullable": true @@ -248,7 +248,7 @@ }, "tzl": { "dataType": "pg/timestamptz", - "codecId": "pg/timestamptz-string@1", + "codecId": "pg/timestamptz-date@1", "nullable": true, "many": { "elementNullable": true @@ -305,7 +305,7 @@ }, "ts6l": { "dataType": "pg/timestamp", - "codecId": "pg/timestamp-string@1", + "codecId": "pg/timestamp-date@1", "nullable": true, "many": { "elementNullable": true @@ -314,8 +314,8 @@ "precision": 6 }, "default": { - "kind": "literal", - "value": ["2024-01-02T03:04:05.123456"] + "kind": "function", + "expression": "ARRAY['2024-01-02T03:04:05.123456']::timestamp(6)[]" } } }, diff --git a/packages/2-sql-family/2-authoring/contract-prisma7/test/fixtures/native-types-accepted/expected-contract.json b/packages/2-sql-family/2-authoring/contract-prisma7/test/fixtures/native-types-accepted/expected-contract.json index 3185681e7cd6..13dadf2c2ad7 100644 --- a/packages/2-sql-family/2-authoring/contract-prisma7/test/fixtures/native-types-accepted/expected-contract.json +++ b/packages/2-sql-family/2-authoring/contract-prisma7/test/fixtures/native-types-accepted/expected-contract.json @@ -188,7 +188,7 @@ "timestamp": { "type": { "kind": "scalar", - "codecId": "pg/timestamp-string@1", + "codecId": "pg/timestamp-date@1", "typeParams": { "precision": 6 } @@ -199,7 +199,7 @@ "timestamptz": { "type": { "kind": "scalar", - "codecId": "pg/timestamptz-string@1", + "codecId": "pg/timestamptz-date@1", "typeParams": { "precision": 6 } @@ -277,7 +277,7 @@ "timestamptzOpt": { "type": { "kind": "scalar", - "codecId": "pg/timestamptz-string@1", + "codecId": "pg/timestamptz-date@1", "typeParams": { "precision": 3 } @@ -303,7 +303,7 @@ "profileHash": "3916f444a8a17ad749191acf9e08dad97d1a327b88c2f1d45d12f240296aa8b2", "meta": {}, "storage": { - "storageHash": "149d8890e723d0b02331ea679cf5b6bc01db44215c8a0c87fb9df29f5a983f35", + "storageHash": "8838cef0f08f8f027e855e594f549c3f55e8fdc5723c6338f5151cc7e903e932", "namespaces": { "public": { "id": "public", @@ -396,7 +396,7 @@ }, "timestamp": { "dataType": "pg/timestamp", - "codecId": "pg/timestamp-string@1", + "codecId": "pg/timestamp-date@1", "nullable": false, "many": false, "typeParams": { @@ -405,7 +405,7 @@ }, "timestamptz": { "dataType": "pg/timestamptz", - "codecId": "pg/timestamptz-string@1", + "codecId": "pg/timestamptz-date@1", "nullable": false, "many": false, "typeParams": { @@ -467,7 +467,7 @@ }, "timestamptzOpt": { "dataType": "pg/timestamptz", - "codecId": "pg/timestamptz-string@1", + "codecId": "pg/timestamptz-date@1", "nullable": true, "many": false, "typeParams": { diff --git a/packages/2-sql-family/2-authoring/contract-prisma7/test/fixtures/native-types-without-arguments/expected-contract.json b/packages/2-sql-family/2-authoring/contract-prisma7/test/fixtures/native-types-without-arguments/expected-contract.json index 6d6d915620cf..d3ff5acdfdf0 100644 --- a/packages/2-sql-family/2-authoring/contract-prisma7/test/fixtures/native-types-without-arguments/expected-contract.json +++ b/packages/2-sql-family/2-authoring/contract-prisma7/test/fixtures/native-types-without-arguments/expected-contract.json @@ -75,7 +75,7 @@ "timestamp": { "type": { "kind": "scalar", - "codecId": "pg/timestamp-string@1" + "codecId": "pg/timestamp-date@1" }, "nullable": false, "many": false @@ -83,7 +83,7 @@ "timestamptz": { "type": { "kind": "scalar", - "codecId": "pg/timestamptz-string@1" + "codecId": "pg/timestamptz-date@1" }, "nullable": false, "many": false @@ -122,7 +122,7 @@ "profileHash": "3916f444a8a17ad749191acf9e08dad97d1a327b88c2f1d45d12f240296aa8b2", "meta": {}, "storage": { - "storageHash": "f28e64193b60a26ff5ceaa3bb9339d2b5a10ff67030771a3a52ab7334105a5a1", + "storageHash": "eae4cf8beefa2f644b3e2d462b69e8447946c347f1aa21fda6587cd3b829dbba", "namespaces": { "public": { "id": "public", @@ -160,13 +160,13 @@ }, "timestamp": { "dataType": "pg/timestamp", - "codecId": "pg/timestamp-string@1", + "codecId": "pg/timestamp-date@1", "nullable": false, "many": false }, "timestamptz": { "dataType": "pg/timestamptz", - "codecId": "pg/timestamptz-string@1", + "codecId": "pg/timestamptz-date@1", "nullable": false, "many": false }, diff --git a/packages/2-sql-family/2-authoring/contract-prisma7/test/fixtures/scalars/expected-contract.json b/packages/2-sql-family/2-authoring/contract-prisma7/test/fixtures/scalars/expected-contract.json index 52868fe228a2..bd142191e7ea 100644 --- a/packages/2-sql-family/2-authoring/contract-prisma7/test/fixtures/scalars/expected-contract.json +++ b/packages/2-sql-family/2-authoring/contract-prisma7/test/fixtures/scalars/expected-contract.json @@ -265,7 +265,7 @@ "dateTime": { "type": { "kind": "scalar", - "codecId": "pg/timestamp-string@1", + "codecId": "pg/timestamp-date@1", "typeParams": { "precision": 3 } @@ -276,7 +276,7 @@ "dateTimeOpt": { "type": { "kind": "scalar", - "codecId": "pg/timestamp-string@1", + "codecId": "pg/timestamp-date@1", "typeParams": { "precision": 3 } @@ -287,7 +287,7 @@ "dateTimeList": { "type": { "kind": "scalar", - "codecId": "pg/timestamp-string@1", + "codecId": "pg/timestamp-date@1", "typeParams": { "precision": 3 } @@ -367,7 +367,7 @@ "profileHash": "3916f444a8a17ad749191acf9e08dad97d1a327b88c2f1d45d12f240296aa8b2", "meta": {}, "storage": { - "storageHash": "936fa7188bbe44706b929d6392eb9f0b9c0329f0b3cd9a513c78ac9126b64424", + "storageHash": "1578dc0d71fb46e51ae88d63cbaad7cb666f0a37b99de9921ea55d88892603e7", "namespaces": { "public": { "id": "public", @@ -510,7 +510,7 @@ }, "dateTime": { "dataType": "pg/timestamp", - "codecId": "pg/timestamp-string@1", + "codecId": "pg/timestamp-date@1", "nullable": false, "many": false, "typeParams": { @@ -519,7 +519,7 @@ }, "dateTimeOpt": { "dataType": "pg/timestamp", - "codecId": "pg/timestamp-string@1", + "codecId": "pg/timestamp-date@1", "nullable": true, "many": false, "typeParams": { @@ -528,7 +528,7 @@ }, "dateTimeList": { "dataType": "pg/timestamp", - "codecId": "pg/timestamp-string@1", + "codecId": "pg/timestamp-date@1", "nullable": true, "many": { "elementNullable": true diff --git a/packages/2-sql-family/2-authoring/contract-prisma7/test/fixtures/updated-at-optional/expected-contract.json b/packages/2-sql-family/2-authoring/contract-prisma7/test/fixtures/updated-at-optional/expected-contract.json index 1fd89ad2fb00..a3d487089d3f 100644 --- a/packages/2-sql-family/2-authoring/contract-prisma7/test/fixtures/updated-at-optional/expected-contract.json +++ b/packages/2-sql-family/2-authoring/contract-prisma7/test/fixtures/updated-at-optional/expected-contract.json @@ -30,7 +30,7 @@ "updatedAtOpt": { "type": { "kind": "scalar", - "codecId": "pg/timestamp-string@1", + "codecId": "pg/timestamp-date@1", "typeParams": { "precision": 3 } @@ -52,7 +52,7 @@ } }, "execution": { - "executionHash": "fd49086f64d04b8fbdb1a5168eae87b6272974b85c774c2ba52bc13665040d49", + "executionHash": "1760fa0f34d6d5e40e3be067e8204259f351ff29ac6f9a4f86b497657976ed77", "mutations": { "defaults": [ { @@ -63,14 +63,14 @@ }, "onCreate": { "kind": "generator", - "id": "timestampStringNow", + "id": "timestampNow", "params": { "precision": 3 } }, "onUpdate": { "kind": "generator", - "id": "timestampStringNow", + "id": "timestampNow", "params": { "precision": 3 } @@ -84,7 +84,7 @@ "profileHash": "3916f444a8a17ad749191acf9e08dad97d1a327b88c2f1d45d12f240296aa8b2", "meta": {}, "storage": { - "storageHash": "9971b630bbcace6e16ebbdbf2faae2ec4401c53c511aa014b9b0138c4a276897", + "storageHash": "cfb60251abfe2853eb71b3ed9282850fd6a1845831007eedc90d771c9f882074", "namespaces": { "public": { "id": "public", @@ -101,7 +101,7 @@ }, "updatedAtOpt": { "dataType": "pg/timestamp", - "codecId": "pg/timestamp-string@1", + "codecId": "pg/timestamp-date@1", "nullable": true, "many": false, "typeParams": { diff --git a/packages/2-sql-family/2-authoring/contract-prisma7/test/fixtures/updated-at-with-default/expected-contract.json b/packages/2-sql-family/2-authoring/contract-prisma7/test/fixtures/updated-at-with-default/expected-contract.json index 8fecb848c6e0..5b7251d10b1c 100644 --- a/packages/2-sql-family/2-authoring/contract-prisma7/test/fixtures/updated-at-with-default/expected-contract.json +++ b/packages/2-sql-family/2-authoring/contract-prisma7/test/fixtures/updated-at-with-default/expected-contract.json @@ -30,7 +30,7 @@ "updatedAtNow": { "type": { "kind": "scalar", - "codecId": "pg/timestamp-string@1", + "codecId": "pg/timestamp-date@1", "typeParams": { "precision": 3 } @@ -52,7 +52,7 @@ } }, "execution": { - "executionHash": "7a7d1cf6cde9c00b1c57375b140d9c74dd354caedbbb7c0e201c8bd51cfc9e1e", + "executionHash": "761c21afb0a2f13f4614a430fa13d19469afa51b650d39e6f27b74969eeec340", "mutations": { "defaults": [ { @@ -63,14 +63,14 @@ }, "onCreate": { "kind": "generator", - "id": "timestampStringNow", + "id": "timestampNow", "params": { "precision": 3 } }, "onUpdate": { "kind": "generator", - "id": "timestampStringNow", + "id": "timestampNow", "params": { "precision": 3 } @@ -84,7 +84,7 @@ "profileHash": "3916f444a8a17ad749191acf9e08dad97d1a327b88c2f1d45d12f240296aa8b2", "meta": {}, "storage": { - "storageHash": "46ca87631e23000ebc3f972b217ed1c5ca71e70935080266337c0388aed2d13b", + "storageHash": "f47affdbe95aea63ee9609e5958360707a670a88f55ce8d5b93d56a9c6a16d8f", "namespaces": { "public": { "id": "public", @@ -101,7 +101,7 @@ }, "updatedAtNow": { "dataType": "pg/timestamp", - "codecId": "pg/timestamp-string@1", + "codecId": "pg/timestamp-date@1", "nullable": false, "many": false, "typeParams": { diff --git a/packages/2-sql-family/2-authoring/contract-prisma7/test/fixtures/updated-at/expected-contract.json b/packages/2-sql-family/2-authoring/contract-prisma7/test/fixtures/updated-at/expected-contract.json index dedc0f97de6b..45976f3255bd 100644 --- a/packages/2-sql-family/2-authoring/contract-prisma7/test/fixtures/updated-at/expected-contract.json +++ b/packages/2-sql-family/2-authoring/contract-prisma7/test/fixtures/updated-at/expected-contract.json @@ -33,7 +33,7 @@ "updatedAt": { "type": { "kind": "scalar", - "codecId": "pg/timestamp-string@1", + "codecId": "pg/timestamp-date@1", "typeParams": { "precision": 3 } @@ -44,7 +44,7 @@ "updatedAtTz": { "type": { "kind": "scalar", - "codecId": "pg/timestamptz-string@1", + "codecId": "pg/timestamptz-date@1", "typeParams": { "precision": 6 } @@ -66,7 +66,7 @@ } }, "execution": { - "executionHash": "6648fa9bcd0cacf082bbb524662c5e16474259ce08470b5ee52cf6826b1b88a8", + "executionHash": "b2ae3a1c323b84ac42b2e3cbf2b9815dda604f2ab28dde7f041581168eeae217", "mutations": { "defaults": [ { @@ -77,14 +77,14 @@ }, "onCreate": { "kind": "generator", - "id": "timestampStringNow", + "id": "timestampNow", "params": { "precision": 3 } }, "onUpdate": { "kind": "generator", - "id": "timestampStringNow", + "id": "timestampNow", "params": { "precision": 3 } @@ -98,14 +98,14 @@ }, "onCreate": { "kind": "generator", - "id": "timestamptzStringNow", + "id": "timestampNow", "params": { "precision": 6 } }, "onUpdate": { "kind": "generator", - "id": "timestamptzStringNow", + "id": "timestampNow", "params": { "precision": 6 } @@ -119,7 +119,7 @@ "profileHash": "3916f444a8a17ad749191acf9e08dad97d1a327b88c2f1d45d12f240296aa8b2", "meta": {}, "storage": { - "storageHash": "b6ff179714a91b72e54295867c9a137b278c3923f5bf7ff03b719763d5dfc60b", + "storageHash": "ca3e29fc4e90265e1b10a48ef33decf69b48a26c5df9b257afc326b457127339", "namespaces": { "public": { "id": "public", @@ -136,7 +136,7 @@ }, "updatedAt": { "dataType": "pg/timestamp", - "codecId": "pg/timestamp-string@1", + "codecId": "pg/timestamp-date@1", "nullable": false, "many": false, "typeParams": { @@ -145,7 +145,7 @@ }, "updatedAtTz": { "dataType": "pg/timestamptz", - "codecId": "pg/timestamptz-string@1", + "codecId": "pg/timestamptz-date@1", "nullable": false, "many": false, "typeParams": { diff --git a/packages/2-sql-family/2-authoring/contract-prisma7/test/now-default-generators.test.ts b/packages/2-sql-family/2-authoring/contract-prisma7/test/now-default-generators.test.ts index abb34d951948..48cb47856ea8 100644 --- a/packages/2-sql-family/2-authoring/contract-prisma7/test/now-default-generators.test.ts +++ b/packages/2-sql-family/2-authoring/contract-prisma7/test/now-default-generators.test.ts @@ -38,10 +38,10 @@ model Post { }); expect(contract.execution?.mutations.defaults).toEqual([ { ref: ref('clock'), onCreate: now('timeStringNow', { precision: 0 }) }, - { ref: ref('createdAt'), onCreate: now('timestampStringNow', { precision: 3 }) }, + { ref: ref('createdAt'), onCreate: now('timestampNow', { precision: 3 }) }, { ref: ref('day'), onCreate: now('dateStringNow') }, - { ref: ref('optionalAt'), onCreate: now('timestampStringNow', { precision: 3 }) }, - { ref: ref('zonedAt'), onCreate: now('timestamptzStringNow', { precision: 6 }) }, + { ref: ref('optionalAt'), onCreate: now('timestampNow', { precision: 3 }) }, + { ref: ref('zonedAt'), onCreate: now('timestampNow', { precision: 6 }) }, ]); }); @@ -61,8 +61,8 @@ model Post { expect(contract.execution?.mutations.defaults).toEqual([ { ref: ref('updatedAt'), - onCreate: now('timestampStringNow', { precision: 3 }), - onUpdate: now('timestampStringNow', { precision: 3 }), + onCreate: now('timestampNow', { precision: 3 }), + onUpdate: now('timestampNow', { precision: 3 }), }, ]); }); diff --git a/packages/2-sql-family/2-authoring/contract-prisma7/test/sub-millisecond-defaults.test.ts b/packages/2-sql-family/2-authoring/contract-prisma7/test/sub-millisecond-defaults.test.ts new file mode 100644 index 000000000000..afd40a0ffda8 --- /dev/null +++ b/packages/2-sql-family/2-authoring/contract-prisma7/test/sub-millisecond-defaults.test.ts @@ -0,0 +1,32 @@ +import { describe, expect, it } from 'vitest'; +import { interpretSchemaText, serializedTable } from './support'; + +describe('a DateTime default with digits below a millisecond', () => { + it('is kept as the SQL the target writes for it, which a Date cannot hold as a value, single or in a list', () => { + const contract = interpretSchemaText(`datasource db { + provider = "postgresql" +} + +model Post { + id Int @id + tsMicro DateTime @default("2024-01-02T03:04:05.123456Z") @db.Timestamp(6) + tsMs DateTime @default("2024-01-02T03:04:05.123Z") + ts6l DateTime[] @default(["2024-01-02T03:04:05.123Z", "2024-01-02T03:04:05.123456Z"]) @db.Timestamp(6) +} +`); + const { columns } = serializedTable(contract, 'Post'); + expect({ + tsMicro: columns['tsMicro']?.['default'], + tsMs: columns['tsMs']?.['default'], + ts6l: columns['ts6l']?.['default'], + }).toEqual({ + tsMicro: { kind: 'function', expression: "'2024-01-02T03:04:05.123456'::timestamp(6)" }, + tsMs: { kind: 'literal', value: '2024-01-02T03:04:05.123' }, + ts6l: { + kind: 'function', + expression: + "ARRAY['2024-01-02T03:04:05.123', '2024-01-02T03:04:05.123456']::timestamp(6)[]", + }, + }); + }); +}); diff --git a/packages/2-sql-family/2-authoring/contract-prisma7/test/updated-at-generators.test.ts b/packages/2-sql-family/2-authoring/contract-prisma7/test/updated-at-generators.test.ts index 453ead0dd568..28554577df53 100644 --- a/packages/2-sql-family/2-authoring/contract-prisma7/test/updated-at-generators.test.ts +++ b/packages/2-sql-family/2-authoring/contract-prisma7/test/updated-at-generators.test.ts @@ -61,13 +61,13 @@ model Clock { }, { ref: ref('stampedAt'), - onCreate: now('timestampStringNow', { precision: 0 }), - onUpdate: now('timestampStringNow', { precision: 0 }), + onCreate: now('timestampNow', { precision: 0 }), + onUpdate: now('timestampNow', { precision: 0 }), }, { ref: ref('updatedAt'), - onCreate: now('timestampStringNow', { precision: 3 }), - onUpdate: now('timestampStringNow', { precision: 3 }), + onCreate: now('timestampNow', { precision: 3 }), + onUpdate: now('timestampNow', { precision: 3 }), }, ]); }); diff --git a/packages/2-sql-family/2-authoring/contract-psl/README.md b/packages/2-sql-family/2-authoring/contract-psl/README.md index d674b77e9fc1..a5a1b18ec211 100644 --- a/packages/2-sql-family/2-authoring/contract-psl/README.md +++ b/packages/2-sql-family/2-authoring/contract-psl/README.md @@ -115,7 +115,7 @@ model Post { - An alias's base is a type constructor, called or bare, or an enum. It may not name another alias or a contributed alias (`PSL_UNSUPPORTED_NAMED_TYPE_BASE`). - An alias may carry `@default`, `@onCreate` and `@onUpdate`. They are lowered once, at the declaration, so a diagnostic is reported once, on the alias. Every field typed by the alias takes them. `@id` and `@unique` on an alias are `PSL_UNSUPPORTED_NAMED_TYPE_ATTRIBUTE`, because they belong to a model's field, not to its type; write them on the field. A refused attribute is dropped and the alias is kept. - A field may add any attribute its alias does not set, including `@id` and `@unique`. A field that writes an attribute its alias already sets is `PSL_DUPLICATE_ATTRIBUTE`, naming the alias. A list field typed by an alias that generates a value is `PSL_LIST_EXECUTION_DEFAULT_UNSUPPORTED`; an optional field may use one. -- The targets contribute aliases with no arguments, written bare: `temporal.CreatedAt` (`now()` on create) and `temporal.UpdatedAt` (`now()` on create and on update). On Postgres they are `TimestamptzTemporal`, with `temporal.CreatedAtString` / `temporal.UpdatedAtString` on `TimestamptzString` and `temporal.CreatedAtJsDate` / `temporal.UpdatedAtJsDate` on `TimestamptzJsDate`; on SQLite they are `DateTime`. Each stores what the matching `temporal.createdAt()` or `temporal.updatedAt()` preset stores. `temporal.CreatedAt(3)` is `PSL_INVALID_ATTRIBUTE_ARGUMENT`; write a precision with the attributes, as in `TimestamptzTemporal(3) @onCreate(now())`. +- The targets contribute aliases with no arguments, written bare: `temporal.CreatedAt` (`now()` on create) and `temporal.UpdatedAt` (`now()` on create and on update). On Postgres they are the convenience type `DateTime` with its defaults, a `timestamptz(3)` column read as a JavaScript `Date`; on SQLite they are `DateTime`. `temporal.CreatedAt(3)` is `PSL_INVALID_ATTRIBUTE_ARGUMENT`; write the type with the attributes, as in `DateTime(as: temporal) @onCreate(now())`. Integer representation authoring surface: diff --git a/packages/2-sql-family/2-authoring/contract-psl/src/data-type-default.ts b/packages/2-sql-family/2-authoring/contract-psl/src/data-type-default.ts index eda332d68ee6..12c35144217a 100644 --- a/packages/2-sql-family/2-authoring/contract-psl/src/data-type-default.ts +++ b/packages/2-sql-family/2-authoring/contract-psl/src/data-type-default.ts @@ -57,13 +57,23 @@ export interface DefaultColumn { readonly typeParams?: Record | undefined; } -/** A value in its stored JSON form that the column's type, under the column's parameters, or the column's codec refuses. */ -type CodecRefusal = { - readonly kind: 'refused-by-column'; - readonly codecId: string; - readonly message: string; - readonly elementIndex: number | undefined; -}; +/** + * A value in its stored JSON form that the column refuses: `refused-by-column` when its type, under the column's parameters, refuses it, and `refused-by-codec` when the type holds it as `stored` but the column's codec cannot hold it exactly, as a JavaScript `Date` cannot hold digits below a millisecond. On a list column, `stored` holds every element, and `elementIndex` is the first the codec refuses. + */ +type CodecRefusal = + | { + readonly kind: 'refused-by-column'; + readonly codecId: string; + readonly message: string; + readonly elementIndex: number | undefined; + } + | { + readonly kind: 'refused-by-codec'; + readonly codecId: string; + readonly message: string; + readonly elementIndex: number | undefined; + readonly stored: JsonValue; + }; /** * Why a default was refused, in parts, so each contract source words its own diagnostic: the cast @@ -89,9 +99,11 @@ export type DefaultRefusal = { /** The element types the receiving type's list cast takes. */ readonly elementTypes: readonly DataTypeId[]; } - | Omit + | DistributiveOmit ); +type DistributiveOmit = T extends unknown ? Omit : never; + export type ReadDefaultResult = | { readonly ok: true; readonly value: JsonValue } | { @@ -195,11 +207,9 @@ function storedValueReader(input: { const suggestedTypes = [descriptor.dataType]; const params = dataTypeParams(codec.dataType, input.column.typeParams); const read = (written: JsonValue, elementIndex: number | undefined): StoredReadResult => { - let stored: JsonValue; + let value: ReturnType; try { - const value = codec.dataType.valueFrom(written, params); - codec.fromDataTypeValue(value); - stored = codec.dataType.toContract(value); + value = codec.dataType.valueFrom(written, params); } catch (error) { if (isInternalError(error)) throw error; return { @@ -213,6 +223,23 @@ function storedValueReader(input: { suggestedTypes, }; } + const stored = codec.dataType.toContract(value); + try { + codec.fromDataTypeValue(value); + } catch (error) { + if (isInternalError(error)) throw error; + return { + ok: false, + refusal: { + kind: 'refused-by-codec', + codecId: input.column.codecId, + message: messageOf(error), + elementIndex, + stored, + }, + suggestedTypes, + }; + } return { ok: true, value: stored }; }; return { descriptor, params, read }; @@ -289,13 +316,33 @@ export function readDataTypeDefault(input: { if (input.isList) { const elements: JsonValue[] = []; + let codecRefusal: Extract | undefined; for (const [elementIndex, written] of input.written.elements.entries()) { if (written.kind === 'list') return nestedList(elementIndex, suggestedTypes); const element = readOne(written, elementIndex); - if (!element.ok) return element; + if (!element.ok && element.refusal.kind === 'refused-by-codec') { + codecRefusal ??= element.refusal; + elements.push(element.refusal.stored); + continue; + } + if (!element.ok) { + return codecRefusal === undefined + ? element + : refused( + { + kind: 'refused-by-column', + codecId: codecRefusal.codecId, + message: codecRefusal.message, + elementIndex: codecRefusal.elementIndex, + }, + suggestedTypes, + ); + } elements.push(element.value); } - return { ok: true, value: elements }; + return codecRefusal === undefined + ? { ok: true, value: elements } + : refused({ ...codecRefusal, stored: elements }, suggestedTypes); } return readListIntoScalar({ ...input, written: input.written, columnType, readStored }); @@ -450,11 +497,7 @@ function codecRefusalMessage(refusal: CodecRefusal, subject: string): RefusalMes } function storedRefusalMessage(refusal: StoredRefusal, subject: string): RefusalMessage { - if (refusal.kind === 'refused-by-column') return codecRefusalMessage(refusal, subject); - return { - code: 'PSL_INVALID_LITERAL', - message: `${location(subject, refusal.elementIndex)}: ${refusal.message}`, - }; + return codecRefusalMessage(refusal, subject); } /** A refusal worded as a PSL diagnostic's code and message; `guidance` says what to write instead. */ @@ -472,6 +515,7 @@ function refusalMessage( message: `${where}: this column holds a list, so its default is a list literal, as in [1, 2]`, }; case 'refused-by-column': + case 'refused-by-codec': return codecRefusalMessage(refusal, subject); case 'no-list-cast': return { @@ -494,6 +538,7 @@ function refusalSpan(refusal: DefaultRefusal, spans: DefaultSpans, subject: stri switch (refusal.kind) { case 'not-a-list': case 'refused-by-column': + case 'refused-by-codec': return spans.attribute; default: return writtenValueSpan(spans, refusal.elementIndex, subject); diff --git a/packages/2-sql-family/2-authoring/contract-ts/README.md b/packages/2-sql-family/2-authoring/contract-ts/README.md index 884f2c349c69..9b09767c76e6 100644 --- a/packages/2-sql-family/2-authoring/contract-ts/README.md +++ b/packages/2-sql-family/2-authoring/contract-ts/README.md @@ -146,7 +146,7 @@ Anything else inside `${…}` is a type error, and `CONTRACT.SQL_EXPRESSION_INTE Pack-provided helper presets are available through the callback overload. This is the surface that exposes `field.id.*`, `field.text()`, `field.temporal.createdAt()`, `field.temporal.updatedAt()`, the `type.*` helpers, and extension helpers such as `type.pgvector.Vector(...)`. -`type.*` has a helper for every PSL type constructor of the stack, such as `type.VarChar(64)`, `type.TimestampString(3)` and `type.String()`, plus the type aliases the packs contribute, such as `type.temporal.CreatedAt()` and `type.temporal.UpdatedAt()`. A type choice, such as PostgreSQL's `DateTime`, takes its arguments as one object, `type.DateTime({ as: 'string', precision: 6 })`, and is typed as the explicit type its arguments choose. Pass the result to `field.column(...)`. A type alias is a constant: field builders are immutable, so one constant serves every field and model that uses it, and each column stores the alias's codec and params inline. A constant may carry a default and generators, and a field may add what the constant does not set: +`type.*` has a helper for every PSL type constructor of the stack, such as `type.VarChar(64)`, `type.TimestampString(3)` and `type.String()`, plus the type aliases the packs contribute, such as `type.temporal.CreatedAt()` and `type.temporal.UpdatedAt()`, which on Postgres are `type.DateTime()` with the `now` generators. A type choice, such as PostgreSQL's `DateTime`, takes its arguments as one object, `type.DateTime({ as: 'string', precision: 6 })`, and is typed as the explicit type its arguments choose. Pass the result to `field.column(...)`. A type alias is a constant: field builders are immutable, so one constant serves every field and model that uses it, and each column stores the alias's codec and params inline. A constant may carry a default and generators, and a field may add what the constant does not set: ```typescript const UpdatedAt = field.column(type.TimestampString(3)).onCreate(gen.now()).onUpdate(gen.now()); diff --git a/packages/2-sql-family/9-family/README.md b/packages/2-sql-family/9-family/README.md index 7bd46ef5587a..9dadf694b12e 100644 --- a/packages/2-sql-family/9-family/README.md +++ b/packages/2-sql-family/9-family/README.md @@ -137,7 +137,7 @@ The runner returns structured errors with the following codes: - **`./control`**: Control plane entry point for CLI/config usage (exports `SqlFamilyDescriptor`) - **`./control-adapter`**: SQL control adapter interface (`SqlControlAdapter`, `SqlControlAdapterDescriptor`) for target-specific adapters -- **`./psl-build`**: PSL building blocks both `contract infer` and `contract print` use, with no dialect knowledge: `mapDefault` (a stored default as the PSL attribute that reads back as it), `toEnumMemberName`, `inferredTypeConstructors` (the constructor marked `inferred` for each data type of a stack), `fewestArgumentsCall` and `pslTypeArgumentText` (the call infer and print write for a column's parameters), and `inferColumnTypes` (each introspected column's type, through the data type that claims its reported type, failing with `CONTRACT.INFER_COLUMN_TYPE_UNCLAIMED` on every column none writes) +- **`./psl-build`**: PSL building blocks both `contract infer` and `contract print` use, with no dialect knowledge: `mapDefault` (a stored default as the PSL attribute that reads back as it), `toEnumMemberName`, `inferredTypeConstructors` (the constructor marked `inferred` for each data type of a stack), `fewestArgumentsCall` and `writtenTypeArguments` (the call infer and print write for a column's parameters), `writtenTypeChoiceCall` (the type choice call, written by name, that becomes a constructor call; ADR 279) and `sameInNormalForm` (the comparison infer uses for it), and `inferColumnTypes` (each introspected column's type, through the data type that claims its reported type, failing with `CONTRACT.INFER_COLUMN_TYPE_UNCLAIMED` on every column none writes) - **`./psl-infer`**: Database-to-PSL inference utilities for `contract infer`: name transforms, relation inference, and the printer-config types - **`./runtime`**: Runtime plane identity exports only (family ID, types, descriptor identity). Does **not** export runtime creation helpers—use `instantiateExecutionStack` from `@internal/framework-components/execution` and `createExecutionContext`, `createRuntime`, `createSqlExecutionStack` from `@internal/sql-runtime`. See [ADR 152](../../../docs/architecture%20docs/adrs/ADR%20152%20-%20Execution%20Plane%20Descriptors%20and%20Instances.md). - **`./verify`**: Marker row parsing helper (`parseContractMarkerRow`). Marker reads are owned by each `SqlControlAdapter` (e.g. `PostgresControlAdapter.readMarker`) so dialect-specific SQL stays target-local. diff --git a/packages/2-sql-family/9-family/src/core/migrations/contract-to-schema-ir.ts b/packages/2-sql-family/9-family/src/core/migrations/contract-to-schema-ir.ts index 57e2cbca66e2..030b8e699aa5 100644 --- a/packages/2-sql-family/9-family/src/core/migrations/contract-to-schema-ir.ts +++ b/packages/2-sql-family/9-family/src/core/migrations/contract-to-schema-ir.ts @@ -158,8 +158,7 @@ function contractDefaultValue( } /** - * The column's type as the database reports it, from the type it is stored as: the written name with its normal-form parameters (`character(1)` for a bare `character`), or for a - * type that claims a kind (an enum), its type name as stored. + * The column's type as the database reports it, from the type it is stored as: the written name with the parameters the catalog prints for those written (`character(1)` for a bare `character`, `timestamptz` for a bare `timestamptz`), or for a type that claims a kind (an enum), its type name as stored. */ function schemaTypeText( type: SqlDataType, @@ -167,7 +166,7 @@ function schemaTypeText( ): string { const params = dataTypeParams(type, typeParams); return type.sql.claimsKind === undefined - ? renderSqlTypeName(type, type.sql.normalize(params)) + ? renderSqlTypeName(type, type.sql.catalogParams(params)) : unquotedSqlBaseName(type, params); } diff --git a/packages/2-sql-family/9-family/src/core/psl-build/column-types.ts b/packages/2-sql-family/9-family/src/core/psl-build/column-types.ts index fe1280671d7b..b8f23d6a9cc0 100644 --- a/packages/2-sql-family/9-family/src/core/psl-build/column-types.ts +++ b/packages/2-sql-family/9-family/src/core/psl-build/column-types.ts @@ -1,4 +1,8 @@ -import type { DataTypeSupport } from '@internal/framework-components/authoring'; +import { + type AuthoringTypeNamespace, + authoringTypeConstructorArguments, + type DataTypeSupport, +} from '@internal/framework-components/authoring'; import type { DataType } from '@internal/framework-components/codec'; import { resolveReportedSqlType } from '@internal/sql-contract/data-type'; import type { ReportedSqlType, SqlColumnIR } from '@internal/sql-schema-ir/types'; @@ -6,15 +10,19 @@ import { InternalError } from '@internal/utils/internal-error'; import { sqlFamilyError } from '../errors'; import { fewestArgumentsCall, + type InferredTypeConstructor, type InferredTypeConstructors, + type PslTypeArgument, + sameInNormalForm, writtenTypeArguments, + writtenTypeChoiceCall, } from './inferred-type-constructors'; -/** A call to a type constructor as PSL writes it: the constructor's path, its arguments' text, and the value each argument gives the constructor. */ +/** A type call as PSL writes it, and the explicit type constructor it becomes, with the value of each of its arguments. */ export interface PslTypeCall { readonly path: readonly string[]; - readonly args: readonly string[]; - readonly values: readonly unknown[]; + readonly args: readonly PslTypeArgument[]; + readonly explicit: { readonly path: readonly string[]; readonly values: readonly unknown[] }; } /** The PSL type `contract infer` writes for a column that is not an enum, by table and column name. */ @@ -37,28 +45,66 @@ function reportedTypeOf(column: SqlColumnIR, tableName: string): ReportedSqlType return column.reportedType; } +/** The call to the type choice that becomes `marked` with every parameter in `params`, compared in the normal form, as `db verify` compares the column, with the explicit call it becomes. */ +function convenienceCall( + marked: InferredTypeConstructor, + params: Readonly>, + authoringTypes: AuthoringTypeNamespace, + support: DataTypeSupport, +): PslTypeCall | undefined { + const values = authoringTypeConstructorArguments(marked.descriptor, { + codecId: marked.descriptor.output.codecId, + typeParams: params, + }); + const written = + values && + writtenTypeChoiceCall({ + authoringTypes, + call: { path: marked.path, args: values }, + dataTypes: support, + sameOutput: sameInNormalForm(marked.dataType), + }); + return ( + written && { + path: written.path, + args: written.args, + explicit: { path: written.becomes.path, values: written.becomes.args }, + } + ); +} + function writtenCall( constructors: InferredTypeConstructors, dataTypes: readonly DataType[], + authoringTypes: AuthoringTypeNamespace, support: DataTypeSupport, reported: ReportedSqlType, ): PslTypeCall | undefined { const resolved = resolveReportedSqlType(reported, dataTypes); const marked = resolved && constructors.get(resolved.dataType.id); - const call = resolved && marked && fewestArgumentsCall(marked, resolved.typeParams); - const args = call && marked && writtenTypeArguments(marked.descriptor, call.args, support); - return call && args && { path: call.path, args, values: call.args }; + if (resolved === undefined || marked === undefined) return undefined; + const convenience = convenienceCall(marked, resolved.typeParams, authoringTypes, support); + if (convenience !== undefined) return convenience; + const call = fewestArgumentsCall(marked, resolved.typeParams); + const args = call && writtenTypeArguments(marked.descriptor, call.args, support); + return ( + call && + args && { + path: call.path, + args: args.map((text) => ({ name: undefined, text })), + explicit: { path: call.path, values: call.args }, + } + ); } /** - * The type of every column of `tables` that `isEnumColumn` does not claim: the call, with the - * fewest arguments, to the constructor marked `inferred` for the data type that claims the - * column's reported type. When any column has no such call, throws - * `CONTRACT.INFER_COLUMN_TYPE_UNCLAIMED` listing every one of them. + * The type of every column of `tables` that `isEnumColumn` does not claim, through the constructor marked `inferred` for the data type that claims the column's reported type: the type choice call that becomes it, with only the arguments that differ from the defaults, or else the call to it with the fewest arguments. When any column has no such call, throws `CONTRACT.INFER_COLUMN_TYPE_UNCLAIMED` listing every one of them. */ export function inferColumnTypes(input: { readonly tables: readonly IntrospectedTable[]; readonly constructors: InferredTypeConstructors; + /** The stack's type namespace, whose type choices infer writes where one becomes a marked constructor. */ + readonly authoringTypes: AuthoringTypeNamespace; /** The stack's data types, whose literals write each argument. */ readonly dataTypes: DataTypeSupport; readonly isEnumColumn: (column: SqlColumnIR) => boolean; @@ -70,7 +116,13 @@ export function inferColumnTypes(input: { for (const column of columns) { if (input.isEnumColumn(column)) continue; const reported = reportedTypeOf(column, tableName); - const call = writtenCall(input.constructors, dataTypes, input.dataTypes, reported); + const call = writtenCall( + input.constructors, + dataTypes, + input.authoringTypes, + input.dataTypes, + reported, + ); if (call === undefined) { unclaimed.push( `${schemaName}.${tableName}.${column.name}: ${reported.text}${column.many === true ? '[]' : ''}`, diff --git a/packages/2-sql-family/9-family/src/core/psl-build/inferred-type-constructors.ts b/packages/2-sql-family/9-family/src/core/psl-build/inferred-type-constructors.ts index 64cecb4cb863..291fde884125 100644 --- a/packages/2-sql-family/9-family/src/core/psl-build/inferred-type-constructors.ts +++ b/packages/2-sql-family/9-family/src/core/psl-build/inferred-type-constructors.ts @@ -2,11 +2,15 @@ import type { JsonValue } from '@internal/contract/types'; import { type AuthoringTypeConstructorCall, type AuthoringTypeConstructorDescriptor, + type AuthoringTypeConstructorOutput, type AuthoringTypeNamespace, authoringTypeConstructorArguments, authoringTypeConstructors, type DataTypeSupport, + findAuthoringTypeChoiceCall, + getAuthoringTypeChoice, instantiateAuthoringTypeConstructor, + isAuthoringTypeChoiceOption, } from '@internal/framework-components/authoring'; import type { CodecLookupWithDescriptors, @@ -110,3 +114,72 @@ export function writtenTypeArguments( } return written; } + +/** An argument of a type call as PSL writes it: by name, or by position when `name` is undefined. */ +export interface PslTypeArgument { + readonly name: string | undefined; + readonly text: string; +} + +/** A call to a type constructor or type choice as PSL writes it. */ +export interface WrittenPslTypeCall { + readonly path: readonly string[]; + readonly args: readonly PslTypeArgument[]; +} + +/** A type choice call as PSL writes it, and the type constructor call it becomes. */ +export interface WrittenTypeChoiceCall extends WrittenPslTypeCall { + readonly becomes: AuthoringTypeConstructorCall; +} + +/** + * Whether two outputs have the same codec and the same parameters in the normal form of `dataType`, which the codecs represent: how `db verify` and the planner compare a column with the database. + */ +export function sameInNormalForm( + dataType: SqlDataType, +): (produced: AuthoringTypeConstructorOutput, wanted: AuthoringTypeConstructorOutput) => boolean { + const normal = (output: AuthoringTypeConstructorOutput) => + canonicalizeJson(dataType.sql.normalize(dataTypeParams(dataType, output.typeParams ?? {}))); + return (produced, wanted) => + produced.codecId === wanted.codecId && normal(produced) === normal(wanted); +} + +/** + * The type choice call that becomes the type constructor call `call`, written by name with only the arguments that differ from the defaults (ADR 279). `sameOutput` decides when it becomes the same type; by default the codec and type parameters must be exactly equal. `undefined` when no type choice call becomes it or an argument has no literal that reads back as it. + */ +export function writtenTypeChoiceCall(input: { + readonly authoringTypes: AuthoringTypeNamespace; + readonly call: AuthoringTypeConstructorCall; + readonly dataTypes: DataTypeSupport; + readonly sameOutput?: ( + produced: AuthoringTypeConstructorOutput, + wanted: AuthoringTypeConstructorOutput, + ) => boolean; +}): WrittenTypeChoiceCall | undefined { + const found = findAuthoringTypeChoiceCall({ + namespace: input.authoringTypes, + call: input.call, + ...(input.sameOutput === undefined ? {} : { sameOutput: input.sameOutput }), + }); + const choice = found && getAuthoringTypeChoice({ type: input.authoringTypes }, found.path); + if (found === undefined || choice === undefined) return undefined; + const args: PslTypeArgument[] = []; + for (const { name, value } of found.args) { + const argument = choice.descriptor.args.find((candidate) => candidate.name === name); + const text = + argument === undefined + ? undefined + : isAuthoringTypeChoiceOption(argument) + ? String(value) + : writeDataTypeValue( + blindCast( + value, + ), + dataTypeId(argument.dataType), + input.dataTypes, + ); + if (text === undefined) return undefined; + args.push({ name, text }); + } + return { path: found.path, args, becomes: found.becomes }; +} diff --git a/packages/2-sql-family/9-family/src/core/timestamp-now-runtime-generator.ts b/packages/2-sql-family/9-family/src/core/timestamp-now-runtime-generator.ts index c809a11e9ef6..fdc220f115e3 100644 --- a/packages/2-sql-family/9-family/src/core/timestamp-now-runtime-generator.ts +++ b/packages/2-sql-family/9-family/src/core/timestamp-now-runtime-generator.ts @@ -1,10 +1,20 @@ import { TIMESTAMP_NOW_GENERATOR_ID } from '@internal/framework-components/authoring'; import type { RuntimeMutationDefaultGenerator } from '@internal/framework-components/runtime'; +const DATE_FRACTION_DIGITS = 3; + +function roundedDown(now: number, params: Readonly> | undefined): Date { + const precision = params?.['precision']; + if (typeof precision !== 'number' || precision >= DATE_FRACTION_DIGITS) return new Date(now); + const step = 10 ** (DATE_FRACTION_DIGITS - Math.max(precision, 0)); + return new Date(Math.floor(now / step) * step); +} + /** * Builds the canonical runtime-plane generator for the wall-clock-now - * mutation default. Returns `new Date()`; semantics are target-agnostic - * so all SQL targets share this single implementation. + * mutation default. Returns a `Date`, rounded down to the `precision` param + * when a target passes one; semantics are target-agnostic so all SQL targets + * share this single implementation. * * Declares `stability: 'query'` so a single ORM bulk operation * (e.g. `createAll([...])`) shares one timestamp across every row and @@ -18,7 +28,7 @@ import type { RuntimeMutationDefaultGenerator } from '@internal/framework-compon export function timestampNowRuntimeGenerator(): RuntimeMutationDefaultGenerator { return { id: TIMESTAMP_NOW_GENERATOR_ID, - generate: () => new Date(), + generate: (params) => roundedDown(Date.now(), params), stability: 'query', }; } diff --git a/packages/2-sql-family/9-family/src/exports/psl-build.ts b/packages/2-sql-family/9-family/src/exports/psl-build.ts index bdcc99468bc1..c426b195edd1 100644 --- a/packages/2-sql-family/9-family/src/exports/psl-build.ts +++ b/packages/2-sql-family/9-family/src/exports/psl-build.ts @@ -30,6 +30,11 @@ export { type InferredTypeConstructor, type InferredTypeConstructors, inferredTypeConstructors, + type PslTypeArgument, + sameInNormalForm, + type WrittenPslTypeCall, + type WrittenTypeChoiceCall, writtenTypeArguments, + writtenTypeChoiceCall, } from '../core/psl-build/inferred-type-constructors'; export { toEnumMemberName } from '../core/psl-build/psl-names'; diff --git a/packages/2-sql-family/9-family/test/contract-to-schema-ir.test.ts b/packages/2-sql-family/9-family/test/contract-to-schema-ir.test.ts index 2dbee368b369..5b58151d262f 100644 --- a/packages/2-sql-family/9-family/test/contract-to-schema-ir.test.ts +++ b/packages/2-sql-family/9-family/test/contract-to-schema-ir.test.ts @@ -85,6 +85,16 @@ const fixedCharacterType = sqlDataType('test/fixed-character', { ], normalize: (params) => (params.length === undefined ? { ...params, length: 1 } : params), }); +const preciseTimestamptzType = sqlDataType('test/precise-timestamptz', { + read: (json) => json, + params: type({ 'precision?': 'number.integer >= 0 & number.integer <= 6' }), + texts: [ + { text: 'timestamptz', written: true }, + { text: 'timestamptz({precision})', written: true }, + ], + normalize: (params) => (params.precision === undefined ? { ...params, precision: 6 } : params), + catalogParams: (params) => params, +}); const codeType = sqlDataType('test/code', { read: (json) => json, params: type({ 'length?': 'number.integer >= 1' }), @@ -113,6 +123,7 @@ const dataTypeOfCodec: Readonly> = { 'pg/timestamptz@1': timestamptzType, 'pg/numeric@1': numericType, 'pg/char@1': fixedCharacterType, + 'test/precise-timestamptz@1': preciseTimestamptzType, 'pg/enum@1': enumType, 'test/code@1': codeType, 'sql/raw@1': sqlRawDataType, @@ -149,6 +160,7 @@ function dataTypeOf(codecId: string): string { } const testDataTypes = createDataTypeLookup([ + preciseTimestamptzType, textType, characterType, vectorType, @@ -396,6 +408,23 @@ describe('contractToSchemaIR', () => { }); }); + it('writes a column type with the parameters the catalog prints for those written, where the type declares them', () => { + const storage = unboundStorage('test' as StorageHashBase, { + T: table({ + columns: { + bare: col({ codecId: 'test/precise-timestamptz@1' }), + micro: col({ codecId: 'test/precise-timestamptz@1', typeParams: { precision: 6 } }), + }, + }), + }); + + const columns = contractToSchemaIR(wrap(storage)).tables['T']!.columns; + expect({ bare: columns['bare']!.typeText, micro: columns['micro']!.typeText }).toEqual({ + bare: 'timestamptz', + micro: 'timestamptz(6)', + }); + }); + it('gives each column its data type, the type it is stored as, and that type parameters in normal form', () => { const storage = unboundStorage('test' as StorageHashBase, { T: table({ diff --git a/packages/2-sql-family/9-family/test/psl-build/inferred-type-constructors.test.ts b/packages/2-sql-family/9-family/test/psl-build/inferred-type-constructors.test.ts index 33afcadd3e48..6470ff690535 100644 --- a/packages/2-sql-family/9-family/test/psl-build/inferred-type-constructors.test.ts +++ b/packages/2-sql-family/9-family/test/psl-build/inferred-type-constructors.test.ts @@ -14,6 +14,7 @@ import { fewestArgumentsCall, inferredTypeConstructors, writtenTypeArguments, + writtenTypeChoiceCall, } from '../../src/core/psl-build/inferred-type-constructors'; const numeric = sqlDataType('t/numeric', { @@ -41,6 +42,26 @@ const int = sqlDataType('t/int', { texts: [{ text: 'int', written: true, catalog: true }], }); +const stamp = sqlDataType('t/stamp', { + read: (json) => json, + params: type({ 'precision?': 'number.integer >= 0 & number.integer <= 6' }), + texts: [ + { text: 'stamp', written: true, catalog: true }, + { text: 'stamp({precision})', written: true, catalog: true }, + ], + normalize: (params) => (params.precision === undefined ? { ...params, precision: 6 } : params), +}); + +const clock = sqlDataType('t/clock', { + read: (json) => json, + params: type({ 'precision?': 'number.integer >= 0 & number.integer <= 6' }), + texts: [ + { text: 'clock', written: true, catalog: true }, + { text: 'clock({precision})', written: true, catalog: true }, + ], + normalize: (params) => (params.precision === undefined ? { ...params, precision: 6 } : params), +}); + const labelled = sqlDataType('t/labelled', { read: (json) => json, params: type({ label: 'string' }), @@ -66,6 +87,42 @@ const authoringTypes = { }, }, }, + StampDate: { + kind: 'typeConstructor', + inferred: true, + args: [{ kind: 'value', dataType: 't/int', name: 'precision', optional: true }], + output: { codecId: 't/stamp-date@1', typeParams: { precision: { kind: 'arg', index: 0 } } }, + }, + StampText: { + kind: 'typeConstructor', + args: [{ kind: 'value', dataType: 't/int', name: 'precision', optional: true }], + output: { codecId: 't/stamp-text@1', typeParams: { precision: { kind: 'arg', index: 0 } } }, + }, + Stamp: { + kind: 'typeChoice', + args: [ + { kind: 'option', name: 'value', values: ['date', 'text'], default: 'date', optional: true }, + { kind: 'value', dataType: 't/int', name: 'precision', optional: true }, + ], + choices: [ + { when: { value: 'date' }, type: 'StampDate', defaults: { precision: 3 } }, + { when: { value: 'text' }, type: 'StampText' }, + ], + }, + ClockText: { + kind: 'typeConstructor', + inferred: true, + args: [{ kind: 'value', dataType: 't/int', name: 'precision', optional: true }], + output: { codecId: 't/clock-text@1', typeParams: { precision: { kind: 'arg', index: 0 } } }, + }, + Clock: { + kind: 'typeChoice', + args: [ + { kind: 'option', name: 'value', values: ['text'], default: 'text', optional: true }, + { kind: 'value', dataType: 't/int', name: 'precision', optional: true }, + ], + choices: [{ when: { value: 'text' }, type: 'ClockText' }], + }, ext: { Labelled: { kind: 'typeConstructor', @@ -80,6 +137,9 @@ const dataTypeOfCodec: Record = { 't/text@1': 't/text', 't/numeric@1': 't/numeric', 't/labelled@1': 't/labelled', + 't/stamp-date@1': 't/stamp', + 't/stamp-text@1': 't/stamp', + 't/clock-text@1': 't/clock', }; const constructors = inferredTypeConstructors({ @@ -90,7 +150,7 @@ const constructors = inferredTypeConstructors({ ? undefined : ({ dataType: dataTypeOfCodec[codecId] } as unknown as AnyCodecDescriptor), }, - dataTypeLookup: createDataTypeLookup([numeric, text, labelled]), + dataTypeLookup: createDataTypeLookup([numeric, text, labelled, stamp, clock]), }); const dataTypes: DataTypeSupport = { @@ -112,7 +172,7 @@ const dataTypes: DataTypeSupport = { documentation: 'Text.', }, }, - lookup: createDataTypeLookup([int, numeric, text, labelled]), + lookup: createDataTypeLookup([int, numeric, text, labelled, stamp, clock]), dataTypeOfCodec: () => undefined, }; @@ -123,6 +183,8 @@ describe('inferredTypeConstructors', () => { ).toEqual({ 't/text': ['Text'], 't/numeric': ['Numeric'], + 't/stamp': ['StampDate'], + 't/clock': ['ClockText'], 't/labelled': ['ext', 'Labelled'], }); }); @@ -162,6 +224,38 @@ const catalog = (text: string): ReportedSqlType => ({ name: undefined, }); +describe('writtenTypeChoiceCall', () => { + const write = (path: readonly string[], args: readonly unknown[]) => + writtenTypeChoiceCall({ authoringTypes, call: { path, args }, dataTypes }); + + it('writes the type choice call that becomes the constructor call, each argument by name', () => { + expect([ + write(['StampDate'], [3]), + write(['StampText'], [2]), + write(['StampText'], []), + ]).toEqual([ + { path: ['Stamp'], args: [], becomes: { path: ['StampDate'], args: [3] } }, + { + path: ['Stamp'], + args: [ + { name: 'value', text: 'text' }, + { name: 'precision', text: '2' }, + ], + becomes: { path: ['StampText'], args: [2] }, + }, + { + path: ['Stamp'], + args: [{ name: 'value', text: 'text' }], + becomes: { path: ['StampText'], args: [] }, + }, + ]); + }); + + it('writes nothing for a call no type choice reproduces exactly, or one no type choice names', () => { + expect([write(['StampDate'], []), write(['Numeric'], [10])]).toEqual([undefined, undefined]); + }); +}); + describe('writtenTypeArguments', () => { const numericConstructor = authoringTypes.Numeric; @@ -182,7 +276,13 @@ describe('writtenTypeArguments', () => { describe('inferColumnTypes', () => { const infer = (tables: readonly IntrospectedTable[]) => - inferColumnTypes({ tables, constructors, dataTypes, isEnumColumn: () => false }); + inferColumnTypes({ + tables, + constructors, + authoringTypes, + dataTypes, + isEnumColumn: () => false, + }); it("writes each column as its data type's marked constructor, quoting a string argument", () => { const typeOf = infer([ @@ -201,8 +301,45 @@ describe('inferColumnTypes', () => { }, ]); expect({ amount: typeOf('thing', 'amount'), label: typeOf('thing', 'label') }).toEqual({ - amount: { path: ['Numeric'], args: ['10'], values: [10] }, - label: { path: ['ext', 'Labelled'], args: ['"gold"'], values: ['gold'] }, + amount: { + path: ['Numeric'], + args: [{ name: undefined, text: '10' }], + explicit: { path: ['Numeric'], values: [10] }, + }, + label: { + path: ['ext', 'Labelled'], + args: [{ name: undefined, text: '"gold"' }], + explicit: { path: ['ext', 'Labelled'], values: ['gold'] }, + }, + }); + }); + + it('writes the type choice that becomes the marked constructor, with the arguments that differ from its defaults', () => { + const typeOf = infer([ + { + schemaName: 'public', + tableName: 'event', + columns: [column('at', catalog('stamp')), column('paid', catalog('stamp(3)'))], + }, + ]); + expect({ at: typeOf('event', 'at'), paid: typeOf('event', 'paid') }).toEqual({ + at: { + path: ['Stamp'], + args: [{ name: 'precision', text: '6' }], + explicit: { path: ['StampDate'], values: [6] }, + }, + paid: { path: ['Stamp'], args: [], explicit: { path: ['StampDate'], values: [3] } }, + }); + }); + + it('records the explicit call the written type choice call becomes, not the column parameters in normal form', () => { + const typeOf = infer([ + { schemaName: 'public', tableName: 'shop', columns: [column('opens', catalog('clock'))] }, + ]); + expect(typeOf('shop', 'opens')).toEqual({ + path: ['Clock'], + args: [], + explicit: { path: ['ClockText'], values: [] }, }); }); diff --git a/packages/2-sql-family/9-family/test/timestamp-now-runtime-generator.test.ts b/packages/2-sql-family/9-family/test/timestamp-now-runtime-generator.test.ts new file mode 100644 index 000000000000..9d8de107d88b --- /dev/null +++ b/packages/2-sql-family/9-family/test/timestamp-now-runtime-generator.test.ts @@ -0,0 +1,29 @@ +import { afterEach, describe, expect, it, vi } from 'vitest'; +import { timestampNowRuntimeGenerator } from '../src/core/timestamp-now-runtime-generator'; + +describe('timestampNowRuntimeGenerator', () => { + afterEach(() => { + vi.useRealTimers(); + }); + + it('generates the current time once per ORM operation', () => { + vi.useFakeTimers({ now: new Date('2024-01-02T23:59:59.999Z') }); + const generator = timestampNowRuntimeGenerator(); + expect(generator).toMatchObject({ id: 'timestampNow', stability: 'query' }); + expect(generator.generate()).toEqual(new Date('2024-01-02T23:59:59.999Z')); + }); + + it.each([ + [0, '2024-01-02T23:59:59.000Z'], + [1, '2024-01-02T23:59:59.900Z'], + [2, '2024-01-02T23:59:59.990Z'], + [3, '2024-01-02T23:59:59.999Z'], + [6, '2024-01-02T23:59:59.999Z'], + ])( + 'rounds down to precision %s, so the column stores the value the client holds', + (precision, iso) => { + vi.useFakeTimers({ now: new Date('2024-01-02T23:59:59.999Z') }); + expect(timestampNowRuntimeGenerator().generate({ precision })).toEqual(new Date(iso)); + }, + ); +}); diff --git a/packages/3-targets/mysql/1-target/src/core/codecs.ts b/packages/3-targets/mysql/1-target/src/core/codecs.ts index e8aca38e2d15..800cb47dfec7 100644 --- a/packages/3-targets/mysql/1-target/src/core/codecs.ts +++ b/packages/3-targets/mysql/1-target/src/core/codecs.ts @@ -9,6 +9,7 @@ import { type CodecInstanceContext, type DataTypeValue, decimalTextBigintLiteral, + refuseJsonValue, renderTsLiteral, } from '@internal/framework-components/codec'; import { sqlRawDataType } from '@internal/sql-contract/data-type'; @@ -227,6 +228,13 @@ export class MysqlDatetimeCodec extends CodecImpl< return decodeMysqlDatetime(wire); } fromDataTypeValue(value: DataTypeValue): Date { + if (/[1-9]/.test(/\.(\d+)/.exec(value.value)?.[1]?.slice(3) ?? '')) { + return refuseJsonValue( + this.id, + `a datetime with no digits below a millisecond, which a Date drops: ${JSON.stringify(value.value)} has them`, + value.value, + ); + } return new Date(`${value.value}Z`); } toDataTypeValue(input: Date): DataTypeValue { diff --git a/packages/3-targets/mysql/1-target/test/codecs.test.ts b/packages/3-targets/mysql/1-target/test/codecs.test.ts index 40fc24407353..8ff498389622 100644 --- a/packages/3-targets/mysql/1-target/test/codecs.test.ts +++ b/packages/3-targets/mysql/1-target/test/codecs.test.ts @@ -50,6 +50,18 @@ describe('MySQL codecs read the text MySQL sends', () => { expect(codec.fromDataTypeValue(value(mysqlBigint, '12'))).toBe(12n); }); + it('refuses a stored datetime with digits below a millisecond, and reads a row with them at the millisecond', async () => { + const codec = mysqlDatetimeDescriptor.factory({ precision: 6 })(instance); + expect(() => + codec.fromDataTypeValue( + mysqlDatetime.valueFrom('2024-01-02T03:04:05.678912', { precision: 6 }), + ), + ).toThrow(/2024-01-02T03:04:05\.678912/); + expect(await codec.fromWire('2024-01-02 03:04:05.678912', ctx)).toEqual( + new Date('2024-01-02T03:04:05.678Z'), + ); + }); + it('reads a datetime as the instant it names in UTC', async () => { const codec = mysqlDatetimeDescriptor.factory({ precision: 3 })(instance); const instant = new Date('2024-01-02T03:04:05.678Z'); diff --git a/packages/3-targets/postgres/1-target/README.md b/packages/3-targets/postgres/1-target/README.md index 3ce5c6d901bc..7f19839e6b3d 100644 --- a/packages/3-targets/postgres/1-target/README.md +++ b/packages/3-targets/postgres/1-target/README.md @@ -26,8 +26,8 @@ Provides the Postgres target descriptor (`SqlControlTargetDescriptor`) for CLI c - **Generated Defaults Policy**: Treats client-generated defaults as non-DB defaults when emitting DDL - **Database Dependency Consumption**: The planner extracts database dependencies from the configured framework components (passed as `frameworkComponents`), verifies each dependency against the live schema, and only emits install operations when required. The runner reuses the same metadata for post-apply verification, so there are no hardcoded extension mappings—database dependencies stay component-owned. - **Storage Type Planning**: The planner dispatches storage type hooks for `storage.types` and emits type operations before table creation when supported by the policy -- **Contract-to-PSL Printing**: Implements the `buildPslContract` descriptor hook, which `prisma contract print` uses. It takes a Postgres contract the contract serializer accepted, from any source, and returns the Prisma 8 PSL document that reads back as the same contract. It does not check the contract's structure again; a broken structure is an internal error. It writes models, value objects, named types, domain and native enums, keys with their names, indexes, checks, relations, polymorphism, control policies, row-level security (`@@rls`, policies, and roles in `namespace unbound`) and defaults. A column is written as a type constructor the configured stack contributes that reads back with its codec, native type and type parameters: the constructor marked `inferred` for the column's data type, which is the one `contract infer` writes, when it does, or else the first constructor in the stack that does, such as `TimestamptzTemporal` or `TimestamptzTemporal(3)` for a `Temporal`-backed `timestamptz` column, or an extension's `pgvector.Vector(3)`. A convenience type such as `DateTime` is a type choice, not a constructor, so it is never written. A value-object member has no column, so its type goes through the stack the same way: the stack's codec names the native type for the member's type parameters, and the member is written as the type constructor for that native type, called with those parameters (`Numeric(10, 2)`); a member typed by a domain enum is written by the enum's name. The contract does not record which named type a member used, so a member written `code Short`, with `types { Short = VarChar(10) }`, is printed as the type it stands for, `code VarChar(10)`; a model field keeps `Short`, because its column names the named type. A literal default is written by the data type of the column's codec. The family passes the stack's type constructors, codecs and data types to the hook for this. Anything it cannot write so that it reads back the same is refused by name with `CONTRACT.PRINT_UNSUPPORTED` (the full list is in `docs/reference/error-reference.md`, one function per case in `src/core/psl-print/refusals.ts`); nothing is dropped. It lives in `src/core/psl-print/`, the counterpart of the `inferPslContract` hook in `src/core/psl-infer/`, which writes PSL from a live database. Both build with the shared builders in `src/core/psl-build/`: literals, index and check attributes, and the native enum block. Both find the constructor marked `inferred` for a data type through the SQL family's `@internal/family-sql/psl-build`. Model and field names come from the contract; `@@map` and `@map` appear only where the name the PSL source would derive differs. A unique constraint prints as `@@unique` and a unique index as `@@index(..., unique: true)`; every index prints explicitly, and a relation writes `index: false` when nothing backs its foreign key, no `index` argument when a relation that says nothing would be backed by the same object (`defaultForeignKeyIndex`), and otherwise `index: ""` naming the index, or the key whose first columns are the foreign key's. Execution generators join back onto their columns, through whatever the configured stack contributes, extensions included. A column that is not a list and whose PSL type stands for its data type prints as the call of the field preset that writes its codec with its generators, built from the preset's print form, when the preset accepts that call and it reads back with the column's codec, type params and generators. A column typed by a named type, a value object or an enum, and a column whose call fails those checks, such as one without a value for a required argument, does not. A column with only a create generator then prints it as `@default((...))`, the call of the default function that declares `printArguments` for it; any other column is refused. -- **Database-to-PSL inference**: Implements the `inferPslContract` descriptor hook, which `prisma contract infer` uses. Introspection records each column's element type as the database reports it (`format_type`, its kind, schema and name). Infer resolves that reported type through the data types of the configured stack, the target's and the extension packs', and writes the constructor marked `inferred` for the data type, with the fewest arguments that give the same parameters (`numeric(10,0)` is written `Numeric(10)`); a list column adds `[]`, and a native enum column is written `pg.enum()`. When a column's type is claimed by no data type, or by one with no marked constructor (such as `tsquery`), infer writes nothing and fails with `CONTRACT.INFER_COLUMN_TYPE_UNCLAIMED`, listing every such column as `.
.: `. No configured pack has a type for such a column. When the type comes from a database extension that has a Prisma 8 pack, such as `pgvector` or `postgis`, adding the pack to `extensions` in `prisma.config.ts` lets infer write it, for the forms of the type the pack supports. Otherwise, as for domains, `"char"`, `hstore` and `tsquery`, the column's type must change, or the column must go. The rule itself (resolving a reported type through the data types, the fewest-arguments call, the unclaimed error) lives in the SQL family's `@internal/family-sql/psl-build`; the target adds the walk over its schema tree and the native enum handling in `src/core/psl-infer/`. +- **Contract-to-PSL Printing**: Implements the `buildPslContract` descriptor hook, which `prisma contract print` uses. It takes a Postgres contract the contract serializer accepted, from any source, and returns the Prisma 8 PSL document that reads back as the same contract. It does not check the contract's structure again; a broken structure is an internal error. It writes models, value objects, named types, domain and native enums, keys with their names, indexes, checks, relations, polymorphism, control policies, row-level security (`@@rls`, policies, and roles in `namespace unbound`) and defaults. A column is written as a type constructor the configured stack contributes that reads back with its codec, native type and type parameters: the constructor marked `inferred` for the column's data type, which is the one `contract infer` writes, when it does, or else the first constructor in the stack that does, such as `TimestampTemporal(3)` or an extension's `pgvector.Vector(3)`. Where a convenience type call becomes that constructor call exactly, the convenience call is written instead, by name with only the arguments that differ from its defaults: `DateTime` for `TimestamptzJsDate(3)`, `DateTime(as: temporal, precision: 3)` for `TimestamptzTemporal(3)` (ADR 279). A value-object member has no column, so its type goes through the stack the same way: the stack's codec names the native type for the member's type parameters, and the member is written as the type constructor for that native type, called with those parameters (`Numeric(10, 2)`); a member typed by a domain enum is written by the enum's name. The contract does not record which named type a member used, so a member written `code Short`, with `types { Short = VarChar(10) }`, is printed as the type it stands for, `code VarChar(10)`; a model field keeps `Short`, because its column names the named type. A literal default is written by the data type of the column's codec. The family passes the stack's type constructors, codecs and data types to the hook for this. Anything it cannot write so that it reads back the same is refused by name with `CONTRACT.PRINT_UNSUPPORTED` (the full list is in `docs/reference/error-reference.md`, one function per case in `src/core/psl-print/refusals.ts`); nothing is dropped. It lives in `src/core/psl-print/`, the counterpart of the `inferPslContract` hook in `src/core/psl-infer/`, which writes PSL from a live database. Both build with the shared builders in `src/core/psl-build/`: literals, index and check attributes, and the native enum block. Both find the constructor marked `inferred` for a data type through the SQL family's `@internal/family-sql/psl-build`. Model and field names come from the contract; `@@map` and `@map` appear only where the name the PSL source would derive differs. A unique constraint prints as `@@unique` and a unique index as `@@index(..., unique: true)`; every index prints explicitly, and a relation writes `index: false` when nothing backs its foreign key, no `index` argument when a relation that says nothing would be backed by the same object (`defaultForeignKeyIndex`), and otherwise `index: ""` naming the index, or the key whose first columns are the foreign key's. Execution generators join back onto their columns, through whatever the configured stack contributes, extensions included. A column that is not a list and whose PSL type stands for its data type prints as the call of the field preset that writes its codec with its generators, built from the preset's print form, when the preset accepts that call and it reads back with the column's codec, type params and generators. A column typed by a named type, a value object or an enum, and a column whose call fails those checks, such as one without a value for a required argument, does not. A column with only a create generator then prints it as `@default((...))`, the call of the default function that declares `printArguments` for it; any other column is refused. +- **Database-to-PSL inference**: Implements the `inferPslContract` descriptor hook, which `prisma contract infer` uses. Introspection records each column's element type as the database reports it (`format_type`, its kind, schema and name). Infer resolves that reported type through the data types of the configured stack, the target's and the extension packs', and writes the constructor marked `inferred` for the data type, with the fewest arguments that give the same parameters (`numeric(10,0)` is written `Numeric(10)`). For a date or time column the marked constructor is the one the convenience type's default gives, and infer writes the convenience call that becomes it, with the arguments that differ from the defaults and the column's parameters compared in normal form: `timestamptz(3)` is `DateTime`, a `timestamptz` with no declared precision `DateTime(precision: 6)`, `time(0)` `Time(precision: 0)`, `interval` `Duration`. A timestamp default a `Date` cannot hold, such as one with microseconds or `infinity`, is written as its raw `sql` expression; a list column adds `[]`, and a native enum column is written `pg.enum()`. When a column's type is claimed by no data type, or by one with no marked constructor (such as `tsquery`), infer writes nothing and fails with `CONTRACT.INFER_COLUMN_TYPE_UNCLAIMED`, listing every such column as `.
.: `. No configured pack has a type for such a column. When the type comes from a database extension that has a Prisma 8 pack, such as `pgvector` or `postgis`, adding the pack to `extensions` in `prisma.config.ts` lets infer write it, for the forms of the type the pack supports. Otherwise, as for domains, `"char"`, `hstore` and `tsquery`, the column's type must change, or the column must go. The rule itself (resolving a reported type through the data types, the fewest-arguments call, the unclaimed error) lives in the SQL family's `@internal/family-sql/psl-build`; the target adds the walk over its schema tree and the native enum handling in `src/core/psl-infer/`. - **`Temporal` for the target's own code**: The `Temporal`-backed codecs (`pg/date-temporal@1`, `pg/time-temporal@1`, `pg/timestamp-temporal@1`, `pg/timestamp-instant@1` and `pg/timestamptz-temporal@1`), when they decode, and the `instantNow`, `plainDateTimeNow`, `plainDateNow` and `plainTimeNow` generators get `Temporal` from `requireTemporal()` (`src/core/require-temporal.ts`): the runtime's global `Temporal` when it has one, else the fallback `Temporal`, else it throws `RUNTIME.TEMPORAL_UNAVAILABLE`. The text codecs (`pg/date-string@1`, `pg/time-string@1`, `pg/timestamp-string@1`, `pg/timestamptz-string@1` and `pg/timetz@1`), `pg/timestamptz-date@1`, `pg/timestamp-date@1`, `pg/interval@1`, `pg/interval-string@1`, the `timestampNow` generator and the string generators (`timestampStringNow`, `timestamptzStringNow`, `dateStringNow`, `timeStringNow` and `timetzNow`) do not use `Temporal`, so an application that uses only them reads and writes its date and time columns without one. The target's control entry (`@internal/target-postgres/control`) sets the fallback `Temporal`, from `temporal-polyfill`, when it is loaded. Nothing sets `globalThis.Temporal`. No other entry of this package imports `temporal-polyfill`. `temporal-polyfill` is a required peer dependency of this package and of the published `@prisma/orm-target-postgres` and `@prisma/orm-postgres`, so a project that already installs it for its own code has one copy. npm, pnpm and bun install it automatically; a project that uses Yarn adds `temporal-polyfill` to its own dependencies. In the published package, `@prisma/orm-target-postgres/target` is the aggregate of every entry of this package, the control entry included, so it loads the control entry too; an application imports the entry it needs, for example `@prisma/orm-target-postgres/target/runtime`. The fallback is held once per process, and every `Temporal`-backed codec in the process uses it. An application process that loads no control-plane code and has no global `Temporal` gets `RUNTIME.TEMPORAL_UNAVAILABLE` when one of those codecs decodes or one of those generators runs. An application that loads control-plane code in its own process, such as server code under `vite dev` with the Prisma Vite plugin or a script that calls the control client, decodes dates without its own `Temporal`, and then fails in production. An application that uses the Temporal codecs must load its own `Temporal`. Every signature that names a `Temporal` type takes it from `temporal-spec`, a package of type declarations with no runtime code (`import type { Temporal } from 'temporal-spec'`), never from a global declaration, so a Temporal field is typed precisely in a project that has no global `Temporal` declarations. `tsconfig.no-temporal-global.json` type-checks `src` without them. - **Runtime List Framing**: Parses inbound Postgres array text for contract-declared list columns before applying the scalar element codec. Builtin arrays and enum arrays therefore share one target-owned decode path; fixed-scale `numeric(30,10)[]` reads database-normalized text such as `"1.5000000000"`, matching scalar numeric decoding rather than the previous driver numeric-array float spelling `"1.5"`. @@ -253,7 +253,7 @@ See `@internal/family-sql/control` README for full error code documentation. ## Prisma 7 binding -`./prisma7-binding` exports `prisma7PostgresBinding`, everything the Postgres target supplies to the Prisma 7 interpreter in `@internal/sql-contract-prisma7`: the accepted `provider` names, the index types, the 63-byte identifier limit, the `@updatedAt` generator for each codec, how `Json`, `Bytes` and `DateTime` literal defaults are read (the SQL text Postgres stores for `Bytes` and `DateTime`, with `ARRAY[...]::type[]` for lists), and the type map. The type map is the table of what Prisma 7.10.0 creates in Postgres for each Prisma 7 scalar and `@db.*` native type, expressed as the Prisma 8 type constructor that produces the same column (`DateTime` is `TimestampString(3)`, `Decimal` is `Numeric(65, 30)`, `Json` is `Jsonb`, `@db.VarChar(n)` passes its argument through); the recorded SQL Prisma 7 generated for the reference schema is what the rows were read from. A `@db.*` spelling missing from the table is a hard error for the source, never a guess. The Postgres facade and the interpreter's tests import this one instance. +`./prisma7-binding` exports `prisma7PostgresBinding`, everything the Postgres target supplies to the Prisma 7 interpreter in `@internal/sql-contract-prisma7`: the accepted `provider` names, the index types, the 63-byte identifier limit, the `@updatedAt` generator for each codec, how `Json`, `Bytes` and `DateTime` literal defaults are read (the SQL text Postgres stores for `Bytes` and `DateTime`, with `ARRAY[...]::type[]` for lists), the SQL of a default whose value the column's codec cannot hold exactly (a quoted literal cast to the column type, or `ARRAY[...]::type[]`), and the type map. The type map is the table of what Prisma 7.10.0 creates in Postgres for each Prisma 7 scalar and `@db.*` native type, expressed as the Prisma 8 type constructor that produces the same column (`DateTime` is `TimestampJsDate(3)`, a `timestamp(3)` column read as a JavaScript `Date`, `Decimal` is `Numeric(65, 30)`, `Json` is `Jsonb`, `@db.VarChar(n)` passes its argument through); the recorded SQL Prisma 7 generated for the reference schema is what the rows were read from. A `@db.*` spelling missing from the table is a hard error for the source, never a guess. The Postgres facade and the interpreter's tests import this one instance. ## Introspection session settings diff --git a/packages/3-targets/postgres/1-target/src/core/authoring.ts b/packages/3-targets/postgres/1-target/src/core/authoring.ts index 28bbf0202723..36049816d8b3 100644 --- a/packages/3-targets/postgres/1-target/src/core/authoring.ts +++ b/packages/3-targets/postgres/1-target/src/core/authoring.ts @@ -170,27 +170,15 @@ export const postgresAuthoringTypes = { temporal: postgresTemporalTypeAliases(), } as const satisfies AuthoringTypeNamespace; +/** + * The created-at and updated-at aliases, on the type bare `DateTime` becomes: a `timestamptz(3)` column read as a JavaScript `Date` (ADR 279). + */ /* @__NO_SIDE_EFFECTS__ */ function postgresTemporalTypeAliases() { - const temporal = temporalTypeAliases( - { codecId: PG_TIMESTAMPTZ_TEMPORAL_CODEC_ID }, - 'a Temporal.Instant', - ); - const string = temporalTypeAliases( - { codecId: PG_TIMESTAMPTZ_STRING_CODEC_ID }, - 'a string, the timestamp as Postgres writes it, such as 2024-01-02 03:04:05+00', - ); - const jsDate = temporalTypeAliases( - { codecId: PG_TIMESTAMPTZ_DATE_CODEC_ID }, + return temporalTypeAliases( + { codecId: PG_TIMESTAMPTZ_DATE_CODEC_ID, typeParams: { precision: 3 } }, 'a JavaScript Date', ); - return { - ...temporal, - CreatedAtString: string.CreatedAt, - UpdatedAtString: string.UpdatedAt, - CreatedAtJsDate: jsDate.CreatedAt, - UpdatedAtJsDate: jsDate.UpdatedAt, - } as const; } const policyTargetParam = { @@ -930,6 +918,7 @@ export const postgresAuthoringFieldPresets = { sizeType: PG_SIZE_TYPE, codecId: PG_TIMESTAMPTZ_DATE_CODEC_ID, generatorId: postgresNowGeneratorIds[PG_TIMESTAMPTZ_DATE_CODEC_ID], + nowTakesPrecision: postgresNowGeneratorTakesPrecision[PG_TIMESTAMPTZ_DATE_CODEC_ID], }), .../* @__PURE__ */ temporalAuthoringPresets({ codecId: PG_TIMESTAMPTZ_TEMPORAL_CODEC_ID, diff --git a/packages/3-targets/postgres/1-target/src/core/data-types.ts b/packages/3-targets/postgres/1-target/src/core/data-types.ts index 76ed5a19daf5..fbf1dd35e80e 100644 --- a/packages/3-targets/postgres/1-target/src/core/data-types.ts +++ b/packages/3-targets/postgres/1-target/src/core/data-types.ts @@ -337,6 +337,14 @@ export const pgPrecisionParams = arktype({ 'precision?': 'number.integer >= 0 & number.integer <= 6', }); +/** PostgreSQL stores microseconds whether a precision of 6 is declared or omitted, so an omitted precision is 6. */ +const withDefaultPrecision = (params: Params) => + params.precision === undefined ? { ...params, precision: 6 } : params; + +/** PostgreSQL's catalog prints a precision only where one was declared. */ +const precisionAsWritten = (params: Params) => + params; + export const pgEnumParams = arktype({ typeName: 'string > 0' }); /** A type with no parameters that is written and reported by its own name. */ @@ -600,6 +608,8 @@ export const pgTimetz = sqlDataType('pg/timetz', { ), spell: spellStoredDateTime(pgTimetzStoredText), params: pgPrecisionParams, + normalize: withDefaultPrecision, + catalogParams: precisionAsWritten, texts: [ written('timetz'), written('timetz({precision})'), @@ -618,6 +628,8 @@ export const pgInterval = sqlDataType('pg/interval', { spell: spellStoredDateTime(pgIntervalStoredText), equalityKey: intervalEqualityKey, params: pgPrecisionParams, + normalize: withDefaultPrecision, + catalogParams: precisionAsWritten, texts: [writtenAndCatalog('interval'), writtenAndCatalog('interval({precision})')], }); @@ -668,6 +680,8 @@ export const pgTime = sqlDataType('pg/time', { ), spell: spellStoredDateTime(pgTimeStoredText), params: pgPrecisionParams, + normalize: withDefaultPrecision, + catalogParams: precisionAsWritten, texts: [ written('time'), written('time({precision})'), @@ -685,6 +699,8 @@ export const pgTimestamp = sqlDataType('pg/timestamp', { ), spell: spellStoredDateTime(pgTimestampStoredText), params: pgPrecisionParams, + normalize: withDefaultPrecision, + catalogParams: precisionAsWritten, texts: [ written('timestamp'), written('timestamp({precision})'), @@ -702,6 +718,8 @@ export const pgTimestamptz = sqlDataType('pg/timestamptz', { ), spell: spellStoredDateTime(pgTimestamptzStoredText), params: pgPrecisionParams, + normalize: withDefaultPrecision, + catalogParams: precisionAsWritten, texts: [ written('timestamptz'), written('timestamptz({precision})'), diff --git a/packages/3-targets/postgres/1-target/src/core/date-codecs.ts b/packages/3-targets/postgres/1-target/src/core/date-codecs.ts index 8690b596b2dc..7500a8967eee 100644 --- a/packages/3-targets/postgres/1-target/src/core/date-codecs.ts +++ b/packages/3-targets/postgres/1-target/src/core/date-codecs.ts @@ -38,6 +38,8 @@ interface DateCodecIdentity { readonly codecId: string; readonly typeDescription: string; readonly stringType: string; + /** The explicit types that hold digits below a millisecond, which a `Date` does not. */ + readonly finerTypes: string; readonly textCodecId: string; /** The instant, in the stored spelling of `pg/timestamptz`, that text of the codec's data type names. */ readonly instantText: (text: string) => string; @@ -47,6 +49,7 @@ const TIMESTAMPTZ_DATE: DateCodecIdentity = { codecId: PG_TIMESTAMPTZ_DATE_CODEC_ID, typeDescription: 'timestamp with time zone', stringType: 'TimestamptzString', + finerTypes: 'TimestamptzString and TimestamptzTemporal', textCodecId: PG_TIMESTAMPTZ_STRING_CODEC_ID, instantText: pgTimestamptzStoredText, }; @@ -55,6 +58,7 @@ const TIMESTAMP_DATE: DateCodecIdentity = { codecId: PG_TIMESTAMP_DATE_CODEC_ID, typeDescription: 'timestamp, read as UTC', stringType: 'TimestampString', + finerTypes: 'TimestampString and TimestampInstant', textCodecId: PG_TIMESTAMP_STRING_CODEC_ID, instantText: (text) => pgTimestamptzStoredText(`${pgTimestampStoredText(text)}Z`), }; @@ -99,8 +103,20 @@ function decodeDate(identity: DateCodecIdentity, wire: unknown): Date { return date; } -/** The `Date` a stored value names. A `Date` holds neither `infinity` nor an instant before 4714-11-24 BC or after +275760-09-13, so those are refused. */ +/** Whether stored text has nonzero digits below a millisecond, which a `Date` drops. */ +function hasDigitsBelowAMillisecond(text: string): boolean { + return /[1-9]/.test(/\.(\d+)/.exec(text)?.[1]?.slice(3) ?? ''); +} + +/** The `Date` a stored value names. A `Date` holds neither `infinity`, nor an instant before 4714-11-24 BC or after +275760-09-13, nor digits below a millisecond, so those are refused. */ function dateOfStoredText(identity: DateCodecIdentity, text: string): Date { + if (hasDigitsBelowAMillisecond(text)) { + return refuseJsonValue( + identity.codecId, + `an instant with no digits below a millisecond, which a Date drops: ${JSON.stringify(text)} has them, and ${identity.finerTypes} hold it`, + text, + ); + } const instant = storedInstant(identity, text); const date = instant === undefined ? undefined : new Date(instant); if (date === undefined || !isRepresentable(date)) { diff --git a/packages/3-targets/postgres/1-target/src/core/date-time-now-generators.ts b/packages/3-targets/postgres/1-target/src/core/date-time-now-generators.ts index 8e96cbc723d4..854b6f628871 100644 --- a/packages/3-targets/postgres/1-target/src/core/date-time-now-generators.ts +++ b/packages/3-targets/postgres/1-target/src/core/date-time-now-generators.ts @@ -3,7 +3,7 @@ import type { Temporal } from 'temporal-spec'; import { requireTemporal } from './require-temporal'; /** - * The Postgres "now" generators, one per date and time codec except `pg/timestamptz-date@1`, which uses the framework's `timestampNow`. All read the clock through `Date.now()` and write UTC. The time generators and the string timestamp generators round down to their `precision` param, so the column stores the value as written. The string generators are named after the codec they serve and write the codec's stored form (ADR 254, "Date and time types") in UTC, with milliseconds and no trailing zeros in the fraction. + * The Postgres "now" generators, one per date and time codec except `pg/timestamptz-date@1` and `pg/timestamp-date@1`, which use the SQL family's `timestampNow`. All read the clock through `Date.now()` and write UTC. The time generators and the string timestamp generators round down to their `precision` param, so the column stores the value as written. The string generators are named after the codec they serve and write the codec's stored form (ADR 254, "Date and time types") in UTC, with milliseconds and no trailing zeros in the fraction. */ export const INSTANT_NOW_GENERATOR_ID = 'instantNow' as const; export const PLAIN_DATE_TIME_NOW_GENERATOR_ID = 'plainDateTimeNow' as const; diff --git a/packages/3-targets/postgres/1-target/src/core/errors.ts b/packages/3-targets/postgres/1-target/src/core/errors.ts index bb1a13b6f7ce..71ab5ed83c9f 100644 --- a/packages/3-targets/postgres/1-target/src/core/errors.ts +++ b/packages/3-targets/postgres/1-target/src/core/errors.ts @@ -71,13 +71,16 @@ export function errorPostgresMigrationStackMissing(operation: string): CliStruct ); } +const TEMPORAL_UNAVAILABLE_FIX = + 'Run on a runtime with Temporal available, install a Temporal polyfill before creating the client, or write the column as a convenience type at its default, which needs no Temporal: `DateTime` reads a JavaScript `Date`, and `Date`, `Time` and `Duration` read ISO 8601 strings.'; + export function errorTemporalUnavailable(codecId: string, operation: string): StructuredError { return postgresError( 'RUNTIME.TEMPORAL_UNAVAILABLE', `Codec '${codecId}' cannot ${operation} a value because this runtime has no global Temporal implementation.`, { why: 'Temporal-backed codecs read and write their values through the global Temporal API, which this runtime does not provide.', - fix: 'Run on a runtime with Temporal available, install a Temporal polyfill before creating the client, or author the column with its *String type to read and write ISO 8601 text instead.', + fix: TEMPORAL_UNAVAILABLE_FIX, meta: { codecId, operation }, }, ); @@ -88,8 +91,8 @@ export function errorTemporalUnavailableForDefault(generatorId: string): Structu 'RUNTIME.TEMPORAL_UNAVAILABLE', `Mutation default generator '${generatorId}' cannot produce a value because this runtime has no global Temporal implementation.`, { - why: 'The generator answers a Temporal-backed column, so the value it produces is a Temporal.Instant, which this runtime cannot construct.', - fix: 'Run on a runtime with Temporal available, install a Temporal polyfill before creating the client, or author the column with `temporal.createdAtString()` / `temporal.updatedAtString()` to store ISO 8601 text instead.', + why: 'The generator answers a Temporal-backed column, so the value it produces is a Temporal value, which this runtime cannot construct.', + fix: TEMPORAL_UNAVAILABLE_FIX, meta: { generatorId }, }, ); diff --git a/packages/3-targets/postgres/1-target/src/core/migrations/planner-sql-checks.ts b/packages/3-targets/postgres/1-target/src/core/migrations/planner-sql-checks.ts index cf6a6061c59f..501ce39be280 100644 --- a/packages/3-targets/postgres/1-target/src/core/migrations/planner-sql-checks.ts +++ b/packages/3-targets/postgres/1-target/src/core/migrations/planner-sql-checks.ts @@ -130,6 +130,7 @@ type WrittenTypeRead = readonly ok: true; readonly type: SqlDataType; readonly typeParams: SqlTypeParams; + readonly writtenTypeParams: SqlTypeParams; readonly many: boolean; } | { readonly ok: false; readonly reason: string }; @@ -147,14 +148,25 @@ function readWrittenType(type: string, dataTypes: readonly DataType[]): WrittenT reason: `more than one data type of the configured stack reads the type "${type}", as ${named}`, }; } - const resolved = byText[0] ?? resolvedByKind(base, sqlTypes); - if (resolved === undefined) { + const [written] = byText; + if (written !== undefined) { + return { + ok: true, + type: written.dataType, + typeParams: written.typeParams, + writtenTypeParams: written.writtenTypeParams, + many: array !== null, + }; + } + const byKind = resolvedByKind(base, sqlTypes); + if (byKind === undefined) { return { ok: false, reason: `no data type of the configured stack reads the type "${type}"` }; } return { ok: true, - type: resolved.dataType, - typeParams: resolved.typeParams, + type: byKind.dataType, + typeParams: byKind.typeParams, + writtenTypeParams: byKind.typeParams, many: array !== null, }; } @@ -177,7 +189,7 @@ function readColumnTypeCheck( }, }; } - const text = renderSqlCatalogText(read.type, read.typeParams); + const text = renderSqlCatalogText(read.type, read.writtenTypeParams); return { ok: true, check: { kind: 'catalog-text', text: `${text}${read.many ? '[]' : ''}` } }; } diff --git a/packages/3-targets/postgres/1-target/src/core/now-generators.ts b/packages/3-targets/postgres/1-target/src/core/now-generators.ts index db8f02261cc8..8bb5b9b01be8 100644 --- a/packages/3-targets/postgres/1-target/src/core/now-generators.ts +++ b/packages/3-targets/postgres/1-target/src/core/now-generators.ts @@ -51,13 +51,15 @@ export function postgresNowGeneratorIdFor(codecId: string): string | undefined { return nowGeneratorIdByCodecId.get(codecId); } -/** The codecs whose "now" generator takes the column's precision and rounds down to it, so PostgreSQL stores the value as generated rather than rounding it up, which for a time can reach `24:00:00`. */ +/** The codecs whose "now" generator takes the column's precision and rounds down to it, so PostgreSQL stores the value the client holds rather than rounding it up, which for a time can reach `24:00:00`. */ export const postgresNowGeneratorTakesPrecision = { [PG_TIME_TEMPORAL_CODEC_ID]: true, [PG_TIME_STRING_CODEC_ID]: true, [PG_TIMETZ_CODEC_ID]: true, [PG_TIMESTAMP_STRING_CODEC_ID]: true, [PG_TIMESTAMPTZ_STRING_CODEC_ID]: true, + [PG_TIMESTAMPTZ_DATE_CODEC_ID]: true, + [PG_TIMESTAMP_DATE_CODEC_ID]: true, } as const; /** The `now` generator function bound to each Postgres date and time codec, with the column's precision on the codecs that take it. */ diff --git a/packages/3-targets/postgres/1-target/src/core/prisma7-binding.ts b/packages/3-targets/postgres/1-target/src/core/prisma7-binding.ts index 4d04e44d335a..8957b541f998 100644 --- a/packages/3-targets/postgres/1-target/src/core/prisma7-binding.ts +++ b/packages/3-targets/postgres/1-target/src/core/prisma7-binding.ts @@ -1,3 +1,6 @@ +import type { JsonValue } from '@internal/contract/types'; +import { isSqlDataType, renderSqlTypeName } from '@internal/sql-contract/data-type'; +import { InternalError } from '@internal/utils/internal-error'; import { postgresDataTypeEntries } from './data-type-entries'; import { pgBytea, @@ -8,13 +11,16 @@ import { pgTimestamp, pgTimestamptz, pgTimetz, + postgresDataTypes, } from './data-types'; import { defaultForeignKeyName, defaultPrimaryKeyName } from './default-constraint-names'; import { postgresTargetDescriptorMeta } from './descriptor-meta'; import { postgresCreateNamespace } from './postgres-schema'; +import { postgresYearText } from './postgres-year-text'; import { prisma7TemporalStored, prisma7TemporalTagBody } from './prisma7-temporal-defaults'; import { prisma7PostgresTypeMap } from './prisma7-type-map'; import { junctionRelationFieldNames } from './psl-infer/junction-relation-field-names'; +import { escapeLiteral } from './sql-utils'; /** The date and time types a Prisma 7 `DateTime` default can be written on. */ const DATE_TIME_TYPES: ReadonlySet = new Set([ @@ -44,6 +50,36 @@ function base64ToHex(base64: string): string | undefined { return `\\x${Buffer.from(base64, 'base64').toString('hex')}`; } +/** The column's type as a migration writes it, such as `timestamp(6)`. */ +function writtenTypeName( + dataType: string, + typeParams: Readonly> | undefined, +): string { + const type = postgresDataTypes.find((candidate) => candidate.id === dataType); + if (type === undefined || !isSqlDataType(type)) { + throw new InternalError(`The Postgres target registers no SQL data type "${dataType}".`); + } + return renderSqlTypeName(type, typeParams ?? {}); +} + +/** A default's SQL: a quoted literal cast to the column type, or for a list an array of them cast to an array of it. A date or time is written with the year text PostgreSQL reads. */ +function refusedLiteralDefault( + stored: JsonValue, + column: { + readonly dataType: string; + readonly typeParams?: Readonly> | undefined; + }, +): string { + const typeName = writtenTypeName(column.dataType, column.typeParams); + const text = (value: string) => + DATE_TIME_TYPES.has(column.dataType) ? postgresYearText(value) : value; + const quoted = (value: JsonValue) => + value === null ? 'NULL' : `'${escapeLiteral(text(String(value)))}'`; + return Array.isArray(stored) + ? `ARRAY[${stored.map(quoted).join(', ')}]::${typeName}[]` + : `${quoted(stored)}::${typeName}`; +} + /** * What the Postgres target supplies to the Prisma 7 interpreter. A `Bytes` or * `DateTime` default is read through the tag its column type's authoring entry @@ -69,6 +105,7 @@ export const prisma7PostgresBinding = { identifierMaxBytes: 63, defaultConstraintNames: { primaryKey: defaultPrimaryKeyName, foreignKey: defaultForeignKeyName }, junctionRelationFieldNames, + refusedLiteralDefault, literalDefaultForm: ({ dataType, typeParams, diff --git a/packages/3-targets/postgres/1-target/src/core/psl-build/psl-literals.ts b/packages/3-targets/postgres/1-target/src/core/psl-build/psl-literals.ts index c62b837ffb41..346f8338805f 100644 --- a/packages/3-targets/postgres/1-target/src/core/psl-build/psl-literals.ts +++ b/packages/3-targets/postgres/1-target/src/core/psl-build/psl-literals.ts @@ -56,3 +56,13 @@ export function positionalArg(value: string): PslAttributeArgument { export function namedArg(name: string, value: string): PslAttributeArgument { return { kind: 'named', name, value, span: SYNTHETIC_SPAN }; } + +/** A type call argument, by name when it has one. */ +export function typeArgument(argument: { + readonly name: string | undefined; + readonly text: string; +}): PslAttributeArgument { + return argument.name === undefined + ? positionalArg(argument.text) + : namedArg(argument.name, argument.text); +} diff --git a/packages/3-targets/postgres/1-target/src/core/psl-infer/infer-model-blocks.ts b/packages/3-targets/postgres/1-target/src/core/psl-infer/infer-model-blocks.ts index 6fe197b11f1f..1c3b73968924 100644 --- a/packages/3-targets/postgres/1-target/src/core/psl-infer/infer-model-blocks.ts +++ b/packages/3-targets/postgres/1-target/src/core/psl-infer/infer-model-blocks.ts @@ -37,6 +37,7 @@ import { parseDefaultAttributeString, positionalArg, SYNTHETIC_SPAN, + typeArgument, } from '../psl-build/psl-literals'; import { createUniqueFieldName } from '../psl-build/unique-name'; import type { InferredColumnDefaults } from './infer-default-codec'; @@ -252,7 +253,7 @@ function buildScalarField( const pslType = enumPslName === undefined ? columnTypes(table.name, column.name) - : { path: [enumPslName], args: [], values: [] }; + : { path: [enumPslName], args: [], explicit: { path: [enumPslName], values: [] } }; const typeName = pslType.path.join('.'); const typeConstructor: PslTypeConstructorCall | undefined = enumPslName !== undefined @@ -266,7 +267,7 @@ function buildScalarField( ? { kind: 'typeConstructor', path: pslType.path, - args: pslType.args.map(positionalArg), + args: pslType.args.map(typeArgument), span: SYNTHETIC_SPAN, } : undefined; @@ -283,11 +284,12 @@ function buildScalarField( defaultOf(table.name, column), { ...defaultMapping, - ...ifDefined('columnCodec', columnDefaults.codecOf(pslType, isEnumColumn)), - ...ifDefined('typeParams', columnDefaults.typeParamsOf(pslType, isEnumColumn)), + ...ifDefined('columnCodec', columnDefaults.codecOf(pslType.explicit, isEnumColumn)), + ...ifDefined('typeParams', columnDefaults.typeParamsOf(pslType.explicit, isEnumColumn)), list: column.many === true, }, - (value) => columnDefaults.readsBack(value, pslType, isEnumColumn, column.many === true), + (value) => + columnDefaults.readsBack(value, pslType.explicit, isEnumColumn, column.many === true), noteDefaultThatDoesNotReadBack, ); if (defaultAttribute !== undefined) { diff --git a/packages/3-targets/postgres/1-target/src/core/psl-infer/infer-psl-contract.ts b/packages/3-targets/postgres/1-target/src/core/psl-infer/infer-psl-contract.ts index e3b8e7f15e72..3e2e5f7ca471 100644 --- a/packages/3-targets/postgres/1-target/src/core/psl-infer/infer-psl-contract.ts +++ b/packages/3-targets/postgres/1-target/src/core/psl-infer/infer-psl-contract.ts @@ -225,6 +225,7 @@ export async function inferPostgresPslContract( codecLookup: context.codecLookup, dataTypeLookup: context.dataTypes.lookup, }), + authoringTypes: context.authoringContributions.type, dataTypes: context.dataTypes, isEnumColumn, }); diff --git a/packages/3-targets/postgres/1-target/src/core/psl-infer/infer-read-defaults.ts b/packages/3-targets/postgres/1-target/src/core/psl-infer/infer-read-defaults.ts index aef9978ee383..abb1afaf44cf 100644 --- a/packages/3-targets/postgres/1-target/src/core/psl-infer/infer-read-defaults.ts +++ b/packages/3-targets/postgres/1-target/src/core/psl-infer/infer-read-defaults.ts @@ -13,7 +13,15 @@ import type { ColumnTypeOf } from '@internal/family-sql/psl-build'; import { type Codec, codecForRef } from '@internal/framework-components/codec'; import type { SqlColumnIR, SqlSchemaIR } from '@internal/sql-schema-ir/types'; import { isInternalError } from '@internal/utils/internal-error'; -import { pgJson, pgJsonb } from '../data-types'; +import { isStructuredError } from '@internal/utils/structured-error'; +import { + pgJson, + pgJsonb, + pgTimestamp, + pgTimestampStoredText, + pgTimestamptz, + pgTimestamptzStoredText, +} from '../data-types'; import { readPostgresDefault } from '../default-normalizer'; import { inferredCodecRef } from './infer-default-codec'; import { jsonKeepsEveryNumber } from './json-exact-numbers'; @@ -90,7 +98,44 @@ export async function readDefaultWithCodec( return rawExpression; } const read = await readReportedDefault(codec, reported.literal, raw); - return 'resolvedDefault' in read ? read.resolvedDefault : rawExpression; + if (!('resolvedDefault' in read)) return rawExpression; + const storedText = TIMESTAMP_STORED_TEXT.get(codec.dataType.id); + if ( + storedText !== undefined && + !readsEveryDigit(storedText, reported.literal, read.resolvedDefault) + ) { + return rawExpression; + } + return read.resolvedDefault; +} + +/** The value each timestamp type holds for the text PostgreSQL prints. A `Date` codec of these types reads only milliseconds. */ +const TIMESTAMP_STORED_TEXT: ReadonlyMap string> = new Map([ + [pgTimestamp.id, pgTimestampStoredText], + [pgTimestamptz.id, pgTimestamptzStoredText], +]); + +/** Whether the codec read each body as the value the column holds, so a `Date` codec's millisecond reading of a microsecond default is printed as the raw expression instead. */ +function readsEveryDigit( + storedText: (text: string) => string, + literal: ReportedLiteralDefault, + read: ColumnDefault, +): boolean { + const value = read.kind === 'literal' ? read.value : undefined; + const values = literal.kind === 'value' ? [value] : Array.isArray(value) ? value : []; + return bodiesOf(literal).every( + (body, index) => typeof body !== 'string' || storedTextOf(storedText, body) === values[index], + ); +} + +/** The value the column holds for `body`, or `undefined` when its type does not read it. */ +function storedTextOf(storedText: (text: string) => string, body: string): string | undefined { + try { + return storedText(body); + } catch (error) { + if (isStructuredError(error)) return undefined; + throw error; + } } const JSON_TYPES: ReadonlySet = new Set([pgJson.id, pgJsonb.id]); @@ -122,7 +167,7 @@ function codecOf( context: SqlPslBuildContext, ) { try { - const ref = inferredCodecRef(context, isEnum ? { path: [] } : pslType(), isEnum); + const ref = inferredCodecRef(context, isEnum ? { path: [] } : pslType().explicit, isEnum); return ref === undefined ? undefined : codecForRef(context.codecLookup, ref); } catch (error) { if (isInternalError(error)) throw error; diff --git a/packages/3-targets/postgres/1-target/src/core/psl-infer/junction-relation-field-names.ts b/packages/3-targets/postgres/1-target/src/core/psl-infer/junction-relation-field-names.ts index 32834c3a08ed..d660eba8437d 100644 --- a/packages/3-targets/postgres/1-target/src/core/psl-infer/junction-relation-field-names.ts +++ b/packages/3-targets/postgres/1-target/src/core/psl-infer/junction-relation-field-names.ts @@ -57,7 +57,7 @@ export function junctionRelationFieldNames( tables: { [tableA]: idTable(tableA), [tableB]: idTable(tableB), [junctionName]: junction }, }), { - columnTypes: () => ({ path: ['Int'], args: [], values: [] }), + columnTypes: () => ({ path: ['Int'], args: [], explicit: { path: ['Int'], values: [] } }), defaultMapping: createPostgresDefaultMapping(), defaultOf: noInferredDefaults, columnDefaults: noColumnDefaults, diff --git a/packages/3-targets/postgres/1-target/src/core/psl-print/column-types.ts b/packages/3-targets/postgres/1-target/src/core/psl-print/column-types.ts index b48d005483dd..8f88260eb34c 100644 --- a/packages/3-targets/postgres/1-target/src/core/psl-print/column-types.ts +++ b/packages/3-targets/postgres/1-target/src/core/psl-print/column-types.ts @@ -1,6 +1,8 @@ import { type InferredTypeConstructors, + type PslTypeArgument, writtenTypeArguments, + writtenTypeChoiceCall, } from '@internal/family-sql/psl-build'; import { type AuthoringTypeConstructorCall, @@ -16,7 +18,7 @@ import type { PslTypeConstructorCall } from '@internal/framework-components/psl- import { isSqlDataType, unquotedSqlBaseName } from '@internal/sql-contract/data-type'; import type { StorageColumn } from '@internal/sql-contract/types'; import { PG_ENUM_CODEC_ID } from '../codec-ids'; -import { positionalArg, SYNTHETIC_SPAN } from '../psl-build/psl-literals'; +import { positionalArg, SYNTHETIC_SPAN, typeArgument } from '../psl-build/psl-literals'; import { refuseColumnWithoutPslType, refuseUnwritableTypeArgument } from './refusals'; /** @@ -50,13 +52,16 @@ function typeArgumentTexts( authoringTypes: AuthoringTypeNamespace, dataTypes: DataTypeSupport, coordinate: string, -): readonly string[] { +): readonly PslTypeArgument[] { const descriptor = getAuthoringTypeConstructor({ type: authoringTypes }, call.path); const written = descriptor && writtenTypeArguments(descriptor, call.args, dataTypes); - return written ?? refuseUnwritableTypeArgument(call, coordinate); + return ( + written?.map((text) => ({ name: undefined, text })) ?? + refuseUnwritableTypeArgument(call, coordinate) + ); } -function typeCall(path: readonly string[], args: readonly string[]): PslColumnType { +function typeCall(path: readonly string[], args: readonly PslTypeArgument[]): PslColumnType { const name = path.join('.'); const isNamespaced = path.length > 1; if (args.length === 0 && !isNamespaced) { @@ -68,7 +73,7 @@ function typeCall(path: readonly string[], args: readonly string[]): PslColumnTy typeConstructor: { kind: 'typeConstructor', path, - args: args.map(positionalArg), + args: args.map(typeArgument), span: SYNTHETIC_SPAN, }, }; @@ -91,8 +96,10 @@ function inferredConstructorCall( * The PSL type position for a storage column: a call to a type constructor the configured stack * contributes that produces exactly the column's codec, native type and type parameters. The * constructor `contract infer` writes for the column's data type comes first; otherwise - * the first one in the stack that produces them, such as `DateTime` for a `timestamptz` column read - * as a `Temporal.Instant`, or `pgvector.Vector(3)`. An enum-typed column takes the + * the first one in the stack that produces them, such as `TimestamptzTemporal` or + * `pgvector.Vector(3)`. Where a type choice call becomes that call exactly, the type choice is + * written instead, by name with only the arguments that differ from its defaults: `DateTime` for + * `TimestamptzJsDate(3)`, `DateTime(as: temporal)` for `TimestamptzTemporal` (ADR 279). An enum-typed column takes the * `pg.enum()` constructor, named after the value set the column points at; a column typed by * a domain enum takes that enum's name. A column no PSL type reads back is refused. */ @@ -137,10 +144,14 @@ export function buildColumnType(input: { inferredConstructorCall(column, inferredConstructors) ?? findAuthoringTypeConstructorCall(authoringTypes, column); if (call !== undefined) { - return typeCall( - call.path, - typeArgumentTexts(call, authoringTypes, input.dataTypes, coordinate), - ); + const convenience = writtenTypeChoiceCall({ + authoringTypes, + call, + dataTypes: input.dataTypes, + }); + return convenience !== undefined + ? typeCall(convenience.path, convenience.args) + : typeCall(call.path, typeArgumentTexts(call, authoringTypes, input.dataTypes, coordinate)); } refuseColumnWithoutPslType(column, coordinate); diff --git a/packages/3-targets/postgres/1-target/src/core/type-constructors.ts b/packages/3-targets/postgres/1-target/src/core/type-constructors.ts index fb788e07ddbf..cea6c2d57826 100644 --- a/packages/3-targets/postgres/1-target/src/core/type-constructors.ts +++ b/packages/3-targets/postgres/1-target/src/core/type-constructors.ts @@ -177,9 +177,6 @@ export const postgresNativeAuthoringTypes = { documentation: 'A calendar date represented as Temporal.PlainDate.', output: { codecId: 'pg/date-temporal@1' }, }, - // The representation-explicit spellings. Same columns, same precision, same native types — the - // only difference is that a read hands back ISO 8601 text instead of a `Temporal.*`, so a value - // Temporal cannot express still round-trips. DateString: { kind: 'typeConstructor', inferred: true, @@ -188,7 +185,6 @@ export const postgresNativeAuthoringTypes = { }, TimestampString: { kind: 'typeConstructor', - inferred: true, documentation: 'A timestamp without a time zone represented as ISO 8601 text.', args: [{ kind: 'value', dataType: PG_SIZE_TYPE, name: 'precision', optional: true }], output: { @@ -198,6 +194,7 @@ export const postgresNativeAuthoringTypes = { }, TimestamptzJsDate: { kind: 'typeConstructor', + inferred: true, documentation: 'An instant stored as PostgreSQL timestamptz and represented as a JavaScript Date.', args: [{ kind: 'value', dataType: PG_SIZE_TYPE, name: 'precision', optional: true }], @@ -208,6 +205,7 @@ export const postgresNativeAuthoringTypes = { }, TimestampJsDate: { kind: 'typeConstructor', + inferred: true, documentation: 'A date and time stored as PostgreSQL timestamp and represented as a JavaScript Date, read and written as UTC.', args: [{ kind: 'value', dataType: PG_SIZE_TYPE, name: 'precision', optional: true }], @@ -228,7 +226,6 @@ export const postgresNativeAuthoringTypes = { }, TimestamptzString: { kind: 'typeConstructor', - inferred: true, documentation: 'A timestamp with time zone represented as ISO 8601 text, the instant in UTC.', args: [{ kind: 'value', dataType: PG_SIZE_TYPE, name: 'precision', optional: true }], output: { @@ -268,7 +265,6 @@ export const postgresNativeAuthoringTypes = { }, IntervalParts: { kind: 'typeConstructor', - inferred: true, documentation: 'A span of time stored as PostgreSQL interval and represented as { months, days, micros }, with optional fractional-second precision.', args: [{ kind: 'value', dataType: PG_SIZE_TYPE, name: 'precision', optional: true }], @@ -279,6 +275,7 @@ export const postgresNativeAuthoringTypes = { }, IntervalString: { kind: 'typeConstructor', + inferred: true, documentation: 'A span of time stored as PostgreSQL interval and represented as an ISO 8601 duration such as P1Y2M, with optional fractional-second precision.', args: [{ kind: 'value', dataType: PG_SIZE_TYPE, name: 'precision', optional: true }], diff --git a/packages/3-targets/postgres/1-target/test/authoring-field-presets.test.ts b/packages/3-targets/postgres/1-target/test/authoring-field-presets.test.ts index a3bd07bf63c6..52dd6bb89df5 100644 --- a/packages/3-targets/postgres/1-target/test/authoring-field-presets.test.ts +++ b/packages/3-targets/postgres/1-target/test/authoring-field-presets.test.ts @@ -116,6 +116,7 @@ describe('postgres temporal per-codec presets', () => { timestamptzJsDate: temporalCodecPresetWithPrecision({ sizeType: 'pg/int4', codecId: 'pg/timestamptz-date@1', + nowTakesPrecision: true, }), timestamp: temporalCodecPresetWithPrecision({ sizeType: 'pg/int4', diff --git a/packages/3-targets/postgres/1-target/test/data-type-texts.test.ts b/packages/3-targets/postgres/1-target/test/data-type-texts.test.ts index 59c113051b38..799c36f98266 100644 --- a/packages/3-targets/postgres/1-target/test/data-type-texts.test.ts +++ b/packages/3-targets/postgres/1-target/test/data-type-texts.test.ts @@ -102,11 +102,11 @@ const ROUND_TRIPS: ReadonlyArray< [pgBit, { length: 8 }, 'bit(8)', 'bit(8)', { length: 8 }], [pgVarbit, {}, 'bit varying', 'bit varying', {}], [pgVarbit, { length: 8 }, 'bit varying(8)', 'bit varying(8)', { length: 8 }], - [pgTime, {}, 'time', 'time without time zone', {}], + [pgTime, {}, 'time', 'time without time zone', { precision: 6 }], [pgTime, { precision: 3 }, 'time(3)', 'time(3) without time zone', { precision: 3 }], - [pgTimetz, {}, 'timetz', 'time with time zone', {}], + [pgTimetz, {}, 'timetz', 'time with time zone', { precision: 6 }], [pgTimetz, { precision: 3 }, 'timetz(3)', 'time(3) with time zone', { precision: 3 }], - [pgTimestamp, {}, 'timestamp', 'timestamp without time zone', {}], + [pgTimestamp, {}, 'timestamp', 'timestamp without time zone', { precision: 6 }], [ pgTimestamp, { precision: 3 }, @@ -121,7 +121,7 @@ const ROUND_TRIPS: ReadonlyArray< 'timestamp(0) without time zone', { precision: 0 }, ], - [pgTimestamptz, {}, 'timestamptz', 'timestamp with time zone', {}], + [pgTimestamptz, {}, 'timestamptz', 'timestamp with time zone', { precision: 6 }], [ pgTimestamptz, { precision: 3 }, @@ -129,7 +129,7 @@ const ROUND_TRIPS: ReadonlyArray< 'timestamp(3) with time zone', { precision: 3 }, ], - [pgInterval, {}, 'interval', 'interval', {}], + [pgInterval, {}, 'interval', 'interval', { precision: 6 }], [pgInterval, { precision: 3 }, 'interval(3)', 'interval(3)', { precision: 3 }], ]; @@ -279,15 +279,15 @@ const CLAIMING_TEXTS: ReadonlyArray = ['bit varying(8)', 'pg/varbit', { length: 8 }], ['varbit', 'pg/varbit', {}], ['varbit(8)', 'pg/varbit', { length: 8 }], - ['time without time zone', 'pg/time', {}], + ['time without time zone', 'pg/time', { precision: 6 }], ['time(3) without time zone', 'pg/time', { precision: 3 }], - ['time with time zone', 'pg/timetz', {}], + ['time with time zone', 'pg/timetz', { precision: 6 }], ['time(3) with time zone', 'pg/timetz', { precision: 3 }], - ['timestamp without time zone', 'pg/timestamp', {}], + ['timestamp without time zone', 'pg/timestamp', { precision: 6 }], ['timestamp(3) without time zone', 'pg/timestamp', { precision: 3 }], - ['timestamp with time zone', 'pg/timestamptz', {}], + ['timestamp with time zone', 'pg/timestamptz', { precision: 6 }], ['timestamp(3) with time zone', 'pg/timestamptz', { precision: 3 }], - ['interval', 'pg/interval', {}], + ['interval', 'pg/interval', { precision: 6 }], ['interval(3)', 'pg/interval', { precision: 3 }], ]; diff --git a/packages/3-targets/postgres/1-target/test/date-codecs.sub-millisecond.test.ts b/packages/3-targets/postgres/1-target/test/date-codecs.sub-millisecond.test.ts new file mode 100644 index 000000000000..7a00cd9413a2 --- /dev/null +++ b/packages/3-targets/postgres/1-target/test/date-codecs.sub-millisecond.test.ts @@ -0,0 +1,40 @@ +import { describe, expect, it } from 'vitest'; +import { pgTimestamp, pgTimestamptz } from '../src/core/data-types'; +import { pgTimestampDateDescriptor, pgTimestamptzDateDescriptor } from '../src/core/date-codecs'; + +const instance = { name: 'test' }; +const timestamptzDate = pgTimestamptzDateDescriptor.factory({ precision: 6 })(instance); +const timestampDate = pgTimestampDateDescriptor.factory({ precision: 6 })(instance); + +describe('the Date codecs and digits below a millisecond', () => { + it('refuse a stored value with them, naming the types that hold it', () => { + expect(() => + timestamptzDate.fromDataTypeValue( + pgTimestamptz.valueFrom('2024-01-01T01:00:00.123456Z', { precision: 6 }), + ), + ).toThrow(/2024-01-01T01:00:00\.123456Z[\s\S]*TimestamptzString[\s\S]*TimestamptzTemporal/); + expect(() => + timestampDate.fromDataTypeValue( + pgTimestamp.valueFrom('2024-01-01T01:00:00.123456', { precision: 6 }), + ), + ).toThrow(/2024-01-01T01:00:00\.123456[\s\S]*TimestampString[\s\S]*TimestampInstant/); + }); + + it('take a stored value at a millisecond', () => { + expect([ + timestamptzDate.fromDataTypeValue( + pgTimestamptz.valueFrom('2024-01-01T01:00:00.123Z', { precision: 6 }), + ), + timestampDate.fromDataTypeValue( + pgTimestamp.valueFrom('2024-01-01T01:00:00.12', { precision: 6 }), + ), + ]).toEqual([new Date('2024-01-01T01:00:00.123Z'), new Date('2024-01-01T01:00:00.120Z')]); + }); + + it('read a row with them as the Date at the millisecond', async () => { + expect([ + await timestamptzDate.fromWire('2024-01-01 01:00:00.123456+00', {}), + await timestampDate.fromWire('2024-01-01 01:00:00.123456', {}), + ]).toEqual([new Date('2024-01-01T01:00:00.123Z'), new Date('2024-01-01T01:00:00.123Z')]); + }); +}); diff --git a/packages/3-targets/postgres/1-target/test/date-time-text-wording.test.ts b/packages/3-targets/postgres/1-target/test/date-time-text-wording.test.ts index e9ba15f10df6..63bb9299e3b5 100644 --- a/packages/3-targets/postgres/1-target/test/date-time-text-wording.test.ts +++ b/packages/3-targets/postgres/1-target/test/date-time-text-wording.test.ts @@ -41,6 +41,15 @@ describe('the wording that sends a user to a text date or time type', () => { }); }); + it.each([ + ['a codec', errorTemporalUnavailable('pg/date-temporal@1', 'decode')], + ['a generator', errorTemporalUnavailableForDefault('instantNow')], + ])('sends the missing Temporal error of %s to the convenience defaults', (_name, error) => { + expect(error.fix).toBe( + 'Run on a runtime with Temporal available, install a Temporal polyfill before creating the client, or write the column as a convenience type at its default, which needs no Temporal: `DateTime` reads a JavaScript `Date`, and `Date`, `Time` and `Duration` read ISO 8601 strings.', + ); + }); + it('says the driver could not ensure DateStyle=ISO, names a pooler as one cause, and gives no advice to set startup options', () => { const error = errorDateStyleNotIso('pg/date-string@1', '02.01.2026'); expect({ diff --git a/packages/3-targets/postgres/1-target/test/existing-column-types-without-temporal.test.ts b/packages/3-targets/postgres/1-target/test/existing-column-types-without-temporal.test.ts index 0c97fb742584..cee3c8ec93bd 100644 --- a/packages/3-targets/postgres/1-target/test/existing-column-types-without-temporal.test.ts +++ b/packages/3-targets/postgres/1-target/test/existing-column-types-without-temporal.test.ts @@ -5,6 +5,7 @@ import { postgresScalarAndNativeTypeConstructors } from '../src/core/type-constr const TEMPORAL_CODEC_IDS = [ 'pg/date-temporal@1', 'pg/timestamp-temporal@1', + 'pg/timestamp-instant@1', 'pg/timestamptz-temporal@1', 'pg/time-temporal@1', ]; diff --git a/packages/3-targets/postgres/1-target/test/generator-functions.test.ts b/packages/3-targets/postgres/1-target/test/generator-functions.test.ts index 7d7e5105c6b6..bfeba03ceea1 100644 --- a/packages/3-targets/postgres/1-target/test/generator-functions.test.ts +++ b/packages/3-targets/postgres/1-target/test/generator-functions.test.ts @@ -13,8 +13,6 @@ describe('the Postgres generator functions', () => { it.each([ ['pg/timestamptz-temporal@1', 'instantNow'], ['pg/timestamp-temporal@1', 'plainDateTimeNow'], - ['pg/timestamptz-date@1', 'timestampNow'], - ['pg/timestamp-date@1', 'timestampNow'], ['pg/timestamp-instant@1', 'instantNow'], ['pg/date-temporal@1', 'plainDateNow'], ['pg/date-string@1', 'dateStringNow'], @@ -31,6 +29,8 @@ describe('the Postgres generator functions', () => { ['pg/timetz@1', 'timetzNow'], ['pg/timestamptz-string@1', 'timestamptzStringNow'], ['pg/timestamp-string@1', 'timestampStringNow'], + ['pg/timestamptz-date@1', 'timestampNow'], + ['pg/timestamp-date@1', 'timestampNow'], ])("resolve now() on %s to %s with the column's precision", (codecId, generatorId) => { expect({ withPrecision: resolve('now', codecId, { precision: 3 }), diff --git a/packages/3-targets/postgres/1-target/test/migrations/alter-type-check.test.ts b/packages/3-targets/postgres/1-target/test/migrations/alter-type-check.test.ts index c02f17888fa7..9e246af72bd3 100644 --- a/packages/3-targets/postgres/1-target/test/migrations/alter-type-check.test.ts +++ b/packages/3-targets/postgres/1-target/test/migrations/alter-type-check.test.ts @@ -31,6 +31,20 @@ describe('alterTypeCheck', () => { ]); }); + it('compares a date or time type by the text format_type prints for its precision as written', () => { + expect( + ['timestamptz', 'timestamptz(6)', 'time', 'timetz(6)', 'interval'].map((type) => + alterTypeCheck(type, postgresDataTypes, where), + ), + ).toEqual([ + catalogText('timestamp with time zone'), + catalogText('timestamp(6) with time zone'), + catalogText('time without time zone'), + catalogText('time(6) with time zone'), + catalogText('interval'), + ]); + }); + it('compares an enum by identity, by the name its data type renders, whatever schema it is in', () => { expect( ['"Mood"', '"app"."status"', '"app"."Status"', '"public"."Mood"', '"Mood"[]'].map((type) => @@ -77,7 +91,7 @@ describe('alterTypeCheck', () => { ); }); - it('gives the catalog text of the parameters in normal form for every written text of every Postgres data type', () => { + it('gives the catalog text of the parameters as written for every written text of every Postgres data type', () => { const sqlTypes = postgresDataTypes.filter( (type): type is SqlDataType => isSqlDataType(type) && type.sql.claimsKind === undefined, ); diff --git a/packages/3-targets/postgres/1-target/test/migrations/column-type-comparison.test.ts b/packages/3-targets/postgres/1-target/test/migrations/column-type-comparison.test.ts index a4d650cafe9e..23fa9440f205 100644 --- a/packages/3-targets/postgres/1-target/test/migrations/column-type-comparison.test.ts +++ b/packages/3-targets/postgres/1-target/test/migrations/column-type-comparison.test.ts @@ -118,6 +118,29 @@ describe('the planner compares a live column with the contract by data type', () ]).toEqual([0, 0, 0, 0, 0]); }); + it('plans no type change between an omitted precision and precision 6 on a date or time column', () => { + const timestamptz = (typeParams?: { precision: number }) => ({ + dataType: 'pg/timestamptz', + codecId: 'pg/timestamptz-date@1', + nullable: true, + ...(typeParams === undefined ? {} : { typeParams }), + }); + expect([ + typeChangesPlanned(timestamptz(), { + typeText: 'timestamp(6) with time zone', + nullable: true, + }), + typeChangesPlanned(timestamptz({ precision: 6 }), { + typeText: 'timestamp with time zone', + nullable: true, + }), + typeChangesPlanned(timestamptz({ precision: 3 }), { + typeText: 'timestamp with time zone', + nullable: true, + }), + ]).toEqual([0, 0, 1]); + }); + it('plans a type change for a live column no data type claims, whatever its text', () => { expect( typeChangesPlanned( diff --git a/packages/3-targets/postgres/1-target/test/now-generators.test.ts b/packages/3-targets/postgres/1-target/test/now-generators.test.ts index 711f69184fa5..f9e4843f3ff2 100644 --- a/packages/3-targets/postgres/1-target/test/now-generators.test.ts +++ b/packages/3-targets/postgres/1-target/test/now-generators.test.ts @@ -170,6 +170,8 @@ describe('the text "now" generators', () => { expect(postgresNowGeneratorTakesPrecision).toMatchObject({ 'pg/timestamp-string@1': true, 'pg/timestamptz-string@1': true, + 'pg/timestamptz-date@1': true, + 'pg/timestamp-date@1': true, }); }); diff --git a/packages/3-targets/postgres/1-target/test/prisma7-binding.refused-defaults.test.ts b/packages/3-targets/postgres/1-target/test/prisma7-binding.refused-defaults.test.ts new file mode 100644 index 000000000000..0924738b0276 --- /dev/null +++ b/packages/3-targets/postgres/1-target/test/prisma7-binding.refused-defaults.test.ts @@ -0,0 +1,34 @@ +import { describe, expect, it } from 'vitest'; +import { prisma7PostgresBinding } from '../src/core/prisma7-binding'; + +const column = (dataType: string, typeParams?: Record) => ({ + codecId: 'test@1', + dataType, + ...(typeParams === undefined ? {} : { typeParams }), +}); + +describe('the default the Postgres binding writes for values a codec refuses', () => { + it('is a quoted literal cast to the column type, for one value', () => { + expect( + prisma7PostgresBinding.refusedLiteralDefault( + '2024-01-02T03:04:05.123456', + column('pg/timestamp', { precision: 6 }), + ), + ).toBe("'2024-01-02T03:04:05.123456'::timestamp(6)"); + }); + + it('is an array of quoted literals cast to an array of the column type, for a list', () => { + expect( + prisma7PostgresBinding.refusedLiteralDefault( + ['2024-01-02T03:04:05.123456Z', null], + column('pg/timestamptz'), + ), + ).toBe("ARRAY['2024-01-02T03:04:05.123456Z', NULL]::timestamptz[]"); + }); + + it('escapes a quote in the value', () => { + expect(prisma7PostgresBinding.refusedLiteralDefault("it's", column('pg/text'))).toBe( + "'it''s'::text", + ); + }); +}); diff --git a/packages/3-targets/postgres/1-target/test/prisma7-type-map.test.ts b/packages/3-targets/postgres/1-target/test/prisma7-type-map.test.ts index 11d7ae6f0b00..028f0fa536e6 100644 --- a/packages/3-targets/postgres/1-target/test/prisma7-type-map.test.ts +++ b/packages/3-targets/postgres/1-target/test/prisma7-type-map.test.ts @@ -11,7 +11,7 @@ describe('the Prisma 7 type table', () => { BigInt: { constructorName: 'BigInt', args: [] }, Float: { constructorName: 'Float', args: [] }, Decimal: { constructorName: 'Numeric', args: [65, 30] }, - DateTime: { constructorName: 'TimestampString', args: [3] }, + DateTime: { constructorName: 'TimestampJsDate', args: [3] }, Json: { constructorName: 'Jsonb', args: [] }, Bytes: { constructorName: 'Bytes', args: [] }, }, @@ -28,8 +28,8 @@ describe('the Prisma 7 type table', () => { Real: { constructorName: 'Real', args: [] }, DoublePrecision: { constructorName: 'Float', args: [] }, Decimal: { constructorName: 'Numeric', args: [] }, - Timestamp: { constructorName: 'TimestampString', args: [] }, - Timestamptz: { constructorName: 'TimestamptzString', args: [] }, + Timestamp: { constructorName: 'TimestampJsDate', args: [] }, + Timestamptz: { constructorName: 'TimestamptzJsDate', args: [] }, Date: { constructorName: 'DateString', args: [] }, Time: { constructorName: 'TimeString', args: [] }, Timetz: { constructorName: 'Timetz', args: [] }, diff --git a/packages/3-targets/postgres/1-target/test/psl-build/marked-type-constructors.test.ts b/packages/3-targets/postgres/1-target/test/psl-build/marked-type-constructors.test.ts index 49da2d6573c3..26499461dab2 100644 --- a/packages/3-targets/postgres/1-target/test/psl-build/marked-type-constructors.test.ts +++ b/packages/3-targets/postgres/1-target/test/psl-build/marked-type-constructors.test.ts @@ -35,7 +35,7 @@ describe('inferredTypeConstructors', () => { ).toEqual({ 'pg/text': ['String'], 'pg/numeric': ['Numeric'], - 'pg/timestamptz': ['TimestamptzString'], + 'pg/timestamptz': ['TimestamptzJsDate'], 'pg/bit': ['Bit'], 'pg/enum': ['pg', 'enum'], }); @@ -69,7 +69,7 @@ describe('fewestArgumentsCall', () => { char5: { path: ['Char'], args: [5] }, bit1: { path: ['Bit'], args: [] }, varbit: { path: ['VarBit'], args: [] }, - timestamptz3: { path: ['TimestamptzString'], args: [3] }, + timestamptz3: { path: ['TimestamptzJsDate'], args: [3] }, text: { path: ['String'], args: [] }, }); }); diff --git a/packages/3-targets/postgres/1-target/test/psl-infer/fixtures.ts b/packages/3-targets/postgres/1-target/test/psl-infer/fixtures.ts index 1bf264729b3d..4ab8b978ada3 100644 --- a/packages/3-targets/postgres/1-target/test/psl-infer/fixtures.ts +++ b/packages/3-targets/postgres/1-target/test/psl-infer/fixtures.ts @@ -61,7 +61,7 @@ export function reportedFromWritten( const [resolved] = resolveWrittenSqlTypes(typeText, postgresDataTypes); const text = resolved !== undefined && resolved.dataType.sql.claimsKind === undefined - ? renderSqlCatalogText(resolved.dataType, resolved.typeParams) + ? renderSqlCatalogText(resolved.dataType, resolved.writtenTypeParams) : typeText; return { text, kind: undefined, schema: 'pg_catalog', name: undefined }; } @@ -158,6 +158,7 @@ export function inferredColumnTypesOf( codecLookup: inferBuildContext.codecLookup, dataTypeLookup: inferBuildContext.dataTypes.lookup, }), + authoringTypes: inferBuildContext.authoringContributions.type, dataTypes: inferBuildContext.dataTypes, isEnumColumn: (column) => column.reportedType?.kind === 'enum', }); diff --git a/packages/3-targets/postgres/1-target/test/psl-infer/infer-column-types.test.ts b/packages/3-targets/postgres/1-target/test/psl-infer/infer-column-types.test.ts index b304447c2e26..5e6cb49e015c 100644 --- a/packages/3-targets/postgres/1-target/test/psl-infer/infer-column-types.test.ts +++ b/packages/3-targets/postgres/1-target/test/psl-infer/infer-column-types.test.ts @@ -7,7 +7,7 @@ function reported(text: string, rest: Partial = {}): ReportedSq return { text, kind: undefined, schema: 'pg_catalog', name: undefined, ...rest }; } -/** Each Postgres data type with a marked constructor, as Postgres reports a column of it, and what infer prints. */ +/** Each Postgres data type with a marked constructor, as Postgres reports a column of it, and what infer prints: the type choice that becomes the marked constructor where one does. */ const COLUMN_TYPES = [ ['text', 'String'], ['boolean', 'Boolean'], @@ -29,21 +29,23 @@ const COLUMN_TYPES = [ ['character varying(20)', 'VarChar(20)'], ['uuid', 'Uuid'], ['inet', 'Inet'], - ['date', 'DateString'], - ['time without time zone', 'TimeString'], - ['time(3) without time zone', 'TimeString(3)'], - ['time with time zone', 'Timetz'], - ['time(2) with time zone', 'Timetz(2)'], - ['timestamp without time zone', 'TimestampString'], - ['timestamp(3) without time zone', 'TimestampString(3)'], - ['timestamp with time zone', 'TimestamptzString'], - ['timestamp(6) with time zone', 'TimestamptzString(6)'], + ['date', 'Date'], + ['time without time zone', 'Time'], + ['time(0) without time zone', 'Time(precision: 0)'], + ['time(3) without time zone', 'Time(precision: 3)'], + ['time with time zone', 'Time(storage: timetz)'], + ['time(2) with time zone', 'Time(storage: timetz, precision: 2)'], + ['timestamp without time zone', 'DateTime(storage: timestamp, precision: 6)'], + ['timestamp(3) without time zone', 'DateTime(storage: timestamp)'], + ['timestamp with time zone', 'DateTime(precision: 6)'], + ['timestamp(3) with time zone', 'DateTime'], + ['timestamp(6) with time zone', 'DateTime(precision: 6)'], ['bit(1)', 'Bit'], ['bit(8)', 'Bit(8)'], ['bit varying', 'VarBit'], ['bit varying(16)', 'VarBit(16)'], - ['interval', 'IntervalParts'], - ['interval(3)', 'IntervalParts(3)'], + ['interval', 'Duration'], + ['interval(3)', 'Duration(precision: 3)'], ] as const; function tableWith(columns: Record) { @@ -75,7 +77,7 @@ function printedFieldTypes(printed: string): Record { } describe('contract infer picks each column type through its data type', () => { - it('prints the marked constructor with the fewest arguments, for a scalar and a list', async () => { + it('prints the type choice with the arguments that differ from its defaults, or else the marked constructor with the fewest arguments, for a scalar and a list', async () => { const columns = Object.fromEntries( COLUMN_TYPES.flatMap(([text], index) => [ [`c${index * 2}`, { reportedType: reported(text) }], diff --git a/packages/3-targets/postgres/1-target/test/psl-infer/infer-parse-emit.test.ts b/packages/3-targets/postgres/1-target/test/psl-infer/infer-parse-emit.test.ts index 06cc9231d7d4..2ba123bf6dc1 100644 --- a/packages/3-targets/postgres/1-target/test/psl-infer/infer-parse-emit.test.ts +++ b/packages/3-targets/postgres/1-target/test/psl-infer/infer-parse-emit.test.ts @@ -24,66 +24,11 @@ import { } from '../../src/core/authoring'; import { createPostgresBuiltinCodecLookup } from '../../src/core/codec-registry'; import { type PostgresSchema, postgresCreateNamespace } from '../../src/core/postgres-schema'; +import { postgresScalarAndNativeTypeConstructors } from '../../src/core/type-constructors'; import { postgresDataTypeSupport } from '../fixtures/postgres-data-type-support'; import { printPslFromFlat } from './fixtures'; -const authoringTypes = { - Int: { kind: 'typeConstructor', output: { codecId: 'pg/int4@1' } }, - Uuid: { kind: 'typeConstructor', output: { codecId: 'pg/uuid@1' } }, - Inet: { kind: 'typeConstructor', output: { codecId: 'pg/inet@1' } }, - TimestampString: { - kind: 'typeConstructor', - args: [{ kind: 'number', name: 'precision', integer: true, optional: true }], - output: { - codecId: 'pg/timestamp-string@1', - typeParams: { precision: { kind: 'arg', index: 0 } }, - }, - }, - TimestamptzString: { - kind: 'typeConstructor', - args: [{ kind: 'number', name: 'precision', integer: true, optional: true }], - output: { - codecId: 'pg/timestamptz-string@1', - typeParams: { precision: { kind: 'arg', index: 0 } }, - }, - }, - DateString: { - kind: 'typeConstructor', - output: { codecId: 'pg/date-string@1' }, - }, - TimeString: { - kind: 'typeConstructor', - args: [{ kind: 'number', name: 'precision', integer: true, optional: true }], - output: { - codecId: 'pg/time-string@1', - typeParams: { precision: { kind: 'arg', index: 0 } }, - }, - }, - VarChar: { - kind: 'typeConstructor', - args: [{ kind: 'number', name: 'length', integer: true, optional: true }], - output: { - codecId: 'sql/varchar@1', - typeParams: { length: { kind: 'arg', index: 0 } }, - }, - }, - Numeric: { - kind: 'typeConstructor', - args: [ - { kind: 'number', name: 'precision', integer: true, optional: true }, - { kind: 'number', name: 'scale', integer: true, optional: true }, - ], - output: { - codecId: 'pg/numeric@1', - typeParams: { - precision: { kind: 'arg', index: 0 }, - scale: { kind: 'arg', index: 1 }, - }, - }, - }, - Json: { kind: 'typeConstructor', output: { codecId: 'pg/json@1' } }, - Jsonb: { kind: 'typeConstructor', output: { codecId: 'pg/jsonb@1' } }, -} as const satisfies AuthoringTypeNamespace; +const authoringTypes: AuthoringTypeNamespace = postgresScalarAndNativeTypeConstructors; const assembled = assembleAuthoringContributions([ { @@ -180,10 +125,10 @@ describe('Postgres PSL inference round trip', () => { expect(inferred).not.toContain('bareAmount Numeric()'); expect(inferred).toMatch(/jsonValue\s+Json/); expect(inferred).toMatch(/jsonbValue\s+Jsonb/); - expect(inferred).toMatch(/occurredAt\s+TimestamptzString\s/); - expect(inferred).toMatch(/loggedAt\s+TimestampString\(3\)/); - expect(inferred).toMatch(/dueOn\s+DateString\s/); - expect(inferred).toMatch(/opensAt\s+TimeString\s/); + expect(inferred).toMatch(/occurredAt\s+DateTime\(precision: 6\)\s/); + expect(inferred).toMatch(/loggedAt\s+DateTime\(storage: timestamp\)\s/); + expect(inferred).toMatch(/dueOn\s+Date\s/); + expect(inferred).toMatch(/opensAt\s+Time\s/); expect(inferred).toMatch(/label\s+VarChar\(191\)/); const emitted = parseAndEmit(inferred); @@ -237,13 +182,14 @@ describe('Postgres PSL inference round trip', () => { many: false, }, occurred_at: { - codecId: 'pg/timestamptz-string@1', + codecId: 'pg/timestamptz-date@1', dataType: 'pg/timestamptz', nullable: false, many: false, + typeParams: { precision: 6 }, }, logged_at: { - codecId: 'pg/timestamp-string@1', + codecId: 'pg/timestamp-date@1', dataType: 'pg/timestamp', nullable: false, many: false, diff --git a/packages/3-targets/postgres/1-target/test/psl-infer/inferred-psl.round-trip.test.ts b/packages/3-targets/postgres/1-target/test/psl-infer/inferred-psl.round-trip.test.ts index 288592a921d9..bbee676be935 100644 --- a/packages/3-targets/postgres/1-target/test/psl-infer/inferred-psl.round-trip.test.ts +++ b/packages/3-targets/postgres/1-target/test/psl-infer/inferred-psl.round-trip.test.ts @@ -198,11 +198,6 @@ two lines é'::text`, '2024-01-01T00:00:00.5', ], ['timestamptz', "'2024-01-01 01:00:00+00'::timestamp with time zone", '2024-01-01T01:00:00Z'], - [ - 'timestamptz(6)', - "'2024-01-01 01:00:00.123456+00'::timestamp(6) with time zone", - '2024-01-01T01:00:00.123456Z', - ], [ 'timestamptz', "'0044-03-15 00:00:00+00 BC'::timestamp with time zone", @@ -213,8 +208,6 @@ two lines é'::text`, ['time', "'12:34:56.5'::time without time zone", '12:34:56.5'], ['time(3)', "'12:34:56.123'::time(3) without time zone", '12:34:56.123'], ['timetz', "'12:34:56+02'::time with time zone", '12:34:56+02:00'], - ['timestamp', "'infinity'::timestamp without time zone", 'infinity'], - ['timestamptz', "'-infinity'::timestamp with time zone", '-infinity'], ['date', "'infinity'::date", 'infinity'], ])('round-trips a %s default of %s', async (typeText, rawDefault, value) => { expect(await roundTrippedDefaults([introspected('stamp', typeText, rawDefault)])).toEqual({ diff --git a/packages/3-targets/postgres/1-target/test/psl-infer/inferred-psl/inferred-psl.core.test.ts b/packages/3-targets/postgres/1-target/test/psl-infer/inferred-psl/inferred-psl.core.test.ts index 0e475669b238..c045c22aa27f 100644 --- a/packages/3-targets/postgres/1-target/test/psl-infer/inferred-psl/inferred-psl.core.test.ts +++ b/packages/3-targets/postgres/1-target/test/psl-infer/inferred-psl/inferred-psl.core.test.ts @@ -75,7 +75,7 @@ describe('printPsl', () => { // WARNING: This table has no primary key in the database model AuditLog { event String - timestamp TimestamptzString + timestamp DateTime(precision: 6) @@map("audit_log") } diff --git a/packages/3-targets/postgres/1-target/test/psl-infer/inferred-psl/inferred-psl.data-type-defaults.test.ts b/packages/3-targets/postgres/1-target/test/psl-infer/inferred-psl/inferred-psl.data-type-defaults.test.ts index e54f1c52dfd1..97f161bdda01 100644 --- a/packages/3-targets/postgres/1-target/test/psl-infer/inferred-psl/inferred-psl.data-type-defaults.test.ts +++ b/packages/3-targets/postgres/1-target/test/psl-infer/inferred-psl/inferred-psl.data-type-defaults.test.ts @@ -1,9 +1,12 @@ import { inferredTypeConstructors } from '@internal/family-sql/psl-build'; +import type { Codec } from '@internal/framework-components/codec'; import { type SqlColumnIRInput, SqlSchemaIR } from '@internal/sql-schema-ir/types'; import { ifDefined } from '@internal/utils/defined'; import { InternalError } from '@internal/utils/internal-error'; import { describe, expect, it } from 'vitest'; +import { pgTimestamp } from '../../../src/core/data-types'; import { inferredColumnDefaults } from '../../../src/core/psl-infer/infer-default-codec'; +import { readDefaultWithCodec } from '../../../src/core/psl-infer/infer-read-defaults'; import { inferBuildContext, printPslFromFlat } from '../fixtures'; /** The backtick fencing a tagged literal, as an escape so no quoted string in this file holds one. */ @@ -154,8 +157,67 @@ describe('printPsl writes each default as the literal the column data type takes ['infinity', 'timestamp', "'infinity'::timestamp without time zone"], ['-infinity', 'timestamp', "'-infinity'::timestamp without time zone"], ['infinity', 'timestamptz', "'infinity'::timestamp with time zone"], - ['-infinity', 'date', "'-infinity'::date"], + [ + 'microseconds', + 'timestamptz(6)', + "'2024-01-01 01:00:00.123456+00'::timestamp(6) with time zone", + ], ])( + 'prints a default of %s on a %s column, which a Date cannot hold, as its raw expression', + async (_value, typeText, rawDefault) => { + expect(await printedDefaults([introspected('stamp', typeText, rawDefault)])).toEqual({ + stamp: `@default(sql\`${rawDefault}\`)`, + }); + }, + ); + + it('prints a list default with an element a Date cannot hold as its raw expression, and one it holds as a literal list', async () => { + const micro = + "ARRAY['2024-01-01 01:00:00.123+00'::timestamp with time zone, '2024-01-01 01:00:00.123456+00'::timestamp with time zone]"; + const milli = + "ARRAY['2024-01-01 01:00:00.123+00'::timestamp with time zone, '2024-01-01 01:00:00.5+00'::timestamp with time zone]"; + expect( + await printedDefaults([ + introspected('micro', 'timestamptz(6)', micro, { many: true }), + introspected('milli', 'timestamptz(6)', milli, { many: true }), + ]), + ).toEqual({ + micro: `@default(sql\`${micro}\`)`, + milli: + '@default([timestamptz`2024-01-01T01:00:00.123Z`, timestamptz`2024-01-01T01:00:00.5Z`])', + }); + }); + + it('prints the raw expression when the column type does not read a body its codec read', async () => { + const codec = { + id: 'test/lenient-timestamp@1', + dataType: pgTimestamp, + fromWire: async () => new Date('2024-01-01T00:00:00Z'), + toDataTypeValue: () => pgTimestamp.valueFrom('2024-01-01T00:00:00', {}), + fromDataTypeValue: () => new Date('2024-01-01T00:00:00Z'), + toWire: async () => '', + } as unknown as Codec; + expect(await readDefaultWithCodec("'soon'::timestamp without time zone", false, codec)).toEqual( + { + kind: 'function', + expression: "'soon'::timestamp without time zone", + }, + ); + }); + + it('prints a timestamp default written with an offset as the instant, when a Date holds it', async () => { + expect( + await printedDefaults([ + introspected( + 'stamp', + 'timestamptz(6)', + "'2024-01-01 03:00:00.5+02'::timestamp with time zone", + ), + ]), + ).toEqual({ stamp: '@default(timestamptz`2024-01-01T01:00:00.5Z`)' }); + }); + + it.each([['-infinity', 'date', "'-infinity'::date"]])( 'prints the %s sentinel on a %s column with the column type tag', async (sentinel, typeText, rawDefault) => { expect(await printedDefaults([introspected('stamp', typeText, rawDefault)])).toEqual({ diff --git a/packages/3-targets/postgres/1-target/test/psl-infer/inferred-psl/inferred-psl.defaults-and-types.test.ts b/packages/3-targets/postgres/1-target/test/psl-infer/inferred-psl/inferred-psl.defaults-and-types.test.ts index 1dc6d14ea43d..98cb5f5c9be4 100644 --- a/packages/3-targets/postgres/1-target/test/psl-infer/inferred-psl/inferred-psl.defaults-and-types.test.ts +++ b/packages/3-targets/postgres/1-target/test/psl-infer/inferred-psl/inferred-psl.defaults-and-types.test.ts @@ -53,11 +53,11 @@ describe('printPsl', () => { // Contract inferred from the live database schema. Edit as needed, then run \`prisma contract emit\`. model Post { - id Int @id @default(autoincrement()) - title String @default("Untitled") - isPublished Boolean @default(false) @map("is_published") - viewCount Int @default(0) @map("view_count") - createdAt TimestamptzString @default(now()) @map("created_at") + id Int @id @default(autoincrement()) + title String @default("Untitled") + isPublished Boolean @default(false) @map("is_published") + viewCount Int @default(0) @map("view_count") + createdAt DateTime(precision: 6) @default(now()) @map("created_at") @@map("post") } @@ -374,9 +374,9 @@ describe('printPsl', () => { // Contract inferred from the live database schema. Edit as needed, then run \`prisma contract emit\`. model Schedule { - id Uuid @id - bookedOn DateString @map("booked_on") - slot TimeString(3) + id Uuid @id + bookedOn Date @map("booked_on") + slot Time(precision: 3) rating SmallInt payload Json @@ -425,10 +425,10 @@ describe('printPsl', () => { // Contract inferred from the live database schema. Edit as needed, then run \`prisma contract emit\`. model Data { - id Int @id - computed String @default(sql\`my_custom_func()\`) - payload Jsonb @default(json\`{}\`) - touchedAt TimestamptzString @default(sql\`clock_timestamp()\`) @map("touched_at") + id Int @id + computed String @default(sql\`my_custom_func()\`) + payload Jsonb @default(json\`{}\`) + touchedAt DateTime(precision: 6) @default(sql\`clock_timestamp()\`) @map("touched_at") @@map("data") } diff --git a/packages/3-targets/postgres/1-target/test/psl-infer/inferred-psl/inferred-psl.literal-defaults.test.ts b/packages/3-targets/postgres/1-target/test/psl-infer/inferred-psl/inferred-psl.literal-defaults.test.ts index 373d0005253a..6a6c9a420388 100644 --- a/packages/3-targets/postgres/1-target/test/psl-infer/inferred-psl/inferred-psl.literal-defaults.test.ts +++ b/packages/3-targets/postgres/1-target/test/psl-infer/inferred-psl/inferred-psl.literal-defaults.test.ts @@ -110,11 +110,11 @@ describe('printPsl literal defaults', () => { // Contract inferred from the live database schema. Edit as needed, then run \`prisma contract emit\`. model RawDefaults { - id Int @id - stamp TimestampString(3) @default(timestamp\`2024-01-01T00:00:00\`) - day DateString @default(date\`2024-01-01\`) - jsonNull Jsonb? @default(json\`null\`) - textNull VarChar(32)? @default(sql\`NULL::character varying\`) + id Int @id + stamp DateTime(storage: timestamp) @default(timestamp\`2024-01-01T00:00:00\`) + day Date @default(date\`2024-01-01\`) + jsonNull Jsonb? @default(json\`null\`) + textNull VarChar(32)? @default(sql\`NULL::character varying\`) @@map("raw_defaults") } @@ -158,12 +158,12 @@ describe('printPsl literal defaults', () => { // Contract inferred from the live database schema. Edit as needed, then run \`prisma contract emit\`. model SpecialValueDefaults { - id Int @id - floatNaN Float @default(NaN) - floatNegInf Float @default(-Infinity) - realNaN Real @default(NaN) - decimalNaN Numeric @default(NaN) - timeWithZone Timetz @default(timetz\`12:34:56Z\`) + id Int @id + floatNaN Float @default(NaN) + floatNegInf Float @default(-Infinity) + realNaN Real @default(NaN) + decimalNaN Numeric @default(NaN) + timeWithZone Time(storage: timetz) @default(timetz\`12:34:56Z\`) @@map("special_value_defaults") } @@ -254,8 +254,8 @@ describe('printPsl literal defaults', () => { // Contract inferred from the live database schema. Edit as needed, then run \`prisma contract emit\`. model RawListDefaults { - id Int @id - timestamps TimestampString(3)?[]? @default([timestamp\`2024-01-01T00:00:00\`]) + id Int @id + timestamps DateTime(storage: timestamp)?[]? @default([timestamp\`2024-01-01T00:00:00\`]) @@map("raw_list_defaults") } diff --git a/packages/3-targets/postgres/1-target/test/psl-print/convenience-types.test.ts b/packages/3-targets/postgres/1-target/test/psl-print/convenience-types.test.ts new file mode 100644 index 000000000000..0d16520ca914 --- /dev/null +++ b/packages/3-targets/postgres/1-target/test/psl-print/convenience-types.test.ts @@ -0,0 +1,52 @@ +import { describe, expect, it } from 'vitest'; +import { buildModels, type ColumnShape, fieldText, INT_COLUMN, table } from './print-support'; + +function printedType(column: ColumnShape): string | undefined { + const [model] = buildModels({ + models: { + Event: { table: 'Event', fields: { id: { column: 'id' }, at: { column: 'at' } } }, + }, + tables: { + Event: table({ columns: { id: INT_COLUMN, at: column }, primaryKey: { columns: ['id'] } }), + }, + }); + const field = model?.fields[1]; + return field === undefined ? undefined : fieldText(field).replace(/^at /, ''); +} + +const column = (dataType: string, codecId: string, precision?: number): ColumnShape => ({ + dataType, + codecId, + nullable: false, + ...(precision === undefined ? {} : { typeParams: { precision } }), +}); + +describe('contract print writes a date or time column as the convenience type it becomes', () => { + it.each([ + [column('pg/timestamptz', 'pg/timestamptz-date@1', 3), 'DateTime'], + [column('pg/timestamptz', 'pg/timestamptz-date@1', 6), 'DateTime(precision: 6)'], + [column('pg/timestamp', 'pg/timestamp-date@1', 3), 'DateTime(storage: timestamp)'], + [column('pg/timestamptz', 'pg/timestamptz-temporal@1'), 'DateTime(as: temporal)'], + [column('pg/timestamptz', 'pg/timestamptz-string@1', 3), 'DateTime(as: string, precision: 3)'], + [ + column('pg/timestamp', 'pg/timestamp-instant@1'), + 'DateTime(as: temporal, storage: timestamp)', + ], + [column('pg/date', 'pg/date-string@1'), 'Date'], + [column('pg/date', 'pg/date-temporal@1'), 'Date(as: temporal)'], + [column('pg/time', 'pg/time-string@1', 0), 'Time(precision: 0)'], + [column('pg/timetz', 'pg/timetz@1'), 'Time(storage: timetz)'], + [column('pg/timetz', 'pg/timetz@1', 3), 'Time(storage: timetz, precision: 3)'], + [column('pg/interval', 'pg/interval-string@1'), 'Duration'], + [column('pg/interval', 'pg/interval@1'), 'Duration(as: parts)'], + ])('%j prints as %s', (shape, expected) => { + expect(printedType(shape)).toBe(expected); + }); + + it('writes the explicit type for a column no convenience call becomes exactly', () => { + expect([ + printedType(column('pg/timestamp', 'pg/timestamp-temporal@1', 3)), + printedType(column('pg/timestamptz', 'pg/timestamptz-date@1')), + ]).toEqual(['TimestampTemporal(3)', 'TimestamptzJsDate']); + }); +}); diff --git a/packages/3-targets/postgres/1-target/test/psl-print/generated-values.test.ts b/packages/3-targets/postgres/1-target/test/psl-print/generated-values.test.ts index 5a3d69c2c60c..2c4241ff07d7 100644 --- a/packages/3-targets/postgres/1-target/test/psl-print/generated-values.test.ts +++ b/packages/3-targets/postgres/1-target/test/psl-print/generated-values.test.ts @@ -124,13 +124,13 @@ describe('generated values', () => { it('writes a now generator on create and on update', () => { const now = generator(postgresNowGeneratorIds[PG_TIMESTAMPTZ_TEMPORAL_CODEC_ID]); expect(withGenerator(timestamptz, { onCreate: now, onUpdate: now })).toBe( - 'value TimestamptzTemporal @onCreate(now()) @onUpdate(now())', + 'value DateTime(as: temporal) @onCreate(now()) @onUpdate(now())', ); }); it("writes a time now generator whose precision is the column's as @onUpdate(now())", () => { expect(withGenerator(time3, { onUpdate: generator('plainTimeNow', { precision: 3 }) })).toBe( - 'value TimeTemporal(3) @onUpdate(now())', + 'value Time(as: temporal, precision: 3) @onUpdate(now())', ); }); @@ -140,7 +140,7 @@ describe('generated values', () => { { ...timestamptz, default: { kind: 'function', expression: 'now()' } }, { onCreate: generator('instantNow') }, ), - ).toBe('value TimestamptzTemporal @default(now()) @onCreate(now())'); + ).toBe('value DateTime(as: temporal) @default(now()) @onCreate(now())'); }); it('writes a generator through the generator function an extension contributes', () => { diff --git a/packages/3-targets/postgres/1-target/test/temporal-unavailable.test.ts b/packages/3-targets/postgres/1-target/test/temporal-unavailable.test.ts index 40894f7a604a..ac7b8a22c953 100644 --- a/packages/3-targets/postgres/1-target/test/temporal-unavailable.test.ts +++ b/packages/3-targets/postgres/1-target/test/temporal-unavailable.test.ts @@ -102,7 +102,7 @@ describe('Temporal-backed codecs in a runtime without Temporal', () => { expect(decoded).toBe('infinity'); }); - it('fails the instantNow generator with the capability error, naming its *String alternative', async () => { + it('fails the instantNow generator with the capability error, naming the convenience default', async () => { const outcome = await withoutTemporal(async () => { await Promise.resolve(); try { @@ -113,7 +113,9 @@ describe('Temporal-backed codecs in a runtime without Temporal', () => { return { threw: true, code: structured.code, - namesTheStringPreset: (structured.fix ?? '').includes('updatedAtString'), + namesTheConvenienceDefault: (structured.fix ?? '').includes( + '`DateTime` reads a JavaScript `Date`', + ), meta: structured.meta, }; } @@ -122,7 +124,7 @@ describe('Temporal-backed codecs in a runtime without Temporal', () => { expect(outcome).toEqual({ threw: true, code: 'RUNTIME.TEMPORAL_UNAVAILABLE', - namesTheStringPreset: true, + namesTheConvenienceDefault: true, meta: { generatorId: 'instantNow' }, }); }); diff --git a/packages/3-targets/postgres/1-target/test/type-constructors.test.ts b/packages/3-targets/postgres/1-target/test/type-constructors.test.ts index 8542893aa8ac..c6f9528ef5b1 100644 --- a/packages/3-targets/postgres/1-target/test/type-constructors.test.ts +++ b/packages/3-targets/postgres/1-target/test/type-constructors.test.ts @@ -2,6 +2,7 @@ import type { AuthoringTypeNamespace } from '@internal/framework-components/auth import { collectScalarTypeConstructors, findAuthoringTypeConstructorCall, + instantiateAuthoringTypeCall, instantiateAuthoringTypeConstructor, validateAuthoringTypeParams, } from '@internal/framework-components/authoring'; @@ -74,21 +75,18 @@ describe('the type constructors the target contributes', () => { ), ).toEqual({ CreatedAt: - 'A timestamp the client sets to now when a row is created. Its value is a Temporal.Instant.', - UpdatedAt: - 'A timestamp the client sets to now when a row is created and every time it is updated. Its value is a Temporal.Instant.', - CreatedAtString: - 'A timestamp the client sets to now when a row is created. Its value is a string, the timestamp as Postgres writes it, such as 2024-01-02 03:04:05+00.', - UpdatedAtString: - 'A timestamp the client sets to now when a row is created and every time it is updated. Its value is a string, the timestamp as Postgres writes it, such as 2024-01-02 03:04:05+00.', - CreatedAtJsDate: 'A timestamp the client sets to now when a row is created. Its value is a JavaScript Date.', - UpdatedAtJsDate: + UpdatedAt: 'A timestamp the client sets to now when a row is created and every time it is updated. Its value is a JavaScript Date.', }); }); - it('contribute the temporal type aliases, one pair per timestamptz codec', () => { + it('contribute the temporal type aliases on the type bare DateTime becomes', () => { + const dateTime = instantiateAuthoringTypeCall( + postgresScalarAndNativeTypeConstructors, + 'DateTime', + [], + ); expect( Object.fromEntries( Object.entries(postgresAuthoringTypes.temporal).map(([name, { kind, output }]) => [ @@ -97,40 +95,10 @@ describe('the type constructors the target contributes', () => { ]), ), ).toEqual({ - CreatedAt: { - kind: 'typeAlias', - type: { codecId: 'pg/timestamptz-temporal@1' }, - onCreate: now, - }, - UpdatedAt: { - kind: 'typeAlias', - type: { codecId: 'pg/timestamptz-temporal@1' }, - onCreate: now, - onUpdate: now, - }, - CreatedAtString: { - kind: 'typeAlias', - type: { codecId: 'pg/timestamptz-string@1' }, - onCreate: now, - }, - UpdatedAtString: { - kind: 'typeAlias', - type: { codecId: 'pg/timestamptz-string@1' }, - onCreate: now, - onUpdate: now, - }, - CreatedAtJsDate: { - kind: 'typeAlias', - type: { codecId: 'pg/timestamptz-date@1' }, - onCreate: now, - }, - UpdatedAtJsDate: { - kind: 'typeAlias', - type: { codecId: 'pg/timestamptz-date@1' }, - onCreate: now, - onUpdate: now, - }, + CreatedAt: { kind: 'typeAlias', type: dateTime, onCreate: now }, + UpdatedAt: { kind: 'typeAlias', type: dateTime, onCreate: now, onUpdate: now }, }); + expect(dateTime).toEqual({ codecId: 'pg/timestamptz-date@1', typeParams: { precision: 3 } }); }); }); @@ -161,11 +129,11 @@ const INFERRED = [ 'DateString', 'TimeString', 'Timetz', - 'TimestampString', - 'TimestamptzString', + 'TimestampJsDate', + 'TimestamptzJsDate', 'Bit', 'VarBit', - 'IntervalParts', + 'IntervalString', 'pg.enum', ]; @@ -414,6 +382,7 @@ describe('postgresNativeAuthoringTypes', () => { expect(everyPostgresConstructor).not.toHaveProperty('TimestamptzDate'); expect(postgresNativeAuthoringTypes).toHaveProperty('TimestamptzJsDate', { kind: 'typeConstructor', + inferred: true, documentation: 'An instant stored as PostgreSQL timestamptz and represented as a JavaScript Date.', args: [{ kind: 'value', dataType: 'pg/int4', name: 'precision', optional: true }], diff --git a/packages/3-targets/postgres/2-adapter/test/migrations/add-date-time-column-postcheck.integration.test.ts b/packages/3-targets/postgres/2-adapter/test/migrations/add-date-time-column-postcheck.integration.test.ts new file mode 100644 index 000000000000..09485eca05b2 --- /dev/null +++ b/packages/3-targets/postgres/2-adapter/test/migrations/add-date-time-column-postcheck.integration.test.ts @@ -0,0 +1,72 @@ +/** + * Adding a date or time column with no declared precision passes its postcheck: the check compares `format_type` with the text PostgreSQL prints for the type as written, though the type's normal form counts the omitted precision as 6. + */ +import { UNBOUND_NAMESPACE_ID } from '@internal/framework-components/ir'; +import { col } from '@internal/sql-relational-core/contract-free'; +import { postgresDataTypes } from '@internal/target-postgres/data-types'; +import { AddColumnCall } from '@internal/target-postgres/op-factory-call'; +import { afterAll, afterEach, beforeAll, beforeEach, describe, expect, it } from 'vitest'; +import { + controlAdapter, + createDriver, + createTestDatabase, + type PostgresControlDriver, + resetDatabase, + testTimeout, +} from './fixtures/runner-fixtures'; + +describe('the ADD COLUMN postcheck of a date or time column', { concurrent: false }, () => { + let database: Awaited>; + let driver: PostgresControlDriver | undefined; + + beforeAll(async () => { + database = await createTestDatabase(); + }, testTimeout); + + afterAll(async () => { + if (database) await database.close(); + }, testTimeout); + + beforeEach(async () => { + driver = await createDriver(database.connectionString); + await resetDatabase(driver); + }, testTimeout); + + afterEach(async () => { + if (driver) { + await driver.close(); + driver = undefined; + } + }, testTimeout); + + async function addColumn(type: string) { + await driver!.query('DROP TABLE IF EXISTS "t"'); + await driver!.query('CREATE TABLE "t" (id int4 PRIMARY KEY)'); + const op = await new AddColumnCall( + UNBOUND_NAMESPACE_ID, + 't', + col('c', type), + postgresDataTypes, + ).toOp(controlAdapter); + for (const step of op.execute) await driver!.query(step.sql, step.params ?? []); + const results = await Promise.all( + op.postcheck.map((step) => driver!.query<{ result: boolean }>(step.sql, step.params ?? [])), + ); + return results.every((result) => result.rows[0]?.result === true); + } + + it('holds with and without a declared precision', { timeout: testTimeout * 2 }, async () => { + const types = [ + 'timestamptz', + 'timestamptz(6)', + 'timestamp', + 'time', + 'timetz', + 'interval', + 'timestamptz(3)', + ]; + const results: Record = {}; + for (const type of types) results[type] = await addColumn(type); + expect(results).toEqual(Object.fromEntries(types.map((type) => [type, true]))); + }); +}); diff --git a/packages/3-targets/postgres/9-facade/test/contract-builder/type-helpers.test-d.ts b/packages/3-targets/postgres/9-facade/test/contract-builder/type-helpers.test-d.ts index 959d05da48df..291ab9922234 100644 --- a/packages/3-targets/postgres/9-facade/test/contract-builder/type-helpers.test-d.ts +++ b/packages/3-targets/postgres/9-facade/test/contract-builder/type-helpers.test-d.ts @@ -44,12 +44,12 @@ type GeneratesOnCreate = Builder extends { readonly __state: infer Stat test('a field typed by temporal.CreatedAt is optional on create', () => { defineContract({}, ({ type, field }) => { const createdAt = field.column(type.temporal.CreatedAt()); - const updatedAt = field.column(type.temporal.UpdatedAtString()).optional(); + const updatedAt = field.column(type.temporal.UpdatedAt()).optional(); const plain = field.column(type.TimestamptzTemporal()); expectTypeOf>().toEqualTypeOf(); expectTypeOf>().toEqualTypeOf(); expectTypeOf>().toEqualTypeOf(); - expectTypeOf(type.temporal.CreatedAtJsDate().codecId).toEqualTypeOf<'pg/timestamptz-date@1'>(); + expectTypeOf(type.temporal.CreatedAt().codecId).toEqualTypeOf<'pg/timestamptz-date@1'>(); return { models: {} }; }); }); diff --git a/projects/datetime-convenience-types/plan.md b/projects/datetime-convenience-types/plan.md index a880bc868de7..e950e732b29e 100644 --- a/projects/datetime-convenience-types/plan.md +++ b/projects/datetime-convenience-types/plan.md @@ -42,6 +42,10 @@ A type constructor's `codecId` may be a `select` over `option` arguments; every - Slices 1 and 4 both touch what string date and time values look like. The convenience-types journey (`convenience-date-time-types-without-temporal.e2e.test.ts`) asserts today's display text for `textAt` (`2024-02-29 10:00:00.123456+00`) and `localAt` (`09:30:00+02`). Whichever of the two slices merges second changes them to `2024-02-29T10:00:00.123456Z` and `09:30:00+02:00`. +## Cross-slice notes + +- Slices 1 and 4 both touch what string date and time values look like. The convenience-types journey (`convenience-date-time-types-without-temporal.e2e.test.ts`) asserts today's display text for `textAt` (`2024-02-29 10:00:00.123456+00`) and `localAt` (`09:30:00+02`). Whichever of the two slices merges second changes them to `2024-02-29T10:00:00.123456Z` and `09:30:00+02:00`. + ## Dependencies - **Field-preset removal project, slice 2.** Delivers the `type.*` helpers that expose slice 4's types in TypeScript, and the contributed aliases built on `DateTime`. Not blocking; coordinate so its `temporal.CreatedAt` and `temporal.UpdatedAt` land on `DateTime` once slice 4 merges. diff --git a/scripts/framework-vocabulary-thresholds.json b/scripts/framework-vocabulary-thresholds.json index f6b04660dea7..dd42b1e69892 100644 --- a/scripts/framework-vocabulary-thresholds.json +++ b/scripts/framework-vocabulary-thresholds.json @@ -2,7 +2,7 @@ "scopes": [ { "path": "packages/1-framework", - "threshold": 236 + "threshold": 237 } ] } diff --git a/skills/prisma-8/references/contract.md b/skills/prisma-8/references/contract.md index b919afa36d19..6e9514bec914 100644 --- a/skills/prisma-8/references/contract.md +++ b/skills/prisma-8/references/contract.md @@ -158,7 +158,7 @@ model User { } ``` -An alias is expanded into each field that uses it: the column stores the alias's codec and type params, and the contract stores no alias. An alias may carry `@default`, `@onCreate` and `@onUpdate`, which every field typed by it takes: `UpdatedAt = TimestampString(3) @onCreate(now()) @onUpdate(now())`. It may not carry `@id` or `@unique`; write those on the field. A field may add attributes its alias does not set (`updatedAt UpdatedAt @default(now())`), but repeating one the alias sets is `PSL_DUPLICATE_ATTRIBUTE`. An alias may not name another alias. The targets contribute `temporal.CreatedAt` (generated on create) and `temporal.UpdatedAt` (on create and on update), written bare: `createdAt temporal.CreatedAt`. On Postgres they are `TimestamptzTemporal` columns, with `temporal.CreatedAtString` / `UpdatedAtString` and `CreatedAtJsDate` / `UpdatedAtJsDate` variants; on SQLite `DateTime`; on Mongo `Date`. They take no arguments; for a precision, write the type and attributes, as in `TimestamptzTemporal(3) @onCreate(now())`. +An alias is expanded into each field that uses it: the column stores the alias's codec and type params, and the contract stores no alias. An alias may carry `@default`, `@onCreate` and `@onUpdate`, which every field typed by it takes: `UpdatedAt = TimestampString(3) @onCreate(now()) @onUpdate(now())`. It may not carry `@id` or `@unique`; write those on the field. A field may add attributes its alias does not set (`updatedAt UpdatedAt @default(now())`), but repeating one the alias sets is `PSL_DUPLICATE_ATTRIBUTE`. An alias may not name another alias. The targets contribute `temporal.CreatedAt` (generated on create) and `temporal.UpdatedAt` (on create and on update), written bare: `createdAt temporal.CreatedAt`. On Postgres they are `DateTime` with its defaults, a `timestamptz(3)` column read as a JavaScript `Date`; on SQLite `DateTime`; on Mongo `Date`. They take no arguments; for another kind of value or a precision, write the type and attributes, as in `DateTime(as: string) @onCreate(now())`. Values the client generates are written `@onCreate(fn())` and `@onUpdate(fn())`: `id String @id @onCreate(uuid(7))`, `slug String @onCreate(nanoid(12))`, `updatedAt DateTime @onCreate(now()) @onUpdate(now())`. `@default(...)` is a database default only (`now()`, `autoincrement()`, a literal, or `` sql`...` ``); `@default(uuid())` is refused with `PSL_GENERATOR_IN_DEFAULT`. In the TS builder, write `.onCreate(gen.uuid(7))` and `.onUpdate(gen.now())`. Column defaults and generators can sit on one field: `@default(now()) @onCreate(now())`. diff --git a/skills/prisma-8/references/quickstart.md b/skills/prisma-8/references/quickstart.md index 16d6490e2c3a..710122936f9b 100644 --- a/skills/prisma-8/references/quickstart.md +++ b/skills/prisma-8/references/quickstart.md @@ -219,7 +219,7 @@ Then run the snippet from *Your first arc* above against the `User` model. When ## Workflow — Brownfield-DB (existing database, no contract) -The concept: against an existing database with no PN contract, `contract infer` walks the live schema (tables, columns, indexes — including expression and partial ones — constraints, and RLS enablement + policies) and writes a PSL contract that describes it. Where authoring would generate a CHECK constraint the database does not carry (the element-non-null check on a list column), infer writes the list with nullable elements (`String?[]`), which derives no such check, so the contract declares exactly what the database enforces. The reverse gap is closed too: a hand-written CHECK constraint the database enforces that authoring would never have generated comes back as `@@check(expression: , map: "")`, so it is a declared object from the first pull instead of an invisible extra a later destructive plan could drop. That inferred check warns (`PN_EXACT_NAME_BODY_COMPARISON`) the next time you run `contract emit` — expected, not a defect: the warning fires on any `map:` body regardless of who wrote it, and the comparison stays sound because both sides are Postgres's own reprint. The result is a *starting point*, not the final contract — review and clean it up, then `db sign` to record the current contract hash as the marker (instead of letting `db init` try to recreate the schema from scratch). By default `db sign` also sets the `db` ref and stores the contract snapshot, so the next `migration plan` chains from the adopted schema — even when the database is named with `--db`. `--advance-ref ` writes another ref instead and `--no-advance-ref` writes none; in either case the next plan needs `--from` or a `db` ref you set yourself. +The concept: against an existing database with no PN contract, `contract infer` walks the live schema (tables, columns, indexes — including expression and partial ones — constraints, and RLS enablement + policies) and writes a PSL contract that describes it. Where authoring would generate a CHECK constraint the database does not carry (the element-non-null check on a list column), infer writes the list with nullable elements (`String?[]`), which derives no such check, so the contract declares exactly what the database enforces. The reverse gap is closed too: a hand-written CHECK constraint the database enforces that authoring would never have generated comes back as `@@check(expression: , map: "")`, so it is a declared object from the first pull instead of an invisible extra a later destructive plan could drop. That inferred check warns (`PN_EXACT_NAME_BODY_COMPARISON`) the next time you run `contract emit` — expected, not a defect: the warning fires on any `map:` body regardless of who wrote it, and the comparison stays sound because both sides are Postgres's own reprint. Date and time columns come back as the convenience types with their defaults, and only the arguments that differ: `timestamptz(3)` as `DateTime` (a JavaScript `Date`), a `timestamptz` with no declared precision as `DateTime(precision: 6)`, `timestamp(3)` as `DateTime(storage: timestamp)`, `date` as `Date`, `time` as `Time` and `interval` as `Duration` (ISO strings). A `Date` holds milliseconds, so a timestamp column whose values use microseconds reads with them dropped: change such a field to `DateTime(as: temporal)` or `DateTime(as: string)` to keep them. The result is a *starting point*, not the final contract — review and clean it up, then `db sign` to record the current contract hash as the marker (instead of letting `db init` try to recreate the schema from scratch). By default `db sign` also sets the `db` ref and stores the contract snapshot, so the next `migration plan` chains from the adopted schema — even when the database is named with `--db`. `--advance-ref ` writes another ref instead and `--no-advance-ref` writes none; in either case the next plan needs `--from` or a `db` ref you set yourself. ```bash mkdir my-app && cd my-app diff --git a/test/integration/test/authoring/parity/temporal-type-aliases.real-packs.test.ts b/test/integration/test/authoring/parity/temporal-type-aliases.real-packs.test.ts index 2e35d293798e..f191ccf3c13b 100644 --- a/test/integration/test/authoring/parity/temporal-type-aliases.real-packs.test.ts +++ b/test/integration/test/authoring/parity/temporal-type-aliases.real-packs.test.ts @@ -73,33 +73,25 @@ function succeeded( } describe('the temporal type aliases with real packs', () => { - it('store on Postgres exactly what the matching presets store', () => { + it('store on Postgres exactly what DateTime with the matching generators stores', () => { const throughAliases = succeeded( interpretPostgres(`model Event { - id Int @id - createdAt temporal.CreatedAt - updatedAt temporal.UpdatedAt - createdAtString temporal.CreatedAtString - updatedAtString temporal.UpdatedAtString - createdAtJsDate temporal.CreatedAtJsDate - updatedAtJsDate temporal.UpdatedAtJsDate + id Int @id + createdAt temporal.CreatedAt + updatedAt temporal.UpdatedAt } `), ); - const throughPresets = succeeded( + const throughAttributes = succeeded( interpretPostgres(`model Event { - id Int @id - createdAt temporal.createdAt() - updatedAt temporal.updatedAt() - createdAtString temporal.createdAtString() - updatedAtString temporal.updatedAtString() - createdAtJsDate temporal.createdAtJsDate() - updatedAtJsDate temporal.updatedAtJsDate() + id Int @id + createdAt DateTime @onCreate(now()) + updatedAt DateTime @onCreate(now()) @onUpdate(now()) } `), ); - expect(throughAliases).toEqual(throughPresets); - expect(throughAliases.execution?.mutations.defaults).toHaveLength(6); + expect(throughAliases).toEqual(throughAttributes); + expect(throughAliases.execution?.mutations.defaults).toHaveLength(2); }); it('store on SQLite exactly what the matching presets store', () => { @@ -123,7 +115,7 @@ describe('the temporal type aliases with real packs', () => { expect(throughAliases.execution?.mutations.defaults).toHaveLength(2); }); - it('give the TypeScript builder on Postgres what the presets give it', () => { + it('give the TypeScript builder on Postgres what DateTime with the matching generators gives it', () => { const throughAliases = defineContract({}, ({ type, field, model }) => ({ models: { Event: model('Event', { @@ -131,40 +123,28 @@ describe('the temporal type aliases with real packs', () => { id: field.column(type.Int()).id(), createdAt: field.column(type.temporal.CreatedAt()), updatedAt: field.column(type.temporal.UpdatedAt()), - createdAtString: field.column(type.temporal.CreatedAtString()), - updatedAtString: field.column(type.temporal.UpdatedAtString()), - createdAtJsDate: field.column(type.temporal.CreatedAtJsDate()), - updatedAtJsDate: field.column(type.temporal.UpdatedAtJsDate()), }, }).sql({ table: 'Event' }), }, })); - const throughPresets = defineContract({}, ({ type, field, model }) => ({ + const throughGenerators = defineContract({}, ({ type, field, model, gen }) => ({ models: { Event: model('Event', { fields: { id: field.column(type.Int()).id(), - createdAt: field.temporal.createdAt(), - updatedAt: field.temporal.updatedAt(), - createdAtString: field.temporal.createdAtString(), - updatedAtString: field.temporal.updatedAtString(), - createdAtJsDate: field.temporal.createdAtJsDate(), - updatedAtJsDate: field.temporal.updatedAtJsDate(), + createdAt: field.column(type.DateTime()).onCreate(gen.now()), + updatedAt: field.column(type.DateTime()).onCreate(gen.now()).onUpdate(gen.now()), }, }).sql({ table: 'Event' }), }, })); - expect(throughAliases).toEqual(throughPresets); + expect(throughAliases).toEqual(throughGenerators); expect(throughAliases).toEqual( succeeded( interpretPostgres(`model Event { - id Int @id - createdAt temporal.CreatedAt - updatedAt temporal.UpdatedAt - createdAtString temporal.CreatedAtString - updatedAtString temporal.UpdatedAtString - createdAtJsDate temporal.CreatedAtJsDate - updatedAtJsDate temporal.UpdatedAtJsDate + id Int @id + createdAt temporal.CreatedAt + updatedAt temporal.UpdatedAt } `), ), diff --git a/test/integration/test/cli-journeys/control-plane-commands-without-temporal.e2e.test.ts b/test/integration/test/cli-journeys/control-plane-commands-without-temporal.e2e.test.ts index 5cb11df307de..986aa7605fc3 100644 --- a/test/integration/test/cli-journeys/control-plane-commands-without-temporal.e2e.test.ts +++ b/test/integration/test/cli-journeys/control-plane-commands-without-temporal.e2e.test.ts @@ -372,11 +372,9 @@ withTempDir(({ createTempDir }) => { const field = readFileSync(join(ctx.testDir, 'contract.prisma'), 'utf-8') .split('\n') .find((line) => line.trim().startsWith('localAt')); - expect(field?.trim().split(/\s+/)).toEqual([ - 'localAt', - 'TimestampString(3)', - '@default(timestamp`2024-01-01T00:00:00`)', - ]); + expect(field?.trim().replace(/\s+/g, ' ')).toBe( + 'localAt DateTime(storage: timestamp) @default(timestamp`2024-01-01T00:00:00`)', + ); }, timeouts.spinUpPpgDev, ); diff --git a/test/integration/test/cli-journeys/date-time-default-canonical-form.e2e.test.ts b/test/integration/test/cli-journeys/date-time-default-canonical-form.e2e.test.ts index c8b056b91a34..f2eb7d0ec2d7 100644 --- a/test/integration/test/cli-journeys/date-time-default-canonical-form.e2e.test.ts +++ b/test/integration/test/cli-journeys/date-time-default-canonical-form.e2e.test.ts @@ -124,14 +124,14 @@ withTempDir(({ createTempDir }) => { const infer = await runContractInfer(ctx); expect(infer.exitCode, `contract infer\n${output(infer)}`).toBe(0); expect(printedDefaultFields(ctx)).toEqual([ - 'a TimestamptzString @default(timestamptz`2024-01-01T00:00:00Z`)', - 'b TimestamptzString @default(timestamptz`2024-01-01T00:00:00Z`)', - 'c TimestamptzString @default(timestamptz`2024-01-01T00:00:00Z`)', - 'clock TimeString @default(time`12:34:00`)', - 'day DateString @default(date`2024-01-01`)', - 'history TimestamptzString[] @default([timestamptz`2024-01-01T00:00:00Z`, timestamptz`2024-06-30T12:34:56Z`])', - 'localAt TimestampString(3) @default(timestamp`2024-01-01T12:34:56.5`)', - 'zoned Timetz @default(timetz`12:34:56+02:00`)', + 'a DateTime(precision: 6) @default(timestamptz`2024-01-01T00:00:00Z`)', + 'b DateTime(precision: 6) @default(timestamptz`2024-01-01T00:00:00Z`)', + 'c DateTime(precision: 6) @default(timestamptz`2024-01-01T00:00:00Z`)', + 'clock Time @default(time`12:34:00`)', + 'day Date @default(date`2024-01-01`)', + 'history DateTime(precision: 6)[] @default([timestamptz`2024-01-01T00:00:00Z`, timestamptz`2024-06-30T12:34:56Z`])', + 'localAt DateTime(storage: timestamp) @default(timestamp`2024-01-01T12:34:56.5`)', + 'zoned Time(storage: timetz) @default(timetz`12:34:56+02:00`)', ]); const emitInferred = await runContractEmit(ctx); diff --git a/test/integration/test/cli-journeys/infer-roundtrip-fidelity.brace-array-defaults.e2e.test.ts b/test/integration/test/cli-journeys/infer-roundtrip-fidelity.brace-array-defaults.e2e.test.ts index a2c9159fe933..cf4e40fee763 100644 --- a/test/integration/test/cli-journeys/infer-roundtrip-fidelity.brace-array-defaults.e2e.test.ts +++ b/test/integration/test/cli-journeys/infer-roundtrip-fidelity.brace-array-defaults.e2e.test.ts @@ -69,7 +69,7 @@ withTempDir(({ createTempDir }) => { flags Boolean?[]? @default([true, false]) roles pg.enum(Role)?[]? @default(["USER"]) names VarChar?[]? @default(["x"]) - days DateString?[]? @default([date\`2024-01-01\`]) + days Date?[]? @default([date\`2024-01-01\`]) @@map("brace_defaults") } diff --git a/test/integration/test/cli-journeys/infer-roundtrip-fidelity.prisma7-defaults.e2e.test.ts b/test/integration/test/cli-journeys/infer-roundtrip-fidelity.prisma7-defaults.e2e.test.ts index 3827eaa9d92f..95350f66a9b7 100644 --- a/test/integration/test/cli-journeys/infer-roundtrip-fidelity.prisma7-defaults.e2e.test.ts +++ b/test/integration/test/cli-journeys/infer-roundtrip-fidelity.prisma7-defaults.e2e.test.ts @@ -206,38 +206,38 @@ withTempDir(({ createTempDir }) => { } model NumberDefaults { - id Int @id(map: "number_defaults_pkey") - negInt Int @default(-1) - negSmallInt SmallInt @default(-2) - negFloat Float @default(-1.5) - tinyFloat Float @default(0.0000001) - negReal Real @default(-2.5) - negDecimal Numeric(65, 30) @default(-0.5) - longDecimal Numeric(65, 30) @default(12345678901234567890.123456789) - tinyDecimal Numeric(65, 30) @default(0.000000000000000001) - scaleDecimal Numeric(65, 30) @default(1.5) - wholeDecimal Numeric(65, 30) @default(10) - scaledDecimal Numeric(10, 2) @default(-1.25) - negSafeBigInt BigInt @default(-5) - negBigInt BigInt @default(-9007199254740993) - hugeBigInt BigInt @default(9007199254740993) - stamp TimestampString(3) @default(timestamp\`2024-01-01T00:00:00\`) - jsonNull Jsonb? @default(json\`null\`) + id Int @id(map: "number_defaults_pkey") + negInt Int @default(-1) + negSmallInt SmallInt @default(-2) + negFloat Float @default(-1.5) + tinyFloat Float @default(0.0000001) + negReal Real @default(-2.5) + negDecimal Numeric(65, 30) @default(-0.5) + longDecimal Numeric(65, 30) @default(12345678901234567890.123456789) + tinyDecimal Numeric(65, 30) @default(0.000000000000000001) + scaleDecimal Numeric(65, 30) @default(1.5) + wholeDecimal Numeric(65, 30) @default(10) + scaledDecimal Numeric(10, 2) @default(-1.25) + negSafeBigInt BigInt @default(-5) + negBigInt BigInt @default(-9007199254740993) + hugeBigInt BigInt @default(9007199254740993) + stamp DateTime(storage: timestamp) @default(timestamp\`2024-01-01T00:00:00\`) + jsonNull Jsonb? @default(json\`null\`) @@map("number_defaults") } model SqlDefaults { - id Int @id(map: "sql_defaults_pkey") - textNull VarChar(32)? @default(sql\`NULL::character varying\`) - floatNaN Float @default(NaN) - floatNegInf Float @default(-Infinity) - realNaN Real @default(NaN) - decimalNaN Numeric @default(NaN) - timeWithZone Timetz @default(timetz\`12:34:56Z\`) - stampInfinity TimestampString(3) @default(timestamp\`infinity\`) - dayNegInfinity DateString @default(date\`-infinity\`) - instantBc TimestamptzString(6) @default(timestamptz\`-000043-03-15T00:00:00Z\`) + id Int @id(map: "sql_defaults_pkey") + textNull VarChar(32)? @default(sql\`NULL::character varying\`) + floatNaN Float @default(NaN) + floatNegInf Float @default(-Infinity) + realNaN Real @default(NaN) + decimalNaN Numeric @default(NaN) + timeWithZone Time(storage: timetz) @default(timetz\`12:34:56Z\`) + stampInfinity DateTime(storage: timestamp) @default(sql\`'infinity'::timestamp without time zone\`) + dayNegInfinity Date @default(date\`-infinity\`) + instantBc DateTime(precision: 6) @default(timestamptz\`-000043-03-15T00:00:00Z\`) @@map("sql_defaults") } @@ -305,7 +305,7 @@ withTempDir(({ createTempDir }) => { }); it( - 'infer prints each element as the string its codec reads, which emit accepts', + 'infer prints each element as the literal its codec reads, which emit accepts', async () => { const ctx = setupJourney({ connectionString: db.connectionString, @@ -318,8 +318,8 @@ withTempDir(({ createTempDir }) => { // Contract inferred from the live database schema. Edit as needed, then run \`prisma contract emit\`. model RawListDefaults { - id Int @id(map: "raw_list_defaults_pkey") - timestamps TimestampString(3)?[]? @default([timestamp\`2024-01-01T00:00:00\`]) + id Int @id(map: "raw_list_defaults_pkey") + timestamps DateTime(storage: timestamp)?[]? @default([timestamp\`2024-01-01T00:00:00\`]) @@map("raw_list_defaults") } diff --git a/test/integration/test/cli-journeys/prisma7-dates-without-temporal.e2e.test.ts b/test/integration/test/cli-journeys/prisma7-dates-without-temporal.e2e.test.ts index 91f48629d210..91f031832602 100644 --- a/test/integration/test/cli-journeys/prisma7-dates-without-temporal.e2e.test.ts +++ b/test/integration/test/cli-journeys/prisma7-dates-without-temporal.e2e.test.ts @@ -77,6 +77,15 @@ registerHooks({ const { default: postgres } = await import('@prisma/orm-postgres/runtime'); const { contractFromJson } = await import('@prisma/orm-postgres/static'); +const dateFields = ['createdAt', 'updatedAt', 'happensAt']; +const withDates = (values) => + Object.fromEntries( + Object.entries(values).map(([name, value]) => [ + name, + dateFields.includes(name) ? new Date(value) : value, + ]), + ); +const readsDates = (row) => dateFields.every((name) => row[name] instanceof Date); const writes = JSON.parse(process.env.APP_WRITES); const db = postgres({ contract: contractFromJson(JSON.parse(readFileSync('contract.json', 'utf8'))), @@ -84,10 +93,10 @@ const db = postgres({ }); try { const events = db.orm.public.Event; - const created = await events.create(writes.create); + const created = await events.create(withDates(writes.create)); const readAfterCreate = await events.where({ id: created.id }).first(); await new Promise((resolve) => setTimeout(resolve, 20)); - const updated = await events.where({ id: created.id }).update(writes.update); + const updated = await events.where({ id: created.id }).update(withDates(writes.update)); const readAfterUpdate = await events.where({ id: created.id }).first(); const [session] = await db.query( db.raw.sql\`SELECT EXTRACT(TIMEZONE FROM CURRENT_TIMESTAMP)::int AS "utcOffsetSeconds"\` @@ -100,6 +109,7 @@ try { readAfterCreate, updated, readAfterUpdate, + readsDates: [created, readAfterCreate, updated, readAfterUpdate].every(readsDates), sessionUtcOffsetSeconds: session.utcOffsetSeconds, temporalPolyfill: [...loaded].filter((url) => url.includes('temporal-polyfill')), timezoneOffset: new Date().getTimezoneOffset(), @@ -114,16 +124,16 @@ const TOKYO_UTC_OFFSET_SECONDS = 9 * 60 * 60; const TOKYO_UTC_OFFSET_MILLIS = TOKYO_UTC_OFFSET_SECONDS * 1000; const DATE_COLUMN_CODECS = { - createdAt: 'pg/timestamp-string@1', - updatedAt: 'pg/timestamp-string@1', - happensAt: 'pg/timestamptz-string@1', + createdAt: 'pg/timestamp-date@1', + updatedAt: 'pg/timestamp-date@1', + happensAt: 'pg/timestamptz-date@1', day: 'pg/date-string@1', opensAt: 'pg/time-string@1', }; -/** The written values, each in its ISO 8601 stored form, so a read returns it unchanged. */ +/** The written values, the timestamps as the JSON text of the JavaScript `Date` the application writes and reads, the others in their ISO 8601 stored form, so a read returns each unchanged. */ const WRITTEN = { - happensAt: '2024-01-15T10:00:00.123456Z', + happensAt: '2024-01-15T10:00:00.123Z', day: '2024-01-15', opensAt: '08:30:15.25', }; @@ -143,6 +153,7 @@ interface AppResult { readonly readAfterCreate: EventRow; readonly updated: EventRow; readonly readAfterUpdate: EventRow; + readonly readsDates: boolean; readonly sessionUtcOffsetSeconds: number; readonly temporalPolyfill: readonly string[]; readonly timezoneOffset: number; @@ -152,8 +163,11 @@ function output(run: EngineCommandResult): string { return `${stripAnsi(run.stderr)}\n${stripAnsi(run.stdout)}`; } +/** The instant of a `Date`'s JSON text, or of the ISO 8601 text of a `timestamp`, read as UTC. */ function utcMillis(timestampText: string): number { - return new Date(`${timestampText}Z`).getTime(); + return new Date( + timestampText.endsWith('Z') ? timestampText : `${timestampText}Z`, + ).getTime(); } function expectNow(timestampText: string, before: number, after: number): void { @@ -183,6 +197,7 @@ async function runApp( expect(run, childOutput(run)).toMatchObject({ exitCode: 0, ...NO_GLOBAL_TEMPORAL }); const result: AppResult = JSON.parse(run.stdout); expect(result.temporalPolyfill).toEqual([]); + expect(result.readsDates, 'every timestamp reads as a JavaScript Date').toBe(true); expect(result.timezoneOffset, 'the child runs at UTC+3').toBe(-180); expect( result.sessionUtcOffsetSeconds, @@ -191,7 +206,7 @@ async function runApp( return { result, before, after }; } -function expectStringCodecs(ctx: JourneyContext): void { +function expectDateCodecs(ctx: JourneyContext): void { const columns = emittedColumns(ctx.testDir); expect( Object.fromEntries( @@ -236,20 +251,20 @@ withTempDir(({ createTempDir }) => { expect(infer.exitCode, `contract infer\n${output(infer)}`).toBe(0); const emit = await runContractEmit(ctx); expect(emit.exitCode, `contract emit\n${output(emit)}`).toBe(0); - expectStringCodecs(ctx); + expectDateCodecs(ctx); const sign = await runDbSign(ctx); expect(sign.exitCode, `db sign\n${output(sign)}`).toBe(0); const { result, before, after } = await runApp(ctx, db.connectionString, { - create: { title: 'a', updatedAt: '2024-01-15T09:00:00.5', ...WRITTEN }, - update: { title: 'b', updatedAt: '2024-01-15T11:00:00.25' }, + create: { title: 'a', updatedAt: '2024-01-15T09:00:00.500Z', ...WRITTEN }, + update: { title: 'b', updatedAt: '2024-01-15T11:00:00.250Z' }, }); expect(result.created).toEqual({ id: result.created.id, title: 'a', createdAt: result.created.createdAt, - updatedAt: '2024-01-15T09:00:00.5', + updatedAt: '2024-01-15T09:00:00.500Z', ...WRITTEN, }); expectNow(result.created.createdAt, before, after); @@ -257,7 +272,7 @@ withTempDir(({ createTempDir }) => { expect(result.updated).toEqual({ ...result.created, title: 'b', - updatedAt: '2024-01-15T11:00:00.25', + updatedAt: '2024-01-15T11:00:00.250Z', }); expect(result.readAfterUpdate).toEqual(result.updated); }, @@ -276,7 +291,7 @@ withTempDir(({ createTempDir }) => { const ctx = setupPrisma7Project(createTempDir, db.connectionString); const emit = await runContractEmit(ctx); expect(emit.exitCode, `contract emit\n${output(emit)}`).toBe(0); - expectStringCodecs(ctx); + expectDateCodecs(ctx); const sign = await runDbSign(ctx); expect(sign.exitCode, `db sign\n${output(sign)}`).toBe(0); diff --git a/test/integration/test/convenience-date-time-types/sub-millisecond-defaults.test.ts b/test/integration/test/convenience-date-time-types/sub-millisecond-defaults.test.ts new file mode 100644 index 000000000000..46d9308c32ea --- /dev/null +++ b/test/integration/test/convenience-date-time-types/sub-millisecond-defaults.test.ts @@ -0,0 +1,62 @@ +import { defineContract } from '@internal/postgres/contract-builder'; +import { describe, expect, it } from 'vitest'; +import { authorSqlContractFromPsl } from '../scalar-lists/psl-list-authoring'; + +const MICROSECONDS = '2024-01-01T01:00:00.123456Z'; + +describe('a default with digits below a millisecond on a field read as a Date', () => { + it('is refused in PSL, naming the types that hold it', async () => { + const authored = await authorSqlContractFromPsl(`model Event { + id Int @id + at DateTime(precision: 6) @default(timestamptz\`${MICROSECONDS}\`) +}`); + expect(authored.diagnostics).toEqual([ + expect.objectContaining({ + code: 'PSL_INVALID_DEFAULT_LITERAL', + message: expect.stringContaining('TimestamptzString'), + }), + ]); + }); + + it('cannot be written in the TypeScript builder, whose Date default is stored at the millisecond', () => { + const contract = defineContract({}, ({ type, field, model }) => ({ + models: { + Event: model('Event', { + fields: { + id: field.column(type.Int()).id(), + at: field + .column(type.DateTime({ precision: 6 })) + .default(new Date('2024-01-01T01:00:00.123Z')), + }, + }).sql({ table: 'event' }), + }, + })); + expect( + contract.storage.namespaces['public']?.entries.table?.['event']?.columns['at']?.default, + ).toEqual({ + kind: 'literal', + value: '2024-01-01T01:00:00.123Z', + }); + }); + + it('in a list, is reported at that element, though a later element is refused too', async () => { + const authored = await authorSqlContractFromPsl(`model Event { + id Int @id + at DateTime(precision: 6)[] @default([timestamptz\`2024-01-01T01:00:00.123Z\`, timestamptz\`${MICROSECONDS}\`, "soon"]) +}`); + expect(authored.diagnostics).toEqual([ + expect.objectContaining({ + code: 'PSL_INVALID_DEFAULT_LITERAL', + message: expect.stringMatching(/^Field "Event\.at" at element 2: .*TimestamptzString/), + }), + ]); + }); + + it('is accepted at a millisecond', async () => { + const authored = await authorSqlContractFromPsl(`model Event { + id Int @id + at DateTime(precision: 6) @default(timestamptz\`2024-01-01T01:00:00.123Z\`) +}`); + expect(authored.diagnostics).toEqual([]); + }); +}); diff --git a/test/integration/test/infer-roundtrip-runtime.integration.test.ts b/test/integration/test/infer-roundtrip-runtime.integration.test.ts index 5b4cc05480c0..24bd0bcda9c6 100644 --- a/test/integration/test/infer-roundtrip-runtime.integration.test.ts +++ b/test/integration/test/infer-roundtrip-runtime.integration.test.ts @@ -163,7 +163,7 @@ withTempDir(({ createTempDir }) => { ); }); - describe('Runtime: inferred date and time columns decode as ISO 8601 in both top-level select and include()', () => { + describe('Runtime: inferred date and time columns decode as the convenience defaults read them, in both top-level select and include()', () => { let database: Awaited>; beforeAll(async () => { @@ -199,8 +199,8 @@ withTempDir(({ createTempDir }) => { const contract = readEmittedContract(ctx); const stored = { notedOn: '2024-01-15', - loggedAt: '2024-01-15T10:00:00.5', - seenAt: '2024-01-15T10:00:00Z', + loggedAt: new Date('2024-01-15T10:00:00.500Z'), + seenAt: new Date('2024-01-15T10:00:00Z'), opensAt: '08:30:00', }; diff --git a/test/integration/test/prisma7-source/refused-defaults-migrate.integration.test.ts b/test/integration/test/prisma7-source/refused-defaults-migrate.integration.test.ts new file mode 100644 index 000000000000..40d61668b222 --- /dev/null +++ b/test/integration/test/prisma7-source/refused-defaults-migrate.integration.test.ts @@ -0,0 +1,171 @@ +/** + * A Prisma 7 default whose value the column's codec cannot hold exactly, such as a timestamp with digits below a millisecond read as a JavaScript `Date`, is kept as the SQL the target writes for it. A database Prisma 8 migrates from that contract holds every digit and verifies clean. + */ +import { mkdtempSync, rmSync, writeFileSync } from 'node:fs'; +import { tmpdir } from 'node:os'; +import postgresAdapter from '@internal/adapter-postgres/control'; +import postgresControlDriver from '@internal/driver-postgres/control'; +import sql, { INIT_ADDITIVE_POLICY } from '@internal/family-sql/control'; +import { APP_SPACE_ID, createControlStack } from '@internal/framework-components/control'; +import { buildFabricatedMigrationEdge } from '@internal/migration-tools/aggregate'; +import postgres from '@internal/target-postgres/control'; +import { prisma7PostgresBinding } from '@internal/target-postgres/prisma7-binding'; +import { createDevDatabase, type DevDatabase, timeouts, withClient } from '@repo/test-utils'; +import { join } from 'pathe'; +import { afterAll, beforeAll, describe, expect, it } from 'vitest'; +import { postgresFrameworkComponents } from '../scalar-lists/psl-list-authoring'; +import { loadPrisma7Schema } from './load-prisma7-schema'; + +const SCHEMA = `datasource db { + provider = "postgresql" +} + +model Stamp { + id Int @id + tsMicro DateTime @default("2024-01-02T03:04:05.123456Z") @db.Timestamp(6) + ts6l DateTime[] @default(["2024-01-02T03:04:05.123Z", "2024-01-02T03:04:05.123456Z"]) @db.Timestamp(6) +} +`; + +const controlStack = createControlStack({ + family: sql, + target: postgres, + adapter: postgresAdapter, + driver: postgresControlDriver, + extensions: [], +}); +const familyInstance = sql.create(controlStack); + +describe('a Prisma 7 default a Date cannot hold, migrated by Prisma 8', { + concurrent: false, +}, () => { + let database: DevDatabase | undefined; + const directory = mkdtempSync(join(tmpdir(), 'prisma7-refused-defaults-')); + + beforeAll(async () => { + database = await createDevDatabase(); + }, timeouts.spinUpPpgDev); + + afterAll(async () => { + if (database) await database.close(); + rmSync(directory, { recursive: true, force: true }); + }, timeouts.spinUpPpgDev); + + it( + 'keeps every digit, single and in a list, and verifies clean', + async () => { + if (!database) throw new Error('database not initialised'); + const schemaPath = join(directory, 'schema.prisma'); + writeFileSync(schemaPath, SCHEMA); + const contract = await loadPrisma7Schema(schemaPath); + const driver = await postgresControlDriver.create(database.connectionString); + try { + const planned = postgres.createPlanner(postgresAdapter.create(controlStack)).plan({ + contract, + schema: await familyInstance.introspect({ driver }), + policy: INIT_ADDITIVE_POLICY, + fromContract: null, + origin: null, + statements: [], + frameworkComponents: postgresFrameworkComponents, + spaceId: APP_SPACE_ID, + snapshotsImportPath: '../../snapshots', + }); + if (planned.kind !== 'success') throw new Error(JSON.stringify(planned)); + const run = await postgres.createRunner(familyInstance).execute({ + driver, + perSpaceOptions: [ + { + space: APP_SPACE_ID, + plan: planned.plan, + migrationEdges: [ + buildFabricatedMigrationEdge({ + currentMarkerStorageHash: planned.plan.origin?.storageHash, + destinationStorageHash: planned.plan.destination.storageHash, + operationCount: planned.plan.operations.length, + }), + ], + driver, + destinationContract: contract, + policy: INIT_ADDITIVE_POLICY, + frameworkComponents: postgresFrameworkComponents, + }, + ], + }); + expect(run.ok ? 'ok' : JSON.stringify(run.failure).slice(0, 1500)).toBe('ok'); + const verified = familyInstance.verifySchema({ + contract, + schema: await familyInstance.introspect({ driver, contract }), + strict: true, + frameworkComponents: postgresFrameworkComponents, + }); + expect(verified.schema.issues).toEqual([]); + } finally { + await driver.close(); + } + const row = await withClient(database.connectionString, async (client) => { + await client.query('INSERT INTO "Stamp" (id) VALUES (1)'); + const read = await client.query<{ micro: string; list: string }>( + `SELECT "tsMicro"::text AS micro, "ts6l"::text AS list FROM "Stamp"`, + ); + return read.rows[0]; + }); + expect(row).toEqual({ + micro: '2024-01-02 03:04:05.123456', + list: '{"2024-01-02 03:04:05.123","2024-01-02 03:04:05.123456"}', + }); + }, + timeouts.spinUpPpgDev, + ); + + it( + 'writes a year before 1 or after 9999 as PostgreSQL reads it', + async () => { + if (!database) throw new Error('database not initialised'); + const timestamp = { + codecId: 'pg/timestamp-date@1', + dataType: 'pg/timestamp', + typeParams: { precision: 6 }, + }; + const timestamptz = { + codecId: 'pg/timestamptz-date@1', + dataType: 'pg/timestamptz', + typeParams: { precision: 6 }, + }; + const expressions = { + bc: prisma7PostgresBinding.refusedLiteralDefault( + '-000043-03-15T00:00:00.123456', + timestamp, + ), + late: prisma7PostgresBinding.refusedLiteralDefault( + '+010000-01-01T00:00:00.123456Z', + timestamptz, + ), + list: prisma7PostgresBinding.refusedLiteralDefault( + ['-000043-03-15T00:00:00.123456', null], + timestamp, + ), + }; + const read = await withClient(database.connectionString, async (client) => { + await client.query("SET TIME ZONE 'UTC'"); + const result = await client.query<{ bc: string; late: string; list: string }>( + `SELECT (${expressions.bc})::text AS bc, (${expressions.late})::text AS late, (${expressions.list})::text AS list`, + ); + return result.rows[0]; + }); + expect({ expressions, read }).toEqual({ + expressions: { + bc: "'0044-03-15T00:00:00.123456 BC'::timestamp(6)", + late: "'10000-01-01T00:00:00.123456Z'::timestamptz(6)", + list: "ARRAY['0044-03-15T00:00:00.123456 BC', NULL]::timestamp(6)[]", + }, + read: { + bc: '0044-03-15 00:00:00.123456 BC', + late: '10000-01-01 00:00:00.123456+00', + list: '{"0044-03-15 00:00:00.123456 BC",NULL}', + }, + }); + }, + timeouts.spinUpPpgDev, + ); +}); diff --git a/test/integration/test/psl-infer/convenience-types-infer.integration.test.ts b/test/integration/test/psl-infer/convenience-types-infer.integration.test.ts new file mode 100644 index 000000000000..ee37adff6bc2 --- /dev/null +++ b/test/integration/test/psl-infer/convenience-types-infer.integration.test.ts @@ -0,0 +1,189 @@ +/** + * `contract infer` writes each date and time column as the convenience type whose default path reads it, with only the arguments that differ from the defaults. The inferred schema emits, verifies clean against the database it was read from, and plans no migration. + */ +import postgresAdapter from '@internal/adapter-postgres/control'; +import postgresControlDriver from '@internal/driver-postgres/control'; +import sql, { INIT_ADDITIVE_POLICY } from '@internal/family-sql/control'; +import { APP_SPACE_ID, createControlStack } from '@internal/framework-components/control'; +import { buildFabricatedMigrationEdge } from '@internal/migration-tools/aggregate'; +import { printPsl } from '@internal/psl-printer'; +import postgres from '@internal/target-postgres/control'; +import { createDevDatabase, type DevDatabase, timeouts, withClient } from '@repo/test-utils'; +import { afterAll, beforeAll, describe, expect, it } from 'vitest'; +import { + authorSqlContractFromPsl, + postgresFrameworkComponents, +} from '../scalar-lists/psl-list-authoring'; + +const controlStack = createControlStack({ + family: sql, + target: postgres, + adapter: postgresAdapter, + driver: postgresControlDriver, + extensions: [], +}); +const familyInstance = sql.create(controlStack); + +const EVENT_TABLE = `CREATE TABLE event ( + id int4 PRIMARY KEY, + at timestamptz, + paid_at timestamptz(3), + legacy timestamp(3), + day date, + opens time(0), + local_at timetz, + span interval, + micro timestamptz(6) DEFAULT '2024-01-01 01:00:00.123456+00', + milli timestamptz(3) DEFAULT '2024-01-01 01:00:00.123+00' +)`; + +function fieldLines(printed: string): readonly string[] { + return printed + .split('\n') + .map((line) => line.trim().replace(/\s+/g, ' ')) + .filter((line) => /^(at|paidAt|legacy|day|opens|localAt|span|micro|milli) /.test(line)); +} + +describe('contract infer over date and time columns', { concurrent: false }, () => { + let database: DevDatabase | undefined; + + beforeAll(async () => { + database = await createDevDatabase(); + }, timeouts.spinUpPpgDev); + + afterAll(async () => { + if (database) await database.close(); + }, timeouts.spinUpPpgDev); + + it( + 'writes the convenience types, which emit, verify clean and plan no migration', + async () => { + if (!database) throw new Error('database not initialised'); + await withClient(database.connectionString, async (client) => { + await client.query(EVENT_TABLE); + }); + const driver = await postgresControlDriver.create(database.connectionString); + try { + const schema = await familyInstance.introspect({ driver }); + const inferred = printPsl(await familyInstance.inferPslContract(schema), { + pslBlockDescriptors: controlStack.authoringContributions.pslBlockDescriptors, + }); + expect(fieldLines(inferred)).toEqual([ + 'at DateTime(precision: 6)?', + 'paidAt DateTime? @map("paid_at")', + 'legacy DateTime(storage: timestamp)?', + 'day Date?', + 'opens Time(precision: 0)?', + 'localAt Time(storage: timetz)? @map("local_at")', + 'span Duration?', + "micro DateTime(precision: 6)? @default(sql`'2024-01-01 01:00:00.123456+00'::timestamp with time zone`)", + 'milli DateTime? @default(timestamptz`2024-01-01T01:00:00.123Z`)', + ]); + + const emitted = await authorSqlContractFromPsl( + inferred.replace(/^\/\/ use prisma-8\n/, ''), + ); + expect(emitted.diagnostics).toEqual([]); + const contract = emitted.contract; + if (!contract) throw new Error('the inferred schema produced no contract'); + + const verified = familyInstance.verifySchema({ + contract, + schema: await familyInstance.introspect({ driver, contract }), + strict: true, + frameworkComponents: postgresFrameworkComponents, + }); + expect(verified.schema.issues).toEqual([]); + + const planned = postgres.createPlanner(postgresAdapter.create(controlStack)).plan({ + contract, + schema: await familyInstance.introspect({ driver, contract }), + policy: INIT_ADDITIVE_POLICY, + fromContract: null, + origin: null, + statements: [], + frameworkComponents: postgresFrameworkComponents, + spaceId: APP_SPACE_ID, + snapshotsImportPath: '../../snapshots', + }); + expect(planned.kind === 'success' ? planned.plan.operations : planned).toEqual([]); + } finally { + await driver.close(); + } + }, + timeouts.spinUpPpgDev, + ); + it( + 'applies a microsecond default written as a raw expression with every digit', + async () => { + if (!database) throw new Error('database not initialised'); + await withClient(database.connectionString, async (client) => { + await client.query('DROP SCHEMA IF EXISTS public CASCADE'); + await client.query('CREATE SCHEMA public'); + await client.query('DROP SCHEMA IF EXISTS prisma_contract CASCADE'); + }); + const authored = await authorSqlContractFromPsl(`model Stamp { + id Int @id + micro DateTime(precision: 6) @default(sql\`'2024-01-01 01:00:00.123456+00'::timestamp with time zone\`) + @@map("stamp") +}`); + expect(authored.diagnostics).toEqual([]); + const contract = authored.contract; + if (!contract) throw new Error('authoring produced no contract'); + const driver = await postgresControlDriver.create(database.connectionString); + try { + const planned = postgres.createPlanner(postgresAdapter.create(controlStack)).plan({ + contract, + schema: await familyInstance.introspect({ driver }), + policy: INIT_ADDITIVE_POLICY, + fromContract: null, + origin: null, + statements: [], + frameworkComponents: postgresFrameworkComponents, + spaceId: APP_SPACE_ID, + snapshotsImportPath: '../../snapshots', + }); + if (planned.kind !== 'success') throw new Error(JSON.stringify(planned)); + const run = await postgres.createRunner(familyInstance).execute({ + driver, + perSpaceOptions: [ + { + space: APP_SPACE_ID, + plan: planned.plan, + migrationEdges: [ + buildFabricatedMigrationEdge({ + currentMarkerStorageHash: planned.plan.origin?.storageHash, + destinationStorageHash: planned.plan.destination.storageHash, + operationCount: planned.plan.operations.length, + }), + ], + driver, + destinationContract: contract, + policy: INIT_ADDITIVE_POLICY, + frameworkComponents: postgresFrameworkComponents, + }, + ], + }); + expect(run.ok ? 'ok' : JSON.stringify(run.failure).slice(0, 1500)).toBe('ok'); + } finally { + await driver.close(); + } + const stored = await withClient(database.connectionString, async (client) => { + const columnDefault = await client.query<{ value: string }>( + `SELECT column_default AS "value" FROM information_schema.columns + WHERE table_name = 'stamp' AND column_name = 'micro'`, + ); + await client.query('INSERT INTO stamp (id) VALUES (1)'); + const row = await client.query<{ value: string }>( + `SELECT (micro AT TIME ZONE 'UTC')::text AS "value" FROM stamp`, + ); + return { columnDefault: columnDefault.rows[0]?.value, row: row.rows[0]?.value }; + }); + expect(stored).toEqual({ + columnDefault: "'2024-01-01 01:00:00.123456+00'::timestamp with time zone", + row: '2024-01-01 01:00:00.123456', + }); + }, + timeouts.spinUpPpgDev, + ); +}); diff --git a/test/integration/test/psl-infer/marked-types-round-trip.integration.test.ts b/test/integration/test/psl-infer/marked-types-round-trip.integration.test.ts index e0fb1de09ecc..b374d98c2d15 100644 --- a/test/integration/test/psl-infer/marked-types-round-trip.integration.test.ts +++ b/test/integration/test/psl-infer/marked-types-round-trip.integration.test.ts @@ -73,16 +73,16 @@ const AUTHORED_TYPES = [ 'TimeString(3)', 'Timetz', 'Timetz(2)', - 'TimestampString', - 'TimestampString(3)', - 'TimestamptzString', - 'TimestamptzString(6)', + 'TimestampJsDate', + 'TimestampJsDate(3)', + 'TimestamptzJsDate', + 'TimestamptzJsDate(6)', 'Bit', 'Bit(8)', 'VarBit', 'VarBit(16)', - 'IntervalParts', - 'IntervalParts(3)', + 'IntervalString', + 'IntervalString(3)', ] as const; const AUTHORED_SCHEMA = `model Sample { diff --git a/test/integration/test/psl-print/date-time-types-print.integration.test.ts b/test/integration/test/psl-print/date-time-types-print.integration.test.ts index 5c591e943f23..980935388750 100644 --- a/test/integration/test/psl-print/date-time-types-print.integration.test.ts +++ b/test/integration/test/psl-print/date-time-types-print.integration.test.ts @@ -31,7 +31,7 @@ model Event { function fieldTypes(printed: string): Record { return Object.fromEntries( printed.split('\n').flatMap((line) => { - const match = /^\s+(\w+)\s+(\S+)/.exec(line); + const match = /^\s+(\w+)\s+([^@]+?)\s*(?:@|$)/.exec(line); return match?.[1] === undefined || match[2] === undefined || match[1] === 'model' ? [] : [[match[1], match[2]]]; @@ -40,31 +40,31 @@ function fieldTypes(printed: string): Record { } describe('contract print spells each date and time column', () => { - it('as the type contract infer writes, or else as the explicit type that reads the column back, a convenience type included', async () => { + it('as the convenience type it becomes, with the arguments that differ from the defaults, or else as its explicit type', async () => { const contract = await readPsl(SCHEMA); expect(fieldTypes(printContract(contract).text)).toEqual({ id: 'Int', - instant: 'TimestamptzJsDate(3)', - zoned: 'TimestamptzTemporal', - zoned3: 'TimestamptzTemporal(3)', + instant: 'DateTime', + zoned: 'DateTime(as: temporal)', + zoned3: 'DateTime(as: temporal, precision: 3)', local: 'TimestampTemporal', local3: 'TimestampTemporal(3)', - utcDate: 'TimestampJsDate(3)', - utcAt: 'TimestampInstant(6)', - dayT: 'DateTemporal', - clockT: 'TimeTemporal(0)', - period: 'IntervalParts', - span: 'IntervalString(3)', - day: 'DateString', - clock: 'TimeString', - clock3: 'TimeString(3)', - zonedText: 'TimestamptzString(3)', - localText: 'TimestampString(3)', - dayText: 'DateString', - clockText: 'TimeString(3)', - offset: 'Timetz', - lasts: 'IntervalString', + utcDate: 'DateTime(storage: timestamp)', + utcAt: 'DateTime(as: temporal, storage: timestamp, precision: 6)', + dayT: 'Date(as: temporal)', + clockT: 'Time(as: temporal, precision: 0)', + period: 'Duration(as: parts)', + span: 'Duration(precision: 3)', + day: 'Date', + clock: 'Time', + clock3: 'Time(precision: 3)', + zonedText: 'DateTime(as: string, precision: 3)', + localText: 'DateTime(as: string, storage: timestamp, precision: 3)', + dayText: 'Date', + clockText: 'Time(precision: 3)', + offset: 'Time(storage: timetz)', + lasts: 'Duration', }); expect(await printAndReadBack(contract)).toEqual(contract); }); diff --git a/test/integration/test/psl-print/generated-values-roundtrip.integration.test.ts b/test/integration/test/psl-print/generated-values-roundtrip.integration.test.ts index fab4edf3fad5..201f493840af 100644 --- a/test/integration/test/psl-print/generated-values-roundtrip.integration.test.ts +++ b/test/integration/test/psl-print/generated-values-roundtrip.integration.test.ts @@ -166,8 +166,8 @@ model Event { fieldLines: [ 'id String @id @onCreate(uuid(7))', 'code VarChar(32) @onCreate(nanoid())', - 'seenAt TimeTemporal(3) @onUpdate(now())', - 'createdAt TimestamptzTemporal @default(now()) @onCreate(now())', + 'seenAt Time(as: temporal, precision: 3) @onUpdate(now())', + 'createdAt DateTime(as: temporal) @default(now()) @onCreate(now())', ], roundTrip: serializedWithoutCapabilities(authored), }); diff --git a/test/integration/test/psl-print/prisma7-convenience-types-print.integration.test.ts b/test/integration/test/psl-print/prisma7-convenience-types-print.integration.test.ts new file mode 100644 index 000000000000..759be33d45a9 --- /dev/null +++ b/test/integration/test/psl-print/prisma7-convenience-types-print.integration.test.ts @@ -0,0 +1,77 @@ +import { mkdtempSync, rmSync, writeFileSync } from 'node:fs'; +import { tmpdir } from 'node:os'; +import type { Contract } from '@internal/contract/types'; +import type { SqlStorage } from '@internal/sql-contract/types'; +import { prisma7Contract } from '@internal/sql-contract-prisma7/provider'; +import { prisma7PostgresBinding } from '@internal/target-postgres/prisma7-binding'; +import { blindCast } from '@internal/utils/casts'; +import { join } from 'pathe'; +import { afterAll, describe, expect, it } from 'vitest'; +import { + composePostgresStack, + printAndReadBack, + printContract, + serializedWithoutCapabilities, + sourceContext, +} from './print-and-read-back'; + +const PRISMA7_SCHEMA = `datasource db { + provider = "postgresql" +} + +model Event { + id Int @id + at DateTime + zoned DateTime @db.Timestamptz(3) + zoned6 DateTime @db.Timestamptz(6) + day DateTime @db.Date + clock DateTime @db.Time(0) + offset DateTime @db.Timetz(3) + touched DateTime @updatedAt +} +`; + +const directory = mkdtempSync(join(tmpdir(), 'prisma7-convenience-')); + +afterAll(() => { + rmSync(directory, { recursive: true, force: true }); +}); + +async function readPrisma7(schema: string): Promise> { + const schemaPath = join(directory, 'schema.prisma'); + writeFileSync(schemaPath, schema); + const result = await prisma7Contract(schemaPath, { binding: prisma7PostgresBinding }).source.load( + sourceContext(composePostgresStack(), [schemaPath]), + ); + if (!result.ok) throw new Error(JSON.stringify(result.failure.diagnostics)); + return blindCast, 'the Prisma 7 source yields a SQL contract'>(result.value); +} + +function fieldLines(printed: string): Record { + return Object.fromEntries( + printed.split('\n').flatMap((line) => { + const match = /^\s+(\w+)\s+(.+?)\s*$/.exec(line.replace(/\s+/g, ' ')); + return match?.[1] === undefined || match[2] === undefined || match[1] === 'model' + ? [] + : [[match[1], match[2]]]; + }), + ); +} + +describe('a Prisma 7 schema printed after prisma7Schema reads it', () => { + it('writes each DateTime field as the convenience type its column and value become', async () => { + const contract = await readPrisma7(PRISMA7_SCHEMA); + expect(fieldLines(printContract(contract).text)).toMatchObject({ + at: 'DateTime(storage: timestamp)', + zoned: 'DateTime', + zoned6: 'DateTime(precision: 6)', + day: 'Date', + clock: 'Time(precision: 0)', + offset: 'Time(storage: timetz, precision: 3)', + touched: 'DateTime(storage: timestamp) @onCreate(now()) @onUpdate(now())', + }); + const printed = await printAndReadBack(contract); + expect(serializedWithoutCapabilities(printed)).toEqual(serializedWithoutCapabilities(contract)); + expect(printed.storage.storageHash).toBe(contract.storage.storageHash); + }); +}); diff --git a/test/integration/test/temporal-defaults/_fixture-aliases/contract.prisma b/test/integration/test/temporal-defaults/_fixture-aliases/contract.prisma index 7f925bd59fbe..4291955c1a99 100644 --- a/test/integration/test/temporal-defaults/_fixture-aliases/contract.prisma +++ b/test/integration/test/temporal-defaults/_fixture-aliases/contract.prisma @@ -1,11 +1,9 @@ // use prisma-8 model Reading { - id Int @id - label String - createdAt temporal.CreatedAt - updatedAt temporal.UpdatedAt - createdAtText temporal.CreatedAtString - updatedAtText temporal.UpdatedAtString + id Int @id + label String + createdAt temporal.CreatedAt + updatedAt temporal.UpdatedAt @@map("reading") } diff --git a/test/integration/test/temporal-defaults/_fixture-aliases/generated/contract.d.ts b/test/integration/test/temporal-defaults/_fixture-aliases/generated/contract.d.ts index 7f140d7dc8a0..dfa7b9190aa8 100644 --- a/test/integration/test/temporal-defaults/_fixture-aliases/generated/contract.d.ts +++ b/test/integration/test/temporal-defaults/_fixture-aliases/generated/contract.d.ts @@ -23,9 +23,9 @@ import type { } from '@internal/contract/types'; export type StorageHash = - StorageHashBase<'0390a420cce478c0e3a83722ef67ca5c5cee0029919f8b950ef83fc5e6b97fa4'>; + StorageHashBase<'28b63df973d82396c9d1242834b6c0b55bb341a68354a8c69ace7a77828fcfde'>; export type ExecutionHash = - ExecutionHashBase<'094fab307f2b28070b48b8e4b97fb2c86739cfacf1c3412304356e5c5a43e05e'>; + ExecutionHashBase<'ffef81acb63dde0c1f9cecb125bbe5f651bfcb4dceae4abe22902f9e2350038f'>; export type ProfileHash = ProfileHashBase<'3916f444a8a17ad749191acf9e08dad97d1a327b88c2f1d45d12f240296aa8b2'>; @@ -304,11 +304,9 @@ export type StorageColumnInputTypes = ColumnTypesFromStorage< export namespace Models { export type public_Reading = { createdAt: FieldOutputTypes['public']['Reading']['createdAt']; - createdAtText: FieldOutputTypes['public']['Reading']['createdAtText']; id: FieldOutputTypes['public']['Reading']['id']; label: FieldOutputTypes['public']['Reading']['label']; updatedAt: FieldOutputTypes['public']['Reading']['updatedAt']; - updatedAtText: FieldOutputTypes['public']['Reading']['updatedAtText']; readonly [RelationKeys]?: never; }; } @@ -341,14 +339,9 @@ type ContractBase = Omit< columns: { readonly createdAt: { readonly dataType: 'pg/timestamptz'; - readonly codecId: 'pg/timestamptz-temporal@1'; - readonly nullable: false; - readonly many: false; - }; - readonly createdAtText: { - readonly dataType: 'pg/timestamptz'; - readonly codecId: 'pg/timestamptz-string@1'; + readonly codecId: 'pg/timestamptz-date@1'; readonly nullable: false; + readonly typeParams: { readonly precision: 3 }; readonly many: false; }; readonly id: { @@ -365,14 +358,9 @@ type ContractBase = Omit< }; readonly updatedAt: { readonly dataType: 'pg/timestamptz'; - readonly codecId: 'pg/timestamptz-temporal@1'; - readonly nullable: false; - readonly many: false; - }; - readonly updatedAtText: { - readonly dataType: 'pg/timestamptz'; - readonly codecId: 'pg/timestamptz-string@1'; + readonly codecId: 'pg/timestamptz-date@1'; readonly nullable: false; + readonly typeParams: { readonly precision: 3 }; readonly many: false; }; }; @@ -404,14 +392,8 @@ type ContractBase = Omit< readonly nullable: false; readonly type: { readonly kind: 'scalar'; - readonly codecId: 'pg/timestamptz-temporal@1'; - }; - }; - readonly createdAtText: { - readonly nullable: false; - readonly type: { - readonly kind: 'scalar'; - readonly codecId: 'pg/timestamptz-string@1'; + readonly codecId: 'pg/timestamptz-date@1'; + readonly typeParams: { readonly precision: 3 }; }; }; readonly id: { @@ -426,14 +408,8 @@ type ContractBase = Omit< readonly nullable: false; readonly type: { readonly kind: 'scalar'; - readonly codecId: 'pg/timestamptz-temporal@1'; - }; - }; - readonly updatedAtText: { - readonly nullable: false; - readonly type: { - readonly kind: 'scalar'; - readonly codecId: 'pg/timestamptz-string@1'; + readonly codecId: 'pg/timestamptz-date@1'; + readonly typeParams: { readonly precision: 3 }; }; }; }; @@ -443,11 +419,9 @@ type ContractBase = Omit< readonly namespaceId: 'public'; readonly fields: { readonly createdAt: { readonly column: 'createdAt' }; - readonly createdAtText: { readonly column: 'createdAtText' }; readonly id: { readonly column: 'id' }; readonly label: { readonly column: 'label' }; readonly updatedAt: { readonly column: 'updatedAt' }; - readonly updatedAtText: { readonly column: 'updatedAtText' }; }; }; }; @@ -488,7 +462,11 @@ type ContractBase = Omit< readonly mutations: { readonly defaults: readonly [ { - readonly onCreate: { readonly id: 'instantNow'; readonly kind: 'generator' }; + readonly onCreate: { + readonly id: 'timestampNow'; + readonly kind: 'generator'; + readonly params: { readonly precision: 3 }; + }; readonly ref: { readonly entry: 'reading'; readonly field: 'createdAt'; @@ -496,28 +474,19 @@ type ContractBase = Omit< }; }, { - readonly onCreate: { readonly id: 'timestamptzStringNow'; readonly kind: 'generator' }; - readonly ref: { - readonly entry: 'reading'; - readonly field: 'createdAtText'; - readonly namespace: 'public'; + readonly onCreate: { + readonly id: 'timestampNow'; + readonly kind: 'generator'; + readonly params: { readonly precision: 3 }; }; - }, - { - readonly onCreate: { readonly id: 'instantNow'; readonly kind: 'generator' }; - readonly onUpdate: { readonly id: 'instantNow'; readonly kind: 'generator' }; - readonly ref: { - readonly entry: 'reading'; - readonly field: 'updatedAt'; - readonly namespace: 'public'; + readonly onUpdate: { + readonly id: 'timestampNow'; + readonly kind: 'generator'; + readonly params: { readonly precision: 3 }; }; - }, - { - readonly onCreate: { readonly id: 'timestamptzStringNow'; readonly kind: 'generator' }; - readonly onUpdate: { readonly id: 'timestamptzStringNow'; readonly kind: 'generator' }; readonly ref: { readonly entry: 'reading'; - readonly field: 'updatedAtText'; + readonly field: 'updatedAt'; readonly namespace: 'public'; }; }, diff --git a/test/integration/test/temporal-defaults/_fixture-aliases/generated/contract.json b/test/integration/test/temporal-defaults/_fixture-aliases/generated/contract.json index f656b2a3f2e6..c89a51e26092 100644 --- a/test/integration/test/temporal-defaults/_fixture-aliases/generated/contract.json +++ b/test/integration/test/temporal-defaults/_fixture-aliases/generated/contract.json @@ -18,15 +18,11 @@ "createdAt": { "nullable": false, "type": { - "codecId": "pg/timestamptz-temporal@1", - "kind": "scalar" - } - }, - "createdAtText": { - "nullable": false, - "type": { - "codecId": "pg/timestamptz-string@1", - "kind": "scalar" + "codecId": "pg/timestamptz-date@1", + "kind": "scalar", + "typeParams": { + "precision": 3 + } } }, "id": { @@ -46,15 +42,11 @@ "updatedAt": { "nullable": false, "type": { - "codecId": "pg/timestamptz-temporal@1", - "kind": "scalar" - } - }, - "updatedAtText": { - "nullable": false, - "type": { - "codecId": "pg/timestamptz-string@1", - "kind": "scalar" + "codecId": "pg/timestamptz-date@1", + "kind": "scalar", + "typeParams": { + "precision": 3 + } } } }, @@ -64,9 +56,6 @@ "createdAt": { "column": "createdAt" }, - "createdAtText": { - "column": "createdAtText" - }, "id": { "column": "id" }, @@ -75,9 +64,6 @@ }, "updatedAt": { "column": "updatedAt" - }, - "updatedAtText": { - "column": "updatedAtText" } }, "namespaceId": "public", @@ -96,14 +82,12 @@ "reading": { "columns": { "createdAt": { - "codecId": "pg/timestamptz-temporal@1", + "codecId": "pg/timestamptz-date@1", "dataType": "pg/timestamptz", - "nullable": false - }, - "createdAtText": { - "codecId": "pg/timestamptz-string@1", - "dataType": "pg/timestamptz", - "nullable": false + "nullable": false, + "typeParams": { + "precision": 3 + } }, "id": { "codecId": "pg/int4@1", @@ -116,14 +100,12 @@ "nullable": false }, "updatedAt": { - "codecId": "pg/timestamptz-temporal@1", + "codecId": "pg/timestamptz-date@1", "dataType": "pg/timestamptz", - "nullable": false - }, - "updatedAtText": { - "codecId": "pg/timestamptz-string@1", - "dataType": "pg/timestamptz", - "nullable": false + "nullable": false, + "typeParams": { + "precision": 3 + } } }, "foreignKeys": [], @@ -141,16 +123,19 @@ "kind": "postgres-schema" } }, - "storageHash": "0390a420cce478c0e3a83722ef67ca5c5cee0029919f8b950ef83fc5e6b97fa4" + "storageHash": "28b63df973d82396c9d1242834b6c0b55bb341a68354a8c69ace7a77828fcfde" }, "execution": { - "executionHash": "094fab307f2b28070b48b8e4b97fb2c86739cfacf1c3412304356e5c5a43e05e", + "executionHash": "ffef81acb63dde0c1f9cecb125bbe5f651bfcb4dceae4abe22902f9e2350038f", "mutations": { "defaults": [ { "onCreate": { - "id": "instantNow", - "kind": "generator" + "id": "timestampNow", + "kind": "generator", + "params": { + "precision": 3 + } }, "ref": { "entry": "reading", @@ -160,44 +145,24 @@ }, { "onCreate": { - "id": "timestamptzStringNow", - "kind": "generator" - }, - "ref": { - "entry": "reading", - "field": "createdAtText", - "namespace": "public" - } - }, - { - "onCreate": { - "id": "instantNow", - "kind": "generator" + "id": "timestampNow", + "kind": "generator", + "params": { + "precision": 3 + } }, "onUpdate": { - "id": "instantNow", - "kind": "generator" + "id": "timestampNow", + "kind": "generator", + "params": { + "precision": 3 + } }, "ref": { "entry": "reading", "field": "updatedAt", "namespace": "public" } - }, - { - "onCreate": { - "id": "timestamptzStringNow", - "kind": "generator" - }, - "onUpdate": { - "id": "timestamptzStringNow", - "kind": "generator" - }, - "ref": { - "entry": "reading", - "field": "updatedAtText", - "namespace": "public" - } } ] } diff --git a/test/integration/test/temporal-defaults/_fixture-attributes/contract.prisma b/test/integration/test/temporal-defaults/_fixture-attributes/contract.prisma index 19727e6aacba..27ae42868309 100644 --- a/test/integration/test/temporal-defaults/_fixture-attributes/contract.prisma +++ b/test/integration/test/temporal-defaults/_fixture-attributes/contract.prisma @@ -29,7 +29,13 @@ model Clock { timestamptzString3 TimestamptzString(3) @onCreate(now()) @onUpdate(now()) timestamptzString6 TimestamptzString(6) @onCreate(now()) @onUpdate(now()) timestamptzJsDate0 TimestamptzJsDate(0) @onCreate(now()) @onUpdate(now()) + timestamptzJsDate1 TimestamptzJsDate(1) @onCreate(now()) @onUpdate(now()) + timestamptzJsDate2 TimestamptzJsDate(2) @onCreate(now()) @onUpdate(now()) timestamptzJsDate3 TimestamptzJsDate(3) @onCreate(now()) @onUpdate(now()) timestamptzJsDate6 TimestamptzJsDate(6) @onCreate(now()) @onUpdate(now()) + timestampJsDate0 TimestampJsDate(0) @onCreate(now()) @onUpdate(now()) + timestampJsDate1 TimestampJsDate(1) @onCreate(now()) @onUpdate(now()) + timestampJsDate2 TimestampJsDate(2) @onCreate(now()) @onUpdate(now()) + dateTime0 DateTime(precision: 0) @onCreate(now()) @onUpdate(now()) @@map("clock") } diff --git a/test/integration/test/temporal-defaults/_fixture-attributes/generated/contract.d.ts b/test/integration/test/temporal-defaults/_fixture-attributes/generated/contract.d.ts index 3b6b863b22c5..d9fddca137a4 100644 --- a/test/integration/test/temporal-defaults/_fixture-attributes/generated/contract.d.ts +++ b/test/integration/test/temporal-defaults/_fixture-attributes/generated/contract.d.ts @@ -23,9 +23,9 @@ import type { } from '@internal/contract/types'; export type StorageHash = - StorageHashBase<'e912a0ac1c484ed92ae8cb217638a889ad671c1f4a9bfb3e469ebfb14916c6dd'>; + StorageHashBase<'6c0d1dcd5ed439ed0d02551b3fdfd56445a86f63c5914439db906a00ced83c9d'>; export type ExecutionHash = - ExecutionHashBase<'51be7e21b1611b41897bee6131dc8d5bfc6abd6380ca0bd6ba5a86d1c2f479c4'>; + ExecutionHashBase<'dfe82a8c68f47cc9f0da170366b163f1f4f27180594ded6a6575360c21894000'>; export type ProfileHash = ProfileHashBase<'3916f444a8a17ad749191acf9e08dad97d1a327b88c2f1d45d12f240296aa8b2'>; @@ -305,6 +305,7 @@ export namespace Models { export type public_Clock = { date: FieldOutputTypes['public']['Clock']['date']; dateString: FieldOutputTypes['public']['Clock']['dateString']; + dateTime0: FieldOutputTypes['public']['Clock']['dateTime0']; id: FieldOutputTypes['public']['Clock']['id']; label: FieldOutputTypes['public']['Clock']['label']; time0: FieldOutputTypes['public']['Clock']['time0']; @@ -316,6 +317,9 @@ export namespace Models { timestamp0: FieldOutputTypes['public']['Clock']['timestamp0']; timestamp3: FieldOutputTypes['public']['Clock']['timestamp3']; timestamp6: FieldOutputTypes['public']['Clock']['timestamp6']; + timestampJsDate0: FieldOutputTypes['public']['Clock']['timestampJsDate0']; + timestampJsDate1: FieldOutputTypes['public']['Clock']['timestampJsDate1']; + timestampJsDate2: FieldOutputTypes['public']['Clock']['timestampJsDate2']; timestampString0: FieldOutputTypes['public']['Clock']['timestampString0']; timestampString3: FieldOutputTypes['public']['Clock']['timestampString3']; timestampString6: FieldOutputTypes['public']['Clock']['timestampString6']; @@ -323,6 +327,8 @@ export namespace Models { timestamptz3: FieldOutputTypes['public']['Clock']['timestamptz3']; timestamptz6: FieldOutputTypes['public']['Clock']['timestamptz6']; timestamptzJsDate0: FieldOutputTypes['public']['Clock']['timestamptzJsDate0']; + timestamptzJsDate1: FieldOutputTypes['public']['Clock']['timestamptzJsDate1']; + timestamptzJsDate2: FieldOutputTypes['public']['Clock']['timestamptzJsDate2']; timestamptzJsDate3: FieldOutputTypes['public']['Clock']['timestamptzJsDate3']; timestamptzJsDate6: FieldOutputTypes['public']['Clock']['timestamptzJsDate6']; timestamptzString0: FieldOutputTypes['public']['Clock']['timestamptzString0']; @@ -375,6 +381,13 @@ type ContractBase = Omit< readonly nullable: false; readonly many: false; }; + readonly dateTime0: { + readonly dataType: 'pg/timestamptz'; + readonly codecId: 'pg/timestamptz-date@1'; + readonly nullable: false; + readonly typeParams: { readonly precision: 0 }; + readonly many: false; + }; readonly id: { readonly dataType: 'pg/int4'; readonly codecId: 'pg/int4@1'; @@ -450,6 +463,27 @@ type ContractBase = Omit< readonly typeParams: { readonly precision: 6 }; readonly many: false; }; + readonly timestampJsDate0: { + readonly dataType: 'pg/timestamp'; + readonly codecId: 'pg/timestamp-date@1'; + readonly nullable: false; + readonly typeParams: { readonly precision: 0 }; + readonly many: false; + }; + readonly timestampJsDate1: { + readonly dataType: 'pg/timestamp'; + readonly codecId: 'pg/timestamp-date@1'; + readonly nullable: false; + readonly typeParams: { readonly precision: 1 }; + readonly many: false; + }; + readonly timestampJsDate2: { + readonly dataType: 'pg/timestamp'; + readonly codecId: 'pg/timestamp-date@1'; + readonly nullable: false; + readonly typeParams: { readonly precision: 2 }; + readonly many: false; + }; readonly timestampString0: { readonly dataType: 'pg/timestamp'; readonly codecId: 'pg/timestamp-string@1'; @@ -499,6 +533,20 @@ type ContractBase = Omit< readonly typeParams: { readonly precision: 0 }; readonly many: false; }; + readonly timestamptzJsDate1: { + readonly dataType: 'pg/timestamptz'; + readonly codecId: 'pg/timestamptz-date@1'; + readonly nullable: false; + readonly typeParams: { readonly precision: 1 }; + readonly many: false; + }; + readonly timestamptzJsDate2: { + readonly dataType: 'pg/timestamptz'; + readonly codecId: 'pg/timestamptz-date@1'; + readonly nullable: false; + readonly typeParams: { readonly precision: 2 }; + readonly many: false; + }; readonly timestamptzJsDate3: { readonly dataType: 'pg/timestamptz'; readonly codecId: 'pg/timestamptz-date@1'; @@ -602,6 +650,14 @@ type ContractBase = Omit< readonly nullable: false; readonly type: { readonly kind: 'scalar'; readonly codecId: 'pg/date-string@1' }; }; + readonly dateTime0: { + readonly nullable: false; + readonly type: { + readonly kind: 'scalar'; + readonly codecId: 'pg/timestamptz-date@1'; + readonly typeParams: { readonly precision: 0 }; + }; + }; readonly id: { readonly nullable: false; readonly type: { readonly kind: 'scalar'; readonly codecId: 'pg/int4@1' }; @@ -682,6 +738,30 @@ type ContractBase = Omit< readonly typeParams: { readonly precision: 6 }; }; }; + readonly timestampJsDate0: { + readonly nullable: false; + readonly type: { + readonly kind: 'scalar'; + readonly codecId: 'pg/timestamp-date@1'; + readonly typeParams: { readonly precision: 0 }; + }; + }; + readonly timestampJsDate1: { + readonly nullable: false; + readonly type: { + readonly kind: 'scalar'; + readonly codecId: 'pg/timestamp-date@1'; + readonly typeParams: { readonly precision: 1 }; + }; + }; + readonly timestampJsDate2: { + readonly nullable: false; + readonly type: { + readonly kind: 'scalar'; + readonly codecId: 'pg/timestamp-date@1'; + readonly typeParams: { readonly precision: 2 }; + }; + }; readonly timestampString0: { readonly nullable: false; readonly type: { @@ -738,6 +818,22 @@ type ContractBase = Omit< readonly typeParams: { readonly precision: 0 }; }; }; + readonly timestamptzJsDate1: { + readonly nullable: false; + readonly type: { + readonly kind: 'scalar'; + readonly codecId: 'pg/timestamptz-date@1'; + readonly typeParams: { readonly precision: 1 }; + }; + }; + readonly timestamptzJsDate2: { + readonly nullable: false; + readonly type: { + readonly kind: 'scalar'; + readonly codecId: 'pg/timestamptz-date@1'; + readonly typeParams: { readonly precision: 2 }; + }; + }; readonly timestamptzJsDate3: { readonly nullable: false; readonly type: { @@ -826,6 +922,7 @@ type ContractBase = Omit< readonly fields: { readonly date: { readonly column: 'date' }; readonly dateString: { readonly column: 'dateString' }; + readonly dateTime0: { readonly column: 'dateTime0' }; readonly id: { readonly column: 'id' }; readonly label: { readonly column: 'label' }; readonly time0: { readonly column: 'time0' }; @@ -837,6 +934,9 @@ type ContractBase = Omit< readonly timestamp0: { readonly column: 'timestamp0' }; readonly timestamp3: { readonly column: 'timestamp3' }; readonly timestamp6: { readonly column: 'timestamp6' }; + readonly timestampJsDate0: { readonly column: 'timestampJsDate0' }; + readonly timestampJsDate1: { readonly column: 'timestampJsDate1' }; + readonly timestampJsDate2: { readonly column: 'timestampJsDate2' }; readonly timestampString0: { readonly column: 'timestampString0' }; readonly timestampString3: { readonly column: 'timestampString3' }; readonly timestampString6: { readonly column: 'timestampString6' }; @@ -844,6 +944,8 @@ type ContractBase = Omit< readonly timestamptz3: { readonly column: 'timestamptz3' }; readonly timestamptz6: { readonly column: 'timestamptz6' }; readonly timestamptzJsDate0: { readonly column: 'timestamptzJsDate0' }; + readonly timestamptzJsDate1: { readonly column: 'timestamptzJsDate1' }; + readonly timestamptzJsDate2: { readonly column: 'timestamptzJsDate2' }; readonly timestamptzJsDate3: { readonly column: 'timestamptzJsDate3' }; readonly timestamptzJsDate6: { readonly column: 'timestamptzJsDate6' }; readonly timestamptzString0: { readonly column: 'timestamptzString0' }; @@ -911,6 +1013,23 @@ type ContractBase = Omit< readonly namespace: 'public'; }; }, + { + readonly onCreate: { + readonly id: 'timestampNow'; + readonly kind: 'generator'; + readonly params: { readonly precision: 0 }; + }; + readonly onUpdate: { + readonly id: 'timestampNow'; + readonly kind: 'generator'; + readonly params: { readonly precision: 0 }; + }; + readonly ref: { + readonly entry: 'clock'; + readonly field: 'dateTime0'; + readonly namespace: 'public'; + }; + }, { readonly onCreate: { readonly id: 'plainTimeNow'; @@ -1040,6 +1159,57 @@ type ContractBase = Omit< readonly namespace: 'public'; }; }, + { + readonly onCreate: { + readonly id: 'timestampNow'; + readonly kind: 'generator'; + readonly params: { readonly precision: 0 }; + }; + readonly onUpdate: { + readonly id: 'timestampNow'; + readonly kind: 'generator'; + readonly params: { readonly precision: 0 }; + }; + readonly ref: { + readonly entry: 'clock'; + readonly field: 'timestampJsDate0'; + readonly namespace: 'public'; + }; + }, + { + readonly onCreate: { + readonly id: 'timestampNow'; + readonly kind: 'generator'; + readonly params: { readonly precision: 1 }; + }; + readonly onUpdate: { + readonly id: 'timestampNow'; + readonly kind: 'generator'; + readonly params: { readonly precision: 1 }; + }; + readonly ref: { + readonly entry: 'clock'; + readonly field: 'timestampJsDate1'; + readonly namespace: 'public'; + }; + }, + { + readonly onCreate: { + readonly id: 'timestampNow'; + readonly kind: 'generator'; + readonly params: { readonly precision: 2 }; + }; + readonly onUpdate: { + readonly id: 'timestampNow'; + readonly kind: 'generator'; + readonly params: { readonly precision: 2 }; + }; + readonly ref: { + readonly entry: 'clock'; + readonly field: 'timestampJsDate2'; + readonly namespace: 'public'; + }; + }, { readonly onCreate: { readonly id: 'timestampStringNow'; @@ -1119,8 +1289,16 @@ type ContractBase = Omit< }; }, { - readonly onCreate: { readonly id: 'timestampNow'; readonly kind: 'generator' }; - readonly onUpdate: { readonly id: 'timestampNow'; readonly kind: 'generator' }; + readonly onCreate: { + readonly id: 'timestampNow'; + readonly kind: 'generator'; + readonly params: { readonly precision: 0 }; + }; + readonly onUpdate: { + readonly id: 'timestampNow'; + readonly kind: 'generator'; + readonly params: { readonly precision: 0 }; + }; readonly ref: { readonly entry: 'clock'; readonly field: 'timestamptzJsDate0'; @@ -1128,8 +1306,50 @@ type ContractBase = Omit< }; }, { - readonly onCreate: { readonly id: 'timestampNow'; readonly kind: 'generator' }; - readonly onUpdate: { readonly id: 'timestampNow'; readonly kind: 'generator' }; + readonly onCreate: { + readonly id: 'timestampNow'; + readonly kind: 'generator'; + readonly params: { readonly precision: 1 }; + }; + readonly onUpdate: { + readonly id: 'timestampNow'; + readonly kind: 'generator'; + readonly params: { readonly precision: 1 }; + }; + readonly ref: { + readonly entry: 'clock'; + readonly field: 'timestamptzJsDate1'; + readonly namespace: 'public'; + }; + }, + { + readonly onCreate: { + readonly id: 'timestampNow'; + readonly kind: 'generator'; + readonly params: { readonly precision: 2 }; + }; + readonly onUpdate: { + readonly id: 'timestampNow'; + readonly kind: 'generator'; + readonly params: { readonly precision: 2 }; + }; + readonly ref: { + readonly entry: 'clock'; + readonly field: 'timestamptzJsDate2'; + readonly namespace: 'public'; + }; + }, + { + readonly onCreate: { + readonly id: 'timestampNow'; + readonly kind: 'generator'; + readonly params: { readonly precision: 3 }; + }; + readonly onUpdate: { + readonly id: 'timestampNow'; + readonly kind: 'generator'; + readonly params: { readonly precision: 3 }; + }; readonly ref: { readonly entry: 'clock'; readonly field: 'timestamptzJsDate3'; @@ -1137,8 +1357,16 @@ type ContractBase = Omit< }; }, { - readonly onCreate: { readonly id: 'timestampNow'; readonly kind: 'generator' }; - readonly onUpdate: { readonly id: 'timestampNow'; readonly kind: 'generator' }; + readonly onCreate: { + readonly id: 'timestampNow'; + readonly kind: 'generator'; + readonly params: { readonly precision: 6 }; + }; + readonly onUpdate: { + readonly id: 'timestampNow'; + readonly kind: 'generator'; + readonly params: { readonly precision: 6 }; + }; readonly ref: { readonly entry: 'clock'; readonly field: 'timestamptzJsDate6'; diff --git a/test/integration/test/temporal-defaults/_fixture-attributes/generated/contract.json b/test/integration/test/temporal-defaults/_fixture-attributes/generated/contract.json index b857e938d057..2732668f158f 100644 --- a/test/integration/test/temporal-defaults/_fixture-attributes/generated/contract.json +++ b/test/integration/test/temporal-defaults/_fixture-attributes/generated/contract.json @@ -29,6 +29,16 @@ "kind": "scalar" } }, + "dateTime0": { + "nullable": false, + "type": { + "codecId": "pg/timestamptz-date@1", + "kind": "scalar", + "typeParams": { + "precision": 0 + } + } + }, "id": { "nullable": false, "type": { @@ -133,6 +143,36 @@ } } }, + "timestampJsDate0": { + "nullable": false, + "type": { + "codecId": "pg/timestamp-date@1", + "kind": "scalar", + "typeParams": { + "precision": 0 + } + } + }, + "timestampJsDate1": { + "nullable": false, + "type": { + "codecId": "pg/timestamp-date@1", + "kind": "scalar", + "typeParams": { + "precision": 1 + } + } + }, + "timestampJsDate2": { + "nullable": false, + "type": { + "codecId": "pg/timestamp-date@1", + "kind": "scalar", + "typeParams": { + "precision": 2 + } + } + }, "timestampString0": { "nullable": false, "type": { @@ -203,6 +243,26 @@ } } }, + "timestamptzJsDate1": { + "nullable": false, + "type": { + "codecId": "pg/timestamptz-date@1", + "kind": "scalar", + "typeParams": { + "precision": 1 + } + } + }, + "timestamptzJsDate2": { + "nullable": false, + "type": { + "codecId": "pg/timestamptz-date@1", + "kind": "scalar", + "typeParams": { + "precision": 2 + } + } + }, "timestamptzJsDate3": { "nullable": false, "type": { @@ -313,6 +373,9 @@ "dateString": { "column": "dateString" }, + "dateTime0": { + "column": "dateTime0" + }, "id": { "column": "id" }, @@ -346,6 +409,15 @@ "timestamp6": { "column": "timestamp6" }, + "timestampJsDate0": { + "column": "timestampJsDate0" + }, + "timestampJsDate1": { + "column": "timestampJsDate1" + }, + "timestampJsDate2": { + "column": "timestampJsDate2" + }, "timestampString0": { "column": "timestampString0" }, @@ -367,6 +439,12 @@ "timestamptzJsDate0": { "column": "timestamptzJsDate0" }, + "timestamptzJsDate1": { + "column": "timestamptzJsDate1" + }, + "timestamptzJsDate2": { + "column": "timestamptzJsDate2" + }, "timestamptzJsDate3": { "column": "timestamptzJsDate3" }, @@ -423,6 +501,14 @@ "dataType": "pg/date", "nullable": false }, + "dateTime0": { + "codecId": "pg/timestamptz-date@1", + "dataType": "pg/timestamptz", + "nullable": false, + "typeParams": { + "precision": 0 + } + }, "id": { "codecId": "pg/int4@1", "dataType": "pg/int4", @@ -505,6 +591,30 @@ "precision": 6 } }, + "timestampJsDate0": { + "codecId": "pg/timestamp-date@1", + "dataType": "pg/timestamp", + "nullable": false, + "typeParams": { + "precision": 0 + } + }, + "timestampJsDate1": { + "codecId": "pg/timestamp-date@1", + "dataType": "pg/timestamp", + "nullable": false, + "typeParams": { + "precision": 1 + } + }, + "timestampJsDate2": { + "codecId": "pg/timestamp-date@1", + "dataType": "pg/timestamp", + "nullable": false, + "typeParams": { + "precision": 2 + } + }, "timestampString0": { "codecId": "pg/timestamp-string@1", "dataType": "pg/timestamp", @@ -561,6 +671,22 @@ "precision": 0 } }, + "timestamptzJsDate1": { + "codecId": "pg/timestamptz-date@1", + "dataType": "pg/timestamptz", + "nullable": false, + "typeParams": { + "precision": 1 + } + }, + "timestamptzJsDate2": { + "codecId": "pg/timestamptz-date@1", + "dataType": "pg/timestamptz", + "nullable": false, + "typeParams": { + "precision": 2 + } + }, "timestamptzJsDate3": { "codecId": "pg/timestamptz-date@1", "dataType": "pg/timestamptz", @@ -657,10 +783,10 @@ "kind": "postgres-schema" } }, - "storageHash": "e912a0ac1c484ed92ae8cb217638a889ad671c1f4a9bfb3e469ebfb14916c6dd" + "storageHash": "6c0d1dcd5ed439ed0d02551b3fdfd56445a86f63c5914439db906a00ced83c9d" }, "execution": { - "executionHash": "51be7e21b1611b41897bee6131dc8d5bfc6abd6380ca0bd6ba5a86d1c2f479c4", + "executionHash": "dfe82a8c68f47cc9f0da170366b163f1f4f27180594ded6a6575360c21894000", "mutations": { "defaults": [ { @@ -693,6 +819,27 @@ "namespace": "public" } }, + { + "onCreate": { + "id": "timestampNow", + "kind": "generator", + "params": { + "precision": 0 + } + }, + "onUpdate": { + "id": "timestampNow", + "kind": "generator", + "params": { + "precision": 0 + } + }, + "ref": { + "entry": "clock", + "field": "dateTime0", + "namespace": "public" + } + }, { "onCreate": { "id": "plainTimeNow", @@ -864,6 +1011,69 @@ "namespace": "public" } }, + { + "onCreate": { + "id": "timestampNow", + "kind": "generator", + "params": { + "precision": 0 + } + }, + "onUpdate": { + "id": "timestampNow", + "kind": "generator", + "params": { + "precision": 0 + } + }, + "ref": { + "entry": "clock", + "field": "timestampJsDate0", + "namespace": "public" + } + }, + { + "onCreate": { + "id": "timestampNow", + "kind": "generator", + "params": { + "precision": 1 + } + }, + "onUpdate": { + "id": "timestampNow", + "kind": "generator", + "params": { + "precision": 1 + } + }, + "ref": { + "entry": "clock", + "field": "timestampJsDate1", + "namespace": "public" + } + }, + { + "onCreate": { + "id": "timestampNow", + "kind": "generator", + "params": { + "precision": 2 + } + }, + "onUpdate": { + "id": "timestampNow", + "kind": "generator", + "params": { + "precision": 2 + } + }, + "ref": { + "entry": "clock", + "field": "timestampJsDate2", + "namespace": "public" + } + }, { "onCreate": { "id": "timestampStringNow", @@ -975,11 +1185,17 @@ { "onCreate": { "id": "timestampNow", - "kind": "generator" + "kind": "generator", + "params": { + "precision": 0 + } }, "onUpdate": { "id": "timestampNow", - "kind": "generator" + "kind": "generator", + "params": { + "precision": 0 + } }, "ref": { "entry": "clock", @@ -990,11 +1206,59 @@ { "onCreate": { "id": "timestampNow", - "kind": "generator" + "kind": "generator", + "params": { + "precision": 1 + } }, "onUpdate": { "id": "timestampNow", - "kind": "generator" + "kind": "generator", + "params": { + "precision": 1 + } + }, + "ref": { + "entry": "clock", + "field": "timestamptzJsDate1", + "namespace": "public" + } + }, + { + "onCreate": { + "id": "timestampNow", + "kind": "generator", + "params": { + "precision": 2 + } + }, + "onUpdate": { + "id": "timestampNow", + "kind": "generator", + "params": { + "precision": 2 + } + }, + "ref": { + "entry": "clock", + "field": "timestamptzJsDate2", + "namespace": "public" + } + }, + { + "onCreate": { + "id": "timestampNow", + "kind": "generator", + "params": { + "precision": 3 + } + }, + "onUpdate": { + "id": "timestampNow", + "kind": "generator", + "params": { + "precision": 3 + } }, "ref": { "entry": "clock", @@ -1005,11 +1269,17 @@ { "onCreate": { "id": "timestampNow", - "kind": "generator" + "kind": "generator", + "params": { + "precision": 6 + } }, "onUpdate": { "id": "timestampNow", - "kind": "generator" + "kind": "generator", + "params": { + "precision": 6 + } }, "ref": { "entry": "clock", diff --git a/test/integration/test/temporal-defaults/_fixture-now/contract.prisma b/test/integration/test/temporal-defaults/_fixture-now/contract.prisma index 9391d3576d3d..7f93849f1c7e 100644 --- a/test/integration/test/temporal-defaults/_fixture-now/contract.prisma +++ b/test/integration/test/temporal-defaults/_fixture-now/contract.prisma @@ -29,7 +29,13 @@ model Clock { timestamptzString3 temporal.timestamptzString(3, onCreate: now, onUpdate: now) timestamptzString6 temporal.timestamptzString(6, onCreate: now, onUpdate: now) timestamptzJsDate0 temporal.timestamptzJsDate(0, onCreate: now, onUpdate: now) + timestamptzJsDate1 temporal.timestamptzJsDate(1, onCreate: now, onUpdate: now) + timestamptzJsDate2 temporal.timestamptzJsDate(2, onCreate: now, onUpdate: now) timestamptzJsDate3 temporal.timestamptzJsDate(3, onCreate: now, onUpdate: now) timestamptzJsDate6 temporal.timestamptzJsDate(6, onCreate: now, onUpdate: now) + timestampJsDate0 TimestampJsDate(0) @onCreate(now()) @onUpdate(now()) + timestampJsDate1 TimestampJsDate(1) @onCreate(now()) @onUpdate(now()) + timestampJsDate2 TimestampJsDate(2) @onCreate(now()) @onUpdate(now()) + dateTime0 DateTime(precision: 0) @onCreate(now()) @onUpdate(now()) @@map("clock") } diff --git a/test/integration/test/temporal-defaults/_fixture-now/generated/contract.d.ts b/test/integration/test/temporal-defaults/_fixture-now/generated/contract.d.ts index 3b6b863b22c5..d9fddca137a4 100644 --- a/test/integration/test/temporal-defaults/_fixture-now/generated/contract.d.ts +++ b/test/integration/test/temporal-defaults/_fixture-now/generated/contract.d.ts @@ -23,9 +23,9 @@ import type { } from '@internal/contract/types'; export type StorageHash = - StorageHashBase<'e912a0ac1c484ed92ae8cb217638a889ad671c1f4a9bfb3e469ebfb14916c6dd'>; + StorageHashBase<'6c0d1dcd5ed439ed0d02551b3fdfd56445a86f63c5914439db906a00ced83c9d'>; export type ExecutionHash = - ExecutionHashBase<'51be7e21b1611b41897bee6131dc8d5bfc6abd6380ca0bd6ba5a86d1c2f479c4'>; + ExecutionHashBase<'dfe82a8c68f47cc9f0da170366b163f1f4f27180594ded6a6575360c21894000'>; export type ProfileHash = ProfileHashBase<'3916f444a8a17ad749191acf9e08dad97d1a327b88c2f1d45d12f240296aa8b2'>; @@ -305,6 +305,7 @@ export namespace Models { export type public_Clock = { date: FieldOutputTypes['public']['Clock']['date']; dateString: FieldOutputTypes['public']['Clock']['dateString']; + dateTime0: FieldOutputTypes['public']['Clock']['dateTime0']; id: FieldOutputTypes['public']['Clock']['id']; label: FieldOutputTypes['public']['Clock']['label']; time0: FieldOutputTypes['public']['Clock']['time0']; @@ -316,6 +317,9 @@ export namespace Models { timestamp0: FieldOutputTypes['public']['Clock']['timestamp0']; timestamp3: FieldOutputTypes['public']['Clock']['timestamp3']; timestamp6: FieldOutputTypes['public']['Clock']['timestamp6']; + timestampJsDate0: FieldOutputTypes['public']['Clock']['timestampJsDate0']; + timestampJsDate1: FieldOutputTypes['public']['Clock']['timestampJsDate1']; + timestampJsDate2: FieldOutputTypes['public']['Clock']['timestampJsDate2']; timestampString0: FieldOutputTypes['public']['Clock']['timestampString0']; timestampString3: FieldOutputTypes['public']['Clock']['timestampString3']; timestampString6: FieldOutputTypes['public']['Clock']['timestampString6']; @@ -323,6 +327,8 @@ export namespace Models { timestamptz3: FieldOutputTypes['public']['Clock']['timestamptz3']; timestamptz6: FieldOutputTypes['public']['Clock']['timestamptz6']; timestamptzJsDate0: FieldOutputTypes['public']['Clock']['timestamptzJsDate0']; + timestamptzJsDate1: FieldOutputTypes['public']['Clock']['timestamptzJsDate1']; + timestamptzJsDate2: FieldOutputTypes['public']['Clock']['timestamptzJsDate2']; timestamptzJsDate3: FieldOutputTypes['public']['Clock']['timestamptzJsDate3']; timestamptzJsDate6: FieldOutputTypes['public']['Clock']['timestamptzJsDate6']; timestamptzString0: FieldOutputTypes['public']['Clock']['timestamptzString0']; @@ -375,6 +381,13 @@ type ContractBase = Omit< readonly nullable: false; readonly many: false; }; + readonly dateTime0: { + readonly dataType: 'pg/timestamptz'; + readonly codecId: 'pg/timestamptz-date@1'; + readonly nullable: false; + readonly typeParams: { readonly precision: 0 }; + readonly many: false; + }; readonly id: { readonly dataType: 'pg/int4'; readonly codecId: 'pg/int4@1'; @@ -450,6 +463,27 @@ type ContractBase = Omit< readonly typeParams: { readonly precision: 6 }; readonly many: false; }; + readonly timestampJsDate0: { + readonly dataType: 'pg/timestamp'; + readonly codecId: 'pg/timestamp-date@1'; + readonly nullable: false; + readonly typeParams: { readonly precision: 0 }; + readonly many: false; + }; + readonly timestampJsDate1: { + readonly dataType: 'pg/timestamp'; + readonly codecId: 'pg/timestamp-date@1'; + readonly nullable: false; + readonly typeParams: { readonly precision: 1 }; + readonly many: false; + }; + readonly timestampJsDate2: { + readonly dataType: 'pg/timestamp'; + readonly codecId: 'pg/timestamp-date@1'; + readonly nullable: false; + readonly typeParams: { readonly precision: 2 }; + readonly many: false; + }; readonly timestampString0: { readonly dataType: 'pg/timestamp'; readonly codecId: 'pg/timestamp-string@1'; @@ -499,6 +533,20 @@ type ContractBase = Omit< readonly typeParams: { readonly precision: 0 }; readonly many: false; }; + readonly timestamptzJsDate1: { + readonly dataType: 'pg/timestamptz'; + readonly codecId: 'pg/timestamptz-date@1'; + readonly nullable: false; + readonly typeParams: { readonly precision: 1 }; + readonly many: false; + }; + readonly timestamptzJsDate2: { + readonly dataType: 'pg/timestamptz'; + readonly codecId: 'pg/timestamptz-date@1'; + readonly nullable: false; + readonly typeParams: { readonly precision: 2 }; + readonly many: false; + }; readonly timestamptzJsDate3: { readonly dataType: 'pg/timestamptz'; readonly codecId: 'pg/timestamptz-date@1'; @@ -602,6 +650,14 @@ type ContractBase = Omit< readonly nullable: false; readonly type: { readonly kind: 'scalar'; readonly codecId: 'pg/date-string@1' }; }; + readonly dateTime0: { + readonly nullable: false; + readonly type: { + readonly kind: 'scalar'; + readonly codecId: 'pg/timestamptz-date@1'; + readonly typeParams: { readonly precision: 0 }; + }; + }; readonly id: { readonly nullable: false; readonly type: { readonly kind: 'scalar'; readonly codecId: 'pg/int4@1' }; @@ -682,6 +738,30 @@ type ContractBase = Omit< readonly typeParams: { readonly precision: 6 }; }; }; + readonly timestampJsDate0: { + readonly nullable: false; + readonly type: { + readonly kind: 'scalar'; + readonly codecId: 'pg/timestamp-date@1'; + readonly typeParams: { readonly precision: 0 }; + }; + }; + readonly timestampJsDate1: { + readonly nullable: false; + readonly type: { + readonly kind: 'scalar'; + readonly codecId: 'pg/timestamp-date@1'; + readonly typeParams: { readonly precision: 1 }; + }; + }; + readonly timestampJsDate2: { + readonly nullable: false; + readonly type: { + readonly kind: 'scalar'; + readonly codecId: 'pg/timestamp-date@1'; + readonly typeParams: { readonly precision: 2 }; + }; + }; readonly timestampString0: { readonly nullable: false; readonly type: { @@ -738,6 +818,22 @@ type ContractBase = Omit< readonly typeParams: { readonly precision: 0 }; }; }; + readonly timestamptzJsDate1: { + readonly nullable: false; + readonly type: { + readonly kind: 'scalar'; + readonly codecId: 'pg/timestamptz-date@1'; + readonly typeParams: { readonly precision: 1 }; + }; + }; + readonly timestamptzJsDate2: { + readonly nullable: false; + readonly type: { + readonly kind: 'scalar'; + readonly codecId: 'pg/timestamptz-date@1'; + readonly typeParams: { readonly precision: 2 }; + }; + }; readonly timestamptzJsDate3: { readonly nullable: false; readonly type: { @@ -826,6 +922,7 @@ type ContractBase = Omit< readonly fields: { readonly date: { readonly column: 'date' }; readonly dateString: { readonly column: 'dateString' }; + readonly dateTime0: { readonly column: 'dateTime0' }; readonly id: { readonly column: 'id' }; readonly label: { readonly column: 'label' }; readonly time0: { readonly column: 'time0' }; @@ -837,6 +934,9 @@ type ContractBase = Omit< readonly timestamp0: { readonly column: 'timestamp0' }; readonly timestamp3: { readonly column: 'timestamp3' }; readonly timestamp6: { readonly column: 'timestamp6' }; + readonly timestampJsDate0: { readonly column: 'timestampJsDate0' }; + readonly timestampJsDate1: { readonly column: 'timestampJsDate1' }; + readonly timestampJsDate2: { readonly column: 'timestampJsDate2' }; readonly timestampString0: { readonly column: 'timestampString0' }; readonly timestampString3: { readonly column: 'timestampString3' }; readonly timestampString6: { readonly column: 'timestampString6' }; @@ -844,6 +944,8 @@ type ContractBase = Omit< readonly timestamptz3: { readonly column: 'timestamptz3' }; readonly timestamptz6: { readonly column: 'timestamptz6' }; readonly timestamptzJsDate0: { readonly column: 'timestamptzJsDate0' }; + readonly timestamptzJsDate1: { readonly column: 'timestamptzJsDate1' }; + readonly timestamptzJsDate2: { readonly column: 'timestamptzJsDate2' }; readonly timestamptzJsDate3: { readonly column: 'timestamptzJsDate3' }; readonly timestamptzJsDate6: { readonly column: 'timestamptzJsDate6' }; readonly timestamptzString0: { readonly column: 'timestamptzString0' }; @@ -911,6 +1013,23 @@ type ContractBase = Omit< readonly namespace: 'public'; }; }, + { + readonly onCreate: { + readonly id: 'timestampNow'; + readonly kind: 'generator'; + readonly params: { readonly precision: 0 }; + }; + readonly onUpdate: { + readonly id: 'timestampNow'; + readonly kind: 'generator'; + readonly params: { readonly precision: 0 }; + }; + readonly ref: { + readonly entry: 'clock'; + readonly field: 'dateTime0'; + readonly namespace: 'public'; + }; + }, { readonly onCreate: { readonly id: 'plainTimeNow'; @@ -1040,6 +1159,57 @@ type ContractBase = Omit< readonly namespace: 'public'; }; }, + { + readonly onCreate: { + readonly id: 'timestampNow'; + readonly kind: 'generator'; + readonly params: { readonly precision: 0 }; + }; + readonly onUpdate: { + readonly id: 'timestampNow'; + readonly kind: 'generator'; + readonly params: { readonly precision: 0 }; + }; + readonly ref: { + readonly entry: 'clock'; + readonly field: 'timestampJsDate0'; + readonly namespace: 'public'; + }; + }, + { + readonly onCreate: { + readonly id: 'timestampNow'; + readonly kind: 'generator'; + readonly params: { readonly precision: 1 }; + }; + readonly onUpdate: { + readonly id: 'timestampNow'; + readonly kind: 'generator'; + readonly params: { readonly precision: 1 }; + }; + readonly ref: { + readonly entry: 'clock'; + readonly field: 'timestampJsDate1'; + readonly namespace: 'public'; + }; + }, + { + readonly onCreate: { + readonly id: 'timestampNow'; + readonly kind: 'generator'; + readonly params: { readonly precision: 2 }; + }; + readonly onUpdate: { + readonly id: 'timestampNow'; + readonly kind: 'generator'; + readonly params: { readonly precision: 2 }; + }; + readonly ref: { + readonly entry: 'clock'; + readonly field: 'timestampJsDate2'; + readonly namespace: 'public'; + }; + }, { readonly onCreate: { readonly id: 'timestampStringNow'; @@ -1119,8 +1289,16 @@ type ContractBase = Omit< }; }, { - readonly onCreate: { readonly id: 'timestampNow'; readonly kind: 'generator' }; - readonly onUpdate: { readonly id: 'timestampNow'; readonly kind: 'generator' }; + readonly onCreate: { + readonly id: 'timestampNow'; + readonly kind: 'generator'; + readonly params: { readonly precision: 0 }; + }; + readonly onUpdate: { + readonly id: 'timestampNow'; + readonly kind: 'generator'; + readonly params: { readonly precision: 0 }; + }; readonly ref: { readonly entry: 'clock'; readonly field: 'timestamptzJsDate0'; @@ -1128,8 +1306,50 @@ type ContractBase = Omit< }; }, { - readonly onCreate: { readonly id: 'timestampNow'; readonly kind: 'generator' }; - readonly onUpdate: { readonly id: 'timestampNow'; readonly kind: 'generator' }; + readonly onCreate: { + readonly id: 'timestampNow'; + readonly kind: 'generator'; + readonly params: { readonly precision: 1 }; + }; + readonly onUpdate: { + readonly id: 'timestampNow'; + readonly kind: 'generator'; + readonly params: { readonly precision: 1 }; + }; + readonly ref: { + readonly entry: 'clock'; + readonly field: 'timestamptzJsDate1'; + readonly namespace: 'public'; + }; + }, + { + readonly onCreate: { + readonly id: 'timestampNow'; + readonly kind: 'generator'; + readonly params: { readonly precision: 2 }; + }; + readonly onUpdate: { + readonly id: 'timestampNow'; + readonly kind: 'generator'; + readonly params: { readonly precision: 2 }; + }; + readonly ref: { + readonly entry: 'clock'; + readonly field: 'timestamptzJsDate2'; + readonly namespace: 'public'; + }; + }, + { + readonly onCreate: { + readonly id: 'timestampNow'; + readonly kind: 'generator'; + readonly params: { readonly precision: 3 }; + }; + readonly onUpdate: { + readonly id: 'timestampNow'; + readonly kind: 'generator'; + readonly params: { readonly precision: 3 }; + }; readonly ref: { readonly entry: 'clock'; readonly field: 'timestamptzJsDate3'; @@ -1137,8 +1357,16 @@ type ContractBase = Omit< }; }, { - readonly onCreate: { readonly id: 'timestampNow'; readonly kind: 'generator' }; - readonly onUpdate: { readonly id: 'timestampNow'; readonly kind: 'generator' }; + readonly onCreate: { + readonly id: 'timestampNow'; + readonly kind: 'generator'; + readonly params: { readonly precision: 6 }; + }; + readonly onUpdate: { + readonly id: 'timestampNow'; + readonly kind: 'generator'; + readonly params: { readonly precision: 6 }; + }; readonly ref: { readonly entry: 'clock'; readonly field: 'timestamptzJsDate6'; diff --git a/test/integration/test/temporal-defaults/_fixture-now/generated/contract.json b/test/integration/test/temporal-defaults/_fixture-now/generated/contract.json index b857e938d057..2732668f158f 100644 --- a/test/integration/test/temporal-defaults/_fixture-now/generated/contract.json +++ b/test/integration/test/temporal-defaults/_fixture-now/generated/contract.json @@ -29,6 +29,16 @@ "kind": "scalar" } }, + "dateTime0": { + "nullable": false, + "type": { + "codecId": "pg/timestamptz-date@1", + "kind": "scalar", + "typeParams": { + "precision": 0 + } + } + }, "id": { "nullable": false, "type": { @@ -133,6 +143,36 @@ } } }, + "timestampJsDate0": { + "nullable": false, + "type": { + "codecId": "pg/timestamp-date@1", + "kind": "scalar", + "typeParams": { + "precision": 0 + } + } + }, + "timestampJsDate1": { + "nullable": false, + "type": { + "codecId": "pg/timestamp-date@1", + "kind": "scalar", + "typeParams": { + "precision": 1 + } + } + }, + "timestampJsDate2": { + "nullable": false, + "type": { + "codecId": "pg/timestamp-date@1", + "kind": "scalar", + "typeParams": { + "precision": 2 + } + } + }, "timestampString0": { "nullable": false, "type": { @@ -203,6 +243,26 @@ } } }, + "timestamptzJsDate1": { + "nullable": false, + "type": { + "codecId": "pg/timestamptz-date@1", + "kind": "scalar", + "typeParams": { + "precision": 1 + } + } + }, + "timestamptzJsDate2": { + "nullable": false, + "type": { + "codecId": "pg/timestamptz-date@1", + "kind": "scalar", + "typeParams": { + "precision": 2 + } + } + }, "timestamptzJsDate3": { "nullable": false, "type": { @@ -313,6 +373,9 @@ "dateString": { "column": "dateString" }, + "dateTime0": { + "column": "dateTime0" + }, "id": { "column": "id" }, @@ -346,6 +409,15 @@ "timestamp6": { "column": "timestamp6" }, + "timestampJsDate0": { + "column": "timestampJsDate0" + }, + "timestampJsDate1": { + "column": "timestampJsDate1" + }, + "timestampJsDate2": { + "column": "timestampJsDate2" + }, "timestampString0": { "column": "timestampString0" }, @@ -367,6 +439,12 @@ "timestamptzJsDate0": { "column": "timestamptzJsDate0" }, + "timestamptzJsDate1": { + "column": "timestamptzJsDate1" + }, + "timestamptzJsDate2": { + "column": "timestamptzJsDate2" + }, "timestamptzJsDate3": { "column": "timestamptzJsDate3" }, @@ -423,6 +501,14 @@ "dataType": "pg/date", "nullable": false }, + "dateTime0": { + "codecId": "pg/timestamptz-date@1", + "dataType": "pg/timestamptz", + "nullable": false, + "typeParams": { + "precision": 0 + } + }, "id": { "codecId": "pg/int4@1", "dataType": "pg/int4", @@ -505,6 +591,30 @@ "precision": 6 } }, + "timestampJsDate0": { + "codecId": "pg/timestamp-date@1", + "dataType": "pg/timestamp", + "nullable": false, + "typeParams": { + "precision": 0 + } + }, + "timestampJsDate1": { + "codecId": "pg/timestamp-date@1", + "dataType": "pg/timestamp", + "nullable": false, + "typeParams": { + "precision": 1 + } + }, + "timestampJsDate2": { + "codecId": "pg/timestamp-date@1", + "dataType": "pg/timestamp", + "nullable": false, + "typeParams": { + "precision": 2 + } + }, "timestampString0": { "codecId": "pg/timestamp-string@1", "dataType": "pg/timestamp", @@ -561,6 +671,22 @@ "precision": 0 } }, + "timestamptzJsDate1": { + "codecId": "pg/timestamptz-date@1", + "dataType": "pg/timestamptz", + "nullable": false, + "typeParams": { + "precision": 1 + } + }, + "timestamptzJsDate2": { + "codecId": "pg/timestamptz-date@1", + "dataType": "pg/timestamptz", + "nullable": false, + "typeParams": { + "precision": 2 + } + }, "timestamptzJsDate3": { "codecId": "pg/timestamptz-date@1", "dataType": "pg/timestamptz", @@ -657,10 +783,10 @@ "kind": "postgres-schema" } }, - "storageHash": "e912a0ac1c484ed92ae8cb217638a889ad671c1f4a9bfb3e469ebfb14916c6dd" + "storageHash": "6c0d1dcd5ed439ed0d02551b3fdfd56445a86f63c5914439db906a00ced83c9d" }, "execution": { - "executionHash": "51be7e21b1611b41897bee6131dc8d5bfc6abd6380ca0bd6ba5a86d1c2f479c4", + "executionHash": "dfe82a8c68f47cc9f0da170366b163f1f4f27180594ded6a6575360c21894000", "mutations": { "defaults": [ { @@ -693,6 +819,27 @@ "namespace": "public" } }, + { + "onCreate": { + "id": "timestampNow", + "kind": "generator", + "params": { + "precision": 0 + } + }, + "onUpdate": { + "id": "timestampNow", + "kind": "generator", + "params": { + "precision": 0 + } + }, + "ref": { + "entry": "clock", + "field": "dateTime0", + "namespace": "public" + } + }, { "onCreate": { "id": "plainTimeNow", @@ -864,6 +1011,69 @@ "namespace": "public" } }, + { + "onCreate": { + "id": "timestampNow", + "kind": "generator", + "params": { + "precision": 0 + } + }, + "onUpdate": { + "id": "timestampNow", + "kind": "generator", + "params": { + "precision": 0 + } + }, + "ref": { + "entry": "clock", + "field": "timestampJsDate0", + "namespace": "public" + } + }, + { + "onCreate": { + "id": "timestampNow", + "kind": "generator", + "params": { + "precision": 1 + } + }, + "onUpdate": { + "id": "timestampNow", + "kind": "generator", + "params": { + "precision": 1 + } + }, + "ref": { + "entry": "clock", + "field": "timestampJsDate1", + "namespace": "public" + } + }, + { + "onCreate": { + "id": "timestampNow", + "kind": "generator", + "params": { + "precision": 2 + } + }, + "onUpdate": { + "id": "timestampNow", + "kind": "generator", + "params": { + "precision": 2 + } + }, + "ref": { + "entry": "clock", + "field": "timestampJsDate2", + "namespace": "public" + } + }, { "onCreate": { "id": "timestampStringNow", @@ -975,11 +1185,17 @@ { "onCreate": { "id": "timestampNow", - "kind": "generator" + "kind": "generator", + "params": { + "precision": 0 + } }, "onUpdate": { "id": "timestampNow", - "kind": "generator" + "kind": "generator", + "params": { + "precision": 0 + } }, "ref": { "entry": "clock", @@ -990,11 +1206,59 @@ { "onCreate": { "id": "timestampNow", - "kind": "generator" + "kind": "generator", + "params": { + "precision": 1 + } }, "onUpdate": { "id": "timestampNow", - "kind": "generator" + "kind": "generator", + "params": { + "precision": 1 + } + }, + "ref": { + "entry": "clock", + "field": "timestamptzJsDate1", + "namespace": "public" + } + }, + { + "onCreate": { + "id": "timestampNow", + "kind": "generator", + "params": { + "precision": 2 + } + }, + "onUpdate": { + "id": "timestampNow", + "kind": "generator", + "params": { + "precision": 2 + } + }, + "ref": { + "entry": "clock", + "field": "timestamptzJsDate2", + "namespace": "public" + } + }, + { + "onCreate": { + "id": "timestampNow", + "kind": "generator", + "params": { + "precision": 3 + } + }, + "onUpdate": { + "id": "timestampNow", + "kind": "generator", + "params": { + "precision": 3 + } }, "ref": { "entry": "clock", @@ -1005,11 +1269,17 @@ { "onCreate": { "id": "timestampNow", - "kind": "generator" + "kind": "generator", + "params": { + "precision": 6 + } }, "onUpdate": { "id": "timestampNow", - "kind": "generator" + "kind": "generator", + "params": { + "precision": 6 + } }, "ref": { "entry": "clock", diff --git a/test/integration/test/temporal-defaults/now-generators-readback.integration.test.ts b/test/integration/test/temporal-defaults/now-generators-readback.integration.test.ts index afa3488a35f4..fd8038645350 100644 --- a/test/integration/test/temporal-defaults/now-generators-readback.integration.test.ts +++ b/test/integration/test/temporal-defaults/now-generators-readback.integration.test.ts @@ -42,9 +42,15 @@ const expectedAtLastMillisecond = { timestamptzString2: '2024-01-02T23:59:59.99Z', timestamptzString3: '2024-01-02T23:59:59.999Z', timestamptzString6: '2024-01-02T23:59:59.999Z', - timestamptzJsDate0: '2024-01-03T00:00:00.000Z', + timestamptzJsDate0: '2024-01-02T23:59:59.000Z', + timestamptzJsDate1: '2024-01-02T23:59:59.900Z', + timestamptzJsDate2: '2024-01-02T23:59:59.990Z', timestamptzJsDate3: '2024-01-02T23:59:59.999Z', timestamptzJsDate6: '2024-01-02T23:59:59.999Z', + timestampJsDate0: '2024-01-02T23:59:59.000Z', + timestampJsDate1: '2024-01-02T23:59:59.900Z', + timestampJsDate2: '2024-01-02T23:59:59.990Z', + dateTime0: '2024-01-02T23:59:59.000Z', }; describe('the "now" generator of every Postgres date and time codec', () => { diff --git a/test/integration/test/temporal-defaults/type-aliases-parity.test.ts b/test/integration/test/temporal-defaults/type-aliases-parity.test.ts index e407a81c97d0..b6ebb0792c9c 100644 --- a/test/integration/test/temporal-defaults/type-aliases-parity.test.ts +++ b/test/integration/test/temporal-defaults/type-aliases-parity.test.ts @@ -1,15 +1,32 @@ import { describe, expect, it } from 'vitest'; -import presets from './_fixture/generated/contract.json' with { type: 'json' }; import aliases from './_fixture-aliases/generated/contract.json' with { type: 'json' }; +const dateTime = { + codecId: 'pg/timestamptz-date@1', + dataType: 'pg/timestamptz', + nullable: false, + typeParams: { precision: 3 }, +}; +const now = { kind: 'generator', id: 'timestampNow', params: { precision: 3 } }; + describe('columns typed by the temporal type aliases', () => { - it('emit the same storage and execution hashes as the temporal presets they replace', () => { + it('store the type bare DateTime becomes, with now on create and on update', () => { + const { columns } = aliases.storage.namespaces.public.entries.table.reading; expect({ - storageHash: aliases.storage.storageHash, - executionHash: aliases.execution?.executionHash, + createdAt: columns.createdAt, + updatedAt: columns.updatedAt, + defaults: aliases.execution?.mutations.defaults, }).toEqual({ - storageHash: presets.storage.storageHash, - executionHash: presets.execution?.executionHash, + createdAt: dateTime, + updatedAt: dateTime, + defaults: [ + { ref: { namespace: 'public', entry: 'reading', field: 'createdAt' }, onCreate: now }, + { + ref: { namespace: 'public', entry: 'reading', field: 'updatedAt' }, + onCreate: now, + onUpdate: now, + }, + ], }); }); }); diff --git a/test/integration/test/upgrade-instructions/tools-write-convenience-types/detection.test.ts b/test/integration/test/upgrade-instructions/tools-write-convenience-types/detection.test.ts new file mode 100644 index 000000000000..eef02fcbd6af --- /dev/null +++ b/test/integration/test/upgrade-instructions/tools-write-convenience-types/detection.test.ts @@ -0,0 +1,92 @@ +import { readFileSync } from 'node:fs'; +import { fileURLToPath } from 'node:url'; +import { dirname, join } from 'pathe'; +import { describe, expect, it } from 'vitest'; + +const here = dirname(fileURLToPath(import.meta.url)); +const repoRoot = join(here, '../../../../..'); + +function patternsOf(id: string): readonly RegExp[] { + const guide = readFileSync( + join( + repoRoot, + 'upgrade-instructions/pending/tools-write-convenience-types/app/instructions.md', + ), + 'utf-8', + ); + const block = guide.split(/\n {2}- id: /).find((entry) => entry.startsWith(`${id}\n`)) ?? ''; + return [...block.matchAll(/^ {8}- '(.*)'$/gm)].map( + ([, pattern]) => new RegExp((pattern ?? '').replaceAll("''", "'")), + ); +} + +const matches = (id: string, content: string) => + patternsOf(id).some((pattern) => pattern.test(content)); + +describe('the detection of the changes the tools writing convenience types make', () => { + it('matches a config that reads a Prisma 7 schema, and no other', () => { + expect({ + prisma7: matches( + 'prisma7-schema-reads-dates', + "export default defineConfig({ contract: prisma7Schema('prisma/schema.prisma') });", + ), + prisma8: matches( + 'prisma7-schema-reads-dates', + "export default defineConfig({ contract: prismaSchema('prisma/schema.prisma') });", + ), + }).toEqual({ prisma7: true, prisma8: false }); + }); + + it('matches each created-at and updated-at alias in PSL and TypeScript, and not the presets', () => { + const detects = (content: string) => matches('temporal-type-aliases-on-datetime', content); + expect({ + createdAt: detects(' createdAt temporal.CreatedAt\n'), + updatedAtString: detects(' updatedAt temporal.UpdatedAtString\n'), + createdAtJsDate: detects('createdAt: field.column(type.temporal.CreatedAtJsDate()),'), + preset: detects(' updatedAt temporal.updatedAtString()\n'), + longerName: detects(' updatedAt temporal.UpdatedAtStringly\n'), + otherNamespace: detects(' updatedAt mytemporal.UpdatedAt\n'), + }).toEqual({ + createdAt: true, + updatedAtString: true, + createdAtJsDate: true, + preset: false, + longerName: false, + otherNamespace: false, + }); + }); + + it('matches a timestamp default with digits below a millisecond, and not one without', () => { + const detects = (content: string) => matches('date-default-below-a-millisecond', content); + expect({ + micro: detects( + ' at DateTime(precision: 6) @default(timestamptz`2024-01-01T01:00:00.123456Z`)\n', + ), + local: detects( + ' at DateTime(storage: timestamp) @default(timestamp`2024-01-01T01:00:00.1234`)\n', + ), + mysql: detects(' at DateTime(6) @default(datetime`2024-01-01T01:00:00.123456`)\n'), + milli: detects(' at DateTime @default(timestamptz`2024-01-01T01:00:00.123Z`)\n'), + zeros: detects(' at DateTime @default(timestamptz`2024-01-01T01:00:00.123000Z`)\n'), + }).toEqual({ micro: true, local: true, mysql: true, milli: false, zeros: false }); + }); + + it('matches a client now generator or a Prisma 7 config, and not a database default', () => { + const detects = (content: string) => matches('date-now-takes-column-precision', content); + expect({ + prisma7: detects( + "export default defineConfig({ contract: prisma7Schema('schema.prisma') });", + ), + attribute: detects(' updatedAt DateTime @onCreate(now()) @onUpdate(now())\n'), + alias: detects(' createdAt temporal.CreatedAt\n'), + typescript: detects('createdAt: field.column(type.DateTime()).onCreate(gen.now()),'), + databaseDefault: detects(' createdAt DateTime @default(now())\n'), + }).toEqual({ + prisma7: true, + attribute: true, + alias: true, + typescript: true, + databaseDefault: false, + }); + }); +}); diff --git a/upgrade-instructions/pending/prisma7-now-default-on-create/app/instructions.md b/upgrade-instructions/pending/prisma7-now-default-on-create/app/instructions.md index 9ae68850d25d..aed611614edb 100644 --- a/upgrade-instructions/pending/prisma7-now-default-on-create/app/instructions.md +++ b/upgrade-instructions/pending/prisma7-now-default-on-create/app/instructions.md @@ -25,8 +25,10 @@ model Post { } ``` -reads as the Prisma 8 PSL field `createdAt TimestampString(3) @default(now()) @onCreate(now())`. A create that supplies `createdAt` still stores the supplied value. An `INSERT` from outside Prisma still gets the column default. +reads as the Prisma 8 PSL field `createdAt DateTime(storage: timestamp) @default(now()) @onCreate(now())`: a `timestamp(3)` column read as a JavaScript `Date`. A create that supplies `createdAt` still stores the supplied value. An `INSERT` from outside Prisma still gets the column default. For each project whose `prisma.config.ts` uses `prisma7Schema(...)`, run `prisma contract emit` again and commit the new `contract.json` and `contract.d.ts`. If the schema has a `@default(now())` field, the generator's id is in the execution section of `contract.json`, so the execution hash changes. The storage hash does not change, and the database marker holds only the storage and profile hashes, so there is no migration to plan and no need to run `prisma db sign`. If the schema has no `@default(now())` field, the contract does not change and there is nothing to do. + +In the same release, `prisma7Schema(...)` also reads `DateTime` fields as a `Date` instead of a string, which changes the storage hash and does need `prisma db sign`; `prisma7-schema-reads-dates` gives those steps. diff --git a/upgrade-instructions/pending/tools-write-convenience-types/app/instructions.md b/upgrade-instructions/pending/tools-write-convenience-types/app/instructions.md new file mode 100644 index 000000000000..bf61f0970a0e --- /dev/null +++ b/upgrade-instructions/pending/tools-write-convenience-types/app/instructions.md @@ -0,0 +1,108 @@ +--- +changes: + - id: prisma7-schema-reads-dates + summary: | + A contract from `prisma7Schema(...)` on PostgreSQL now reads a Prisma 7 `DateTime` field as a JavaScript `Date`, as Prisma 7 did, instead of a string: a plain `DateTime` is `TimestampJsDate(3)` and a `@db.Timestamptz(p)` field is `TimestamptzJsDate(p)`. The columns do not change, but the contract's codecs and storage hash do. Re-emit with `prisma contract emit`, run `prisma db sign`, and change application code that read those fields as strings to read a `Date`. + detection: + glob: "**/prisma.config.{ts,mts,cts,js,mjs}" + matches: + - '\bprisma7Schema\s*\(' + - id: temporal-type-aliases-on-datetime + summary: | + On PostgreSQL, `temporal.CreatedAt` and `temporal.UpdatedAt` are now built on `DateTime`, a `timestamptz(3)` column read as a JavaScript `Date`, instead of a `timestamptz` column read as a `Temporal.Instant`, and the `temporal.CreatedAtString`, `temporal.UpdatedAtString`, `temporal.CreatedAtJsDate` and `temporal.UpdatedAtJsDate` aliases are removed, in PSL and as `type.temporal.*()` in a TypeScript contract. Write the type and the generators instead, as the table in the body shows, to keep each field and its contract. + detection: + glob: "**/*.{prisma,ts,mts,cts,tsx}" + matches: + - '(?.` and apply it with `prisma db migrate`, or run `prisma db update --delete .`. + +The detection also matches `temporal.CreatedAt` and `temporal.UpdatedAt` in SQLite and MongoDB projects. There they keep their meaning and need no change. + +## `date-default-below-a-millisecond` + +A JavaScript `Date` holds milliseconds. A column read as a `Date` now refuses a default with more digits, instead of storing it and reading it back at the millisecond. For a field such as + +```prisma +at DateTime(precision: 6) @default(timestamptz`2024-01-01T01:00:00.123456Z`) +``` + +write one of: + +```prisma +at DateTime(as: string, precision: 6) @default(timestamptz`2024-01-01T01:00:00.123456Z`) +at DateTime(precision: 6) @default(sql`'2024-01-01 01:00:00.123456+00'::timestamptz`) +``` + +The first reads the field as a string, which keeps every digit. The second keeps the field a `Date` and the database default as written. Run `prisma contract emit` again. A Prisma 7 schema read by `prisma7Schema(...)` needs no change: such a default is kept as the SQL Prisma 7 created. + +## `date-now-takes-column-precision` + +A JavaScript `Date` holds milliseconds. On a column with fewer fraction digits, such as a Prisma 7 `@db.Timestamp(0) @updatedAt` field, the client used to generate a time such as `10:00:00.700` and PostgreSQL stored `10:00:01`, so the value a create returned before a read differed from the stored one. The `now` generator of a column read as a `Date` now receives the column's precision, as the string and time generators already do, and writes `10:00:00`. + +The contract records the precision beside the generator, for example `{ "kind": "generator", "id": "timestampNow", "params": { "precision": 3 } }`. For each project with such a field, including every project whose `prisma.config.ts` uses `prisma7Schema(...)`, run `prisma contract emit` and commit the new `contract.json` and `contract.d.ts`. The storage hash does not change, and the database marker holds only the storage and profile hashes, so there is no migration to plan and no need to run `prisma db sign`. SQLite and MongoDB contracts do not change. From a26b835056429c9c7ea372d77a8a3f35d70142b6 Mon Sep 17 00:00:00 2001 From: willbot Date: Fri, 23 Oct 2026 17:31:51 +0200 Subject: [PATCH 086/118] Format the Prisma 7 dates journey test Signed-off-by: willbot Signed-off-by: Will Madden Co-Authored-By: Claude Opus 5.5 --- .../cli-journeys/prisma7-dates-without-temporal.e2e.test.ts | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/test/integration/test/cli-journeys/prisma7-dates-without-temporal.e2e.test.ts b/test/integration/test/cli-journeys/prisma7-dates-without-temporal.e2e.test.ts index 91f031832602..fdbc27bb9c4e 100644 --- a/test/integration/test/cli-journeys/prisma7-dates-without-temporal.e2e.test.ts +++ b/test/integration/test/cli-journeys/prisma7-dates-without-temporal.e2e.test.ts @@ -165,9 +165,7 @@ function output(run: EngineCommandResult): string { /** The instant of a `Date`'s JSON text, or of the ISO 8601 text of a `timestamp`, read as UTC. */ function utcMillis(timestampText: string): number { - return new Date( - timestampText.endsWith('Z') ? timestampText : `${timestampText}Z`, - ).getTime(); + return new Date(timestampText.endsWith('Z') ? timestampText : `${timestampText}Z`).getTime(); } function expectNow(timestampText: string, before: number, after: number): void { From 3bb4ef541b5a239952f859f5256e75b635a14ece Mon Sep 17 00:00:00 2001 From: willbot Date: Fri, 23 Oct 2026 17:31:53 +0200 Subject: [PATCH 087/118] now() on a Temporal timestamp column rounds down to the column precision A TimestamptzTemporal(0), TimestampTemporal(0) or TimestampInstant(p) column with @onCreate(now()) got a generated time with milliseconds, which PostgreSQL rounded up to the next second, at the end of a day into the next date. The now() function now passes the column precision to instantNow and plainDateTimeNow, as it already did for the string, Date and time generators, and they round down to it. The temporal.timestamp and temporal.timestamptz presets pass it too. Re-emitted contracts change only their execution hash; the two now() fixtures also gain columns at precision 1 and 2 and TimestampInstant columns. Signed-off-by: willbot Signed-off-by: Will Madden Co-Authored-By: Claude Opus 5.5 --- ...type alias is expanded into each column.md | 2 +- .../postgres/1-target/src/core/authoring.ts | 2 + .../src/core/date-time-now-generators.ts | 33 ++- .../1-target/src/core/now-generators.ts | 3 + .../test/authoring-field-presets.test.ts | 21 +- .../1-target/test/generator-functions.test.ts | 6 +- .../1-target/test/now-generators.test.ts | 28 +++ .../fixtures/postgres/generated/contract.d.ts | 8 +- .../fixtures/postgres/generated/contract.json | 7 +- .../_fixture-attributes/contract.prisma | 4 + .../generated/contract.d.ts | 212 ++++++++++++++-- .../generated/contract.json | 232 ++++++++++++++++-- .../_fixture-now/contract.prisma | 4 + .../_fixture-now/generated/contract.d.ts | 212 ++++++++++++++-- .../_fixture-now/generated/contract.json | 232 ++++++++++++++++-- .../generated/contract.d.ts | 26 +- .../generated/contract.json | 22 +- ...ow-generators-readback.integration.test.ts | 8 +- .../detection.test.ts | 4 + .../app/instructions.md | 5 +- 20 files changed, 964 insertions(+), 107 deletions(-) diff --git a/docs/architecture docs/adrs/ADR 275 - Client generators are field attributes, and a type alias is expanded into each column.md b/docs/architecture docs/adrs/ADR 275 - Client generators are field attributes, and a type alias is expanded into each column.md index 83da45596a56..f1c51de024eb 100644 --- a/docs/architecture docs/adrs/ADR 275 - Client generators are field attributes, and a type alias is expanded into each column.md +++ b/docs/architecture docs/adrs/ADR 275 - Client generators are field attributes, and a type alias is expanded into each column.md @@ -78,7 +78,7 @@ For Postgres, `now` maps each date and time codec to its generator. That map exi | `pg/date-temporal@1`, `pg/date-string@1` | `plainDateNow`, `dateStringNow` | none | | `pg/time-temporal@1`, `pg/time-string@1`, `pg/timetz@1` | `plainTimeNow`, `timeStringNow`, `timetzNow` | `precision` | -(Amended 2026-10-23: the string and `Date` timestamp codecs, `pg/timestamptz-string@1`, `pg/timestamp-string@1`, `pg/timestamptz-date@1` and `pg/timestamp-date@1`, also pass `precision`, and their generators round down to it, so the client holds the value PostgreSQL stores instead of one PostgreSQL rounds up.) +(Amended 2026-10-23: every timestamp codec also passes `precision`, and its generator rounds down to it, so the client holds the value PostgreSQL stores instead of one PostgreSQL rounds up: the string and `Date` codecs, `pg/timestamptz-string@1`, `pg/timestamp-string@1`, `pg/timestamptz-date@1` and `pg/timestamp-date@1`, and the `Temporal` codecs, `pg/timestamptz-temporal@1`, `pg/timestamp-temporal@1` and `pg/timestamp-instant@1`. Only the date codecs, which have no precision, pass none.) SQLite maps `sqlite/datetime@1` to `timestampNow`. Mongo maps `mongo/date@1` to `timestampNow`. diff --git a/packages/3-targets/postgres/1-target/src/core/authoring.ts b/packages/3-targets/postgres/1-target/src/core/authoring.ts index 36049816d8b3..8d039785b321 100644 --- a/packages/3-targets/postgres/1-target/src/core/authoring.ts +++ b/packages/3-targets/postgres/1-target/src/core/authoring.ts @@ -932,11 +932,13 @@ export const postgresAuthoringFieldPresets = { sizeType: PG_SIZE_TYPE, codecId: PG_TIMESTAMP_TEMPORAL_CODEC_ID, generatorId: postgresNowGeneratorIds[PG_TIMESTAMP_TEMPORAL_CODEC_ID], + nowTakesPrecision: postgresNowGeneratorTakesPrecision[PG_TIMESTAMP_TEMPORAL_CODEC_ID], }), timestamptz: /* @__PURE__ */ temporalCodecPresetWithPrecision({ sizeType: PG_SIZE_TYPE, codecId: PG_TIMESTAMPTZ_TEMPORAL_CODEC_ID, generatorId: postgresNowGeneratorIds[PG_TIMESTAMPTZ_TEMPORAL_CODEC_ID], + nowTakesPrecision: postgresNowGeneratorTakesPrecision[PG_TIMESTAMPTZ_TEMPORAL_CODEC_ID], }), timestampString: /* @__PURE__ */ temporalCodecPresetWithPrecision({ sizeType: PG_SIZE_TYPE, diff --git a/packages/3-targets/postgres/1-target/src/core/date-time-now-generators.ts b/packages/3-targets/postgres/1-target/src/core/date-time-now-generators.ts index 854b6f628871..2b6a6695d4c8 100644 --- a/packages/3-targets/postgres/1-target/src/core/date-time-now-generators.ts +++ b/packages/3-targets/postgres/1-target/src/core/date-time-now-generators.ts @@ -3,7 +3,7 @@ import type { Temporal } from 'temporal-spec'; import { requireTemporal } from './require-temporal'; /** - * The Postgres "now" generators, one per date and time codec except `pg/timestamptz-date@1` and `pg/timestamp-date@1`, which use the SQL family's `timestampNow`. All read the clock through `Date.now()` and write UTC. The time generators and the string timestamp generators round down to their `precision` param, so the column stores the value as written. The string generators are named after the codec they serve and write the codec's stored form (ADR 254, "Date and time types") in UTC, with milliseconds and no trailing zeros in the fraction. + * The Postgres "now" generators, one per date and time codec except `pg/timestamptz-date@1` and `pg/timestamp-date@1`, which use the SQL family's `timestampNow`. All read the clock through `Date.now()` and write UTC. The time and timestamp generators round down to their `precision` param, so the column stores the value as written. The string generators are named after the codec they serve and write the codec's stored form (ADR 254, "Date and time types") in UTC, with milliseconds and no trailing zeros in the fraction. */ export const INSTANT_NOW_GENERATOR_ID = 'instantNow' as const; export const PLAIN_DATE_TIME_NOW_GENERATOR_ID = 'plainDateTimeNow' as const; @@ -17,6 +17,7 @@ export const TIMETZ_NOW_GENERATOR_ID = 'timetzNow' as const; const UTC_OFFSET = 'Z'; const MAX_TIME_PRECISION = 6; +const MILLISECOND_DIGITS = 3; type GeneratorParams = Readonly> | undefined; @@ -50,20 +51,32 @@ function truncateTime(time: Temporal.PlainTime, fractionDigits: number): Tempora }); } -function utcDateTimeNow(generatorId: string): Temporal.ZonedDateTime { - return requireTemporal({ generatorId }) - .Instant.fromEpochMilliseconds(Date.now()) - .toZonedDateTimeISO('UTC'); +function epochMillisecondsNow(fractionDigits = MAX_TIME_PRECISION): number { + const now = Date.now(); + if (fractionDigits >= MILLISECOND_DIGITS) return now; + const step = 10 ** (MILLISECOND_DIGITS - Math.max(fractionDigits, 0)); + return Math.floor(now / step) * step; } -export function instantNow(): Temporal.Instant { - return requireTemporal({ generatorId: INSTANT_NOW_GENERATOR_ID }).Instant.fromEpochMilliseconds( - Date.now(), +function utcInstantNow(generatorId: string, fractionDigits?: number): Temporal.Instant { + return requireTemporal({ generatorId }).Instant.fromEpochMilliseconds( + epochMillisecondsNow(fractionDigits), ); } -export function plainDateTimeNow(): Temporal.PlainDateTime { - return utcDateTimeNow(PLAIN_DATE_TIME_NOW_GENERATOR_ID).toPlainDateTime(); +function utcDateTimeNow(generatorId: string, fractionDigits?: number): Temporal.ZonedDateTime { + return utcInstantNow(generatorId, fractionDigits).toZonedDateTimeISO('UTC'); +} + +export function instantNow(params?: GeneratorParams): Temporal.Instant { + return utcInstantNow(INSTANT_NOW_GENERATOR_ID, fractionDigitsOf(params)); +} + +export function plainDateTimeNow(params?: GeneratorParams): Temporal.PlainDateTime { + return utcDateTimeNow( + PLAIN_DATE_TIME_NOW_GENERATOR_ID, + fractionDigitsOf(params), + ).toPlainDateTime(); } export function plainDateNow(): Temporal.PlainDate { diff --git a/packages/3-targets/postgres/1-target/src/core/now-generators.ts b/packages/3-targets/postgres/1-target/src/core/now-generators.ts index 8bb5b9b01be8..f28eb7696d90 100644 --- a/packages/3-targets/postgres/1-target/src/core/now-generators.ts +++ b/packages/3-targets/postgres/1-target/src/core/now-generators.ts @@ -60,6 +60,9 @@ export const postgresNowGeneratorTakesPrecision = { [PG_TIMESTAMPTZ_STRING_CODEC_ID]: true, [PG_TIMESTAMPTZ_DATE_CODEC_ID]: true, [PG_TIMESTAMP_DATE_CODEC_ID]: true, + [PG_TIMESTAMP_TEMPORAL_CODEC_ID]: true, + [PG_TIMESTAMPTZ_TEMPORAL_CODEC_ID]: true, + [PG_TIMESTAMP_INSTANT_CODEC_ID]: true, } as const; /** The `now` generator function bound to each Postgres date and time codec, with the column's precision on the codecs that take it. */ diff --git a/packages/3-targets/postgres/1-target/test/authoring-field-presets.test.ts b/packages/3-targets/postgres/1-target/test/authoring-field-presets.test.ts index 52dd6bb89df5..e8c134b17c1e 100644 --- a/packages/3-targets/postgres/1-target/test/authoring-field-presets.test.ts +++ b/packages/3-targets/postgres/1-target/test/authoring-field-presets.test.ts @@ -122,11 +122,13 @@ describe('postgres temporal per-codec presets', () => { sizeType: 'pg/int4', codecId: 'pg/timestamp-temporal@1', generatorId: 'plainDateTimeNow', + nowTakesPrecision: true, }), timestamptz: temporalCodecPresetWithPrecision({ sizeType: 'pg/int4', codecId: 'pg/timestamptz-temporal@1', generatorId: 'instantNow', + nowTakesPrecision: true, }), timestampString: temporalCodecPresetWithPrecision({ sizeType: 'pg/int4', @@ -169,25 +171,6 @@ describe('postgres temporal per-codec presets', () => { it.each([ ['timestamp', 'plainDateTimeNow'], ['timestamptz', 'instantNow'], - ] as const)( - 'temporal.%s answers `now` with the %s generator, the representation its codec encodes', - (helper, generatorId) => { - expect(postgresAuthoringFieldPresets.temporal[helper].output.executionDefaults).toEqual({ - onCreate: { - kind: 'select', - index: 1, - cases: { now: { kind: 'generator', id: generatorId } }, - }, - onUpdate: { - kind: 'select', - index: 2, - cases: { now: { kind: 'generator', id: generatorId } }, - }, - }); - }, - ); - - it.each([ ['time', 'plainTimeNow'], ['timeString', 'timeStringNow'], ['timetz', 'timetzNow'], diff --git a/packages/3-targets/postgres/1-target/test/generator-functions.test.ts b/packages/3-targets/postgres/1-target/test/generator-functions.test.ts index bfeba03ceea1..dfbce407fbb2 100644 --- a/packages/3-targets/postgres/1-target/test/generator-functions.test.ts +++ b/packages/3-targets/postgres/1-target/test/generator-functions.test.ts @@ -11,9 +11,6 @@ function resolve(name: string, codecId: string, typeParams?: Record { it.each([ - ['pg/timestamptz-temporal@1', 'instantNow'], - ['pg/timestamp-temporal@1', 'plainDateTimeNow'], - ['pg/timestamp-instant@1', 'instantNow'], ['pg/date-temporal@1', 'plainDateNow'], ['pg/date-string@1', 'dateStringNow'], ])('resolve now() on %s to %s with no params', (codecId, generatorId) => { @@ -31,6 +28,9 @@ describe('the Postgres generator functions', () => { ['pg/timestamp-string@1', 'timestampStringNow'], ['pg/timestamptz-date@1', 'timestampNow'], ['pg/timestamp-date@1', 'timestampNow'], + ['pg/timestamptz-temporal@1', 'instantNow'], + ['pg/timestamp-temporal@1', 'plainDateTimeNow'], + ['pg/timestamp-instant@1', 'instantNow'], ])("resolve now() on %s to %s with the column's precision", (codecId, generatorId) => { expect({ withPrecision: resolve('now', codecId, { precision: 3 }), diff --git a/packages/3-targets/postgres/1-target/test/now-generators.test.ts b/packages/3-targets/postgres/1-target/test/now-generators.test.ts index f9e4843f3ff2..ea044317a1bc 100644 --- a/packages/3-targets/postgres/1-target/test/now-generators.test.ts +++ b/packages/3-targets/postgres/1-target/test/now-generators.test.ts @@ -172,6 +172,9 @@ describe('the text "now" generators', () => { 'pg/timestamptz-string@1': true, 'pg/timestamptz-date@1': true, 'pg/timestamp-date@1': true, + 'pg/timestamptz-temporal@1': true, + 'pg/timestamp-temporal@1': true, + 'pg/timestamp-instant@1': true, }); }); @@ -208,6 +211,31 @@ describe('the time "now" generators', () => { }); }); +describe('the Temporal timestamp "now" generators', () => { + afterEach(() => { + vi.useRealTimers(); + }); + + it.each([ + [0, '2024-01-02T23:59:59'], + [1, '2024-01-02T23:59:59.9'], + [2, '2024-01-02T23:59:59.99'], + [3, '2024-01-02T23:59:59.999'], + [6, '2024-01-02T23:59:59.999'], + [undefined, '2024-01-02T23:59:59.999'], + ])( + 'round down to precision %s, so the column stores the value the client holds', + (precision, text) => { + vi.useFakeTimers({ now: new Date('2024-01-02T23:59:59.999Z') }); + const params = precision === undefined ? undefined : { precision }; + expect([instantNow(params).toString(), plainDateTimeNow(params).toString()]).toEqual([ + `${text}Z`, + text, + ]); + }, + ); +}); + describe('the Temporal date and time "now" generators', () => { const DAY_MILLISECONDS = 86_400_000; const utcDate = (epochMilliseconds: number) => diff --git a/test/integration/test/contract-parity/fixtures/postgres/generated/contract.d.ts b/test/integration/test/contract-parity/fixtures/postgres/generated/contract.d.ts index c9f5bae1c1b6..9a4c847ee823 100644 --- a/test/integration/test/contract-parity/fixtures/postgres/generated/contract.d.ts +++ b/test/integration/test/contract-parity/fixtures/postgres/generated/contract.d.ts @@ -26,7 +26,7 @@ import type { export type StorageHash = StorageHashBase<'4e5aa929c76c9181dd640f59692e6743a0626b5ab9023a02e5960ec96509c225'>; export type ExecutionHash = - ExecutionHashBase<'3212cd023cdce9916f261da80be0ba8357cd4263dde560c3b8164673d80e9d4b'>; + ExecutionHashBase<'4a96a7146e13898bf97aa37dba3cc7c9cae8e2599993a25be7fe3bdb78b45b9e'>; export type ProfileHash = ProfileHashBase<'3916f444a8a17ad749191acf9e08dad97d1a327b88c2f1d45d12f240296aa8b2'>; @@ -2242,7 +2242,11 @@ type ContractBase = Omit< }; }, { - readonly onCreate: { readonly id: 'instantNow'; readonly kind: 'generator' }; + readonly onCreate: { + readonly id: 'instantNow'; + readonly kind: 'generator'; + readonly params: { readonly precision: 6 }; + }; readonly ref: { readonly entry: 'users'; readonly field: 'timestamptz'; diff --git a/test/integration/test/contract-parity/fixtures/postgres/generated/contract.json b/test/integration/test/contract-parity/fixtures/postgres/generated/contract.json index 230eecb4ff9a..c6e1edf26ec2 100644 --- a/test/integration/test/contract-parity/fixtures/postgres/generated/contract.json +++ b/test/integration/test/contract-parity/fixtures/postgres/generated/contract.json @@ -2127,7 +2127,7 @@ "storageHash": "4e5aa929c76c9181dd640f59692e6743a0626b5ab9023a02e5960ec96509c225" }, "execution": { - "executionHash": "3212cd023cdce9916f261da80be0ba8357cd4263dde560c3b8164673d80e9d4b", + "executionHash": "4a96a7146e13898bf97aa37dba3cc7c9cae8e2599993a25be7fe3bdb78b45b9e", "mutations": { "defaults": [ { @@ -2345,7 +2345,10 @@ { "onCreate": { "id": "instantNow", - "kind": "generator" + "kind": "generator", + "params": { + "precision": 6 + } }, "ref": { "entry": "users", diff --git a/test/integration/test/temporal-defaults/_fixture-attributes/contract.prisma b/test/integration/test/temporal-defaults/_fixture-attributes/contract.prisma index 27ae42868309..1e0ef91abaaf 100644 --- a/test/integration/test/temporal-defaults/_fixture-attributes/contract.prisma +++ b/test/integration/test/temporal-defaults/_fixture-attributes/contract.prisma @@ -15,12 +15,14 @@ model Clock { timetz3 Timetz(3) @onCreate(now()) @onUpdate(now()) timetz6 Timetz(6) @onCreate(now()) @onUpdate(now()) timestamp0 TimestampTemporal(0) @onCreate(now()) @onUpdate(now()) + timestamp1 TimestampTemporal(1) @onCreate(now()) @onUpdate(now()) timestamp3 TimestampTemporal(3) @onCreate(now()) @onUpdate(now()) timestamp6 TimestampTemporal(6) @onCreate(now()) @onUpdate(now()) timestampString0 TimestampString(0) @onCreate(now()) @onUpdate(now()) timestampString3 TimestampString(3) @onCreate(now()) @onUpdate(now()) timestampString6 TimestampString(6) @onCreate(now()) @onUpdate(now()) timestamptz0 TimestamptzTemporal(0) @onCreate(now()) @onUpdate(now()) + timestamptz2 TimestamptzTemporal(2) @onCreate(now()) @onUpdate(now()) timestamptz3 TimestamptzTemporal(3) @onCreate(now()) @onUpdate(now()) timestamptz6 TimestamptzTemporal(6) @onCreate(now()) @onUpdate(now()) timestamptzString0 TimestamptzString(0) @onCreate(now()) @onUpdate(now()) @@ -36,6 +38,8 @@ model Clock { timestampJsDate0 TimestampJsDate(0) @onCreate(now()) @onUpdate(now()) timestampJsDate1 TimestampJsDate(1) @onCreate(now()) @onUpdate(now()) timestampJsDate2 TimestampJsDate(2) @onCreate(now()) @onUpdate(now()) + timestampInstant0 TimestampInstant(0) @onCreate(now()) @onUpdate(now()) + timestampInstant2 TimestampInstant(2) @onCreate(now()) @onUpdate(now()) dateTime0 DateTime(precision: 0) @onCreate(now()) @onUpdate(now()) @@map("clock") } diff --git a/test/integration/test/temporal-defaults/_fixture-attributes/generated/contract.d.ts b/test/integration/test/temporal-defaults/_fixture-attributes/generated/contract.d.ts index d9fddca137a4..2c11e8c27d4a 100644 --- a/test/integration/test/temporal-defaults/_fixture-attributes/generated/contract.d.ts +++ b/test/integration/test/temporal-defaults/_fixture-attributes/generated/contract.d.ts @@ -23,9 +23,9 @@ import type { } from '@internal/contract/types'; export type StorageHash = - StorageHashBase<'6c0d1dcd5ed439ed0d02551b3fdfd56445a86f63c5914439db906a00ced83c9d'>; + StorageHashBase<'738a9bc34c15df72b535e7a13fcf0e72883a57d137b88ff175eeed75af13f33b'>; export type ExecutionHash = - ExecutionHashBase<'dfe82a8c68f47cc9f0da170366b163f1f4f27180594ded6a6575360c21894000'>; + ExecutionHashBase<'4eaa44c328b6ff71b05bc0c8397058c76293da5aad12e41c649f4c2f63fb67f6'>; export type ProfileHash = ProfileHashBase<'3916f444a8a17ad749191acf9e08dad97d1a327b88c2f1d45d12f240296aa8b2'>; @@ -315,8 +315,11 @@ export namespace Models { timeString3: FieldOutputTypes['public']['Clock']['timeString3']; timeString6: FieldOutputTypes['public']['Clock']['timeString6']; timestamp0: FieldOutputTypes['public']['Clock']['timestamp0']; + timestamp1: FieldOutputTypes['public']['Clock']['timestamp1']; timestamp3: FieldOutputTypes['public']['Clock']['timestamp3']; timestamp6: FieldOutputTypes['public']['Clock']['timestamp6']; + timestampInstant0: FieldOutputTypes['public']['Clock']['timestampInstant0']; + timestampInstant2: FieldOutputTypes['public']['Clock']['timestampInstant2']; timestampJsDate0: FieldOutputTypes['public']['Clock']['timestampJsDate0']; timestampJsDate1: FieldOutputTypes['public']['Clock']['timestampJsDate1']; timestampJsDate2: FieldOutputTypes['public']['Clock']['timestampJsDate2']; @@ -324,6 +327,7 @@ export namespace Models { timestampString3: FieldOutputTypes['public']['Clock']['timestampString3']; timestampString6: FieldOutputTypes['public']['Clock']['timestampString6']; timestamptz0: FieldOutputTypes['public']['Clock']['timestamptz0']; + timestamptz2: FieldOutputTypes['public']['Clock']['timestamptz2']; timestamptz3: FieldOutputTypes['public']['Clock']['timestamptz3']; timestamptz6: FieldOutputTypes['public']['Clock']['timestamptz6']; timestamptzJsDate0: FieldOutputTypes['public']['Clock']['timestamptzJsDate0']; @@ -449,6 +453,13 @@ type ContractBase = Omit< readonly typeParams: { readonly precision: 0 }; readonly many: false; }; + readonly timestamp1: { + readonly dataType: 'pg/timestamp'; + readonly codecId: 'pg/timestamp-temporal@1'; + readonly nullable: false; + readonly typeParams: { readonly precision: 1 }; + readonly many: false; + }; readonly timestamp3: { readonly dataType: 'pg/timestamp'; readonly codecId: 'pg/timestamp-temporal@1'; @@ -463,6 +474,20 @@ type ContractBase = Omit< readonly typeParams: { readonly precision: 6 }; readonly many: false; }; + readonly timestampInstant0: { + readonly dataType: 'pg/timestamp'; + readonly codecId: 'pg/timestamp-instant@1'; + readonly nullable: false; + readonly typeParams: { readonly precision: 0 }; + readonly many: false; + }; + readonly timestampInstant2: { + readonly dataType: 'pg/timestamp'; + readonly codecId: 'pg/timestamp-instant@1'; + readonly nullable: false; + readonly typeParams: { readonly precision: 2 }; + readonly many: false; + }; readonly timestampJsDate0: { readonly dataType: 'pg/timestamp'; readonly codecId: 'pg/timestamp-date@1'; @@ -512,6 +537,13 @@ type ContractBase = Omit< readonly typeParams: { readonly precision: 0 }; readonly many: false; }; + readonly timestamptz2: { + readonly dataType: 'pg/timestamptz'; + readonly codecId: 'pg/timestamptz-temporal@1'; + readonly nullable: false; + readonly typeParams: { readonly precision: 2 }; + readonly many: false; + }; readonly timestamptz3: { readonly dataType: 'pg/timestamptz'; readonly codecId: 'pg/timestamptz-temporal@1'; @@ -722,6 +754,14 @@ type ContractBase = Omit< readonly typeParams: { readonly precision: 0 }; }; }; + readonly timestamp1: { + readonly nullable: false; + readonly type: { + readonly kind: 'scalar'; + readonly codecId: 'pg/timestamp-temporal@1'; + readonly typeParams: { readonly precision: 1 }; + }; + }; readonly timestamp3: { readonly nullable: false; readonly type: { @@ -738,6 +778,22 @@ type ContractBase = Omit< readonly typeParams: { readonly precision: 6 }; }; }; + readonly timestampInstant0: { + readonly nullable: false; + readonly type: { + readonly kind: 'scalar'; + readonly codecId: 'pg/timestamp-instant@1'; + readonly typeParams: { readonly precision: 0 }; + }; + }; + readonly timestampInstant2: { + readonly nullable: false; + readonly type: { + readonly kind: 'scalar'; + readonly codecId: 'pg/timestamp-instant@1'; + readonly typeParams: { readonly precision: 2 }; + }; + }; readonly timestampJsDate0: { readonly nullable: false; readonly type: { @@ -794,6 +850,14 @@ type ContractBase = Omit< readonly typeParams: { readonly precision: 0 }; }; }; + readonly timestamptz2: { + readonly nullable: false; + readonly type: { + readonly kind: 'scalar'; + readonly codecId: 'pg/timestamptz-temporal@1'; + readonly typeParams: { readonly precision: 2 }; + }; + }; readonly timestamptz3: { readonly nullable: false; readonly type: { @@ -932,8 +996,11 @@ type ContractBase = Omit< readonly timeString3: { readonly column: 'timeString3' }; readonly timeString6: { readonly column: 'timeString6' }; readonly timestamp0: { readonly column: 'timestamp0' }; + readonly timestamp1: { readonly column: 'timestamp1' }; readonly timestamp3: { readonly column: 'timestamp3' }; readonly timestamp6: { readonly column: 'timestamp6' }; + readonly timestampInstant0: { readonly column: 'timestampInstant0' }; + readonly timestampInstant2: { readonly column: 'timestampInstant2' }; readonly timestampJsDate0: { readonly column: 'timestampJsDate0' }; readonly timestampJsDate1: { readonly column: 'timestampJsDate1' }; readonly timestampJsDate2: { readonly column: 'timestampJsDate2' }; @@ -941,6 +1008,7 @@ type ContractBase = Omit< readonly timestampString3: { readonly column: 'timestampString3' }; readonly timestampString6: { readonly column: 'timestampString6' }; readonly timestamptz0: { readonly column: 'timestamptz0' }; + readonly timestamptz2: { readonly column: 'timestamptz2' }; readonly timestamptz3: { readonly column: 'timestamptz3' }; readonly timestamptz6: { readonly column: 'timestamptz6' }; readonly timestamptzJsDate0: { readonly column: 'timestamptzJsDate0' }; @@ -1133,8 +1201,16 @@ type ContractBase = Omit< }; }, { - readonly onCreate: { readonly id: 'plainDateTimeNow'; readonly kind: 'generator' }; - readonly onUpdate: { readonly id: 'plainDateTimeNow'; readonly kind: 'generator' }; + readonly onCreate: { + readonly id: 'plainDateTimeNow'; + readonly kind: 'generator'; + readonly params: { readonly precision: 0 }; + }; + readonly onUpdate: { + readonly id: 'plainDateTimeNow'; + readonly kind: 'generator'; + readonly params: { readonly precision: 0 }; + }; readonly ref: { readonly entry: 'clock'; readonly field: 'timestamp0'; @@ -1142,8 +1218,33 @@ type ContractBase = Omit< }; }, { - readonly onCreate: { readonly id: 'plainDateTimeNow'; readonly kind: 'generator' }; - readonly onUpdate: { readonly id: 'plainDateTimeNow'; readonly kind: 'generator' }; + readonly onCreate: { + readonly id: 'plainDateTimeNow'; + readonly kind: 'generator'; + readonly params: { readonly precision: 1 }; + }; + readonly onUpdate: { + readonly id: 'plainDateTimeNow'; + readonly kind: 'generator'; + readonly params: { readonly precision: 1 }; + }; + readonly ref: { + readonly entry: 'clock'; + readonly field: 'timestamp1'; + readonly namespace: 'public'; + }; + }, + { + readonly onCreate: { + readonly id: 'plainDateTimeNow'; + readonly kind: 'generator'; + readonly params: { readonly precision: 3 }; + }; + readonly onUpdate: { + readonly id: 'plainDateTimeNow'; + readonly kind: 'generator'; + readonly params: { readonly precision: 3 }; + }; readonly ref: { readonly entry: 'clock'; readonly field: 'timestamp3'; @@ -1151,14 +1252,56 @@ type ContractBase = Omit< }; }, { - readonly onCreate: { readonly id: 'plainDateTimeNow'; readonly kind: 'generator' }; - readonly onUpdate: { readonly id: 'plainDateTimeNow'; readonly kind: 'generator' }; + readonly onCreate: { + readonly id: 'plainDateTimeNow'; + readonly kind: 'generator'; + readonly params: { readonly precision: 6 }; + }; + readonly onUpdate: { + readonly id: 'plainDateTimeNow'; + readonly kind: 'generator'; + readonly params: { readonly precision: 6 }; + }; readonly ref: { readonly entry: 'clock'; readonly field: 'timestamp6'; readonly namespace: 'public'; }; }, + { + readonly onCreate: { + readonly id: 'instantNow'; + readonly kind: 'generator'; + readonly params: { readonly precision: 0 }; + }; + readonly onUpdate: { + readonly id: 'instantNow'; + readonly kind: 'generator'; + readonly params: { readonly precision: 0 }; + }; + readonly ref: { + readonly entry: 'clock'; + readonly field: 'timestampInstant0'; + readonly namespace: 'public'; + }; + }, + { + readonly onCreate: { + readonly id: 'instantNow'; + readonly kind: 'generator'; + readonly params: { readonly precision: 2 }; + }; + readonly onUpdate: { + readonly id: 'instantNow'; + readonly kind: 'generator'; + readonly params: { readonly precision: 2 }; + }; + readonly ref: { + readonly entry: 'clock'; + readonly field: 'timestampInstant2'; + readonly namespace: 'public'; + }; + }, { readonly onCreate: { readonly id: 'timestampNow'; @@ -1262,8 +1405,16 @@ type ContractBase = Omit< }; }, { - readonly onCreate: { readonly id: 'instantNow'; readonly kind: 'generator' }; - readonly onUpdate: { readonly id: 'instantNow'; readonly kind: 'generator' }; + readonly onCreate: { + readonly id: 'instantNow'; + readonly kind: 'generator'; + readonly params: { readonly precision: 0 }; + }; + readonly onUpdate: { + readonly id: 'instantNow'; + readonly kind: 'generator'; + readonly params: { readonly precision: 0 }; + }; readonly ref: { readonly entry: 'clock'; readonly field: 'timestamptz0'; @@ -1271,8 +1422,33 @@ type ContractBase = Omit< }; }, { - readonly onCreate: { readonly id: 'instantNow'; readonly kind: 'generator' }; - readonly onUpdate: { readonly id: 'instantNow'; readonly kind: 'generator' }; + readonly onCreate: { + readonly id: 'instantNow'; + readonly kind: 'generator'; + readonly params: { readonly precision: 2 }; + }; + readonly onUpdate: { + readonly id: 'instantNow'; + readonly kind: 'generator'; + readonly params: { readonly precision: 2 }; + }; + readonly ref: { + readonly entry: 'clock'; + readonly field: 'timestamptz2'; + readonly namespace: 'public'; + }; + }, + { + readonly onCreate: { + readonly id: 'instantNow'; + readonly kind: 'generator'; + readonly params: { readonly precision: 3 }; + }; + readonly onUpdate: { + readonly id: 'instantNow'; + readonly kind: 'generator'; + readonly params: { readonly precision: 3 }; + }; readonly ref: { readonly entry: 'clock'; readonly field: 'timestamptz3'; @@ -1280,8 +1456,16 @@ type ContractBase = Omit< }; }, { - readonly onCreate: { readonly id: 'instantNow'; readonly kind: 'generator' }; - readonly onUpdate: { readonly id: 'instantNow'; readonly kind: 'generator' }; + readonly onCreate: { + readonly id: 'instantNow'; + readonly kind: 'generator'; + readonly params: { readonly precision: 6 }; + }; + readonly onUpdate: { + readonly id: 'instantNow'; + readonly kind: 'generator'; + readonly params: { readonly precision: 6 }; + }; readonly ref: { readonly entry: 'clock'; readonly field: 'timestamptz6'; diff --git a/test/integration/test/temporal-defaults/_fixture-attributes/generated/contract.json b/test/integration/test/temporal-defaults/_fixture-attributes/generated/contract.json index 2732668f158f..10e456cc02d1 100644 --- a/test/integration/test/temporal-defaults/_fixture-attributes/generated/contract.json +++ b/test/integration/test/temporal-defaults/_fixture-attributes/generated/contract.json @@ -123,6 +123,16 @@ } } }, + "timestamp1": { + "nullable": false, + "type": { + "codecId": "pg/timestamp-temporal@1", + "kind": "scalar", + "typeParams": { + "precision": 1 + } + } + }, "timestamp3": { "nullable": false, "type": { @@ -143,6 +153,26 @@ } } }, + "timestampInstant0": { + "nullable": false, + "type": { + "codecId": "pg/timestamp-instant@1", + "kind": "scalar", + "typeParams": { + "precision": 0 + } + } + }, + "timestampInstant2": { + "nullable": false, + "type": { + "codecId": "pg/timestamp-instant@1", + "kind": "scalar", + "typeParams": { + "precision": 2 + } + } + }, "timestampJsDate0": { "nullable": false, "type": { @@ -213,6 +243,16 @@ } } }, + "timestamptz2": { + "nullable": false, + "type": { + "codecId": "pg/timestamptz-temporal@1", + "kind": "scalar", + "typeParams": { + "precision": 2 + } + } + }, "timestamptz3": { "nullable": false, "type": { @@ -403,12 +443,21 @@ "timestamp0": { "column": "timestamp0" }, + "timestamp1": { + "column": "timestamp1" + }, "timestamp3": { "column": "timestamp3" }, "timestamp6": { "column": "timestamp6" }, + "timestampInstant0": { + "column": "timestampInstant0" + }, + "timestampInstant2": { + "column": "timestampInstant2" + }, "timestampJsDate0": { "column": "timestampJsDate0" }, @@ -430,6 +479,9 @@ "timestamptz0": { "column": "timestamptz0" }, + "timestamptz2": { + "column": "timestamptz2" + }, "timestamptz3": { "column": "timestamptz3" }, @@ -575,6 +627,14 @@ "precision": 0 } }, + "timestamp1": { + "codecId": "pg/timestamp-temporal@1", + "dataType": "pg/timestamp", + "nullable": false, + "typeParams": { + "precision": 1 + } + }, "timestamp3": { "codecId": "pg/timestamp-temporal@1", "dataType": "pg/timestamp", @@ -591,6 +651,22 @@ "precision": 6 } }, + "timestampInstant0": { + "codecId": "pg/timestamp-instant@1", + "dataType": "pg/timestamp", + "nullable": false, + "typeParams": { + "precision": 0 + } + }, + "timestampInstant2": { + "codecId": "pg/timestamp-instant@1", + "dataType": "pg/timestamp", + "nullable": false, + "typeParams": { + "precision": 2 + } + }, "timestampJsDate0": { "codecId": "pg/timestamp-date@1", "dataType": "pg/timestamp", @@ -647,6 +723,14 @@ "precision": 0 } }, + "timestamptz2": { + "codecId": "pg/timestamptz-temporal@1", + "dataType": "pg/timestamptz", + "nullable": false, + "typeParams": { + "precision": 2 + } + }, "timestamptz3": { "codecId": "pg/timestamptz-temporal@1", "dataType": "pg/timestamptz", @@ -783,10 +867,10 @@ "kind": "postgres-schema" } }, - "storageHash": "6c0d1dcd5ed439ed0d02551b3fdfd56445a86f63c5914439db906a00ced83c9d" + "storageHash": "738a9bc34c15df72b535e7a13fcf0e72883a57d137b88ff175eeed75af13f33b" }, "execution": { - "executionHash": "dfe82a8c68f47cc9f0da170366b163f1f4f27180594ded6a6575360c21894000", + "executionHash": "4eaa44c328b6ff71b05bc0c8397058c76293da5aad12e41c649f4c2f63fb67f6", "mutations": { "defaults": [ { @@ -969,11 +1053,17 @@ { "onCreate": { "id": "plainDateTimeNow", - "kind": "generator" + "kind": "generator", + "params": { + "precision": 0 + } }, "onUpdate": { "id": "plainDateTimeNow", - "kind": "generator" + "kind": "generator", + "params": { + "precision": 0 + } }, "ref": { "entry": "clock", @@ -984,11 +1074,38 @@ { "onCreate": { "id": "plainDateTimeNow", - "kind": "generator" + "kind": "generator", + "params": { + "precision": 1 + } }, "onUpdate": { "id": "plainDateTimeNow", - "kind": "generator" + "kind": "generator", + "params": { + "precision": 1 + } + }, + "ref": { + "entry": "clock", + "field": "timestamp1", + "namespace": "public" + } + }, + { + "onCreate": { + "id": "plainDateTimeNow", + "kind": "generator", + "params": { + "precision": 3 + } + }, + "onUpdate": { + "id": "plainDateTimeNow", + "kind": "generator", + "params": { + "precision": 3 + } }, "ref": { "entry": "clock", @@ -999,11 +1116,17 @@ { "onCreate": { "id": "plainDateTimeNow", - "kind": "generator" + "kind": "generator", + "params": { + "precision": 6 + } }, "onUpdate": { "id": "plainDateTimeNow", - "kind": "generator" + "kind": "generator", + "params": { + "precision": 6 + } }, "ref": { "entry": "clock", @@ -1011,6 +1134,48 @@ "namespace": "public" } }, + { + "onCreate": { + "id": "instantNow", + "kind": "generator", + "params": { + "precision": 0 + } + }, + "onUpdate": { + "id": "instantNow", + "kind": "generator", + "params": { + "precision": 0 + } + }, + "ref": { + "entry": "clock", + "field": "timestampInstant0", + "namespace": "public" + } + }, + { + "onCreate": { + "id": "instantNow", + "kind": "generator", + "params": { + "precision": 2 + } + }, + "onUpdate": { + "id": "instantNow", + "kind": "generator", + "params": { + "precision": 2 + } + }, + "ref": { + "entry": "clock", + "field": "timestampInstant2", + "namespace": "public" + } + }, { "onCreate": { "id": "timestampNow", @@ -1140,11 +1305,17 @@ { "onCreate": { "id": "instantNow", - "kind": "generator" + "kind": "generator", + "params": { + "precision": 0 + } }, "onUpdate": { "id": "instantNow", - "kind": "generator" + "kind": "generator", + "params": { + "precision": 0 + } }, "ref": { "entry": "clock", @@ -1155,11 +1326,38 @@ { "onCreate": { "id": "instantNow", - "kind": "generator" + "kind": "generator", + "params": { + "precision": 2 + } }, "onUpdate": { "id": "instantNow", - "kind": "generator" + "kind": "generator", + "params": { + "precision": 2 + } + }, + "ref": { + "entry": "clock", + "field": "timestamptz2", + "namespace": "public" + } + }, + { + "onCreate": { + "id": "instantNow", + "kind": "generator", + "params": { + "precision": 3 + } + }, + "onUpdate": { + "id": "instantNow", + "kind": "generator", + "params": { + "precision": 3 + } }, "ref": { "entry": "clock", @@ -1170,11 +1368,17 @@ { "onCreate": { "id": "instantNow", - "kind": "generator" + "kind": "generator", + "params": { + "precision": 6 + } }, "onUpdate": { "id": "instantNow", - "kind": "generator" + "kind": "generator", + "params": { + "precision": 6 + } }, "ref": { "entry": "clock", diff --git a/test/integration/test/temporal-defaults/_fixture-now/contract.prisma b/test/integration/test/temporal-defaults/_fixture-now/contract.prisma index 7f93849f1c7e..ba40aeb8c8bc 100644 --- a/test/integration/test/temporal-defaults/_fixture-now/contract.prisma +++ b/test/integration/test/temporal-defaults/_fixture-now/contract.prisma @@ -15,12 +15,14 @@ model Clock { timetz3 temporal.timetz(3, onCreate: now, onUpdate: now) timetz6 temporal.timetz(6, onCreate: now, onUpdate: now) timestamp0 temporal.timestamp(0, onCreate: now, onUpdate: now) + timestamp1 temporal.timestamp(1, onCreate: now, onUpdate: now) timestamp3 temporal.timestamp(3, onCreate: now, onUpdate: now) timestamp6 temporal.timestamp(6, onCreate: now, onUpdate: now) timestampString0 temporal.timestampString(0, onCreate: now, onUpdate: now) timestampString3 temporal.timestampString(3, onCreate: now, onUpdate: now) timestampString6 temporal.timestampString(6, onCreate: now, onUpdate: now) timestamptz0 temporal.timestamptz(0, onCreate: now, onUpdate: now) + timestamptz2 temporal.timestamptz(2, onCreate: now, onUpdate: now) timestamptz3 temporal.timestamptz(3, onCreate: now, onUpdate: now) timestamptz6 temporal.timestamptz(6, onCreate: now, onUpdate: now) timestamptzString0 temporal.timestamptzString(0, onCreate: now, onUpdate: now) @@ -36,6 +38,8 @@ model Clock { timestampJsDate0 TimestampJsDate(0) @onCreate(now()) @onUpdate(now()) timestampJsDate1 TimestampJsDate(1) @onCreate(now()) @onUpdate(now()) timestampJsDate2 TimestampJsDate(2) @onCreate(now()) @onUpdate(now()) + timestampInstant0 TimestampInstant(0) @onCreate(now()) @onUpdate(now()) + timestampInstant2 TimestampInstant(2) @onCreate(now()) @onUpdate(now()) dateTime0 DateTime(precision: 0) @onCreate(now()) @onUpdate(now()) @@map("clock") } diff --git a/test/integration/test/temporal-defaults/_fixture-now/generated/contract.d.ts b/test/integration/test/temporal-defaults/_fixture-now/generated/contract.d.ts index d9fddca137a4..2c11e8c27d4a 100644 --- a/test/integration/test/temporal-defaults/_fixture-now/generated/contract.d.ts +++ b/test/integration/test/temporal-defaults/_fixture-now/generated/contract.d.ts @@ -23,9 +23,9 @@ import type { } from '@internal/contract/types'; export type StorageHash = - StorageHashBase<'6c0d1dcd5ed439ed0d02551b3fdfd56445a86f63c5914439db906a00ced83c9d'>; + StorageHashBase<'738a9bc34c15df72b535e7a13fcf0e72883a57d137b88ff175eeed75af13f33b'>; export type ExecutionHash = - ExecutionHashBase<'dfe82a8c68f47cc9f0da170366b163f1f4f27180594ded6a6575360c21894000'>; + ExecutionHashBase<'4eaa44c328b6ff71b05bc0c8397058c76293da5aad12e41c649f4c2f63fb67f6'>; export type ProfileHash = ProfileHashBase<'3916f444a8a17ad749191acf9e08dad97d1a327b88c2f1d45d12f240296aa8b2'>; @@ -315,8 +315,11 @@ export namespace Models { timeString3: FieldOutputTypes['public']['Clock']['timeString3']; timeString6: FieldOutputTypes['public']['Clock']['timeString6']; timestamp0: FieldOutputTypes['public']['Clock']['timestamp0']; + timestamp1: FieldOutputTypes['public']['Clock']['timestamp1']; timestamp3: FieldOutputTypes['public']['Clock']['timestamp3']; timestamp6: FieldOutputTypes['public']['Clock']['timestamp6']; + timestampInstant0: FieldOutputTypes['public']['Clock']['timestampInstant0']; + timestampInstant2: FieldOutputTypes['public']['Clock']['timestampInstant2']; timestampJsDate0: FieldOutputTypes['public']['Clock']['timestampJsDate0']; timestampJsDate1: FieldOutputTypes['public']['Clock']['timestampJsDate1']; timestampJsDate2: FieldOutputTypes['public']['Clock']['timestampJsDate2']; @@ -324,6 +327,7 @@ export namespace Models { timestampString3: FieldOutputTypes['public']['Clock']['timestampString3']; timestampString6: FieldOutputTypes['public']['Clock']['timestampString6']; timestamptz0: FieldOutputTypes['public']['Clock']['timestamptz0']; + timestamptz2: FieldOutputTypes['public']['Clock']['timestamptz2']; timestamptz3: FieldOutputTypes['public']['Clock']['timestamptz3']; timestamptz6: FieldOutputTypes['public']['Clock']['timestamptz6']; timestamptzJsDate0: FieldOutputTypes['public']['Clock']['timestamptzJsDate0']; @@ -449,6 +453,13 @@ type ContractBase = Omit< readonly typeParams: { readonly precision: 0 }; readonly many: false; }; + readonly timestamp1: { + readonly dataType: 'pg/timestamp'; + readonly codecId: 'pg/timestamp-temporal@1'; + readonly nullable: false; + readonly typeParams: { readonly precision: 1 }; + readonly many: false; + }; readonly timestamp3: { readonly dataType: 'pg/timestamp'; readonly codecId: 'pg/timestamp-temporal@1'; @@ -463,6 +474,20 @@ type ContractBase = Omit< readonly typeParams: { readonly precision: 6 }; readonly many: false; }; + readonly timestampInstant0: { + readonly dataType: 'pg/timestamp'; + readonly codecId: 'pg/timestamp-instant@1'; + readonly nullable: false; + readonly typeParams: { readonly precision: 0 }; + readonly many: false; + }; + readonly timestampInstant2: { + readonly dataType: 'pg/timestamp'; + readonly codecId: 'pg/timestamp-instant@1'; + readonly nullable: false; + readonly typeParams: { readonly precision: 2 }; + readonly many: false; + }; readonly timestampJsDate0: { readonly dataType: 'pg/timestamp'; readonly codecId: 'pg/timestamp-date@1'; @@ -512,6 +537,13 @@ type ContractBase = Omit< readonly typeParams: { readonly precision: 0 }; readonly many: false; }; + readonly timestamptz2: { + readonly dataType: 'pg/timestamptz'; + readonly codecId: 'pg/timestamptz-temporal@1'; + readonly nullable: false; + readonly typeParams: { readonly precision: 2 }; + readonly many: false; + }; readonly timestamptz3: { readonly dataType: 'pg/timestamptz'; readonly codecId: 'pg/timestamptz-temporal@1'; @@ -722,6 +754,14 @@ type ContractBase = Omit< readonly typeParams: { readonly precision: 0 }; }; }; + readonly timestamp1: { + readonly nullable: false; + readonly type: { + readonly kind: 'scalar'; + readonly codecId: 'pg/timestamp-temporal@1'; + readonly typeParams: { readonly precision: 1 }; + }; + }; readonly timestamp3: { readonly nullable: false; readonly type: { @@ -738,6 +778,22 @@ type ContractBase = Omit< readonly typeParams: { readonly precision: 6 }; }; }; + readonly timestampInstant0: { + readonly nullable: false; + readonly type: { + readonly kind: 'scalar'; + readonly codecId: 'pg/timestamp-instant@1'; + readonly typeParams: { readonly precision: 0 }; + }; + }; + readonly timestampInstant2: { + readonly nullable: false; + readonly type: { + readonly kind: 'scalar'; + readonly codecId: 'pg/timestamp-instant@1'; + readonly typeParams: { readonly precision: 2 }; + }; + }; readonly timestampJsDate0: { readonly nullable: false; readonly type: { @@ -794,6 +850,14 @@ type ContractBase = Omit< readonly typeParams: { readonly precision: 0 }; }; }; + readonly timestamptz2: { + readonly nullable: false; + readonly type: { + readonly kind: 'scalar'; + readonly codecId: 'pg/timestamptz-temporal@1'; + readonly typeParams: { readonly precision: 2 }; + }; + }; readonly timestamptz3: { readonly nullable: false; readonly type: { @@ -932,8 +996,11 @@ type ContractBase = Omit< readonly timeString3: { readonly column: 'timeString3' }; readonly timeString6: { readonly column: 'timeString6' }; readonly timestamp0: { readonly column: 'timestamp0' }; + readonly timestamp1: { readonly column: 'timestamp1' }; readonly timestamp3: { readonly column: 'timestamp3' }; readonly timestamp6: { readonly column: 'timestamp6' }; + readonly timestampInstant0: { readonly column: 'timestampInstant0' }; + readonly timestampInstant2: { readonly column: 'timestampInstant2' }; readonly timestampJsDate0: { readonly column: 'timestampJsDate0' }; readonly timestampJsDate1: { readonly column: 'timestampJsDate1' }; readonly timestampJsDate2: { readonly column: 'timestampJsDate2' }; @@ -941,6 +1008,7 @@ type ContractBase = Omit< readonly timestampString3: { readonly column: 'timestampString3' }; readonly timestampString6: { readonly column: 'timestampString6' }; readonly timestamptz0: { readonly column: 'timestamptz0' }; + readonly timestamptz2: { readonly column: 'timestamptz2' }; readonly timestamptz3: { readonly column: 'timestamptz3' }; readonly timestamptz6: { readonly column: 'timestamptz6' }; readonly timestamptzJsDate0: { readonly column: 'timestamptzJsDate0' }; @@ -1133,8 +1201,16 @@ type ContractBase = Omit< }; }, { - readonly onCreate: { readonly id: 'plainDateTimeNow'; readonly kind: 'generator' }; - readonly onUpdate: { readonly id: 'plainDateTimeNow'; readonly kind: 'generator' }; + readonly onCreate: { + readonly id: 'plainDateTimeNow'; + readonly kind: 'generator'; + readonly params: { readonly precision: 0 }; + }; + readonly onUpdate: { + readonly id: 'plainDateTimeNow'; + readonly kind: 'generator'; + readonly params: { readonly precision: 0 }; + }; readonly ref: { readonly entry: 'clock'; readonly field: 'timestamp0'; @@ -1142,8 +1218,33 @@ type ContractBase = Omit< }; }, { - readonly onCreate: { readonly id: 'plainDateTimeNow'; readonly kind: 'generator' }; - readonly onUpdate: { readonly id: 'plainDateTimeNow'; readonly kind: 'generator' }; + readonly onCreate: { + readonly id: 'plainDateTimeNow'; + readonly kind: 'generator'; + readonly params: { readonly precision: 1 }; + }; + readonly onUpdate: { + readonly id: 'plainDateTimeNow'; + readonly kind: 'generator'; + readonly params: { readonly precision: 1 }; + }; + readonly ref: { + readonly entry: 'clock'; + readonly field: 'timestamp1'; + readonly namespace: 'public'; + }; + }, + { + readonly onCreate: { + readonly id: 'plainDateTimeNow'; + readonly kind: 'generator'; + readonly params: { readonly precision: 3 }; + }; + readonly onUpdate: { + readonly id: 'plainDateTimeNow'; + readonly kind: 'generator'; + readonly params: { readonly precision: 3 }; + }; readonly ref: { readonly entry: 'clock'; readonly field: 'timestamp3'; @@ -1151,14 +1252,56 @@ type ContractBase = Omit< }; }, { - readonly onCreate: { readonly id: 'plainDateTimeNow'; readonly kind: 'generator' }; - readonly onUpdate: { readonly id: 'plainDateTimeNow'; readonly kind: 'generator' }; + readonly onCreate: { + readonly id: 'plainDateTimeNow'; + readonly kind: 'generator'; + readonly params: { readonly precision: 6 }; + }; + readonly onUpdate: { + readonly id: 'plainDateTimeNow'; + readonly kind: 'generator'; + readonly params: { readonly precision: 6 }; + }; readonly ref: { readonly entry: 'clock'; readonly field: 'timestamp6'; readonly namespace: 'public'; }; }, + { + readonly onCreate: { + readonly id: 'instantNow'; + readonly kind: 'generator'; + readonly params: { readonly precision: 0 }; + }; + readonly onUpdate: { + readonly id: 'instantNow'; + readonly kind: 'generator'; + readonly params: { readonly precision: 0 }; + }; + readonly ref: { + readonly entry: 'clock'; + readonly field: 'timestampInstant0'; + readonly namespace: 'public'; + }; + }, + { + readonly onCreate: { + readonly id: 'instantNow'; + readonly kind: 'generator'; + readonly params: { readonly precision: 2 }; + }; + readonly onUpdate: { + readonly id: 'instantNow'; + readonly kind: 'generator'; + readonly params: { readonly precision: 2 }; + }; + readonly ref: { + readonly entry: 'clock'; + readonly field: 'timestampInstant2'; + readonly namespace: 'public'; + }; + }, { readonly onCreate: { readonly id: 'timestampNow'; @@ -1262,8 +1405,16 @@ type ContractBase = Omit< }; }, { - readonly onCreate: { readonly id: 'instantNow'; readonly kind: 'generator' }; - readonly onUpdate: { readonly id: 'instantNow'; readonly kind: 'generator' }; + readonly onCreate: { + readonly id: 'instantNow'; + readonly kind: 'generator'; + readonly params: { readonly precision: 0 }; + }; + readonly onUpdate: { + readonly id: 'instantNow'; + readonly kind: 'generator'; + readonly params: { readonly precision: 0 }; + }; readonly ref: { readonly entry: 'clock'; readonly field: 'timestamptz0'; @@ -1271,8 +1422,33 @@ type ContractBase = Omit< }; }, { - readonly onCreate: { readonly id: 'instantNow'; readonly kind: 'generator' }; - readonly onUpdate: { readonly id: 'instantNow'; readonly kind: 'generator' }; + readonly onCreate: { + readonly id: 'instantNow'; + readonly kind: 'generator'; + readonly params: { readonly precision: 2 }; + }; + readonly onUpdate: { + readonly id: 'instantNow'; + readonly kind: 'generator'; + readonly params: { readonly precision: 2 }; + }; + readonly ref: { + readonly entry: 'clock'; + readonly field: 'timestamptz2'; + readonly namespace: 'public'; + }; + }, + { + readonly onCreate: { + readonly id: 'instantNow'; + readonly kind: 'generator'; + readonly params: { readonly precision: 3 }; + }; + readonly onUpdate: { + readonly id: 'instantNow'; + readonly kind: 'generator'; + readonly params: { readonly precision: 3 }; + }; readonly ref: { readonly entry: 'clock'; readonly field: 'timestamptz3'; @@ -1280,8 +1456,16 @@ type ContractBase = Omit< }; }, { - readonly onCreate: { readonly id: 'instantNow'; readonly kind: 'generator' }; - readonly onUpdate: { readonly id: 'instantNow'; readonly kind: 'generator' }; + readonly onCreate: { + readonly id: 'instantNow'; + readonly kind: 'generator'; + readonly params: { readonly precision: 6 }; + }; + readonly onUpdate: { + readonly id: 'instantNow'; + readonly kind: 'generator'; + readonly params: { readonly precision: 6 }; + }; readonly ref: { readonly entry: 'clock'; readonly field: 'timestamptz6'; diff --git a/test/integration/test/temporal-defaults/_fixture-now/generated/contract.json b/test/integration/test/temporal-defaults/_fixture-now/generated/contract.json index 2732668f158f..10e456cc02d1 100644 --- a/test/integration/test/temporal-defaults/_fixture-now/generated/contract.json +++ b/test/integration/test/temporal-defaults/_fixture-now/generated/contract.json @@ -123,6 +123,16 @@ } } }, + "timestamp1": { + "nullable": false, + "type": { + "codecId": "pg/timestamp-temporal@1", + "kind": "scalar", + "typeParams": { + "precision": 1 + } + } + }, "timestamp3": { "nullable": false, "type": { @@ -143,6 +153,26 @@ } } }, + "timestampInstant0": { + "nullable": false, + "type": { + "codecId": "pg/timestamp-instant@1", + "kind": "scalar", + "typeParams": { + "precision": 0 + } + } + }, + "timestampInstant2": { + "nullable": false, + "type": { + "codecId": "pg/timestamp-instant@1", + "kind": "scalar", + "typeParams": { + "precision": 2 + } + } + }, "timestampJsDate0": { "nullable": false, "type": { @@ -213,6 +243,16 @@ } } }, + "timestamptz2": { + "nullable": false, + "type": { + "codecId": "pg/timestamptz-temporal@1", + "kind": "scalar", + "typeParams": { + "precision": 2 + } + } + }, "timestamptz3": { "nullable": false, "type": { @@ -403,12 +443,21 @@ "timestamp0": { "column": "timestamp0" }, + "timestamp1": { + "column": "timestamp1" + }, "timestamp3": { "column": "timestamp3" }, "timestamp6": { "column": "timestamp6" }, + "timestampInstant0": { + "column": "timestampInstant0" + }, + "timestampInstant2": { + "column": "timestampInstant2" + }, "timestampJsDate0": { "column": "timestampJsDate0" }, @@ -430,6 +479,9 @@ "timestamptz0": { "column": "timestamptz0" }, + "timestamptz2": { + "column": "timestamptz2" + }, "timestamptz3": { "column": "timestamptz3" }, @@ -575,6 +627,14 @@ "precision": 0 } }, + "timestamp1": { + "codecId": "pg/timestamp-temporal@1", + "dataType": "pg/timestamp", + "nullable": false, + "typeParams": { + "precision": 1 + } + }, "timestamp3": { "codecId": "pg/timestamp-temporal@1", "dataType": "pg/timestamp", @@ -591,6 +651,22 @@ "precision": 6 } }, + "timestampInstant0": { + "codecId": "pg/timestamp-instant@1", + "dataType": "pg/timestamp", + "nullable": false, + "typeParams": { + "precision": 0 + } + }, + "timestampInstant2": { + "codecId": "pg/timestamp-instant@1", + "dataType": "pg/timestamp", + "nullable": false, + "typeParams": { + "precision": 2 + } + }, "timestampJsDate0": { "codecId": "pg/timestamp-date@1", "dataType": "pg/timestamp", @@ -647,6 +723,14 @@ "precision": 0 } }, + "timestamptz2": { + "codecId": "pg/timestamptz-temporal@1", + "dataType": "pg/timestamptz", + "nullable": false, + "typeParams": { + "precision": 2 + } + }, "timestamptz3": { "codecId": "pg/timestamptz-temporal@1", "dataType": "pg/timestamptz", @@ -783,10 +867,10 @@ "kind": "postgres-schema" } }, - "storageHash": "6c0d1dcd5ed439ed0d02551b3fdfd56445a86f63c5914439db906a00ced83c9d" + "storageHash": "738a9bc34c15df72b535e7a13fcf0e72883a57d137b88ff175eeed75af13f33b" }, "execution": { - "executionHash": "dfe82a8c68f47cc9f0da170366b163f1f4f27180594ded6a6575360c21894000", + "executionHash": "4eaa44c328b6ff71b05bc0c8397058c76293da5aad12e41c649f4c2f63fb67f6", "mutations": { "defaults": [ { @@ -969,11 +1053,17 @@ { "onCreate": { "id": "plainDateTimeNow", - "kind": "generator" + "kind": "generator", + "params": { + "precision": 0 + } }, "onUpdate": { "id": "plainDateTimeNow", - "kind": "generator" + "kind": "generator", + "params": { + "precision": 0 + } }, "ref": { "entry": "clock", @@ -984,11 +1074,38 @@ { "onCreate": { "id": "plainDateTimeNow", - "kind": "generator" + "kind": "generator", + "params": { + "precision": 1 + } }, "onUpdate": { "id": "plainDateTimeNow", - "kind": "generator" + "kind": "generator", + "params": { + "precision": 1 + } + }, + "ref": { + "entry": "clock", + "field": "timestamp1", + "namespace": "public" + } + }, + { + "onCreate": { + "id": "plainDateTimeNow", + "kind": "generator", + "params": { + "precision": 3 + } + }, + "onUpdate": { + "id": "plainDateTimeNow", + "kind": "generator", + "params": { + "precision": 3 + } }, "ref": { "entry": "clock", @@ -999,11 +1116,17 @@ { "onCreate": { "id": "plainDateTimeNow", - "kind": "generator" + "kind": "generator", + "params": { + "precision": 6 + } }, "onUpdate": { "id": "plainDateTimeNow", - "kind": "generator" + "kind": "generator", + "params": { + "precision": 6 + } }, "ref": { "entry": "clock", @@ -1011,6 +1134,48 @@ "namespace": "public" } }, + { + "onCreate": { + "id": "instantNow", + "kind": "generator", + "params": { + "precision": 0 + } + }, + "onUpdate": { + "id": "instantNow", + "kind": "generator", + "params": { + "precision": 0 + } + }, + "ref": { + "entry": "clock", + "field": "timestampInstant0", + "namespace": "public" + } + }, + { + "onCreate": { + "id": "instantNow", + "kind": "generator", + "params": { + "precision": 2 + } + }, + "onUpdate": { + "id": "instantNow", + "kind": "generator", + "params": { + "precision": 2 + } + }, + "ref": { + "entry": "clock", + "field": "timestampInstant2", + "namespace": "public" + } + }, { "onCreate": { "id": "timestampNow", @@ -1140,11 +1305,17 @@ { "onCreate": { "id": "instantNow", - "kind": "generator" + "kind": "generator", + "params": { + "precision": 0 + } }, "onUpdate": { "id": "instantNow", - "kind": "generator" + "kind": "generator", + "params": { + "precision": 0 + } }, "ref": { "entry": "clock", @@ -1155,11 +1326,38 @@ { "onCreate": { "id": "instantNow", - "kind": "generator" + "kind": "generator", + "params": { + "precision": 2 + } }, "onUpdate": { "id": "instantNow", - "kind": "generator" + "kind": "generator", + "params": { + "precision": 2 + } + }, + "ref": { + "entry": "clock", + "field": "timestamptz2", + "namespace": "public" + } + }, + { + "onCreate": { + "id": "instantNow", + "kind": "generator", + "params": { + "precision": 3 + } + }, + "onUpdate": { + "id": "instantNow", + "kind": "generator", + "params": { + "precision": 3 + } }, "ref": { "entry": "clock", @@ -1170,11 +1368,17 @@ { "onCreate": { "id": "instantNow", - "kind": "generator" + "kind": "generator", + "params": { + "precision": 6 + } }, "onUpdate": { "id": "instantNow", - "kind": "generator" + "kind": "generator", + "params": { + "precision": 6 + } }, "ref": { "entry": "clock", diff --git a/test/integration/test/temporal-defaults/_fixture-timestamp/generated/contract.d.ts b/test/integration/test/temporal-defaults/_fixture-timestamp/generated/contract.d.ts index 207db1337b24..fdb85aedf3f3 100644 --- a/test/integration/test/temporal-defaults/_fixture-timestamp/generated/contract.d.ts +++ b/test/integration/test/temporal-defaults/_fixture-timestamp/generated/contract.d.ts @@ -25,7 +25,7 @@ import type { export type StorageHash = StorageHashBase<'a3de0e95bb70b3a6106b3f84c9f7162b697f7f18d34c390e26200f08d60c159f'>; export type ExecutionHash = - ExecutionHashBase<'75fabf32f466f30de3808c2f1f8b7a60d87724063dfb332b032468a6621ad80a'>; + ExecutionHashBase<'a10097483cd46a82fb13f13a0605fc0c0c6454f2f99eea3e3ed6db4590cea32f'>; export type ProfileHash = ProfileHashBase<'3916f444a8a17ad749191acf9e08dad97d1a327b88c2f1d45d12f240296aa8b2'>; @@ -462,8 +462,16 @@ type ContractBase = Omit< readonly mutations: { readonly defaults: readonly [ { - readonly onCreate: { readonly id: 'plainDateTimeNow'; readonly kind: 'generator' }; - readonly onUpdate: { readonly id: 'plainDateTimeNow'; readonly kind: 'generator' }; + readonly onCreate: { + readonly id: 'plainDateTimeNow'; + readonly kind: 'generator'; + readonly params: { readonly precision: 3 }; + }; + readonly onUpdate: { + readonly id: 'plainDateTimeNow'; + readonly kind: 'generator'; + readonly params: { readonly precision: 3 }; + }; readonly ref: { readonly entry: 'stamp'; readonly field: 'updatedAt'; @@ -471,8 +479,16 @@ type ContractBase = Omit< }; }, { - readonly onCreate: { readonly id: 'instantNow'; readonly kind: 'generator' }; - readonly onUpdate: { readonly id: 'instantNow'; readonly kind: 'generator' }; + readonly onCreate: { + readonly id: 'instantNow'; + readonly kind: 'generator'; + readonly params: { readonly precision: 3 }; + }; + readonly onUpdate: { + readonly id: 'instantNow'; + readonly kind: 'generator'; + readonly params: { readonly precision: 3 }; + }; readonly ref: { readonly entry: 'stamp'; readonly field: 'updatedAtTz'; diff --git a/test/integration/test/temporal-defaults/_fixture-timestamp/generated/contract.json b/test/integration/test/temporal-defaults/_fixture-timestamp/generated/contract.json index ff63a5e3d4ed..051a82dd75e9 100644 --- a/test/integration/test/temporal-defaults/_fixture-timestamp/generated/contract.json +++ b/test/integration/test/temporal-defaults/_fixture-timestamp/generated/contract.json @@ -126,17 +126,23 @@ "storageHash": "a3de0e95bb70b3a6106b3f84c9f7162b697f7f18d34c390e26200f08d60c159f" }, "execution": { - "executionHash": "75fabf32f466f30de3808c2f1f8b7a60d87724063dfb332b032468a6621ad80a", + "executionHash": "a10097483cd46a82fb13f13a0605fc0c0c6454f2f99eea3e3ed6db4590cea32f", "mutations": { "defaults": [ { "onCreate": { "id": "plainDateTimeNow", - "kind": "generator" + "kind": "generator", + "params": { + "precision": 3 + } }, "onUpdate": { "id": "plainDateTimeNow", - "kind": "generator" + "kind": "generator", + "params": { + "precision": 3 + } }, "ref": { "entry": "stamp", @@ -147,11 +153,17 @@ { "onCreate": { "id": "instantNow", - "kind": "generator" + "kind": "generator", + "params": { + "precision": 3 + } }, "onUpdate": { "id": "instantNow", - "kind": "generator" + "kind": "generator", + "params": { + "precision": 3 + } }, "ref": { "entry": "stamp", diff --git a/test/integration/test/temporal-defaults/now-generators-readback.integration.test.ts b/test/integration/test/temporal-defaults/now-generators-readback.integration.test.ts index fd8038645350..0f7598ff5e62 100644 --- a/test/integration/test/temporal-defaults/now-generators-readback.integration.test.ts +++ b/test/integration/test/temporal-defaults/now-generators-readback.integration.test.ts @@ -28,13 +28,15 @@ const expectedAtLastMillisecond = { timetz0: '23:59:59Z', timetz3: '23:59:59.999Z', timetz6: '23:59:59.999Z', - timestamp0: '2024-01-03T00:00:00', + timestamp0: '2024-01-02T23:59:59', + timestamp1: '2024-01-02T23:59:59.9', timestamp3: '2024-01-02T23:59:59.999', timestamp6: '2024-01-02T23:59:59.999', timestampString0: '2024-01-02T23:59:59', timestampString3: '2024-01-02T23:59:59.999', timestampString6: '2024-01-02T23:59:59.999', - timestamptz0: '2024-01-03T00:00:00Z', + timestamptz0: '2024-01-02T23:59:59Z', + timestamptz2: '2024-01-02T23:59:59.99Z', timestamptz3: '2024-01-02T23:59:59.999Z', timestamptz6: '2024-01-02T23:59:59.999Z', timestamptzString0: '2024-01-02T23:59:59Z', @@ -50,6 +52,8 @@ const expectedAtLastMillisecond = { timestampJsDate0: '2024-01-02T23:59:59.000Z', timestampJsDate1: '2024-01-02T23:59:59.900Z', timestampJsDate2: '2024-01-02T23:59:59.990Z', + timestampInstant0: '2024-01-02T23:59:59Z', + timestampInstant2: '2024-01-02T23:59:59.99Z', dateTime0: '2024-01-02T23:59:59.000Z', }; diff --git a/test/integration/test/upgrade-instructions/tools-write-convenience-types/detection.test.ts b/test/integration/test/upgrade-instructions/tools-write-convenience-types/detection.test.ts index eef02fcbd6af..c9effb882096 100644 --- a/test/integration/test/upgrade-instructions/tools-write-convenience-types/detection.test.ts +++ b/test/integration/test/upgrade-instructions/tools-write-convenience-types/detection.test.ts @@ -80,12 +80,16 @@ describe('the detection of the changes the tools writing convenience types make' attribute: detects(' updatedAt DateTime @onCreate(now()) @onUpdate(now())\n'), alias: detects(' createdAt temporal.CreatedAt\n'), typescript: detects('createdAt: field.column(type.DateTime()).onCreate(gen.now()),'), + temporalPreset: detects(' createdAt temporal.timestamptz(0, onCreate: now)\n'), + stringPreset: detects(' createdAt temporal.timestamptzString(0)\n'), databaseDefault: detects(' createdAt DateTime @default(now())\n'), }).toEqual({ prisma7: true, attribute: true, alias: true, typescript: true, + temporalPreset: true, + stringPreset: false, databaseDefault: false, }); }); diff --git a/upgrade-instructions/pending/tools-write-convenience-types/app/instructions.md b/upgrade-instructions/pending/tools-write-convenience-types/app/instructions.md index bf61f0970a0e..439d0138f50a 100644 --- a/upgrade-instructions/pending/tools-write-convenience-types/app/instructions.md +++ b/upgrade-instructions/pending/tools-write-convenience-types/app/instructions.md @@ -23,13 +23,14 @@ changes: - '@default\((?:timestamp(?:tz)?|datetime)`[^`]*\.\d{3}\d*[1-9]' - id: date-now-takes-column-precision summary: | - On PostgreSQL, the `now` generator of a column read as a JavaScript `Date` (`DateTime`, `TimestamptzJsDate`, `TimestampJsDate`, `temporal.CreatedAt` and `temporal.UpdatedAt`) now takes the column's precision and rounds the time down to it, so the value the client holds is the value PostgreSQL stores. A `timestamp(0)` column no longer gets a time PostgreSQL rounds up to the next second. The generator's precision is in the contract's execution section, so re-emit with `prisma contract emit` and commit `contract.json` and `contract.d.ts`; only the execution hash changes, so there is no migration and no `prisma db sign`. + On PostgreSQL, the `now` generator of a timestamp column read as a JavaScript `Date` (`DateTime`, `TimestamptzJsDate`, `TimestampJsDate`, `temporal.CreatedAt` and `temporal.UpdatedAt`) or as a `Temporal` value (`DateTime(as: temporal)`, `TimestamptzTemporal`, `TimestampTemporal`, `TimestampInstant`, `temporal.timestamptz(...)` and `temporal.timestamp(...)`) now takes the column's precision and rounds the time down to it, so the value the client holds is the value PostgreSQL stores. A `timestamp(0)` column no longer gets a time PostgreSQL rounds up to the next second. The generator's precision is in the contract's execution section, so re-emit with `prisma contract emit` and commit `contract.json` and `contract.d.ts`; only the execution hash changes, so there is no migration and no `prisma db sign`. detection: glob: "**/*.{prisma,ts,mts,cts,tsx}" matches: - '\bprisma7Schema\s*\(' - '@on(?:Create|Update)\(now\(\)\)' - '(? Date: Fri, 23 Oct 2026 18:12:52 +0200 Subject: [PATCH 088/118] Detect the temporal.timestamptzJsDate preset in the now-precision upgrade instruction The preset now passes the column's precision to its now generator, but the detection pattern and the summary did not name it. Co-Authored-By: Claude Opus 5.5 Signed-off-by: willbot Signed-off-by: Will Madden --- .../tools-write-convenience-types/detection.test.ts | 4 ++++ .../tools-write-convenience-types/app/instructions.md | 6 +++--- 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/test/integration/test/upgrade-instructions/tools-write-convenience-types/detection.test.ts b/test/integration/test/upgrade-instructions/tools-write-convenience-types/detection.test.ts index c9effb882096..ce8af1a3f9c6 100644 --- a/test/integration/test/upgrade-instructions/tools-write-convenience-types/detection.test.ts +++ b/test/integration/test/upgrade-instructions/tools-write-convenience-types/detection.test.ts @@ -81,6 +81,8 @@ describe('the detection of the changes the tools writing convenience types make' alias: detects(' createdAt temporal.CreatedAt\n'), typescript: detects('createdAt: field.column(type.DateTime()).onCreate(gen.now()),'), temporalPreset: detects(' createdAt temporal.timestamptz(0, onCreate: now)\n'), + jsDatePreset: detects("createdAt: field.temporal.timestamptzJsDate(3, 'now'),"), + jsDatePresetPsl: detects(' createdAt temporal.timestamptzJsDate(3, onCreate: now)\n'), stringPreset: detects(' createdAt temporal.timestamptzString(0)\n'), databaseDefault: detects(' createdAt DateTime @default(now())\n'), }).toEqual({ @@ -89,6 +91,8 @@ describe('the detection of the changes the tools writing convenience types make' alias: true, typescript: true, temporalPreset: true, + jsDatePreset: true, + jsDatePresetPsl: true, stringPreset: false, databaseDefault: false, }); diff --git a/upgrade-instructions/pending/tools-write-convenience-types/app/instructions.md b/upgrade-instructions/pending/tools-write-convenience-types/app/instructions.md index 439d0138f50a..0224b52ce0ca 100644 --- a/upgrade-instructions/pending/tools-write-convenience-types/app/instructions.md +++ b/upgrade-instructions/pending/tools-write-convenience-types/app/instructions.md @@ -23,14 +23,14 @@ changes: - '@default\((?:timestamp(?:tz)?|datetime)`[^`]*\.\d{3}\d*[1-9]' - id: date-now-takes-column-precision summary: | - On PostgreSQL, the `now` generator of a timestamp column read as a JavaScript `Date` (`DateTime`, `TimestamptzJsDate`, `TimestampJsDate`, `temporal.CreatedAt` and `temporal.UpdatedAt`) or as a `Temporal` value (`DateTime(as: temporal)`, `TimestamptzTemporal`, `TimestampTemporal`, `TimestampInstant`, `temporal.timestamptz(...)` and `temporal.timestamp(...)`) now takes the column's precision and rounds the time down to it, so the value the client holds is the value PostgreSQL stores. A `timestamp(0)` column no longer gets a time PostgreSQL rounds up to the next second. The generator's precision is in the contract's execution section, so re-emit with `prisma contract emit` and commit `contract.json` and `contract.d.ts`; only the execution hash changes, so there is no migration and no `prisma db sign`. + On PostgreSQL, the `now` generator of a timestamp column read as a JavaScript `Date` (`DateTime`, `TimestamptzJsDate`, `TimestampJsDate`, `temporal.CreatedAt`, `temporal.UpdatedAt` and `temporal.timestamptzJsDate(...)`) or as a `Temporal` value (`DateTime(as: temporal)`, `TimestamptzTemporal`, `TimestampTemporal`, `TimestampInstant`, `temporal.timestamptz(...)` and `temporal.timestamp(...)`) now takes the column's precision and rounds the time down to it, so the value the client holds is the value PostgreSQL stores. A `timestamp(0)` column no longer gets a time PostgreSQL rounds up to the next second. The generator's precision is in the contract's execution section, so re-emit with `prisma contract emit` and commit `contract.json` and `contract.d.ts`; only the execution hash changes, so there is no migration and no `prisma db sign`. detection: glob: "**/*.{prisma,ts,mts,cts,tsx}" matches: - '\bprisma7Schema\s*\(' - '@on(?:Create|Update)\(now\(\)\)' - '(? Date: Fri, 23 Oct 2026 18:16:03 +0200 Subject: [PATCH 089/118] Name the arguments a convenience type takes when a PSL call writes one it does not Date(precision: 3) was refused with only "received unknown named argument". A PSL type choice call now says, as the TypeScript helper does, that the type has no such argument and lists the arguments it takes. Both use one wording. Co-Authored-By: Claude Opus 5.5 Signed-off-by: willbot Signed-off-by: Will Madden --- .../framework-components/src/exports/authoring.ts | 1 + .../src/shared/authoring-type-choice.ts | 12 +++++++++++- .../2-authoring/psl-parser/src/type-choice-calls.ts | 13 +++++++++++++ .../test/interpreter.type-choices.test.ts | 2 +- .../test/interpreter.type-choices.test.ts | 13 +++++++++++++ .../convenience-types.integration.test.ts | 4 ++-- 6 files changed, 41 insertions(+), 4 deletions(-) diff --git a/packages/1-framework/1-core/framework-components/src/exports/authoring.ts b/packages/1-framework/1-core/framework-components/src/exports/authoring.ts index 89e9aab8c056..81ed36a934cb 100644 --- a/packages/1-framework/1-core/framework-components/src/exports/authoring.ts +++ b/packages/1-framework/1-core/framework-components/src/exports/authoring.ts @@ -13,6 +13,7 @@ export { typeChoiceArgumentsFromObject, typeChoiceCombinationText, typeConstructorLookup, + unknownTypeChoiceArgumentMessage, } from '../shared/authoring-type-choice'; export type { AuthoringTypeChoiceCall, diff --git a/packages/1-framework/1-core/framework-components/src/shared/authoring-type-choice.ts b/packages/1-framework/1-core/framework-components/src/shared/authoring-type-choice.ts index 488c186b5c20..55b56b9c8c24 100644 --- a/packages/1-framework/1-core/framework-components/src/shared/authoring-type-choice.ts +++ b/packages/1-framework/1-core/framework-components/src/shared/authoring-type-choice.ts @@ -79,6 +79,16 @@ function isArgumentObject(value: unknown): value is Readonly argument.name); + return `${subject} has no argument "${name}"; its arguments are ${listedNames(names)}.`; +} + /** * The values of a type choice call that writes its arguments as one object, by the choice's argument positions, as {@link resolveAuthoringTypeChoice} reads them. `written` is the call's arguments: none, or one object keyed by argument name. Anything else, and a key the choice has no argument for, is refused with `CONTRACT.ARGUMENT_INVALID`. */ @@ -103,7 +113,7 @@ export function typeChoiceArgumentsFromObject( if (unknown !== undefined) { throw runtimeError( 'CONTRACT.ARGUMENT_INVALID', - `${helperPath} has no argument "${unknown}"; its arguments are ${listedNames(names)}.`, + unknownTypeChoiceArgumentMessage(helperPath, descriptor, unknown), { helperPath }, ); } diff --git a/packages/1-framework/2-authoring/psl-parser/src/type-choice-calls.ts b/packages/1-framework/2-authoring/psl-parser/src/type-choice-calls.ts index dfc48ed0a5e7..5ff52e7e9285 100644 --- a/packages/1-framework/2-authoring/psl-parser/src/type-choice-calls.ts +++ b/packages/1-framework/2-authoring/psl-parser/src/type-choice-calls.ts @@ -4,6 +4,7 @@ import { instantiateAuthoringTypeConstructor, resolveAuthoringTypeChoice, type ScalarTypeConstructorOutput, + unknownTypeChoiceArgumentMessage, validateAuthoringTypeChoiceParams, } from '@internal/framework-components/authoring'; import type { CodecLookupWithDescriptors } from '@internal/framework-components/codec'; @@ -55,6 +56,18 @@ export function instantiatePslTypeChoice(input: { const helperPath = symbol.path.join('.'); const call = input.call ?? { path: symbol.path, args: [], span: input.span }; const subject = `${input.entityLabel} type "${helperPath}"`; + const unknown = call.args.find( + (arg) => + arg.kind === 'named' && !descriptor.args.some((declared) => declared.name === arg.name), + ); + if (unknown?.name !== undefined) { + input.diagnostics.push({ + code: 'PSL_INVALID_ATTRIBUTE_ARGUMENT', + message: unknownTypeChoiceArgumentMessage(subject, descriptor, unknown.name), + ...input.source.at(unknown.span), + }); + return undefined; + } const args = mapPslHelperArgs({ args: call.args, descriptors: descriptor.args, diff --git a/packages/2-mongo-family/2-authoring/contract-psl/test/interpreter.type-choices.test.ts b/packages/2-mongo-family/2-authoring/contract-psl/test/interpreter.type-choices.test.ts index da9334f9d234..3117ea061b78 100644 --- a/packages/2-mongo-family/2-authoring/contract-psl/test/interpreter.type-choices.test.ts +++ b/packages/2-mongo-family/2-authoring/contract-psl/test/interpreter.type-choices.test.ts @@ -78,7 +78,7 @@ describe('a MongoDB field typed by a type choice', () => { [ 'an unknown named argument', 'Moment(bogus: date)', - 'Field "Doc.at" type "Moment" received unknown named argument "bogus".', + 'Field "Doc.at" type "Moment" has no argument "bogus"; its arguments are as and label.', ], [ 'a value the option does not list', diff --git a/packages/2-sql-family/2-authoring/contract-psl/test/interpreter.type-choices.test.ts b/packages/2-sql-family/2-authoring/contract-psl/test/interpreter.type-choices.test.ts index 1be01b6e7f39..ecd088f7ff52 100644 --- a/packages/2-sql-family/2-authoring/contract-psl/test/interpreter.type-choices.test.ts +++ b/packages/2-sql-family/2-authoring/contract-psl/test/interpreter.type-choices.test.ts @@ -192,6 +192,19 @@ describe('a type choice in a field type', () => { ]); }); + it('refuses an argument the choice does not declare, naming the arguments it does', () => { + const line = 'at Moment(value: 3)'; + const start = ' '.length + line.indexOf('value'); + expect(diagnostics(model(line))).toEqual([ + { + code: 'PSL_INVALID_ATTRIBUTE_ARGUMENT', + message: + 'Field "T.at" type "Moment" has no argument "value"; its arguments are as, storage and precision.', + columns: [start + 1, start + 1 + 'value: 3'.length], + }, + ]); + }); + it('refuses a precision the chosen type’s codec refuses, naming the argument of the choice', () => { const line = 'at Moment(as: temporal, storage: local, precision: 7)'; const start = ' '.length + line.indexOf('precision'); diff --git a/test/integration/test/convenience-date-time-types/convenience-types.integration.test.ts b/test/integration/test/convenience-date-time-types/convenience-types.integration.test.ts index 43dbf7acead0..0744ad17bfc6 100644 --- a/test/integration/test/convenience-date-time-types/convenience-types.integration.test.ts +++ b/test/integration/test/convenience-date-time-types/convenience-types.integration.test.ts @@ -115,9 +115,9 @@ model Order { ); }); - it('refuses a precision the type it becomes does not take', async () => { + it('refuses a precision the type it becomes does not take, naming the arguments it takes', async () => { await expect(readPsl(model('at Date(precision: 3)'))).rejects.toThrow( - 'received unknown named argument \\"precision\\"', + 'Field \\"Order.at\\" type \\"Date\\" has no argument \\"precision\\"; its arguments are as and storage.', ); }); }); From d026159c12cd455593e88b7cdb944b03988d8ec8 Mon Sep 17 00:00:00 2001 From: willbot Date: Fri, 23 Oct 2026 18:17:01 +0200 Subject: [PATCH 090/118] Try every type choice that names a constructor when print and infer write a call The helper gave up when the first type choice naming the constructor could not reproduce the call, and wrote the explicit type even when a later choice would have. It now tries each, in namespace order, and writes the first that reproduces the call. Co-Authored-By: Claude Opus 5.5 Signed-off-by: willbot Signed-off-by: Will Madden --- .../src/shared/authoring-type-choice-call.ts | 57 +++++++++++++------ .../test/authoring-type-choice-call.test.ts | 13 +++++ 2 files changed, 52 insertions(+), 18 deletions(-) diff --git a/packages/1-framework/1-core/framework-components/src/shared/authoring-type-choice-call.ts b/packages/1-framework/1-core/framework-components/src/shared/authoring-type-choice-call.ts index a205eacc8432..158cd9565d74 100644 --- a/packages/1-framework/1-core/framework-components/src/shared/authoring-type-choice-call.ts +++ b/packages/1-framework/1-core/framework-components/src/shared/authoring-type-choice-call.ts @@ -45,22 +45,21 @@ function namespaceLevel( return level; } -function firstChoiceNaming( +interface ChoiceNaming { + readonly name: string; + readonly descriptor: AuthoringTypeChoiceDescriptor; + readonly when: Readonly>; +} + +function choicesNaming( level: AuthoringTypeNamespace, constructorName: string, -): - | { - readonly name: string; - readonly descriptor: AuthoringTypeChoiceDescriptor; - readonly when: Readonly>; - } - | undefined { - for (const [name, member] of Object.entries(level)) { - if (!isAuthoringTypeChoiceDescriptor(member)) continue; +): readonly ChoiceNaming[] { + return Object.entries(level).flatMap(([name, member]) => { + if (!isAuthoringTypeChoiceDescriptor(member)) return []; const row = member.choices.find((candidate) => candidate.type === constructorName); - if (row !== undefined) return { name, descriptor: member, when: row.when }; - } - return undefined; + return row === undefined ? [] : [{ name, descriptor: member, when: row.when }]; + }); } function outputOf(produce: () => AuthoringTypeConstructorOutput) { @@ -73,7 +72,7 @@ function outputOf(produce: () => AuthoringTypeConstructorOutput) { } /** - * The call to a type choice that becomes the type constructor call `call`: the first type choice, in namespace order at the constructor's level, with a row naming the constructor. Its options come from that row and its passed-on arguments from the call, and every argument whose omission still gives the same output is left out. `sameOutput` decides what counts as the same output; by default the codec and type parameters must be exactly equal. `undefined` when no type choice names the constructor or no call to it gives the same output. + * The call to a type choice that becomes the type constructor call `call`: of the type choices at the constructor's level with a row naming the constructor, the first in namespace order whose call gives the same output. Its options come from that row and its passed-on arguments from the call, and every argument whose omission still gives the same output is left out. `sameOutput` decides what counts as the same output; by default the codec and type parameters must be exactly equal. `undefined` when no type choice names the constructor or no call to one gives the same output. */ export function findAuthoringTypeChoiceCall(input: { readonly namespace: AuthoringTypeNamespace; @@ -90,20 +89,42 @@ export function findAuthoringTypeChoiceCall(input: { const level = namespaceLevel(input.namespace, prefix); if (level === undefined || constructorName === undefined) return undefined; const explicit = typeConstructorLookup(level)(constructorName); - const choice = explicit && firstChoiceNaming(level, constructorName); const wanted = explicit && outputOf(() => instantiateAuthoringTypeConstructor(explicit, call.args)); - if (explicit === undefined || choice === undefined || wanted === undefined) return undefined; + if (explicit === undefined || wanted === undefined) return undefined; const passedValue = (name: string) => call.args[(explicit.args ?? []).findIndex((parameter) => parameter.name === name)]; + for (const choice of choicesNaming(level, constructorName)) { + const found = choiceCallReproducing({ + level, + prefix, + choice, + passedValue, + reproduces: (produced) => sameOutput(produced, wanted), + }); + if (found !== undefined) return found; + } + return undefined; +} + +function choiceCallReproducing(input: { + readonly level: AuthoringTypeNamespace; + readonly prefix: readonly string[]; + readonly choice: ChoiceNaming; + readonly passedValue: (name: string) => unknown; + readonly reproduces: (produced: AuthoringTypeConstructorOutput) => boolean; +}): AuthoringTypeChoiceCall | undefined { + const { level, prefix, choice } = input; const reproduces = (args: readonly unknown[]) => { const produced = outputOf(() => instantiateAuthoringTypeCall(level, choice.name, args)); - return produced !== undefined && sameOutput(produced, wanted); + return produced !== undefined && input.reproduces(produced); }; const args: unknown[] = choice.descriptor.args.map((argument) => - isAuthoringTypeChoiceOption(argument) ? choice.when[argument.name] : passedValue(argument.name), + isAuthoringTypeChoiceOption(argument) + ? choice.when[argument.name] + : input.passedValue(argument.name), ); if (!reproduces(args)) return undefined; for (const index of args.keys()) { diff --git a/packages/1-framework/1-core/framework-components/test/authoring-type-choice-call.test.ts b/packages/1-framework/1-core/framework-components/test/authoring-type-choice-call.test.ts index 572c7b97700b..beb0c495c0aa 100644 --- a/packages/1-framework/1-core/framework-components/test/authoring-type-choice-call.test.ts +++ b/packages/1-framework/1-core/framework-components/test/authoring-type-choice-call.test.ts @@ -154,6 +154,19 @@ describe('findAuthoringTypeChoiceCall', () => { }); }); + it('passes over a type choice that names the constructor but cannot reproduce the call', () => { + const fixed = { + kind: 'typeChoice', + args: [{ kind: 'option', name: 'value', values: ['date'], default: 'date', optional: true }], + choices: [{ when: { value: 'date' }, type: 'WideDate', defaults: { precision: 6 } }], + } as const; + expect(find(['WideDate'], [3], { ...constructors, Fixed: fixed, Moment: moment })).toEqual({ + path: ['Moment'], + args: [], + becomes: { path: ['WideDate'], args: [3] }, + }); + }); + it('writes a type choice of the same sub-namespace as the constructor', () => { expect(find(['pg', 'NarrowDate'], [], { pg: namespace })).toEqual({ path: ['pg', 'Moment'], From 12d2cdb36e83367985d160f2fc3887121dbaaea3 Mon Sep 17 00:00:00 2001 From: willbot Date: Fri, 23 Oct 2026 18:17:52 +0200 Subject: [PATCH 091/118] Show how a TypeScript convenience type takes its options from a variable Type tests record that an options object written with as const compiles, that one with a widened option value does not, and that an unknown key is refused through a variable too. The README and the scalar types reference say to write the object inline or with as const. Co-Authored-By: Claude Opus 5.5 Signed-off-by: willbot Signed-off-by: Will Madden --- docs/reference/scalar-types.md | 2 +- .../2-authoring/contract-ts/README.md | 2 +- .../test/type-choice-helpers.test-d.ts | 20 +++++++++++++++++++ 3 files changed, 22 insertions(+), 2 deletions(-) diff --git a/docs/reference/scalar-types.md b/docs/reference/scalar-types.md index 0a5fcf73ae32..d0a3b476f446 100644 --- a/docs/reference/scalar-types.md +++ b/docs/reference/scalar-types.md @@ -84,7 +84,7 @@ The date and time types have two layers ([ADR 279](../architecture%20docs/adrs/A | `Duration(as?, storage?, precision?)` | `string` (default) | `interval` (default) | `IntervalString` | | | `parts` | `interval` | `IntervalParts` | -The arguments are written as bare names, by name or in this order: `DateTime(as: string, precision: 6)`, `DateTime(temporal, timestamp)`. A combination the table does not list, such as `Time(as: temporal, storage: timetz)` or `Date(as: jsDate)`, is refused at the argument that makes it invalid, naming the values it accepts; so is a `precision` the chosen type does not take. No default needs a global `Temporal`. In a TypeScript contract the helpers take the same arguments as one object, `type.DateTime({ as: 'string', precision: 6 })`, and are typed as the explicit type they choose; a combination the table does not list, or an argument the chosen type does not take, does not compile. +The arguments are written as bare names, by name or in this order: `DateTime(as: string, precision: 6)`, `DateTime(temporal, timestamp)`. A combination the table does not list, such as `Time(as: temporal, storage: timetz)` or `Date(as: jsDate)`, is refused at the argument that makes it invalid, naming the values it accepts; so is a `precision` the chosen type does not take. No default needs a global `Temporal`. In a TypeScript contract the helpers take the same arguments as one object, `type.DateTime({ as: 'string', precision: 6 })`, and are typed as the explicit type they choose; a combination the table does not list, or an argument the chosen type does not take, does not compile. An options object kept in a variable needs `as const`, so each option keeps its literal value: `const options = { as: 'string' } as const; type.DateTime(options)`. | PSL name | TS helper | Codec id | Storage type | Application type | | --- | --- | --- | --- | --- | diff --git a/packages/2-sql-family/2-authoring/contract-ts/README.md b/packages/2-sql-family/2-authoring/contract-ts/README.md index 9b09767c76e6..02d95948d235 100644 --- a/packages/2-sql-family/2-authoring/contract-ts/README.md +++ b/packages/2-sql-family/2-authoring/contract-ts/README.md @@ -146,7 +146,7 @@ Anything else inside `${…}` is a type error, and `CONTRACT.SQL_EXPRESSION_INTE Pack-provided helper presets are available through the callback overload. This is the surface that exposes `field.id.*`, `field.text()`, `field.temporal.createdAt()`, `field.temporal.updatedAt()`, the `type.*` helpers, and extension helpers such as `type.pgvector.Vector(...)`. -`type.*` has a helper for every PSL type constructor of the stack, such as `type.VarChar(64)`, `type.TimestampString(3)` and `type.String()`, plus the type aliases the packs contribute, such as `type.temporal.CreatedAt()` and `type.temporal.UpdatedAt()`, which on Postgres are `type.DateTime()` with the `now` generators. A type choice, such as PostgreSQL's `DateTime`, takes its arguments as one object, `type.DateTime({ as: 'string', precision: 6 })`, and is typed as the explicit type its arguments choose. Pass the result to `field.column(...)`. A type alias is a constant: field builders are immutable, so one constant serves every field and model that uses it, and each column stores the alias's codec and params inline. A constant may carry a default and generators, and a field may add what the constant does not set: +`type.*` has a helper for every PSL type constructor of the stack, such as `type.VarChar(64)`, `type.TimestampString(3)` and `type.String()`, plus the type aliases the packs contribute, such as `type.temporal.CreatedAt()` and `type.temporal.UpdatedAt()`, which on Postgres are `type.DateTime()` with the `now` generators. A type choice, such as PostgreSQL's `DateTime`, takes its arguments as one object, `type.DateTime({ as: 'string', precision: 6 })`, and is typed as the explicit type its arguments choose. Because the type depends on the option values, each must be a literal: write the object inline, or build it in a variable with `as const`, such as `const options = { as: 'string' } as const`. An object whose option is typed `string`, or a union of values, does not compile, since no single explicit type fits it. Pass the result to `field.column(...)`. A type alias is a constant: field builders are immutable, so one constant serves every field and model that uses it, and each column stores the alias's codec and params inline. A constant may carry a default and generators, and a field may add what the constant does not set: ```typescript const UpdatedAt = field.column(type.TimestampString(3)).onCreate(gen.now()).onUpdate(gen.now()); diff --git a/packages/2-sql-family/2-authoring/contract-ts/test/type-choice-helpers.test-d.ts b/packages/2-sql-family/2-authoring/contract-ts/test/type-choice-helpers.test-d.ts index 333b038897f8..e3ea91336152 100644 --- a/packages/2-sql-family/2-authoring/contract-ts/test/type-choice-helpers.test-d.ts +++ b/packages/2-sql-family/2-authoring/contract-ts/test/type-choice-helpers.test-d.ts @@ -68,3 +68,23 @@ test('positional arguments do not compile', () => { // @ts-expect-error a type choice takes one object type.Moment({}, {}); }); + +test('an arguments object from a variable compiles when written as const', () => { + const options = { as: 'string', storage: 'local' } as const; + expectTypeOf(type.Moment(options)).toEqualTypeOf(type.TimestampString()); +}); + +test('an arguments object from a variable with a widened option value does not compile', () => { + const widened = { as: 'string', storage: 'local' }; + // @ts-expect-error as is typed string, so no overload can say which explicit type the call is + type.Moment(widened); + const union: { as: 'temporal' | 'string'; storage: 'local' } = { as: 'string', storage: 'local' }; + // @ts-expect-error as is typed 'temporal' | 'string', which two rows share, so no overload takes it + type.Moment(union); +}); + +test('a key the type choice has no argument for does not compile through a variable', () => { + const extra = { storage: 'local', bogus: 1 } as const; + // @ts-expect-error Moment has no argument bogus + type.Moment(extra); +}); From 70e3c037adf50b89a3ecb0f2b1a7f33deb5c48f6 Mon Sep 17 00:00:00 2001 From: willbot Date: Fri, 23 Oct 2026 18:19:26 +0200 Subject: [PATCH 092/118] Use ifDefined for the conditional properties the type choice code adds The conditional spreads become ifDefined, and writtenTypeChoiceCall's internal input takes sameOutput as a required key typed with undefined. Co-Authored-By: Claude Opus 5.5 Signed-off-by: willbot Signed-off-by: Will Madden --- .../src/shared/framework-authoring.ts | 2 +- .../core/psl-build/inferred-type-constructors.ts | 13 ++++++++----- .../psl-build/inferred-type-constructors.test.ts | 7 ++++++- .../1-target/src/core/psl-print/column-types.ts | 1 + 4 files changed, 16 insertions(+), 7 deletions(-) diff --git a/packages/1-framework/1-core/framework-components/src/shared/framework-authoring.ts b/packages/1-framework/1-core/framework-components/src/shared/framework-authoring.ts index 24db87103d0d..ecb69c903dac 100644 --- a/packages/1-framework/1-core/framework-components/src/shared/framework-authoring.ts +++ b/packages/1-framework/1-core/framework-components/src/shared/framework-authoring.ts @@ -1908,7 +1908,7 @@ export function validateAuthoringTypeChoiceParams( argumentIndex < 0 ? `The type parameters of ${helperPath} are invalid: ${issue.message}` : `Argument "${name}" of ${helperPath} is invalid: ${issue.message}`, - { helperPath, ...(argumentIndex < 0 ? {} : { argumentIndex }) }, + { helperPath, ...ifDefined('argumentIndex', argumentIndex < 0 ? undefined : argumentIndex) }, ); } diff --git a/packages/2-sql-family/9-family/src/core/psl-build/inferred-type-constructors.ts b/packages/2-sql-family/9-family/src/core/psl-build/inferred-type-constructors.ts index 291fde884125..f92487c9e9a5 100644 --- a/packages/2-sql-family/9-family/src/core/psl-build/inferred-type-constructors.ts +++ b/packages/2-sql-family/9-family/src/core/psl-build/inferred-type-constructors.ts @@ -24,6 +24,7 @@ import { type SqlTypeParams, } from '@internal/sql-contract/data-type'; import { blindCast } from '@internal/utils/casts'; +import { ifDefined } from '@internal/utils/defined'; import { isInternalError } from '@internal/utils/internal-error'; import { writeDataTypeValue } from './default-mapping'; @@ -151,15 +152,17 @@ export function writtenTypeChoiceCall(input: { readonly authoringTypes: AuthoringTypeNamespace; readonly call: AuthoringTypeConstructorCall; readonly dataTypes: DataTypeSupport; - readonly sameOutput?: ( - produced: AuthoringTypeConstructorOutput, - wanted: AuthoringTypeConstructorOutput, - ) => boolean; + readonly sameOutput: + | (( + produced: AuthoringTypeConstructorOutput, + wanted: AuthoringTypeConstructorOutput, + ) => boolean) + | undefined; }): WrittenTypeChoiceCall | undefined { const found = findAuthoringTypeChoiceCall({ namespace: input.authoringTypes, call: input.call, - ...(input.sameOutput === undefined ? {} : { sameOutput: input.sameOutput }), + ...ifDefined('sameOutput', input.sameOutput), }); const choice = found && getAuthoringTypeChoice({ type: input.authoringTypes }, found.path); if (found === undefined || choice === undefined) return undefined; diff --git a/packages/2-sql-family/9-family/test/psl-build/inferred-type-constructors.test.ts b/packages/2-sql-family/9-family/test/psl-build/inferred-type-constructors.test.ts index 6470ff690535..36e3ce383198 100644 --- a/packages/2-sql-family/9-family/test/psl-build/inferred-type-constructors.test.ts +++ b/packages/2-sql-family/9-family/test/psl-build/inferred-type-constructors.test.ts @@ -226,7 +226,12 @@ const catalog = (text: string): ReportedSqlType => ({ describe('writtenTypeChoiceCall', () => { const write = (path: readonly string[], args: readonly unknown[]) => - writtenTypeChoiceCall({ authoringTypes, call: { path, args }, dataTypes }); + writtenTypeChoiceCall({ + authoringTypes, + call: { path, args }, + dataTypes, + sameOutput: undefined, + }); it('writes the type choice call that becomes the constructor call, each argument by name', () => { expect([ diff --git a/packages/3-targets/postgres/1-target/src/core/psl-print/column-types.ts b/packages/3-targets/postgres/1-target/src/core/psl-print/column-types.ts index 8f88260eb34c..7bccf3021842 100644 --- a/packages/3-targets/postgres/1-target/src/core/psl-print/column-types.ts +++ b/packages/3-targets/postgres/1-target/src/core/psl-print/column-types.ts @@ -148,6 +148,7 @@ export function buildColumnType(input: { authoringTypes, call, dataTypes: input.dataTypes, + sameOutput: undefined, }); return convenience !== undefined ? typeCall(convenience.path, convenience.args) From 3574b98f315b386a710f78b70c0e7dfa1cedffd4 Mon Sep 17 00:00:00 2001 From: willbot Date: Fri, 23 Oct 2026 18:24:06 +0200 Subject: [PATCH 093/118] Split the hover, completion and contributed-type binder tests by topic Each file was over the 500-line limit for test files. The tests move unchanged into files named for what they cover, with their shared setup in helper files. Co-Authored-By: Claude Opus 5.5 Signed-off-by: willbot Signed-off-by: Will Madden --- ...-contributed-types.entity-argument.test.ts | 372 +++ .../test/binder-contributed-types.helpers.ts | 124 + .../test/binder-contributed-types.test.ts | 499 +--- ...etion-provider.attribute-arguments.test.ts | 248 ++ .../completion-provider.attributes.test.ts | 479 ++++ .../completion-provider.block-values.test.ts | 167 ++ ...tion-provider.declaration-keywords.test.ts | 282 +++ .../completion-provider.field-types.test.ts | 301 +++ .../completion-provider.inclusions.test.ts | 201 ++ .../completion-provider.mixin-bodies.test.ts | 141 ++ .../test/completion-provider.test.ts | 2199 +---------------- .../test/helpers/completion-provider.ts | 468 ++++ .../language-server/test/helpers/hover.ts | 278 +++ .../test/hover.attributes.test.ts | 151 ++ .../test/hover.contributed-types.test.ts | 92 + .../language-server/test/hover.test.ts | 513 +--- 16 files changed, 3318 insertions(+), 3197 deletions(-) create mode 100644 packages/1-framework/2-authoring/psl-parser/test/binder-contributed-types.entity-argument.test.ts create mode 100644 packages/1-framework/2-authoring/psl-parser/test/binder-contributed-types.helpers.ts create mode 100644 packages/1-framework/3-tooling/language-server/test/completion-provider.attribute-arguments.test.ts create mode 100644 packages/1-framework/3-tooling/language-server/test/completion-provider.attributes.test.ts create mode 100644 packages/1-framework/3-tooling/language-server/test/completion-provider.block-values.test.ts create mode 100644 packages/1-framework/3-tooling/language-server/test/completion-provider.declaration-keywords.test.ts create mode 100644 packages/1-framework/3-tooling/language-server/test/completion-provider.field-types.test.ts create mode 100644 packages/1-framework/3-tooling/language-server/test/completion-provider.inclusions.test.ts create mode 100644 packages/1-framework/3-tooling/language-server/test/completion-provider.mixin-bodies.test.ts create mode 100644 packages/1-framework/3-tooling/language-server/test/helpers/completion-provider.ts create mode 100644 packages/1-framework/3-tooling/language-server/test/helpers/hover.ts create mode 100644 packages/1-framework/3-tooling/language-server/test/hover.attributes.test.ts create mode 100644 packages/1-framework/3-tooling/language-server/test/hover.contributed-types.test.ts diff --git a/packages/1-framework/2-authoring/psl-parser/test/binder-contributed-types.entity-argument.test.ts b/packages/1-framework/2-authoring/psl-parser/test/binder-contributed-types.entity-argument.test.ts new file mode 100644 index 000000000000..37b77484c6fc --- /dev/null +++ b/packages/1-framework/2-authoring/psl-parser/test/binder-contributed-types.entity-argument.test.ts @@ -0,0 +1,372 @@ +import { describe, expect, it } from 'vitest'; +import type { FieldSymbol, NamedTypeSymbol } from '../src/symbol-table'; +import { PathExprAst } from '../src/syntax/ast/expressions'; +import type { SyntaxNode } from '../src/syntax/red'; +import { bindProject } from './binder-contributed-types.helpers'; + +function argumentNode( + holder: FieldSymbol | NamedTypeSymbol | undefined, + index = 0, +): SyntaxNode | undefined { + return holder?.typeConstructor?.args[index]?.expression?.syntax; +} + +const entities = + 'tag TopTag {}\nmodel TopModel {\n id String\n}\nnamespace auth {\n tag AuthTag {}\n}\n'; + +describe('createBinder — entity argument of a type constructor', () => { + it('binds a name in scope to the block it names', () => { + const { symbolTable, binder, diagnostics } = bindProject( + `${entities}model Post {\n value db.tagged(TopTag)\n}`, + ); + const argument = argumentNode(symbolTable.defaultNamespace.models['Post']?.fields['value']); + + expect(diagnostics).toEqual([]); + expect(argument && binder.symbolForNode(argument)).toEqual({ + kind: 'block', + symbol: symbolTable.defaultNamespace.blocks['TopTag'], + namespace: symbolTable.defaultNamespace, + }); + }); + + it('binds a name that is a model, without checking the kind', () => { + const { symbolTable, binder, diagnostics } = bindProject( + `${entities}model Post {\n value db.tagged(TopModel)\n}`, + ); + const argument = argumentNode(symbolTable.defaultNamespace.models['Post']?.fields['value']); + + expect(diagnostics).toEqual([]); + expect(argument && binder.symbolForNode(argument)).toEqual({ + kind: 'model', + symbol: symbolTable.defaultNamespace.models['TopModel'], + namespace: symbolTable.defaultNamespace, + }); + }); + + it('binds a qualified name to the member, and its qualifier to the namespace', () => { + const { symbolTable, binder, diagnostics } = bindProject( + `${entities}model Post {\n value db.tagged(auth.AuthTag)\n}`, + ); + const argument = argumentNode(symbolTable.defaultNamespace.models['Post']?.fields['value']); + const [qualifier] = (argument && PathExprAst.cast(argument)?.segments()) ?? []; + + expect(diagnostics).toEqual([]); + expect(argument && binder.symbolForNode(argument)).toEqual({ + kind: 'block', + symbol: symbolTable.namespaces['auth']?.blocks['AuthTag'], + namespace: symbolTable.namespaces['auth'], + }); + expect(qualifier && binder.symbolForNode(qualifier.syntax)).toEqual({ + kind: 'namespace', + symbol: symbolTable.namespaces['auth'], + }); + }); + + it('reports an unknown name once, on the argument, and records it as unresolved', () => { + const { symbolTable, binder, diagnostics, sources } = bindProject( + `${entities}model Post {\n value db.tagged(Nope)\n}`, + ); + const argument = argumentNode(symbolTable.defaultNamespace.models['Post']?.fields['value']); + const file = argument && sources.sourceFileFor(argument); + + expect(diagnostics).toEqual([ + { + code: 'PSL_UNRESOLVED_REFERENCE', + message: 'Cannot find entity "Nope"', + data: { reference: 'entity' }, + filename: 'schema.prisma', + range: argument && + file && { + start: file.positionAt(argument.offset), + end: file.positionAt(argument.endOffset), + }, + }, + ]); + expect(argument && binder.symbolForNode(argument)).toEqual({ + kind: 'unresolved', + name: 'Nope', + }); + }); + + it('does not see a name declared inside a namespace when it is written unqualified', () => { + const { symbolTable, binder, diagnostics } = bindProject( + `${entities}model Post {\n value db.tagged(AuthTag)\n}`, + ); + const argument = argumentNode(symbolTable.defaultNamespace.models['Post']?.fields['value']); + + expect(diagnostics.map(({ code, message }) => ({ code, message }))).toEqual([ + { code: 'PSL_UNRESOLVED_REFERENCE', message: 'Cannot find entity "AuthTag"' }, + ]); + expect(argument && binder.symbolForNode(argument)).toEqual({ + kind: 'unresolved', + name: 'AuthTag', + }); + }); + + it('binds the positional argument at the index the constructor declares', () => { + const { symbolTable, binder, diagnostics } = bindProject( + `${entities}model Post {\n value db.secondTagged(TopModel, TopTag)\n}`, + ); + const argument = argumentNode(symbolTable.defaultNamespace.models['Post']?.fields['value'], 1); + + expect(diagnostics).toEqual([]); + expect(argument && binder.symbolForNode(argument)).toEqual({ + kind: 'block', + symbol: symbolTable.defaultNamespace.blocks['TopTag'], + namespace: symbolTable.defaultNamespace, + }); + }); + + it('counts positional arguments only when named arguments are mixed in', () => { + const { symbolTable, binder, diagnostics } = bindProject( + `${entities}model Post {\n value db.secondTagged(TopModel, extra: TopModel, TopTag)\n}`, + ); + const argument = argumentNode(symbolTable.defaultNamespace.models['Post']?.fields['value'], 2); + + expect(diagnostics).toEqual([]); + expect(argument && binder.symbolForNode(argument)).toEqual({ + kind: 'block', + symbol: symbolTable.defaultNamespace.blocks['TopTag'], + namespace: symbolTable.defaultNamespace, + }); + }); + + it('records nothing for a string', () => { + const { symbolTable, binder, diagnostics } = bindProject( + `${entities}model Post {\n value db.tagged("TopTag")\n}`, + ); + const argument = argumentNode(symbolTable.defaultNamespace.models['Post']?.fields['value']); + + expect(diagnostics).toEqual([]); + expect(argument && binder.symbolForNode(argument)).toBeUndefined(); + }); + + it('records nothing for a number', () => { + const { symbolTable, binder, diagnostics } = bindProject( + `${entities}model Post {\n value db.tagged(1)\n}`, + ); + const argument = argumentNode(symbolTable.defaultNamespace.models['Post']?.fields['value']); + + expect(diagnostics).toEqual([]); + expect(argument && binder.symbolForNode(argument)).toBeUndefined(); + }); + + it('records nothing for a path of three segments', () => { + const { symbolTable, binder, diagnostics } = bindProject( + `${entities}model Post {\n value db.tagged(auth.AuthTag.more)\n}`, + ); + const argument = argumentNode(symbolTable.defaultNamespace.models['Post']?.fields['value']); + + expect(diagnostics).toEqual([]); + expect(argument && binder.symbolForNode(argument)).toBeUndefined(); + }); + + it('records nothing for a named argument', () => { + const { symbolTable, binder, diagnostics } = bindProject( + `${entities}model Post {\n value db.tagged(tag: TopTag)\n}`, + ); + const argument = argumentNode(symbolTable.defaultNamespace.models['Post']?.fields['value']); + + expect(diagnostics).toEqual([]); + expect(argument && binder.symbolForNode(argument)).toBeUndefined(); + }); + + it('records nothing for an empty argument list', () => { + const { symbolTable, binder, diagnostics } = bindProject( + `${entities}model Post {\n value db.tagged()\n}`, + ); + const argument = argumentNode(symbolTable.defaultNamespace.models['Post']?.fields['value']); + + expect(diagnostics).toEqual([]); + expect(argument && binder.symbolForNode(argument)).toBeUndefined(); + }); + + it('records nothing for the arguments of a constructor that declares no entity argument', () => { + const { symbolTable, binder, diagnostics } = bindProject( + `${entities}model Post {\n value db.VarChar(TopTag)\n}`, + ); + const argument = argumentNode(symbolTable.defaultNamespace.models['Post']?.fields['value']); + + expect(diagnostics).toEqual([]); + expect(argument && binder.symbolForNode(argument)).toBeUndefined(); + }); + + it('binds a name of the namespace of a model declared in a namespace', () => { + const { symbolTable, binder, diagnostics } = bindProject( + `${entities}namespace auth {\n model Post {\n value db.tagged(AuthTag)\n }\n}`, + ); + const argument = argumentNode(symbolTable.namespaces['auth']?.models['Post']?.fields['value']); + + expect(diagnostics).toEqual([]); + expect(argument && binder.symbolForNode(argument)).toEqual({ + kind: 'block', + symbol: symbolTable.namespaces['auth']?.blocks['AuthTag'], + namespace: symbolTable.namespaces['auth'], + }); + }); + + it('binds a top-level name from a model declared in a namespace', () => { + const { symbolTable, binder, diagnostics } = bindProject( + `${entities}namespace auth {\n model Post {\n value db.tagged(TopTag)\n }\n}`, + ); + const argument = argumentNode(symbolTable.namespaces['auth']?.models['Post']?.fields['value']); + + expect(diagnostics).toEqual([]); + expect(argument && binder.symbolForNode(argument)).toEqual({ + kind: 'block', + symbol: symbolTable.defaultNamespace.blocks['TopTag'], + namespace: symbolTable.defaultNamespace, + }); + }); + + it('binds a qualified name and its qualifier from a model declared in a namespace', () => { + const { symbolTable, binder, diagnostics } = bindProject( + `${entities}namespace auth {\n model Post {\n value db.tagged(auth.AuthTag)\n }\n}`, + ); + const argument = argumentNode(symbolTable.namespaces['auth']?.models['Post']?.fields['value']); + const [qualifier] = (argument && PathExprAst.cast(argument)?.segments()) ?? []; + + expect(diagnostics).toEqual([]); + expect(argument && binder.symbolForNode(argument)).toEqual({ + kind: 'block', + symbol: symbolTable.namespaces['auth']?.blocks['AuthTag'], + namespace: symbolTable.namespaces['auth'], + }); + expect(qualifier && binder.symbolForNode(qualifier.syntax)).toEqual({ + kind: 'namespace', + symbol: symbolTable.namespaces['auth'], + }); + }); + + it('reports an unknown name from a model declared in a namespace', () => { + const { symbolTable, binder, diagnostics } = bindProject( + `${entities}namespace auth {\n model Post {\n value db.tagged(Nope)\n }\n}`, + ); + const argument = argumentNode(symbolTable.namespaces['auth']?.models['Post']?.fields['value']); + + expect(diagnostics.map(({ code, message }) => ({ code, message }))).toEqual([ + { code: 'PSL_UNRESOLVED_REFERENCE', message: 'Cannot find entity "Nope"' }, + ]); + expect(argument && binder.symbolForNode(argument)).toEqual({ + kind: 'unresolved', + name: 'Nope', + }); + }); + + it('binds a name in scope from a field of a composite type', () => { + const { symbolTable, binder, diagnostics } = bindProject( + `${entities}type Address {\n value db.tagged(TopTag)\n}`, + ); + const argument = argumentNode( + symbolTable.defaultNamespace.compositeTypes['Address']?.fields['value'], + ); + + expect(diagnostics).toEqual([]); + expect(argument && binder.symbolForNode(argument)).toEqual({ + kind: 'block', + symbol: symbolTable.defaultNamespace.blocks['TopTag'], + namespace: symbolTable.defaultNamespace, + }); + }); + + it('binds a qualified name and its qualifier from a field of a composite type', () => { + const { symbolTable, binder, diagnostics } = bindProject( + `${entities}type Address {\n value db.tagged(auth.AuthTag)\n}`, + ); + const argument = argumentNode( + symbolTable.defaultNamespace.compositeTypes['Address']?.fields['value'], + ); + const [qualifier] = (argument && PathExprAst.cast(argument)?.segments()) ?? []; + + expect(diagnostics).toEqual([]); + expect(argument && binder.symbolForNode(argument)).toEqual({ + kind: 'block', + symbol: symbolTable.namespaces['auth']?.blocks['AuthTag'], + namespace: symbolTable.namespaces['auth'], + }); + expect(qualifier && binder.symbolForNode(qualifier.syntax)).toEqual({ + kind: 'namespace', + symbol: symbolTable.namespaces['auth'], + }); + }); + + it('reports an unknown name from a field of a composite type', () => { + const { symbolTable, binder, diagnostics } = bindProject( + `${entities}type Address {\n value db.tagged(Nope)\n}`, + ); + const argument = argumentNode( + symbolTable.defaultNamespace.compositeTypes['Address']?.fields['value'], + ); + + expect(diagnostics.map(({ code, message }) => ({ code, message }))).toEqual([ + { code: 'PSL_UNRESOLVED_REFERENCE', message: 'Cannot find entity "Nope"' }, + ]); + expect(argument && binder.symbolForNode(argument)).toEqual({ + kind: 'unresolved', + name: 'Nope', + }); + }); + + it('binds a top-level name from a named type', () => { + const { symbolTable, binder, diagnostics } = bindProject( + `${entities}types {\n Alias = db.tagged(TopTag)\n}`, + ); + const argument = argumentNode(symbolTable.namedTypes['Alias']); + + expect(diagnostics).toEqual([]); + expect(argument && binder.symbolForNode(argument)).toEqual({ + kind: 'block', + symbol: symbolTable.defaultNamespace.blocks['TopTag'], + namespace: symbolTable.defaultNamespace, + }); + }); + + it('binds a name of a namespace when it is qualified, from a named type', () => { + const { symbolTable, binder, diagnostics } = bindProject( + `${entities}types {\n Alias = db.tagged(auth.AuthTag)\n}`, + ); + const argument = argumentNode(symbolTable.namedTypes['Alias']); + const [qualifier] = (argument && PathExprAst.cast(argument)?.segments()) ?? []; + + expect(diagnostics).toEqual([]); + expect(argument && binder.symbolForNode(argument)).toEqual({ + kind: 'block', + symbol: symbolTable.namespaces['auth']?.blocks['AuthTag'], + namespace: symbolTable.namespaces['auth'], + }); + expect(qualifier && binder.symbolForNode(qualifier.syntax)).toEqual({ + kind: 'namespace', + symbol: symbolTable.namespaces['auth'], + }); + }); + + it('reports an unknown name from a named type', () => { + const { symbolTable, binder, diagnostics } = bindProject( + `${entities}types {\n Alias = db.tagged(Nope)\n}`, + ); + const argument = argumentNode(symbolTable.namedTypes['Alias']); + + expect(diagnostics.map(({ code, message }) => ({ code, message }))).toEqual([ + { code: 'PSL_UNRESOLVED_REFERENCE', message: 'Cannot find entity "Nope"' }, + ]); + expect(argument && binder.symbolForNode(argument)).toEqual({ + kind: 'unresolved', + name: 'Nope', + }); + }); + + it('does not resolve the own name of a named type to itself', () => { + const { symbolTable, binder, diagnostics } = bindProject( + `${entities}types {\n Alias = db.tagged(Alias)\n}`, + ); + const argument = argumentNode(symbolTable.namedTypes['Alias']); + + expect(diagnostics.map(({ code, message }) => ({ code, message }))).toEqual([ + { code: 'PSL_UNRESOLVED_REFERENCE', message: 'Cannot find entity "Alias"' }, + ]); + expect(argument && binder.symbolForNode(argument)).toEqual({ + kind: 'unresolved', + name: 'Alias', + }); + }); +}); diff --git a/packages/1-framework/2-authoring/psl-parser/test/binder-contributed-types.helpers.ts b/packages/1-framework/2-authoring/psl-parser/test/binder-contributed-types.helpers.ts new file mode 100644 index 000000000000..87083dd64363 --- /dev/null +++ b/packages/1-framework/2-authoring/psl-parser/test/binder-contributed-types.helpers.ts @@ -0,0 +1,124 @@ +import type { + AuthoringFieldPresetDescriptor, + AuthoringTypeChoiceDescriptor, + AuthoringTypeConstructorDescriptor, +} from '@internal/framework-components/authoring'; +import { EMPTY_GENERATOR_FUNCTIONS } from '@internal/framework-components/components'; +import { EMPTY_DATA_TYPES } from '../src/attribute-spec/spec-context'; +import { type BinderContext, createBinder, typeReferenceNode } from '../src/binder'; +import { parse } from '../src/parse'; +import { PslSources } from '../src/source-file'; +import { buildSymbolTable } from '../src/symbol-table'; + +export const createdAt: AuthoringFieldPresetDescriptor = { + kind: 'fieldPreset', + output: { codecId: 'fixture/timestamp@1' }, +}; + +export const uuid: AuthoringFieldPresetDescriptor = { + kind: 'fieldPreset', + args: [{ kind: 'number', name: 'version' }], + output: { codecId: 'fixture/uuid@1', id: true }, +}; + +export const text: AuthoringTypeConstructorDescriptor = { + kind: 'typeConstructor', + output: { codecId: 'fixture/text@1' }, +}; + +export const varchar: AuthoringTypeConstructorDescriptor = { + kind: 'typeConstructor', + args: [{ kind: 'number', name: 'length' }], + output: { codecId: 'fixture/varchar@1' }, +}; + +const tagged: AuthoringTypeConstructorDescriptor = { + kind: 'typeConstructor', + entityRefArg: { index: 0, entityKind: 'tag' }, + output: { codecId: 'fixture/tagged@1' }, +}; + +const secondTagged: AuthoringTypeConstructorDescriptor = { + kind: 'typeConstructor', + entityRefArg: { index: 1, entityKind: 'tag' }, + output: { codecId: 'fixture/tagged@1' }, +}; + +const wideDate: AuthoringTypeConstructorDescriptor = { + kind: 'typeConstructor', + output: { codecId: 'fixture/wide-date@1' }, +}; + +const wideText: AuthoringTypeConstructorDescriptor = { + kind: 'typeConstructor', + output: { codecId: 'fixture/wide-text@1' }, +}; + +const narrowDate: AuthoringTypeConstructorDescriptor = { + kind: 'typeConstructor', + output: { codecId: 'fixture/narrow-date@1' }, +}; + +const moment: AuthoringTypeChoiceDescriptor = { + kind: 'typeChoice', + args: [ + { kind: 'option', name: 'as', values: ['date', 'text'], default: 'date', optional: true }, + { + kind: 'option', + name: 'storage', + values: ['wide', 'narrow'], + default: 'wide', + optional: true, + }, + ], + choices: [ + { when: { as: 'date', storage: 'wide' }, type: 'WideDate' }, + { when: { as: 'text', storage: 'wide' }, type: 'WideText' }, + { when: { as: 'date', storage: 'narrow' }, type: 'NarrowDate' }, + ], +}; + +function context(): BinderContext { + return { + authoringContributions: { + type: { + String: text, + Moment: moment, + WideDate: wideDate, + WideText: wideText, + NarrowDate: narrowDate, + db: { VarChar: varchar, tagged, secondTagged }, + }, + field: { temporal: { createdAt }, db: { uuid } }, + entityTypes: {}, + attributeSpecs: { model: {}, field: {} }, + modelAttributes: {}, + pslBlockDescriptors: {}, + dataTypes: {}, + }, + controlMutationDefaults: { defaultFunctionRegistry: new Map() }, + generatorFunctions: EMPTY_GENERATOR_FUNCTIONS, + dataTypes: EMPTY_DATA_TYPES, + }; +} + +export function bindProject(text: string) { + const { document, sources } = parse(text, 'schema.prisma'); + const pslSources = new PslSources([[document.syntax, sources.sourceFileFor(document.syntax)]]); + const { symbolTable } = buildSymbolTable({ + documents: [document], + sources: pslSources, + defaultNamespaceId: 'public', + }); + const { binder, diagnostics } = createBinder({ + symbolTable, + sources: pslSources, + context: context(), + }); + const resolve = (fieldName: string) => { + const field = symbolTable.defaultNamespace.models['Post']?.fields[fieldName]; + const node = field === undefined ? undefined : typeReferenceNode(field); + return node === undefined ? undefined : binder.symbolForNode(node); + }; + return { resolve, diagnostics, binder, symbolTable, sources: pslSources }; +} diff --git a/packages/1-framework/2-authoring/psl-parser/test/binder-contributed-types.test.ts b/packages/1-framework/2-authoring/psl-parser/test/binder-contributed-types.test.ts index fc4be3dab42e..0569de42414e 100644 --- a/packages/1-framework/2-authoring/psl-parser/test/binder-contributed-types.test.ts +++ b/packages/1-framework/2-authoring/psl-parser/test/binder-contributed-types.test.ts @@ -1,146 +1,8 @@ -import type { - AuthoringFieldPresetDescriptor, - AuthoringTypeChoiceDescriptor, - AuthoringTypeConstructorDescriptor, -} from '@internal/framework-components/authoring'; -import { EMPTY_GENERATOR_FUNCTIONS } from '@internal/framework-components/components'; import { describe, expect, it } from 'vitest'; import { EMPTY_DATA_TYPES } from '../src/attribute-spec/spec-context'; -import { - type BinderContext, - contributedTypeOf, - createBinder, - typeReferenceNode, -} from '../src/binder'; +import { contributedTypeOf } from '../src/binder'; import { contributedTypeCodecId } from '../src/contributed-type-scope'; -import { parse } from '../src/parse'; -import { PslSources } from '../src/source-file'; -import { buildSymbolTable, type FieldSymbol, type NamedTypeSymbol } from '../src/symbol-table'; -import { PathExprAst } from '../src/syntax/ast/expressions'; -import type { SyntaxNode } from '../src/syntax/red'; - -const createdAt: AuthoringFieldPresetDescriptor = { - kind: 'fieldPreset', - output: { codecId: 'fixture/timestamp@1' }, -}; - -const uuid: AuthoringFieldPresetDescriptor = { - kind: 'fieldPreset', - args: [{ kind: 'number', name: 'version' }], - output: { codecId: 'fixture/uuid@1', id: true }, -}; - -const text: AuthoringTypeConstructorDescriptor = { - kind: 'typeConstructor', - output: { codecId: 'fixture/text@1' }, -}; - -const varchar: AuthoringTypeConstructorDescriptor = { - kind: 'typeConstructor', - args: [{ kind: 'number', name: 'length' }], - output: { codecId: 'fixture/varchar@1' }, -}; - -const tagged: AuthoringTypeConstructorDescriptor = { - kind: 'typeConstructor', - entityRefArg: { index: 0, entityKind: 'tag' }, - output: { codecId: 'fixture/tagged@1' }, -}; - -const secondTagged: AuthoringTypeConstructorDescriptor = { - kind: 'typeConstructor', - entityRefArg: { index: 1, entityKind: 'tag' }, - output: { codecId: 'fixture/tagged@1' }, -}; - -const wideDate: AuthoringTypeConstructorDescriptor = { - kind: 'typeConstructor', - output: { codecId: 'fixture/wide-date@1' }, -}; - -const wideText: AuthoringTypeConstructorDescriptor = { - kind: 'typeConstructor', - output: { codecId: 'fixture/wide-text@1' }, -}; - -const narrowDate: AuthoringTypeConstructorDescriptor = { - kind: 'typeConstructor', - output: { codecId: 'fixture/narrow-date@1' }, -}; - -const moment: AuthoringTypeChoiceDescriptor = { - kind: 'typeChoice', - args: [ - { kind: 'option', name: 'as', values: ['date', 'text'], default: 'date', optional: true }, - { - kind: 'option', - name: 'storage', - values: ['wide', 'narrow'], - default: 'wide', - optional: true, - }, - ], - choices: [ - { when: { as: 'date', storage: 'wide' }, type: 'WideDate' }, - { when: { as: 'text', storage: 'wide' }, type: 'WideText' }, - { when: { as: 'date', storage: 'narrow' }, type: 'NarrowDate' }, - ], -}; - -function context(): BinderContext { - return { - authoringContributions: { - type: { - String: text, - Moment: moment, - WideDate: wideDate, - WideText: wideText, - NarrowDate: narrowDate, - db: { VarChar: varchar, tagged, secondTagged }, - }, - field: { temporal: { createdAt }, db: { uuid } }, - entityTypes: {}, - attributeSpecs: { model: {}, field: {} }, - modelAttributes: {}, - pslBlockDescriptors: {}, - dataTypes: {}, - }, - controlMutationDefaults: { defaultFunctionRegistry: new Map() }, - generatorFunctions: EMPTY_GENERATOR_FUNCTIONS, - dataTypes: EMPTY_DATA_TYPES, - }; -} - -function bindProject(text: string) { - const { document, sources } = parse(text, 'schema.prisma'); - const pslSources = new PslSources([[document.syntax, sources.sourceFileFor(document.syntax)]]); - const { symbolTable } = buildSymbolTable({ - documents: [document], - sources: pslSources, - defaultNamespaceId: 'public', - }); - const { binder, diagnostics } = createBinder({ - symbolTable, - sources: pslSources, - context: context(), - }); - const resolve = (fieldName: string) => { - const field = symbolTable.defaultNamespace.models['Post']?.fields[fieldName]; - const node = field === undefined ? undefined : typeReferenceNode(field); - return node === undefined ? undefined : binder.symbolForNode(node); - }; - return { resolve, diagnostics, binder, symbolTable, sources: pslSources }; -} - -function argumentNode( - holder: FieldSymbol | NamedTypeSymbol | undefined, - index = 0, -): SyntaxNode | undefined { - return holder?.typeConstructor?.args[index]?.expression?.syntax; -} - -const entities = - 'tag TopTag {}\nmodel TopModel {\n id String\n}\nnamespace auth {\n tag AuthTag {}\n}\n'; +import { bindProject, createdAt, text, uuid, varchar } from './binder-contributed-types.helpers'; describe('createBinder — contributed types', () => { it('binds a field-preset name to the preset descriptor', () => { @@ -279,360 +141,3 @@ describe('createBinder — contributed types', () => { }); }); }); - -describe('createBinder — entity argument of a type constructor', () => { - it('binds a name in scope to the block it names', () => { - const { symbolTable, binder, diagnostics } = bindProject( - `${entities}model Post {\n value db.tagged(TopTag)\n}`, - ); - const argument = argumentNode(symbolTable.defaultNamespace.models['Post']?.fields['value']); - - expect(diagnostics).toEqual([]); - expect(argument && binder.symbolForNode(argument)).toEqual({ - kind: 'block', - symbol: symbolTable.defaultNamespace.blocks['TopTag'], - namespace: symbolTable.defaultNamespace, - }); - }); - - it('binds a name that is a model, without checking the kind', () => { - const { symbolTable, binder, diagnostics } = bindProject( - `${entities}model Post {\n value db.tagged(TopModel)\n}`, - ); - const argument = argumentNode(symbolTable.defaultNamespace.models['Post']?.fields['value']); - - expect(diagnostics).toEqual([]); - expect(argument && binder.symbolForNode(argument)).toEqual({ - kind: 'model', - symbol: symbolTable.defaultNamespace.models['TopModel'], - namespace: symbolTable.defaultNamespace, - }); - }); - - it('binds a qualified name to the member, and its qualifier to the namespace', () => { - const { symbolTable, binder, diagnostics } = bindProject( - `${entities}model Post {\n value db.tagged(auth.AuthTag)\n}`, - ); - const argument = argumentNode(symbolTable.defaultNamespace.models['Post']?.fields['value']); - const [qualifier] = (argument && PathExprAst.cast(argument)?.segments()) ?? []; - - expect(diagnostics).toEqual([]); - expect(argument && binder.symbolForNode(argument)).toEqual({ - kind: 'block', - symbol: symbolTable.namespaces['auth']?.blocks['AuthTag'], - namespace: symbolTable.namespaces['auth'], - }); - expect(qualifier && binder.symbolForNode(qualifier.syntax)).toEqual({ - kind: 'namespace', - symbol: symbolTable.namespaces['auth'], - }); - }); - - it('reports an unknown name once, on the argument, and records it as unresolved', () => { - const { symbolTable, binder, diagnostics, sources } = bindProject( - `${entities}model Post {\n value db.tagged(Nope)\n}`, - ); - const argument = argumentNode(symbolTable.defaultNamespace.models['Post']?.fields['value']); - const file = argument && sources.sourceFileFor(argument); - - expect(diagnostics).toEqual([ - { - code: 'PSL_UNRESOLVED_REFERENCE', - message: 'Cannot find entity "Nope"', - data: { reference: 'entity' }, - filename: 'schema.prisma', - range: argument && - file && { - start: file.positionAt(argument.offset), - end: file.positionAt(argument.endOffset), - }, - }, - ]); - expect(argument && binder.symbolForNode(argument)).toEqual({ - kind: 'unresolved', - name: 'Nope', - }); - }); - - it('does not see a name declared inside a namespace when it is written unqualified', () => { - const { symbolTable, binder, diagnostics } = bindProject( - `${entities}model Post {\n value db.tagged(AuthTag)\n}`, - ); - const argument = argumentNode(symbolTable.defaultNamespace.models['Post']?.fields['value']); - - expect(diagnostics.map(({ code, message }) => ({ code, message }))).toEqual([ - { code: 'PSL_UNRESOLVED_REFERENCE', message: 'Cannot find entity "AuthTag"' }, - ]); - expect(argument && binder.symbolForNode(argument)).toEqual({ - kind: 'unresolved', - name: 'AuthTag', - }); - }); - - it('binds the positional argument at the index the constructor declares', () => { - const { symbolTable, binder, diagnostics } = bindProject( - `${entities}model Post {\n value db.secondTagged(TopModel, TopTag)\n}`, - ); - const argument = argumentNode(symbolTable.defaultNamespace.models['Post']?.fields['value'], 1); - - expect(diagnostics).toEqual([]); - expect(argument && binder.symbolForNode(argument)).toEqual({ - kind: 'block', - symbol: symbolTable.defaultNamespace.blocks['TopTag'], - namespace: symbolTable.defaultNamespace, - }); - }); - - it('counts positional arguments only when named arguments are mixed in', () => { - const { symbolTable, binder, diagnostics } = bindProject( - `${entities}model Post {\n value db.secondTagged(TopModel, extra: TopModel, TopTag)\n}`, - ); - const argument = argumentNode(symbolTable.defaultNamespace.models['Post']?.fields['value'], 2); - - expect(diagnostics).toEqual([]); - expect(argument && binder.symbolForNode(argument)).toEqual({ - kind: 'block', - symbol: symbolTable.defaultNamespace.blocks['TopTag'], - namespace: symbolTable.defaultNamespace, - }); - }); - - it('records nothing for a string', () => { - const { symbolTable, binder, diagnostics } = bindProject( - `${entities}model Post {\n value db.tagged("TopTag")\n}`, - ); - const argument = argumentNode(symbolTable.defaultNamespace.models['Post']?.fields['value']); - - expect(diagnostics).toEqual([]); - expect(argument && binder.symbolForNode(argument)).toBeUndefined(); - }); - - it('records nothing for a number', () => { - const { symbolTable, binder, diagnostics } = bindProject( - `${entities}model Post {\n value db.tagged(1)\n}`, - ); - const argument = argumentNode(symbolTable.defaultNamespace.models['Post']?.fields['value']); - - expect(diagnostics).toEqual([]); - expect(argument && binder.symbolForNode(argument)).toBeUndefined(); - }); - - it('records nothing for a path of three segments', () => { - const { symbolTable, binder, diagnostics } = bindProject( - `${entities}model Post {\n value db.tagged(auth.AuthTag.more)\n}`, - ); - const argument = argumentNode(symbolTable.defaultNamespace.models['Post']?.fields['value']); - - expect(diagnostics).toEqual([]); - expect(argument && binder.symbolForNode(argument)).toBeUndefined(); - }); - - it('records nothing for a named argument', () => { - const { symbolTable, binder, diagnostics } = bindProject( - `${entities}model Post {\n value db.tagged(tag: TopTag)\n}`, - ); - const argument = argumentNode(symbolTable.defaultNamespace.models['Post']?.fields['value']); - - expect(diagnostics).toEqual([]); - expect(argument && binder.symbolForNode(argument)).toBeUndefined(); - }); - - it('records nothing for an empty argument list', () => { - const { symbolTable, binder, diagnostics } = bindProject( - `${entities}model Post {\n value db.tagged()\n}`, - ); - const argument = argumentNode(symbolTable.defaultNamespace.models['Post']?.fields['value']); - - expect(diagnostics).toEqual([]); - expect(argument && binder.symbolForNode(argument)).toBeUndefined(); - }); - - it('records nothing for the arguments of a constructor that declares no entity argument', () => { - const { symbolTable, binder, diagnostics } = bindProject( - `${entities}model Post {\n value db.VarChar(TopTag)\n}`, - ); - const argument = argumentNode(symbolTable.defaultNamespace.models['Post']?.fields['value']); - - expect(diagnostics).toEqual([]); - expect(argument && binder.symbolForNode(argument)).toBeUndefined(); - }); - - it('binds a name of the namespace of a model declared in a namespace', () => { - const { symbolTable, binder, diagnostics } = bindProject( - `${entities}namespace auth {\n model Post {\n value db.tagged(AuthTag)\n }\n}`, - ); - const argument = argumentNode(symbolTable.namespaces['auth']?.models['Post']?.fields['value']); - - expect(diagnostics).toEqual([]); - expect(argument && binder.symbolForNode(argument)).toEqual({ - kind: 'block', - symbol: symbolTable.namespaces['auth']?.blocks['AuthTag'], - namespace: symbolTable.namespaces['auth'], - }); - }); - - it('binds a top-level name from a model declared in a namespace', () => { - const { symbolTable, binder, diagnostics } = bindProject( - `${entities}namespace auth {\n model Post {\n value db.tagged(TopTag)\n }\n}`, - ); - const argument = argumentNode(symbolTable.namespaces['auth']?.models['Post']?.fields['value']); - - expect(diagnostics).toEqual([]); - expect(argument && binder.symbolForNode(argument)).toEqual({ - kind: 'block', - symbol: symbolTable.defaultNamespace.blocks['TopTag'], - namespace: symbolTable.defaultNamespace, - }); - }); - - it('binds a qualified name and its qualifier from a model declared in a namespace', () => { - const { symbolTable, binder, diagnostics } = bindProject( - `${entities}namespace auth {\n model Post {\n value db.tagged(auth.AuthTag)\n }\n}`, - ); - const argument = argumentNode(symbolTable.namespaces['auth']?.models['Post']?.fields['value']); - const [qualifier] = (argument && PathExprAst.cast(argument)?.segments()) ?? []; - - expect(diagnostics).toEqual([]); - expect(argument && binder.symbolForNode(argument)).toEqual({ - kind: 'block', - symbol: symbolTable.namespaces['auth']?.blocks['AuthTag'], - namespace: symbolTable.namespaces['auth'], - }); - expect(qualifier && binder.symbolForNode(qualifier.syntax)).toEqual({ - kind: 'namespace', - symbol: symbolTable.namespaces['auth'], - }); - }); - - it('reports an unknown name from a model declared in a namespace', () => { - const { symbolTable, binder, diagnostics } = bindProject( - `${entities}namespace auth {\n model Post {\n value db.tagged(Nope)\n }\n}`, - ); - const argument = argumentNode(symbolTable.namespaces['auth']?.models['Post']?.fields['value']); - - expect(diagnostics.map(({ code, message }) => ({ code, message }))).toEqual([ - { code: 'PSL_UNRESOLVED_REFERENCE', message: 'Cannot find entity "Nope"' }, - ]); - expect(argument && binder.symbolForNode(argument)).toEqual({ - kind: 'unresolved', - name: 'Nope', - }); - }); - - it('binds a name in scope from a field of a composite type', () => { - const { symbolTable, binder, diagnostics } = bindProject( - `${entities}type Address {\n value db.tagged(TopTag)\n}`, - ); - const argument = argumentNode( - symbolTable.defaultNamespace.compositeTypes['Address']?.fields['value'], - ); - - expect(diagnostics).toEqual([]); - expect(argument && binder.symbolForNode(argument)).toEqual({ - kind: 'block', - symbol: symbolTable.defaultNamespace.blocks['TopTag'], - namespace: symbolTable.defaultNamespace, - }); - }); - - it('binds a qualified name and its qualifier from a field of a composite type', () => { - const { symbolTable, binder, diagnostics } = bindProject( - `${entities}type Address {\n value db.tagged(auth.AuthTag)\n}`, - ); - const argument = argumentNode( - symbolTable.defaultNamespace.compositeTypes['Address']?.fields['value'], - ); - const [qualifier] = (argument && PathExprAst.cast(argument)?.segments()) ?? []; - - expect(diagnostics).toEqual([]); - expect(argument && binder.symbolForNode(argument)).toEqual({ - kind: 'block', - symbol: symbolTable.namespaces['auth']?.blocks['AuthTag'], - namespace: symbolTable.namespaces['auth'], - }); - expect(qualifier && binder.symbolForNode(qualifier.syntax)).toEqual({ - kind: 'namespace', - symbol: symbolTable.namespaces['auth'], - }); - }); - - it('reports an unknown name from a field of a composite type', () => { - const { symbolTable, binder, diagnostics } = bindProject( - `${entities}type Address {\n value db.tagged(Nope)\n}`, - ); - const argument = argumentNode( - symbolTable.defaultNamespace.compositeTypes['Address']?.fields['value'], - ); - - expect(diagnostics.map(({ code, message }) => ({ code, message }))).toEqual([ - { code: 'PSL_UNRESOLVED_REFERENCE', message: 'Cannot find entity "Nope"' }, - ]); - expect(argument && binder.symbolForNode(argument)).toEqual({ - kind: 'unresolved', - name: 'Nope', - }); - }); - - it('binds a top-level name from a named type', () => { - const { symbolTable, binder, diagnostics } = bindProject( - `${entities}types {\n Alias = db.tagged(TopTag)\n}`, - ); - const argument = argumentNode(symbolTable.namedTypes['Alias']); - - expect(diagnostics).toEqual([]); - expect(argument && binder.symbolForNode(argument)).toEqual({ - kind: 'block', - symbol: symbolTable.defaultNamespace.blocks['TopTag'], - namespace: symbolTable.defaultNamespace, - }); - }); - - it('binds a name of a namespace when it is qualified, from a named type', () => { - const { symbolTable, binder, diagnostics } = bindProject( - `${entities}types {\n Alias = db.tagged(auth.AuthTag)\n}`, - ); - const argument = argumentNode(symbolTable.namedTypes['Alias']); - const [qualifier] = (argument && PathExprAst.cast(argument)?.segments()) ?? []; - - expect(diagnostics).toEqual([]); - expect(argument && binder.symbolForNode(argument)).toEqual({ - kind: 'block', - symbol: symbolTable.namespaces['auth']?.blocks['AuthTag'], - namespace: symbolTable.namespaces['auth'], - }); - expect(qualifier && binder.symbolForNode(qualifier.syntax)).toEqual({ - kind: 'namespace', - symbol: symbolTable.namespaces['auth'], - }); - }); - - it('reports an unknown name from a named type', () => { - const { symbolTable, binder, diagnostics } = bindProject( - `${entities}types {\n Alias = db.tagged(Nope)\n}`, - ); - const argument = argumentNode(symbolTable.namedTypes['Alias']); - - expect(diagnostics.map(({ code, message }) => ({ code, message }))).toEqual([ - { code: 'PSL_UNRESOLVED_REFERENCE', message: 'Cannot find entity "Nope"' }, - ]); - expect(argument && binder.symbolForNode(argument)).toEqual({ - kind: 'unresolved', - name: 'Nope', - }); - }); - - it('does not resolve the own name of a named type to itself', () => { - const { symbolTable, binder, diagnostics } = bindProject( - `${entities}types {\n Alias = db.tagged(Alias)\n}`, - ); - const argument = argumentNode(symbolTable.namedTypes['Alias']); - - expect(diagnostics.map(({ code, message }) => ({ code, message }))).toEqual([ - { code: 'PSL_UNRESOLVED_REFERENCE', message: 'Cannot find entity "Alias"' }, - ]); - expect(argument && binder.symbolForNode(argument)).toEqual({ - kind: 'unresolved', - name: 'Alias', - }); - }); -}); diff --git a/packages/1-framework/3-tooling/language-server/test/completion-provider.attribute-arguments.test.ts b/packages/1-framework/3-tooling/language-server/test/completion-provider.attribute-arguments.test.ts new file mode 100644 index 000000000000..51f9258591a2 --- /dev/null +++ b/packages/1-framework/3-tooling/language-server/test/completion-provider.attribute-arguments.test.ts @@ -0,0 +1,248 @@ +import type { DataTypeAuthoringEntry } from '@internal/framework-components/authoring'; +import { EMPTY_DATA_TYPES, type FuncCallSig } from '@internal/psl-parser'; +import { describe, expect, it } from 'vitest'; +import { InsertTextFormat } from 'vscode-languageserver'; +import { renderSignatureLabel, resolveSignatureParameters } from '../src/signature-help'; +import { + type ActualPostgresDataTypesModule, + type ActualPostgresDefaultsModule, + type ActualSqlExpressionModule, + type ActualSqliteDataTypesModule, + actualMongoStack, + actualSqlStack, + completeWithActualStack, + completionItemByLabel, + controlMutationDefaults, + emptySnippetPlaceholder1, + importFromPackageRoot, +} from './helpers/completion-provider'; + +describe('providePslCompletionItems', () => { + it('uses actual SQL enum metadata and rejects an empty enum without invented values', async () => { + const stack = await actualSqlStack(); + const names = (schema: string) => + completeWithActualStack(schema, stack).items.map((item) => item.label); + expect(names('enum Mood { Happy Sad }\nmodel Post { mood Mood @default(|) }')).toEqual([ + 'Happy', + 'Sad', + ]); + expect(names('enum Mood {}\nmodel Post { mood Mood @default(|) }')).toEqual([]); + expect( + names( + 'enum Mood { Top }\nnamespace scoped { enum Mood { Scoped }\nmodel Post { mood scoped.Mood @default(|) } }', + ), + ).toEqual(['Scoped']); + const degraded = names('enum Mood { Happy Happy }\nmodel Post { mood Mood @default(|) }'); + expect(degraded).toEqual(['Happy']); + }, 5_000); + + it('uses actual adapter default-function signatures through the SQL factory', async () => { + const stack = await actualSqlStack(); + const defaults = await importFromPackageRoot( + '../../../3-targets/postgres/2-adapter/src/core/migrations/control-mutation-defaults.ts', + ); + const options = { + controlMutationDefaults: { + ...controlMutationDefaults, + defaultFunctionRegistry: defaults.createPostgresDefaultFunctionRegistry(), + }, + }; + const source = (args: string) => `model Post { value String @default(${args}) }`; + const candidates = completeWithActualStack(source('|'), stack, options).items; + expect(candidates.map((item) => item.label)).toEqual([ + 'true', + 'false', + 'null', + 'autoincrement', + 'now', + ]); + const snippetItems = completeWithActualStack(source('|'), stack, { + ...options, + clientSupportsSnippets: true, + }).items; + expect(completionItemByLabel(snippetItems, 'now').textEdit?.newText).toBe('now()'); + }, 5_000); + + it('labels each generator function argument by the values the function takes', async () => { + const postgres = await importFromPackageRoot<{ + readonly postgresGeneratorFunctions: readonly { + readonly name: string; + readonly signature?: unknown; + }[]; + }>('../../../3-targets/postgres/1-target/src/core/generator-functions.ts'); + const labelOf = (name: string) => { + const declared = postgres.postgresGeneratorFunctions.find( + (contribution) => contribution.name === name && contribution.signature !== undefined, + )!.signature as (dataTypes: unknown) => FuncCallSig; + const signature = declared(EMPTY_DATA_TYPES); + return renderSignatureLabel(name, signature, resolveSignatureParameters(signature, undefined)) + .label; + }; + expect(['uuid', 'cuid', 'nanoid'].map(labelOf)).toEqual([ + 'uuid((4 | 7)?)', + 'cuid(2)', + 'nanoid(integer from 2 to 255?)', + ]); + }); + + it('offers each registered tag inside @default( with its own documentation', async () => { + const stack = await actualSqlStack(); + const [postgres, sqlite, family] = await Promise.all([ + importFromPackageRoot( + '../../../3-targets/postgres/1-target/src/exports/data-types.ts', + ), + importFromPackageRoot( + '../../../3-targets/sqlite/1-target/src/exports/data-types.ts', + ), + importFromPackageRoot( + '../../../2-sql-family/1-core/contract/src/exports/sql-expression.ts', + ), + ]); + const withFamilyEntry = ( + targetEntries: Readonly>, + ): Readonly> => ({ + ...family.sqlExpressionRegistration.authoring.dataTypes, + ...targetEntries, + }); + const complete = ( + dataTypes: Readonly>, + clientSupportsSnippets: boolean, + ) => + completeWithActualStack('model Post { value String @default(|) }', stack, { + clientSupportsSnippets, + controlMutationDefaults, + dataTypes, + }).items.map((item) => ({ + label: item.label, + detail: item.detail, + newText: item.textEdit?.newText, + insertTextFormat: item.insertTextFormat, + })); + const postgresEntries = withFamilyEntry(postgres.postgresDataTypeEntries()); + // This stack has no type constructors, so no column has a known type and every tag is offered. + const postgresTags = [ + 'sql', + 'json', + 'date', + 'time', + 'timetz', + 'timestamp', + 'timestamptz', + 'interval', + 'bytea', + ]; + const documentationOf = ( + entries: Readonly>, + tag: string, + ) => + Object.values(entries).find( + (entry) => entry.written.kind === 'tag' && entry.written.tag === tag, + )?.documentation; + const value = (label: string) => ({ + label, + detail: 'PSL argument value', + newText: label, + insertTextFormat: undefined, + }); + const tag = ( + entries: Readonly>, + label: string, + snippet: boolean, + ) => ({ + label, + detail: documentationOf(entries, label), + newText: snippet ? `${label}\`$1\`` : label, + insertTextFormat: snippet ? InsertTextFormat.Snippet : undefined, + }); + + expect(complete(postgresEntries, true)).toEqual([ + value('true'), + value('false'), + value('null'), + ...postgresTags.map((name) => tag(postgresEntries, name, true)), + ]); + const sqliteEntries = withFamilyEntry(sqlite.sqliteDataTypeEntries()); + expect(complete(sqliteEntries, true)).toEqual([ + value('true'), + value('false'), + value('null'), + tag(sqliteEntries, 'sql', true), + tag(sqliteEntries, 'json', true), + tag(sqliteEntries, 'blob', true), + tag(sqliteEntries, 'datetime', true), + ]); + expect(complete(postgresEntries, false)).toEqual([ + value('true'), + value('false'), + value('null'), + ...postgresTags.map((name) => tag(postgresEntries, name, false)), + ]); + + // Each tag carries the text of the tag it names, not every registered tag's text. + expect(documentationOf(postgresEntries, 'json')).not.toBe( + documentationOf(postgresEntries, 'sql'), + ); + }, 5_000); + + it('uses distinct local and referenced fields through actual SQL relation specs', async () => { + const stack = await actualSqlStack(); + const schema = (args: string) => + `model Target { topOnly Int }\nnamespace remote { model Target { remoteOnly Int } }\nmodel Owner { ownOnly Int\n relation remote.Target @relation(${args}) }`; + expect( + completeWithActualStack(schema('fields: [|]'), stack).items.map((item) => item.label), + ).toEqual(['ownOnly']); + expect( + completeWithActualStack(schema('references: [|]'), stack).items.map((item) => item.label), + ).toEqual(['remoteOnly']); + expect( + completeWithActualStack(schema('onDelete: |'), stack).items.map((item) => item.label), + ).toEqual(['NoAction', 'Restrict', 'Cascade', 'SetNull', 'SetDefault']); + }, 5_000); + + it('uses actual Mongo sort functions and completes their positional arguments', async () => { + const stack = await actualMongoStack(); + const schema = (args: string) => `model Post { title String\n slug String\n @@index(${args}) }`; + const plain = completeWithActualStack(schema('[|]'), stack).items; + expect(plain.map((item) => item.label)).toEqual(['title', 'slug', 'wildcard', 'sort']); + const snippets = completeWithActualStack(schema('[|]'), stack, { + clientSupportsSnippets: true, + }).items; + expect(snippets.map((item) => [item.label, item.textEdit?.newText])).toEqual([ + ['title', 'title'], + ['slug', 'slug'], + ['wildcard', `wildcard(${emptySnippetPlaceholder1})`], + ['sort', 'sort($' + '{1:field}, $' + '{2:direction})'], + ]); + expect( + completeWithActualStack(schema('[sort(|)]'), stack).items.map((item) => item.label), + ).toEqual(['title', 'slug']); + expect( + completeWithActualStack(schema('[sort(title, |)]'), stack).items.map((item) => item.label), + ).toEqual(['Asc', 'Desc']); + expect(completeWithActualStack(schema('[wildcard(|)]'), stack).items).toEqual([]); + expect( + completeWithActualStack(schema('[title], type: |'), stack).items.map((item) => item.label), + ).toEqual(['1', '-1', '"text"', '"2dsphere"', '"2d"', '"hashed"']); + }, 5_000); + + it('scopes actual Mongo sort fields to the declaring namespace model', async () => { + const stack = await actualMongoStack(); + const schema = + 'model Post { topOnly String }\nnamespace scoped { model Post { scopedOnly String\n @@index([|]) } }'; + expect( + completeWithActualStack(schema, stack, { clientSupportsSnippets: true }).items.map((item) => [ + item.label, + item.textEdit?.newText, + ]), + ).toEqual([ + ['scopedOnly', 'scopedOnly'], + ['wildcard', `wildcard(${emptySnippetPlaceholder1})`], + ['sort', 'sort($' + '{1:field}, $' + '{2:direction})'], + ]); + expect( + completeWithActualStack(schema.replace('[|]', '[sort(|)]'), stack).items.map( + (item) => item.label, + ), + ).toEqual(['scopedOnly']); + }, 5_000); +}); diff --git a/packages/1-framework/3-tooling/language-server/test/completion-provider.attributes.test.ts b/packages/1-framework/3-tooling/language-server/test/completion-provider.attributes.test.ts new file mode 100644 index 000000000000..2feb807a506f --- /dev/null +++ b/packages/1-framework/3-tooling/language-server/test/completion-provider.attributes.test.ts @@ -0,0 +1,479 @@ +import { EMPTY_GENERATOR_FUNCTIONS } from '@internal/framework-components/components'; +import { assembleAuthoringContributions } from '@internal/framework-components/control'; +import { + buildSymbolTable, + type FieldAttributeSpecContext, + fieldAttribute, +} from '@internal/psl-parser'; +import { parse } from '@internal/psl-parser/syntax'; +import { describe, expect, it } from 'vitest'; +import { CompletionItemKind, InsertTextFormat } from 'vscode-languageserver'; +import { classifyPslCompletionContext } from '../src/completion-context'; +import { providePslCompletionItems } from '../src/completion-provider'; +import { testBinder } from './helpers/binder'; +import { + actualMongoStack, + actualSqlStack, + applyCompletionItem, + attributeContributions, + candidateSource, + complete, + completeWithActualStack, + completionItemByLabel, + controlMutationDefaults, + pslBlockDescriptors, + scalarTypes, + sqlExpressionDataTypes, + sqlExpressionSnippet, +} from './helpers/completion-provider'; + +describe('providePslCompletionItems', () => { + it('returns registry-backed attribute name completions as function items', () => { + const fieldItems = complete(['model Post {', ' id Int @|', '}'].join('\n')).items; + expect(fieldItems.map((item) => item.label)).toEqual(['marker', 'orderFixture', 'ownerAware']); + expect(fieldItems.find((item) => item.label === 'marker')?.detail).toBe( + 'Attaches a named marker to a target.', + ); + expect(fieldItems.map((item) => item.kind)).toEqual([ + CompletionItemKind.Function, + CompletionItemKind.Function, + CompletionItemKind.Function, + ]); + + const modelItems = complete(['model Post {', ' id Int', ' @@|', '}'].join('\n')).items; + expect(modelItems.map((item) => item.label)).toEqual(['rls']); + expect(modelItems.map((item) => item.kind)).toEqual([CompletionItemKind.Function]); + + const blockItems = complete(['policy Rule {', ' @@|', '}'].join('\n')).items; + expect(blockItems.map((item) => item.label)).toEqual(['audit']); + expect(blockItems.map((item) => item.kind)).toEqual([CompletionItemKind.Function]); + }); + + it('resolves the attribute owner without enumerating declarations', () => { + const markedSource = ['model User {', ' id Int @|', '}'].join('\n'); + const cursorOffset = markedSource.indexOf('|'); + const source = `${markedSource.slice(0, cursorOffset)}${markedSource.slice(cursorOffset + 1)}`; + const { document, sources } = parse(source, 'language-server-test.psl'); + const sourceFile = sources.sourceFileFor(document.syntax); + const { symbolTable } = buildSymbolTable({ + documents: [document], + sources, + defaultNamespaceId: 'public', + }); + const context = classifyPslCompletionContext({ + document, + sourceFile, + position: sourceFile.positionAt(cursorOffset), + }); + let modelEnumerationCount = 0; + const observedDefaultNamespace = { + ...symbolTable.defaultNamespace, + models: new Proxy(symbolTable.defaultNamespace.models, { + ownKeys(target) { + modelEnumerationCount += 1; + return Reflect.ownKeys(target); + }, + }), + }; + const observedSymbolTable = { + ...symbolTable, + defaultNamespace: observedDefaultNamespace, + namespaces: { [observedDefaultNamespace.id]: observedDefaultNamespace }, + }; + const factoryOwnerNames: string[] = []; + const observedAuthoringContributions = assembleAuthoringContributions([ + { + id: 'observed-family', + authoring: { + attributeSpecs: { + field: { + first: (ctx: FieldAttributeSpecContext) => { + factoryOwnerNames.push(ctx.model.name); + return fieldAttribute('first', { + documentation: 'Marks the first contributed field attribute.', + }); + }, + second: (ctx: FieldAttributeSpecContext) => { + factoryOwnerNames.push(ctx.model.name); + return fieldAttribute('second', { + documentation: 'Marks the second contributed field attribute.', + }); + }, + }, + model: {}, + }, + }, + }, + ]); + + const items = providePslCompletionItems({ + context, + sourceFile, + candidates: { + scalarTypes, + pslBlockDescriptors, + symbolTable: observedSymbolTable, + binder: testBinder({ + sources, + symbolTable, + scalarTypes, + authoringContributions: attributeContributions, + controlMutationDefaults, + pslBlockDescriptors, + }), + authoringContributions: observedAuthoringContributions, + controlMutationDefaults, + generatorFunctions: EMPTY_GENERATOR_FUNCTIONS, + }, + clientSupportsSnippets: true, + }); + + expect(items.map((item) => item.label)).toEqual(['first', 'second']); + expect(factoryOwnerNames).toEqual(['User', 'User']); + expect(modelEnumerationCount).toBe(0); + }); + + it('returns attribute named keys including optional keys while omitting supplied keys', () => { + const { items, sourceFile, cursorOffset } = complete( + ['model Post {', ' id Int @marker(name: "id", pr|)', '}'].join('\n'), + ); + + expect(items.map((item) => item.label)).toEqual(['priority']); + expect(items[0]?.textEdit).toEqual({ + range: { + start: sourceFile.positionAt(cursorOffset - 'pr'.length), + end: sourceFile.positionAt(cursorOffset), + }, + newText: 'priority: ', + }); + }); + + it('preserves named-key declaration order while filtering supplied keys', () => { + const { items } = complete( + ['model Post {', ' id Int @orderFixture(alpha: 1, |)', '}'].join('\n'), + ); + + expect(items.map((item) => [item.label, item.kind])).toEqual([ + ['zebra', CompletionItemKind.Property], + ['middle', CompletionItemKind.Property], + ]); + }); + + it('uses the current declaration owner when model names collide across namespaces', () => { + const { items } = complete( + [ + 'namespace feature {', + ' model User {', + ' scopedOnly String @ownerAware(|)', + ' }', + '}', + ].join('\n'), + ); + + expect(items.map((item) => item.label)).toEqual(['scopedKey']); + }); + + it('inserts required contributed attribute arguments as snippets for snippet clients', () => { + const { items, sourceFile } = complete( + ['model Post {', ' id Int @mar| // keep', '}'].join('\n'), + { + clientSupportsSnippets: true, + }, + ); + const item = completionItemByLabel(items, 'marker'); + + expect(item).toMatchObject({ + insertTextFormat: InsertTextFormat.Snippet, + textEdit: { + newText: `marker("\${1:target}", name: "\${2:name}")`, + }, + }); + expect(applyCompletionItem({ sourceFile, item })).toEqual( + [ + candidateSource, + 'model Post {', + ` id Int @marker("\${1:target}", name: "\${2:name}") // keep`, + '}', + ].join('\n'), + ); + }); + + it.each([false, true])('gates argument snippet hints on client support: %s', (supported) => { + for (const [source, snippets, hints] of [ + ['model Post { id Int @mar| }', true, true], + ['model Post { id Int @mar| }', false, false], + ['model Post { id Int @mar|ker("x") }', true, false], + ['|', true, false], + ] as const) { + const { items } = complete(source, { + clientSupportsSnippets: snippets, + clientSupportsTriggerParameterHintsCommand: supported, + }); + const item = completionItemByLabel(items, source === '|' ? 'model' : 'marker'); + expect(item.command).toEqual( + supported && hints + ? { title: 'Show argument hints', command: 'editor.action.triggerParameterHints' } + : undefined, + ); + } + }); + + it('keeps plain contributed attribute completion free of snippet syntax', () => { + const { items, sourceFile } = complete( + ['model Post {', ' id Int @mar| // keep', '}'].join('\n'), + ); + const item = completionItemByLabel(items, 'marker'); + + expect(item.insertTextFormat).toBeUndefined(); + expect(item.textEdit).toMatchObject({ newText: 'marker' }); + expect(applyCompletionItem({ sourceFile, item })).toEqual( + [candidateSource, 'model Post {', ' id Int @marker // keep', '}'].join('\n'), + ); + }); + + it('preserves existing attribute delimiters and suffixes instead of inserting required arguments again', () => { + const { items, sourceFile } = complete( + ['model Post {', ' id Int @mar|ker(name: "id") @unique', '}'].join('\n'), + { clientSupportsSnippets: true }, + ); + const item = completionItemByLabel(items, 'marker'); + + expect(item.insertTextFormat).toBeUndefined(); + expect(item.textEdit).toMatchObject({ newText: 'marker' }); + expect(applyCompletionItem({ sourceFile, item })).toEqual( + [candidateSource, 'model Post {', ' id Int @marker(name: "id") @unique', '}'].join('\n'), + ); + }); + + it('uses the attribute AST to preserve an incomplete existing argument list', () => { + const { items, sourceFile } = complete(['model Post {', ' id Int @mar|ker(', '}'].join('\n'), { + clientSupportsSnippets: true, + }); + const item = completionItemByLabel(items, 'marker'); + + expect(item.insertTextFormat).toBeUndefined(); + expect(item.textEdit).toMatchObject({ newText: 'marker' }); + expect(applyCompletionItem({ sourceFile, item })).toEqual( + [candidateSource, 'model Post {', ' id Int @marker(', '}'].join('\n'), + ); + }); + + it('uses actual SQL attribute specs for names and top-level named keys', async () => { + const stack = await actualSqlStack(); + + expect( + completeWithActualStack(['model Post {', ' id Int @|', '}'].join('\n'), stack).items.map( + (item) => item.label, + ), + ).toEqual(['default', 'id', 'map', 'onCreate', 'onUpdate', 'relation', 'unique']); + expect( + completeWithActualStack( + ['model Post {', ' id Int', ' @@|', '}'].join('\n'), + stack, + ).items.map((item) => item.label), + ).toEqual(['base', 'check', 'control', 'discriminator', 'id', 'index', 'map', 'unique']); + expect( + completeWithActualStack(['enum Role {', ' Admin', ' @@|', '}'].join('\n'), stack).items.map( + (item) => item.label, + ), + ).toEqual(['type']); + + const { items, sourceFile, cursorOffset } = completeWithActualStack( + ['model Post {', ' id Int', ' @@index(expression: sql`lower(name)`, ma|)', '}'].join('\n'), + stack, + ); + expect(items.map((item) => item.label)).toEqual([ + 'where', + 'unique', + 'name', + 'map', + 'type', + 'options', + ]); + expect(items.find((item) => item.label === 'map')?.textEdit).toEqual({ + range: { + start: sourceFile.positionAt(cursorOffset - 'ma'.length), + end: sourceFile.positionAt(cursorOffset), + }, + newText: 'map: ', + }); + + expect( + completeWithActualStack( + ['model Post {', ' id Int @default(|)', '}'].join('\n'), + stack, + ).items.map((item) => item.label), + ).toEqual(['true', 'false', 'null']); + + const mapCompletion = completeWithActualStack( + ['model Post {', ' id Int @ma| // keep', '}'].join('\n'), + stack, + { clientSupportsSnippets: true }, + ); + const mapItem = completionItemByLabel(mapCompletion.items, 'map'); + expect(mapItem).toMatchObject({ + insertTextFormat: InsertTextFormat.Snippet, + textEdit: { newText: `map("\${1:name}")` }, + }); + expect(applyCompletionItem({ sourceFile: mapCompletion.sourceFile, item: mapItem })).toEqual( + ['model Post {', ` id Int @map("\${1:name}") // keep`, '}'].join('\n'), + ); + + const checkCompletion = completeWithActualStack( + ['model Post {', ' id Int', ' @@che| // keep', '}'].join('\n'), + stack, + { clientSupportsSnippets: true, dataTypes: await sqlExpressionDataTypes() }, + ); + const checkItem = completionItemByLabel(checkCompletion.items, 'check'); + expect(checkItem).toMatchObject({ + insertTextFormat: InsertTextFormat.Snippet, + textEdit: { newText: `check(expression: ${sqlExpressionSnippet})` }, + }); + expect( + applyCompletionItem({ sourceFile: checkCompletion.sourceFile, item: checkItem }), + ).toEqual( + [ + 'model Post {', + ' id Int', + ` @@check(expression: ${sqlExpressionSnippet}) // keep`, + '}', + ].join('\n'), + ); + }, 5_000); + + it('offers a sql literal where @@index and @@check take SQL', async () => { + const stack = await actualSqlStack(); + const dataTypes = await sqlExpressionDataTypes(); + const [entry] = Object.values(dataTypes); + const valuesAt = (attribute: string) => + completeWithActualStack( + ['model Post {', ' id Int', ` ${attribute}`, '}'].join('\n'), + stack, + { + clientSupportsSnippets: true, + dataTypes, + }, + ).items.map((item) => ({ + label: item.label, + detail: item.detail, + newText: item.textEdit?.newText, + insertTextFormat: item.insertTextFormat, + })); + const sqlItem = { + label: 'sql', + detail: entry?.documentation, + newText: 'sql`$1`', + insertTextFormat: InsertTextFormat.Snippet, + }; + + expect(valuesAt('@@index([id], where: |)')).toEqual([sqlItem]); + expect(valuesAt('@@index(expression: |, map: "post_idx")')).toEqual([sqlItem]); + expect(valuesAt('@@check(expression: |, name: "post_check")')).toEqual([sqlItem]); + }, 5_000); + + it('completes model and field attributes when the source has no data types', async () => { + const stack = await actualSqlStack(); + const labelsAt = (markedSource: string) => + completeWithActualStack(markedSource, stack).items.map((item) => item.label); + + expect(labelsAt(['model Post {', ' id Int', ' @@|', '}'].join('\n'))).toEqual([ + 'base', + 'check', + 'control', + 'discriminator', + 'id', + 'index', + 'map', + 'unique', + ]); + expect(labelsAt(['model Post {', ' id Int @|', '}'].join('\n'))).toEqual([ + 'default', + 'id', + 'map', + 'onCreate', + 'onUpdate', + 'relation', + 'unique', + ]); + expect( + labelsAt(['model Post {', ' id Int', ' @@check(expression: |)', '}'].join('\n')), + ).toEqual([]); + }, 5_000); + + it('uses actual Mongo attribute specs for names and dynamic factory keys', async () => { + const stack = await actualMongoStack(); + + expect( + completeWithActualStack(['model Post {', ' id String @|', '}'].join('\n'), stack).items.map( + (item) => item.label, + ), + ).toEqual(['id', 'map', 'onCreate', 'onUpdate', 'relation', 'unique']); + expect( + completeWithActualStack( + ['model Post {', ' id String', ' @@|', '}'].join('\n'), + stack, + ).items.map((item) => item.label), + ).toEqual(['base', 'discriminator', 'index', 'map', 'textIndex', 'unique']); + expect( + completeWithActualStack(['enum Role {', ' Admin', ' @@|', '}'].join('\n'), stack).items.map( + (item) => item.label, + ), + ).toEqual(['type']); + + expect( + completeWithActualStack( + [ + 'namespace scoped {', + ' model User {', + ' id String', + ' localOnly String', + ' @@index(fields: [id], la|)', + ' }', + '}', + 'model User {', + ' id String', + '}', + ].join('\n'), + stack, + ).items.map((item) => item.label), + ).toEqual([ + 'type', + 'sparse', + 'expireAfterSeconds', + 'filter', + 'include', + 'exclude', + 'default_language', + 'languageOverride', + 'collationLocale', + 'collationStrength', + 'collationCaseLevel', + 'collationCaseFirst', + 'collationNumericOrdering', + 'collationAlternate', + 'collationMaxVariable', + 'collationBackwards', + 'collationNormalization', + ]); + expect( + completeWithActualStack( + ['model Post {', ' author User @relation(name: "Author", f|)', '}'].join('\n'), + stack, + ).items.map((item) => item.label), + ).toEqual(['fields', 'references']); + + const mapCompletion = completeWithActualStack( + ['model Post {', ' id String @ma| // keep', '}'].join('\n'), + stack, + { clientSupportsSnippets: true }, + ); + const mapItem = completionItemByLabel(mapCompletion.items, 'map'); + expect(mapItem).toMatchObject({ + insertTextFormat: InsertTextFormat.Snippet, + textEdit: { newText: `map("\${1:name}")` }, + }); + expect(applyCompletionItem({ sourceFile: mapCompletion.sourceFile, item: mapItem })).toEqual( + ['model Post {', ` id String @map("\${1:name}") // keep`, '}'].join('\n'), + ); + }, 5_000); +}); diff --git a/packages/1-framework/3-tooling/language-server/test/completion-provider.block-values.test.ts b/packages/1-framework/3-tooling/language-server/test/completion-provider.block-values.test.ts new file mode 100644 index 000000000000..e7d17291d867 --- /dev/null +++ b/packages/1-framework/3-tooling/language-server/test/completion-provider.block-values.test.ts @@ -0,0 +1,167 @@ +import type { DataTypeSupport } from '@internal/framework-components/authoring'; +import { createDataTypeLookup } from '@internal/framework-components/codec'; +import { assembleAuthoringContributions } from '@internal/framework-components/control'; +import { + type BlockSpecContext, + fieldAttribute, + jsonValue, + structBlock, +} from '@internal/psl-parser'; +import { describe, expect, it } from 'vitest'; +import { + complete, + completeWithSource, + completionItemByLabel, + controlMutationDefaults, +} from './helpers/completion-provider'; + +describe('providePslCompletionItems', () => { + it('completes a generic block value from the block spec', () => { + const { items } = complete(['policy Rule {', ' on = |', '}'].join('\n')); + + expect(items.map((item) => item.label)).toEqual(['User', 'public', 'auth']); + }); + + it('returns descriptor-backed generic block parameter completions', () => { + const { items, sourceFile, cursorOffset } = complete(['policy Rule {', ' |', '}'].join('\n')); + + expect(items.map((item) => item.label)).toEqual(['on', 'where', 'mode', 'using']); + expect(items.map((item) => item.detail)).toEqual([ + 'Generic block parameter', + 'The policy predicate.', + 'Generic block parameter', + 'Generic block parameter', + ]); + expect(items[0]?.textEdit).toEqual({ + range: { + start: sourceFile.positionAt(cursorOffset), + end: sourceFile.positionAt(cursorOffset), + }, + newText: 'on = ', + }); + }); + + it('returns the full descriptor-backed generic block parameter set excluding already-present sibling keys', () => { + const { items, sourceFile, cursorOffset } = complete( + ['policy Rule {', ' on = User', ' wh|', '}'].join('\n'), + ); + + expect(items.map((item) => item.label)).toEqual(['where', 'mode', 'using']); + expect(items.find((item) => item.label === 'where')).toMatchObject({ + filterText: 'where', + textEdit: { + range: { + start: sourceFile.positionAt(cursorOffset - 'wh'.length), + end: sourceFile.positionAt(cursorOffset), + }, + newText: 'where = ', + }, + }); + }); + + it('uses generic block parameter documentation with a fallback for undocumented parameters', () => { + const { items } = complete('policy Rule { | }'); + expect(completionItemByLabel(items, 'where').detail).toBe('The policy predicate.'); + expect(completionItemByLabel(items, 'on').detail).toBe('Generic block parameter'); + }); + + it('still offers the in-progress key while excluding an already-present sibling key', () => { + const { items } = complete(['policy Rule {', ' where = "x"', ' on|', '}'].join('\n')); + + expect(items.map((item) => item.label)).toEqual(['on', 'mode', 'using']); + }); + + it('returns no generic block parameter completions without a matching descriptor', () => { + const { items } = complete(['extension Rule {', ' |', '}'].join('\n')); + + expect(items).toEqual([]); + }); + + it('offers no key candidates for an arbitrary-key block spec', () => { + const { items } = complete(['inventory Stock {', ' |', '}'].join('\n')); + + expect(items).toEqual([]); + }); + + it('keeps key completion available inside an invalid block', () => { + const { items } = complete( + ['policy Rule {', ' bogus = "not a declared key"', ' |', '}'].join('\n'), + ); + + expect(items.map((item) => item.label)).toEqual(['on', 'where', 'mode', 'using']); + }); + + it('calls the spec factory with the spec context and never invokes rule parsing', () => { + const dataTypes: DataTypeSupport = { + entries: {}, + lookup: createDataTypeLookup([]), + dataTypeOfCodec: () => undefined, + }; + const factoryContexts: BlockSpecContext[] = []; + const throwingRule = { + kind: 'str' as const, + label: 'string', + value: undefined, + parse: () => { + throw new Error('metadata inspection must not invoke rule parsing'); + }, + }; + const { items } = completeWithSource({ + markedSource: ['guard Rule {', ' |', '}'].join('\n'), + pslBlockDescriptors: { + guard: { + kind: 'pslBlock', + keyword: 'guard', + discriminator: 'fixture-guard', + name: { required: true }, + spec: (ctx: BlockSpecContext) => { + factoryContexts.push(ctx); + return structBlock({ + parameters: { + shield: { type: throwingRule, documentation: 'The shield key.' }, + }, + }); + }, + }, + }, + dataTypes, + }); + + expect(items.map((item) => item.label)).toEqual(['shield']); + expect(items[0]?.detail).toBe('The shield key.'); + expect(factoryContexts).toHaveLength(2); + for (const ctx of factoryContexts) { + expect(ctx).toEqual({ symbols: expect.any(Object), dataTypes: expect.any(Object) }); + expect(ctx.dataTypes).toBe(dataTypes); + } + }); + + it.each(['|', '[|]', '{ nested: | }', '[{ nested: [|] }]'])( + 'completes recursive JSON values at %s', + (value) => { + const { items } = completeWithSource({ + markedSource: `model Post { value String @jsonFixture(${value}) }`, + pslBlockDescriptors: {}, + authoringContributions: assembleAuthoringContributions([ + { + id: 'json-fixture', + authoring: { + attributeSpecs: { + field: { + jsonFixture: () => + fieldAttribute('jsonFixture', { + documentation: '', + positional: [{ key: 'value', type: jsonValue(), documentation: '' }], + }), + }, + model: {}, + }, + }, + }, + ]), + controlMutationDefaults, + }); + expect(items.map((item) => item.label)).toEqual(['true', 'false', 'null']); + }, + ); +}); diff --git a/packages/1-framework/3-tooling/language-server/test/completion-provider.declaration-keywords.test.ts b/packages/1-framework/3-tooling/language-server/test/completion-provider.declaration-keywords.test.ts new file mode 100644 index 000000000000..86e0216ed2e1 --- /dev/null +++ b/packages/1-framework/3-tooling/language-server/test/completion-provider.declaration-keywords.test.ts @@ -0,0 +1,282 @@ +import { describe, expect, it } from 'vitest'; +import { type CompletionItem, CompletionItemKind, InsertTextFormat } from 'vscode-languageserver'; +import { + auditAttribute, + complete, + completeWithSource, + completionItemByLabel, + nameSnippetPlaceholder, + policySpec, +} from './helpers/completion-provider'; + +describe('providePslCompletionItems', () => { + it('returns document-level declaration keyword candidates with stable plain-text edits', () => { + const { items, sourceFile, cursorOffset } = complete('|'); + + expect(items.map((item) => item.label)).toEqual([ + 'model', + 'model mixin', + 'type', + 'type mixin', + 'types', + 'namespace', + 'audit', + 'audit mixin', + 'inventory', + 'inventory mixin', + 'policy', + 'policy mixin', + ]); + expect(items.map((item) => item.detail)).toEqual([ + 'Defines a data model.', + 'Declares a mixin for model blocks.', + 'Defines a reusable composite type.', + 'Declares a mixin for type blocks.', + 'Defines reusable type aliases.', + 'Groups declarations belonging to the same database schema or database.', + 'Generic block keyword', + 'Declares a mixin for audit blocks.', + 'An arbitrary-key block.', + 'Declares a mixin for inventory blocks.', + 'Defines a security policy.', + 'Declares a mixin for policy blocks.', + ]); + expect(items[0]).toMatchObject({ + kind: CompletionItemKind.Keyword, + filterText: 'model', + textEdit: { + range: { + start: sourceFile.positionAt(cursorOffset), + end: sourceFile.positionAt(cursorOffset), + }, + newText: 'model ', + }, + }); + expect(items[0]?.insertTextFormat).toBeUndefined(); + }); + + it('returns the full document-level declaration keyword set with a replace range over the typed segment', () => { + const { items, sourceFile, cursorOffset } = complete('mo|'); + + expect(items.map((item) => item.label)).toEqual([ + 'model', + 'model mixin', + 'type', + 'type mixin', + 'types', + 'namespace', + 'audit', + 'audit mixin', + 'inventory', + 'inventory mixin', + 'policy', + 'policy mixin', + ]); + expect(items[0]).toMatchObject({ + filterText: 'model', + textEdit: { + range: { + start: sourceFile.positionAt(cursorOffset - 'mo'.length), + end: sourceFile.positionAt(cursorOffset), + }, + newText: 'model ', + }, + }); + }); + + it('returns namespace-body declaration keywords without document-only native keywords', () => { + const { items } = complete(['namespace feature {', ' |', '}'].join('\n')); + + expect(items.map((item) => item.label)).toEqual([ + 'model', + 'model mixin', + 'type', + 'type mixin', + 'audit', + 'audit mixin', + 'inventory', + 'inventory mixin', + 'policy', + 'policy mixin', + ]); + expect(items.map((item) => item.label)).not.toContain('types'); + expect(items.map((item) => item.label)).not.toContain('namespace'); + }); + + it('returns the full namespace-body declaration keyword set with a replace range over the typed segment', () => { + const { items, sourceFile, cursorOffset } = complete( + ['namespace feature {', ' po|', '}'].join('\n'), + ); + + expect(items.map((item) => item.label)).toEqual([ + 'model', + 'model mixin', + 'type', + 'type mixin', + 'audit', + 'audit mixin', + 'inventory', + 'inventory mixin', + 'policy', + 'policy mixin', + ]); + expect(items.find((item) => item.label === 'policy')).toMatchObject({ + filterText: 'policy', + textEdit: { + range: { + start: sourceFile.positionAt(cursorOffset - 'po'.length), + end: sourceFile.positionAt(cursorOffset), + }, + newText: 'policy ', + }, + }); + }); + + it('returns snippet declaration keyword edits only when the client supports snippets', () => { + const { items } = complete('|', { clientSupportsSnippets: true }); + + expect(items.find((item) => item.label === 'model')).toMatchObject({ + insertTextFormat: InsertTextFormat.Snippet, + textEdit: { newText: `model ${nameSnippetPlaceholder} {\n \${0:// Fields}\n}` }, + }); + expect(items.find((item) => item.label === 'policy')).toMatchObject({ + insertTextFormat: InsertTextFormat.Snippet, + textEdit: { + newText: `policy ${nameSnippetPlaceholder} {\n \${0:// Block keys and attributes}\n}`, + }, + }); + }); + + it.each([true, false])( + 'inserts a body placeholder without pre-filled keys, snippets=%s', + (snippets) => { + const { items } = completeWithSource({ + markedSource: '|', + clientSupportsSnippets: snippets, + pslBlockDescriptors: { + security: { + policy: { + kind: 'pslBlock', + keyword: 'policy', + discriminator: 'policy', + name: { required: true }, + spec: policySpec, + attributes: { audit: () => auditAttribute }, + }, + }, + }, + }); + const item = completionItemByLabel(items, 'policy'); + expect(item.textEdit?.newText).toBe( + snippets + ? [ + `policy ${nameSnippetPlaceholder} {`, + ' $' + '{0:// Block keys and attributes}', + '}', + ].join('\n') + : 'policy ', + ); + expect(item.insertTextFormat).toBe(snippets ? InsertTextFormat.Snippet : undefined); + }, + ); + + describe('mixin declaration items', () => { + const sortedLabels = (items: readonly CompletionItem[]) => + [...items] + .sort((left, right) => ((left.sortText ?? '') < (right.sortText ?? '') ? -1 : 1)) + .map((item) => item.label); + + it('offers model before model mixin when a prefix of the keyword is typed', () => { + const { items, sourceFile, cursorOffset } = complete('mo|'); + const range = { + start: sourceFile.positionAt(cursorOffset - 'mo'.length), + end: sourceFile.positionAt(cursorOffset), + }; + + expect(sortedLabels(items).slice(0, 2)).toEqual(['model', 'model mixin']); + expect(completionItemByLabel(items, 'model mixin')).toMatchObject({ + kind: CompletionItemKind.Keyword, + detail: 'Declares a mixin for model blocks.', + filterText: 'model mixin', + sortText: '0:model mixin', + textEdit: { range, newText: 'model mixin ' }, + }); + }); + + it('sorts each mixin item directly after the item of its keyword', () => { + expect(sortedLabels(complete('|').items)).toEqual([ + 'model', + 'model mixin', + 'namespace', + 'type', + 'type mixin', + 'types', + 'audit', + 'audit mixin', + 'inventory', + 'inventory mixin', + 'policy', + 'policy mixin', + ]); + }); + + it('offers no mixin item for namespace or types, and never the word mixin alone', () => { + const labels = complete('|').items.map((item) => item.label); + + expect(labels).not.toContain('namespace mixin'); + expect(labels).not.toContain('types mixin'); + expect(labels).not.toContain('mixin'); + }); + + it('inserts a mixin declaration with an empty body for a native and for a struct block keyword', () => { + const { items } = completeWithSource({ + markedSource: '|', + clientSupportsSnippets: true, + pslBlockDescriptors: { + security: { + policy: { + kind: 'pslBlock', + keyword: 'policy', + discriminator: 'policy', + name: { required: true }, + spec: policySpec, + attributes: { audit: () => auditAttribute }, + }, + }, + }, + }); + + expect(completionItemByLabel(items, 'model mixin')).toMatchObject({ + insertTextFormat: InsertTextFormat.Snippet, + textEdit: { newText: `model mixin ${nameSnippetPlaceholder} {\n \${0:// Fields}\n}` }, + }); + expect(completionItemByLabel(items, 'policy mixin')).toMatchObject({ + kind: CompletionItemKind.Keyword, + detail: 'Declares a mixin for policy blocks.', + filterText: 'policy mixin', + sortText: '1:policy mixin', + insertTextFormat: InsertTextFormat.Snippet, + textEdit: { + newText: `policy mixin ${nameSnippetPlaceholder} {\n \${0:// Block keys and attributes}\n}`, + }, + }); + }); + + it('offers the mixin item while the whole keyword is the word before the cursor, and nothing once more is typed', () => { + expect(complete('model|').items.map((item) => item.label)).toContain('model mixin'); + expect(complete('model m|').items).toEqual([]); + expect(complete('model mixin|').items).toEqual([]); + }); + + it.each([ + ['a model', ['model User {', ' |', '}']], + ['a block', ['policy P {', ' |', '}']], + ['a model mixin', ['model mixin M {', ' |', '}']], + ['a block mixin', ['policy mixin M {', ' |', '}']], + ])('offers none inside %s', (_where, lines) => { + const labels = complete(lines.join('\n')).items.map((item) => item.label); + + expect(labels.filter((label) => label.endsWith(' mixin'))).toEqual([]); + }); + }); +}); diff --git a/packages/1-framework/3-tooling/language-server/test/completion-provider.field-types.test.ts b/packages/1-framework/3-tooling/language-server/test/completion-provider.field-types.test.ts new file mode 100644 index 000000000000..fe04b8b3c19f --- /dev/null +++ b/packages/1-framework/3-tooling/language-server/test/completion-provider.field-types.test.ts @@ -0,0 +1,301 @@ +import type { AuthoringTypeNamespace } from '@internal/framework-components/authoring'; +import { assembleAuthoringContributions } from '@internal/framework-components/control'; +import { timeouts } from '@repo/test-utils'; +import { describe, expect, it } from 'vitest'; +import { CompletionItemKind, CompletionItemTag } from 'vscode-languageserver'; +import { + complete, + completeWithSource, + completionItemByLabel, + controlMutationDefaults, + importFromPackageRoot, +} from './helpers/completion-provider'; + +describe('providePslCompletionItems', () => { + it('returns bare model field type completion candidates without custom ranking', () => { + const { items, sourceFile, cursorOffset } = complete( + ['model Post {', ' author |', '}'].join('\n'), + ); + + expect(items.map(({ label, detail }) => [label, detail]).sort()).toEqual([ + ['Address', 'Composite type'], + ['Boolean', 'Configured scalar type'], + ['DateTime', 'Configured scalar type'], + ['Email', 'Scalar type'], + ['Int', 'Configured scalar type'], + ['Post', 'Model'], + ['String', 'Configured scalar type'], + ['User', 'Model'], + ['UserId', 'Type alias'], + ['auth', 'Namespace'], + ['public', 'Namespace'], + ]); + for (const item of items) expect(item).not.toHaveProperty('sortText'); + expect(completionItemByLabel(items, 'Boolean').textEdit).toEqual({ + range: { + start: sourceFile.positionAt(cursorOffset), + end: sourceFile.positionAt(cursorOffset), + }, + newText: 'Boolean', + }); + }); + + it('returns the full bare candidate set with a replace range over the typed segment', () => { + const { items, sourceFile, cursorOffset } = complete( + ['model Post {', ' reviewer U|', '}'].join('\n'), + ); + + expect(items.map((item) => item.label).sort()).toEqual([ + 'Address', + 'Boolean', + 'DateTime', + 'Email', + 'Int', + 'Post', + 'String', + 'User', + 'UserId', + 'auth', + 'public', + ]); + expect(items.find((item) => item.label === 'User')).toMatchObject({ + filterText: 'User', + textEdit: { + range: { + start: sourceFile.positionAt(cursorOffset - 'U'.length), + end: sourceFile.positionAt(cursorOffset), + }, + newText: 'User', + }, + }); + }); + + it('returns the full bare candidate set including the namespace qualifier with a replace range over the typed segment', () => { + const { items, sourceFile, cursorOffset } = complete( + ['model Post {', ' reviewer a|', '}'].join('\n'), + ); + + expect(items.map((item) => item.label).sort()).toEqual([ + 'Address', + 'Boolean', + 'DateTime', + 'Email', + 'Int', + 'Post', + 'String', + 'User', + 'UserId', + 'auth', + 'public', + ]); + expect(items.find((item) => item.label === 'auth')).toMatchObject({ + kind: CompletionItemKind.Module, + detail: 'Namespace', + filterText: 'auth', + textEdit: { + range: { + start: sourceFile.positionAt(cursorOffset - 'a'.length), + end: sourceFile.positionAt(cursorOffset), + }, + newText: 'auth.', + }, + }); + }); + + it('returns namespace members after a namespace qualifier', () => { + const { items, sourceFile, cursorOffset } = complete( + ['model Post {', ' owner auth.|', '}'].join('\n'), + ); + + expect(items.map((item) => item.label).sort()).toEqual(['Account', 'Profile', 'User']); + expect(completionItemByLabel(items, 'Account').textEdit).toEqual({ + range: { + start: sourceFile.positionAt(cursorOffset), + end: sourceFile.positionAt(cursorOffset), + }, + newText: 'Account', + }); + }); + + it('returns the full namespace member set with replacement metadata for the typed segment', () => { + const { items, sourceFile, cursorOffset } = complete( + ['model Post {', ' owner auth.U|', '}'].join('\n'), + ); + + expect(items.map((item) => item.label).sort()).toEqual(['Account', 'Profile', 'User']); + for (const item of items) expect(item).not.toHaveProperty('sortText'); + expect(items.find((item) => item.label === 'User')).toMatchObject({ + filterText: 'User', + detail: 'Model', + textEdit: { + range: { + start: sourceFile.positionAt(cursorOffset - 'U'.length), + end: sourceFile.positionAt(cursorOffset), + }, + newText: 'User', + }, + }); + }); + + it('does not leak local namespace members into a foreign contract-space reference', () => { + const { items } = complete(['model Post {', ' owner supabase:auth.P|', '}'].join('\n')); + + expect(items).toEqual([]); + }); + + it('returns no completions for a contract-space-qualified position', () => { + const { items } = complete(['model Post {', ' external supabase:|', '}'].join('\n')); + + expect(items).toEqual([]); + }); + + it('uses registered scalar documentation for completion details', () => { + const { items } = completeWithSource({ + markedSource: 'model Post { value | }', + pslBlockDescriptors: {}, + authoringContributions: assembleAuthoringContributions([ + { + id: 'documented-scalars', + authoring: { + type: { + String: { + kind: 'typeConstructor', + documentation: 'Variable-length Unicode text.', + output: { codecId: 'fixture/text@1' }, + }, + }, + }, + }, + ]), + controlMutationDefaults, + }); + expect(completionItemByLabel(items, 'String').detail).toBe('Variable-length Unicode text.'); + expect(completionItemByLabel(items, 'Int').detail).toBe('Configured scalar type'); + }); + + it('completes a field preset as a call', () => { + const { items } = completeWithSource({ + markedSource: 'model Post { value | }', + pslBlockDescriptors: {}, + clientSupportsSnippets: false, + authoringContributions: assembleAuthoringContributions([ + { + id: 'presets', + authoring: { + field: { + stamp: { + kind: 'fieldPreset', + output: { codecId: 'fixture/timestamp@1' }, + }, + }, + }, + }, + ]), + controlMutationDefaults, + }); + const item = completionItemByLabel(items, 'stamp'); + expect({ + kind: item.kind, + detail: item.detail, + newText: item.textEdit === undefined ? undefined : item.textEdit.newText, + }).toEqual({ kind: CompletionItemKind.Function, detail: 'Field preset', newText: 'stamp()' }); + }); + + it( + 'lists deprecated Mongo scalar names tagged deprecated, naming the replacement', + async () => { + const { mongoScalarAuthoringTypes } = await importFromPackageRoot<{ + readonly mongoScalarAuthoringTypes: AuthoringTypeNamespace; + }>('../../../3-targets/mongo/2-adapter/src/exports/control.ts'); + const { items } = completeWithSource({ + markedSource: 'model Post { value | }', + pslBlockDescriptors: {}, + scalarTypes: Object.keys(mongoScalarAuthoringTypes), + authoringContributions: assembleAuthoringContributions([ + { id: 'mongo-scalars', authoring: { type: mongoScalarAuthoringTypes } }, + ]), + controlMutationDefaults, + }); + const scalars = items.filter((item) => item.kind === CompletionItemKind.Class); + const current = ['Int32', 'Double', 'Bool', 'Date']; + const deprecated = [ + ['Int', 'Int32'], + ['Float', 'Double'], + ['Boolean', 'Bool'], + ['DateTime', 'Date'], + ] as const; + + expect( + scalars + .filter((item) => item.tags?.includes(CompletionItemTag.Deprecated)) + .map((item) => item.label) + .sort(), + ).toEqual(deprecated.map(([name]) => name).sort()); + for (const name of current) { + expect(completionItemByLabel(items, name)).not.toHaveProperty('tags'); + } + for (const [name, replacement] of deprecated) { + expect(completionItemByLabel(items, name)).toMatchObject({ + tags: [CompletionItemTag.Deprecated], + detail: expect.stringContaining(`Deprecated: use ${replacement}`), + }); + } + }, + timeouts.coldTransformImport, + ); + + it('does not return generic block symbols as model field type candidates', () => { + const { items } = complete(['model Post {', ' audit |', '}'].join('\n')); + + expect(items.map((item) => item.label)).not.toContain('Audit'); + expect(items.map((item) => item.label)).not.toContain('auth.ScopedAudit'); + }); + + it('offers the same field type candidates in a document that holds a mixin and an inclusion', () => { + const model = ['model Post {', ' author |', '}'].join('\n'); + const mixins = [ + 'model mixin Timestamps {', + ' createdAt DateTime', + '}', + 'model Tagged {', + ' id Int', + ' +Timestamps', + '}', + ].join('\n'); + const plain = ['', '', '', 'model Tagged {', ' id Int', '', '}'].join('\n'); + + expect(complete(`${mixins}\n${model}`).items).toEqual(complete(`${plain}\n${model}`).items); + }); + + it.each([ + ['in a mixin field type slot', 'model mixin Timestamps {\n createdAt |\n}'], + ['in a mixin header', 'model mixin |'], + ['after the plus of an inclusion', 'model Post {\n id Int\n +|\n}'], + ['inside the name of an inclusion', 'model Post {\n +auth.Ti|\n}'], + ['in an enum mixin body', 'enum mixin BaseRoles {\n |\n}'], + ])('completes without an error %s', (_case, markedSource) => { + expect(() => complete(markedSource)).not.toThrow(); + }); + + it('does not offer a mixin as a field type, by its name or as a namespace member', () => { + const mixins = [ + 'model mixin Timestamps {', + ' createdAt DateTime', + '}', + 'namespace shared {', + ' model mixin Audited {', + ' actor String', + ' }', + ' model Marker {', + ' id Int', + ' }', + '}', + ].join('\n'); + const unqualified = complete(`${mixins}\nmodel Post {\n stamps |\n}`).items; + const qualified = complete(`${mixins}\nmodel Post {\n stamps shared.|\n}`).items; + + expect(unqualified.map((item) => item.label)).toContain('shared'); + expect(unqualified.map((item) => item.label)).not.toContain('Timestamps'); + expect(qualified.map((item) => item.label)).toEqual(['Marker']); + }); +}); diff --git a/packages/1-framework/3-tooling/language-server/test/completion-provider.inclusions.test.ts b/packages/1-framework/3-tooling/language-server/test/completion-provider.inclusions.test.ts new file mode 100644 index 000000000000..f801c77e0b2a --- /dev/null +++ b/packages/1-framework/3-tooling/language-server/test/completion-provider.inclusions.test.ts @@ -0,0 +1,201 @@ +import { describe, expect, it } from 'vitest'; +import { CompletionItemKind } from 'vscode-languageserver'; +import { + completeWithSource, + completionItemByLabel, + pslBlockDescriptors, +} from './helpers/completion-provider'; + +describe('providePslCompletionItems — after the plus of an inclusion', () => { + const mixinsOf = (prefix: string, indent: string) => + [ + `model mixin ${prefix}Stamps {`, + '}', + `type mixin ${prefix}Geo {`, + '}', + `enum mixin ${prefix}Roles {`, + '}', + `policy mixin ${prefix}Policy {`, + '}', + ].map((line) => `${indent}${line}`); + + function after(block: readonly string[], placement: 'billing' | 'top' = 'billing') { + const namespace = placement === 'top' ? undefined : placement; + const blockLines = + namespace === undefined ? block : block.map((line) => (line === '' ? line : ` ${line}`)); + const markedSource = [ + ...mixinsOf('Top', ''), + ...(namespace === undefined ? blockLines : []), + 'namespace auth {', + ...mixinsOf('Auth', ' '), + '}', + 'namespace billing {', + ...mixinsOf('Billing', ' '), + ' model mixin Used {', + ' }', + ...(namespace === undefined ? [] : blockLines), + '}', + ].join('\n'); + return completeWithSource({ markedSource, pslBlockDescriptors }); + } + + const offered = (block: readonly string[], placement: 'billing' | 'top' = 'billing') => + after(block, placement) + .items.map(({ label, detail }) => [label, detail]) + .sort(); + + const namespaces = [ + ['auth', 'Namespace'], + ['billing', 'Namespace'], + ['public', 'Namespace'], + ]; + + it.each([ + ['a model', ['model Invoice {', ' id Int', ' +Used', ' +|', '}'], 'Stamps', 'model mixin'], + ['a composite type', ['type Address {', ' +|', ' street String', '}'], 'Geo', 'type mixin'], + ['an enum', ['enum Role {', ' +|', ' GUEST', '}'], 'Roles', 'enum mixin'], + ['a struct block', ['policy P {', ' +|', '}'], 'Policy', 'policy mixin'], + ])( + 'offers the mixins of the keyword that an unqualified name reaches, and each namespace, in %s', + (_block, lines, suffix, detail) => { + expect(offered(lines)).toEqual( + [[`Billing${suffix}`, detail], [`Top${suffix}`, detail], ...namespaces].sort(), + ); + }, + ); + + it('offers the same while a name is being typed after the plus', () => { + expect(offered(['model Invoice {', ' +Bil|', '}'])).toEqual( + offered(['model Invoice {', ' +|', '}']), + ); + }); + + it('offers only top-level mixins of the keyword and each namespace in a top-level block', () => { + expect(offered(['enum Role {', ' +|', '}'], 'top')).toEqual( + [['TopRoles', 'enum mixin'], ...namespaces].sort(), + ); + }); + + it.each([ + ['a model', ['model Invoice {', ' +auth.|', '}'], [['AuthStamps', 'model mixin']]], + ['a composite type', ['type Address {', ' +auth.|', '}'], [['AuthGeo', 'type mixin']]], + ['an enum', ['enum Role {', ' +auth.|', '}'], [['AuthRoles', 'enum mixin']]], + ['a struct block', ['policy P {', ' +auth.|', '}'], [['AuthPolicy', 'policy mixin']]], + ])( + 'offers the mixins of the keyword in the named namespace after a qualifier, in %s', + (_block, lines, expected) => { + expect(offered(lines)).toEqual(expected); + expect(offered(lines.map((line) => line.replace('auth.|', 'auth.Au|')))).toEqual(expected); + }, + ); + + it('offers a namespace only when it has a mixin that would be offered after its qualifier', () => { + const source = (block: readonly string[]) => + [ + 'namespace models {', + ' model mixin Stamps {', + ' }', + '}', + 'namespace enums {', + ' enum mixin Roles {', + ' }', + '}', + 'namespace empty {', + ' model Plain {', + ' id Int', + ' }', + '}', + ...block, + ].join('\n'); + const labels = (block: readonly string[]) => + completeWithSource({ markedSource: source(block), pslBlockDescriptors }) + .items.map(({ label, detail }) => [label, detail]) + .sort(); + + expect(labels(['model Invoice {', ' +|', '}'])).toEqual([['models', 'Namespace']]); + expect(labels(['enum Role {', ' +|', '}'])).toEqual([['enums', 'Namespace']]); + expect(labels(['model Invoice {', ' +models.Stamps', ' +|', '}'])).toEqual([]); + expect(labels(['policy P {', ' +|', '}'])).toEqual([]); + }); + + it('does not offer a top-level mixin whose name a model of the namespace hides', () => { + const { items } = completeWithSource({ + markedSource: [ + 'model mixin Timestamps {', + ' createdAt DateTime', + '}', + 'namespace app {', + ' model Timestamps {', + ' id Int', + ' }', + ' model User {', + ' id Int', + ' +|', + ' }', + '}', + ].join('\n'), + pslBlockDescriptors, + }); + + expect(items.map(({ label, detail }) => [label, detail])).toEqual([['public', 'Namespace']]); + }); + + it('offers nothing after a qualifier that names no namespace', () => { + expect(offered(['model Invoice {', ' +nowhere.|', '}'])).toEqual([]); + expect(offered(['model Invoice {', ' +Invoice.|', '}'])).toEqual([]); + }); + + it('leaves out a mixin the block already includes, by either spelling', () => { + expect( + offered(['model Invoice {', ' +Used', ' +billing.BillingStamps', ' +|', '}']), + ).toEqual([ + ['TopStamps', 'model mixin'], + ['auth', 'Namespace'], + ['public', 'Namespace'], + ]); + expect(offered(['model Invoice {', ' +Used', ' +billing.|', '}'])).toEqual([ + ['BillingStamps', 'model mixin'], + ]); + }); + + it('still offers the mixin whose complete name the cursor is at the end of', () => { + expect(offered(['model Invoice {', ' +Used|', '}'])).toContainEqual(['Used', 'model mixin']); + }); + + it('offers nothing after a plus inside a mixin body', () => { + expect(offered(['model mixin Audited {', ' +|', '}'])).toEqual([]); + expect(offered(['policy mixin Shared {', ' +auth.|', '}'])).toEqual([]); + }); + + it('offers no entry keys after a plus in a struct block', () => { + const labels = after(['policy P {', ' +|', '}']).items.map((item) => item.label); + const keys = after(['policy P {', ' |', '}']).items.map((item) => item.label); + + expect(keys.length).toBeGreaterThan(0); + expect(labels.filter((label) => keys.includes(label))).toEqual([]); + }); + + it('offers no mixins on the line after a plus', () => { + expect(offered(['model Invoice {', ' +', ' |', '}'])).toEqual([]); + }); + + it('replaces the typed name with the mixin name, and a namespace with its name and a dot', () => { + const { items, sourceFile, cursorOffset } = after(['model Invoice {', ' +Bil|', '}']); + const range = { + start: sourceFile.positionAt(cursorOffset - 3), + end: sourceFile.positionAt(cursorOffset), + }; + + expect(completionItemByLabel(items, 'BillingStamps')).toMatchObject({ + kind: CompletionItemKind.Interface, + detail: 'model mixin', + filterText: 'BillingStamps', + textEdit: { range, newText: 'BillingStamps' }, + }); + expect(completionItemByLabel(items, 'auth')).toMatchObject({ + kind: CompletionItemKind.Module, + detail: 'Namespace', + textEdit: { range, newText: 'auth.' }, + }); + }); +}); diff --git a/packages/1-framework/3-tooling/language-server/test/completion-provider.mixin-bodies.test.ts b/packages/1-framework/3-tooling/language-server/test/completion-provider.mixin-bodies.test.ts new file mode 100644 index 000000000000..9d90ab4c6bef --- /dev/null +++ b/packages/1-framework/3-tooling/language-server/test/completion-provider.mixin-bodies.test.ts @@ -0,0 +1,141 @@ +import { describe, expect, it } from 'vitest'; +import type { CompletionItem } from 'vscode-languageserver'; +import { complete } from './helpers/completion-provider'; + +describe('providePslCompletionItems — inside a mixin body', () => { + const declarationKeywords = ['namespace', 'types']; + + const withoutOwnName = (items: readonly CompletionItem[]) => + items.filter((item) => item.label !== 'Probe'); + + function itemsIn(header: string, body: readonly string[], closed = true) { + return complete([header, ...body, ...(closed ? ['}'] : [])].join('\n')).items; + } + + it.each([ + ['a field type', [' scopedOnly Int', ' value |']], + ['a field attribute name', [' scopedOnly Int', ' value Int @|']], + ['a field attribute argument', [' value Int @marker(|)']], + [ + 'a field attribute key that depends on the fields of the owner', + [' scopedOnly Int', ' value Int @ownerAware(|)'], + ], + ['a field attribute key when the owner lacks the field', [' value Int @ownerAware(|)']], + ['a block attribute name', [' value Int', ' @@|']], + ['a block attribute argument', [' value Int', ' @@security.rls(|)']], + ])('offers in a model mixin what a model offers at %s', (_position, body) => { + const inMixin = itemsIn('model mixin Probe {', body); + + expect(inMixin).toEqual(withoutOwnName(itemsIn('model Probe {', body))); + expect( + inMixin.map((item) => item.label).filter((label) => declarationKeywords.includes(label)), + ).toEqual([]); + }); + + it('offers something at each model position the fixture has candidates for', () => { + expect(itemsIn('model mixin Probe {', [' value |']).length).toBeGreaterThan(0); + expect(itemsIn('model mixin Probe {', [' value Int @|']).length).toBeGreaterThan(0); + expect(itemsIn('model mixin Probe {', [' value Int', ' @@|']).length).toBeGreaterThan(0); + expect( + itemsIn('model mixin Probe {', [' scopedOnly Int', ' value Int @ownerAware(|)']).map( + (item) => item.label, + ), + ).toEqual(['scopedKey']); + }); + + it.each([ + ['a field type', [' value |']], + ['a field attribute name', [' value Int @|']], + ['a field attribute argument', [' value Int @marker(|)']], + ['a block attribute name', [' value Int', ' @@|']], + ])('offers in a type mixin what a composite type offers at %s', (_position, body) => { + const inMixin = itemsIn('type mixin Probe {', body); + + expect(inMixin).toEqual(withoutOwnName(itemsIn('type Probe {', body))); + expect( + inMixin.map((item) => item.label).filter((label) => declarationKeywords.includes(label)), + ).toEqual([]); + }); + + it.each([ + ['an entry key', 'policy', [' |']], + ['an entry key after another entry', 'policy', [' on = User', ' |']], + ['an entry value that names a model', 'policy', [' on = |']], + ['an entry value with fixed identifiers', 'policy', [' mode = |']], + ['a block attribute name', 'policy', [' @@|']], + ['a block attribute argument', 'policy', [' @@audit(|)']], + ['an entry key of a map block', 'inventory', [' |']], + ['an entry value of a map block', 'inventory', [' count = |']], + ])( + 'offers in a mixin for a key = value keyword what a block of that keyword offers at %s', + (_position, keyword, body) => { + const inMixin = itemsIn(`${keyword} mixin Probe {`, body); + + expect(inMixin).toEqual(itemsIn(`${keyword} Probe {`, body)); + expect( + inMixin.map((item) => item.label).filter((label) => declarationKeywords.includes(label)), + ).toEqual([]); + }, + ); + + it('offers something at each struct block position', () => { + expect(itemsIn('policy mixin Probe {', [' |']).map((item) => item.label)).toContain('on'); + expect(itemsIn('policy mixin Probe {', [' mode = |']).map((item) => item.label)).toEqual([ + 'permissive', + 'restrictive', + ]); + expect(itemsIn('policy mixin Probe {', [' @@|']).map((item) => item.label)).toEqual(['audit']); + }); + + it('offers nothing and does not fail in a mixin for a keyword with no registered block', () => { + expect(itemsIn('widget mixin Probe {', [' |'])).toEqual(itemsIn('widget Probe {', [' |'])); + expect(itemsIn('widget mixin Probe {', [' |'])).toEqual([]); + expect(itemsIn('widget mixin Probe {', [' @@|'])).toEqual([]); + expect(itemsIn('widget mixin Probe {', [' key = |'])).toEqual([]); + }); + + it('completes inside a mixin that is not closed yet as inside a model that is not closed yet', () => { + const body = [' scopedOnly Int', ' value Int @|']; + + expect(itemsIn('model mixin Probe {', body, false)).toEqual( + itemsIn('model Probe {', body, false), + ); + expect(itemsIn('model mixin Probe {', body, false).length).toBeGreaterThan(0); + expect(itemsIn('policy mixin Probe {', [' |'], false)).toEqual( + itemsIn('policy Probe {', [' |'], false), + ); + }); + + it('offers the same inside a mixin declared in a namespace', () => { + const inNamespace = (header: string) => + complete(['namespace scoped {', ` ${header}`, ' value |', ' }', '}'].join('\n')).items; + + expect(inNamespace('model mixin Probe {')).toEqual( + withoutOwnName(inNamespace('model Probe {')), + ); + }); +}); + +describe('providePslCompletionItems — keys a block already has', () => { + const keysOf = (block: readonly string[]) => + complete(['policy mixin Shared {', ' on = User', ' where = "true"', '}', ...block].join('\n')) + .items.map((item) => item.label) + .sort(); + + it('does not offer a key that an included mixin provides', () => { + expect(keysOf(['policy P {', ' +Shared', ' |', '}'])).toEqual(['mode', 'using']); + }); + + it('offers that key in the same block without the inclusion', () => { + expect(keysOf(['policy P {', ' |', '}'])).toEqual(['mode', 'on', 'using', 'where']); + }); + + it('does not offer a key written in the block itself, and still offers the key under the cursor', () => { + expect(keysOf(['policy P {', ' mode = permissive', ' |', '}'])).toEqual([ + 'on', + 'using', + 'where', + ]); + expect(keysOf(['policy P {', ' +Shared', ' mo|', '}'])).toEqual(['mode', 'using']); + }); +}); diff --git a/packages/1-framework/3-tooling/language-server/test/completion-provider.test.ts b/packages/1-framework/3-tooling/language-server/test/completion-provider.test.ts index 8e4efc291657..06baff941994 100644 --- a/packages/1-framework/3-tooling/language-server/test/completion-provider.test.ts +++ b/packages/1-framework/3-tooling/language-server/test/completion-provider.test.ts @@ -1,476 +1,14 @@ -import { dirname, resolve } from 'node:path'; -import { fileURLToPath, pathToFileURL } from 'node:url'; -import type { - AuthoringEntityTypeNamespace, - AuthoringPslBlockDescriptorNamespace, - AuthoringTypeNamespace, - DataTypeAuthoringEntry, - DataTypeSupport, -} from '@internal/framework-components/authoring'; -import { createDataTypeLookup, dataType } from '@internal/framework-components/codec'; -import { - EMPTY_GENERATOR_FUNCTIONS, - type GeneratorFunctionRegistry, -} from '@internal/framework-components/components'; - -import { - assembleAuthoringContributions, - assembleControlMutationDefaults, - type ControlMutationDefaultRegistry, -} from '@internal/framework-components/control'; -import { - type AttributeSpecNamespace, - type BlockSpecContext, - blockAttribute, - buildSymbolTable, - EMPTY_DATA_TYPES, - entityRef, - type FieldAttributeSpecContext, - type FuncCallSig, - fieldAttribute, - identifier, - int, - jsonValue, - mapBlock, - modelAttribute, - oneOf, - optional, - str, - structBlock, -} from '@internal/psl-parser'; -import { parse, type SourceFile } from '@internal/psl-parser/syntax'; -import { timeouts } from '@repo/test-utils'; +import type { AuthoringTypeNamespace } from '@internal/framework-components/authoring'; +import { assembleAuthoringContributions } from '@internal/framework-components/control'; import { describe, expect, it } from 'vitest'; +import { CompletionItemKind, CompletionItemTag, InsertTextFormat } from 'vscode-languageserver'; import { - type CompletionItem, - CompletionItemKind, - CompletionItemTag, - InsertTextFormat, -} from 'vscode-languageserver'; -import { classifyPslCompletionContext } from '../src/completion-context'; -import { providePslCompletionItems } from '../src/completion-provider'; -import { renderSignatureLabel, resolveSignatureParameters } from '../src/signature-help'; -import { testBinder } from './helpers/binder'; - -const scalarTypes = ['String', 'Int', 'Boolean', 'DateTime'] as const; -const nameSnippetPlaceholder = '$' + '{1:Name}'; -const emptySnippetPlaceholder1 = '$' + '{1:}'; -const sqlExpressionSnippet = 'sql`$' + '{1:expression}`'; -const packageRoot = dirname(dirname(fileURLToPath(import.meta.url))); - -const markerAttribute = fieldAttribute('marker', { - documentation: 'Attaches a named marker to a target.', - positional: [{ key: 'target', type: str(), documentation: 'The marker target.' }], - named: { - name: { type: str(), documentation: 'The marker name.' }, - priority: { type: optional(int()), documentation: 'The marker priority.' }, - }, -}); -const orderFixtureAttribute = fieldAttribute('orderFixture', { - documentation: 'Accepts named values in declaration order rather than alphabetical order.', - named: { - zebra: { type: int(), documentation: 'The first declared value.' }, - alpha: { type: int(), documentation: 'The second declared value.' }, - middle: { type: int(), documentation: 'The third declared value.' }, - }, -}); -const rlsAttribute = modelAttribute('rls', { - documentation: 'Configures row-level security for this model.', - named: { - enabled: { type: optional(str()), documentation: 'The security enablement setting.' }, - mode: { type: str(), documentation: 'The security mode.' }, - }, -}); -const auditAttribute = blockAttribute('audit', { - documentation: 'Configures auditing for this block.', - named: { - reason: { type: optional(str()), documentation: 'The reason for auditing.' }, - level: { type: int(), documentation: 'The audit level.' }, - }, -}); - -const attributeContributions = assembleAuthoringContributions([ - { - id: 'fixture-family', - authoring: { - attributeSpecs: { - field: { - marker: () => markerAttribute, - orderFixture: () => orderFixtureAttribute, - ownerAware: (ctx: FieldAttributeSpecContext) => - fieldAttribute('ownerAware', { - documentation: 'Selects a key based on the declaring model’s fields.', - named: { - [Object.hasOwn(ctx.model.fields, 'scopedOnly') ? 'scopedKey' : 'topKey']: { - type: str(), - documentation: 'The value for the owner-specific key.', - }, - }, - }), - }, - model: {}, - }, - }, - }, - { - id: 'fixture-target', - authoring: { - modelAttributes: { - security: { - rls: { - kind: 'modelAttribute', - attribute: 'rls', - spec: () => rlsAttribute, - lower: () => undefined, - }, - }, - }, - }, - }, -]); -const controlMutationDefaults = assembleControlMutationDefaults([]); -const policySpec = () => - structBlock({ - parameters: { - on: { type: optional(entityRef({ kind: 'model' })), documentation: '' }, - where: { type: optional(str()), documentation: 'The policy predicate.' }, - mode: { - type: optional( - oneOf( - identifier('permissive', { documentation: 'Combined with OR.' }), - identifier('restrictive', { documentation: 'Combined with AND.' }), - ), - ), - documentation: '', - }, - using: { type: optional(str()), documentation: '' }, - }, - }); - -const pslBlockDescriptors: AuthoringPslBlockDescriptorNamespace = { - policy: { - kind: 'pslBlock', - keyword: 'policy', - documentation: 'Defines a security policy.', - discriminator: 'fixture-policy', - name: { required: true }, - spec: policySpec, - attributes: { audit: () => auditAttribute }, - }, - inventory: { - kind: 'pslBlock', - keyword: 'inventory', - documentation: 'An arbitrary-key block.', - discriminator: 'fixture-inventory', - name: { required: true }, - spec: () => - mapBlock({ - value: { type: jsonValue(), documentation: 'The entry value.' }, - allowBare: true, - }), - }, - access: { - audit: { - kind: 'pslBlock', - keyword: 'audit', - discriminator: 'fixture-audit', - name: { required: true }, - spec: () => - structBlock({ - parameters: { - on: { type: optional(entityRef({ kind: 'model' })), documentation: '' }, - }, - }), - }, - }, -}; - -const candidateSource = [ - 'types {', - ' Email = String', - ' UserId = User', - '}', - 'model User {', - ' id Int', - ' topOnly String', - '}', - 'type Address {', - ' street String', - '}', - 'policy Audit {', - ' on = read', - '}', - 'namespace auth {', - ' model Account {', - ' id Int', - ' }', - ' model User {', - ' id Int', - ' scopedOnly String', - ' }', - ' type Profile {', - ' displayName String', - ' }', - ' policy ScopedAudit {', - ' on = read', - ' }', - '}', -].join('\n'); - -function complete( - markedFieldSource: string, - options: { - readonly clientSupportsSnippets?: boolean; - readonly clientSupportsTriggerParameterHintsCommand?: boolean; - } = {}, -) { - return completeWithSource({ - markedSource: `${candidateSource}\n${markedFieldSource}`, - pslBlockDescriptors, - authoringContributions: attributeContributions, - controlMutationDefaults, - clientSupportsSnippets: options.clientSupportsSnippets === true, - clientSupportsTriggerParameterHintsCommand: - options.clientSupportsTriggerParameterHintsCommand === true, - }); -} - -interface CompletionTestStack { - readonly attributeSpecs: AttributeSpecNamespace; - readonly entityTypes: AuthoringEntityTypeNamespace; - readonly pslBlockDescriptors: AuthoringPslBlockDescriptorNamespace; -} - -interface ActualSqlAttributeModule { - readonly sqlAttributeSpecs: AttributeSpecNamespace; -} - -interface ActualSqlBlockModule { - readonly sqlFamilyEntityTypes: AuthoringEntityTypeNamespace; - readonly sqlFamilyPslBlockDescriptors: AuthoringPslBlockDescriptorNamespace; -} - -interface ActualPostgresDefaultsModule { - createPostgresDefaultFunctionRegistry(): ControlMutationDefaultRegistry; -} - -interface ActualPostgresDataTypesModule { - postgresDataTypeEntries(): Readonly>; -} - -interface ActualSqliteDataTypesModule { - sqliteDataTypeEntries(): Readonly>; -} - -interface ActualSqlExpressionModule { - readonly sqlExpressionRegistration: { - readonly authoring: { - readonly dataTypes: Readonly>; - }; - }; -} - -interface ActualMongoAttributeModule { - readonly mongoAttributeSpecs: AttributeSpecNamespace; -} - -interface ActualMongoBlockModule { - readonly mongoFamilyEntityTypes: AuthoringEntityTypeNamespace; - readonly mongoFamilyPslBlockDescriptors: AuthoringPslBlockDescriptorNamespace; -} - -function completeWithSource(input: { - readonly markedSource: string; - readonly siblings?: readonly string[]; - readonly pslBlockDescriptors: AuthoringPslBlockDescriptorNamespace; - readonly authoringContributions?: typeof attributeContributions; - readonly controlMutationDefaults?: typeof controlMutationDefaults; - readonly dataTypes?: DataTypeSupport; - readonly clientSupportsSnippets?: boolean; - readonly clientSupportsTriggerSuggestCommand?: boolean; - readonly clientSupportsTriggerParameterHintsCommand?: boolean; - readonly scalarTypes?: readonly string[]; - readonly generatorFunctions?: GeneratorFunctionRegistry; -}) { - const cursorOffset = input.markedSource.indexOf('|'); - expect(cursorOffset).toBeGreaterThanOrEqual(0); - const source = `${input.markedSource.slice(0, cursorOffset)}${input.markedSource.slice(cursorOffset + 1)}`; - const parsed = parse(source, 'language-server-test.psl'); - const { document } = parsed; - const others = (input.siblings ?? []).map((text, index) => parse(text, `sibling-${index}.psl`)); - const sources = parsed.sources.merge(...others.map((other) => other.sources)); - const sourceFile = sources.sourceFileFor(document.syntax); - const { symbolTable } = buildSymbolTable({ - documents: [...others.map((other) => other.document), document], - sources, - defaultNamespaceId: 'public', - }); - const context = classifyPslCompletionContext({ - document, - sourceFile, - position: sourceFile.positionAt(cursorOffset), - }); - - return { - items: providePslCompletionItems({ - context, - sourceFile, - candidates: { - binder: testBinder({ - ...input, - sources, - symbolTable, - scalarTypes: input.scalarTypes ?? scalarTypes, - }), - scalarTypes: input.scalarTypes ?? scalarTypes, - pslBlockDescriptors: input.pslBlockDescriptors, - symbolTable, - ...(input.authoringContributions === undefined - ? {} - : { authoringContributions: input.authoringContributions }), - ...(input.controlMutationDefaults === undefined - ? {} - : { controlMutationDefaults: input.controlMutationDefaults }), - ...(input.dataTypes === undefined ? {} : { dataTypes: input.dataTypes }), - generatorFunctions: input.generatorFunctions ?? EMPTY_GENERATOR_FUNCTIONS, - }, - clientSupportsSnippets: input.clientSupportsSnippets === true, - clientSupportsTriggerSuggestCommand: input.clientSupportsTriggerSuggestCommand === true, - clientSupportsTriggerParameterHintsCommand: - input.clientSupportsTriggerParameterHintsCommand === true, - }), - sourceFile, - cursorOffset, - }; -} - -async function actualSqlStack(): Promise { - const [attributes, blocks] = await Promise.all([ - importFromPackageRoot( - '../../../2-sql-family/2-authoring/contract-psl/src/sql-attribute-specs.ts', - ), - importFromPackageRoot( - '../../../2-sql-family/9-family/src/core/authoring-entity-types.ts', - ), - ]); - return { - attributeSpecs: attributes.sqlAttributeSpecs, - entityTypes: blocks.sqlFamilyEntityTypes, - pslBlockDescriptors: blocks.sqlFamilyPslBlockDescriptors, - }; -} - -async function actualMongoStack(): Promise { - const [attributes, blocks] = await Promise.all([ - importFromPackageRoot( - '../../../2-mongo-family/2-authoring/contract-psl/src/mongo-attribute-specs.ts', - ), - importFromPackageRoot( - '../../../2-mongo-family/9-family/src/core/authoring-entity-types.ts', - ), - ]); - return { - attributeSpecs: attributes.mongoAttributeSpecs, - entityTypes: blocks.mongoFamilyEntityTypes, - pslBlockDescriptors: blocks.mongoFamilyPslBlockDescriptors, - }; -} - -const fixtureInt = dataType('fixture/int', { read: (json) => json }); -const fixtureExpression = dataType('fixture/expression', { read: (json) => json }); -const constructorArgumentDataTypes: DataTypeSupport = { - entries: { - [fixtureInt.id]: { - written: { - kind: 'plain', - syntax: 'number', - types: [fixtureInt.id], - classify: (text) => ({ type: fixtureInt.id, value: Number(text) }), - }, - print: String, - documentation: 'A whole number.', - }, - [fixtureExpression.id]: { - written: { kind: 'tag', tag: 'sql', parse: (text) => text }, - print: String, - documentation: 'A SQL expression.', - }, - }, - lookup: createDataTypeLookup([fixtureInt, fixtureExpression]), - dataTypeOfCodec: () => undefined, -}; - -async function sqlExpressionDataTypes(): Promise>> { - const family = await importFromPackageRoot( - '../../../2-sql-family/1-core/contract/src/exports/sql-expression.ts', - ); - return family.sqlExpressionRegistration.authoring.dataTypes; -} - -async function importFromPackageRoot(relativePath: string): Promise { - return (await import(pathToFileURL(resolve(packageRoot, relativePath)).href)) as T; -} - -function actualAuthoringContributions(stack: CompletionTestStack): typeof attributeContributions { - return assembleAuthoringContributions([ - { - id: 'actual-family', - authoring: { - attributeSpecs: stack.attributeSpecs, - entityTypes: stack.entityTypes, - pslBlockDescriptors: stack.pslBlockDescriptors, - }, - }, - ]); -} - -function completeWithActualStack( - markedSource: string, - stack: CompletionTestStack, - options: { - readonly clientSupportsSnippets?: boolean; - readonly controlMutationDefaults?: typeof controlMutationDefaults; - readonly dataTypes?: Readonly>; - } = {}, -) { - return completeWithSource({ - markedSource, - pslBlockDescriptors: stack.pslBlockDescriptors, - authoringContributions: actualAuthoringContributions(stack), - ...(options.dataTypes === undefined - ? {} - : { - dataTypes: { - entries: options.dataTypes, - lookup: createDataTypeLookup([]), - dataTypeOfCodec: () => undefined, - }, - }), - controlMutationDefaults: options.controlMutationDefaults ?? controlMutationDefaults, - clientSupportsSnippets: options.clientSupportsSnippets === true, - }); -} - -function applyCompletionItem(input: { - readonly sourceFile: SourceFile; - readonly item: CompletionItem; -}) { - const edit = input.item.textEdit; - if (edit === undefined || !('range' in edit)) { - throw new Error('Expected a range text edit'); - } - const start = input.sourceFile.offsetAt(edit.range.start); - const end = input.sourceFile.offsetAt(edit.range.end); - return `${input.sourceFile.text.slice(0, start)}${edit.newText}${input.sourceFile.text.slice(end)}`; -} - -function completionItemByLabel(items: readonly CompletionItem[], label: string): CompletionItem { - const item = items.find((candidate) => candidate.label === label); - if (item === undefined) { - throw new Error(`Expected completion item "${label}"`); - } - return item; -} + complete, + completeWithSource, + completionItemByLabel, + constructorArgumentDataTypes, + pslBlockDescriptors, +} from './helpers/completion-provider'; describe('providePslCompletionItems', () => { it('filters candidates after nearest-name shadowing and includes namespace locals', () => { @@ -758,1724 +296,9 @@ namespace app { expect(items).toEqual([]); }); - it('returns document-level declaration keyword candidates with stable plain-text edits', () => { - const { items, sourceFile, cursorOffset } = complete('|'); - - expect(items.map((item) => item.label)).toEqual([ - 'model', - 'model mixin', - 'type', - 'type mixin', - 'types', - 'namespace', - 'audit', - 'audit mixin', - 'inventory', - 'inventory mixin', - 'policy', - 'policy mixin', - ]); - expect(items.map((item) => item.detail)).toEqual([ - 'Defines a data model.', - 'Declares a mixin for model blocks.', - 'Defines a reusable composite type.', - 'Declares a mixin for type blocks.', - 'Defines reusable type aliases.', - 'Groups declarations belonging to the same database schema or database.', - 'Generic block keyword', - 'Declares a mixin for audit blocks.', - 'An arbitrary-key block.', - 'Declares a mixin for inventory blocks.', - 'Defines a security policy.', - 'Declares a mixin for policy blocks.', - ]); - expect(items[0]).toMatchObject({ - kind: CompletionItemKind.Keyword, - filterText: 'model', - textEdit: { - range: { - start: sourceFile.positionAt(cursorOffset), - end: sourceFile.positionAt(cursorOffset), - }, - newText: 'model ', - }, - }); - expect(items[0]?.insertTextFormat).toBeUndefined(); - }); - - it('returns the full document-level declaration keyword set with a replace range over the typed segment', () => { - const { items, sourceFile, cursorOffset } = complete('mo|'); - - expect(items.map((item) => item.label)).toEqual([ - 'model', - 'model mixin', - 'type', - 'type mixin', - 'types', - 'namespace', - 'audit', - 'audit mixin', - 'inventory', - 'inventory mixin', - 'policy', - 'policy mixin', - ]); - expect(items[0]).toMatchObject({ - filterText: 'model', - textEdit: { - range: { - start: sourceFile.positionAt(cursorOffset - 'mo'.length), - end: sourceFile.positionAt(cursorOffset), - }, - newText: 'model ', - }, - }); - }); - - it('returns namespace-body declaration keywords without document-only native keywords', () => { - const { items } = complete(['namespace feature {', ' |', '}'].join('\n')); - - expect(items.map((item) => item.label)).toEqual([ - 'model', - 'model mixin', - 'type', - 'type mixin', - 'audit', - 'audit mixin', - 'inventory', - 'inventory mixin', - 'policy', - 'policy mixin', - ]); - expect(items.map((item) => item.label)).not.toContain('types'); - expect(items.map((item) => item.label)).not.toContain('namespace'); - }); - - it('returns the full namespace-body declaration keyword set with a replace range over the typed segment', () => { - const { items, sourceFile, cursorOffset } = complete( - ['namespace feature {', ' po|', '}'].join('\n'), - ); - - expect(items.map((item) => item.label)).toEqual([ - 'model', - 'model mixin', - 'type', - 'type mixin', - 'audit', - 'audit mixin', - 'inventory', - 'inventory mixin', - 'policy', - 'policy mixin', - ]); - expect(items.find((item) => item.label === 'policy')).toMatchObject({ - filterText: 'policy', - textEdit: { - range: { - start: sourceFile.positionAt(cursorOffset - 'po'.length), - end: sourceFile.positionAt(cursorOffset), - }, - newText: 'policy ', - }, - }); - }); - - it('returns snippet declaration keyword edits only when the client supports snippets', () => { - const { items } = complete('|', { clientSupportsSnippets: true }); - - expect(items.find((item) => item.label === 'model')).toMatchObject({ - insertTextFormat: InsertTextFormat.Snippet, - textEdit: { newText: `model ${nameSnippetPlaceholder} {\n \${0:// Fields}\n}` }, - }); - expect(items.find((item) => item.label === 'policy')).toMatchObject({ - insertTextFormat: InsertTextFormat.Snippet, - textEdit: { - newText: `policy ${nameSnippetPlaceholder} {\n \${0:// Block keys and attributes}\n}`, - }, - }); - }); - - it.each([true, false])( - 'inserts a body placeholder without pre-filled keys, snippets=%s', - (snippets) => { - const { items } = completeWithSource({ - markedSource: '|', - clientSupportsSnippets: snippets, - pslBlockDescriptors: { - security: { - policy: { - kind: 'pslBlock', - keyword: 'policy', - discriminator: 'policy', - name: { required: true }, - spec: policySpec, - attributes: { audit: () => auditAttribute }, - }, - }, - }, - }); - const item = completionItemByLabel(items, 'policy'); - expect(item.textEdit?.newText).toBe( - snippets - ? [ - `policy ${nameSnippetPlaceholder} {`, - ' $' + '{0:// Block keys and attributes}', - '}', - ].join('\n') - : 'policy ', - ); - expect(item.insertTextFormat).toBe(snippets ? InsertTextFormat.Snippet : undefined); - }, - ); - - describe('mixin declaration items', () => { - const sortedLabels = (items: readonly CompletionItem[]) => - [...items] - .sort((left, right) => ((left.sortText ?? '') < (right.sortText ?? '') ? -1 : 1)) - .map((item) => item.label); - - it('offers model before model mixin when a prefix of the keyword is typed', () => { - const { items, sourceFile, cursorOffset } = complete('mo|'); - const range = { - start: sourceFile.positionAt(cursorOffset - 'mo'.length), - end: sourceFile.positionAt(cursorOffset), - }; - - expect(sortedLabels(items).slice(0, 2)).toEqual(['model', 'model mixin']); - expect(completionItemByLabel(items, 'model mixin')).toMatchObject({ - kind: CompletionItemKind.Keyword, - detail: 'Declares a mixin for model blocks.', - filterText: 'model mixin', - sortText: '0:model mixin', - textEdit: { range, newText: 'model mixin ' }, - }); - }); - - it('sorts each mixin item directly after the item of its keyword', () => { - expect(sortedLabels(complete('|').items)).toEqual([ - 'model', - 'model mixin', - 'namespace', - 'type', - 'type mixin', - 'types', - 'audit', - 'audit mixin', - 'inventory', - 'inventory mixin', - 'policy', - 'policy mixin', - ]); - }); - - it('offers no mixin item for namespace or types, and never the word mixin alone', () => { - const labels = complete('|').items.map((item) => item.label); - - expect(labels).not.toContain('namespace mixin'); - expect(labels).not.toContain('types mixin'); - expect(labels).not.toContain('mixin'); - }); - - it('inserts a mixin declaration with an empty body for a native and for a struct block keyword', () => { - const { items } = completeWithSource({ - markedSource: '|', - clientSupportsSnippets: true, - pslBlockDescriptors: { - security: { - policy: { - kind: 'pslBlock', - keyword: 'policy', - discriminator: 'policy', - name: { required: true }, - spec: policySpec, - attributes: { audit: () => auditAttribute }, - }, - }, - }, - }); - - expect(completionItemByLabel(items, 'model mixin')).toMatchObject({ - insertTextFormat: InsertTextFormat.Snippet, - textEdit: { newText: `model mixin ${nameSnippetPlaceholder} {\n \${0:// Fields}\n}` }, - }); - expect(completionItemByLabel(items, 'policy mixin')).toMatchObject({ - kind: CompletionItemKind.Keyword, - detail: 'Declares a mixin for policy blocks.', - filterText: 'policy mixin', - sortText: '1:policy mixin', - insertTextFormat: InsertTextFormat.Snippet, - textEdit: { - newText: `policy mixin ${nameSnippetPlaceholder} {\n \${0:// Block keys and attributes}\n}`, - }, - }); - }); - - it('offers the mixin item while the whole keyword is the word before the cursor, and nothing once more is typed', () => { - expect(complete('model|').items.map((item) => item.label)).toContain('model mixin'); - expect(complete('model m|').items).toEqual([]); - expect(complete('model mixin|').items).toEqual([]); - }); - - it.each([ - ['a model', ['model User {', ' |', '}']], - ['a block', ['policy P {', ' |', '}']], - ['a model mixin', ['model mixin M {', ' |', '}']], - ['a block mixin', ['policy mixin M {', ' |', '}']], - ])('offers none inside %s', (_where, lines) => { - const labels = complete(lines.join('\n')).items.map((item) => item.label); - - expect(labels.filter((label) => label.endsWith(' mixin'))).toEqual([]); - }); - }); - - it('returns registry-backed attribute name completions as function items', () => { - const fieldItems = complete(['model Post {', ' id Int @|', '}'].join('\n')).items; - expect(fieldItems.map((item) => item.label)).toEqual(['marker', 'orderFixture', 'ownerAware']); - expect(fieldItems.find((item) => item.label === 'marker')?.detail).toBe( - 'Attaches a named marker to a target.', - ); - expect(fieldItems.map((item) => item.kind)).toEqual([ - CompletionItemKind.Function, - CompletionItemKind.Function, - CompletionItemKind.Function, - ]); - - const modelItems = complete(['model Post {', ' id Int', ' @@|', '}'].join('\n')).items; - expect(modelItems.map((item) => item.label)).toEqual(['rls']); - expect(modelItems.map((item) => item.kind)).toEqual([CompletionItemKind.Function]); - - const blockItems = complete(['policy Rule {', ' @@|', '}'].join('\n')).items; - expect(blockItems.map((item) => item.label)).toEqual(['audit']); - expect(blockItems.map((item) => item.kind)).toEqual([CompletionItemKind.Function]); - }); - - it('resolves the attribute owner without enumerating declarations', () => { - const markedSource = ['model User {', ' id Int @|', '}'].join('\n'); - const cursorOffset = markedSource.indexOf('|'); - const source = `${markedSource.slice(0, cursorOffset)}${markedSource.slice(cursorOffset + 1)}`; - const { document, sources } = parse(source, 'language-server-test.psl'); - const sourceFile = sources.sourceFileFor(document.syntax); - const { symbolTable } = buildSymbolTable({ - documents: [document], - sources, - defaultNamespaceId: 'public', - }); - const context = classifyPslCompletionContext({ - document, - sourceFile, - position: sourceFile.positionAt(cursorOffset), - }); - let modelEnumerationCount = 0; - const observedDefaultNamespace = { - ...symbolTable.defaultNamespace, - models: new Proxy(symbolTable.defaultNamespace.models, { - ownKeys(target) { - modelEnumerationCount += 1; - return Reflect.ownKeys(target); - }, - }), - }; - const observedSymbolTable = { - ...symbolTable, - defaultNamespace: observedDefaultNamespace, - namespaces: { [observedDefaultNamespace.id]: observedDefaultNamespace }, - }; - const factoryOwnerNames: string[] = []; - const observedAuthoringContributions = assembleAuthoringContributions([ - { - id: 'observed-family', - authoring: { - attributeSpecs: { - field: { - first: (ctx: FieldAttributeSpecContext) => { - factoryOwnerNames.push(ctx.model.name); - return fieldAttribute('first', { - documentation: 'Marks the first contributed field attribute.', - }); - }, - second: (ctx: FieldAttributeSpecContext) => { - factoryOwnerNames.push(ctx.model.name); - return fieldAttribute('second', { - documentation: 'Marks the second contributed field attribute.', - }); - }, - }, - model: {}, - }, - }, - }, - ]); - - const items = providePslCompletionItems({ - context, - sourceFile, - candidates: { - scalarTypes, - pslBlockDescriptors, - symbolTable: observedSymbolTable, - binder: testBinder({ - sources, - symbolTable, - scalarTypes, - authoringContributions: attributeContributions, - controlMutationDefaults, - pslBlockDescriptors, - }), - authoringContributions: observedAuthoringContributions, - controlMutationDefaults, - generatorFunctions: EMPTY_GENERATOR_FUNCTIONS, - }, - clientSupportsSnippets: true, - }); - - expect(items.map((item) => item.label)).toEqual(['first', 'second']); - expect(factoryOwnerNames).toEqual(['User', 'User']); - expect(modelEnumerationCount).toBe(0); - }); - - it('returns attribute named keys including optional keys while omitting supplied keys', () => { - const { items, sourceFile, cursorOffset } = complete( - ['model Post {', ' id Int @marker(name: "id", pr|)', '}'].join('\n'), - ); - - expect(items.map((item) => item.label)).toEqual(['priority']); - expect(items[0]?.textEdit).toEqual({ - range: { - start: sourceFile.positionAt(cursorOffset - 'pr'.length), - end: sourceFile.positionAt(cursorOffset), - }, - newText: 'priority: ', - }); - }); - - it('preserves named-key declaration order while filtering supplied keys', () => { - const { items } = complete( - ['model Post {', ' id Int @orderFixture(alpha: 1, |)', '}'].join('\n'), - ); - - expect(items.map((item) => [item.label, item.kind])).toEqual([ - ['zebra', CompletionItemKind.Property], - ['middle', CompletionItemKind.Property], - ]); - }); - - it('uses the current declaration owner when model names collide across namespaces', () => { - const { items } = complete( - [ - 'namespace feature {', - ' model User {', - ' scopedOnly String @ownerAware(|)', - ' }', - '}', - ].join('\n'), - ); - - expect(items.map((item) => item.label)).toEqual(['scopedKey']); - }); - - it('inserts required contributed attribute arguments as snippets for snippet clients', () => { - const { items, sourceFile } = complete( - ['model Post {', ' id Int @mar| // keep', '}'].join('\n'), - { - clientSupportsSnippets: true, - }, - ); - const item = completionItemByLabel(items, 'marker'); - - expect(item).toMatchObject({ - insertTextFormat: InsertTextFormat.Snippet, - textEdit: { - newText: `marker("\${1:target}", name: "\${2:name}")`, - }, - }); - expect(applyCompletionItem({ sourceFile, item })).toEqual( - [ - candidateSource, - 'model Post {', - ` id Int @marker("\${1:target}", name: "\${2:name}") // keep`, - '}', - ].join('\n'), - ); - }); - - it.each([false, true])('gates argument snippet hints on client support: %s', (supported) => { - for (const [source, snippets, hints] of [ - ['model Post { id Int @mar| }', true, true], - ['model Post { id Int @mar| }', false, false], - ['model Post { id Int @mar|ker("x") }', true, false], - ['|', true, false], - ] as const) { - const { items } = complete(source, { - clientSupportsSnippets: snippets, - clientSupportsTriggerParameterHintsCommand: supported, - }); - const item = completionItemByLabel(items, source === '|' ? 'model' : 'marker'); - expect(item.command).toEqual( - supported && hints - ? { title: 'Show argument hints', command: 'editor.action.triggerParameterHints' } - : undefined, - ); - } - }); - - it('keeps plain contributed attribute completion free of snippet syntax', () => { - const { items, sourceFile } = complete( - ['model Post {', ' id Int @mar| // keep', '}'].join('\n'), - ); - const item = completionItemByLabel(items, 'marker'); - - expect(item.insertTextFormat).toBeUndefined(); - expect(item.textEdit).toMatchObject({ newText: 'marker' }); - expect(applyCompletionItem({ sourceFile, item })).toEqual( - [candidateSource, 'model Post {', ' id Int @marker // keep', '}'].join('\n'), - ); - }); - - it('preserves existing attribute delimiters and suffixes instead of inserting required arguments again', () => { - const { items, sourceFile } = complete( - ['model Post {', ' id Int @mar|ker(name: "id") @unique', '}'].join('\n'), - { clientSupportsSnippets: true }, - ); - const item = completionItemByLabel(items, 'marker'); - - expect(item.insertTextFormat).toBeUndefined(); - expect(item.textEdit).toMatchObject({ newText: 'marker' }); - expect(applyCompletionItem({ sourceFile, item })).toEqual( - [candidateSource, 'model Post {', ' id Int @marker(name: "id") @unique', '}'].join('\n'), - ); - }); - - it('uses the attribute AST to preserve an incomplete existing argument list', () => { - const { items, sourceFile } = complete(['model Post {', ' id Int @mar|ker(', '}'].join('\n'), { - clientSupportsSnippets: true, - }); - const item = completionItemByLabel(items, 'marker'); - - expect(item.insertTextFormat).toBeUndefined(); - expect(item.textEdit).toMatchObject({ newText: 'marker' }); - expect(applyCompletionItem({ sourceFile, item })).toEqual( - [candidateSource, 'model Post {', ' id Int @marker(', '}'].join('\n'), - ); - }); - - it('uses actual SQL attribute specs for names and top-level named keys', async () => { - const stack = await actualSqlStack(); - - expect( - completeWithActualStack(['model Post {', ' id Int @|', '}'].join('\n'), stack).items.map( - (item) => item.label, - ), - ).toEqual(['default', 'id', 'map', 'onCreate', 'onUpdate', 'relation', 'unique']); - expect( - completeWithActualStack( - ['model Post {', ' id Int', ' @@|', '}'].join('\n'), - stack, - ).items.map((item) => item.label), - ).toEqual(['base', 'check', 'control', 'discriminator', 'id', 'index', 'map', 'unique']); - expect( - completeWithActualStack(['enum Role {', ' Admin', ' @@|', '}'].join('\n'), stack).items.map( - (item) => item.label, - ), - ).toEqual(['type']); - - const { items, sourceFile, cursorOffset } = completeWithActualStack( - ['model Post {', ' id Int', ' @@index(expression: sql`lower(name)`, ma|)', '}'].join('\n'), - stack, - ); - expect(items.map((item) => item.label)).toEqual([ - 'where', - 'unique', - 'name', - 'map', - 'type', - 'options', - ]); - expect(items.find((item) => item.label === 'map')?.textEdit).toEqual({ - range: { - start: sourceFile.positionAt(cursorOffset - 'ma'.length), - end: sourceFile.positionAt(cursorOffset), - }, - newText: 'map: ', - }); - - expect( - completeWithActualStack( - ['model Post {', ' id Int @default(|)', '}'].join('\n'), - stack, - ).items.map((item) => item.label), - ).toEqual(['true', 'false', 'null']); - - const mapCompletion = completeWithActualStack( - ['model Post {', ' id Int @ma| // keep', '}'].join('\n'), - stack, - { clientSupportsSnippets: true }, - ); - const mapItem = completionItemByLabel(mapCompletion.items, 'map'); - expect(mapItem).toMatchObject({ - insertTextFormat: InsertTextFormat.Snippet, - textEdit: { newText: `map("\${1:name}")` }, - }); - expect(applyCompletionItem({ sourceFile: mapCompletion.sourceFile, item: mapItem })).toEqual( - ['model Post {', ` id Int @map("\${1:name}") // keep`, '}'].join('\n'), - ); - - const checkCompletion = completeWithActualStack( - ['model Post {', ' id Int', ' @@che| // keep', '}'].join('\n'), - stack, - { clientSupportsSnippets: true, dataTypes: await sqlExpressionDataTypes() }, - ); - const checkItem = completionItemByLabel(checkCompletion.items, 'check'); - expect(checkItem).toMatchObject({ - insertTextFormat: InsertTextFormat.Snippet, - textEdit: { newText: `check(expression: ${sqlExpressionSnippet})` }, - }); - expect( - applyCompletionItem({ sourceFile: checkCompletion.sourceFile, item: checkItem }), - ).toEqual( - [ - 'model Post {', - ' id Int', - ` @@check(expression: ${sqlExpressionSnippet}) // keep`, - '}', - ].join('\n'), - ); - }, 5_000); - - it('offers a sql literal where @@index and @@check take SQL', async () => { - const stack = await actualSqlStack(); - const dataTypes = await sqlExpressionDataTypes(); - const [entry] = Object.values(dataTypes); - const valuesAt = (attribute: string) => - completeWithActualStack( - ['model Post {', ' id Int', ` ${attribute}`, '}'].join('\n'), - stack, - { - clientSupportsSnippets: true, - dataTypes, - }, - ).items.map((item) => ({ - label: item.label, - detail: item.detail, - newText: item.textEdit?.newText, - insertTextFormat: item.insertTextFormat, - })); - const sqlItem = { - label: 'sql', - detail: entry?.documentation, - newText: 'sql`$1`', - insertTextFormat: InsertTextFormat.Snippet, - }; - - expect(valuesAt('@@index([id], where: |)')).toEqual([sqlItem]); - expect(valuesAt('@@index(expression: |, map: "post_idx")')).toEqual([sqlItem]); - expect(valuesAt('@@check(expression: |, name: "post_check")')).toEqual([sqlItem]); - }, 5_000); - - it('completes model and field attributes when the source has no data types', async () => { - const stack = await actualSqlStack(); - const labelsAt = (markedSource: string) => - completeWithActualStack(markedSource, stack).items.map((item) => item.label); - - expect(labelsAt(['model Post {', ' id Int', ' @@|', '}'].join('\n'))).toEqual([ - 'base', - 'check', - 'control', - 'discriminator', - 'id', - 'index', - 'map', - 'unique', - ]); - expect(labelsAt(['model Post {', ' id Int @|', '}'].join('\n'))).toEqual([ - 'default', - 'id', - 'map', - 'onCreate', - 'onUpdate', - 'relation', - 'unique', - ]); - expect( - labelsAt(['model Post {', ' id Int', ' @@check(expression: |)', '}'].join('\n')), - ).toEqual([]); - }, 5_000); - - it('uses actual Mongo attribute specs for names and dynamic factory keys', async () => { - const stack = await actualMongoStack(); - - expect( - completeWithActualStack(['model Post {', ' id String @|', '}'].join('\n'), stack).items.map( - (item) => item.label, - ), - ).toEqual(['id', 'map', 'onCreate', 'onUpdate', 'relation', 'unique']); - expect( - completeWithActualStack( - ['model Post {', ' id String', ' @@|', '}'].join('\n'), - stack, - ).items.map((item) => item.label), - ).toEqual(['base', 'discriminator', 'index', 'map', 'textIndex', 'unique']); - expect( - completeWithActualStack(['enum Role {', ' Admin', ' @@|', '}'].join('\n'), stack).items.map( - (item) => item.label, - ), - ).toEqual(['type']); - - expect( - completeWithActualStack( - [ - 'namespace scoped {', - ' model User {', - ' id String', - ' localOnly String', - ' @@index(fields: [id], la|)', - ' }', - '}', - 'model User {', - ' id String', - '}', - ].join('\n'), - stack, - ).items.map((item) => item.label), - ).toEqual([ - 'type', - 'sparse', - 'expireAfterSeconds', - 'filter', - 'include', - 'exclude', - 'default_language', - 'languageOverride', - 'collationLocale', - 'collationStrength', - 'collationCaseLevel', - 'collationCaseFirst', - 'collationNumericOrdering', - 'collationAlternate', - 'collationMaxVariable', - 'collationBackwards', - 'collationNormalization', - ]); - expect( - completeWithActualStack( - ['model Post {', ' author User @relation(name: "Author", f|)', '}'].join('\n'), - stack, - ).items.map((item) => item.label), - ).toEqual(['fields', 'references']); - - const mapCompletion = completeWithActualStack( - ['model Post {', ' id String @ma| // keep', '}'].join('\n'), - stack, - { clientSupportsSnippets: true }, - ); - const mapItem = completionItemByLabel(mapCompletion.items, 'map'); - expect(mapItem).toMatchObject({ - insertTextFormat: InsertTextFormat.Snippet, - textEdit: { newText: `map("\${1:name}")` }, - }); - expect(applyCompletionItem({ sourceFile: mapCompletion.sourceFile, item: mapItem })).toEqual( - ['model Post {', ` id String @map("\${1:name}") // keep`, '}'].join('\n'), - ); - }, 5_000); - - it('returns bare model field type completion candidates without custom ranking', () => { - const { items, sourceFile, cursorOffset } = complete( - ['model Post {', ' author |', '}'].join('\n'), - ); - - expect(items.map(({ label, detail }) => [label, detail]).sort()).toEqual([ - ['Address', 'Composite type'], - ['Boolean', 'Configured scalar type'], - ['DateTime', 'Configured scalar type'], - ['Email', 'Scalar type'], - ['Int', 'Configured scalar type'], - ['Post', 'Model'], - ['String', 'Configured scalar type'], - ['User', 'Model'], - ['UserId', 'Type alias'], - ['auth', 'Namespace'], - ['public', 'Namespace'], - ]); - for (const item of items) expect(item).not.toHaveProperty('sortText'); - expect(completionItemByLabel(items, 'Boolean').textEdit).toEqual({ - range: { - start: sourceFile.positionAt(cursorOffset), - end: sourceFile.positionAt(cursorOffset), - }, - newText: 'Boolean', - }); - }); - - it('returns the full bare candidate set with a replace range over the typed segment', () => { - const { items, sourceFile, cursorOffset } = complete( - ['model Post {', ' reviewer U|', '}'].join('\n'), - ); - - expect(items.map((item) => item.label).sort()).toEqual([ - 'Address', - 'Boolean', - 'DateTime', - 'Email', - 'Int', - 'Post', - 'String', - 'User', - 'UserId', - 'auth', - 'public', - ]); - expect(items.find((item) => item.label === 'User')).toMatchObject({ - filterText: 'User', - textEdit: { - range: { - start: sourceFile.positionAt(cursorOffset - 'U'.length), - end: sourceFile.positionAt(cursorOffset), - }, - newText: 'User', - }, - }); - }); - - it('returns the full bare candidate set including the namespace qualifier with a replace range over the typed segment', () => { - const { items, sourceFile, cursorOffset } = complete( - ['model Post {', ' reviewer a|', '}'].join('\n'), - ); - - expect(items.map((item) => item.label).sort()).toEqual([ - 'Address', - 'Boolean', - 'DateTime', - 'Email', - 'Int', - 'Post', - 'String', - 'User', - 'UserId', - 'auth', - 'public', - ]); - expect(items.find((item) => item.label === 'auth')).toMatchObject({ - kind: CompletionItemKind.Module, - detail: 'Namespace', - filterText: 'auth', - textEdit: { - range: { - start: sourceFile.positionAt(cursorOffset - 'a'.length), - end: sourceFile.positionAt(cursorOffset), - }, - newText: 'auth.', - }, - }); - }); - - it('returns namespace members after a namespace qualifier', () => { - const { items, sourceFile, cursorOffset } = complete( - ['model Post {', ' owner auth.|', '}'].join('\n'), - ); - - expect(items.map((item) => item.label).sort()).toEqual(['Account', 'Profile', 'User']); - expect(completionItemByLabel(items, 'Account').textEdit).toEqual({ - range: { - start: sourceFile.positionAt(cursorOffset), - end: sourceFile.positionAt(cursorOffset), - }, - newText: 'Account', - }); - }); - - it('returns the full namespace member set with replacement metadata for the typed segment', () => { - const { items, sourceFile, cursorOffset } = complete( - ['model Post {', ' owner auth.U|', '}'].join('\n'), - ); - - expect(items.map((item) => item.label).sort()).toEqual(['Account', 'Profile', 'User']); - for (const item of items) expect(item).not.toHaveProperty('sortText'); - expect(items.find((item) => item.label === 'User')).toMatchObject({ - filterText: 'User', - detail: 'Model', - textEdit: { - range: { - start: sourceFile.positionAt(cursorOffset - 'U'.length), - end: sourceFile.positionAt(cursorOffset), - }, - newText: 'User', - }, - }); - }); - - it('does not leak local namespace members into a foreign contract-space reference', () => { - const { items } = complete(['model Post {', ' owner supabase:auth.P|', '}'].join('\n')); - - expect(items).toEqual([]); - }); - - it('returns no completions for a contract-space-qualified position', () => { - const { items } = complete(['model Post {', ' external supabase:|', '}'].join('\n')); - - expect(items).toEqual([]); - }); - - it('completes a generic block value from the block spec', () => { - const { items } = complete(['policy Rule {', ' on = |', '}'].join('\n')); - - expect(items.map((item) => item.label)).toEqual(['User', 'public', 'auth']); - }); - - it('returns descriptor-backed generic block parameter completions', () => { - const { items, sourceFile, cursorOffset } = complete(['policy Rule {', ' |', '}'].join('\n')); - - expect(items.map((item) => item.label)).toEqual(['on', 'where', 'mode', 'using']); - expect(items.map((item) => item.detail)).toEqual([ - 'Generic block parameter', - 'The policy predicate.', - 'Generic block parameter', - 'Generic block parameter', - ]); - expect(items[0]?.textEdit).toEqual({ - range: { - start: sourceFile.positionAt(cursorOffset), - end: sourceFile.positionAt(cursorOffset), - }, - newText: 'on = ', - }); - }); - - it('returns the full descriptor-backed generic block parameter set excluding already-present sibling keys', () => { - const { items, sourceFile, cursorOffset } = complete( - ['policy Rule {', ' on = User', ' wh|', '}'].join('\n'), - ); - - expect(items.map((item) => item.label)).toEqual(['where', 'mode', 'using']); - expect(items.find((item) => item.label === 'where')).toMatchObject({ - filterText: 'where', - textEdit: { - range: { - start: sourceFile.positionAt(cursorOffset - 'wh'.length), - end: sourceFile.positionAt(cursorOffset), - }, - newText: 'where = ', - }, - }); - }); - - it('uses generic block parameter documentation with a fallback for undocumented parameters', () => { - const { items } = complete('policy Rule { | }'); - expect(completionItemByLabel(items, 'where').detail).toBe('The policy predicate.'); - expect(completionItemByLabel(items, 'on').detail).toBe('Generic block parameter'); - }); - - it('still offers the in-progress key while excluding an already-present sibling key', () => { - const { items } = complete(['policy Rule {', ' where = "x"', ' on|', '}'].join('\n')); - - expect(items.map((item) => item.label)).toEqual(['on', 'mode', 'using']); - }); - - it('returns no generic block parameter completions without a matching descriptor', () => { - const { items } = complete(['extension Rule {', ' |', '}'].join('\n')); - - expect(items).toEqual([]); - }); - - it('offers no key candidates for an arbitrary-key block spec', () => { - const { items } = complete(['inventory Stock {', ' |', '}'].join('\n')); - - expect(items).toEqual([]); - }); - - it('keeps key completion available inside an invalid block', () => { - const { items } = complete( - ['policy Rule {', ' bogus = "not a declared key"', ' |', '}'].join('\n'), - ); - - expect(items.map((item) => item.label)).toEqual(['on', 'where', 'mode', 'using']); - }); - - it('calls the spec factory with the spec context and never invokes rule parsing', () => { - const dataTypes: DataTypeSupport = { - entries: {}, - lookup: createDataTypeLookup([]), - dataTypeOfCodec: () => undefined, - }; - const factoryContexts: BlockSpecContext[] = []; - const throwingRule = { - kind: 'str' as const, - label: 'string', - value: undefined, - parse: () => { - throw new Error('metadata inspection must not invoke rule parsing'); - }, - }; - const { items } = completeWithSource({ - markedSource: ['guard Rule {', ' |', '}'].join('\n'), - pslBlockDescriptors: { - guard: { - kind: 'pslBlock', - keyword: 'guard', - discriminator: 'fixture-guard', - name: { required: true }, - spec: (ctx: BlockSpecContext) => { - factoryContexts.push(ctx); - return structBlock({ - parameters: { - shield: { type: throwingRule, documentation: 'The shield key.' }, - }, - }); - }, - }, - }, - dataTypes, - }); - - expect(items.map((item) => item.label)).toEqual(['shield']); - expect(items[0]?.detail).toBe('The shield key.'); - expect(factoryContexts).toHaveLength(2); - for (const ctx of factoryContexts) { - expect(ctx).toEqual({ symbols: expect.any(Object), dataTypes: expect.any(Object) }); - expect(ctx.dataTypes).toBe(dataTypes); - } - }); - - it.each(['|', '[|]', '{ nested: | }', '[{ nested: [|] }]'])( - 'completes recursive JSON values at %s', - (value) => { - const { items } = completeWithSource({ - markedSource: `model Post { value String @jsonFixture(${value}) }`, - pslBlockDescriptors: {}, - authoringContributions: assembleAuthoringContributions([ - { - id: 'json-fixture', - authoring: { - attributeSpecs: { - field: { - jsonFixture: () => - fieldAttribute('jsonFixture', { - documentation: '', - positional: [{ key: 'value', type: jsonValue(), documentation: '' }], - }), - }, - model: {}, - }, - }, - }, - ]), - controlMutationDefaults, - }); - expect(items.map((item) => item.label)).toEqual(['true', 'false', 'null']); - }, - ); - - it('returns an empty list for unsupported classifier contexts', () => { - const { items } = complete(['model Post {', ' // @|', '}'].join('\n')); + it('returns an empty list for unsupported classifier contexts', () => { + const { items } = complete(['model Post {', ' // @|', '}'].join('\n')); expect(items).toEqual([]); }); - - it('uses registered scalar documentation for completion details', () => { - const { items } = completeWithSource({ - markedSource: 'model Post { value | }', - pslBlockDescriptors: {}, - authoringContributions: assembleAuthoringContributions([ - { - id: 'documented-scalars', - authoring: { - type: { - String: { - kind: 'typeConstructor', - documentation: 'Variable-length Unicode text.', - output: { codecId: 'fixture/text@1' }, - }, - }, - }, - }, - ]), - controlMutationDefaults, - }); - expect(completionItemByLabel(items, 'String').detail).toBe('Variable-length Unicode text.'); - expect(completionItemByLabel(items, 'Int').detail).toBe('Configured scalar type'); - }); - - it('completes a field preset as a call', () => { - const { items } = completeWithSource({ - markedSource: 'model Post { value | }', - pslBlockDescriptors: {}, - clientSupportsSnippets: false, - authoringContributions: assembleAuthoringContributions([ - { - id: 'presets', - authoring: { - field: { - stamp: { - kind: 'fieldPreset', - output: { codecId: 'fixture/timestamp@1' }, - }, - }, - }, - }, - ]), - controlMutationDefaults, - }); - const item = completionItemByLabel(items, 'stamp'); - expect({ - kind: item.kind, - detail: item.detail, - newText: item.textEdit === undefined ? undefined : item.textEdit.newText, - }).toEqual({ kind: CompletionItemKind.Function, detail: 'Field preset', newText: 'stamp()' }); - }); - - it( - 'lists deprecated Mongo scalar names tagged deprecated, naming the replacement', - async () => { - const { mongoScalarAuthoringTypes } = await importFromPackageRoot<{ - readonly mongoScalarAuthoringTypes: AuthoringTypeNamespace; - }>('../../../3-targets/mongo/2-adapter/src/exports/control.ts'); - const { items } = completeWithSource({ - markedSource: 'model Post { value | }', - pslBlockDescriptors: {}, - scalarTypes: Object.keys(mongoScalarAuthoringTypes), - authoringContributions: assembleAuthoringContributions([ - { id: 'mongo-scalars', authoring: { type: mongoScalarAuthoringTypes } }, - ]), - controlMutationDefaults, - }); - const scalars = items.filter((item) => item.kind === CompletionItemKind.Class); - const current = ['Int32', 'Double', 'Bool', 'Date']; - const deprecated = [ - ['Int', 'Int32'], - ['Float', 'Double'], - ['Boolean', 'Bool'], - ['DateTime', 'Date'], - ] as const; - - expect( - scalars - .filter((item) => item.tags?.includes(CompletionItemTag.Deprecated)) - .map((item) => item.label) - .sort(), - ).toEqual(deprecated.map(([name]) => name).sort()); - for (const name of current) { - expect(completionItemByLabel(items, name)).not.toHaveProperty('tags'); - } - for (const [name, replacement] of deprecated) { - expect(completionItemByLabel(items, name)).toMatchObject({ - tags: [CompletionItemTag.Deprecated], - detail: expect.stringContaining(`Deprecated: use ${replacement}`), - }); - } - }, - timeouts.coldTransformImport, - ); - - it('uses actual SQL enum metadata and rejects an empty enum without invented values', async () => { - const stack = await actualSqlStack(); - const names = (schema: string) => - completeWithActualStack(schema, stack).items.map((item) => item.label); - expect(names('enum Mood { Happy Sad }\nmodel Post { mood Mood @default(|) }')).toEqual([ - 'Happy', - 'Sad', - ]); - expect(names('enum Mood {}\nmodel Post { mood Mood @default(|) }')).toEqual([]); - expect( - names( - 'enum Mood { Top }\nnamespace scoped { enum Mood { Scoped }\nmodel Post { mood scoped.Mood @default(|) } }', - ), - ).toEqual(['Scoped']); - const degraded = names('enum Mood { Happy Happy }\nmodel Post { mood Mood @default(|) }'); - expect(degraded).toEqual(['Happy']); - }, 5_000); - - it('uses actual adapter default-function signatures through the SQL factory', async () => { - const stack = await actualSqlStack(); - const defaults = await importFromPackageRoot( - '../../../3-targets/postgres/2-adapter/src/core/migrations/control-mutation-defaults.ts', - ); - const options = { - controlMutationDefaults: { - ...controlMutationDefaults, - defaultFunctionRegistry: defaults.createPostgresDefaultFunctionRegistry(), - }, - }; - const source = (args: string) => `model Post { value String @default(${args}) }`; - const candidates = completeWithActualStack(source('|'), stack, options).items; - expect(candidates.map((item) => item.label)).toEqual([ - 'true', - 'false', - 'null', - 'autoincrement', - 'now', - ]); - const snippetItems = completeWithActualStack(source('|'), stack, { - ...options, - clientSupportsSnippets: true, - }).items; - expect(completionItemByLabel(snippetItems, 'now').textEdit?.newText).toBe('now()'); - }, 5_000); - - it('labels each generator function argument by the values the function takes', async () => { - const postgres = await importFromPackageRoot<{ - readonly postgresGeneratorFunctions: readonly { - readonly name: string; - readonly signature?: unknown; - }[]; - }>('../../../3-targets/postgres/1-target/src/core/generator-functions.ts'); - const labelOf = (name: string) => { - const declared = postgres.postgresGeneratorFunctions.find( - (contribution) => contribution.name === name && contribution.signature !== undefined, - )!.signature as (dataTypes: unknown) => FuncCallSig; - const signature = declared(EMPTY_DATA_TYPES); - return renderSignatureLabel(name, signature, resolveSignatureParameters(signature, undefined)) - .label; - }; - expect(['uuid', 'cuid', 'nanoid'].map(labelOf)).toEqual([ - 'uuid((4 | 7)?)', - 'cuid(2)', - 'nanoid(integer from 2 to 255?)', - ]); - }); - - it('offers each registered tag inside @default( with its own documentation', async () => { - const stack = await actualSqlStack(); - const [postgres, sqlite, family] = await Promise.all([ - importFromPackageRoot( - '../../../3-targets/postgres/1-target/src/exports/data-types.ts', - ), - importFromPackageRoot( - '../../../3-targets/sqlite/1-target/src/exports/data-types.ts', - ), - importFromPackageRoot( - '../../../2-sql-family/1-core/contract/src/exports/sql-expression.ts', - ), - ]); - const withFamilyEntry = ( - targetEntries: Readonly>, - ): Readonly> => ({ - ...family.sqlExpressionRegistration.authoring.dataTypes, - ...targetEntries, - }); - const complete = ( - dataTypes: Readonly>, - clientSupportsSnippets: boolean, - ) => - completeWithActualStack('model Post { value String @default(|) }', stack, { - clientSupportsSnippets, - controlMutationDefaults, - dataTypes, - }).items.map((item) => ({ - label: item.label, - detail: item.detail, - newText: item.textEdit?.newText, - insertTextFormat: item.insertTextFormat, - })); - const postgresEntries = withFamilyEntry(postgres.postgresDataTypeEntries()); - // This stack has no type constructors, so no column has a known type and every tag is offered. - const postgresTags = [ - 'sql', - 'json', - 'date', - 'time', - 'timetz', - 'timestamp', - 'timestamptz', - 'interval', - 'bytea', - ]; - const documentationOf = ( - entries: Readonly>, - tag: string, - ) => - Object.values(entries).find( - (entry) => entry.written.kind === 'tag' && entry.written.tag === tag, - )?.documentation; - const value = (label: string) => ({ - label, - detail: 'PSL argument value', - newText: label, - insertTextFormat: undefined, - }); - const tag = ( - entries: Readonly>, - label: string, - snippet: boolean, - ) => ({ - label, - detail: documentationOf(entries, label), - newText: snippet ? `${label}\`$1\`` : label, - insertTextFormat: snippet ? InsertTextFormat.Snippet : undefined, - }); - - expect(complete(postgresEntries, true)).toEqual([ - value('true'), - value('false'), - value('null'), - ...postgresTags.map((name) => tag(postgresEntries, name, true)), - ]); - const sqliteEntries = withFamilyEntry(sqlite.sqliteDataTypeEntries()); - expect(complete(sqliteEntries, true)).toEqual([ - value('true'), - value('false'), - value('null'), - tag(sqliteEntries, 'sql', true), - tag(sqliteEntries, 'json', true), - tag(sqliteEntries, 'blob', true), - tag(sqliteEntries, 'datetime', true), - ]); - expect(complete(postgresEntries, false)).toEqual([ - value('true'), - value('false'), - value('null'), - ...postgresTags.map((name) => tag(postgresEntries, name, false)), - ]); - - // Each tag carries the text of the tag it names, not every registered tag's text. - expect(documentationOf(postgresEntries, 'json')).not.toBe( - documentationOf(postgresEntries, 'sql'), - ); - }, 5_000); - - it('uses distinct local and referenced fields through actual SQL relation specs', async () => { - const stack = await actualSqlStack(); - const schema = (args: string) => - `model Target { topOnly Int }\nnamespace remote { model Target { remoteOnly Int } }\nmodel Owner { ownOnly Int\n relation remote.Target @relation(${args}) }`; - expect( - completeWithActualStack(schema('fields: [|]'), stack).items.map((item) => item.label), - ).toEqual(['ownOnly']); - expect( - completeWithActualStack(schema('references: [|]'), stack).items.map((item) => item.label), - ).toEqual(['remoteOnly']); - expect( - completeWithActualStack(schema('onDelete: |'), stack).items.map((item) => item.label), - ).toEqual(['NoAction', 'Restrict', 'Cascade', 'SetNull', 'SetDefault']); - }, 5_000); - - it('uses actual Mongo sort functions and completes their positional arguments', async () => { - const stack = await actualMongoStack(); - const schema = (args: string) => `model Post { title String\n slug String\n @@index(${args}) }`; - const plain = completeWithActualStack(schema('[|]'), stack).items; - expect(plain.map((item) => item.label)).toEqual(['title', 'slug', 'wildcard', 'sort']); - const snippets = completeWithActualStack(schema('[|]'), stack, { - clientSupportsSnippets: true, - }).items; - expect(snippets.map((item) => [item.label, item.textEdit?.newText])).toEqual([ - ['title', 'title'], - ['slug', 'slug'], - ['wildcard', `wildcard(${emptySnippetPlaceholder1})`], - ['sort', 'sort($' + '{1:field}, $' + '{2:direction})'], - ]); - expect( - completeWithActualStack(schema('[sort(|)]'), stack).items.map((item) => item.label), - ).toEqual(['title', 'slug']); - expect( - completeWithActualStack(schema('[sort(title, |)]'), stack).items.map((item) => item.label), - ).toEqual(['Asc', 'Desc']); - expect(completeWithActualStack(schema('[wildcard(|)]'), stack).items).toEqual([]); - expect( - completeWithActualStack(schema('[title], type: |'), stack).items.map((item) => item.label), - ).toEqual(['1', '-1', '"text"', '"2dsphere"', '"2d"', '"hashed"']); - }, 5_000); - - it('scopes actual Mongo sort fields to the declaring namespace model', async () => { - const stack = await actualMongoStack(); - const schema = - 'model Post { topOnly String }\nnamespace scoped { model Post { scopedOnly String\n @@index([|]) } }'; - expect( - completeWithActualStack(schema, stack, { clientSupportsSnippets: true }).items.map((item) => [ - item.label, - item.textEdit?.newText, - ]), - ).toEqual([ - ['scopedOnly', 'scopedOnly'], - ['wildcard', `wildcard(${emptySnippetPlaceholder1})`], - ['sort', 'sort($' + '{1:field}, $' + '{2:direction})'], - ]); - expect( - completeWithActualStack(schema.replace('[|]', '[sort(|)]'), stack).items.map( - (item) => item.label, - ), - ).toEqual(['scopedOnly']); - }, 5_000); - - it('does not return generic block symbols as model field type candidates', () => { - const { items } = complete(['model Post {', ' audit |', '}'].join('\n')); - - expect(items.map((item) => item.label)).not.toContain('Audit'); - expect(items.map((item) => item.label)).not.toContain('auth.ScopedAudit'); - }); - - it('offers the same field type candidates in a document that holds a mixin and an inclusion', () => { - const model = ['model Post {', ' author |', '}'].join('\n'); - const mixins = [ - 'model mixin Timestamps {', - ' createdAt DateTime', - '}', - 'model Tagged {', - ' id Int', - ' +Timestamps', - '}', - ].join('\n'); - const plain = ['', '', '', 'model Tagged {', ' id Int', '', '}'].join('\n'); - - expect(complete(`${mixins}\n${model}`).items).toEqual(complete(`${plain}\n${model}`).items); - }); - - it.each([ - ['in a mixin field type slot', 'model mixin Timestamps {\n createdAt |\n}'], - ['in a mixin header', 'model mixin |'], - ['after the plus of an inclusion', 'model Post {\n id Int\n +|\n}'], - ['inside the name of an inclusion', 'model Post {\n +auth.Ti|\n}'], - ['in an enum mixin body', 'enum mixin BaseRoles {\n |\n}'], - ])('completes without an error %s', (_case, markedSource) => { - expect(() => complete(markedSource)).not.toThrow(); - }); - - it('does not offer a mixin as a field type, by its name or as a namespace member', () => { - const mixins = [ - 'model mixin Timestamps {', - ' createdAt DateTime', - '}', - 'namespace shared {', - ' model mixin Audited {', - ' actor String', - ' }', - ' model Marker {', - ' id Int', - ' }', - '}', - ].join('\n'); - const unqualified = complete(`${mixins}\nmodel Post {\n stamps |\n}`).items; - const qualified = complete(`${mixins}\nmodel Post {\n stamps shared.|\n}`).items; - - expect(unqualified.map((item) => item.label)).toContain('shared'); - expect(unqualified.map((item) => item.label)).not.toContain('Timestamps'); - expect(qualified.map((item) => item.label)).toEqual(['Marker']); - }); -}); - -describe('providePslCompletionItems — after the plus of an inclusion', () => { - const mixinsOf = (prefix: string, indent: string) => - [ - `model mixin ${prefix}Stamps {`, - '}', - `type mixin ${prefix}Geo {`, - '}', - `enum mixin ${prefix}Roles {`, - '}', - `policy mixin ${prefix}Policy {`, - '}', - ].map((line) => `${indent}${line}`); - - function after(block: readonly string[], placement: 'billing' | 'top' = 'billing') { - const namespace = placement === 'top' ? undefined : placement; - const blockLines = - namespace === undefined ? block : block.map((line) => (line === '' ? line : ` ${line}`)); - const markedSource = [ - ...mixinsOf('Top', ''), - ...(namespace === undefined ? blockLines : []), - 'namespace auth {', - ...mixinsOf('Auth', ' '), - '}', - 'namespace billing {', - ...mixinsOf('Billing', ' '), - ' model mixin Used {', - ' }', - ...(namespace === undefined ? [] : blockLines), - '}', - ].join('\n'); - return completeWithSource({ markedSource, pslBlockDescriptors }); - } - - const offered = (block: readonly string[], placement: 'billing' | 'top' = 'billing') => - after(block, placement) - .items.map(({ label, detail }) => [label, detail]) - .sort(); - - const namespaces = [ - ['auth', 'Namespace'], - ['billing', 'Namespace'], - ['public', 'Namespace'], - ]; - - it.each([ - ['a model', ['model Invoice {', ' id Int', ' +Used', ' +|', '}'], 'Stamps', 'model mixin'], - ['a composite type', ['type Address {', ' +|', ' street String', '}'], 'Geo', 'type mixin'], - ['an enum', ['enum Role {', ' +|', ' GUEST', '}'], 'Roles', 'enum mixin'], - ['a struct block', ['policy P {', ' +|', '}'], 'Policy', 'policy mixin'], - ])( - 'offers the mixins of the keyword that an unqualified name reaches, and each namespace, in %s', - (_block, lines, suffix, detail) => { - expect(offered(lines)).toEqual( - [[`Billing${suffix}`, detail], [`Top${suffix}`, detail], ...namespaces].sort(), - ); - }, - ); - - it('offers the same while a name is being typed after the plus', () => { - expect(offered(['model Invoice {', ' +Bil|', '}'])).toEqual( - offered(['model Invoice {', ' +|', '}']), - ); - }); - - it('offers only top-level mixins of the keyword and each namespace in a top-level block', () => { - expect(offered(['enum Role {', ' +|', '}'], 'top')).toEqual( - [['TopRoles', 'enum mixin'], ...namespaces].sort(), - ); - }); - - it.each([ - ['a model', ['model Invoice {', ' +auth.|', '}'], [['AuthStamps', 'model mixin']]], - ['a composite type', ['type Address {', ' +auth.|', '}'], [['AuthGeo', 'type mixin']]], - ['an enum', ['enum Role {', ' +auth.|', '}'], [['AuthRoles', 'enum mixin']]], - ['a struct block', ['policy P {', ' +auth.|', '}'], [['AuthPolicy', 'policy mixin']]], - ])( - 'offers the mixins of the keyword in the named namespace after a qualifier, in %s', - (_block, lines, expected) => { - expect(offered(lines)).toEqual(expected); - expect(offered(lines.map((line) => line.replace('auth.|', 'auth.Au|')))).toEqual(expected); - }, - ); - - it('offers a namespace only when it has a mixin that would be offered after its qualifier', () => { - const source = (block: readonly string[]) => - [ - 'namespace models {', - ' model mixin Stamps {', - ' }', - '}', - 'namespace enums {', - ' enum mixin Roles {', - ' }', - '}', - 'namespace empty {', - ' model Plain {', - ' id Int', - ' }', - '}', - ...block, - ].join('\n'); - const labels = (block: readonly string[]) => - completeWithSource({ markedSource: source(block), pslBlockDescriptors }) - .items.map(({ label, detail }) => [label, detail]) - .sort(); - - expect(labels(['model Invoice {', ' +|', '}'])).toEqual([['models', 'Namespace']]); - expect(labels(['enum Role {', ' +|', '}'])).toEqual([['enums', 'Namespace']]); - expect(labels(['model Invoice {', ' +models.Stamps', ' +|', '}'])).toEqual([]); - expect(labels(['policy P {', ' +|', '}'])).toEqual([]); - }); - - it('does not offer a top-level mixin whose name a model of the namespace hides', () => { - const { items } = completeWithSource({ - markedSource: [ - 'model mixin Timestamps {', - ' createdAt DateTime', - '}', - 'namespace app {', - ' model Timestamps {', - ' id Int', - ' }', - ' model User {', - ' id Int', - ' +|', - ' }', - '}', - ].join('\n'), - pslBlockDescriptors, - }); - - expect(items.map(({ label, detail }) => [label, detail])).toEqual([['public', 'Namespace']]); - }); - - it('offers nothing after a qualifier that names no namespace', () => { - expect(offered(['model Invoice {', ' +nowhere.|', '}'])).toEqual([]); - expect(offered(['model Invoice {', ' +Invoice.|', '}'])).toEqual([]); - }); - - it('leaves out a mixin the block already includes, by either spelling', () => { - expect( - offered(['model Invoice {', ' +Used', ' +billing.BillingStamps', ' +|', '}']), - ).toEqual([ - ['TopStamps', 'model mixin'], - ['auth', 'Namespace'], - ['public', 'Namespace'], - ]); - expect(offered(['model Invoice {', ' +Used', ' +billing.|', '}'])).toEqual([ - ['BillingStamps', 'model mixin'], - ]); - }); - - it('still offers the mixin whose complete name the cursor is at the end of', () => { - expect(offered(['model Invoice {', ' +Used|', '}'])).toContainEqual(['Used', 'model mixin']); - }); - - it('offers nothing after a plus inside a mixin body', () => { - expect(offered(['model mixin Audited {', ' +|', '}'])).toEqual([]); - expect(offered(['policy mixin Shared {', ' +auth.|', '}'])).toEqual([]); - }); - - it('offers no entry keys after a plus in a struct block', () => { - const labels = after(['policy P {', ' +|', '}']).items.map((item) => item.label); - const keys = after(['policy P {', ' |', '}']).items.map((item) => item.label); - - expect(keys.length).toBeGreaterThan(0); - expect(labels.filter((label) => keys.includes(label))).toEqual([]); - }); - - it('offers no mixins on the line after a plus', () => { - expect(offered(['model Invoice {', ' +', ' |', '}'])).toEqual([]); - }); - - it('replaces the typed name with the mixin name, and a namespace with its name and a dot', () => { - const { items, sourceFile, cursorOffset } = after(['model Invoice {', ' +Bil|', '}']); - const range = { - start: sourceFile.positionAt(cursorOffset - 3), - end: sourceFile.positionAt(cursorOffset), - }; - - expect(completionItemByLabel(items, 'BillingStamps')).toMatchObject({ - kind: CompletionItemKind.Interface, - detail: 'model mixin', - filterText: 'BillingStamps', - textEdit: { range, newText: 'BillingStamps' }, - }); - expect(completionItemByLabel(items, 'auth')).toMatchObject({ - kind: CompletionItemKind.Module, - detail: 'Namespace', - textEdit: { range, newText: 'auth.' }, - }); - }); -}); - -describe('providePslCompletionItems — inside a mixin body', () => { - const declarationKeywords = ['namespace', 'types']; - - const withoutOwnName = (items: readonly CompletionItem[]) => - items.filter((item) => item.label !== 'Probe'); - - function itemsIn(header: string, body: readonly string[], closed = true) { - return complete([header, ...body, ...(closed ? ['}'] : [])].join('\n')).items; - } - - it.each([ - ['a field type', [' scopedOnly Int', ' value |']], - ['a field attribute name', [' scopedOnly Int', ' value Int @|']], - ['a field attribute argument', [' value Int @marker(|)']], - [ - 'a field attribute key that depends on the fields of the owner', - [' scopedOnly Int', ' value Int @ownerAware(|)'], - ], - ['a field attribute key when the owner lacks the field', [' value Int @ownerAware(|)']], - ['a block attribute name', [' value Int', ' @@|']], - ['a block attribute argument', [' value Int', ' @@security.rls(|)']], - ])('offers in a model mixin what a model offers at %s', (_position, body) => { - const inMixin = itemsIn('model mixin Probe {', body); - - expect(inMixin).toEqual(withoutOwnName(itemsIn('model Probe {', body))); - expect( - inMixin.map((item) => item.label).filter((label) => declarationKeywords.includes(label)), - ).toEqual([]); - }); - - it('offers something at each model position the fixture has candidates for', () => { - expect(itemsIn('model mixin Probe {', [' value |']).length).toBeGreaterThan(0); - expect(itemsIn('model mixin Probe {', [' value Int @|']).length).toBeGreaterThan(0); - expect(itemsIn('model mixin Probe {', [' value Int', ' @@|']).length).toBeGreaterThan(0); - expect( - itemsIn('model mixin Probe {', [' scopedOnly Int', ' value Int @ownerAware(|)']).map( - (item) => item.label, - ), - ).toEqual(['scopedKey']); - }); - - it.each([ - ['a field type', [' value |']], - ['a field attribute name', [' value Int @|']], - ['a field attribute argument', [' value Int @marker(|)']], - ['a block attribute name', [' value Int', ' @@|']], - ])('offers in a type mixin what a composite type offers at %s', (_position, body) => { - const inMixin = itemsIn('type mixin Probe {', body); - - expect(inMixin).toEqual(withoutOwnName(itemsIn('type Probe {', body))); - expect( - inMixin.map((item) => item.label).filter((label) => declarationKeywords.includes(label)), - ).toEqual([]); - }); - - it.each([ - ['an entry key', 'policy', [' |']], - ['an entry key after another entry', 'policy', [' on = User', ' |']], - ['an entry value that names a model', 'policy', [' on = |']], - ['an entry value with fixed identifiers', 'policy', [' mode = |']], - ['a block attribute name', 'policy', [' @@|']], - ['a block attribute argument', 'policy', [' @@audit(|)']], - ['an entry key of a map block', 'inventory', [' |']], - ['an entry value of a map block', 'inventory', [' count = |']], - ])( - 'offers in a mixin for a key = value keyword what a block of that keyword offers at %s', - (_position, keyword, body) => { - const inMixin = itemsIn(`${keyword} mixin Probe {`, body); - - expect(inMixin).toEqual(itemsIn(`${keyword} Probe {`, body)); - expect( - inMixin.map((item) => item.label).filter((label) => declarationKeywords.includes(label)), - ).toEqual([]); - }, - ); - - it('offers something at each struct block position', () => { - expect(itemsIn('policy mixin Probe {', [' |']).map((item) => item.label)).toContain('on'); - expect(itemsIn('policy mixin Probe {', [' mode = |']).map((item) => item.label)).toEqual([ - 'permissive', - 'restrictive', - ]); - expect(itemsIn('policy mixin Probe {', [' @@|']).map((item) => item.label)).toEqual(['audit']); - }); - - it('offers nothing and does not fail in a mixin for a keyword with no registered block', () => { - expect(itemsIn('widget mixin Probe {', [' |'])).toEqual(itemsIn('widget Probe {', [' |'])); - expect(itemsIn('widget mixin Probe {', [' |'])).toEqual([]); - expect(itemsIn('widget mixin Probe {', [' @@|'])).toEqual([]); - expect(itemsIn('widget mixin Probe {', [' key = |'])).toEqual([]); - }); - - it('completes inside a mixin that is not closed yet as inside a model that is not closed yet', () => { - const body = [' scopedOnly Int', ' value Int @|']; - - expect(itemsIn('model mixin Probe {', body, false)).toEqual( - itemsIn('model Probe {', body, false), - ); - expect(itemsIn('model mixin Probe {', body, false).length).toBeGreaterThan(0); - expect(itemsIn('policy mixin Probe {', [' |'], false)).toEqual( - itemsIn('policy Probe {', [' |'], false), - ); - }); - - it('offers the same inside a mixin declared in a namespace', () => { - const inNamespace = (header: string) => - complete(['namespace scoped {', ` ${header}`, ' value |', ' }', '}'].join('\n')).items; - - expect(inNamespace('model mixin Probe {')).toEqual( - withoutOwnName(inNamespace('model Probe {')), - ); - }); -}); - -describe('providePslCompletionItems — keys a block already has', () => { - const keysOf = (block: readonly string[]) => - complete(['policy mixin Shared {', ' on = User', ' where = "true"', '}', ...block].join('\n')) - .items.map((item) => item.label) - .sort(); - - it('does not offer a key that an included mixin provides', () => { - expect(keysOf(['policy P {', ' +Shared', ' |', '}'])).toEqual(['mode', 'using']); - }); - - it('offers that key in the same block without the inclusion', () => { - expect(keysOf(['policy P {', ' |', '}'])).toEqual(['mode', 'on', 'using', 'where']); - }); - - it('does not offer a key written in the block itself, and still offers the key under the cursor', () => { - expect(keysOf(['policy P {', ' mode = permissive', ' |', '}'])).toEqual([ - 'on', - 'using', - 'where', - ]); - expect(keysOf(['policy P {', ' +Shared', ' mo|', '}'])).toEqual(['mode', 'using']); - }); }); diff --git a/packages/1-framework/3-tooling/language-server/test/helpers/completion-provider.ts b/packages/1-framework/3-tooling/language-server/test/helpers/completion-provider.ts new file mode 100644 index 000000000000..2ed58487a9dd --- /dev/null +++ b/packages/1-framework/3-tooling/language-server/test/helpers/completion-provider.ts @@ -0,0 +1,468 @@ +import { dirname, resolve } from 'node:path'; +import { fileURLToPath, pathToFileURL } from 'node:url'; +import type { + AuthoringEntityTypeNamespace, + AuthoringPslBlockDescriptorNamespace, + DataTypeAuthoringEntry, + DataTypeSupport, +} from '@internal/framework-components/authoring'; +import { createDataTypeLookup, dataType } from '@internal/framework-components/codec'; +import { + EMPTY_GENERATOR_FUNCTIONS, + type GeneratorFunctionRegistry, +} from '@internal/framework-components/components'; +import { + assembleAuthoringContributions, + assembleControlMutationDefaults, + type ControlMutationDefaultRegistry, +} from '@internal/framework-components/control'; +import { + type AttributeSpecNamespace, + blockAttribute, + buildSymbolTable, + entityRef, + type FieldAttributeSpecContext, + fieldAttribute, + identifier, + int, + jsonValue, + mapBlock, + modelAttribute, + oneOf, + optional, + str, + structBlock, +} from '@internal/psl-parser'; +import { parse, type SourceFile } from '@internal/psl-parser/syntax'; +import { expect } from 'vitest'; +import type { CompletionItem } from 'vscode-languageserver'; +import { classifyPslCompletionContext } from '../../src/completion-context'; +import { providePslCompletionItems } from '../../src/completion-provider'; +import { testBinder } from './binder'; + +export const scalarTypes = ['String', 'Int', 'Boolean', 'DateTime'] as const; +export const nameSnippetPlaceholder = '$' + '{1:Name}'; +export const emptySnippetPlaceholder1 = '$' + '{1:}'; +export const sqlExpressionSnippet = 'sql`$' + '{1:expression}`'; +export const packageRoot = dirname(dirname(dirname(fileURLToPath(import.meta.url)))); + +export const markerAttribute = fieldAttribute('marker', { + documentation: 'Attaches a named marker to a target.', + positional: [{ key: 'target', type: str(), documentation: 'The marker target.' }], + named: { + name: { type: str(), documentation: 'The marker name.' }, + priority: { type: optional(int()), documentation: 'The marker priority.' }, + }, +}); +export const orderFixtureAttribute = fieldAttribute('orderFixture', { + documentation: 'Accepts named values in declaration order rather than alphabetical order.', + named: { + zebra: { type: int(), documentation: 'The first declared value.' }, + alpha: { type: int(), documentation: 'The second declared value.' }, + middle: { type: int(), documentation: 'The third declared value.' }, + }, +}); +export const rlsAttribute = modelAttribute('rls', { + documentation: 'Configures row-level security for this model.', + named: { + enabled: { type: optional(str()), documentation: 'The security enablement setting.' }, + mode: { type: str(), documentation: 'The security mode.' }, + }, +}); +export const auditAttribute = blockAttribute('audit', { + documentation: 'Configures auditing for this block.', + named: { + reason: { type: optional(str()), documentation: 'The reason for auditing.' }, + level: { type: int(), documentation: 'The audit level.' }, + }, +}); + +export const attributeContributions = assembleAuthoringContributions([ + { + id: 'fixture-family', + authoring: { + attributeSpecs: { + field: { + marker: () => markerAttribute, + orderFixture: () => orderFixtureAttribute, + ownerAware: (ctx: FieldAttributeSpecContext) => + fieldAttribute('ownerAware', { + documentation: 'Selects a key based on the declaring model’s fields.', + named: { + [Object.hasOwn(ctx.model.fields, 'scopedOnly') ? 'scopedKey' : 'topKey']: { + type: str(), + documentation: 'The value for the owner-specific key.', + }, + }, + }), + }, + model: {}, + }, + }, + }, + { + id: 'fixture-target', + authoring: { + modelAttributes: { + security: { + rls: { + kind: 'modelAttribute', + attribute: 'rls', + spec: () => rlsAttribute, + lower: () => undefined, + }, + }, + }, + }, + }, +]); +export const controlMutationDefaults = assembleControlMutationDefaults([]); +export const policySpec = () => + structBlock({ + parameters: { + on: { type: optional(entityRef({ kind: 'model' })), documentation: '' }, + where: { type: optional(str()), documentation: 'The policy predicate.' }, + mode: { + type: optional( + oneOf( + identifier('permissive', { documentation: 'Combined with OR.' }), + identifier('restrictive', { documentation: 'Combined with AND.' }), + ), + ), + documentation: '', + }, + using: { type: optional(str()), documentation: '' }, + }, + }); + +export const pslBlockDescriptors: AuthoringPslBlockDescriptorNamespace = { + policy: { + kind: 'pslBlock', + keyword: 'policy', + documentation: 'Defines a security policy.', + discriminator: 'fixture-policy', + name: { required: true }, + spec: policySpec, + attributes: { audit: () => auditAttribute }, + }, + inventory: { + kind: 'pslBlock', + keyword: 'inventory', + documentation: 'An arbitrary-key block.', + discriminator: 'fixture-inventory', + name: { required: true }, + spec: () => + mapBlock({ + value: { type: jsonValue(), documentation: 'The entry value.' }, + allowBare: true, + }), + }, + access: { + audit: { + kind: 'pslBlock', + keyword: 'audit', + discriminator: 'fixture-audit', + name: { required: true }, + spec: () => + structBlock({ + parameters: { + on: { type: optional(entityRef({ kind: 'model' })), documentation: '' }, + }, + }), + }, + }, +}; + +export const candidateSource = [ + 'types {', + ' Email = String', + ' UserId = User', + '}', + 'model User {', + ' id Int', + ' topOnly String', + '}', + 'type Address {', + ' street String', + '}', + 'policy Audit {', + ' on = read', + '}', + 'namespace auth {', + ' model Account {', + ' id Int', + ' }', + ' model User {', + ' id Int', + ' scopedOnly String', + ' }', + ' type Profile {', + ' displayName String', + ' }', + ' policy ScopedAudit {', + ' on = read', + ' }', + '}', +].join('\n'); + +export function complete( + markedFieldSource: string, + options: { + readonly clientSupportsSnippets?: boolean; + readonly clientSupportsTriggerParameterHintsCommand?: boolean; + } = {}, +) { + return completeWithSource({ + markedSource: `${candidateSource}\n${markedFieldSource}`, + pslBlockDescriptors, + authoringContributions: attributeContributions, + controlMutationDefaults, + clientSupportsSnippets: options.clientSupportsSnippets === true, + clientSupportsTriggerParameterHintsCommand: + options.clientSupportsTriggerParameterHintsCommand === true, + }); +} + +export interface CompletionTestStack { + readonly attributeSpecs: AttributeSpecNamespace; + readonly entityTypes: AuthoringEntityTypeNamespace; + readonly pslBlockDescriptors: AuthoringPslBlockDescriptorNamespace; +} + +export interface ActualSqlAttributeModule { + readonly sqlAttributeSpecs: AttributeSpecNamespace; +} + +export interface ActualSqlBlockModule { + readonly sqlFamilyEntityTypes: AuthoringEntityTypeNamespace; + readonly sqlFamilyPslBlockDescriptors: AuthoringPslBlockDescriptorNamespace; +} + +export interface ActualPostgresDefaultsModule { + createPostgresDefaultFunctionRegistry(): ControlMutationDefaultRegistry; +} + +export interface ActualPostgresDataTypesModule { + postgresDataTypeEntries(): Readonly>; +} + +export interface ActualSqliteDataTypesModule { + sqliteDataTypeEntries(): Readonly>; +} + +export interface ActualSqlExpressionModule { + readonly sqlExpressionRegistration: { + readonly authoring: { + readonly dataTypes: Readonly>; + }; + }; +} + +export interface ActualMongoAttributeModule { + readonly mongoAttributeSpecs: AttributeSpecNamespace; +} + +export interface ActualMongoBlockModule { + readonly mongoFamilyEntityTypes: AuthoringEntityTypeNamespace; + readonly mongoFamilyPslBlockDescriptors: AuthoringPslBlockDescriptorNamespace; +} + +export function completeWithSource(input: { + readonly markedSource: string; + readonly siblings?: readonly string[]; + readonly pslBlockDescriptors: AuthoringPslBlockDescriptorNamespace; + readonly authoringContributions?: typeof attributeContributions; + readonly controlMutationDefaults?: typeof controlMutationDefaults; + readonly dataTypes?: DataTypeSupport; + readonly clientSupportsSnippets?: boolean; + readonly clientSupportsTriggerSuggestCommand?: boolean; + readonly clientSupportsTriggerParameterHintsCommand?: boolean; + readonly scalarTypes?: readonly string[]; + readonly generatorFunctions?: GeneratorFunctionRegistry; +}) { + const cursorOffset = input.markedSource.indexOf('|'); + expect(cursorOffset).toBeGreaterThanOrEqual(0); + const source = `${input.markedSource.slice(0, cursorOffset)}${input.markedSource.slice(cursorOffset + 1)}`; + const parsed = parse(source, 'language-server-test.psl'); + const { document } = parsed; + const others = (input.siblings ?? []).map((text, index) => parse(text, `sibling-${index}.psl`)); + const sources = parsed.sources.merge(...others.map((other) => other.sources)); + const sourceFile = sources.sourceFileFor(document.syntax); + const { symbolTable } = buildSymbolTable({ + documents: [...others.map((other) => other.document), document], + sources, + defaultNamespaceId: 'public', + }); + const context = classifyPslCompletionContext({ + document, + sourceFile, + position: sourceFile.positionAt(cursorOffset), + }); + + return { + items: providePslCompletionItems({ + context, + sourceFile, + candidates: { + binder: testBinder({ + ...input, + sources, + symbolTable, + scalarTypes: input.scalarTypes ?? scalarTypes, + }), + scalarTypes: input.scalarTypes ?? scalarTypes, + pslBlockDescriptors: input.pslBlockDescriptors, + symbolTable, + ...(input.authoringContributions === undefined + ? {} + : { authoringContributions: input.authoringContributions }), + ...(input.controlMutationDefaults === undefined + ? {} + : { controlMutationDefaults: input.controlMutationDefaults }), + ...(input.dataTypes === undefined ? {} : { dataTypes: input.dataTypes }), + generatorFunctions: input.generatorFunctions ?? EMPTY_GENERATOR_FUNCTIONS, + }, + clientSupportsSnippets: input.clientSupportsSnippets === true, + clientSupportsTriggerSuggestCommand: input.clientSupportsTriggerSuggestCommand === true, + clientSupportsTriggerParameterHintsCommand: + input.clientSupportsTriggerParameterHintsCommand === true, + }), + sourceFile, + cursorOffset, + }; +} + +export async function actualSqlStack(): Promise { + const [attributes, blocks] = await Promise.all([ + importFromPackageRoot( + '../../../2-sql-family/2-authoring/contract-psl/src/sql-attribute-specs.ts', + ), + importFromPackageRoot( + '../../../2-sql-family/9-family/src/core/authoring-entity-types.ts', + ), + ]); + return { + attributeSpecs: attributes.sqlAttributeSpecs, + entityTypes: blocks.sqlFamilyEntityTypes, + pslBlockDescriptors: blocks.sqlFamilyPslBlockDescriptors, + }; +} + +export async function actualMongoStack(): Promise { + const [attributes, blocks] = await Promise.all([ + importFromPackageRoot( + '../../../2-mongo-family/2-authoring/contract-psl/src/mongo-attribute-specs.ts', + ), + importFromPackageRoot( + '../../../2-mongo-family/9-family/src/core/authoring-entity-types.ts', + ), + ]); + return { + attributeSpecs: attributes.mongoAttributeSpecs, + entityTypes: blocks.mongoFamilyEntityTypes, + pslBlockDescriptors: blocks.mongoFamilyPslBlockDescriptors, + }; +} + +export const fixtureInt = dataType('fixture/int', { read: (json) => json }); +export const fixtureExpression = dataType('fixture/expression', { read: (json) => json }); +export const constructorArgumentDataTypes: DataTypeSupport = { + entries: { + [fixtureInt.id]: { + written: { + kind: 'plain', + syntax: 'number', + types: [fixtureInt.id], + classify: (text) => ({ type: fixtureInt.id, value: Number(text) }), + }, + print: String, + documentation: 'A whole number.', + }, + [fixtureExpression.id]: { + written: { kind: 'tag', tag: 'sql', parse: (text) => text }, + print: String, + documentation: 'A SQL expression.', + }, + }, + lookup: createDataTypeLookup([fixtureInt, fixtureExpression]), + dataTypeOfCodec: () => undefined, +}; + +export async function sqlExpressionDataTypes(): Promise< + Readonly> +> { + const family = await importFromPackageRoot( + '../../../2-sql-family/1-core/contract/src/exports/sql-expression.ts', + ); + return family.sqlExpressionRegistration.authoring.dataTypes; +} + +export async function importFromPackageRoot(relativePath: string): Promise { + return (await import(pathToFileURL(resolve(packageRoot, relativePath)).href)) as T; +} + +export function actualAuthoringContributions( + stack: CompletionTestStack, +): typeof attributeContributions { + return assembleAuthoringContributions([ + { + id: 'actual-family', + authoring: { + attributeSpecs: stack.attributeSpecs, + entityTypes: stack.entityTypes, + pslBlockDescriptors: stack.pslBlockDescriptors, + }, + }, + ]); +} + +export function completeWithActualStack( + markedSource: string, + stack: CompletionTestStack, + options: { + readonly clientSupportsSnippets?: boolean; + readonly controlMutationDefaults?: typeof controlMutationDefaults; + readonly dataTypes?: Readonly>; + } = {}, +) { + return completeWithSource({ + markedSource, + pslBlockDescriptors: stack.pslBlockDescriptors, + authoringContributions: actualAuthoringContributions(stack), + ...(options.dataTypes === undefined + ? {} + : { + dataTypes: { + entries: options.dataTypes, + lookup: createDataTypeLookup([]), + dataTypeOfCodec: () => undefined, + }, + }), + controlMutationDefaults: options.controlMutationDefaults ?? controlMutationDefaults, + clientSupportsSnippets: options.clientSupportsSnippets === true, + }); +} + +export function applyCompletionItem(input: { + readonly sourceFile: SourceFile; + readonly item: CompletionItem; +}) { + const edit = input.item.textEdit; + if (edit === undefined || !('range' in edit)) { + throw new Error('Expected a range text edit'); + } + const start = input.sourceFile.offsetAt(edit.range.start); + const end = input.sourceFile.offsetAt(edit.range.end); + return `${input.sourceFile.text.slice(0, start)}${edit.newText}${input.sourceFile.text.slice(end)}`; +} + +export function completionItemByLabel( + items: readonly CompletionItem[], + label: string, +): CompletionItem { + const item = items.find((candidate) => candidate.label === label); + if (item === undefined) { + throw new Error(`Expected completion item "${label}"`); + } + return item; +} diff --git a/packages/1-framework/3-tooling/language-server/test/helpers/hover.ts b/packages/1-framework/3-tooling/language-server/test/helpers/hover.ts new file mode 100644 index 000000000000..0297d6587399 --- /dev/null +++ b/packages/1-framework/3-tooling/language-server/test/helpers/hover.ts @@ -0,0 +1,278 @@ +import type { + AuthoringPslBlockDescriptorNamespace, + DataTypeSupport, +} from '@internal/framework-components/authoring'; +import { createDataTypeLookup, dataType } from '@internal/framework-components/codec'; +import { assembleAuthoringContributions } from '@internal/framework-components/control'; +import { + type ArgType, + type AttributeCtx, + blockAttribute, + bool, + buildSymbolTable, + entityRef, + fieldAttribute, + fieldRef, + funcCall, + identifier, + list, + modelAttribute, + numLiteral, + oneOf, + optional, + str, + structBlock, +} from '@internal/psl-parser'; +import { parse } from '@internal/psl-parser/syntax'; +import { ok } from '@internal/utils/result'; +import { expect } from 'vitest'; +import { providePslHover } from '../../src/hover'; +import { testBinder } from './binder'; + +const relatesTo = fieldAttribute('relatesTo', { + documentation: '', + named: { fields: { type: fieldRef(), documentation: '' } }, +}); +const guardedBy = modelAttribute('guardedBy', { + documentation: '', + named: { policy: { type: entityRef({ kind: 'block', keyword: 'policy' }), documentation: '' } }, +}); +const relationSpec = fieldAttribute('relation', { + documentation: 'Declares a relation.', + named: { + name: { type: optional(str()), documentation: 'An explicit relation name.' }, + }, +}); +const incompleteKeySpec = fieldAttribute('incompleteKey', { + documentation: '', + named: { + references: { type: optional(str()), documentation: 'Referenced fields.' }, + }, +}); +const onDeleteSpec = fieldAttribute('onDeleteExample', { + documentation: '', + named: { + onDelete: { + type: oneOf( + identifier('NoAction', { documentation: 'Takes no action.' }), + identifier('Restrict', { documentation: 'Rejects the delete.' }), + identifier('Cascade', { documentation: 'Cascades the delete.' }), + ), + documentation: '', + }, + }, +}); +const indexSpec = modelAttribute('index', { + documentation: 'Declares an index.', + named: { fields: { type: list(str()), documentation: 'Indexed field names.' } }, +}); +const autoincrementFunc = funcCall('autoincrement', { + documentation: 'Generates sequential integers.', +}); +const dbgeneratedFunc = funcCall('dbgenerated', { + documentation: 'Lets the database generate the value.', + named: { expr: { type: str(), documentation: 'The database expression.' } }, +}); +const defaultSpec = fieldAttribute('default', { + documentation: '', + positional: [ + { key: 'value', type: oneOf(autoincrementFunc, dbgeneratedFunc), documentation: '' }, + ], +}); +function nullLiteral(): ArgType { + const nullIdentifier = identifier('null', { documentation: 'A null value.' }); + return { + kind: 'null', + label: 'null', + parse: (arg, ctx) => { + const result = nullIdentifier.parse(arg, ctx); + return result.ok ? ok(null) : result; + }, + }; +} +const autoincrementRealOrderFunc = funcCall('autoincrement', { + documentation: 'Generates sequential integers.', +}); +const defaultRealOrderSpec = fieldAttribute('default', { + documentation: '', + positional: [ + { + key: 'value', + type: oneOf(str(), numLiteral(), bool(), nullLiteral(), autoincrementRealOrderFunc), + documentation: '', + }, + ], +}); +const someBlockAttribute = blockAttribute('someBlockAttribute', { + documentation: 'Does something.', + positional: [{ key: 'value', type: str(), documentation: 'The value.' }], +}); +const authoringContributions = assembleAuthoringContributions([ + { + id: 'hover-fixture', + authoring: { + attributeSpecs: { + field: { + relatesTo: () => relatesTo, + relation: () => relationSpec, + incompleteKey: () => incompleteKeySpec, + onDeleteExample: () => onDeleteSpec, + default: () => defaultSpec, + defaultRealOrder: () => defaultRealOrderSpec, + }, + model: { guardedBy: () => guardedBy, index: () => indexSpec }, + }, + type: { + pg: { + Varchar: { + kind: 'typeConstructor', + documentation: 'A variable-length string.', + args: [{ name: 'length', kind: 'value', dataType: 'fixture/int' }], + output: { codecId: 'fixture/varchar' }, + }, + Text: { + kind: 'typeConstructor', + output: { codecId: 'fixture/text' }, + }, + Raw: { + kind: 'typeConstructor', + documentation: 'A type written as SQL.', + args: [{ name: 'sql', kind: 'value', dataType: 'fixture/expression' }], + output: { codecId: 'fixture/raw' }, + }, + Mode: { + kind: 'typeConstructor', + documentation: 'A deletion mode.', + args: [{ name: 'mode', kind: 'option', values: ['cascade', 'restrict'] }], + output: { codecId: 'fixture/mode' }, + }, + Flag: { + kind: 'typeConstructor', + documentation: '', + output: { codecId: 'fixture/flag' }, + }, + Moment: { + kind: 'typeChoice', + documentation: 'A moment in time.', + args: [ + { + kind: 'option', + name: 'as', + values: ['date', 'text'], + default: 'date', + optional: true, + }, + { + kind: 'option', + name: 'storage', + values: ['wide', 'narrow'], + default: 'wide', + optional: true, + }, + { kind: 'value', name: 'precision', dataType: 'fixture/int', optional: true }, + ], + choices: [ + { + when: { as: 'date', storage: 'wide' }, + type: 'Stamp', + defaults: { precision: 3 }, + }, + { when: { as: 'text', storage: 'wide' }, type: 'Text' }, + { when: { as: 'date', storage: 'narrow' }, type: 'Flag' }, + ], + }, + Stamp: { + kind: 'typeConstructor', + args: [{ name: 'precision', kind: 'value', dataType: 'fixture/int', optional: true }], + output: { codecId: 'fixture/stamp' }, + }, + CreatedAt: { + kind: 'typeAlias', + documentation: 'Set to now when a row is created.', + output: { type: { codecId: 'fixture/text' }, onCreate: { name: 'now', args: {} } }, + }, + }, + }, + field: { + pg: { + Serial: { + kind: 'fieldPreset', + args: [{ name: 'start', kind: 'number' }], + output: { codecId: 'fixture/serial' }, + }, + }, + }, + }, + }, +]); +const pslBlockDescriptors: AuthoringPslBlockDescriptorNamespace = { + policy: { + kind: 'pslBlock', + documentation: 'Row-level access rule.', + keyword: 'policy', + discriminator: 'hover-policy', + name: { required: true }, + spec: () => + structBlock({ + parameters: { + using: { type: str(), documentation: 'The policy filter expression.' }, + }, + }), + attributes: { someBlockAttribute: () => someBlockAttribute }, + }, + view: { + kind: 'pslBlock', + keyword: 'view', + discriminator: 'hover-view', + name: { required: true }, + spec: () => structBlock({ parameters: {} }), + }, +}; + +const fixtureInt = dataType('fixture/int', { read: (json) => json }); +const fixtureExpression = dataType('fixture/expression', { read: (json) => json }); +const dataTypes: DataTypeSupport = { + entries: { + [fixtureInt.id]: { + written: { + kind: 'plain', + syntax: 'number', + types: [fixtureInt.id], + classify: (text) => ({ type: fixtureInt.id, value: Number(text) }), + }, + print: String, + documentation: 'A whole number.', + }, + [fixtureExpression.id]: { + written: { kind: 'tag', tag: 'sql', parse: (text) => text }, + print: String, + documentation: 'A SQL expression.', + }, + }, + lookup: createDataTypeLookup([fixtureInt, fixtureExpression]), + dataTypeOfCodec: () => undefined, +}; + +export function hover(markedSource: string, siblings: readonly string[] = []) { + const offset = markedSource.indexOf('|'); + expect(offset).toBeGreaterThanOrEqual(0); + const source = markedSource.slice(0, offset) + markedSource.slice(offset + 1); + const parsed = parse(source, 'language-server-test.psl'); + const { document } = parsed; + const others = siblings.map((text, index) => parse(text, `sibling-${index}.psl`)); + const sources = parsed.sources.merge(...others.map((other) => other.sources)); + const sourceFile = sources.sourceFileFor(document.syntax); + const { symbolTable } = buildSymbolTable({ + documents: [...others.map((other) => other.document), document], + sources, + defaultNamespaceId: 'public', + }); + return providePslHover({ + document, + sourceFile, + position: sourceFile.positionAt(offset), + binder: testBinder({ sources, symbolTable, authoringContributions, pslBlockDescriptors }), + pslBlockDescriptors, + dataTypes, + }); +} diff --git a/packages/1-framework/3-tooling/language-server/test/hover.attributes.test.ts b/packages/1-framework/3-tooling/language-server/test/hover.attributes.test.ts new file mode 100644 index 000000000000..dc83368118f7 --- /dev/null +++ b/packages/1-framework/3-tooling/language-server/test/hover.attributes.test.ts @@ -0,0 +1,151 @@ +import { describe, expect, it } from 'vitest'; +import { hover } from './helpers/hover'; + +describe('providePslHover', () => { + it('shows a field-attribute signature label and its documentation', () => { + const result = hover('model User {\n id Int\n author User @relat|ion\n}'); + expect(result).toEqual({ + contents: { + kind: 'markdown', + value: '```prisma\n@relation(name?: string)\n```\n\nDeclares a relation.', + }, + range: { start: { line: 2, character: 15 }, end: { line: 2, character: 23 } }, + }); + }); + + it('shows a model-attribute signature label and its documentation', () => { + const result = hover('model User {\n id Int\n @@ind|ex\n}'); + expect(result).toEqual({ + contents: { + kind: 'markdown', + value: '```prisma\n@@index(fields: string[])\n```\n\nDeclares an index.', + }, + range: { start: { line: 2, character: 4 }, end: { line: 2, character: 9 } }, + }); + }); + + it('omits the documentation section when spec.documentation is empty', () => { + const result = hover('model User {\n id Int\n author User @relates|To(fields: id)\n}'); + expect(result).toEqual({ + contents: { kind: 'markdown', value: '```prisma\n@relatesTo(fields: field name)\n```' }, + range: { start: { line: 2, character: 15 }, end: { line: 2, character: 24 } }, + }); + }); + + it('shows a block keyword with documentation', () => { + const result = hover('polic|y ReadOwn {\n}'); + expect(result).toEqual({ + contents: { kind: 'markdown', value: 'Row-level access rule.' }, + range: { start: { line: 0, character: 0 }, end: { line: 0, character: 6 } }, + }); + }); + + it('returns null for a block keyword without documentation', () => { + const result = hover('vie|w Summary {\n}'); + expect(result).toBeNull(); + }); + + it('shows a named attribute-argument key and its documentation', () => { + const result = hover('model User {\n author User @relation(nam|e: "x")\n}'); + expect(result).toEqual({ + contents: { + kind: 'markdown', + value: '```prisma\nname?: string\n```\n\nAn explicit relation name.', + }, + range: { start: { line: 1, character: 24 }, end: { line: 1, character: 28 } }, + }); + }); + + it('shows a named attribute-argument key while its value is missing', () => { + const result = hover('model User {\n author User @incompleteKey(referen|ces: )\n}'); + expect(result).toEqual({ + contents: { + kind: 'markdown', + value: '```prisma\nreferences?: string\n```\n\nReferenced fields.', + }, + range: { start: { line: 1, character: 29 }, end: { line: 1, character: 39 } }, + }); + }); + + it('omits the documentation section for a parameter with empty documentation', () => { + const result = hover('model User {\n id Int\n author User @relatesTo(field|s: id)\n}'); + expect(result).toEqual({ + contents: { kind: 'markdown', value: '```prisma\nfields: field name\n```' }, + range: { start: { line: 2, character: 25 }, end: { line: 2, character: 31 } }, + }); + }); + + it('shows a struct-block entry key and its documentation', () => { + const result = hover('policy ReadOwn {\n usi|ng = "x"\n}'); + expect(result).toEqual({ + contents: { + kind: 'markdown', + value: '```prisma\nusing: string\n```\n\nThe policy filter expression.', + }, + range: { start: { line: 1, character: 2 }, end: { line: 1, character: 7 } }, + }); + }); + + it('shows a function name without args and its documentation', () => { + const result = hover('model User {\n id Int @default(autoincreme|nt())\n}'); + expect(result).toEqual({ + contents: { + kind: 'markdown', + value: '```prisma\nautoincrement()\n```\n\nGenerates sequential integers.', + }, + range: { start: { line: 1, character: 18 }, end: { line: 1, character: 31 } }, + }); + }); + + it('shows a function name past leading scalar leaves, in the real @default arm order', () => { + const result = hover('model User {\n id Int @defaultRealOrder(autoincreme|nt())\n}'); + expect(result).toEqual({ + contents: { + kind: 'markdown', + value: '```prisma\nautoincrement()\n```\n\nGenerates sequential integers.', + }, + range: { start: { line: 1, character: 27 }, end: { line: 1, character: 40 } }, + }); + }); + + it('shows a function name with args and its documentation', () => { + const result = hover('model User {\n value String @default(dbgenerat|ed(expr: "now()"))\n}'); + expect(result).toEqual({ + contents: { + kind: 'markdown', + value: '```prisma\ndbgenerated(expr: string)\n```\n\nLets the database generate the value.', + }, + range: { start: { line: 1, character: 24 }, end: { line: 1, character: 35 } }, + }); + }); + + it('shows a named function-call-argument key and its documentation', () => { + const result = hover('model User {\n value String @default(dbgenerated(ex|pr: "now()"))\n}'); + expect(result).toEqual({ + contents: { + kind: 'markdown', + value: '```prisma\nexpr: string\n```\n\nThe database expression.', + }, + range: { start: { line: 1, character: 36 }, end: { line: 1, character: 40 } }, + }); + }); + + it('shows a constant not first in a oneOf and its documentation', () => { + const result = hover('model User {\n author User @onDeleteExample(onDelete: Casc|ade)\n}'); + expect(result).toEqual({ + contents: { kind: 'markdown', value: '```prisma\nCascade\n```\n\nCascades the delete.' }, + range: { start: { line: 1, character: 41 }, end: { line: 1, character: 48 } }, + }); + }); + + it('shows a block attribute and its documentation', () => { + const result = hover('policy ReadOwn {\n @@someBlockAttri|bute("x")\n}'); + expect(result).toEqual({ + contents: { + kind: 'markdown', + value: '```prisma\n@@someBlockAttribute(string)\n```\n\nDoes something.', + }, + range: { start: { line: 1, character: 4 }, end: { line: 1, character: 22 } }, + }); + }); +}); diff --git a/packages/1-framework/3-tooling/language-server/test/hover.contributed-types.test.ts b/packages/1-framework/3-tooling/language-server/test/hover.contributed-types.test.ts new file mode 100644 index 000000000000..99bd221fa05e --- /dev/null +++ b/packages/1-framework/3-tooling/language-server/test/hover.contributed-types.test.ts @@ -0,0 +1,92 @@ +import { describe, expect, it } from 'vitest'; +import { hover } from './helpers/hover'; + +describe('providePslHover', () => { + it('shows a contributed type with documentation and args', () => { + const result = hover('model Product {\n price pg.Varc|har(255)\n}'); + expect(result).toEqual({ + contents: { + kind: 'markdown', + value: '```prisma\npg.Varchar(length: a number)\n```\n\nA variable-length string.', + }, + range: { start: { line: 1, character: 11 }, end: { line: 1, character: 18 } }, + }); + }); + + it('shows a contributed type alias with its documentation', () => { + const result = hover('model Product {\n createdAt pg.Created|At\n}'); + expect(result).toEqual({ + contents: { + kind: 'markdown', + value: '```prisma\npg.CreatedAt\n```\n\nSet to now when a row is created.', + }, + range: { start: { line: 1, character: 15 }, end: { line: 1, character: 24 } }, + }); + }); + + it('shows a contributed type without documentation as the bare path', () => { + const result = hover('model Product {\n label pg.Te|xt\n}'); + expect(result).toEqual({ + contents: { kind: 'markdown', value: '```prisma\npg.Text\n```' }, + range: { start: { line: 1, character: 11 }, end: { line: 1, character: 15 } }, + }); + }); + + it('omits the documentation section when descriptor.documentation is empty', () => { + const result = hover('model Product {\n flag pg.Fl|ag\n}'); + expect(result).toEqual({ + contents: { kind: 'markdown', value: '```prisma\npg.Flag\n```' }, + range: { start: { line: 1, character: 10 }, end: { line: 1, character: 14 } }, + }); + }); + + it('shows a field-preset contributed type as the fence only, with its arg labels', () => { + const result = hover('model Counter {\n value pg.Ser|ial(1)\n}'); + expect(result).toEqual({ + contents: { kind: 'markdown', value: '```prisma\npg.Serial(start: number)\n```' }, + range: { start: { line: 1, character: 11 }, end: { line: 1, character: 17 } }, + }); + }); + + it('shows a value argument by the forms its data type is written in', () => { + const result = hover('model Product {\n labels pg.Ra|w(sql`text`)\n}'); + expect(result).toEqual({ + contents: { + kind: 'markdown', + value: '```prisma\npg.Raw(sql: sql`...`)\n```\n\nA type written as SQL.', + }, + range: { start: { line: 1, character: 12 }, end: { line: 1, character: 15 } }, + }); + }); + + it('shows an option contributed-type arg as its joined allowed values', () => { + const result = hover('model Product {\n onDelete pg.Mo|de(cascade)\n}'); + expect(result).toEqual({ + contents: { + kind: 'markdown', + value: "```prisma\npg.Mode(mode: 'cascade' | 'restrict')\n```\n\nA deletion mode.", + }, + range: { start: { line: 1, character: 14 }, end: { line: 1, character: 18 } }, + }); + }); + + it('shows a type choice with its choices as written and the explicit type each becomes', () => { + const result = hover('model Product {\n at pg.Mo|ment(as: text)\n}'); + expect(result?.contents).toEqual({ + kind: 'markdown', + value: [ + '```prisma', + 'pg.Moment(as?: date | text, storage?: wide | narrow, precision?: a number)', + '```', + '', + 'A moment in time.', + '', + '| as | storage | Becomes |', + '| --- | --- | --- |', + '| date (default) | wide (default) | `Stamp(precision: 3)` |', + '| text | wide (default) | `Text` |', + '| date (default) | narrow | `Flag` |', + ].join('\n'), + }); + }); +}); diff --git a/packages/1-framework/3-tooling/language-server/test/hover.test.ts b/packages/1-framework/3-tooling/language-server/test/hover.test.ts index 892fd8db6e90..d64f64ee1537 100644 --- a/packages/1-framework/3-tooling/language-server/test/hover.test.ts +++ b/packages/1-framework/3-tooling/language-server/test/hover.test.ts @@ -1,281 +1,5 @@ -import type { - AuthoringPslBlockDescriptorNamespace, - DataTypeSupport, -} from '@internal/framework-components/authoring'; -import { createDataTypeLookup, dataType } from '@internal/framework-components/codec'; -import { assembleAuthoringContributions } from '@internal/framework-components/control'; -import { - type ArgType, - type AttributeCtx, - blockAttribute, - bool, - buildSymbolTable, - entityRef, - fieldAttribute, - fieldRef, - funcCall, - identifier, - list, - modelAttribute, - numLiteral, - oneOf, - optional, - str, - structBlock, -} from '@internal/psl-parser'; -import { parse } from '@internal/psl-parser/syntax'; -import { ok } from '@internal/utils/result'; import { describe, expect, it } from 'vitest'; -import { providePslHover } from '../src/hover'; -import { testBinder } from './helpers/binder'; - -const relatesTo = fieldAttribute('relatesTo', { - documentation: '', - named: { fields: { type: fieldRef(), documentation: '' } }, -}); -const guardedBy = modelAttribute('guardedBy', { - documentation: '', - named: { policy: { type: entityRef({ kind: 'block', keyword: 'policy' }), documentation: '' } }, -}); -const relationSpec = fieldAttribute('relation', { - documentation: 'Declares a relation.', - named: { - name: { type: optional(str()), documentation: 'An explicit relation name.' }, - }, -}); -const incompleteKeySpec = fieldAttribute('incompleteKey', { - documentation: '', - named: { - references: { type: optional(str()), documentation: 'Referenced fields.' }, - }, -}); -const onDeleteSpec = fieldAttribute('onDeleteExample', { - documentation: '', - named: { - onDelete: { - type: oneOf( - identifier('NoAction', { documentation: 'Takes no action.' }), - identifier('Restrict', { documentation: 'Rejects the delete.' }), - identifier('Cascade', { documentation: 'Cascades the delete.' }), - ), - documentation: '', - }, - }, -}); -const indexSpec = modelAttribute('index', { - documentation: 'Declares an index.', - named: { fields: { type: list(str()), documentation: 'Indexed field names.' } }, -}); -const autoincrementFunc = funcCall('autoincrement', { - documentation: 'Generates sequential integers.', -}); -const dbgeneratedFunc = funcCall('dbgenerated', { - documentation: 'Lets the database generate the value.', - named: { expr: { type: str(), documentation: 'The database expression.' } }, -}); -const defaultSpec = fieldAttribute('default', { - documentation: '', - positional: [ - { key: 'value', type: oneOf(autoincrementFunc, dbgeneratedFunc), documentation: '' }, - ], -}); -function nullLiteral(): ArgType { - const nullIdentifier = identifier('null', { documentation: 'A null value.' }); - return { - kind: 'null', - label: 'null', - parse: (arg, ctx) => { - const result = nullIdentifier.parse(arg, ctx); - return result.ok ? ok(null) : result; - }, - }; -} -const autoincrementRealOrderFunc = funcCall('autoincrement', { - documentation: 'Generates sequential integers.', -}); -const defaultRealOrderSpec = fieldAttribute('default', { - documentation: '', - positional: [ - { - key: 'value', - type: oneOf(str(), numLiteral(), bool(), nullLiteral(), autoincrementRealOrderFunc), - documentation: '', - }, - ], -}); -const someBlockAttribute = blockAttribute('someBlockAttribute', { - documentation: 'Does something.', - positional: [{ key: 'value', type: str(), documentation: 'The value.' }], -}); -const authoringContributions = assembleAuthoringContributions([ - { - id: 'hover-fixture', - authoring: { - attributeSpecs: { - field: { - relatesTo: () => relatesTo, - relation: () => relationSpec, - incompleteKey: () => incompleteKeySpec, - onDeleteExample: () => onDeleteSpec, - default: () => defaultSpec, - defaultRealOrder: () => defaultRealOrderSpec, - }, - model: { guardedBy: () => guardedBy, index: () => indexSpec }, - }, - type: { - pg: { - Varchar: { - kind: 'typeConstructor', - documentation: 'A variable-length string.', - args: [{ name: 'length', kind: 'value', dataType: 'fixture/int' }], - output: { codecId: 'fixture/varchar' }, - }, - Text: { - kind: 'typeConstructor', - output: { codecId: 'fixture/text' }, - }, - Raw: { - kind: 'typeConstructor', - documentation: 'A type written as SQL.', - args: [{ name: 'sql', kind: 'value', dataType: 'fixture/expression' }], - output: { codecId: 'fixture/raw' }, - }, - Mode: { - kind: 'typeConstructor', - documentation: 'A deletion mode.', - args: [{ name: 'mode', kind: 'option', values: ['cascade', 'restrict'] }], - output: { codecId: 'fixture/mode' }, - }, - Flag: { - kind: 'typeConstructor', - documentation: '', - output: { codecId: 'fixture/flag' }, - }, - Moment: { - kind: 'typeChoice', - documentation: 'A moment in time.', - args: [ - { - kind: 'option', - name: 'as', - values: ['date', 'text'], - default: 'date', - optional: true, - }, - { - kind: 'option', - name: 'storage', - values: ['wide', 'narrow'], - default: 'wide', - optional: true, - }, - { kind: 'value', name: 'precision', dataType: 'fixture/int', optional: true }, - ], - choices: [ - { - when: { as: 'date', storage: 'wide' }, - type: 'Stamp', - defaults: { precision: 3 }, - }, - { when: { as: 'text', storage: 'wide' }, type: 'Text' }, - { when: { as: 'date', storage: 'narrow' }, type: 'Flag' }, - ], - }, - Stamp: { - kind: 'typeConstructor', - args: [{ name: 'precision', kind: 'value', dataType: 'fixture/int', optional: true }], - output: { codecId: 'fixture/stamp' }, - }, - CreatedAt: { - kind: 'typeAlias', - documentation: 'Set to now when a row is created.', - output: { type: { codecId: 'fixture/text' }, onCreate: { name: 'now', args: {} } }, - }, - }, - }, - field: { - pg: { - Serial: { - kind: 'fieldPreset', - args: [{ name: 'start', kind: 'number' }], - output: { codecId: 'fixture/serial' }, - }, - }, - }, - }, - }, -]); -const pslBlockDescriptors: AuthoringPslBlockDescriptorNamespace = { - policy: { - kind: 'pslBlock', - documentation: 'Row-level access rule.', - keyword: 'policy', - discriminator: 'hover-policy', - name: { required: true }, - spec: () => - structBlock({ - parameters: { - using: { type: str(), documentation: 'The policy filter expression.' }, - }, - }), - attributes: { someBlockAttribute: () => someBlockAttribute }, - }, - view: { - kind: 'pslBlock', - keyword: 'view', - discriminator: 'hover-view', - name: { required: true }, - spec: () => structBlock({ parameters: {} }), - }, -}; - -const fixtureInt = dataType('fixture/int', { read: (json) => json }); -const fixtureExpression = dataType('fixture/expression', { read: (json) => json }); -const dataTypes: DataTypeSupport = { - entries: { - [fixtureInt.id]: { - written: { - kind: 'plain', - syntax: 'number', - types: [fixtureInt.id], - classify: (text) => ({ type: fixtureInt.id, value: Number(text) }), - }, - print: String, - documentation: 'A whole number.', - }, - [fixtureExpression.id]: { - written: { kind: 'tag', tag: 'sql', parse: (text) => text }, - print: String, - documentation: 'A SQL expression.', - }, - }, - lookup: createDataTypeLookup([fixtureInt, fixtureExpression]), - dataTypeOfCodec: () => undefined, -}; - -function hover(markedSource: string, siblings: readonly string[] = []) { - const offset = markedSource.indexOf('|'); - expect(offset).toBeGreaterThanOrEqual(0); - const source = markedSource.slice(0, offset) + markedSource.slice(offset + 1); - const parsed = parse(source, 'language-server-test.psl'); - const { document } = parsed; - const others = siblings.map((text, index) => parse(text, `sibling-${index}.psl`)); - const sources = parsed.sources.merge(...others.map((other) => other.sources)); - const sourceFile = sources.sourceFileFor(document.syntax); - const { symbolTable } = buildSymbolTable({ - documents: [...others.map((other) => other.document), document], - sources, - defaultNamespaceId: 'public', - }); - return providePslHover({ - document, - sourceFile, - position: sourceFile.positionAt(offset), - binder: testBinder({ sources, symbolTable, authoringContributions, pslBlockDescriptors }), - pslBlockDescriptors, - dataTypes, - }); -} +import { hover } from './helpers/hover'; describe('providePslHover', () => { it('shows a model declaration line and doc at a reference', () => { @@ -502,239 +226,4 @@ describe('providePslHover', () => { const result = hover('model User {| id Int\n}'); expect(result).toBeNull(); }); - - it('shows a field-attribute signature label and its documentation', () => { - const result = hover('model User {\n id Int\n author User @relat|ion\n}'); - expect(result).toEqual({ - contents: { - kind: 'markdown', - value: '```prisma\n@relation(name?: string)\n```\n\nDeclares a relation.', - }, - range: { start: { line: 2, character: 15 }, end: { line: 2, character: 23 } }, - }); - }); - - it('shows a model-attribute signature label and its documentation', () => { - const result = hover('model User {\n id Int\n @@ind|ex\n}'); - expect(result).toEqual({ - contents: { - kind: 'markdown', - value: '```prisma\n@@index(fields: string[])\n```\n\nDeclares an index.', - }, - range: { start: { line: 2, character: 4 }, end: { line: 2, character: 9 } }, - }); - }); - - it('omits the documentation section when spec.documentation is empty', () => { - const result = hover('model User {\n id Int\n author User @relates|To(fields: id)\n}'); - expect(result).toEqual({ - contents: { kind: 'markdown', value: '```prisma\n@relatesTo(fields: field name)\n```' }, - range: { start: { line: 2, character: 15 }, end: { line: 2, character: 24 } }, - }); - }); - - it('shows a contributed type with documentation and args', () => { - const result = hover('model Product {\n price pg.Varc|har(255)\n}'); - expect(result).toEqual({ - contents: { - kind: 'markdown', - value: '```prisma\npg.Varchar(length: a number)\n```\n\nA variable-length string.', - }, - range: { start: { line: 1, character: 11 }, end: { line: 1, character: 18 } }, - }); - }); - - it('shows a contributed type alias with its documentation', () => { - const result = hover('model Product {\n createdAt pg.Created|At\n}'); - expect(result).toEqual({ - contents: { - kind: 'markdown', - value: '```prisma\npg.CreatedAt\n```\n\nSet to now when a row is created.', - }, - range: { start: { line: 1, character: 15 }, end: { line: 1, character: 24 } }, - }); - }); - - it('shows a contributed type without documentation as the bare path', () => { - const result = hover('model Product {\n label pg.Te|xt\n}'); - expect(result).toEqual({ - contents: { kind: 'markdown', value: '```prisma\npg.Text\n```' }, - range: { start: { line: 1, character: 11 }, end: { line: 1, character: 15 } }, - }); - }); - - it('omits the documentation section when descriptor.documentation is empty', () => { - const result = hover('model Product {\n flag pg.Fl|ag\n}'); - expect(result).toEqual({ - contents: { kind: 'markdown', value: '```prisma\npg.Flag\n```' }, - range: { start: { line: 1, character: 10 }, end: { line: 1, character: 14 } }, - }); - }); - - it('shows a field-preset contributed type as the fence only, with its arg labels', () => { - const result = hover('model Counter {\n value pg.Ser|ial(1)\n}'); - expect(result).toEqual({ - contents: { kind: 'markdown', value: '```prisma\npg.Serial(start: number)\n```' }, - range: { start: { line: 1, character: 11 }, end: { line: 1, character: 17 } }, - }); - }); - - it('shows a value argument by the forms its data type is written in', () => { - const result = hover('model Product {\n labels pg.Ra|w(sql`text`)\n}'); - expect(result).toEqual({ - contents: { - kind: 'markdown', - value: '```prisma\npg.Raw(sql: sql`...`)\n```\n\nA type written as SQL.', - }, - range: { start: { line: 1, character: 12 }, end: { line: 1, character: 15 } }, - }); - }); - - it('shows an option contributed-type arg as its joined allowed values', () => { - const result = hover('model Product {\n onDelete pg.Mo|de(cascade)\n}'); - expect(result).toEqual({ - contents: { - kind: 'markdown', - value: "```prisma\npg.Mode(mode: 'cascade' | 'restrict')\n```\n\nA deletion mode.", - }, - range: { start: { line: 1, character: 14 }, end: { line: 1, character: 18 } }, - }); - }); - - it('shows a type choice with its choices as written and the explicit type each becomes', () => { - const result = hover('model Product {\n at pg.Mo|ment(as: text)\n}'); - expect(result?.contents).toEqual({ - kind: 'markdown', - value: [ - '```prisma', - 'pg.Moment(as?: date | text, storage?: wide | narrow, precision?: a number)', - '```', - '', - 'A moment in time.', - '', - '| as | storage | Becomes |', - '| --- | --- | --- |', - '| date (default) | wide (default) | `Stamp(precision: 3)` |', - '| text | wide (default) | `Text` |', - '| date (default) | narrow | `Flag` |', - ].join('\n'), - }); - }); - - it('shows a block keyword with documentation', () => { - const result = hover('polic|y ReadOwn {\n}'); - expect(result).toEqual({ - contents: { kind: 'markdown', value: 'Row-level access rule.' }, - range: { start: { line: 0, character: 0 }, end: { line: 0, character: 6 } }, - }); - }); - - it('returns null for a block keyword without documentation', () => { - const result = hover('vie|w Summary {\n}'); - expect(result).toBeNull(); - }); - - it('shows a named attribute-argument key and its documentation', () => { - const result = hover('model User {\n author User @relation(nam|e: "x")\n}'); - expect(result).toEqual({ - contents: { - kind: 'markdown', - value: '```prisma\nname?: string\n```\n\nAn explicit relation name.', - }, - range: { start: { line: 1, character: 24 }, end: { line: 1, character: 28 } }, - }); - }); - - it('shows a named attribute-argument key while its value is missing', () => { - const result = hover('model User {\n author User @incompleteKey(referen|ces: )\n}'); - expect(result).toEqual({ - contents: { - kind: 'markdown', - value: '```prisma\nreferences?: string\n```\n\nReferenced fields.', - }, - range: { start: { line: 1, character: 29 }, end: { line: 1, character: 39 } }, - }); - }); - - it('omits the documentation section for a parameter with empty documentation', () => { - const result = hover('model User {\n id Int\n author User @relatesTo(field|s: id)\n}'); - expect(result).toEqual({ - contents: { kind: 'markdown', value: '```prisma\nfields: field name\n```' }, - range: { start: { line: 2, character: 25 }, end: { line: 2, character: 31 } }, - }); - }); - - it('shows a struct-block entry key and its documentation', () => { - const result = hover('policy ReadOwn {\n usi|ng = "x"\n}'); - expect(result).toEqual({ - contents: { - kind: 'markdown', - value: '```prisma\nusing: string\n```\n\nThe policy filter expression.', - }, - range: { start: { line: 1, character: 2 }, end: { line: 1, character: 7 } }, - }); - }); - - it('shows a function name without args and its documentation', () => { - const result = hover('model User {\n id Int @default(autoincreme|nt())\n}'); - expect(result).toEqual({ - contents: { - kind: 'markdown', - value: '```prisma\nautoincrement()\n```\n\nGenerates sequential integers.', - }, - range: { start: { line: 1, character: 18 }, end: { line: 1, character: 31 } }, - }); - }); - - it('shows a function name past leading scalar leaves, in the real @default arm order', () => { - const result = hover('model User {\n id Int @defaultRealOrder(autoincreme|nt())\n}'); - expect(result).toEqual({ - contents: { - kind: 'markdown', - value: '```prisma\nautoincrement()\n```\n\nGenerates sequential integers.', - }, - range: { start: { line: 1, character: 27 }, end: { line: 1, character: 40 } }, - }); - }); - - it('shows a function name with args and its documentation', () => { - const result = hover('model User {\n value String @default(dbgenerat|ed(expr: "now()"))\n}'); - expect(result).toEqual({ - contents: { - kind: 'markdown', - value: '```prisma\ndbgenerated(expr: string)\n```\n\nLets the database generate the value.', - }, - range: { start: { line: 1, character: 24 }, end: { line: 1, character: 35 } }, - }); - }); - - it('shows a named function-call-argument key and its documentation', () => { - const result = hover('model User {\n value String @default(dbgenerated(ex|pr: "now()"))\n}'); - expect(result).toEqual({ - contents: { - kind: 'markdown', - value: '```prisma\nexpr: string\n```\n\nThe database expression.', - }, - range: { start: { line: 1, character: 36 }, end: { line: 1, character: 40 } }, - }); - }); - - it('shows a constant not first in a oneOf and its documentation', () => { - const result = hover('model User {\n author User @onDeleteExample(onDelete: Casc|ade)\n}'); - expect(result).toEqual({ - contents: { kind: 'markdown', value: '```prisma\nCascade\n```\n\nCascades the delete.' }, - range: { start: { line: 1, character: 41 }, end: { line: 1, character: 48 } }, - }); - }); - - it('shows a block attribute and its documentation', () => { - const result = hover('policy ReadOwn {\n @@someBlockAttri|bute("x")\n}'); - expect(result).toEqual({ - contents: { - kind: 'markdown', - value: '```prisma\n@@someBlockAttribute(string)\n```\n\nDoes something.', - }, - range: { start: { line: 1, character: 4 }, end: { line: 1, character: 22 } }, - }); - }); }); From 8bc20aa595064fd2e8987e179f3700d86875003a Mon Sep 17 00:00:00 2001 From: willbot Date: Fri, 23 Oct 2026 18:28:23 +0200 Subject: [PATCH 094/118] Ask the column codec whether contract infer can print a default exactly contract infer kept its own table of timestamp stored-text functions and compared digits itself to decide that a Date cannot hold a default. It now reads each body with the column's data type and asks the codec's fromDataTypeValue, as PSL and the Prisma 7 reader do, so a codec that cannot hold a value exactly is honoured without a change to infer. The JSON number check stays, since the JSON codec rounds while it parses the body. The unit test for a fake codec whose data type does not read a body is replaced by one for a codec that refuses the value its data type reads. Co-Authored-By: Claude Opus 5.5 Signed-off-by: willbot Signed-off-by: Will Madden --- .../adrs/ADR 254 - Data types and casts.md | 2 +- .../src/core/psl-infer/infer-read-defaults.ts | 63 +++++++------------ .../inferred-psl.data-type-defaults.test.ts | 19 +++--- 3 files changed, 32 insertions(+), 52 deletions(-) diff --git a/docs/architecture docs/adrs/ADR 254 - Data types and casts.md b/docs/architecture docs/adrs/ADR 254 - Data types and casts.md index df71ad3c6d26..89a0c85cf7a8 100644 --- a/docs/architecture docs/adrs/ADR 254 - Data types and casts.md +++ b/docs/architecture docs/adrs/ADR 254 - Data types and casts.md @@ -231,7 +231,7 @@ Every consumer of a stored default reads it through the column's data type and c - **Reading a default the database reports.** Postgres reports a default as a literal with a cast, `'2024-01-01 00:00:00+00'::timestamp with time zone`; SQLite reports the literal its `DEFAULT` clause holds. The target takes the literal's body out of that text, which is what its driver would hand `fromWire` for a row of the column, and introspection reads it with the column's codec, `fromWire` then `toDataTypeValue`. Introspection is asynchronous and receives the contract, so this runs there, and the tree it builds holds the value ([ADR 235](ADR%20235%20-%20The%20schema%20differ%20walks%20two%20derived%20schema%20IRs.md): each side of a diff is built in the form it is compared in). A default the database holds as an expression, such as `now()`, is compared as expression text. A contract default written as a `sql` expression that holds only a literal, such as `'hello'::bytea`, has its literal read the same way, so the two sides compare as values; text is compared only where no codec can read a literal, and then case and spaces inside quotes count. A literal the codec cannot read, such as a value written outside Prisma that the application value cannot hold, is reported as a difference naming the column and the codec's message. - **Comparing defaults.** `db verify` and the planner compare two values of the column's type for equality. A `Date` default is a value of its column's type like any other: the database's default is read through the column's codec, and the comparison never goes through `toISOString()`. On a column whose codec reads a `Date`, both sides are read as the application reads them, so a difference below a millisecond is not reported. When no database is in reach, both sides come from contracts and no codec is needed. - **Rows the database returns as JSON.** An `include` or an aggregated child row set comes back as JSON. Each column enters it through its codec's JSON projection ([codec authoring guide](../../reference/codec-authoring-guide.md#rows-the-database-returns-as-json)), and the runtime reads it with `fromWire`, the same method that reads an ordinary row. A projection puts into the JSON a value `fromWire` reads exactly, giving the same application value as an ordinary row. On Postgres a type whose value would enter the JSON in another form than its row's text is cast to text, `CAST(x AS text)`, including `json`, `jsonb` and vectors: a document embedded as JSON would come back already parsed, so `fromWire` would parse the string document `"42"` a second time, and a vector widened to double precision would read `0.10000000149011612` where a row reads `0.1`. A type whose JSON value `fromWire` already reads, such as a number, a boolean, or text like a uuid or an enum label, enters as the column itself, and its `fromWire` reads both forms; `pg/char` enters with its padding removed. The runtime has one way to read what the database returns, and the stored form never has to match the database's spelling. -- **Inferring a contract.** `contract infer` has no contract, so it reads a reported default with the codec of the type constructor it prints for the column, `fromWire` of the literal's body then `toDataTypeValue`, and an enum column's through `pg/text@1`. It prints a reported literal only when the codec reads it exactly, as it does for a JSON number the codec would round, and otherwise prints the raw expression. It prints the value as "Printing" describes. +- **Inferring a contract.** `contract infer` has no contract, so it reads a reported default with the codec of the type constructor it prints for the column, `fromWire` of the literal's body then `toDataTypeValue`, and an enum column's through `pg/text@1`. It prints a reported literal only when the codec holds it exactly, and otherwise prints the raw expression. Where the column's data type reads the body's text, infer asks the codec's `fromDataTypeValue`, as `contract emit` asks of a written default, so a `Date` codec's refusal of digits below a millisecond reaches infer from the codec. A JSON number keeps its own check, because the JSON codec parses the document's text into JavaScript numbers when it reads the body, so a number such as `12345678901234567890` is rounded before any value reaches `fromDataTypeValue`. It prints the value as "Printing" describes. ## How a data type names its database type diff --git a/packages/3-targets/postgres/1-target/src/core/psl-infer/infer-read-defaults.ts b/packages/3-targets/postgres/1-target/src/core/psl-infer/infer-read-defaults.ts index abb1afaf44cf..a743cd3d987a 100644 --- a/packages/3-targets/postgres/1-target/src/core/psl-infer/infer-read-defaults.ts +++ b/packages/3-targets/postgres/1-target/src/core/psl-infer/infer-read-defaults.ts @@ -10,18 +10,10 @@ import { type SqlPslBuildContext, } from '@internal/family-sql/control'; import type { ColumnTypeOf } from '@internal/family-sql/psl-build'; -import { type Codec, codecForRef } from '@internal/framework-components/codec'; +import { type Codec, codecForRef, type DataTypeValue } from '@internal/framework-components/codec'; import type { SqlColumnIR, SqlSchemaIR } from '@internal/sql-schema-ir/types'; import { isInternalError } from '@internal/utils/internal-error'; -import { isStructuredError } from '@internal/utils/structured-error'; -import { - pgJson, - pgJsonb, - pgTimestamp, - pgTimestampStoredText, - pgTimestamptz, - pgTimestamptzStoredText, -} from '../data-types'; +import { pgJson, pgJsonb } from '../data-types'; import { readPostgresDefault } from '../default-normalizer'; import { inferredCodecRef } from './infer-default-codec'; import { jsonKeepsEveryNumber } from './json-exact-numbers'; @@ -97,44 +89,31 @@ export async function readDefaultWithCodec( if (JSON_TYPES.has(codec.dataType.id) && !bodiesOf(reported.literal).every(jsonBodyIsExact)) { return rawExpression; } - const read = await readReportedDefault(codec, reported.literal, raw); - if (!('resolvedDefault' in read)) return rawExpression; - const storedText = TIMESTAMP_STORED_TEXT.get(codec.dataType.id); - if ( - storedText !== undefined && - !readsEveryDigit(storedText, reported.literal, read.resolvedDefault) - ) { + if (!bodiesOf(reported.literal).every((body) => codecHoldsExactly(codec, body))) { return rawExpression; } - return read.resolvedDefault; -} - -/** The value each timestamp type holds for the text PostgreSQL prints. A `Date` codec of these types reads only milliseconds. */ -const TIMESTAMP_STORED_TEXT: ReadonlyMap string> = new Map([ - [pgTimestamp.id, pgTimestampStoredText], - [pgTimestamptz.id, pgTimestamptzStoredText], -]); - -/** Whether the codec read each body as the value the column holds, so a `Date` codec's millisecond reading of a microsecond default is printed as the raw expression instead. */ -function readsEveryDigit( - storedText: (text: string) => string, - literal: ReportedLiteralDefault, - read: ColumnDefault, -): boolean { - const value = read.kind === 'literal' ? read.value : undefined; - const values = literal.kind === 'value' ? [value] : Array.isArray(value) ? value : []; - return bodiesOf(literal).every( - (body, index) => typeof body !== 'string' || storedTextOf(storedText, body) === values[index], - ); + const read = await readReportedDefault(codec, reported.literal, raw); + return 'resolvedDefault' in read ? read.resolvedDefault : rawExpression; } -/** The value the column holds for `body`, or `undefined` when its type does not read it. */ -function storedTextOf(storedText: (text: string) => string, body: string): string | undefined { +/** + * Whether the codec's application value holds `body` exactly, as `contract emit` asks of a written default: the codec's `fromDataTypeValue` refuses a value it cannot hold exactly, such as a `Date` codec a time with digits below a millisecond. A body the data type does not read as text is left to the codec's wire reading. + */ +function codecHoldsExactly(codec: Codec, body: ReportedBody | null): boolean { + if (typeof body !== 'string') return true; + let value: DataTypeValue; try { - return storedText(body); + value = codec.dataType.valueFrom(body, {}); } catch (error) { - if (isStructuredError(error)) return undefined; - throw error; + if (isInternalError(error)) throw error; + return true; + } + try { + codec.fromDataTypeValue(value); + return true; + } catch (error) { + if (isInternalError(error)) throw error; + return false; } } diff --git a/packages/3-targets/postgres/1-target/test/psl-infer/inferred-psl/inferred-psl.data-type-defaults.test.ts b/packages/3-targets/postgres/1-target/test/psl-infer/inferred-psl/inferred-psl.data-type-defaults.test.ts index 97f161bdda01..fd53f41ebeda 100644 --- a/packages/3-targets/postgres/1-target/test/psl-infer/inferred-psl/inferred-psl.data-type-defaults.test.ts +++ b/packages/3-targets/postgres/1-target/test/psl-infer/inferred-psl/inferred-psl.data-type-defaults.test.ts @@ -188,21 +188,22 @@ describe('printPsl writes each default as the literal the column data type takes }); }); - it('prints the raw expression when the column type does not read a body its codec read', async () => { + it('prints the raw expression when the codec refuses the value its data type reads from the body', async () => { const codec = { - id: 'test/lenient-timestamp@1', + id: 'test/strict-timestamp@1', dataType: pgTimestamp, fromWire: async () => new Date('2024-01-01T00:00:00Z'), toDataTypeValue: () => pgTimestamp.valueFrom('2024-01-01T00:00:00', {}), - fromDataTypeValue: () => new Date('2024-01-01T00:00:00Z'), + fromDataTypeValue: () => { + throw new Error('the application value cannot hold this instant exactly'); + }, toWire: async () => '', } as unknown as Codec; - expect(await readDefaultWithCodec("'soon'::timestamp without time zone", false, codec)).toEqual( - { - kind: 'function', - expression: "'soon'::timestamp without time zone", - }, - ); + const raw = "'2024-01-01 00:00:00'::timestamp without time zone"; + expect(await readDefaultWithCodec(raw, false, codec)).toEqual({ + kind: 'function', + expression: raw, + }); }); it('prints a timestamp default written with an offset as the instant, when a Date holds it', async () => { From 1c8b383f4c78cbdf4d85876431a26b33c56664f5 Mon Sep 17 00:00:00 2001 From: willbot Date: Fri, 23 Oct 2026 18:34:07 +0200 Subject: [PATCH 095/118] Write a Prisma 7 default the codec refuses with the adapter's literal rendering The Prisma 7 binding built its own quoted literals and arrays for a default whose value the codec cannot hold exactly, beside the adapter's renderer of the same values. The renderer moves to the target's SQL utilities, where the adapter and the binding both use it; the adapter still writes a Date as its ISO text and refuses an invalid one. The binding member is renamed literalDefaultExpression, for what it returns. A text default is no longer cast to text, as DDL writes it. Co-Authored-By: Claude Opus 5.5 Signed-off-by: willbot Signed-off-by: Will Madden --- .../2-authoring/contract-prisma7/README.md | 2 +- .../contract-prisma7/src/defaults.ts | 6 +- .../contract-prisma7/src/interpreter.ts | 4 +- .../contract-prisma7/src/target-binding.ts | 2 +- .../1-target/src/core/prisma7-binding.ts | 20 +++--- .../postgres/1-target/src/core/sql-utils.ts | 54 ++++++++++++++++ .../1-target/src/exports/sql-utils.ts | 1 + .../prisma7-binding.refused-defaults.test.ts | 12 ++-- .../2-adapter/src/core/control-adapter.ts | 64 +++---------------- ...fused-defaults-migrate.integration.test.ts | 6 +- 10 files changed, 91 insertions(+), 80 deletions(-) diff --git a/packages/2-sql-family/2-authoring/contract-prisma7/README.md b/packages/2-sql-family/2-authoring/contract-prisma7/README.md index 4b1c65e1e32c..23fc7ea0bc80 100644 --- a/packages/2-sql-family/2-authoring/contract-prisma7/README.md +++ b/packages/2-sql-family/2-authoring/contract-prisma7/README.md @@ -81,7 +81,7 @@ Explicit relations keep their fields, references, and actions; an omitted `onDel ## Defaults, generators, `@updatedAt`, and indexes -`@default(autoincrement())` and `@default(now())` become column defaults through the target's default function registry (`context.controlMutationDefaults`). `@default(now())` also becomes the `now` generator on create, resolved for the column's codec through the generator function registry, as Prisma 8 PSL resolves `@default(now()) @onCreate(now())`: Prisma 7's client filled the value itself and wrote the column default for other writers, so a create through Prisma 8 writes UTC whatever the session time zone, and an `INSERT` from outside Prisma still gets the column default. On a field that is not a `DateTime`, or on a `DateTime` field whose native type is not a date or time (`@db.Text`), it is `PSL.PRISMA7_UNKNOWN_DEFAULT`, as Prisma 7 refuses both. The ORM-side generators `uuid()`, `uuid(4)`, `uuid(7)`, `ulid()`, `nanoid()`, `nanoid(n)`, `cuid()`, and `cuid(2)` become execution generators on create with no column default. `cuid()` maps to `cuid2` because Prisma 7's `cuid()` is cuid v1, which Prisma 8 does not ship; the column type is the same and the ids are opaque, so existing rows keep working beside new ones. Every other call's arguments are bound through the target's signatures, as Prisma 8 PSL binds them: an argument the function does not take (`uuid(5)`, `cuid(1)`, `nanoid(1)`, `nanoid(1000)`, `nanoid(8.5)`) or a named argument (`nanoid(size: 8)`) is `PSL.PRISMA7_UNKNOWN_DEFAULT` at the argument, with the function's message, such as `Field "User.id": uuid: version must be 4 or 7, got 5`. `@default(dbgenerated(""))` becomes a raw SQL default carrying the string as written, without going through the registry; `@default(dbgenerated())` with no argument means the column has no default, on required, optional, and list fields alike. Literals of every scalar, list literals, and enum members (the member's mapped storage value) become literal defaults; a number literal is carried as the value the column codec reads: the number itself when the codec reads a JSON number, otherwise what the codec decodes from the literal exactly as written, so `Decimal` and `BigInt` defaults keep every digit and trailing zero (`"1.50"`, `"9007199254740993"`); a number with a fraction on an `Int` or `BigInt` field, or one the codec reads neither way, is `PSL.PRISMA7_UNKNOWN_DEFAULT`, as Prisma 7 rejects it, and the message says an `Int` or `BigInt` default must be a whole number; `Json` literals are parsed, and `Bytes` and `DateTime` literals are read through the column type's tag, as the current schema language reads them, and stored as values: a `Bytes` default's base64 as the hex body of `bytea`, stored as base64, and a `DateTime` default as the body that keeps what it means on the column: the written date on `date`, the written wall-clock time without its offset on `timestamp` and `time`, the time with its offset on `timetz`, and the written instant on `timestamptz`, stored in UTC (`2024-01-02T01:04:05Z`). A value with more fraction digits than its column holds (three on a plain `DateTime`, which is `timestamp(3)`) is `PSL.PRISMA7_UNKNOWN_DEFAULT`, and the message names the value PostgreSQL stores instead, rounded as PostgreSQL rounds it, to write in its place. Second 60, which PostgreSQL carries into the next minute, is refused with the tag's reason. A `Bytes[]` or `DateTime[]` list default stores each element the same way. A value the column's type holds but its codec cannot hold exactly, such as a `DateTime` default with digits below a millisecond on a column read as a JavaScript `Date`, is kept as the SQL expression the target binding writes for it (`refusedLiteralDefault`), for the whole default, list or not. `@updatedAt` becomes an ORM-side "now" generator on create and update; the generator function registry picks the generator from the column's codec and type parameters, and on Postgres every date and time column type has one, each writing the current UTC time as the column's codec reads it (`timestampNow`, a `Date`, for a plain `DateTime` and a `@db.Timestamptz`, `dateStringNow` for `@db.Date`, and so on); the timestamp and `@db.Time(p)` and `@db.Timetz(p)` generators get the column's precision as their `precision` param and round down to it, so the client holds the value Postgres stores, which never rounds a `@db.Timestamp(0)` value up to the next second or a time up to `24:00:00`. `@updatedAt` on a field that is not a `DateTime`, on a list field, or on a `DateTime` field whose native type is not a date or time (`@db.Text`), is `PSL.PRISMA7_UPDATED_AT_FIELD_INVALID`, as Prisma 7 refuses all three. `@updatedAt` beside `@default(now())` keeps the column default as well, so writes outside the ORM still get a value. A generator or `@updatedAt` on an optional field gives a nullable column: a value the client leaves out is generated, and an explicit `null` is stored as `null`, as in Prisma 7. List columns decline the element-not-null check Prisma 8 would otherwise derive, because Prisma 7 creates none. +`@default(autoincrement())` and `@default(now())` become column defaults through the target's default function registry (`context.controlMutationDefaults`). `@default(now())` also becomes the `now` generator on create, resolved for the column's codec through the generator function registry, as Prisma 8 PSL resolves `@default(now()) @onCreate(now())`: Prisma 7's client filled the value itself and wrote the column default for other writers, so a create through Prisma 8 writes UTC whatever the session time zone, and an `INSERT` from outside Prisma still gets the column default. On a field that is not a `DateTime`, or on a `DateTime` field whose native type is not a date or time (`@db.Text`), it is `PSL.PRISMA7_UNKNOWN_DEFAULT`, as Prisma 7 refuses both. The ORM-side generators `uuid()`, `uuid(4)`, `uuid(7)`, `ulid()`, `nanoid()`, `nanoid(n)`, `cuid()`, and `cuid(2)` become execution generators on create with no column default. `cuid()` maps to `cuid2` because Prisma 7's `cuid()` is cuid v1, which Prisma 8 does not ship; the column type is the same and the ids are opaque, so existing rows keep working beside new ones. Every other call's arguments are bound through the target's signatures, as Prisma 8 PSL binds them: an argument the function does not take (`uuid(5)`, `cuid(1)`, `nanoid(1)`, `nanoid(1000)`, `nanoid(8.5)`) or a named argument (`nanoid(size: 8)`) is `PSL.PRISMA7_UNKNOWN_DEFAULT` at the argument, with the function's message, such as `Field "User.id": uuid: version must be 4 or 7, got 5`. `@default(dbgenerated(""))` becomes a raw SQL default carrying the string as written, without going through the registry; `@default(dbgenerated())` with no argument means the column has no default, on required, optional, and list fields alike. Literals of every scalar, list literals, and enum members (the member's mapped storage value) become literal defaults; a number literal is carried as the value the column codec reads: the number itself when the codec reads a JSON number, otherwise what the codec decodes from the literal exactly as written, so `Decimal` and `BigInt` defaults keep every digit and trailing zero (`"1.50"`, `"9007199254740993"`); a number with a fraction on an `Int` or `BigInt` field, or one the codec reads neither way, is `PSL.PRISMA7_UNKNOWN_DEFAULT`, as Prisma 7 rejects it, and the message says an `Int` or `BigInt` default must be a whole number; `Json` literals are parsed, and `Bytes` and `DateTime` literals are read through the column type's tag, as the current schema language reads them, and stored as values: a `Bytes` default's base64 as the hex body of `bytea`, stored as base64, and a `DateTime` default as the body that keeps what it means on the column: the written date on `date`, the written wall-clock time without its offset on `timestamp` and `time`, the time with its offset on `timetz`, and the written instant on `timestamptz`, stored in UTC (`2024-01-02T01:04:05Z`). A value with more fraction digits than its column holds (three on a plain `DateTime`, which is `timestamp(3)`) is `PSL.PRISMA7_UNKNOWN_DEFAULT`, and the message names the value PostgreSQL stores instead, rounded as PostgreSQL rounds it, to write in its place. Second 60, which PostgreSQL carries into the next minute, is refused with the tag's reason. A `Bytes[]` or `DateTime[]` list default stores each element the same way. A value the column's type holds but its codec cannot hold exactly, such as a `DateTime` default with digits below a millisecond on a column read as a JavaScript `Date`, is kept as the SQL expression the target binding writes for it (`literalDefaultExpression`), for the whole default, list or not. `@updatedAt` becomes an ORM-side "now" generator on create and update; the generator function registry picks the generator from the column's codec and type parameters, and on Postgres every date and time column type has one, each writing the current UTC time as the column's codec reads it (`timestampNow`, a `Date`, for a plain `DateTime` and a `@db.Timestamptz`, `dateStringNow` for `@db.Date`, and so on); the timestamp and `@db.Time(p)` and `@db.Timetz(p)` generators get the column's precision as their `precision` param and round down to it, so the client holds the value Postgres stores, which never rounds a `@db.Timestamp(0)` value up to the next second or a time up to `24:00:00`. `@updatedAt` on a field that is not a `DateTime`, on a list field, or on a `DateTime` field whose native type is not a date or time (`@db.Text`), is `PSL.PRISMA7_UPDATED_AT_FIELD_INVALID`, as Prisma 7 refuses all three. `@updatedAt` beside `@default(now())` keeps the column default as well, so writes outside the ORM still get a value. A generator or `@updatedAt` on an optional field gives a nullable column: a value the client leaves out is generated, and an explicit `null` is stored as `null`, as in Prisma 7. List columns decline the element-not-null check Prisma 8 would otherwise derive, because Prisma 7 creates none. `@unique` and `@@unique` become unique indexes named `{table}_{columns}_key` and `@@index` becomes an index named `{table}_{columns}_idx`, `map` overriding either (`name` on `@@unique` is the client-side name and is ignored). A generated name is cut the way Prisma 7 cuts it to fit PostgreSQL's 63-byte identifier limit: the `{table}_{columns}` part is shortened to 63 bytes minus the suffix, on a character boundary, and the suffix stays whole (`AVeryLongModelNameThatKeepsGoingAndGoingForever_aVeryLongCo_idx`). The same rule cuts an implicit junction's table name (no suffix) and its `_B_index`. `db verify` compares indexes by name, so the contract must carry the name Prisma 7 created. `type: Hash` and the other Prisma 8 index types map through; field arguments such as `sort` and `length`, and `ops`, are `PSL.PRISMA7_INDEX_ARGUMENT_UNSUPPORTED` because Prisma 8 indexes carry none. diff --git a/packages/2-sql-family/2-authoring/contract-prisma7/src/defaults.ts b/packages/2-sql-family/2-authoring/contract-prisma7/src/defaults.ts index f1bc49d30132..aadc96948f9b 100644 --- a/packages/2-sql-family/2-authoring/contract-prisma7/src/defaults.ts +++ b/packages/2-sql-family/2-authoring/contract-prisma7/src/defaults.ts @@ -58,8 +58,8 @@ export interface LowerPrisma7DefaultInput { readonly nativeTypeName: string | undefined; readonly codecLookup: CodecLookupWithDescriptors; readonly literalForm: Prisma7LiteralDefaultForm | undefined; - /** The SQL expression of a default whose value the column's codec cannot hold exactly; see `Prisma7TargetBinding.refusedLiteralDefault`. */ - readonly refusedLiteralDefault: (stored: JsonValue) => string; + /** The SQL expression of a default whose value the column's codec cannot hold exactly; see `Prisma7TargetBinding.literalDefaultExpression`. */ + readonly literalDefaultExpression: (stored: JsonValue) => string; /** Storage value per member name when the field is typed by a Prisma 7 enum. */ readonly enumMembers: ReadonlyMap | undefined; readonly controlMutationDefaults: ControlMutationDefaults; @@ -183,7 +183,7 @@ function scalarLiteral( if (read.ok) return { value: read.value }; const { refusal } = read; if (refusal.kind === 'refused-by-codec') { - return { kind: 'function', expression: input.refusedLiteralDefault(refusal.stored) }; + return { kind: 'function', expression: input.literalDefaultExpression(refusal.stored) }; } return unknown(refusalReason(refusal), span); } diff --git a/packages/2-sql-family/2-authoring/contract-prisma7/src/interpreter.ts b/packages/2-sql-family/2-authoring/contract-prisma7/src/interpreter.ts index 6ca95344072d..c8862e738207 100644 --- a/packages/2-sql-family/2-authoring/contract-prisma7/src/interpreter.ts +++ b/packages/2-sql-family/2-authoring/contract-prisma7/src/interpreter.ts @@ -1425,8 +1425,8 @@ function readField(args: ReadFieldArgs): void { dataType: sqlDataTypeOfCodec(resolved.descriptor.codecId, typeLookups).id, typeParams: resolved.descriptor.typeParams, }), - refusedLiteralDefault: (stored) => - binding.refusedLiteralDefault(stored, { + literalDefaultExpression: (stored) => + binding.literalDefaultExpression(stored, { codecId: resolved.descriptor.codecId, dataType: sqlDataTypeOfCodec(resolved.descriptor.codecId, typeLookups).id, typeParams: resolved.descriptor.typeParams, diff --git a/packages/2-sql-family/2-authoring/contract-prisma7/src/target-binding.ts b/packages/2-sql-family/2-authoring/contract-prisma7/src/target-binding.ts index 7cbd34dd20b7..efcaa1db3819 100644 --- a/packages/2-sql-family/2-authoring/contract-prisma7/src/target-binding.ts +++ b/packages/2-sql-family/2-authoring/contract-prisma7/src/target-binding.ts @@ -64,5 +64,5 @@ export interface Prisma7TargetBinding { /** How a literal `@default` on the column is read; `undefined` when the codec takes the written value. */ readonly literalDefaultForm: (column: Prisma7ColumnType) => Prisma7LiteralDefaultForm | undefined; /** The SQL expression of a default whose value the column's type holds but its codec cannot hold exactly, such as a timestamp with digits below a millisecond read as a JavaScript `Date`: `stored` is the value, or a list column's elements, as the type stores them. */ - readonly refusedLiteralDefault: (stored: JsonValue, column: Prisma7ColumnType) => string; + readonly literalDefaultExpression: (stored: JsonValue, column: Prisma7ColumnType) => string; } diff --git a/packages/3-targets/postgres/1-target/src/core/prisma7-binding.ts b/packages/3-targets/postgres/1-target/src/core/prisma7-binding.ts index 8957b541f998..21d8a255a3d8 100644 --- a/packages/3-targets/postgres/1-target/src/core/prisma7-binding.ts +++ b/packages/3-targets/postgres/1-target/src/core/prisma7-binding.ts @@ -20,7 +20,7 @@ import { postgresYearText } from './postgres-year-text'; import { prisma7TemporalStored, prisma7TemporalTagBody } from './prisma7-temporal-defaults'; import { prisma7PostgresTypeMap } from './prisma7-type-map'; import { junctionRelationFieldNames } from './psl-infer/junction-relation-field-names'; -import { escapeLiteral } from './sql-utils'; +import { inlineLiteral } from './sql-utils'; /** The date and time types a Prisma 7 `DateTime` default can be written on. */ const DATE_TIME_TYPES: ReadonlySet = new Set([ @@ -62,8 +62,8 @@ function writtenTypeName( return renderSqlTypeName(type, typeParams ?? {}); } -/** A default's SQL: a quoted literal cast to the column type, or for a list an array of them cast to an array of it. A date or time is written with the year text PostgreSQL reads. */ -function refusedLiteralDefault( +/** The SQL expression of a literal default, written as DDL writes the value: one literal, or for a list an array of them cast to an array of the column type. A date or time is written with the year text PostgreSQL reads. */ +function literalDefaultExpression( stored: JsonValue, column: { readonly dataType: string; @@ -71,13 +71,13 @@ function refusedLiteralDefault( }, ): string { const typeName = writtenTypeName(column.dataType, column.typeParams); - const text = (value: string) => - DATE_TIME_TYPES.has(column.dataType) ? postgresYearText(value) : value; - const quoted = (value: JsonValue) => - value === null ? 'NULL' : `'${escapeLiteral(text(String(value)))}'`; + const text = (value: JsonValue) => + typeof value === 'string' && DATE_TIME_TYPES.has(column.dataType) + ? postgresYearText(value) + : value; return Array.isArray(stored) - ? `ARRAY[${stored.map(quoted).join(', ')}]::${typeName}[]` - : `${quoted(stored)}::${typeName}`; + ? inlineLiteral(stored.map(text), `${typeName}[]`) + : inlineLiteral(text(stored), typeName); } /** @@ -105,7 +105,7 @@ export const prisma7PostgresBinding = { identifierMaxBytes: 63, defaultConstraintNames: { primaryKey: defaultPrimaryKeyName, foreignKey: defaultForeignKeyName }, junctionRelationFieldNames, - refusedLiteralDefault, + literalDefaultExpression, literalDefaultForm: ({ dataType, typeParams, diff --git a/packages/3-targets/postgres/1-target/src/core/sql-utils.ts b/packages/3-targets/postgres/1-target/src/core/sql-utils.ts index 0b335d4bf69c..8d1edf6fd1f0 100644 --- a/packages/3-targets/postgres/1-target/src/core/sql-utils.ts +++ b/packages/3-targets/postgres/1-target/src/core/sql-utils.ts @@ -117,3 +117,57 @@ export const POSTGRES_QUOTING = [ { open: "'", close: "'", doubledCloseEscapes: true, backslashEscapes: false }, { open: '"', close: '"', doubledCloseEscapes: true, backslashEscapes: false }, ] as const; + +function isTextLikeTypeText(typeText: string): boolean { + return ( + typeText === 'text' || + typeText === 'varchar' || + typeText.startsWith('varchar(') || + typeText === 'character varying' || + typeText.startsWith('character varying(') || + typeText === 'char' || + typeText.startsWith('char(') || + typeText === 'character' || + typeText.startsWith('character(') + ); +} + +function literalNeedsCast(value: unknown, typeText: string): boolean { + if (typeof value === 'number') return !Number.isFinite(value); + if (typeof value === 'string') return !isTextLikeTypeText(typeText); + return typeof value === 'object' && value !== null; +} + +/** A value's literal without a cast: SQL NULL, a bare number or boolean, or quoted text. */ +function literalText(value: unknown): string { + if (value === null) return 'NULL'; + if (typeof value === 'boolean') return value ? 'true' : 'false'; + if (typeof value === 'number') + return Number.isFinite(value) ? String(value) : `'${String(value)}'`; + if (typeof value === 'bigint') return String(value); + if (typeof value === 'string') return `'${escapeLiteral(value)}'`; + if (value instanceof Uint8Array) { + const hex = Array.from(value) + .map((b) => b.toString(16).padStart(2, '0')) + .join(''); + return `'\\x${hex}'`; + } + if (typeof value === 'object') return `'${escapeLiteral(JSON.stringify(value))}'`; + throw postgresError( + 'CONTRACT.PACK_CONTRIBUTION_INVALID', + `pgRenderDdlExecuteRequest: unexpected wire type "${typeof value}"`, + { meta: { wireType: typeof value } }, + ); +} + +/** + * A value as DDL writes it for a column of `typeText`: `ARRAY[...]` of each element's literal, cast to the list type, for a list written to a list type, and otherwise the value's literal, cast to the column type where PostgreSQL would read it as another type. `value` is what the driver exchanges for the column, with a `Date` already written as text. + */ +export function inlineLiteral(value: unknown, typeText: string): string { + if (Array.isArray(value) && typeText.endsWith('[]')) { + if (value.length === 0) return "'{}'"; + return `ARRAY[${value.map(literalText).join(', ')}]::${typeText}`; + } + const text = literalText(value); + return literalNeedsCast(value, typeText) ? `${text}::${typeText}` : text; +} diff --git a/packages/3-targets/postgres/1-target/src/exports/sql-utils.ts b/packages/3-targets/postgres/1-target/src/exports/sql-utils.ts index 03fa97b0fc8a..1583dea68ebc 100644 --- a/packages/3-targets/postgres/1-target/src/exports/sql-utils.ts +++ b/packages/3-targets/postgres/1-target/src/exports/sql-utils.ts @@ -1,6 +1,7 @@ export { renderFullTextIndexDocument } from '../core/full-text-search-document'; export { escapeLiteral, + inlineLiteral, qualifyName, quoteIdentifier, quoteQualifiedName, diff --git a/packages/3-targets/postgres/1-target/test/prisma7-binding.refused-defaults.test.ts b/packages/3-targets/postgres/1-target/test/prisma7-binding.refused-defaults.test.ts index 0924738b0276..027666738f8b 100644 --- a/packages/3-targets/postgres/1-target/test/prisma7-binding.refused-defaults.test.ts +++ b/packages/3-targets/postgres/1-target/test/prisma7-binding.refused-defaults.test.ts @@ -7,10 +7,10 @@ const column = (dataType: string, typeParams?: Record) => ({ ...(typeParams === undefined ? {} : { typeParams }), }); -describe('the default the Postgres binding writes for values a codec refuses', () => { +describe('the SQL expression the Postgres binding writes for a literal default', () => { it('is a quoted literal cast to the column type, for one value', () => { expect( - prisma7PostgresBinding.refusedLiteralDefault( + prisma7PostgresBinding.literalDefaultExpression( '2024-01-02T03:04:05.123456', column('pg/timestamp', { precision: 6 }), ), @@ -19,16 +19,16 @@ describe('the default the Postgres binding writes for values a codec refuses', ( it('is an array of quoted literals cast to an array of the column type, for a list', () => { expect( - prisma7PostgresBinding.refusedLiteralDefault( + prisma7PostgresBinding.literalDefaultExpression( ['2024-01-02T03:04:05.123456Z', null], column('pg/timestamptz'), ), ).toBe("ARRAY['2024-01-02T03:04:05.123456Z', NULL]::timestamptz[]"); }); - it('escapes a quote in the value', () => { - expect(prisma7PostgresBinding.refusedLiteralDefault("it's", column('pg/text'))).toBe( - "'it''s'::text", + it('escapes a quote in the value, and casts text only where PostgreSQL would read another type', () => { + expect(prisma7PostgresBinding.literalDefaultExpression("it's", column('pg/text'))).toBe( + "'it''s'", ); }); }); diff --git a/packages/3-targets/postgres/2-adapter/src/core/control-adapter.ts b/packages/3-targets/postgres/2-adapter/src/core/control-adapter.ts index 925caca41f17..bea8e729a018 100644 --- a/packages/3-targets/postgres/2-adapter/src/core/control-adapter.ts +++ b/packages/3-targets/postgres/2-adapter/src/core/control-adapter.ts @@ -104,7 +104,7 @@ import { postgresError } from '@internal/target-postgres/errors'; import { MARKER_LOCK_KEY, MARKER_LOCK_SQL } from '@internal/target-postgres/marker-lock'; import { introspectedTypeText } from '@internal/target-postgres/native-type-normalizer'; import { POLICY_COMMAND_SQL, policyRoleList } from '@internal/target-postgres/rls-canonicalize'; -import { escapeLiteral, quoteIdentifier } from '@internal/target-postgres/sql-utils'; +import { escapeLiteral, inlineLiteral, quoteIdentifier } from '@internal/target-postgres/sql-utils'; import { PostgresDatabaseSchemaNode, PostgresNamespaceSchemaNode, @@ -1720,20 +1720,6 @@ function groupBy(items: readonly T[], key: K): Map( return result; } -/** - * A value as DDL writes it: `ARRAY[...]` of each element's literal, cast to the list type, for a list written to a list type, and otherwise the value's literal, cast to the column type where PostgreSQL would read it as another type. - */ +/** A value as DDL writes it, with a `Date` written as its ISO 8601 text, which an invalid `Date` has none of. */ function pgInlineLiteral(wire: unknown, typeText: string, where: LiteralDefaultColumn): string { - if (Array.isArray(wire) && typeText.endsWith('[]')) { - if (wire.length === 0) return "'{}'"; - return `ARRAY[${wire.map((element) => pgLiteralText(element, where)).join(', ')}]::${typeText}`; - } - const text = pgLiteralText(wire, where); - return pgLiteralNeedsCast(wire, typeText) ? `${text}::${typeText}` : text; -} - -function pgLiteralNeedsCast(wire: unknown, typeText: string): boolean { - if (typeof wire === 'number') return !Number.isFinite(wire); - if (typeof wire === 'string' || wire instanceof Date) return !pgIsTextLikeNativeType(typeText); - return typeof wire === 'object' && wire !== null; -} - -/** A value's literal without a cast: SQL NULL, a bare number or boolean, or quoted text. */ -function pgLiteralText(wire: unknown, where: LiteralDefaultColumn): string { - if (wire === null) return 'NULL'; - if (typeof wire === 'boolean') return wire ? 'true' : 'false'; - if (typeof wire === 'number') return Number.isFinite(wire) ? String(wire) : `'${String(wire)}'`; - if (typeof wire === 'bigint') return String(wire); - if (wire instanceof Date) { - if (Number.isNaN(wire.getTime())) { + const dateAsText = (value: unknown) => { + if (!(value instanceof Date)) return value; + if (Number.isNaN(value.getTime())) { throw adapterError( 'CONTRACT.DEFAULT_INVALID', `Column "${where.table}"."${where.column}" has an invalid Date default`, { meta: { table: where.table, column: where.column, reason: 'invalid-date-default' } }, ); } - return `'${escapeLiteral(wire.toISOString())}'`; - } - if (typeof wire === 'string') return `'${escapeLiteral(wire)}'`; - if (wire instanceof Uint8Array) { - const hex = Array.from(wire) - .map((b) => b.toString(16).padStart(2, '0')) - .join(''); - return `'\\x${hex}'`; - } - if (typeof wire === 'object') return `'${escapeLiteral(JSON.stringify(wire))}'`; - throw adapterError( - 'CONTRACT.PACK_CONTRIBUTION_INVALID', - `pgRenderDdlExecuteRequest: unexpected wire type "${typeof wire}"`, - { meta: { wireType: typeof wire } }, + return value.toISOString(); + }; + return inlineLiteral( + Array.isArray(wire) && typeText.endsWith('[]') ? wire.map(dateAsText) : dateAsText(wire), + typeText, ); } diff --git a/test/integration/test/prisma7-source/refused-defaults-migrate.integration.test.ts b/test/integration/test/prisma7-source/refused-defaults-migrate.integration.test.ts index 40d61668b222..5f07cdb2e556 100644 --- a/test/integration/test/prisma7-source/refused-defaults-migrate.integration.test.ts +++ b/test/integration/test/prisma7-source/refused-defaults-migrate.integration.test.ts @@ -133,15 +133,15 @@ describe('a Prisma 7 default a Date cannot hold, migrated by Prisma 8', { typeParams: { precision: 6 }, }; const expressions = { - bc: prisma7PostgresBinding.refusedLiteralDefault( + bc: prisma7PostgresBinding.literalDefaultExpression( '-000043-03-15T00:00:00.123456', timestamp, ), - late: prisma7PostgresBinding.refusedLiteralDefault( + late: prisma7PostgresBinding.literalDefaultExpression( '+010000-01-01T00:00:00.123456Z', timestamptz, ), - list: prisma7PostgresBinding.refusedLiteralDefault( + list: prisma7PostgresBinding.literalDefaultExpression( ['-000043-03-15T00:00:00.123456', null], timestamp, ), From 9b69c6be7661d8d99e385064537f68010e476041 Mon Sep 17 00:00:00 2001 From: willbot Date: Fri, 23 Oct 2026 18:34:26 +0200 Subject: [PATCH 096/118] Say in the codec authoring guide that print and infer write a type choice call The guide still said they write the explicit constructor, which was true before the tools learned to write the convenience form. Co-Authored-By: Claude Opus 5.5 Signed-off-by: willbot Signed-off-by: Will Madden --- docs/reference/codec-authoring-guide.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/reference/codec-authoring-guide.md b/docs/reference/codec-authoring-guide.md index f0445e6cde3b..315f3583cfec 100644 --- a/docs/reference/codec-authoring-guide.md +++ b/docs/reference/codec-authoring-guide.md @@ -591,7 +591,7 @@ A codec with parameters of its own sets `paramsSchema` to `dataType.params.and(o A type constructor maps its arguments onto the parameters and names only the codec; the data type follows from the codec. Every argument of a type constructor is a value argument, `{ kind: 'value', dataType }`, and assembly refuses any other kind with `CONTRACT.PACK_CONTRIBUTION_INVALID`; field presets keep their own argument kinds until they are removed. A value argument is a value of the named data type, read and cast as an attribute argument is (ADR 254), so `Vector(1536)` reads `1536` as a `pg/int2` and casts it to `pg/int4`, and `Vector("1536")` is refused. On Postgres every length, precision, scale and SRID is a `pg/int4`, the type of a typmod. A value argument carries no bounds, because the parameter's schema checks the value it gives. Assembly refuses a value argument whose data type no component registers (`CONTRACT.DATA_TYPE_UNREGISTERED`) or that no contract source can write (`CONTRACT.DATA_TYPE_NOT_WRITABLE`). In the TypeScript builder, the pack that registers a data type declares the TypeScript value an argument of it takes as `__dataTypeArguments` in its type, such as `{ 'pg/int4': { argument: number; parameter: number } }`: `argument` is what an author passes, and `parameter` is the type parameter the data type's reader gives for it, and `contract print` writes each argument as a literal of its data type. `inferred: true` marks the constructor written for a column of the data type when no schema names one: `contract infer` and the Prisma 7 reader write it. `contract print` writes it when it produces the column's codec, and otherwise the first constructor in the stack that does, so a `timestamptz` column with the `Temporal` codec prints `TimestamptzTemporal`. It is called with the fewest arguments that give the column's parameters in normal form, so a `numeric(10,0)` column is written `Numeric(10)`. The Prisma 7 reader builds a contract rather than printing PSL, so it passes every parameter the Prisma 7 column gives. At most one constructor per data type carries the mark. A data type with no marked constructor, such as `pg/tsquery`, is still recognised, but `contract infer` cannot write a column of it and fails with `CONTRACT.INFER_COLUMN_TYPE_UNCLAIMED`. -A type choice (`kind: 'typeChoice'`) is a second kind of type declaration, for a type named after what a field means rather than a column type ([ADR 279](../architecture%20docs/adrs/ADR%20279%20-%20Convenience%20types%20choose%20an%20explicit%20type%20from%20their%20arguments.md)). It names no codec. It declares option arguments, `{ kind: 'option', name, values, default, optional: true }`, written as bare names, and value arguments it passes on by name, such as `precision`; and `choices`, a table whose rows map one value of each option to the name of a type constructor at the same level of the namespace, with `defaults` for passed-on arguments the call leaves out. A call resolves to its row's constructor, called with the passed-on arguments, so the contract records only that constructor's codec and parameters. A combination with no row, and a passed-on argument the chosen constructor does not take, are refused at the argument. Every argument is optional, so a type choice is also written bare, as its default row. Assembly refuses a table that names no registered constructor, a value or default an option does not list, defaults with no row, and a passed-on argument of another data type than its constructor's. A type choice is never marked `inferred`, and `contract print` and `contract infer` write the constructor. In the TypeScript builder its `type.*` helper takes the same arguments as one object keyed by name, such as `type.DateTime({ as: 'string', precision: 6 })`, and is typed as one overload per row. +A type choice (`kind: 'typeChoice'`) is a second kind of type declaration, for a type named after what a field means rather than a column type ([ADR 279](../architecture%20docs/adrs/ADR%20279%20-%20Convenience%20types%20choose%20an%20explicit%20type%20from%20their%20arguments.md)). It names no codec. It declares option arguments, `{ kind: 'option', name, values, default, optional: true }`, written as bare names, and value arguments it passes on by name, such as `precision`; and `choices`, a table whose rows map one value of each option to the name of a type constructor at the same level of the namespace, with `defaults` for passed-on arguments the call leaves out. A call resolves to its row's constructor, called with the passed-on arguments, so the contract records only that constructor's codec and parameters. A combination with no row, and a passed-on argument the chosen constructor does not take, are refused at the argument. Every argument is optional, so a type choice is also written bare, as its default row. Assembly refuses a table that names no registered constructor, a value or default an option does not list, defaults with no row, and a passed-on argument of another data type than its constructor's. A type choice is never marked `inferred`. Where a call of a type choice becomes the constructor that `contract print` or `contract infer` writes for a column, they write the type choice call instead, by name, with only the arguments that differ from its defaults, and only when that call gives the same column; otherwise they write the constructor. In the TypeScript builder its `type.*` helper takes the same arguments as one object keyed by name, such as `type.DateTime({ as: 'string', precision: 6 })`, and is typed as one overload per row. ```ts import type { AuthoringTypeNamespace } from '@internal/framework-components/authoring'; From a0d1045f5b47b326130fc784ede675fac07e91bc Mon Sep 17 00:00:00 2001 From: willbot Date: Fri, 23 Oct 2026 18:35:33 +0200 Subject: [PATCH 097/118] Test that each date and time type infer writes is its convenience default For each storage value of the four convenience types, the explicit type a call with every other option at its default becomes must be the one marked inferred for its data type, so the marks cannot drift from the tables. Co-Authored-By: Claude Opus 5.5 Signed-off-by: willbot Signed-off-by: Will Madden --- .../test/type-choice-inferred-marks.test.ts | 69 +++++++++++++++++++ 1 file changed, 69 insertions(+) create mode 100644 packages/3-targets/postgres/1-target/test/type-choice-inferred-marks.test.ts diff --git a/packages/3-targets/postgres/1-target/test/type-choice-inferred-marks.test.ts b/packages/3-targets/postgres/1-target/test/type-choice-inferred-marks.test.ts new file mode 100644 index 000000000000..da4491c54b53 --- /dev/null +++ b/packages/3-targets/postgres/1-target/test/type-choice-inferred-marks.test.ts @@ -0,0 +1,69 @@ +import { + type AuthoringTypeChoiceDescriptor, + type AuthoringTypeConstructorDescriptor, + isAuthoringTypeChoiceOption, + resolveAuthoringTypeChoice, +} from '@internal/framework-components/authoring'; +import { describe, expect, it } from 'vitest'; +import { createPostgresBuiltinCodecLookup } from '../src/core/codec-registry'; +import { + postgresConvenienceDateTimeTypes, + postgresScalarAndNativeTypeConstructors, +} from '../src/core/type-constructors'; + +const codecLookup = createPostgresBuiltinCodecLookup(); + +const constructors: ReadonlyMap = new Map( + Object.entries(postgresScalarAndNativeTypeConstructors).flatMap(([name, member]) => + member.kind === 'typeConstructor' ? [[name, member]] : [], + ), +); + +function dataTypeOf(constructorName: string): string | undefined { + const codecId = constructors.get(constructorName)?.output.codecId; + return codecId === undefined ? undefined : codecLookup.descriptorFor(codecId)?.dataType; +} + +/** For each `storage` value, the explicit type a call writing only that value becomes. */ +function convenienceDefaults(choice: AuthoringTypeChoiceDescriptor): readonly string[] { + const storage = choice.args.find( + (argument) => isAuthoringTypeChoiceOption(argument) && argument.name === 'storage', + ); + const values = + storage !== undefined && isAuthoringTypeChoiceOption(storage) ? storage.values : []; + return values.map( + (value) => + resolveAuthoringTypeChoice({ + helperPath: 'choice', + descriptor: choice, + args: choice.args.map((argument) => (argument === storage ? value : undefined)), + constructorNamed: (name) => constructors.get(name), + }).type, + ); +} + +const defaults = Object.values(postgresConvenienceDateTimeTypes).flatMap(convenienceDefaults); + +describe('the explicit type contract infer writes for a date or time column', () => { + it.each(defaults)( + 'is %s, the type the convenience default for its data type gives', + (constructorName) => { + const dataType = dataTypeOf(constructorName); + const marked = [...constructors].flatMap(([name, descriptor]) => + descriptor.inferred === true && dataTypeOf(name) === dataType ? [name] : [], + ); + expect({ dataType, marked }).toEqual({ dataType, marked: [constructorName] }); + }, + ); + + it('covers every date and time data type', () => { + expect(defaults.map(dataTypeOf).sort()).toEqual([ + 'pg/date', + 'pg/interval', + 'pg/time', + 'pg/timestamp', + 'pg/timestamptz', + 'pg/timetz', + ]); + }); +}); From 4d3db71d234b502d617b210ba2e1035a45e12382 Mon Sep 17 00:00:00 2001 From: willbot Date: Fri, 23 Oct 2026 18:37:40 +0200 Subject: [PATCH 098/118] Remove the import cycle between the authoring module and the type choice module framework-authoring imported type choice resolution, and the type choice module imported types back, so it kept its own copies of the kind checks. The functions that resolve a type choice call (getAuthoringTypeChoice, instantiateAuthoringTypeCall and collectScalarTypeConstructors) move into the type choice module, which now uses the authoring module's kind checks, and the namespace walk is written once. Co-Authored-By: Claude Opus 5.5 Signed-off-by: willbot Signed-off-by: Will Madden --- .../src/control/control-stack.ts | 6 +- .../src/exports/authoring.ts | 6 +- .../src/shared/authoring-type-choice-call.ts | 19 +-- .../src/shared/authoring-type-choice.ts | 147 ++++++++++++++---- .../src/shared/framework-authoring.ts | 90 +---------- .../test/authoring-type-choice.test.ts | 4 +- .../framework-components.authoring.test.ts | 2 +- 7 files changed, 133 insertions(+), 141 deletions(-) diff --git a/packages/1-framework/1-core/framework-components/src/control/control-stack.ts b/packages/1-framework/1-core/framework-components/src/control/control-stack.ts index 82bd33c2ef34..39acd8a81daa 100644 --- a/packages/1-framework/1-core/framework-components/src/control/control-stack.ts +++ b/packages/1-framework/1-core/framework-components/src/control/control-stack.ts @@ -3,7 +3,10 @@ import { blindCast } from '@internal/utils/casts'; import { InternalError, isInternalError } from '@internal/utils/internal-error'; import type { AggregateDescriptor } from '../shared/aggregate-descriptor'; import { aggregateDescriptorKey, isAggregateDescriptor } from '../shared/aggregate-descriptor'; -import { assertTypeChoicesValid } from '../shared/authoring-type-choice'; +import { + assertTypeChoicesValid, + collectScalarTypeConstructors, +} from '../shared/authoring-type-choice'; import type { CapabilityMatrix } from '../shared/capabilities'; import { mergeCapabilityMatrices } from '../shared/capabilities'; import type { Codec } from '../shared/codec'; @@ -36,7 +39,6 @@ import { assertResolvableTypeConstructorTemplates, assertTypeAliasesValid, collectContributedDescriptorPaths, - collectScalarTypeConstructors, isAuthoringArgRef, mergeAuthoringAttributeSpecs, mergeAuthoringNamespaces, diff --git a/packages/1-framework/1-core/framework-components/src/exports/authoring.ts b/packages/1-framework/1-core/framework-components/src/exports/authoring.ts index 81ed36a934cb..302c525f1c6a 100644 --- a/packages/1-framework/1-core/framework-components/src/exports/authoring.ts +++ b/packages/1-framework/1-core/framework-components/src/exports/authoring.ts @@ -8,6 +8,9 @@ export type { } from '../shared/authoring-type-choice'; export { assertTypeChoicesValid, + collectScalarTypeConstructors, + getAuthoringTypeChoice, + instantiateAuthoringTypeCall, isAuthoringTypeChoiceOption, resolveAuthoringTypeChoice, typeChoiceArgumentsFromObject, @@ -117,14 +120,11 @@ export { assertResolvableTypeConstructorTemplates, assertTypeAliasesValid, classifyEnumMemberType, - collectScalarTypeConstructors, flushAuthoringWarnings, - getAuthoringTypeChoice, getAuthoringTypeConstructor, instantiateAuthoringEntityType, instantiateAuthoringFieldPreset, instantiateAuthoringTypeAlias, - instantiateAuthoringTypeCall, instantiateAuthoringTypeConstructor, instantiateAuthoringTypeConstructorFromValues, isAuthoringArgRef, diff --git a/packages/1-framework/1-core/framework-components/src/shared/authoring-type-choice-call.ts b/packages/1-framework/1-core/framework-components/src/shared/authoring-type-choice-call.ts index 158cd9565d74..8334a1c03529 100644 --- a/packages/1-framework/1-core/framework-components/src/shared/authoring-type-choice-call.ts +++ b/packages/1-framework/1-core/framework-components/src/shared/authoring-type-choice-call.ts @@ -1,6 +1,8 @@ import { isInternalError } from '@internal/utils/internal-error'; import { type AuthoringTypeChoiceDescriptor, + authoringTypeNamespaceLevel, + instantiateAuthoringTypeCall, isAuthoringTypeChoiceOption, resolveAuthoringTypeChoice, typeConstructorLookup, @@ -12,8 +14,6 @@ import { } from './authoring-type-constructor-call'; import { type AuthoringTypeNamespace, - type AuthoringTypeNamespaceMember, - instantiateAuthoringTypeCall, instantiateAuthoringTypeConstructor, isAuthoringTypeChoiceDescriptor, } from './framework-authoring'; @@ -32,19 +32,6 @@ export interface AuthoringTypeChoiceCall { readonly becomes: AuthoringTypeConstructorCall; } -function namespaceLevel( - namespace: AuthoringTypeNamespace, - path: readonly string[], -): AuthoringTypeNamespace | undefined { - let level: AuthoringTypeNamespace | undefined = namespace; - for (const segment of path) { - const member: AuthoringTypeNamespaceMember | undefined = - level !== undefined && Object.hasOwn(level, segment) ? level[segment] : undefined; - level = member !== undefined && !('kind' in member) ? member : undefined; - } - return level; -} - interface ChoiceNaming { readonly name: string; readonly descriptor: AuthoringTypeChoiceDescriptor; @@ -86,7 +73,7 @@ export function findAuthoringTypeChoiceCall(input: { const sameOutput = input.sameOutput ?? sameAuthoringTypeConstructorOutput; const prefix = call.path.slice(0, -1); const constructorName = call.path.at(-1); - const level = namespaceLevel(input.namespace, prefix); + const level = authoringTypeNamespaceLevel(input.namespace, prefix); if (level === undefined || constructorName === undefined) return undefined; const explicit = typeConstructorLookup(level)(constructorName); const wanted = diff --git a/packages/1-framework/1-core/framework-components/src/shared/authoring-type-choice.ts b/packages/1-framework/1-core/framework-components/src/shared/authoring-type-choice.ts index 55b56b9c8c24..83596d683fd9 100644 --- a/packages/1-framework/1-core/framework-components/src/shared/authoring-type-choice.ts +++ b/packages/1-framework/1-core/framework-components/src/shared/authoring-type-choice.ts @@ -1,7 +1,15 @@ import type { JsonValue } from '@internal/contract/types'; -import type { - AuthoringTypeConstructorDescriptor, - AuthoringValueArgument, +import { InternalError } from '@internal/utils/internal-error'; +import { + type AuthoringTypeConstructorDescriptor, + type AuthoringTypeNamespace, + type AuthoringTypeNamespaceMember, + type AuthoringValueArgument, + instantiateAuthoringTypeConstructor, + isAuthoringTypeChoiceDescriptor, + isAuthoringTypeConstructorDescriptor, + isAuthoringTypeDescriptor, + type ScalarTypeConstructorOutput, } from './framework-authoring'; import type { AuthoringOption } from './option-descriptor'; import { runtimeError } from './runtime-error'; @@ -200,42 +208,124 @@ export function resolveAuthoringTypeChoice(input: { }; } -type TypeNamespaceLevel = { readonly [name: string]: unknown }; - /** Looks a type constructor up by name in one level of a type namespace, as a type choice's rows name them. */ export function typeConstructorLookup( - namespace: TypeNamespaceLevel, + namespace: AuthoringTypeNamespace, ): (name: string) => AuthoringTypeConstructorDescriptor | undefined { return (name) => { - const member = Object.hasOwn(namespace, name) ? namespace[name] : undefined; - return isTypeConstructor(member) ? member : undefined; + const member = memberNamed(namespace, name); + return member !== undefined && isAuthoringTypeConstructorDescriptor(member) + ? member + : undefined; }; } -function isTypeChoice(value: unknown): value is AuthoringTypeChoiceDescriptor { - return ( - typeof value === 'object' && value !== null && 'kind' in value && value.kind === 'typeChoice' - ); +function memberNamed( + namespace: AuthoringTypeNamespace, + name: string, +): AuthoringTypeNamespaceMember | undefined { + return Object.hasOwn(namespace, name) ? namespace[name] : undefined; } -function isTypeConstructor(value: unknown): value is AuthoringTypeConstructorDescriptor { - return ( - typeof value === 'object' && - value !== null && - 'kind' in value && - value.kind === 'typeConstructor' - ); +function subNamespace( + member: AuthoringTypeNamespaceMember | undefined, +): AuthoringTypeNamespace | undefined { + return member === undefined || isAuthoringTypeDescriptor(member) ? undefined : member; +} + +/** The sub-namespace at `path` of a type namespace, or `undefined` when a segment names no sub-namespace. */ +export function authoringTypeNamespaceLevel( + namespace: AuthoringTypeNamespace | undefined, + path: readonly string[], +): AuthoringTypeNamespace | undefined { + let level = namespace; + for (const segment of path) { + level = level === undefined ? undefined : subNamespace(memberNamed(level, segment)); + } + return level; +} + +/** The type choice at `path` in the contributions' type namespace, with a lookup of the type constructors beside it that its rows name, or `undefined` when none is there. */ +export function getAuthoringTypeChoice( + contributions: { readonly type?: AuthoringTypeNamespace } | undefined, + path: readonly string[], +): + | { + readonly descriptor: AuthoringTypeChoiceDescriptor; + readonly constructorNamed: (name: string) => AuthoringTypeConstructorDescriptor | undefined; + } + | undefined { + const level = authoringTypeNamespaceLevel(contributions?.type, path.slice(0, -1)); + const name = path.at(-1); + const member = level === undefined || name === undefined ? undefined : memberNamed(level, name); + return member !== undefined && level !== undefined && isAuthoringTypeChoiceDescriptor(member) + ? { descriptor: member, constructorNamed: typeConstructorLookup(level) } + : undefined; +} + +/** + * The column a call of the type constructor or type choice `name`, in one level of a type namespace, makes from its arguments. A type choice's arguments choose a constructor of the same level, which is instantiated with the arguments it passes on. + */ +export function instantiateAuthoringTypeCall( + namespace: AuthoringTypeNamespace, + name: string, + args: readonly unknown[], +): ScalarTypeConstructorOutput { + const member = memberNamed(namespace, name); + if (member !== undefined && isAuthoringTypeConstructorDescriptor(member)) { + return instantiateAuthoringTypeConstructor(member, args); + } + if (member === undefined || !isAuthoringTypeChoiceDescriptor(member)) { + throw new InternalError( + `"${name}" is not a type constructor or type choice of this namespace.`, + ); + } + const chosen = resolveAuthoringTypeChoice({ + helperPath: name, + descriptor: member, + args, + constructorNamed: typeConstructorLookup(namespace), + }); + return instantiateAuthoringTypeConstructor(chosen.descriptor, chosen.args); } -function isSubNamespace(value: unknown): value is TypeNamespaceLevel { - return typeof value === 'object' && value !== null && !('kind' in value); +/** + * Derives the scalar view of an assembled authoring type namespace: every + * **top-level** type constructor that is instantiable with an empty argument + * list — all declared args optional and no entity-ref argument. A bare type + * name `T` in a schema is semantically the zero-arg instantiation `T()`, so + * each entry is exactly what that call produces (defaulted template values + * applied, absent optional-arg typeParams keys omitted). Constructors + * registered under a namespace segment, constructors with required args, and + * entity-ref constructors are not scalars and are excluded. + * + * Eligibility needs no template inspection: templates that cannot resolve + * for their legal call shapes are rejected at the composition boundary by + * {@link assertResolvableTypeConstructorTemplates}, so the zero-arg + * instantiation below cannot fail for an eligible constructor. + */ +export function collectScalarTypeConstructors( + namespace: AuthoringTypeNamespace, +): ReadonlyMap { + const result = new Map(); + for (const [name, value] of Object.entries(namespace)) { + if (isAuthoringTypeChoiceDescriptor(value)) { + result.set(name, instantiateAuthoringTypeCall(namespace, name, [])); + continue; + } + if (!isAuthoringTypeConstructorDescriptor(value)) continue; + if (value.entityRefArg !== undefined) continue; + if (value.args?.some((arg) => arg.optional !== true)) continue; + result.set(name, instantiateAuthoringTypeConstructor(value, [])); + } + return result; } function assertTypeChoiceValid( path: string, descriptor: AuthoringTypeChoiceDescriptor, contributedBy: string, - level: TypeNamespaceLevel, + level: AuthoringTypeNamespace, ): void { const invalidError = (detail: string) => runtimeError( @@ -315,20 +405,21 @@ function assertTypeChoiceValid( * Refuses a type choice of a component's `contributed` type namespace whose table cannot be resolved: a row that names no type constructor at the same level of the `assembled` namespace, a combination or default an option does not list, defaults with no row, a parameter default for an argument it does not pass on, or a passed-on argument of another data type than the constructor takes. */ export function assertTypeChoicesValid( - contributed: TypeNamespaceLevel, + contributed: AuthoringTypeNamespace, contributedBy: string, - assembled: TypeNamespaceLevel, + assembled: AuthoringTypeNamespace, path: readonly string[] = [], ): void { for (const [name, value] of Object.entries(contributed)) { const currentPath = [...path, name]; - if (isTypeChoice(value)) { + if (isAuthoringTypeChoiceDescriptor(value)) { assertTypeChoiceValid(currentPath.join('.'), value, contributedBy, assembled); continue; } - const assembledLevel = Object.hasOwn(assembled, name) ? assembled[name] : undefined; - if (isSubNamespace(value) && isSubNamespace(assembledLevel)) { - assertTypeChoicesValid(value, contributedBy, assembledLevel, currentPath); + const contributedLevel = subNamespace(value); + const assembledLevel = subNamespace(memberNamed(assembled, name)); + if (contributedLevel !== undefined && assembledLevel !== undefined) { + assertTypeChoicesValid(contributedLevel, contributedBy, assembledLevel, currentPath); } } } diff --git a/packages/1-framework/1-core/framework-components/src/shared/framework-authoring.ts b/packages/1-framework/1-core/framework-components/src/shared/framework-authoring.ts index ecb69c903dac..bff99f207c7e 100644 --- a/packages/1-framework/1-core/framework-components/src/shared/framework-authoring.ts +++ b/packages/1-framework/1-core/framework-components/src/shared/framework-authoring.ts @@ -15,11 +15,7 @@ import { InternalError } from '@internal/utils/internal-error'; import { notOk, ok, type Result } from '@internal/utils/result'; import type { StandardSchemaV1 } from '@standard-schema/spec'; import type { Type } from 'arktype'; -import { - type AuthoringTypeChoiceDescriptor, - resolveAuthoringTypeChoice, - typeConstructorLookup, -} from './authoring-type-choice'; +import type { AuthoringTypeChoiceDescriptor } from './authoring-type-choice'; import type { CodecLookupWithDescriptors } from './codec-types'; import type { DataTypeId } from './data-type'; import type { @@ -775,32 +771,6 @@ export function getAuthoringTypeConstructor( : undefined; } -/** The type choice at `path` in the contributions' type namespace, with a lookup of the type constructors beside it that its rows name, or `undefined` when none is there. */ -export function getAuthoringTypeChoice( - contributions: { readonly type?: AuthoringTypeNamespace } | undefined, - path: readonly string[], -): - | { - readonly descriptor: AuthoringTypeChoiceDescriptor; - readonly constructorNamed: (name: string) => AuthoringTypeConstructorDescriptor | undefined; - } - | undefined { - let level: AuthoringTypeNamespace | undefined = contributions?.type; - for (const segment of path.slice(0, -1)) { - const member: AuthoringTypeNamespaceMember | undefined = - level !== undefined && Object.hasOwn(level, segment) ? level[segment] : undefined; - level = member !== undefined && !('kind' in member) ? member : undefined; - } - const name = path.at(-1); - const member = - level === undefined || name === undefined || !Object.hasOwn(level, name) - ? undefined - : level[name]; - return member !== undefined && level !== undefined && isAuthoringTypeChoiceDescriptor(member) - ? { descriptor: member, constructorNamed: typeConstructorLookup(level) } - : undefined; -} - export function isAuthoringFieldPresetDescriptor( value: AuthoringFieldPresetDescriptor | AuthoringFieldNamespace, ): value is AuthoringFieldPresetDescriptor { @@ -1103,38 +1073,6 @@ export interface ScalarTypeConstructorOutput { readonly typeParams?: Record; } -/** - * Derives the scalar view of an assembled authoring type namespace: every - * **top-level** type constructor that is instantiable with an empty argument - * list — all declared args optional and no entity-ref argument. A bare type - * name `T` in a schema is semantically the zero-arg instantiation `T()`, so - * each entry is exactly what that call produces (defaulted template values - * applied, absent optional-arg typeParams keys omitted). Constructors - * registered under a namespace segment, constructors with required args, and - * entity-ref constructors are not scalars and are excluded. - * - * Eligibility needs no template inspection: templates that cannot resolve - * for their legal call shapes are rejected at the composition boundary by - * {@link assertResolvableTypeConstructorTemplates}, so the zero-arg - * instantiation below cannot fail for an eligible constructor. - */ -export function collectScalarTypeConstructors( - namespace: AuthoringTypeNamespace, -): ReadonlyMap { - const result = new Map(); - for (const [name, value] of Object.entries(namespace)) { - if (isAuthoringTypeChoiceDescriptor(value)) { - result.set(name, instantiateAuthoringTypeCall(namespace, name, [])); - continue; - } - if (!isAuthoringTypeConstructorDescriptor(value)) continue; - if (value.entityRefArg !== undefined) continue; - if (value.args?.some((arg) => arg.optional !== true)) continue; - result.set(name, instantiateAuthoringTypeConstructor(value, [])); - } - return result; -} - function visitTemplateArgRefs( template: AuthoringTemplateValue | undefined, visit: (ref: AuthoringArgRef) => void, @@ -2022,32 +1960,6 @@ export function instantiateAuthoringTypeConstructor( return resolveAuthoringStorageTypeTemplate(descriptor.output, args); } -/** - * The column a call of the type constructor or type choice `name`, in one level of a type namespace, makes from its arguments. A type choice's arguments choose a constructor of the same level, which is instantiated with the arguments it passes on. - */ -export function instantiateAuthoringTypeCall( - namespace: AuthoringTypeNamespace, - name: string, - args: readonly unknown[], -): ScalarTypeConstructorOutput { - const member = Object.hasOwn(namespace, name) ? namespace[name] : undefined; - if (member !== undefined && isAuthoringTypeConstructorDescriptor(member)) { - return instantiateAuthoringTypeConstructor(member, args); - } - if (member === undefined || !isAuthoringTypeChoiceDescriptor(member)) { - throw new InternalError( - `"${name}" is not a type constructor or type choice of this namespace.`, - ); - } - const chosen = resolveAuthoringTypeChoice({ - helperPath: name, - descriptor: member, - args, - constructorNamed: typeConstructorLookup(namespace), - }); - return instantiateAuthoringTypeConstructor(chosen.descriptor, chosen.args); -} - /** What a field typed by a contributed type alias takes: the type, and the alias's generator calls. */ export interface AuthoringTypeAliasOutput { readonly type: ScalarTypeConstructorOutput; diff --git a/packages/1-framework/1-core/framework-components/test/authoring-type-choice.test.ts b/packages/1-framework/1-core/framework-components/test/authoring-type-choice.test.ts index f2efe0c978fe..8d3dc6bdff06 100644 --- a/packages/1-framework/1-core/framework-components/test/authoring-type-choice.test.ts +++ b/packages/1-framework/1-core/framework-components/test/authoring-type-choice.test.ts @@ -1,6 +1,8 @@ import { type } from 'arktype'; import { describe, expect, it } from 'vitest'; import { + collectScalarTypeConstructors, + instantiateAuthoringTypeCall, resolveAuthoringTypeChoice, typeChoiceArgumentsFromObject, typeConstructorLookup, @@ -8,8 +10,6 @@ import { import { type AuthoringTypeConstructorDescriptor, type AuthoringTypeNamespace, - collectScalarTypeConstructors, - instantiateAuthoringTypeCall, validateAuthoringTypeChoiceParams, } from '../src/shared/framework-authoring'; diff --git a/packages/1-framework/1-core/framework-components/test/framework-components.authoring.test.ts b/packages/1-framework/1-core/framework-components/test/framework-components.authoring.test.ts index b1f41b1cf1de..d7890457be0e 100644 --- a/packages/1-framework/1-core/framework-components/test/framework-components.authoring.test.ts +++ b/packages/1-framework/1-core/framework-components/test/framework-components.authoring.test.ts @@ -1,4 +1,5 @@ import { describe, expect, it } from 'vitest'; +import { collectScalarTypeConstructors } from '../src/shared/authoring-type-choice'; import type { AuthoringFieldNamespace, AuthoringFieldPresetDescriptor, @@ -8,7 +9,6 @@ import type { import { assertNoCrossRegistryCollisions, classifyEnumMemberType, - collectScalarTypeConstructors, instantiateAuthoringFieldPreset, instantiateAuthoringTypeConstructor, isAuthoringArgRef, From 365048e38601eaa9ae757d4a0ea66b8ecdd13d8b Mon Sep 17 00:00:00 2001 From: willbot Date: Fri, 23 Oct 2026 18:40:32 +0200 Subject: [PATCH 099/118] Refuse a type choice whose row names another component's type constructor ADR 279 says each row of a convenience type names an explicit type of the same target. Assembly checked rows against the assembled namespace, so an extension could ship a type choice naming the target's constructors. Each component's type choices are now checked against the constructors it contributes itself. Co-Authored-By: Claude Opus 5.5 Signed-off-by: willbot Signed-off-by: Will Madden --- docs/reference/codec-authoring-guide.md | 2 +- docs/reference/error-reference.md | 2 +- .../src/control/control-stack.ts | 2 +- .../src/shared/authoring-type-choice.ts | 14 ++--- .../test/type-choice-assembly.test.ts | 54 ++++++++++++++----- .../src/composed-authoring-helpers.ts | 6 +-- 6 files changed, 49 insertions(+), 31 deletions(-) diff --git a/docs/reference/codec-authoring-guide.md b/docs/reference/codec-authoring-guide.md index 315f3583cfec..3d7874e78937 100644 --- a/docs/reference/codec-authoring-guide.md +++ b/docs/reference/codec-authoring-guide.md @@ -591,7 +591,7 @@ A codec with parameters of its own sets `paramsSchema` to `dataType.params.and(o A type constructor maps its arguments onto the parameters and names only the codec; the data type follows from the codec. Every argument of a type constructor is a value argument, `{ kind: 'value', dataType }`, and assembly refuses any other kind with `CONTRACT.PACK_CONTRIBUTION_INVALID`; field presets keep their own argument kinds until they are removed. A value argument is a value of the named data type, read and cast as an attribute argument is (ADR 254), so `Vector(1536)` reads `1536` as a `pg/int2` and casts it to `pg/int4`, and `Vector("1536")` is refused. On Postgres every length, precision, scale and SRID is a `pg/int4`, the type of a typmod. A value argument carries no bounds, because the parameter's schema checks the value it gives. Assembly refuses a value argument whose data type no component registers (`CONTRACT.DATA_TYPE_UNREGISTERED`) or that no contract source can write (`CONTRACT.DATA_TYPE_NOT_WRITABLE`). In the TypeScript builder, the pack that registers a data type declares the TypeScript value an argument of it takes as `__dataTypeArguments` in its type, such as `{ 'pg/int4': { argument: number; parameter: number } }`: `argument` is what an author passes, and `parameter` is the type parameter the data type's reader gives for it, and `contract print` writes each argument as a literal of its data type. `inferred: true` marks the constructor written for a column of the data type when no schema names one: `contract infer` and the Prisma 7 reader write it. `contract print` writes it when it produces the column's codec, and otherwise the first constructor in the stack that does, so a `timestamptz` column with the `Temporal` codec prints `TimestamptzTemporal`. It is called with the fewest arguments that give the column's parameters in normal form, so a `numeric(10,0)` column is written `Numeric(10)`. The Prisma 7 reader builds a contract rather than printing PSL, so it passes every parameter the Prisma 7 column gives. At most one constructor per data type carries the mark. A data type with no marked constructor, such as `pg/tsquery`, is still recognised, but `contract infer` cannot write a column of it and fails with `CONTRACT.INFER_COLUMN_TYPE_UNCLAIMED`. -A type choice (`kind: 'typeChoice'`) is a second kind of type declaration, for a type named after what a field means rather than a column type ([ADR 279](../architecture%20docs/adrs/ADR%20279%20-%20Convenience%20types%20choose%20an%20explicit%20type%20from%20their%20arguments.md)). It names no codec. It declares option arguments, `{ kind: 'option', name, values, default, optional: true }`, written as bare names, and value arguments it passes on by name, such as `precision`; and `choices`, a table whose rows map one value of each option to the name of a type constructor at the same level of the namespace, with `defaults` for passed-on arguments the call leaves out. A call resolves to its row's constructor, called with the passed-on arguments, so the contract records only that constructor's codec and parameters. A combination with no row, and a passed-on argument the chosen constructor does not take, are refused at the argument. Every argument is optional, so a type choice is also written bare, as its default row. Assembly refuses a table that names no registered constructor, a value or default an option does not list, defaults with no row, and a passed-on argument of another data type than its constructor's. A type choice is never marked `inferred`. Where a call of a type choice becomes the constructor that `contract print` or `contract infer` writes for a column, they write the type choice call instead, by name, with only the arguments that differ from its defaults, and only when that call gives the same column; otherwise they write the constructor. In the TypeScript builder its `type.*` helper takes the same arguments as one object keyed by name, such as `type.DateTime({ as: 'string', precision: 6 })`, and is typed as one overload per row. +A type choice (`kind: 'typeChoice'`) is a second kind of type declaration, for a type named after what a field means rather than a column type ([ADR 279](../architecture%20docs/adrs/ADR%20279%20-%20Convenience%20types%20choose%20an%20explicit%20type%20from%20their%20arguments.md)). It names no codec. It declares option arguments, `{ kind: 'option', name, values, default, optional: true }`, written as bare names, and value arguments it passes on by name, such as `precision`; and `choices`, a table whose rows map one value of each option to the name of a type constructor the same component contributes at the same level of the namespace, with `defaults` for passed-on arguments the call leaves out. A call resolves to its row's constructor, called with the passed-on arguments, so the contract records only that constructor's codec and parameters. A combination with no row, and a passed-on argument the chosen constructor does not take, are refused at the argument. Every argument is optional, so a type choice is also written bare, as its default row. Assembly refuses a table that names a constructor the component does not contribute beside the choice, a value or default an option does not list, defaults with no row, and a passed-on argument of another data type than its constructor's. A type choice is never marked `inferred`. Where a call of a type choice becomes the constructor that `contract print` or `contract infer` writes for a column, they write the type choice call instead, by name, with only the arguments that differ from its defaults, and only when that call gives the same column; otherwise they write the constructor. In the TypeScript builder its `type.*` helper takes the same arguments as one object keyed by name, such as `type.DateTime({ as: 'string', precision: 6 })`, and is typed as one overload per row. ```ts import type { AuthoringTypeNamespace } from '@internal/framework-components/authoring'; diff --git a/docs/reference/error-reference.md b/docs/reference/error-reference.md index 1db9649075af..6a988fff63bb 100644 --- a/docs/reference/error-reference.md +++ b/docs/reference/error-reference.md @@ -543,7 +543,7 @@ Namespaces are declared (contract-level list or a model-level `namespace`) on a A composed pack's contribution is malformed or collides with another contribution; this is the extension-author-facing bucket. Covers: entity types colliding with reserved helper keys, duplicate entity kinds or index-type registrations, a registered entity kind with no `lowerEntityHandles` lowering, an invalid `indexTypes` shape, entries-slot collisions between a model attribute and a block entry kind, a model attribute that lowers to a malformed index, bad authoring-helper paths, a codec registered with an entity-ref arg but no `columnFromEntity` hook, and print-time contribution mismatches (a block keyword with no PSL block descriptor, or a descriptor whose discriminator disagrees with the block's kind). Raised during contract authoring/lowering and PSL printing. -A contributed type choice (`kind: 'typeChoice'`, such as PostgreSQL's `DateTime`) is refused when the stack is assembled, by the control stack and by the TypeScript builders, when its table cannot be resolved: `Type choice "" contributed by "" chooses "", which is not a type constructor any component registers beside it.`, a row whose combination or an option default the option does not list, two rows for one combination (`… has two rows for as: ; each combination chooses one type.`), a combination of defaults with no row (`… has no row for its defaults as: , so it cannot be written bare.`), a parameter default for an argument it does not pass on or that the chosen constructor does not take (`… gives a default for "" to "", which takes no argument of that name.`), or a passed-on argument of another data type than the chosen constructor takes. Payload: `packId`, `contribution`. +A contributed type choice (`kind: 'typeChoice'`, such as PostgreSQL's `DateTime`) is refused when the stack is assembled, by the control stack and by the TypeScript builders, when its table cannot be resolved: `Type choice "" contributed by "" chooses "", which is not a type constructor "" registers beside it.`, for a row naming a constructor no component or only another component contributes, a row whose combination or an option default the option does not list, two rows for one combination (`… has two rows for as: ; each combination chooses one type.`), a combination of defaults with no row (`… has no row for its defaults as: , so it cannot be written bare.`), a parameter default for an argument it does not pass on or that the chosen constructor does not take (`… gives a default for "" to "", which takes no argument of that name.`), or a passed-on argument of another data type than the chosen constructor takes. Payload: `packId`, `contribution`. A contributed type alias (`kind: 'typeAlias'`, such as `temporal.CreatedAt`) is refused when the stack is assembled, by the control stack and by the TypeScript builders, so PSL and TypeScript refuse the same packs. Its type params may not reference an argument, because a type alias takes none: `Invalid authoring type alias "" contributed by descriptor "". output.type.typeParams. references an argument, but a type alias takes none. Write the value itself.` Each generator call must resolve for the alias's codec: `Type alias "" calls now() on create, which does not apply to its codec "".`, `…, which its codec "" refuses: `, or `…, but no component declares the generator function "".` Payload: `packId`, `contribution`, `reason`, `keyword`, `paramName`, `codecId`. diff --git a/packages/1-framework/1-core/framework-components/src/control/control-stack.ts b/packages/1-framework/1-core/framework-components/src/control/control-stack.ts index 39acd8a81daa..88f84bcd1531 100644 --- a/packages/1-framework/1-core/framework-components/src/control/control-stack.ts +++ b/packages/1-framework/1-core/framework-components/src/control/control-stack.ts @@ -358,7 +358,7 @@ export function assembleAuthoringContributions( ); for (const descriptor of descriptors) { if (descriptor.authoring?.type === undefined) continue; - assertTypeChoicesValid(descriptor.authoring.type, descriptor.id ?? '', typeNamespace); + assertTypeChoicesValid(descriptor.authoring.type, descriptor.id ?? ''); } if ( diff --git a/packages/1-framework/1-core/framework-components/src/shared/authoring-type-choice.ts b/packages/1-framework/1-core/framework-components/src/shared/authoring-type-choice.ts index 83596d683fd9..9ec0d0dbea94 100644 --- a/packages/1-framework/1-core/framework-components/src/shared/authoring-type-choice.ts +++ b/packages/1-framework/1-core/framework-components/src/shared/authoring-type-choice.ts @@ -366,7 +366,7 @@ function assertTypeChoiceValid( const chosenType = constructorNamed(row.type); if (chosenType === undefined) { throw invalidError( - `chooses "${row.type}", which is not a type constructor any component registers beside it.`, + `chooses "${row.type}", which is not a type constructor "${contributedBy}" registers beside it.`, ); } for (const name of Object.keys(row.defaults ?? {})) { @@ -402,24 +402,20 @@ function assertTypeChoiceValid( } /** - * Refuses a type choice of a component's `contributed` type namespace whose table cannot be resolved: a row that names no type constructor at the same level of the `assembled` namespace, a combination or default an option does not list, defaults with no row, a parameter default for an argument it does not pass on, or a passed-on argument of another data type than the constructor takes. + * Refuses a type choice of a component's `contributed` type namespace whose table cannot be resolved: a row that names no type constructor the same component contributes at the same level, a combination or default an option does not list, defaults with no row, a parameter default for an argument it does not pass on, or a passed-on argument of another data type than the constructor takes. ADR 279: each row names an explicit type of the same target. */ export function assertTypeChoicesValid( contributed: AuthoringTypeNamespace, contributedBy: string, - assembled: AuthoringTypeNamespace, path: readonly string[] = [], ): void { for (const [name, value] of Object.entries(contributed)) { const currentPath = [...path, name]; if (isAuthoringTypeChoiceDescriptor(value)) { - assertTypeChoiceValid(currentPath.join('.'), value, contributedBy, assembled); + assertTypeChoiceValid(currentPath.join('.'), value, contributedBy, contributed); continue; } - const contributedLevel = subNamespace(value); - const assembledLevel = subNamespace(memberNamed(assembled, name)); - if (contributedLevel !== undefined && assembledLevel !== undefined) { - assertTypeChoicesValid(contributedLevel, contributedBy, assembledLevel, currentPath); - } + const level = subNamespace(value); + if (level !== undefined) assertTypeChoicesValid(level, contributedBy, currentPath); } } diff --git a/packages/1-framework/1-core/framework-components/test/type-choice-assembly.test.ts b/packages/1-framework/1-core/framework-components/test/type-choice-assembly.test.ts index ce1bd6b78cd8..b9c14ecc7d0b 100644 --- a/packages/1-framework/1-core/framework-components/test/type-choice-assembly.test.ts +++ b/packages/1-framework/1-core/framework-components/test/type-choice-assembly.test.ts @@ -42,31 +42,25 @@ const constructors = { WideText: { kind: 'typeConstructor', output: { codecId: 'test/wide-text@1' } }, } as const satisfies AuthoringTypeNamespace; -const check = (moment: AuthoringTypeChoiceDescriptor, assembled: AuthoringTypeNamespace = {}) => { - const contributed = { Moment: moment }; - return () => - assertTypeChoicesValid(contributed, 'x-pack', { - ...constructors, - ...contributed, - ...assembled, - }); -}; +const check = (moment: AuthoringTypeChoiceDescriptor) => () => + assertTypeChoicesValid({ ...constructors, Moment: moment }, 'x-pack'); describe('assertTypeChoicesValid', () => { it('passes a choice whose rows name constructors of its namespace', () => { expect(check(choice())).not.toThrow(); }); - it('passes a choice naming a constructor another component registers', () => { - const contributed = { Moment: choice() }; + it('refuses a choice naming a constructor only another component registers', () => { expect(() => - assertTypeChoicesValid(contributed, 'x-pack', { ...contributed, ...constructors }), - ).not.toThrow(); + assertTypeChoicesValid({ Moment: choice(), WideText: constructors.WideText }, 'x-pack'), + ).toThrow( + 'Type choice "Moment" contributed by "x-pack" chooses "WideDate", which is not a type constructor "x-pack" registers beside it.', + ); }); it('refuses a row naming a constructor no component registers, naming the pack and the name', () => { expect(check(choice({ choices: [{ when: { as: 'date' }, type: 'Missing' }] }))).toThrow( - 'Type choice "Moment" contributed by "x-pack" chooses "Missing", which is not a type constructor any component registers beside it.', + 'Type choice "Moment" contributed by "x-pack" chooses "Missing", which is not a type constructor "x-pack" registers beside it.', ); }); @@ -157,6 +151,38 @@ describe('assertTypeChoicesValid', () => { }); describe('createControlStack', () => { + it('refuses a type choice naming a constructor another component registers', () => { + const input = { + family: { + kind: 'family', + id: 'fam', + version: '0.0.1', + authoring: { type: { WideText: constructors.WideText } }, + }, + target: { + kind: 'target', + id: 'tgt', + version: '0.0.1', + authoring: { + type: { + Moment: choice({ + args: [asOption], + choices: [ + { when: { as: 'date' }, type: 'WideText' }, + { when: { as: 'text' }, type: 'WideText' }, + ], + }), + }, + }, + }, + extensions: [], + } as unknown as CreateControlStackInput; + + expect(() => createControlStack(input)).toThrow( + 'Type choice "Moment" contributed by "tgt" chooses "WideText", which is not a type constructor "tgt" registers beside it.', + ); + }); + it('refuses a type choice naming a constructor no component registers', () => { const input = { family: { kind: 'family', id: 'fam', version: '0.0.1' }, diff --git a/packages/2-sql-family/2-authoring/contract-ts/src/composed-authoring-helpers.ts b/packages/2-sql-family/2-authoring/contract-ts/src/composed-authoring-helpers.ts index 5d35d95c4e10..67371c379849 100644 --- a/packages/2-sql-family/2-authoring/contract-ts/src/composed-authoring-helpers.ts +++ b/packages/2-sql-family/2-authoring/contract-ts/src/composed-authoring-helpers.ts @@ -517,11 +517,7 @@ export function createComposedAuthoringHelpers< assertNoCrossRegistryCollisions(typeNamespace, fieldNamespace, entityNamespace); assertNoBuiltInEntityCollisions(entityNamespace); for (const component of components) { - assertTypeChoicesValid( - component.authoring?.type ?? {}, - component.id ?? '', - typeNamespace, - ); + assertTypeChoicesValid(component.authoring?.type ?? {}, component.id ?? ''); } const { support } = assembleDataTypeSupport( components, From 03df270089caebcda02acf05666277452b21f7a3 Mon Sep 17 00:00:00 2001 From: willbot Date: Fri, 23 Oct 2026 18:41:58 +0200 Subject: [PATCH 100/118] Label an undocumented type choice "Convenience type" in completion It showed "Configured scalar type", the label of type constructors; the glossary's word for it is convenience type. Co-Authored-By: Claude Opus 5.5 Signed-off-by: willbot Signed-off-by: Will Madden --- .../language-server/src/completion-scope.ts | 2 +- .../test/completion-provider.test.ts | 13 ++++++++++++- 2 files changed, 13 insertions(+), 2 deletions(-) diff --git a/packages/1-framework/3-tooling/language-server/src/completion-scope.ts b/packages/1-framework/3-tooling/language-server/src/completion-scope.ts index fe9ef4c0eb0b..79dd22c2b5ce 100644 --- a/packages/1-framework/3-tooling/language-server/src/completion-scope.ts +++ b/packages/1-framework/3-tooling/language-server/src/completion-scope.ts @@ -89,7 +89,7 @@ export function scopeCompletionItems( item.kind = CompletionItemKind.Class; item.detail = descriptor.documentation || - (descriptor.kind === 'typeAlias' ? 'Type alias' : 'Configured scalar type'); + (descriptor.kind === 'typeAlias' ? 'Type alias' : 'Convenience type'); break; } const callable = diff --git a/packages/1-framework/3-tooling/language-server/test/completion-provider.test.ts b/packages/1-framework/3-tooling/language-server/test/completion-provider.test.ts index 06baff941994..302c16ac371e 100644 --- a/packages/1-framework/3-tooling/language-server/test/completion-provider.test.ts +++ b/packages/1-framework/3-tooling/language-server/test/completion-provider.test.ts @@ -262,6 +262,11 @@ namespace other { model Hidden { id Int } }`, { when: { as: 'text' }, type: 'Stamp' }, ], }, + Undocumented: { + kind: 'typeChoice', + args: [], + choices: [{ when: {}, type: 'Stamp' }], + }, Stamp: { kind: 'typeConstructor', output: { codecId: 'fixture/value' } }, }, }, @@ -270,7 +275,7 @@ namespace other { model Hidden { id Int } }`, }); expect( items - .filter((item) => item.label === 'Moment') + .filter((item) => item.label === 'Moment' || item.label === 'Undocumented') .map((item) => ({ kind: item.kind, detail: item.detail, @@ -284,6 +289,12 @@ namespace other { model Hidden { id Int } }`, newText: 'Moment', format: undefined, }, + { + kind: CompletionItemKind.Class, + detail: 'Convenience type', + newText: 'Undocumented', + format: undefined, + }, ]); }); From 0977c233fec07ce3d26a0d655f62e86215fc9bdf Mon Sep 17 00:00:00 2001 From: willbot Date: Fri, 23 Oct 2026 18:42:04 +0200 Subject: [PATCH 101/118] Say in the glossary that convenience and explicit types exist in TypeScript too Both entries began "A PSL type", but ADR 279 declares them for PSL and the TypeScript builder alike. Co-Authored-By: Claude Opus 5.5 Signed-off-by: willbot Signed-off-by: Will Madden --- docs/glossary.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/glossary.md b/docs/glossary.md index 733df434055e..2390e59b894e 100644 --- a/docs/glossary.md +++ b/docs/glossary.md @@ -215,11 +215,11 @@ Handles the translation between JavaScript values and database values. When you ### Convenience type -A PSL type named for what a field means in the application, such as `DateTime`, `Date`, `Time` or `Duration`, that becomes exactly one explicit type. Optional arguments choose which, with defaults. The contract stores only the explicit type. See ADR 279. +A type, in PSL or the TypeScript builder (`type.DateTime(...)`), named for what a field means in the application, such as `DateTime`, `Date`, `Time` or `Duration`, that becomes exactly one explicit type. Optional arguments choose which, with defaults. The contract stores only the explicit type. See ADR 279. ### Explicit type -A PSL type that fixes one codec, so it fixes both the column type and the value the application reads. It is named after the database, with a suffix for the kind of value when a column type has several codecs: `TimestamptzTemporal`, `TimestamptzJsDate`, `DateString`. See ADR 257 and ADR 279. +A type, in PSL or the TypeScript builder, that fixes one codec, so it fixes both the column type and the value the application reads. It is named after the database, with a suffix for the kind of value when a column type has several codecs: `TimestamptzTemporal`, `TimestamptzJsDate`, `DateString`. See ADR 257 and ADR 279. ### Marker From 4611823ae27932ec1d650d70c2c8e968beac1e97 Mon Sep 17 00:00:00 2001 From: willbot Date: Fri, 23 Oct 2026 18:45:30 +0200 Subject: [PATCH 102/118] Let the type constructor lookup read a PSL namespace that holds field presets The PSL binder's contributed namespace holds field presets beside types, so typeConstructorLookup takes any level of named descriptors again, as it did before the type choice module moved onto the authoring module's kind checks. Co-Authored-By: Claude Opus 5.5 Signed-off-by: willbot Signed-off-by: Will Madden --- .../src/shared/authoring-type-choice.ts | 8 ++++---- .../src/shared/framework-authoring.ts | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/packages/1-framework/1-core/framework-components/src/shared/authoring-type-choice.ts b/packages/1-framework/1-core/framework-components/src/shared/authoring-type-choice.ts index 9ec0d0dbea94..00c7f862cc52 100644 --- a/packages/1-framework/1-core/framework-components/src/shared/authoring-type-choice.ts +++ b/packages/1-framework/1-core/framework-components/src/shared/authoring-type-choice.ts @@ -209,11 +209,11 @@ export function resolveAuthoringTypeChoice(input: { } /** Looks a type constructor up by name in one level of a type namespace, as a type choice's rows name them. */ -export function typeConstructorLookup( - namespace: AuthoringTypeNamespace, -): (name: string) => AuthoringTypeConstructorDescriptor | undefined { +export function typeConstructorLookup(namespace: { + readonly [name: string]: object; +}): (name: string) => AuthoringTypeConstructorDescriptor | undefined { return (name) => { - const member = memberNamed(namespace, name); + const member = Object.hasOwn(namespace, name) ? namespace[name] : undefined; return member !== undefined && isAuthoringTypeConstructorDescriptor(member) ? member : undefined; diff --git a/packages/1-framework/1-core/framework-components/src/shared/framework-authoring.ts b/packages/1-framework/1-core/framework-components/src/shared/framework-authoring.ts index bff99f207c7e..d71fe83c6704 100644 --- a/packages/1-framework/1-core/framework-components/src/shared/framework-authoring.ts +++ b/packages/1-framework/1-core/framework-components/src/shared/framework-authoring.ts @@ -723,7 +723,7 @@ function readTemplateArgumentValue( } export function isAuthoringTypeConstructorDescriptor( - value: AuthoringTypeNamespaceMember, + value: object, ): value is AuthoringTypeConstructorDescriptor { return 'kind' in value && value.kind === 'typeConstructor'; } From ecb621c145013e3ede1e8dbfef66fcc8d650127e Mon Sep 17 00:00:00 2001 From: willbot Date: Fri, 23 Oct 2026 19:01:19 +0200 Subject: [PATCH 103/118] The project spec and plan describe the type choice, the one-object TypeScript helpers and ADR 279 as accepted Co-Authored-By: Claude Opus 5.5 Signed-off-by: willbot Signed-off-by: Will Madden --- .../datetime-convenience-types/design-notes.md | 4 ++-- projects/datetime-convenience-types/plan.md | 4 ++-- .../slices/04-convenience-types/spec.md | 4 ++-- .../05-tools-write-convenience-types/spec.md | 2 +- projects/datetime-convenience-types/spec.md | 17 ++++++++--------- 5 files changed, 15 insertions(+), 16 deletions(-) diff --git a/projects/datetime-convenience-types/design-notes.md b/projects/datetime-convenience-types/design-notes.md index ddc9bc11c81d..4e0257d47cab 100644 --- a/projects/datetime-convenience-types/design-notes.md +++ b/projects/datetime-convenience-types/design-notes.md @@ -39,7 +39,7 @@ Record of the design discussion between the operator and the orchestrator on 202 **The Prisma 7 reader gives back `Date`.** Prisma 7's `DateTime` is a `timestamp(3)` column read as a `Date`. Mapping it to `DateTime(storage: timestamp)` keeps an upgraded app's code compiling. This reverses the choice made when inferred and Prisma 7 columns moved to string types, which rejected `Date` only because no `timestamp` `Date` codec existed. -**TypeScript follows the `type.*` helpers.** The field-preset removal project gives every PSL type constructor a `type.*` helper. This project makes the builder's compile-time resolver evaluate a codec chosen by arguments, so `type.DateTime(...)` types precisely once the helpers land, instead of building a separate TypeScript surface now. +**TypeScript helpers take one object.** `type.DateTime({ as: 'string', precision: 6 })`, with one overload per row of the table, so a call is typed as the explicit type it picks and an unlisted combination does not compile (settled 2026-10-23). Explicit constructors stay positional. **PostgreSQL only.** A convenience name must mean the same on every target. SQLite's `DateTime` and MongoDB's `Date` already use the names for explicit types, and MongoDB's `Date` is a moment in time. Resolving them is TML-3714. @@ -66,6 +66,6 @@ None. Settled on 2026-10-22: ## References -- ADR 279 (draft) and ADR 241, 246, 254, 257, 275. +- ADR 279 and ADR 241, 254, 257, 275. - Architecture overview: "Domain-first surfaces", "Explicit over implicit". - The research report (private artifact, 2026-10-21). diff --git a/projects/datetime-convenience-types/plan.md b/projects/datetime-convenience-types/plan.md index e950e732b29e..45090e7cf6ab 100644 --- a/projects/datetime-convenience-types/plan.md +++ b/projects/datetime-convenience-types/plan.md @@ -32,7 +32,7 @@ Renames outright, with no deprecated aliases before RC: `Timestamptz` → `Times ### Slice 4 — The convenience types -A type constructor's `codecId` may be a `select` over `option` arguments; every reader of a constructor's output resolves it. The four types with the spec's choices and defaults; an unlisted combination is an error at the argument. `DateTime`, `Date` and `Time` take over their names (breaking, with upgrade instructions). The TypeScript builder's type-level resolver evaluates the choice. Hover text and completion list each type's choices. ADR 279 moves to Accepted, with notes in ADR 246, 254, 257 and 275. +A new framework declaration, the type choice, picks one explicit type constructor from a table by its `as:` and `storage:` options; every reader of a field's codec resolves the call first. The four types with the spec's choices and defaults; an unlisted combination is an error at the argument. `DateTime`, `Date` and `Time` take over their names (breaking, with upgrade instructions). The TypeScript `type.*` helpers take one object and are typed per row. Hover text and completion list each type's choices. ADR 254, 257 and 275 get notes pointing to ADR 279. ### Slice 5 — The tools write convenience types @@ -48,7 +48,7 @@ A type constructor's `codecId` may be a `select` over `option` arguments; every ## Dependencies -- **Field-preset removal project, slice 2.** Delivers the `type.*` helpers that expose slice 4's types in TypeScript, and the contributed aliases built on `DateTime`. Not blocking; coordinate so its `temporal.CreatedAt` and `temporal.UpdatedAt` land on `DateTime` once slice 4 merges. +- **Field-preset removal project.** Its remaining slices remove the presets; slice 5 here rebuilds its contributed `temporal.CreatedAt` and `temporal.UpdatedAt` on `DateTime`. - **PostgreSQL and SQLite rename project** (not started). Must not remove `DateTime` and must take slice 3's names. Its spec gets a note in this project's shaping PR. - **Release.** Users get the convenience types with the first `@prisma/orm-postgres` release after slice 4 merges. diff --git a/projects/datetime-convenience-types/slices/04-convenience-types/spec.md b/projects/datetime-convenience-types/slices/04-convenience-types/spec.md index be848506e868..a0b1982bede8 100644 --- a/projects/datetime-convenience-types/slices/04-convenience-types/spec.md +++ b/projects/datetime-convenience-types/slices/04-convenience-types/spec.md @@ -29,9 +29,9 @@ model Order { 2. **Every reader resolves the field's call.** The PSL column resolution, the TypeScript builder's runtime and type-level resolution (`type.*` helpers), the contract-prisma7 values path, and every place that today reads a constructor's fixed `output.codecId` without the call's arguments: `@default` literal admission (`receivingDataType`) and the `@onCreate`/`@onUpdate` generator lookup (`generatorCallArm`) must use the codec the field's call resolves to, including through a `types {}` alias. The control stack checks every reachable explicit constructor at assembly. 3. **Bare names are scalars.** `DateTime`, `Date`, `Time` and `Duration` work bare and called; the scalar view treats them like any all-optional constructor. 4. **`inferred` marks stay on the explicit constructors** in this slice; a type choice is never inferred. `contract print` and `contract infer` keep writing explicit names until slice 5. -5. **The four PostgreSQL types and their tables** are exactly ADR 279 item 5 and the project spec's decision 3, with defaults: `DateTime` → `jsDate`, `timestamptz`, precision 3 for `jsDate` else none; `Date` → `string`; `Time` → `string`, `time`; `Duration` → `string`. +5. **The four PostgreSQL types and their tables** are exactly ADR 279's convenience types table and the project spec's decision 3, with defaults: `DateTime` → `jsDate`, `timestamptz`, precision 3 for `jsDate` else none; `Date` → `string`; `Time` → `string`, `time`; `Duration` → `string`. 6. **The old meanings move to explicit names.** Every existing Prisma 8 PostgreSQL schema in the repository that writes `DateTime`, `Date` or `Time` (examples, apps, integration fixtures, test PSL strings, docs) is rewritten mechanically to the explicit type it means today (`TimestamptzTemporal`, `DateTemporal`, `TimeTemporal`), so no committed `contract.json` or hash changes. The `Date` and `Time` second names and `DateTime`'s current constructor are removed. The upgrade instruction gives users the same rewrite and says the bare names now choose different values. -7. **TypeScript builder.** `type.DateTime(...)`, `type.Date(...)`, `type.Time(...)`, `type.Duration(...)` exist through the `type.*` helpers, positional, with a type-level resolution that yields the explicit type's codec and application type for literal arguments (a type test per table row). +7. **TypeScript builder.** `type.DateTime(...)`, `type.Date(...)`, `type.Time(...)`, `type.Duration(...)` exist through the `type.*` helpers and take one object (`type.DateTime({ as: 'string', precision: 6 })`), with one overload per row and a type-level resolution that yields the explicit type's codec and application type for literal arguments (a type test per table row). 8. **Language server.** Hover on a convenience type shows each argument's choices as PSL writes them (bare names) and which explicit type each combination becomes; completion of a bare convenience type inserts the bare name. 9. **ADR updates in the same PR:** ADR 279 to Accepted, its "How it works" describing the type-choice descriptor; notes in ADR 254 (a type constructor may resolve to another constructor), ADR 257 (unsuffixed names belong to convenience types), ADR 275 (the contributed `temporal.*` aliases are built on `DateTime`; the `String`/`JsDate` variants are not needed). diff --git a/projects/datetime-convenience-types/slices/05-tools-write-convenience-types/spec.md b/projects/datetime-convenience-types/slices/05-tools-write-convenience-types/spec.md index 465889326062..122c690a601b 100644 --- a/projects/datetime-convenience-types/slices/05-tools-write-convenience-types/spec.md +++ b/projects/datetime-convenience-types/slices/05-tools-write-convenience-types/spec.md @@ -1,6 +1,6 @@ # Slice 5 — `contract infer`, `contract print`, the Prisma 7 reader and `orm init` write the convenience types -**Linear:** [TML-3713](https://linear.app/prisma-company/issue/TML-3713) · **Parent:** [project spec](../../spec.md), decision 10 · **Design record:** ADR 279, items 8 and 10 · **Builds on:** slice 4 (the type choices) and slice 2 (the Prisma 7 `now()` mapping) +**Linear:** [TML-3713](https://linear.app/prisma-company/issue/TML-3713) · **Parent:** [project spec](../../spec.md), decision 10 · **Design record:** ADR 279, "What the tools write" · **Builds on:** slice 4 (the type choices) and slice 2 (the Prisma 7 `now()` mapping) ## At a glance diff --git a/projects/datetime-convenience-types/spec.md b/projects/datetime-convenience-types/spec.md index 127b70ed326d..258c3d2312b8 100644 --- a/projects/datetime-convenience-types/spec.md +++ b/projects/datetime-convenience-types/spec.md @@ -49,25 +49,24 @@ The reasoning and the alternatives rejected are in [`design-notes.md`](./design- 9. **Temporal fields are never typed `any`.** The Temporal codecs and generators take their types from `temporal-spec`, a types-only package, instead of an undeclared global. No polyfill code is added anywhere. A missing global `Temporal` at runtime is still reported on first use, as today; a startup check was considered and dropped, because Temporal values are now an explicit opt-in. 10. **`contract infer` and `contract print` write convenience types**, with arguments only where they differ from the defaults; a column no convenience type covers (`Temporal.PlainDateTime`) is written as its explicit type. **The Prisma 7 reader** maps Prisma 7's `DateTime` to `TimestampJsDate(3)`, printed `DateTime(storage: timestamp)`, so an upgraded app reads `Date` values again, and maps Prisma 7's `@default(now())` to `@default(now()) @onCreate(now())`, as Prisma 7 behaved ([TML-3719](https://linear.app/prisma-company/issue/TML-3719)). 11. **PostgreSQL only.** A convenience name means the same thing on every target; SQLite and MongoDB follow in [TML-3714](https://linear.app/prisma-company/issue/TML-3714). -12. **TypeScript:** this project ships the PSL types and makes the TypeScript builder's compile-time resolver evaluate a codec chosen by arguments. The `type.DateTime(...)` helper itself arrives with the `type.*` helpers of the field-preset removal project. +12. **TypeScript:** `type.DateTime(...)`, `type.Date(...)`, `type.Time(...)` and `type.Duration(...)` take their arguments as one object, `type.DateTime({ as: 'string', precision: 6 })`, with one overload per row, so a call is typed as the explicit type it picks and a combination with no row does not compile. Explicit constructors stay positional. Settled 2026-10-23 with the option names `as:` and `storage:`. ## Non-goals - SQLite and MongoDB convenience types ([TML-3714](https://linear.app/prisma-company/issue/TML-3714)). - Convenience types for other scalars, such as an `Int` choosing between `Int2`, `Int4` and `Int8` ([TML-3715](https://linear.app/prisma-company/issue/TML-3715)). -- The `type.*` helpers in the TypeScript builder (field-preset removal project, its second slice). -- `temporal.CreatedAt` and `temporal.UpdatedAt`. They belong to the field-preset removal project; this project only says they are built on `DateTime` (ADR 279). +- The rest of the field-preset removal. This project rebuilds only the contributed `temporal.CreatedAt` and `temporal.UpdatedAt` aliases on `DateTime` (slice 5). - A `Temporal.PlainDateTime` convenience form. It stays reachable as the explicit `TimestampTemporal`. - Bundling a Temporal polyfill. - Any change to how values are stored: the contract form of a value (ADR 254) and every existing codec's stored form stay as they are. ## Place in the larger world -- **ADR 254, data types and casts.** Codecs, data types and stored forms are unchanged. A type constructor gains the ability to choose its codec, which amends the definition there. The text codecs today keep PostgreSQL's year text (` BC`); decision 8 changes that to the ISO form, which amends the "Date and time types" section. +- **ADR 254, data types and casts.** Codecs, data types and stored forms are unchanged. A type constructor still names exactly one codec; a type choice is a second kind of declaration that resolves to a type constructor. The text codecs today keep PostgreSQL's year text (` BC`); decision 8 changes that to the ISO form, which amends the "Date and time types" section. - **ADR 257, scalar names.** The explicit layer follows it. The `Temporal` suffix applies the suffix rule to every codec of a multi-codec column. Its one-release deprecation rule is not applied before RC: renamed names become errors at once. -- **ADR 246 and ADR 275.** The `option` argument kind and the `select` template, which ADR 275 removes with the presets, are kept for type constructors. +- **ADR 275.** The `option` argument kind and the `select` template stay removed with the presets; a type choice has its own option arguments. The contributed `temporal.*` aliases are built on `DateTime`. - **The PostgreSQL and SQLite rename project** (`projects/target-named-scalars-sql/`, not started) must keep `DateTime` and take this project's renames instead of its own `DateTime` and `Timestamptz` rows. -- **The field-preset removal project** (`projects/remove-field-presets/`, slice 1 merged): its `type.*` helpers carry the convenience types into TypeScript, and its contributed aliases are built on `DateTime`; its `String` and `JsDate` alias variants are not needed. +- **The field-preset removal project** (`projects/remove-field-presets/`, slice 1 merged): its contributed aliases are built on `DateTime` (slice 5), and their `String` and `JsDate` variants are removed. - **`prisma orm init`** writes `TimestamptzString` and `temporal.updatedAtString()` today. It moves to the convenience types in slice 5. ## Cross-cutting requirements @@ -95,7 +94,7 @@ PostgreSQL target and adapter only: data-type-bound codecs, type constructors, t ## ADR pointer -ADR 279 (draft in this project's shaping PR, status Proposed until slice 4 merges). It amends ADR 254, 257 and 275 and brings back ADR 246 for type constructors; each amended ADR gets a note in the slice that implements the change. +ADR 279, Accepted. It builds on ADR 241, 254 and 257 and amends ADR 275; ADR 254, 257 and 275 get a note in the slice that implements the change. ## Project DoD @@ -104,7 +103,7 @@ Inherits the team floor in [`drive/calibration/dod.md`](../../drive/calibration/ - [ ] Every row of the decision 3 table works end to end: a PSL schema emits, `migration plan` produces the expected column DDL, and a query on PostgreSQL reads and writes the expected value type, with and without a global `Temporal` as each row requires. - [ ] A new project from `prisma orm init`, and a Prisma 7 project adopted with `prisma7Schema(...)`, run their first query on Node with no polyfill and read `Date` values for their timestamps; a create through Prisma 8 on an adopted project, on a server whose `TimeZone` is not UTC, stores UTC. - [ ] The report's use-case table (see References) re-run against the shipped surface, with each row's Prisma 8 cell updated. -- [ ] ADR 279 accepted; ADR 246, 254, 257 and 275 carry notes; `docs/reference/scalar-types.md` describes both layers. +- [ ] ADR 279 accepted; ADR 254, 257 and 275 carry notes; `docs/reference/scalar-types.md` describes both layers. - [ ] TML-3714 and TML-3715 remain open with their context. ## Open questions @@ -113,7 +112,7 @@ Inherits the team floor in [`drive/calibration/dod.md`](../../drive/calibration/ ## References -- ADR 279 (draft) and its amended ADRs: 241, 246, 254, 257, 275. +- ADR 279 and the ADRs it builds on or amends: 241, 254, 257, 275. - The research report comparing Prisma 7 and Prisma 8 date and time fields on PostgreSQL (2026-10-21, published as a private artifact; ask the operator). - [TML-3418](https://linear.app/prisma-company/issue/TML-3418), the server-dependent string bug, now slice 1. - The pull request that moved inferred and Prisma 7 date columns to string types: prisma/orm#30553. From b632adcc4598ba98773c5b85dae18e39dbc5144d Mon Sep 17 00:00:00 2001 From: willbot Date: Tue, 27 Oct 2026 18:19:39 +0100 Subject: [PATCH 104/118] contract infer picks a type that reads the column's own default When the codec of the default-path type cannot read a column's reported default as a row (its fromWire refuses the literal), infer writes the first row of the same type choice whose codec can. A timestamp(3) column with DEFAULT 'infinity' is now DateTime(as: string, storage: timestamp, precision: 3) @default(timestamp`infinity`), so rows created with the default read back instead of failing in the Date codec. A default the codec reads but cannot hold exactly (microseconds on a Date) keeps the Date type and its raw SQL default, as before. Co-Authored-By: Claude Opus 5.5 Signed-off-by: willbot Signed-off-by: Will Madden --- docs/reference/scalar-types.md | 2 +- packages/2-sql-family/9-family/README.md | 2 +- .../src/core/psl-build/column-types.ts | 69 +++++++++-- .../psl-build/inferred-type-constructors.ts | 31 ++++- .../9-family/src/exports/psl-build.ts | 1 + .../inferred-type-constructors.test.ts | 90 +++++++++++--- .../3-targets/postgres/1-target/README.md | 2 +- .../src/core/psl-infer/infer-psl-contract.ts | 9 +- .../src/core/psl-infer/infer-read-defaults.ts | 26 +++- .../1-target/test/psl-infer/fixtures.ts | 3 +- .../inferred-psl.data-type-defaults.test.ts | 23 +--- .../inferred-psl/inferred-psl.enums.test.ts | 35 +++--- .../inferred-psl.top-level-blocks.test.ts | 17 +-- .../inferred-psl.type-reading-default.test.ts | 60 ++++++++++ ...trip-fidelity.prisma7-defaults.e2e.test.ts | 20 ++-- ...nfer-roundtrip-runtime.integration.test.ts | 111 +++++++++++++----- 16 files changed, 388 insertions(+), 113 deletions(-) create mode 100644 packages/3-targets/postgres/1-target/test/psl-infer/inferred-psl/inferred-psl.type-reading-default.test.ts diff --git a/docs/reference/scalar-types.md b/docs/reference/scalar-types.md index d0a3b476f446..719200c8225b 100644 --- a/docs/reference/scalar-types.md +++ b/docs/reference/scalar-types.md @@ -130,7 +130,7 @@ A literal default of a date, time, interval or bytes type is written with the ta The tools that write a schema for you write the convenience types, with only the arguments that differ from the defaults, and the explicit type only where no convenience call becomes it, such as `TimestampTemporal` for a `timestamp` read as `Temporal.PlainDateTime`. - `prisma orm init` writes `createdAt DateTime @default(now()) @onCreate(now())` and `updatedAt temporal.UpdatedAt` in a PSL schema, and `field.column(type.DateTime()).default(now()).onCreate(gen.now())` and `field.column(type.temporal.UpdatedAt())` in a TypeScript contract. -- `prisma contract infer` takes the default path for each column: a `timestamptz(3)` column is `DateTime`, a `timestamp(3)` column `DateTime(storage: timestamp)`, a `date` column `Date`, a `time(0)` column `Time(precision: 0)`, a `timetz` column `Time(storage: timetz)` and an `interval` column `Duration`. A column with no declared precision holds microseconds, as precision 6 does, so a `timestamptz` column is `DateTime(precision: 6)`; `db verify` and the migration planner count an omitted precision on a date or time column as 6. A `Date` holds milliseconds, so a timestamp column with more digits reads with them dropped, and a timestamp default a `Date` cannot hold, such as one with microseconds or `infinity`, is written as its raw `sql` expression. To keep the digits, change the field to `DateTime(as: temporal)` or `DateTime(as: string)`. +- `prisma contract infer` takes the default path for each column: a `timestamptz(3)` column is `DateTime`, a `timestamp(3)` column `DateTime(storage: timestamp)`, a `date` column `Date`, a `time(0)` column `Time(precision: 0)`, a `timetz` column `Time(storage: timetz)` and an `interval` column `Duration`. A column with no declared precision holds microseconds, as precision 6 does, so a `timestamptz` column is `DateTime(precision: 6)`; `db verify` and the migration planner count an omitted precision on a date or time column as 6. A `Date` holds milliseconds, so a timestamp column with more digits reads with them dropped, and a timestamp default with digits below a millisecond is written as its raw `sql` expression. To keep the digits, change the field to `DateTime(as: temporal)` or `DateTime(as: string)`. When the default-path type cannot read the column's own default as a row at all, as a `Date` cannot read `infinity` or `-infinity`, infer writes the first row of the same convenience type whose type can, which for those values is `as: string`: a `timestamp(3)` column with `DEFAULT 'infinity'` is ``DateTime(as: string, storage: timestamp, precision: 3) @default(timestamp`infinity`)``. - `prisma contract print` writes each column as the convenience call that becomes its explicit type exactly, such as `DateTime(as: temporal)` for `TimestamptzTemporal`, and otherwise the explicit type. - `prisma7Schema(...)` reads Prisma 7's `DateTime` as `TimestampJsDate(3)`, a `timestamp(3)` column read as a `Date` as Prisma 7 read it, printed `DateTime(storage: timestamp)`, and `@db.Timestamptz(p)` as `TimestamptzJsDate(p)`. diff --git a/packages/2-sql-family/9-family/README.md b/packages/2-sql-family/9-family/README.md index 9dadf694b12e..2b3a2a404181 100644 --- a/packages/2-sql-family/9-family/README.md +++ b/packages/2-sql-family/9-family/README.md @@ -137,7 +137,7 @@ The runner returns structured errors with the following codes: - **`./control`**: Control plane entry point for CLI/config usage (exports `SqlFamilyDescriptor`) - **`./control-adapter`**: SQL control adapter interface (`SqlControlAdapter`, `SqlControlAdapterDescriptor`) for target-specific adapters -- **`./psl-build`**: PSL building blocks both `contract infer` and `contract print` use, with no dialect knowledge: `mapDefault` (a stored default as the PSL attribute that reads back as it), `toEnumMemberName`, `inferredTypeConstructors` (the constructor marked `inferred` for each data type of a stack), `fewestArgumentsCall` and `writtenTypeArguments` (the call infer and print write for a column's parameters), `writtenTypeChoiceCall` (the type choice call, written by name, that becomes a constructor call; ADR 279) and `sameInNormalForm` (the comparison infer uses for it), and `inferColumnTypes` (each introspected column's type, through the data type that claims its reported type, failing with `CONTRACT.INFER_COLUMN_TYPE_UNCLAIMED` on every column none writes) +- **`./psl-build`**: PSL building blocks both `contract infer` and `contract print` use, with no dialect knowledge: `mapDefault` (a stored default as the PSL attribute that reads back as it), `toEnumMemberName`, `inferredTypeConstructors` (the constructor marked `inferred` for each data type of a stack, with the other constructors of that data type), `fewestArgumentsCall` and `writtenTypeArguments` (the call infer and print write for a column's parameters), `writtenTypeChoiceCall` (the type choice call, written by name, that becomes a constructor call; ADR 279) and `sameInNormalForm` (the comparison infer uses for it), and `inferColumnTypes` (each introspected column's type, through the data type that claims its reported type, moving to another row of the same type choice when the target says the default-path codec cannot read the column default, failing with `CONTRACT.INFER_COLUMN_TYPE_UNCLAIMED` on every column none writes) - **`./psl-infer`**: Database-to-PSL inference utilities for `contract infer`: name transforms, relation inference, and the printer-config types - **`./runtime`**: Runtime plane identity exports only (family ID, types, descriptor identity). Does **not** export runtime creation helpers—use `instantiateExecutionStack` from `@internal/framework-components/execution` and `createExecutionContext`, `createRuntime`, `createSqlExecutionStack` from `@internal/sql-runtime`. See [ADR 152](../../../docs/architecture%20docs/adrs/ADR%20152%20-%20Execution%20Plane%20Descriptors%20and%20Instances.md). - **`./verify`**: Marker row parsing helper (`parseContractMarkerRow`). Marker reads are owned by each `SqlControlAdapter` (e.g. `PostgresControlAdapter.readMarker`) so dialect-specific SQL stays target-local. diff --git a/packages/2-sql-family/9-family/src/core/psl-build/column-types.ts b/packages/2-sql-family/9-family/src/core/psl-build/column-types.ts index b8f23d6a9cc0..cea4f079126e 100644 --- a/packages/2-sql-family/9-family/src/core/psl-build/column-types.ts +++ b/packages/2-sql-family/9-family/src/core/psl-build/column-types.ts @@ -2,6 +2,7 @@ import { type AuthoringTypeNamespace, authoringTypeConstructorArguments, type DataTypeSupport, + getAuthoringTypeChoice, } from '@internal/framework-components/authoring'; import type { DataType } from '@internal/framework-components/codec'; import { resolveReportedSqlType } from '@internal/sql-contract/data-type'; @@ -11,6 +12,7 @@ import { sqlFamilyError } from '../errors'; import { fewestArgumentsCall, type InferredTypeConstructor, + type InferredTypeConstructorPath, type InferredTypeConstructors, type PslTypeArgument, sameInNormalForm, @@ -28,6 +30,13 @@ export interface PslTypeCall { /** The PSL type `contract infer` writes for a column that is not an enum, by table and column name. */ export type ColumnTypeOf = (tableName: string, columnName: string) => PslTypeCall; +/** Whether the codec of `type` reads the default the database reports for `column` as a row value. */ +export type ColumnTypeReadsDefault = ( + tableName: string, + column: SqlColumnIR, + type: PslTypeCall, +) => Promise; + /** One introspected table, with the schema it was read from. */ export interface IntrospectedTable { readonly schemaName: string; @@ -47,7 +56,7 @@ function reportedTypeOf(column: SqlColumnIR, tableName: string): ReportedSqlType /** The call to the type choice that becomes `marked` with every parameter in `params`, compared in the normal form, as `db verify` compares the column, with the explicit call it becomes. */ function convenienceCall( - marked: InferredTypeConstructor, + marked: InferredTypeConstructorPath & Pick, params: Readonly>, authoringTypes: AuthoringTypeNamespace, support: DataTypeSupport, @@ -73,18 +82,58 @@ function convenienceCall( ); } -function writtenCall( +/** + * The first call to a row of the type choice `written` names whose type constructor represents the same data type, with the same parameters, and whose codec reads the column default. + */ +async function rowReadingDefault( + written: PslTypeCall, + marked: InferredTypeConstructor, + params: Readonly>, + authoringTypes: AuthoringTypeNamespace, + support: DataTypeSupport, + readsDefault: (type: PslTypeCall) => Promise, +): Promise { + const choice = getAuthoringTypeChoice({ type: authoringTypes }, written.path); + for (const row of choice?.descriptor.choices ?? []) { + const path = [...written.path.slice(0, -1), row.type].join('.'); + const alternative = marked.alternatives.find((candidate) => candidate.path.join('.') === path); + const call = + alternative && + convenienceCall( + { ...alternative, dataType: marked.dataType }, + params, + authoringTypes, + support, + ); + if (call !== undefined && (await readsDefault(call))) return call; + } + return undefined; +} + +async function writtenCall( constructors: InferredTypeConstructors, dataTypes: readonly DataType[], authoringTypes: AuthoringTypeNamespace, support: DataTypeSupport, reported: ReportedSqlType, -): PslTypeCall | undefined { + readsDefault: (type: PslTypeCall) => Promise, +): Promise { const resolved = resolveReportedSqlType(reported, dataTypes); const marked = resolved && constructors.get(resolved.dataType.id); if (resolved === undefined || marked === undefined) return undefined; const convenience = convenienceCall(marked, resolved.typeParams, authoringTypes, support); - if (convenience !== undefined) return convenience; + if (convenience !== undefined) { + if (await readsDefault(convenience)) return convenience; + const reading = await rowReadingDefault( + convenience, + marked, + resolved.typeParams, + authoringTypes, + support, + readsDefault, + ); + return reading ?? convenience; + } const call = fewestArgumentsCall(marked, resolved.typeParams); const args = call && writtenTypeArguments(marked.descriptor, call.args, support); return ( @@ -98,9 +147,9 @@ function writtenCall( } /** - * The type of every column of `tables` that `isEnumColumn` does not claim, through the constructor marked `inferred` for the data type that claims the column's reported type: the type choice call that becomes it, with only the arguments that differ from the defaults, or else the call to it with the fewest arguments. When any column has no such call, throws `CONTRACT.INFER_COLUMN_TYPE_UNCLAIMED` listing every one of them. + * The type of every column of `tables` that `isEnumColumn` does not claim, through the constructor marked `inferred` for the data type that claims the column's reported type: the type choice call that becomes it, with only the arguments that differ from the defaults, or else the call to it with the fewest arguments. When the codec of that type choice call cannot read the column default as a row, the first row of the same type choice whose codec can is written instead. When any column has no such call, throws `CONTRACT.INFER_COLUMN_TYPE_UNCLAIMED` listing every one of them. */ -export function inferColumnTypes(input: { +export async function inferColumnTypes(input: { readonly tables: readonly IntrospectedTable[]; readonly constructors: InferredTypeConstructors; /** The stack's type namespace, whose type choices infer writes where one becomes a marked constructor. */ @@ -108,7 +157,9 @@ export function inferColumnTypes(input: { /** The stack's data types, whose literals write each argument. */ readonly dataTypes: DataTypeSupport; readonly isEnumColumn: (column: SqlColumnIR) => boolean; -}): ColumnTypeOf { + /** Whether a type's codec reads the column default; every type does when `undefined`. */ + readonly readsDefault: ColumnTypeReadsDefault | undefined; +}): Promise { const dataTypes = [...input.constructors.values()].map((marked) => marked.dataType); const types = new Map(); const unclaimed: string[] = []; @@ -116,12 +167,14 @@ export function inferColumnTypes(input: { for (const column of columns) { if (input.isEnumColumn(column)) continue; const reported = reportedTypeOf(column, tableName); - const call = writtenCall( + const readsDefault = input.readsDefault; + const call = await writtenCall( input.constructors, dataTypes, input.authoringTypes, input.dataTypes, reported, + async (type) => (readsDefault === undefined ? true : readsDefault(tableName, column, type)), ); if (call === undefined) { unclaimed.push( diff --git a/packages/2-sql-family/9-family/src/core/psl-build/inferred-type-constructors.ts b/packages/2-sql-family/9-family/src/core/psl-build/inferred-type-constructors.ts index f92487c9e9a5..fadc8b45ee5e 100644 --- a/packages/2-sql-family/9-family/src/core/psl-build/inferred-type-constructors.ts +++ b/packages/2-sql-family/9-family/src/core/psl-build/inferred-type-constructors.ts @@ -33,6 +33,14 @@ export interface InferredTypeConstructor { readonly path: readonly string[]; readonly descriptor: AuthoringTypeConstructorDescriptor; readonly dataType: SqlDataType; + /** The other type constructors whose codec represents the same data type, in namespace order. */ + readonly alternatives: readonly InferredTypeConstructorPath[]; +} + +/** A type constructor by its path in an authoring namespace. */ +export interface InferredTypeConstructorPath { + readonly path: readonly string[]; + readonly descriptor: AuthoringTypeConstructorDescriptor; } /** The constructors marked `inferred` in a stack, keyed by the id of the data type each produces. */ @@ -44,15 +52,30 @@ export function inferredTypeConstructors(input: { readonly codecLookup: Pick; readonly dataTypeLookup: DataTypeLookup; }): InferredTypeConstructors { + const byDataType = new Map(); const found = new Map(); - for (const { path, descriptor } of authoringTypeConstructors(input.authoringTypes)) { - if (descriptor.inferred !== true) continue; + for (const typeConstructor of authoringTypeConstructors(input.authoringTypes)) { + const { path, descriptor } = typeConstructor; const dataTypeId = input.codecLookup.descriptorFor(descriptor.output.codecId)?.dataType; const dataType = dataTypeId === undefined ? undefined : input.dataTypeLookup.get(dataTypeId); if (dataType === undefined || !isSqlDataType(dataType)) continue; - found.set(dataType.id, { path, descriptor, dataType }); + const ofDataType = byDataType.get(dataType.id) ?? []; + ofDataType.push(typeConstructor); + byDataType.set(dataType.id, ofDataType); + if (descriptor.inferred !== true) continue; + found.set(dataType.id, { path, descriptor, dataType, alternatives: [] }); } - return found; + return new Map( + [...found].map(([id, marked]) => [ + id, + { + ...marked, + alternatives: (byDataType.get(id) ?? []).filter( + ({ descriptor }) => descriptor !== marked.descriptor, + ), + }, + ]), + ); } function normalParams( diff --git a/packages/2-sql-family/9-family/src/exports/psl-build.ts b/packages/2-sql-family/9-family/src/exports/psl-build.ts index c426b195edd1..72bdfd673fe5 100644 --- a/packages/2-sql-family/9-family/src/exports/psl-build.ts +++ b/packages/2-sql-family/9-family/src/exports/psl-build.ts @@ -6,6 +6,7 @@ export { type ColumnTypeOf, + type ColumnTypeReadsDefault, type IntrospectedTable, inferColumnTypes, type PslTypeCall, diff --git a/packages/2-sql-family/9-family/test/psl-build/inferred-type-constructors.test.ts b/packages/2-sql-family/9-family/test/psl-build/inferred-type-constructors.test.ts index 36e3ce383198..c9b22b38c1d8 100644 --- a/packages/2-sql-family/9-family/test/psl-build/inferred-type-constructors.test.ts +++ b/packages/2-sql-family/9-family/test/psl-build/inferred-type-constructors.test.ts @@ -9,7 +9,11 @@ import type { ReportedSqlType, SqlColumnIRInput } from '@internal/sql-schema-ir/ import { SqlColumnIR } from '@internal/sql-schema-ir/types'; import { type } from 'arktype'; import { describe, expect, it } from 'vitest'; -import { type IntrospectedTable, inferColumnTypes } from '../../src/core/psl-build/column-types'; +import { + type IntrospectedTable, + inferColumnTypes, + type PslTypeCall, +} from '../../src/core/psl-build/column-types'; import { fewestArgumentsCall, inferredTypeConstructors, @@ -280,17 +284,21 @@ describe('writtenTypeArguments', () => { }); describe('inferColumnTypes', () => { - const infer = (tables: readonly IntrospectedTable[]) => + const infer = ( + tables: readonly IntrospectedTable[], + readsDefault: Parameters[0]['readsDefault'] = undefined, + ) => inferColumnTypes({ tables, constructors, authoringTypes, dataTypes, isEnumColumn: () => false, + readsDefault, }); - it("writes each column as its data type's marked constructor, quoting a string argument", () => { - const typeOf = infer([ + it("writes each column as its data type's marked constructor, quoting a string argument", async () => { + const typeOf = await infer([ { schemaName: 'public', tableName: 'thing', @@ -319,8 +327,8 @@ describe('inferColumnTypes', () => { }); }); - it('writes the type choice that becomes the marked constructor, with the arguments that differ from its defaults', () => { - const typeOf = infer([ + it('writes the type choice that becomes the marked constructor, with the arguments that differ from its defaults', async () => { + const typeOf = await infer([ { schemaName: 'public', tableName: 'event', @@ -337,8 +345,8 @@ describe('inferColumnTypes', () => { }); }); - it('records the explicit call the written type choice call becomes, not the column parameters in normal form', () => { - const typeOf = infer([ + it('records the explicit call the written type choice call becomes, not the column parameters in normal form', async () => { + const typeOf = await infer([ { schemaName: 'public', tableName: 'shop', columns: [column('opens', catalog('clock'))] }, ]); expect(typeOf('shop', 'opens')).toEqual({ @@ -348,8 +356,8 @@ describe('inferColumnTypes', () => { }); }); - it('fails on every column no marked constructor writes, listing them sorted', () => { - expect(() => + it('fails on every column no marked constructor writes, listing them sorted', async () => { + await expect( infer([ { schemaName: 'public', @@ -358,7 +366,7 @@ describe('inferColumnTypes', () => { }, { schemaName: 'public', tableName: 'alpha', columns: [column('meta', catalog('hstore'))] }, ]), - ).toThrow( + ).rejects.toThrow( expect.objectContaining({ code: 'CONTRACT.INFER_COLUMN_TYPE_UNCLAIMED', fix: `No configured pack has a type for these columns. If a type comes from a database extension that has a Prisma 8 pack, add the pack to extensions in prisma.config.ts. Otherwise, to keep such a column, write the schema by hand and declare its field as Raw(sql\`\`) with the type as listed, or change the column's type or remove the column from the database.`, @@ -367,8 +375,8 @@ describe('inferColumnTypes', () => { ); }); - it('treats a column without a reported type as an internal error', () => { - expect(() => + it('treats a column without a reported type as an internal error', async () => { + await expect( infer([ { schemaName: 'public', @@ -376,6 +384,60 @@ describe('inferColumnTypes', () => { columns: [new SqlColumnIR({ name: 'id', typeText: 'int4', nullable: false })], }, ]), - ).toThrow(/without the type the database reported/); + ).rejects.toThrow(/without the type the database reported/); + }); + + describe('when the codec of the type choice call cannot read the column default', () => { + const event: readonly IntrospectedTable[] = [ + { schemaName: 'public', tableName: 'event', columns: [column('at', catalog('stamp(3)'))] }, + ]; + + it('writes the first row of the same type choice whose codec reads it', async () => { + const readsDefault = async (_table: string, _column: SqlColumnIR, call: PslTypeCall) => + call.explicit.path.join('.') !== 'StampDate'; + const typeOf = await infer(event, readsDefault); + expect(typeOf('event', 'at')).toEqual({ + path: ['Stamp'], + args: [ + { name: 'value', text: 'text' }, + { name: 'precision', text: '3' }, + ], + explicit: { path: ['StampText'], values: [3] }, + }); + }); + + it('keeps the type choice call when no row reads it', async () => { + const typeOf = await infer(event, async () => false); + expect(typeOf('event', 'at')).toEqual({ + path: ['Stamp'], + args: [], + explicit: { path: ['StampDate'], values: [3] }, + }); + }); + }); + + it('asks nothing of a column written as the fewest-arguments call of its constructor', async () => { + const asked: string[] = []; + const typeOf = await infer( + [ + { + schemaName: 'public', + tableName: 'thing', + columns: [column('amount', catalog('numeric(10,0)'))], + }, + ], + async (_table, column) => { + asked.push(column.name); + return false; + }, + ); + expect({ amount: typeOf('thing', 'amount'), asked }).toEqual({ + amount: { + path: ['Numeric'], + args: [{ name: undefined, text: '10' }], + explicit: { path: ['Numeric'], values: [10] }, + }, + asked: [], + }); }); }); diff --git a/packages/3-targets/postgres/1-target/README.md b/packages/3-targets/postgres/1-target/README.md index 7f19839e6b3d..2730b6a7bc43 100644 --- a/packages/3-targets/postgres/1-target/README.md +++ b/packages/3-targets/postgres/1-target/README.md @@ -27,7 +27,7 @@ Provides the Postgres target descriptor (`SqlControlTargetDescriptor`) for CLI c - **Database Dependency Consumption**: The planner extracts database dependencies from the configured framework components (passed as `frameworkComponents`), verifies each dependency against the live schema, and only emits install operations when required. The runner reuses the same metadata for post-apply verification, so there are no hardcoded extension mappings—database dependencies stay component-owned. - **Storage Type Planning**: The planner dispatches storage type hooks for `storage.types` and emits type operations before table creation when supported by the policy - **Contract-to-PSL Printing**: Implements the `buildPslContract` descriptor hook, which `prisma contract print` uses. It takes a Postgres contract the contract serializer accepted, from any source, and returns the Prisma 8 PSL document that reads back as the same contract. It does not check the contract's structure again; a broken structure is an internal error. It writes models, value objects, named types, domain and native enums, keys with their names, indexes, checks, relations, polymorphism, control policies, row-level security (`@@rls`, policies, and roles in `namespace unbound`) and defaults. A column is written as a type constructor the configured stack contributes that reads back with its codec, native type and type parameters: the constructor marked `inferred` for the column's data type, which is the one `contract infer` writes, when it does, or else the first constructor in the stack that does, such as `TimestampTemporal(3)` or an extension's `pgvector.Vector(3)`. Where a convenience type call becomes that constructor call exactly, the convenience call is written instead, by name with only the arguments that differ from its defaults: `DateTime` for `TimestamptzJsDate(3)`, `DateTime(as: temporal, precision: 3)` for `TimestamptzTemporal(3)` (ADR 279). A value-object member has no column, so its type goes through the stack the same way: the stack's codec names the native type for the member's type parameters, and the member is written as the type constructor for that native type, called with those parameters (`Numeric(10, 2)`); a member typed by a domain enum is written by the enum's name. The contract does not record which named type a member used, so a member written `code Short`, with `types { Short = VarChar(10) }`, is printed as the type it stands for, `code VarChar(10)`; a model field keeps `Short`, because its column names the named type. A literal default is written by the data type of the column's codec. The family passes the stack's type constructors, codecs and data types to the hook for this. Anything it cannot write so that it reads back the same is refused by name with `CONTRACT.PRINT_UNSUPPORTED` (the full list is in `docs/reference/error-reference.md`, one function per case in `src/core/psl-print/refusals.ts`); nothing is dropped. It lives in `src/core/psl-print/`, the counterpart of the `inferPslContract` hook in `src/core/psl-infer/`, which writes PSL from a live database. Both build with the shared builders in `src/core/psl-build/`: literals, index and check attributes, and the native enum block. Both find the constructor marked `inferred` for a data type through the SQL family's `@internal/family-sql/psl-build`. Model and field names come from the contract; `@@map` and `@map` appear only where the name the PSL source would derive differs. A unique constraint prints as `@@unique` and a unique index as `@@index(..., unique: true)`; every index prints explicitly, and a relation writes `index: false` when nothing backs its foreign key, no `index` argument when a relation that says nothing would be backed by the same object (`defaultForeignKeyIndex`), and otherwise `index: ""` naming the index, or the key whose first columns are the foreign key's. Execution generators join back onto their columns, through whatever the configured stack contributes, extensions included. A column that is not a list and whose PSL type stands for its data type prints as the call of the field preset that writes its codec with its generators, built from the preset's print form, when the preset accepts that call and it reads back with the column's codec, type params and generators. A column typed by a named type, a value object or an enum, and a column whose call fails those checks, such as one without a value for a required argument, does not. A column with only a create generator then prints it as `@default((...))`, the call of the default function that declares `printArguments` for it; any other column is refused. -- **Database-to-PSL inference**: Implements the `inferPslContract` descriptor hook, which `prisma contract infer` uses. Introspection records each column's element type as the database reports it (`format_type`, its kind, schema and name). Infer resolves that reported type through the data types of the configured stack, the target's and the extension packs', and writes the constructor marked `inferred` for the data type, with the fewest arguments that give the same parameters (`numeric(10,0)` is written `Numeric(10)`). For a date or time column the marked constructor is the one the convenience type's default gives, and infer writes the convenience call that becomes it, with the arguments that differ from the defaults and the column's parameters compared in normal form: `timestamptz(3)` is `DateTime`, a `timestamptz` with no declared precision `DateTime(precision: 6)`, `time(0)` `Time(precision: 0)`, `interval` `Duration`. A timestamp default a `Date` cannot hold, such as one with microseconds or `infinity`, is written as its raw `sql` expression; a list column adds `[]`, and a native enum column is written `pg.enum()`. When a column's type is claimed by no data type, or by one with no marked constructor (such as `tsquery`), infer writes nothing and fails with `CONTRACT.INFER_COLUMN_TYPE_UNCLAIMED`, listing every such column as `.
.: `. No configured pack has a type for such a column. When the type comes from a database extension that has a Prisma 8 pack, such as `pgvector` or `postgis`, adding the pack to `extensions` in `prisma.config.ts` lets infer write it, for the forms of the type the pack supports. Otherwise, as for domains, `"char"`, `hstore` and `tsquery`, the column's type must change, or the column must go. The rule itself (resolving a reported type through the data types, the fewest-arguments call, the unclaimed error) lives in the SQL family's `@internal/family-sql/psl-build`; the target adds the walk over its schema tree and the native enum handling in `src/core/psl-infer/`. +- **Database-to-PSL inference**: Implements the `inferPslContract` descriptor hook, which `prisma contract infer` uses. Introspection records each column's element type as the database reports it (`format_type`, its kind, schema and name). Infer resolves that reported type through the data types of the configured stack, the target's and the extension packs', and writes the constructor marked `inferred` for the data type, with the fewest arguments that give the same parameters (`numeric(10,0)` is written `Numeric(10)`). For a date or time column the marked constructor is the one the convenience type's default gives, and infer writes the convenience call that becomes it, with the arguments that differ from the defaults and the column's parameters compared in normal form: `timestamptz(3)` is `DateTime`, a `timestamptz` with no declared precision `DateTime(precision: 6)`, `time(0)` `Time(precision: 0)`, `interval` `Duration`. When the codec of that call cannot read the column's reported default as a row (its `fromWire` refuses the literal), infer writes the first row of the same convenience type whose codec can, so a `timestamp(3)` column with `DEFAULT 'infinity'` is `DateTime(as: string, storage: timestamp, precision: 3)`. A default that codec reads but whose value it cannot hold exactly, such as a timestamp with microseconds on a `Date` column, is written as its raw `sql` expression; a list column adds `[]`, and a native enum column is written `pg.enum()`. When a column's type is claimed by no data type, or by one with no marked constructor (such as `tsquery`), infer writes nothing and fails with `CONTRACT.INFER_COLUMN_TYPE_UNCLAIMED`, listing every such column as `.
.: `. No configured pack has a type for such a column. When the type comes from a database extension that has a Prisma 8 pack, such as `pgvector` or `postgis`, adding the pack to `extensions` in `prisma.config.ts` lets infer write it, for the forms of the type the pack supports. Otherwise, as for domains, `"char"`, `hstore` and `tsquery`, the column's type must change, or the column must go. The rule itself (resolving a reported type through the data types, the fewest-arguments call, the unclaimed error) lives in the SQL family's `@internal/family-sql/psl-build`; the target adds the walk over its schema tree and the native enum handling in `src/core/psl-infer/`. - **`Temporal` for the target's own code**: The `Temporal`-backed codecs (`pg/date-temporal@1`, `pg/time-temporal@1`, `pg/timestamp-temporal@1`, `pg/timestamp-instant@1` and `pg/timestamptz-temporal@1`), when they decode, and the `instantNow`, `plainDateTimeNow`, `plainDateNow` and `plainTimeNow` generators get `Temporal` from `requireTemporal()` (`src/core/require-temporal.ts`): the runtime's global `Temporal` when it has one, else the fallback `Temporal`, else it throws `RUNTIME.TEMPORAL_UNAVAILABLE`. The text codecs (`pg/date-string@1`, `pg/time-string@1`, `pg/timestamp-string@1`, `pg/timestamptz-string@1` and `pg/timetz@1`), `pg/timestamptz-date@1`, `pg/timestamp-date@1`, `pg/interval@1`, `pg/interval-string@1`, the `timestampNow` generator and the string generators (`timestampStringNow`, `timestamptzStringNow`, `dateStringNow`, `timeStringNow` and `timetzNow`) do not use `Temporal`, so an application that uses only them reads and writes its date and time columns without one. The target's control entry (`@internal/target-postgres/control`) sets the fallback `Temporal`, from `temporal-polyfill`, when it is loaded. Nothing sets `globalThis.Temporal`. No other entry of this package imports `temporal-polyfill`. `temporal-polyfill` is a required peer dependency of this package and of the published `@prisma/orm-target-postgres` and `@prisma/orm-postgres`, so a project that already installs it for its own code has one copy. npm, pnpm and bun install it automatically; a project that uses Yarn adds `temporal-polyfill` to its own dependencies. In the published package, `@prisma/orm-target-postgres/target` is the aggregate of every entry of this package, the control entry included, so it loads the control entry too; an application imports the entry it needs, for example `@prisma/orm-target-postgres/target/runtime`. The fallback is held once per process, and every `Temporal`-backed codec in the process uses it. An application process that loads no control-plane code and has no global `Temporal` gets `RUNTIME.TEMPORAL_UNAVAILABLE` when one of those codecs decodes or one of those generators runs. An application that loads control-plane code in its own process, such as server code under `vite dev` with the Prisma Vite plugin or a script that calls the control client, decodes dates without its own `Temporal`, and then fails in production. An application that uses the Temporal codecs must load its own `Temporal`. Every signature that names a `Temporal` type takes it from `temporal-spec`, a package of type declarations with no runtime code (`import type { Temporal } from 'temporal-spec'`), never from a global declaration, so a Temporal field is typed precisely in a project that has no global `Temporal` declarations. `tsconfig.no-temporal-global.json` type-checks `src` without them. - **Runtime List Framing**: Parses inbound Postgres array text for contract-declared list columns before applying the scalar element codec. Builtin arrays and enum arrays therefore share one target-owned decode path; fixed-scale `numeric(30,10)[]` reads database-normalized text such as `"1.5000000000"`, matching scalar numeric decoding rather than the previous driver numeric-array float spelling `"1.5"`. diff --git a/packages/3-targets/postgres/1-target/src/core/psl-infer/infer-psl-contract.ts b/packages/3-targets/postgres/1-target/src/core/psl-infer/infer-psl-contract.ts index 3e2e5f7ca471..470895138ddf 100644 --- a/packages/3-targets/postgres/1-target/src/core/psl-infer/infer-psl-contract.ts +++ b/packages/3-targets/postgres/1-target/src/core/psl-infer/infer-psl-contract.ts @@ -38,7 +38,11 @@ import { import { buildModel } from './infer-model-blocks'; import { buildFieldNamesByTable, buildTopLevelNameMap, topologicalSort } from './infer-names'; import { buildIntrospectedPolicyBlocks } from './infer-policy-blocks'; -import { type InferredDefaultOf, readInferredDefaults } from './infer-read-defaults'; +import { + columnTypeReadsDefault, + type InferredDefaultOf, + readInferredDefaults, +} from './infer-read-defaults'; import { createPostgresDefaultMapping } from './postgres-default-mapping'; /** @@ -218,7 +222,7 @@ export async function inferPostgresPslContract( const enumInfo: EnumInfo = { definitions: enumDefinitions }; const isEnumColumn = (column: SqlColumnIR) => adoptedEnumTypeName(column, enumInfo.definitions, wrapNamespaceName) !== undefined; - const columnTypes = inferColumnTypes({ + const columnTypes = await inferColumnTypes({ tables: introspectedTables, constructors: inferredTypeConstructors({ authoringTypes: context.authoringContributions.type, @@ -228,6 +232,7 @@ export async function inferPostgresPslContract( authoringTypes: context.authoringContributions.type, dataTypes: context.dataTypes, isEnumColumn, + readsDefault: columnTypeReadsDefault(context), }); const options: PostgresPslInferOptions = { columnTypes, diff --git a/packages/3-targets/postgres/1-target/src/core/psl-infer/infer-read-defaults.ts b/packages/3-targets/postgres/1-target/src/core/psl-infer/infer-read-defaults.ts index a743cd3d987a..566a1d984e24 100644 --- a/packages/3-targets/postgres/1-target/src/core/psl-infer/infer-read-defaults.ts +++ b/packages/3-targets/postgres/1-target/src/core/psl-infer/infer-read-defaults.ts @@ -9,7 +9,7 @@ import { readReportedDefault, type SqlPslBuildContext, } from '@internal/family-sql/control'; -import type { ColumnTypeOf } from '@internal/family-sql/psl-build'; +import type { ColumnTypeOf, ColumnTypeReadsDefault } from '@internal/family-sql/psl-build'; import { type Codec, codecForRef, type DataTypeValue } from '@internal/framework-components/codec'; import type { SqlColumnIR, SqlSchemaIR } from '@internal/sql-schema-ir/types'; import { isInternalError } from '@internal/utils/internal-error'; @@ -96,6 +96,30 @@ export async function readDefaultWithCodec( return 'resolvedDefault' in read ? read.resolvedDefault : rawExpression; } +/** + * Whether the codec of the type `contract infer` would write reads the column's reported default as a row value, with `fromWire`. A default that is not a literal, or a type that names no codec, has nothing to read. A `Date` codec reads a time with digits below a millisecond, dropping them, so such a default stays with it. + */ +export function columnTypeReadsDefault(context: SqlPslBuildContext): ColumnTypeReadsDefault { + return async (_tableName, column, type) => { + const raw: unknown = column.default; + if (typeof raw !== 'string') return true; + const reported = readPostgresDefault(raw, column.many === true); + if (reported.kind !== 'literal') return true; + const codec = codecOf(() => type, false, context); + if (codec === undefined) return true; + for (const body of bodiesOf(reported.literal)) { + if (body === null) continue; + try { + await codec.fromWire(body, {}); + } catch (error) { + if (isInternalError(error)) throw error; + return false; + } + } + return true; + }; +} + /** * Whether the codec's application value holds `body` exactly, as `contract emit` asks of a written default: the codec's `fromDataTypeValue` refuses a value it cannot hold exactly, such as a `Date` codec a time with digits below a millisecond. A body the data type does not read as text is left to the codec's wire reading. */ diff --git a/packages/3-targets/postgres/1-target/test/psl-infer/fixtures.ts b/packages/3-targets/postgres/1-target/test/psl-infer/fixtures.ts index 4ab8b978ada3..e5cc1af72375 100644 --- a/packages/3-targets/postgres/1-target/test/psl-infer/fixtures.ts +++ b/packages/3-targets/postgres/1-target/test/psl-infer/fixtures.ts @@ -146,7 +146,7 @@ export function inferFromFixtureTree( export function inferredColumnTypesOf( tables: Readonly>, enums: FixtureEnums = new Map(), -): ColumnTypeOf { +): Promise { return inferColumnTypes({ tables: Object.values(tables).map((table) => ({ schemaName: 'public', @@ -161,6 +161,7 @@ export function inferredColumnTypesOf( authoringTypes: inferBuildContext.authoringContributions.type, dataTypes: inferBuildContext.dataTypes, isEnumColumn: (column) => column.reportedType?.kind === 'enum', + readsDefault: undefined, }); } diff --git a/packages/3-targets/postgres/1-target/test/psl-infer/inferred-psl/inferred-psl.data-type-defaults.test.ts b/packages/3-targets/postgres/1-target/test/psl-infer/inferred-psl/inferred-psl.data-type-defaults.test.ts index fd53f41ebeda..a15188da17fb 100644 --- a/packages/3-targets/postgres/1-target/test/psl-infer/inferred-psl/inferred-psl.data-type-defaults.test.ts +++ b/packages/3-targets/postgres/1-target/test/psl-infer/inferred-psl/inferred-psl.data-type-defaults.test.ts @@ -153,23 +153,12 @@ describe('printPsl writes each default as the literal the column data type takes }, ); - it.each([ - ['infinity', 'timestamp', "'infinity'::timestamp without time zone"], - ['-infinity', 'timestamp', "'-infinity'::timestamp without time zone"], - ['infinity', 'timestamptz', "'infinity'::timestamp with time zone"], - [ - 'microseconds', - 'timestamptz(6)', - "'2024-01-01 01:00:00.123456+00'::timestamp(6) with time zone", - ], - ])( - 'prints a default of %s on a %s column, which a Date cannot hold, as its raw expression', - async (_value, typeText, rawDefault) => { - expect(await printedDefaults([introspected('stamp', typeText, rawDefault)])).toEqual({ - stamp: `@default(sql\`${rawDefault}\`)`, - }); - }, - ); + it('prints a default with digits below a millisecond, which a Date cannot hold, as its raw expression', async () => { + const rawDefault = "'2024-01-01 01:00:00.123456+00'::timestamp(6) with time zone"; + expect(await printedDefaults([introspected('stamp', 'timestamptz(6)', rawDefault)])).toEqual({ + stamp: `@default(sql\`${rawDefault}\`)`, + }); + }); it('prints a list default with an element a Date cannot hold as its raw expression, and one it holds as a literal list', async () => { const micro = diff --git a/packages/3-targets/postgres/1-target/test/psl-infer/inferred-psl/inferred-psl.enums.test.ts b/packages/3-targets/postgres/1-target/test/psl-infer/inferred-psl/inferred-psl.enums.test.ts index 50d5884329ea..8cc3bad14539 100644 --- a/packages/3-targets/postgres/1-target/test/psl-infer/inferred-psl/inferred-psl.enums.test.ts +++ b/packages/3-targets/postgres/1-target/test/psl-infer/inferred-psl/inferred-psl.enums.test.ts @@ -36,10 +36,10 @@ function enumInfoOf(definitions: Record): EnumInfo { }; } -function printWithEnums( +async function printWithEnums( tables: Record, definitions: Record, -): string { +): Promise { const enumInfo = enumInfoOf(definitions); const enums = fixtureEnums('public', Object.keys(definitions)); const schemaIR = new SqlSchemaIR({ @@ -52,7 +52,7 @@ function printWithEnums( }); const options: PostgresPslInferOptions = { columnDefaults: inferredColumnDefaults(inferBuildContext), - columnTypes: inferredColumnTypesOf(schemaIR.tables, enums), + columnTypes: await inferredColumnTypesOf(schemaIR.tables, enums), defaultMapping: createPostgresDefaultMapping(), defaultOf: noInferredDefaults, enumInfo, @@ -79,7 +79,7 @@ const sessionsTable: SqlTableIRInput = { describe('native_enum block emission', () => { it('emits a block with @@map and declaration-ordered members (the auth.aal_level shape)', async () => { - const output = printWithEnums( + const output = await printWithEnums( { sessions: sessionsTable }, { aal_level: ['aal1', 'aal2', 'aal3'] }, ); @@ -90,14 +90,14 @@ describe('native_enum block emission', () => { }); it('omits @@map when the type name already is the PSL name', async () => { - const output = printWithEnums({}, { Status: ['draft', 'done'] }); + const output = await printWithEnums({}, { Status: ['draft', 'done'] }); expect(output).toContain('native_enum Status {\n draft = "draft"\n done = "done"\n}'); expect(output).not.toContain('@@map'); }); it('emits a block for an enum type no column references', async () => { - const output = printWithEnums( + const output = await printWithEnums( { user: { name: 'user', @@ -116,7 +116,7 @@ describe('native_enum block emission', () => { }); it('preserves declaration order, not alphabetical order', async () => { - const output = printWithEnums({}, { priority: ['low', 'high', 'medium'] }); + const output = await printWithEnums({}, { priority: ['low', 'high', 'medium'] }); const lowAt = output.indexOf('low = "low"'); const highAt = output.indexOf('high = "high"'); @@ -129,7 +129,10 @@ describe('native_enum block emission', () => { describe('member-name sanitization with explicit values', () => { it('sanitizes non-identifier values while the string value carries the truth', async () => { - const output = printWithEnums({}, { ticket_priority: ['low', 'high-priority', '2nd', 'enum'] }); + const output = await printWithEnums( + {}, + { ticket_priority: ['low', 'high-priority', '2nd', 'enum'] }, + ); expect(output).toContain('low = "low"'); expect(output).toContain('highPriority = "high-priority"'); @@ -138,14 +141,14 @@ describe('member-name sanitization with explicit values', () => { }); it('deduplicates sanitized member names that collide', async () => { - const output = printWithEnums({}, { weird: ['a-b', 'a b'] }); + const output = await printWithEnums({}, { weird: ['a-b', 'a b'] }); expect(output).toContain('aB = "a-b"'); expect(output).toContain('aB2 = "a b"'); }); it('escapes quotes and backslashes in member values', async () => { - const output = printWithEnums({}, { quoted: ['say "hi"', 'back\\slash'] }); + const output = await printWithEnums({}, { quoted: ['say "hi"', 'back\\slash'] }); expect(output).toContain('= "say \\"hi\\""'); expect(output).toContain('= "back\\\\slash"'); @@ -154,7 +157,7 @@ describe('member-name sanitization with explicit values', () => { describe('name collision against a model of the same name', () => { it('renames the enum away from the model and keeps @@map carrying the type name', async () => { - const output = printWithEnums( + const output = await printWithEnums( { status: { name: 'status', @@ -180,7 +183,7 @@ describe('name collision against a model of the same name', () => { describe('pg.enum(Name) column emission', () => { it('emits the call syntax with optionality composing like any other field type', async () => { - const output = printWithEnums( + const output = await printWithEnums( { sessions: { ...sessionsTable, @@ -199,7 +202,7 @@ describe('pg.enum(Name) column emission', () => { }); it('emits the list form for an enum array column', async () => { - const output = printWithEnums( + const output = await printWithEnums( { user: { name: 'user', @@ -220,7 +223,7 @@ describe('pg.enum(Name) column emission', () => { }); it('types an enum column by its enum, not through a data type', async () => { - const output = printWithEnums( + const output = await printWithEnums( { sessions: sessionsTable }, { aal_level: ['aal1', 'aal2', 'aal3'] }, ); @@ -241,7 +244,7 @@ describe('emitted PSL parses', () => { } it('the @@map + sanitized-member shape parses with zero diagnostics', async () => { - const output = printWithEnums( + const output = await printWithEnums( { sessions: sessionsTable }, { aal_level: ['aal1', 'aal2', 'aal3'], ticket_priority: ['low', 'high-priority', '2nd'] }, ); @@ -250,7 +253,7 @@ describe('emitted PSL parses', () => { }); it('the model-collision + array shape parses with zero diagnostics', async () => { - const output = printWithEnums( + const output = await printWithEnums( { status: { name: 'status', diff --git a/packages/3-targets/postgres/1-target/test/psl-infer/inferred-psl/inferred-psl.top-level-blocks.test.ts b/packages/3-targets/postgres/1-target/test/psl-infer/inferred-psl/inferred-psl.top-level-blocks.test.ts index 9530d327399c..967010096d74 100644 --- a/packages/3-targets/postgres/1-target/test/psl-infer/inferred-psl/inferred-psl.top-level-blocks.test.ts +++ b/packages/3-targets/postgres/1-target/test/psl-infer/inferred-psl/inferred-psl.top-level-blocks.test.ts @@ -43,7 +43,7 @@ import { import { sqlContextInput } from '@internal/sql-contract-psl/test'; import { SqlSchemaIR } from '@internal/sql-schema-ir/types'; import { postgresDataTypes } from '@internal/target-postgres/data-types'; -import { assert, describe, expect, it } from 'vitest'; +import { assert, beforeAll, describe, expect, it } from 'vitest'; import { postgresAuthoringEntityTypes, postgresAuthoringPslBlockDescriptors, @@ -312,12 +312,15 @@ describe('buildPslDocumentAst and the top-level bucket', () => { }, }); - const printerOptions: PostgresPslInferOptions = { - columnDefaults: inferredColumnDefaults(inferBuildContext), - columnTypes: inferredColumnTypesOf(schemaIR.tables), - defaultMapping: createPostgresDefaultMapping(), - defaultOf: noInferredDefaults, - }; + let printerOptions: PostgresPslInferOptions; + beforeAll(async () => { + printerOptions = { + columnDefaults: inferredColumnDefaults(inferBuildContext), + columnTypes: await inferredColumnTypesOf(schemaIR.tables), + defaultMapping: createPostgresDefaultMapping(), + defaultOf: noInferredDefaults, + }; + }); const foreignKeyExtras = { extraRelationsByTable: new Map(), diff --git a/packages/3-targets/postgres/1-target/test/psl-infer/inferred-psl/inferred-psl.type-reading-default.test.ts b/packages/3-targets/postgres/1-target/test/psl-infer/inferred-psl/inferred-psl.type-reading-default.test.ts new file mode 100644 index 000000000000..a57d281c2ec0 --- /dev/null +++ b/packages/3-targets/postgres/1-target/test/psl-infer/inferred-psl/inferred-psl.type-reading-default.test.ts @@ -0,0 +1,60 @@ +import { SqlSchemaIR } from '@internal/sql-schema-ir/types'; +import { describe, expect, it } from 'vitest'; +import { printPslFromFlat } from '../fixtures'; + +async function printedStampField(typeText: string, rawDefault: string): Promise { + const output = await printPslFromFlat( + new SqlSchemaIR({ + tables: { + account: { + name: 'account', + columns: { + id: { name: 'id', typeText: 'int4', nullable: false }, + stamp: { name: 'stamp', typeText, nullable: false, default: rawDefault }, + }, + primaryKey: { columns: ['id'] }, + foreignKeys: [], + uniques: [], + indexes: [], + }, + }, + }), + ); + const line = output.split('\n').find((candidate) => /^\s+stamp\s/.test(candidate)); + return (line ?? '').trim().replace(/\s+/g, ' '); +} + +describe('contract infer picks a type whose codec reads the column default as a row', () => { + it.each([ + [ + 'timestamp(3)', + "'infinity'::timestamp without time zone", + 'stamp DateTime(as: string, storage: timestamp, precision: 3) @default(timestamp`infinity`)', + ], + [ + 'timestamp(3)', + "'-infinity'::timestamp without time zone", + 'stamp DateTime(as: string, storage: timestamp, precision: 3) @default(timestamp`-infinity`)', + ], + [ + 'timestamptz', + "'-infinity'::timestamp with time zone", + 'stamp DateTime(as: string) @default(timestamptz`-infinity`)', + ], + ])('writes a %s column with default %s as a string', async (typeText, rawDefault, expected) => { + expect(await printedStampField(typeText, rawDefault)).toBe(expected); + }); + + it('keeps a Date for a default with digits below a millisecond, which a Date reads, and writes the default as SQL', async () => { + const rawDefault = "'2024-01-01 01:00:00.123456+00'::timestamp(6) with time zone"; + expect(await printedStampField('timestamptz(6)', rawDefault)).toBe( + `stamp DateTime(precision: 6) @default(sql\`${rawDefault}\`)`, + ); + }); + + it('keeps the string date type for an infinite date default, which it reads', async () => { + expect(await printedStampField('date', "'infinity'::date")).toBe( + 'stamp Date @default(date`infinity`)', + ); + }); +}); diff --git a/test/integration/test/cli-journeys/infer-roundtrip-fidelity.prisma7-defaults.e2e.test.ts b/test/integration/test/cli-journeys/infer-roundtrip-fidelity.prisma7-defaults.e2e.test.ts index 95350f66a9b7..4dd442c202f8 100644 --- a/test/integration/test/cli-journeys/infer-roundtrip-fidelity.prisma7-defaults.e2e.test.ts +++ b/test/integration/test/cli-journeys/infer-roundtrip-fidelity.prisma7-defaults.e2e.test.ts @@ -228,16 +228,16 @@ withTempDir(({ createTempDir }) => { } model SqlDefaults { - id Int @id(map: "sql_defaults_pkey") - textNull VarChar(32)? @default(sql\`NULL::character varying\`) - floatNaN Float @default(NaN) - floatNegInf Float @default(-Infinity) - realNaN Real @default(NaN) - decimalNaN Numeric @default(NaN) - timeWithZone Time(storage: timetz) @default(timetz\`12:34:56Z\`) - stampInfinity DateTime(storage: timestamp) @default(sql\`'infinity'::timestamp without time zone\`) - dayNegInfinity Date @default(date\`-infinity\`) - instantBc DateTime(precision: 6) @default(timestamptz\`-000043-03-15T00:00:00Z\`) + id Int @id(map: "sql_defaults_pkey") + textNull VarChar(32)? @default(sql\`NULL::character varying\`) + floatNaN Float @default(NaN) + floatNegInf Float @default(-Infinity) + realNaN Real @default(NaN) + decimalNaN Numeric @default(NaN) + timeWithZone Time(storage: timetz) @default(timetz\`12:34:56Z\`) + stampInfinity DateTime(as: string, storage: timestamp, precision: 3) @default(timestamp\`infinity\`) + dayNegInfinity Date @default(date\`-infinity\`) + instantBc DateTime(precision: 6) @default(timestamptz\`-000043-03-15T00:00:00Z\`) @@map("sql_defaults") } diff --git a/test/integration/test/infer-roundtrip-runtime.integration.test.ts b/test/integration/test/infer-roundtrip-runtime.integration.test.ts index 24bd0bcda9c6..09f26fb10151 100644 --- a/test/integration/test/infer-roundtrip-runtime.integration.test.ts +++ b/test/integration/test/infer-roundtrip-runtime.integration.test.ts @@ -59,6 +59,42 @@ async function inferAndEmit( return ctx; } +async function withRuntime( + contract: Contract, + connectionString: string, + use: (session: { + readonly runtime: PostgresRuntimeImpl; + readonly context: ReturnType; + readonly client: Client; + }) => Promise, +): Promise { + const stack = createSqlExecutionStack({ + target: postgresTarget, + adapter: postgresAdapter, + driver: postgresDriver, + }); + const context = createExecutionContext({ contract, stack }); + const stackInstance = instantiateExecutionStack(stack); + const adapter = stackInstance.adapter; + const driver = stackInstance.driver; + if (!adapter || !driver) { + throw new Error('Adapter or driver descriptor missing from execution stack'); + } + const client = new Client({ connectionString }); + await client.connect(); + try { + await driver.connect({ kind: 'pgClient', client, ownedByDriver: false }); + const runtime = new PostgresRuntimeImpl({ context, adapter, driver, closeRefusal: undefined }); + try { + await use({ runtime, context, client }); + } finally { + await runtime.close(); + } + } finally { + await client.end(); + } +} + withTempDir(({ createTempDir }) => { describe('Runtime: unbounded numeric crashes ExecutionContext construction', () => { let database: Awaited>; @@ -204,30 +240,10 @@ withTempDir(({ createTempDir }) => { opensAt: '08:30:00', }; - const stack = createSqlExecutionStack({ - target: postgresTarget, - adapter: postgresAdapter, - driver: postgresDriver, - }); - const context = createExecutionContext({ contract, stack }); - const stackInstance = instantiateExecutionStack(stack); - const adapter = stackInstance.adapter; - const driver = stackInstance.driver; - if (!adapter || !driver) { - throw new Error('Adapter or driver descriptor missing from execution stack'); - } - - const client = new Client({ connectionString: database.connectionString }); - await client.connect(); - try { - await driver.connect({ kind: 'pgClient', client, ownedByDriver: false }); - const runtime = new PostgresRuntimeImpl({ - context, - adapter, - driver, - closeRefusal: undefined, - }); - try { + await withRuntime( + contract, + database.connectionString, + async ({ runtime, context, client }) => { const records = new Collection({ runtime, context }, 'Record', { namespaceId: 'public', }); @@ -265,12 +281,47 @@ withTempDir(({ createTempDir }) => { code: 'RUNTIME.DECODE_FAILED', cause: refusal, }); - } finally { - await runtime.close(); - } - } finally { - await client.end(); - } + }, + ); + }, + timeouts.spinUpPpgDev, + ); + }); + + describe('Runtime: a timestamp column whose default a Date cannot hold', () => { + let database: Awaited>; + + beforeAll(async () => { + database = await createDevDatabase(); + await withClient(database.connectionString, (client) => + client.query(` + CREATE TABLE horizon ( + id int4 PRIMARY KEY, + ends_at timestamp(3) NOT NULL DEFAULT 'infinity', + began_at timestamptz NOT NULL DEFAULT '-infinity' + ); + INSERT INTO horizon (id) VALUES (1); + `), + ); + }, timeouts.spinUpPpgDev); + + afterAll(async () => { + if (database) await database.close(); + }, timeouts.spinUpPpgDev); + + it( + 'reads a row created with the default, which infer types as a string', + async () => { + const ctx = await inferAndEmit(database.connectionString, createTempDir); + const contract = readEmittedContract(ctx); + await withRuntime(contract, database.connectionString, async ({ runtime, context }) => { + const horizons = new Collection({ runtime, context }, 'Horizon', { + namespaceId: 'public', + }); + expect(await horizons.select('id', 'endsAt', 'beganAt').all()).toEqual([ + { id: 1, endsAt: 'infinity', beganAt: '-infinity' }, + ]); + }); }, timeouts.spinUpPpgDev, ); From 4828a4444f03afaebfb4eb49284162db1fde965a Mon Sep 17 00:00:00 2001 From: willbot Date: Tue, 27 Oct 2026 18:20:35 +0100 Subject: [PATCH 105/118] ADR 279 says infer picks a type that reads the column's own default Co-Authored-By: Claude Opus 5.5 Signed-off-by: willbot Signed-off-by: Will Madden --- ...ence types choose an explicit type from their arguments.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/architecture docs/adrs/ADR 279 - Convenience types choose an explicit type from their arguments.md b/docs/architecture docs/adrs/ADR 279 - Convenience types choose an explicit type from their arguments.md index 0dc2859b2a2f..75131039a31d 100644 --- a/docs/architecture docs/adrs/ADR 279 - Convenience types choose an explicit type from their arguments.md +++ b/docs/architecture docs/adrs/ADR 279 - Convenience types choose an explicit type from their arguments.md @@ -113,7 +113,7 @@ Each place that needs a field's codec resolves the field's call first, including ### What the tools write `contract infer` reads a database and `contract print` reads a contract, and neither knows which kind of type the user wrote. Both write a convenience call wherever one gives the column's explicit type, and the explicit type otherwise: -1. For each database type, one explicit type is marked `inferred`: the one its convenience default gives. Infer and the Prisma 7 schema reader pick that type for a column. +1. For each database type, one explicit type is marked `inferred`: the one its convenience default gives. Infer and the Prisma 7 schema reader pick that type for a column. If that type cannot read the column's own default, such as `infinity` for a `Date`, infer picks the type of the same convenience type that can, so it never writes a field whose default fails to read. 2. To write the call for an explicit type, the tools find the row that names it, build the arguments from the row, and drop those equal to the defaults. 3. They resolve the call again and keep it only if it gives the same codec and parameters. @@ -213,4 +213,4 @@ The PostgreSQL target ships two type aliases (ADR 275), in the namespace `tempor **A Prisma-owned value for calendar dates, times and durations, or a branded string.** A class is one more date API, does not survive JSON, and duplicates Temporal. A branded string forces every write through a helper or a cast and catches nothing the codec doesn't. -**Infer the string types for columns a `Date` cannot hold exactly.** Nothing is lost, but the schema would differ from what a user would write for the same model; infer takes the default. +**Infer the string types for columns whose values have more digits than a `Date` holds.** Nothing is lost, but the schema would differ from what a user would write for the same model; infer takes the default, which reads those values with the extra digits dropped. A default the type cannot read at all is different: the row would fail to read, so infer picks a type that reads it. From f0c57a9400109edd3f8759414032cd3b64127773 Mon Sep 17 00:00:00 2001 From: willbot Date: Tue, 27 Oct 2026 18:26:58 +0100 Subject: [PATCH 106/118] Assembly refuses a type choice row that leaves a required argument without a value Signed-off-by: willbot Signed-off-by: Will Madden Co-Authored-By: Claude Opus 5.5 --- docs/reference/error-reference.md | 2 +- .../src/shared/authoring-type-choice.ts | 11 ++- .../test/type-choice-assembly.test.ts | 68 +++++++++++++++++++ 3 files changed, 79 insertions(+), 2 deletions(-) diff --git a/docs/reference/error-reference.md b/docs/reference/error-reference.md index 6a988fff63bb..6eba42735b9f 100644 --- a/docs/reference/error-reference.md +++ b/docs/reference/error-reference.md @@ -543,7 +543,7 @@ Namespaces are declared (contract-level list or a model-level `namespace`) on a A composed pack's contribution is malformed or collides with another contribution; this is the extension-author-facing bucket. Covers: entity types colliding with reserved helper keys, duplicate entity kinds or index-type registrations, a registered entity kind with no `lowerEntityHandles` lowering, an invalid `indexTypes` shape, entries-slot collisions between a model attribute and a block entry kind, a model attribute that lowers to a malformed index, bad authoring-helper paths, a codec registered with an entity-ref arg but no `columnFromEntity` hook, and print-time contribution mismatches (a block keyword with no PSL block descriptor, or a descriptor whose discriminator disagrees with the block's kind). Raised during contract authoring/lowering and PSL printing. -A contributed type choice (`kind: 'typeChoice'`, such as PostgreSQL's `DateTime`) is refused when the stack is assembled, by the control stack and by the TypeScript builders, when its table cannot be resolved: `Type choice "" contributed by "" chooses "", which is not a type constructor "" registers beside it.`, for a row naming a constructor no component or only another component contributes, a row whose combination or an option default the option does not list, two rows for one combination (`… has two rows for as: ; each combination chooses one type.`), a combination of defaults with no row (`… has no row for its defaults as: , so it cannot be written bare.`), a parameter default for an argument it does not pass on or that the chosen constructor does not take (`… gives a default for "" to "", which takes no argument of that name.`), or a passed-on argument of another data type than the chosen constructor takes. Payload: `packId`, `contribution`. +A contributed type choice (`kind: 'typeChoice'`, such as PostgreSQL's `DateTime`) is refused when the stack is assembled, by the control stack and by the TypeScript builders, when its table cannot be resolved: `Type choice "" contributed by "" chooses "", which is not a type constructor "" registers beside it.`, for a row naming a constructor no component or only another component contributes, a row whose combination or an option default the option does not list, two rows for one combination (`… has two rows for as: ; each combination chooses one type.`), a combination of defaults with no row (`… has no row for its defaults as: , so it cannot be written bare.`), a parameter default for an argument it does not pass on or that the chosen constructor does not take (`… gives a default for "" to "", which takes no argument of that name.`), a passed-on argument of another data type than the chosen constructor takes, or a row whose chosen constructor requires an argument the row gives no default for, since a type choice is also written bare (`… has a row for as: that chooses "", which requires "", and gives it no default.`). Payload: `packId`, `contribution`. A contributed type alias (`kind: 'typeAlias'`, such as `temporal.CreatedAt`) is refused when the stack is assembled, by the control stack and by the TypeScript builders, so PSL and TypeScript refuse the same packs. Its type params may not reference an argument, because a type alias takes none: `Invalid authoring type alias "" contributed by descriptor "". output.type.typeParams. references an argument, but a type alias takes none. Write the value itself.` Each generator call must resolve for the alias's codec: `Type alias "" calls now() on create, which does not apply to its codec "".`, `…, which its codec "" refuses: `, or `…, but no component declares the generator function "".` Payload: `packId`, `contribution`, `reason`, `keyword`, `paramName`, `codecId`. diff --git a/packages/1-framework/1-core/framework-components/src/shared/authoring-type-choice.ts b/packages/1-framework/1-core/framework-components/src/shared/authoring-type-choice.ts index 00c7f862cc52..78f925755093 100644 --- a/packages/1-framework/1-core/framework-components/src/shared/authoring-type-choice.ts +++ b/packages/1-framework/1-core/framework-components/src/shared/authoring-type-choice.ts @@ -387,6 +387,15 @@ function assertTypeChoiceValid( ); } } + chosenType.args?.forEach((parameter, index) => { + if (parameter.optional === true) return; + if (parameter.name !== undefined && Object.hasOwn(row.defaults ?? {}, parameter.name)) return; + const required = + parameter.name === undefined ? `an unnamed argument ${index + 1}` : `"${parameter.name}"`; + invalid( + `has a row for ${typeChoiceCombinationText(row)} that chooses "${row.type}", which requires ${required}, and gives it no default.`, + ); + }); } const defaults: AuthoringTypeChoiceRow = { when: Object.fromEntries(options.map((option) => [option.name, option.default])), @@ -402,7 +411,7 @@ function assertTypeChoiceValid( } /** - * Refuses a type choice of a component's `contributed` type namespace whose table cannot be resolved: a row that names no type constructor the same component contributes at the same level, a combination or default an option does not list, defaults with no row, a parameter default for an argument it does not pass on, or a passed-on argument of another data type than the constructor takes. ADR 279: each row names an explicit type of the same target. + * Refuses a type choice of a component's `contributed` type namespace whose table cannot be resolved: a row that names no type constructor the same component contributes at the same level, a combination or default an option does not list, defaults with no row, a parameter default for an argument it does not pass on, a passed-on argument of another data type than the constructor takes, or a row whose constructor requires an argument the row gives no default. ADR 279: each row names an explicit type of the same target. */ export function assertTypeChoicesValid( contributed: AuthoringTypeNamespace, diff --git a/packages/1-framework/1-core/framework-components/test/type-choice-assembly.test.ts b/packages/1-framework/1-core/framework-components/test/type-choice-assembly.test.ts index b9c14ecc7d0b..83c60d8f643a 100644 --- a/packages/1-framework/1-core/framework-components/test/type-choice-assembly.test.ts +++ b/packages/1-framework/1-core/framework-components/test/type-choice-assembly.test.ts @@ -40,6 +40,11 @@ const constructors = { output: { codecId: 'test/wide-date@1', typeParams: { precision: { kind: 'arg', index: 0 } } }, }, WideText: { kind: 'typeConstructor', output: { codecId: 'test/wide-text@1' } }, + Bounded: { + kind: 'typeConstructor', + args: [{ kind: 'value', dataType: 'test/int', name: 'length' }], + output: { codecId: 'test/bounded@1', typeParams: { length: { kind: 'arg', index: 0 } } }, + }, } as const satisfies AuthoringTypeNamespace; const check = (moment: AuthoringTypeChoiceDescriptor) => () => @@ -148,6 +153,69 @@ describe('assertTypeChoicesValid', () => { 'Type choice "Moment" contributed by "x-pack" passes "precision" as a value of data type "test/text" to "WideDate", which takes "test/int".', ); }); + + describe('a row naming a constructor with a required argument', () => { + const length = { kind: 'value', dataType: 'test/int', name: 'length', optional: true } as const; + const boundedRow = (defaults?: Record) => + choice({ + args: [asOption, length], + choices: [ + { when: { as: 'date' }, type: 'WideText' }, + { when: { as: 'text' }, type: 'Bounded', ...(defaults ? { defaults } : {}) }, + ], + }); + + it('refuses the row when the choice does not pass the argument on', () => { + expect( + check( + choice({ + args: [asOption], + choices: [ + { when: { as: 'date' }, type: 'WideText' }, + { when: { as: 'text' }, type: 'Bounded' }, + ], + }), + ), + ).toThrow( + 'Type choice "Moment" contributed by "x-pack" has a row for as: text that chooses "Bounded", which requires "length", and gives it no default.', + ); + }); + + it('refuses the row when the choice passes the argument on as optional with no row default', () => { + expect(check(boundedRow())).toThrow( + 'Type choice "Moment" contributed by "x-pack" has a row for as: text that chooses "Bounded", which requires "length", and gives it no default.', + ); + }); + + it('refuses the row when the required argument has no name a default could use', () => { + expect(() => + assertTypeChoicesValid( + { + ...constructors, + Unnamed: { + kind: 'typeConstructor', + args: [{ kind: 'value', dataType: 'test/int' }], + output: { codecId: 'test/unnamed@1' }, + }, + Moment: choice({ + args: [asOption], + choices: [ + { when: { as: 'date' }, type: 'WideText' }, + { when: { as: 'text' }, type: 'Unnamed' }, + ], + }), + }, + 'x-pack', + ), + ).toThrow( + 'Type choice "Moment" contributed by "x-pack" has a row for as: text that chooses "Unnamed", which requires an unnamed argument 1, and gives it no default.', + ); + }); + + it('passes the row when it gives the argument a default', () => { + expect(check(boundedRow({ length: 255 }))).not.toThrow(); + }); + }); }); describe('createControlStack', () => { From 5f93d22671380d914fb876af904c0680bfd87c2a Mon Sep 17 00:00:00 2001 From: willbot Date: Wed, 28 Oct 2026 14:13:05 +0100 Subject: [PATCH 107/118] Hover on a convenience call names the explicit type that call is Co-Authored-By: Claude Opus 5.5 Signed-off-by: willbot Signed-off-by: Will Madden --- ...e an explicit type from their arguments.md | 2 +- .../psl-parser/src/contributed-type-scope.ts | 9 +- .../psl-parser/src/exports/index.ts | 1 + .../3-tooling/language-server/src/hover.ts | 96 +++++++++++++------ .../3-tooling/language-server/src/project.ts | 1 + .../test/generator-attributes.test.ts | 5 +- .../language-server/test/helpers/hover.ts | 1 + .../test/helpers/reference-fixtures.ts | 13 ++- .../test/hover.contributed-types.test.ts | 21 ++++ 9 files changed, 110 insertions(+), 39 deletions(-) diff --git a/docs/architecture docs/adrs/ADR 279 - Convenience types choose an explicit type from their arguments.md b/docs/architecture docs/adrs/ADR 279 - Convenience types choose an explicit type from their arguments.md index 75131039a31d..6c72c1699e21 100644 --- a/docs/architecture docs/adrs/ADR 279 - Convenience types choose an explicit type from their arguments.md +++ b/docs/architecture docs/adrs/ADR 279 - Convenience types choose an explicit type from their arguments.md @@ -186,7 +186,7 @@ The PostgreSQL target ships two type aliases (ADR 275), in the namespace `tempor - A type constructor still names exactly one codec (ADR 254). A type choice is a second kind of type declaration, which resolves to a type constructor. - Every explicit type of a database type with several codecs carries a suffix for its value (ADR 257); bare concept names belong to convenience types. - The `temporal.*` aliases (ADR 275) are built on `DateTime` and need no variants for other values. -- The editor's hover lists a convenience type's options and the explicit type each combination gives. +- The editor's hover on a convenience call names the explicit type that call is, such as `DateTime(as: temporal)` is exactly `TimestamptzTemporal`, and lists the explicit type each combination gives. ## Not decided here diff --git a/packages/1-framework/2-authoring/psl-parser/src/contributed-type-scope.ts b/packages/1-framework/2-authoring/psl-parser/src/contributed-type-scope.ts index 54f56ee1b91c..4b104b1b4496 100644 --- a/packages/1-framework/2-authoring/psl-parser/src/contributed-type-scope.ts +++ b/packages/1-framework/2-authoring/psl-parser/src/contributed-type-scope.ts @@ -3,6 +3,7 @@ import { type AuthoringTypeChoiceDescriptor, type AuthoringTypeConstructorDescriptor, type DataTypeSupport, + type ResolvedAuthoringTypeChoice, resolveAuthoringTypeChoice, typeConstructorLookup, } from '@internal/framework-components/authoring'; @@ -74,12 +75,12 @@ export function isContributedTypeDescriptor( ); } -/** The type constructor a call of the type choice `symbol` chooses, reading the call as the column does, or why it chooses none. */ +/** The explicit type a call of the type choice `symbol` chooses and the arguments it is built with, reading the call as the column does, or why it chooses none. */ export function chosenTypeConstructor( symbol: ContributedTypeChoiceSymbol, call: ResolvedTypeConstructorCall | undefined, dataTypes: DataTypeSupport, -): Result { +): Result { const helperPath = symbol.path.join('.'); const args = readPslHelperArgs({ args: call?.args ?? [], @@ -94,7 +95,7 @@ export function chosenTypeConstructor( descriptor: symbol.descriptor, args, constructorNamed: (name) => symbol.choiceTypes.get(name), - }).descriptor, + }), ); } catch (error) { if (isInternalError(error) || !(error instanceof Error)) throw error; @@ -110,7 +111,7 @@ export function contributedTypeCodecId( const { symbol } = use; if (isContributedTypeChoice(symbol)) { const chosen = chosenTypeConstructor(symbol, use.call, dataTypes); - return chosen.ok ? chosen.value.output.codecId : undefined; + return chosen.ok ? chosen.value.descriptor.output.codecId : undefined; } const { descriptor } = symbol; return descriptor.kind === 'typeAlias' diff --git a/packages/1-framework/2-authoring/psl-parser/src/exports/index.ts b/packages/1-framework/2-authoring/psl-parser/src/exports/index.ts index 6ecf096853f0..39af461053f3 100644 --- a/packages/1-framework/2-authoring/psl-parser/src/exports/index.ts +++ b/packages/1-framework/2-authoring/psl-parser/src/exports/index.ts @@ -173,6 +173,7 @@ export type { MapBlockSpec, StructBlockSpec, } from '../block-spec/types'; +export { printTypeConstructorCall } from '../call-arguments'; export type { ContributedMember, ContributedNamespaceSymbol, diff --git a/packages/1-framework/3-tooling/language-server/src/hover.ts b/packages/1-framework/3-tooling/language-server/src/hover.ts index 19de68dec3a2..88eb881a3df1 100644 --- a/packages/1-framework/3-tooling/language-server/src/hover.ts +++ b/packages/1-framework/3-tooling/language-server/src/hover.ts @@ -9,27 +9,34 @@ import { isAuthoringTypeChoiceOption, } from '@internal/framework-components/authoring'; import { dataTypeId } from '@internal/framework-components/codec'; -import type { - AttributeSymbol, - Binder, - BlockSymbol, - CompositeTypeSymbol, - ConstantSymbol, - ContributedTypeSymbol, - FieldSymbol, - FunctionSymbol, - MixinSymbol, - ModelSymbol, - NamedTypeSymbol, - NamespaceSymbol, - ParameterSymbol, +import { + type AttributeSymbol, + type Binder, + type BlockSymbol, + type CompositeTypeSymbol, + type ConstantSymbol, + type ContributedTypeChoiceSymbol, + type ContributedTypeSymbol, + chosenTypeConstructor, + type FieldSymbol, + type FunctionSymbol, + findBlockDescriptor, + isContributedTypeChoice, + type MixinSymbol, + type ModelSymbol, + type NamedTypeSymbol, + type NamespaceSymbol, + type ParameterSymbol, + printTypeConstructorCall, + readResolvedConstructorCall, } from '@internal/psl-parser'; -import { findBlockDescriptor } from '@internal/psl-parser'; import { GenericBlockDeclarationAst, MixinDeclarationAst, + type PslSources, type SyntaxNode, type SyntaxToken, + TypeAnnotationAst, } from '@internal/psl-parser/syntax'; import { type Hover, MarkupKind } from 'vscode-languageserver'; import type { PslCursorInput } from './attribute-syntax-context'; @@ -45,6 +52,7 @@ export interface ProvidePslHoverInput extends PslCursorInput { readonly pslBlockDescriptors: AuthoringPslBlockDescriptorNamespace; /** The stack's data types, which say how a type constructor's value arguments are written. */ readonly dataTypes: DataTypeSupport; + readonly sources: PslSources; } type HoverEntitySymbol = @@ -59,7 +67,11 @@ type HoverResult = | { readonly kind: 'entity'; readonly symbol: HoverEntitySymbol } | { readonly kind: 'namespace'; readonly symbol: NamespaceSymbol } | { readonly kind: 'attribute'; readonly symbol: AttributeSymbol } - | { readonly kind: 'contributedType'; readonly symbol: ContributedTypeSymbol } + | { + readonly kind: 'contributedType'; + readonly symbol: ContributedTypeSymbol; + readonly annotation: TypeAnnotationAst | undefined; + } | { readonly kind: 'parameter'; readonly symbol: ParameterSymbol } | { readonly kind: 'function'; readonly symbol: FunctionSymbol } | { readonly kind: 'constant'; readonly symbol: ConstantSymbol }; @@ -81,7 +93,7 @@ export function providePslHover(input: ProvidePslHoverInput): Hover | null { function hoverValueAt(input: ProvidePslHoverInput, token: SyntaxToken): string | undefined { const result = resolveHoverResult(input.binder, token); - if (result !== undefined) return renderHoverResult(result, input.dataTypes); + if (result !== undefined) return renderHoverResult(result, input); return blockKeywordDocumentationAt(token, input.pslBlockDescriptors); } @@ -100,7 +112,11 @@ function resolveHoverResult(binder: Binder, token: SyntaxToken): HoverResult | u case 'attribute': return { kind: 'attribute', symbol: resolution.symbol }; case 'contributedType': - return { kind: 'contributedType', symbol: resolution.symbol }; + return { + kind: 'contributedType', + symbol: resolution.symbol, + annotation: token.parent.findAncestor(TypeAnnotationAst.cast), + }; case 'parameter': return { kind: 'parameter', symbol: resolution.symbol }; case 'function': @@ -112,13 +128,11 @@ function resolveHoverResult(binder: Binder, token: SyntaxToken): HoverResult | u } } -function renderHoverResult(result: HoverResult, dataTypes: DataTypeSupport): string { +function renderHoverResult(result: HoverResult, input: ProvidePslHoverInput): string { if (result.kind === 'entity') return renderEntityContent(result.symbol); if (result.kind === 'namespace') return renderNamespaceContent(result.symbol); if (result.kind === 'attribute') return renderAttributeContent(result.symbol); - if (result.kind === 'contributedType') { - return renderContributedTypeContent(result.symbol, dataTypes); - } + if (result.kind === 'contributedType') return renderContributedTypeContent(result, input); if (result.kind === 'parameter') return renderParameterContent(result.symbol); if (result.kind === 'function') return renderFunctionContent(result.symbol); return renderConstantContent(result.symbol); @@ -142,14 +156,38 @@ function renderAttributeContent(symbol: AttributeSymbol): string { } function renderContributedTypeContent( - symbol: ContributedTypeSymbol, - dataTypes: DataTypeSupport, + result: Extract, + input: ProvidePslHoverInput, ): string { - const fence = ['```prisma', renderContributedTypeLabel(symbol, dataTypes), '```'].join('\n'); - const content = withDocumentation(fence, symbol.descriptor.documentation); - return symbol.descriptor.kind === 'typeChoice' - ? `${content}\n\n${renderTypeChoices(symbol.descriptor)}` - : content; + const { symbol } = result; + const fence = ['```prisma', renderContributedTypeLabel(symbol, input.dataTypes), '```'].join( + '\n', + ); + if (!isContributedTypeChoice(symbol)) { + return withDocumentation(fence, symbol.descriptor.documentation); + } + const chosen = renderChosenType(symbol, result.annotation, input); + const head = chosen === undefined ? fence : `${fence}\n\n${chosen}`; + return `${withDocumentation(head, symbol.descriptor.documentation)}\n\n${renderTypeChoices(symbol.descriptor)}`; +} + +/** The explicit type the call at `annotation` becomes, or `undefined` when its arguments choose none. */ +function renderChosenType( + symbol: ContributedTypeChoiceSymbol, + annotation: TypeAnnotationAst | undefined, + input: ProvidePslHoverInput, +): string | undefined { + const call = readResolvedConstructorCall(annotation, input.sources); + const chosen = chosenTypeConstructor(symbol, call, input.dataTypes); + if (!chosen.ok) return undefined; + const { type, descriptor, args } = chosen.value; + const written = (descriptor.args ?? []).flatMap((arg, index) => + args[index] === undefined + ? [] + : [`${arg.name ?? `arg${index + 1}`}: ${JSON.stringify(args[index])}`], + ); + const callText = call === undefined ? symbol.path.join('.') : printTypeConstructorCall(call); + return `\`${callText}\` is exactly \`${type}${written.length === 0 ? '' : `(${written.join(', ')})`}\`.`; } /** Each combination of a type choice's options, as written, and the explicit type it becomes. */ diff --git a/packages/1-framework/3-tooling/language-server/src/project.ts b/packages/1-framework/3-tooling/language-server/src/project.ts index c9eabad51407..b8d01192abea 100644 --- a/packages/1-framework/3-tooling/language-server/src/project.ts +++ b/packages/1-framework/3-tooling/language-server/src/project.ts @@ -199,6 +199,7 @@ export class Project { binder: data.artifacts.binder(), pslBlockDescriptors: data.controlStack.pslBlockDescriptors, dataTypes: data.controlStack.dataTypes ?? EMPTY_DATA_TYPES, + sources: data.artifacts.sources, }); } catch { return null; diff --git a/packages/1-framework/3-tooling/language-server/test/generator-attributes.test.ts b/packages/1-framework/3-tooling/language-server/test/generator-attributes.test.ts index 018b16a72f4d..c1add9d134e4 100644 --- a/packages/1-framework/3-tooling/language-server/test/generator-attributes.test.ts +++ b/packages/1-framework/3-tooling/language-server/test/generator-attributes.test.ts @@ -102,7 +102,7 @@ function prepare(markedSource: string, stack: Stack) { }); const controlMutationDefaults = { defaultFunctionRegistry: new Map(), generatorDescriptors: [] }; const binder = testBinder({ ...stack, controlMutationDefaults, sources, symbolTable }); - return { document, sourceFile, symbolTable, binder, offset, controlMutationDefaults }; + return { document, sourceFile, symbolTable, binder, offset, controlMutationDefaults, sources }; } function complete(markedSource: string, stack: Stack, clientSupportsSnippets = false) { @@ -158,7 +158,7 @@ describe('generator attributes in the language server', { it('show the signature of a generator function on hover', async () => { const stack = await actualPostgresStack(); - const { document, sourceFile, binder, offset } = prepare( + const { document, sourceFile, binder, offset, sources } = prepare( 'model Post {\n id String @onCreate(uu|id(7))\n}', stack, ); @@ -170,6 +170,7 @@ describe('generator attributes in the language server', { binder, pslBlockDescriptors: stack.pslBlockDescriptors, dataTypes: EMPTY_DATA_TYPES, + sources, })?.contents, ).toEqual({ kind: 'markdown', diff --git a/packages/1-framework/3-tooling/language-server/test/helpers/hover.ts b/packages/1-framework/3-tooling/language-server/test/helpers/hover.ts index 15209f34ad86..e5850952c209 100644 --- a/packages/1-framework/3-tooling/language-server/test/helpers/hover.ts +++ b/packages/1-framework/3-tooling/language-server/test/helpers/hover.ts @@ -259,5 +259,6 @@ export function hover(markedSource: string, siblings: readonly string[] = []) { binder: testBinder({ sources, symbolTable, authoringContributions, pslBlockDescriptors }), pslBlockDescriptors, dataTypes, + sources, }); } diff --git a/packages/1-framework/3-tooling/language-server/test/helpers/reference-fixtures.ts b/packages/1-framework/3-tooling/language-server/test/helpers/reference-fixtures.ts index 0ccee50538e7..be92fb0445c3 100644 --- a/packages/1-framework/3-tooling/language-server/test/helpers/reference-fixtures.ts +++ b/packages/1-framework/3-tooling/language-server/test/helpers/reference-fixtures.ts @@ -26,7 +26,12 @@ import { structBlock, } from '@internal/psl-parser'; import { type ResolvedFormatOptions, resolveFormatOptions } from '@internal/psl-parser/format'; -import { type DocumentAst, parse, type SourceFile } from '@internal/psl-parser/syntax'; +import { + type DocumentAst, + type PslSources, + parse, + type SourceFile, +} from '@internal/psl-parser/syntax'; import type { Position } from 'vscode-languageserver'; import type { ReferencesDocument } from '../../src/references'; import { testBinder } from './binder'; @@ -260,7 +265,7 @@ function project(files: Files, stack: FixtureStack) { sourceFile: file.sources.sourceFileFor(file.document.syntax), }), ); - return { documents, binder, symbolTable }; + return { documents, binder, symbolTable, sources }; } export interface FixtureCursorInput extends FixtureStack { @@ -274,6 +279,7 @@ export interface FixtureCursorInput extends FixtureStack { readonly generatorFunctions: GeneratorFunctionRegistry; readonly formatOptions: ResolvedFormatOptions; readonly dataTypes: DataTypeSupport; + readonly sources: PslSources; } export function cursorInput( @@ -282,7 +288,7 @@ export function cursorInput( marked: string, stack: FixtureStack = fixtureStack, ): FixtureCursorInput { - const { documents, binder, symbolTable } = project(files, stack); + const { documents, binder, symbolTable, sources } = project(files, stack); const current = documents.find((document) => document.sourceFile.filename === name); if (current === undefined) throw new Error(`no file ${name}`); const needle = marked.replace('|', ''); @@ -303,5 +309,6 @@ export function cursorInput( generatorFunctions: EMPTY_GENERATOR_FUNCTIONS, formatOptions: resolveFormatOptions(undefined), dataTypes: EMPTY_DATA_TYPES, + sources, }; } diff --git a/packages/1-framework/3-tooling/language-server/test/hover.contributed-types.test.ts b/packages/1-framework/3-tooling/language-server/test/hover.contributed-types.test.ts index 1fbddacb9c36..752529e01fe0 100644 --- a/packages/1-framework/3-tooling/language-server/test/hover.contributed-types.test.ts +++ b/packages/1-framework/3-tooling/language-server/test/hover.contributed-types.test.ts @@ -60,6 +60,8 @@ describe('providePslHover', () => { 'pg.Moment(as?: date | text, storage?: wide | narrow, precision?: a number)', '```', '', + '`pg.Moment(as: text)` is exactly `Text`.', + '', 'A moment in time.', '', '| as | storage | Becomes |', @@ -70,4 +72,23 @@ describe('providePslHover', () => { ].join('\n'), }); }); + + it('shows the explicit type a bare type choice is, with the arguments its row defaults', () => { + const value = hover('model Product {\n at pg.Mo|ment\n}')?.contents; + expect(value).toMatchObject({ + value: expect.stringContaining('`pg.Moment` is exactly `Stamp(precision: 3)`.'), + }); + }); + + it('shows the explicit type with the arguments a call hands on', () => { + const value = hover('model Product {\n at pg.Mo|ment(precision: 6)\n}')?.contents; + expect(value).toMatchObject({ + value: expect.stringContaining('`pg.Moment(precision: 6)` is exactly `Stamp(precision: 6)`.'), + }); + }); + + it('names no explicit type for a call whose combination has no row', () => { + const value = hover('model Product {\n at pg.Mo|ment(as: text, storage: narrow)\n}')?.contents; + expect(value).toMatchObject({ value: expect.not.stringContaining('is exactly') }); + }); }); From 154aae460760f74615cfd70a45606518cba80963 Mon Sep 17 00:00:00 2001 From: willbot Date: Wed, 28 Oct 2026 14:26:37 +0100 Subject: [PATCH 108/118] The released rc.21 upgrade sources keep the text that shipped This PR's edits had been carried into them by the release bump; its guidance lives in the pending fragments. Co-Authored-By: Claude Opus 5.5 Signed-off-by: willbot Signed-off-by: Will Madden --- .../app/instructions.md | 4 +-- .../remove-field-presets/app/instructions.md | 34 +++++++++---------- 2 files changed, 17 insertions(+), 21 deletions(-) diff --git a/upgrade-instructions/releases/8.0.0-rc.20-to-8.0.0-rc.21/sources/prisma7-now-default-on-create/app/instructions.md b/upgrade-instructions/releases/8.0.0-rc.20-to-8.0.0-rc.21/sources/prisma7-now-default-on-create/app/instructions.md index aed611614edb..9ae68850d25d 100644 --- a/upgrade-instructions/releases/8.0.0-rc.20-to-8.0.0-rc.21/sources/prisma7-now-default-on-create/app/instructions.md +++ b/upgrade-instructions/releases/8.0.0-rc.20-to-8.0.0-rc.21/sources/prisma7-now-default-on-create/app/instructions.md @@ -25,10 +25,8 @@ model Post { } ``` -reads as the Prisma 8 PSL field `createdAt DateTime(storage: timestamp) @default(now()) @onCreate(now())`: a `timestamp(3)` column read as a JavaScript `Date`. A create that supplies `createdAt` still stores the supplied value. An `INSERT` from outside Prisma still gets the column default. +reads as the Prisma 8 PSL field `createdAt TimestampString(3) @default(now()) @onCreate(now())`. A create that supplies `createdAt` still stores the supplied value. An `INSERT` from outside Prisma still gets the column default. For each project whose `prisma.config.ts` uses `prisma7Schema(...)`, run `prisma contract emit` again and commit the new `contract.json` and `contract.d.ts`. If the schema has a `@default(now())` field, the generator's id is in the execution section of `contract.json`, so the execution hash changes. The storage hash does not change, and the database marker holds only the storage and profile hashes, so there is no migration to plan and no need to run `prisma db sign`. If the schema has no `@default(now())` field, the contract does not change and there is nothing to do. - -In the same release, `prisma7Schema(...)` also reads `DateTime` fields as a `Date` instead of a string, which changes the storage hash and does need `prisma db sign`; `prisma7-schema-reads-dates` gives those steps. diff --git a/upgrade-instructions/releases/8.0.0-rc.20-to-8.0.0-rc.21/sources/remove-field-presets/app/instructions.md b/upgrade-instructions/releases/8.0.0-rc.20-to-8.0.0-rc.21/sources/remove-field-presets/app/instructions.md index 787b40c89ded..c31325bd7381 100644 --- a/upgrade-instructions/releases/8.0.0-rc.20-to-8.0.0-rc.21/sources/remove-field-presets/app/instructions.md +++ b/upgrade-instructions/releases/8.0.0-rc.20-to-8.0.0-rc.21/sources/remove-field-presets/app/instructions.md @@ -54,10 +54,10 @@ model Event { // After model Event { - id UuidString @id @onCreate(uuid()) + id UuidString @id @onCreate(uuid()) code Char(12) - seenAt TimeTemporal(3) @onUpdate(now()) - createdAt TimestamptzTemporal @onCreate(now()) + seenAt Time(3) @onUpdate(now()) + createdAt temporal.CreatedAt payload Jsonb } ``` @@ -86,8 +86,8 @@ model Event { fields: { id: field.column(type.UuidString()).id().onCreate(gen.uuid()), code: field.column(type.Char(12)), - seenAt: field.column(type.TimeTemporal(3)).onUpdate(gen.now()), - createdAt: field.column(type.TimestamptzTemporal()).onCreate(gen.now()), + seenAt: field.column(type.Time(3)).onUpdate(gen.now()), + createdAt: field.column(type.temporal.CreatedAt()), payload: field.column(type.Jsonb()), slug: field.column(type.Char(16)).onCreate(gen.nanoid(16)), }, @@ -160,32 +160,30 @@ A TypeScript `id` preset's trailing `{ name }`, as in `field.id.uuidv4String({ n | `boolean()` | `pg/bool@1` | `Boolean` | `field.column(type.Boolean())` | | `json()` | `pg/jsonb@1` | `Jsonb` (not `Json`, which is `pg/json@1`) | `field.column(type.Jsonb())` | | `bytes()` | `pg/bytea@1` | `Bytes` | `field.column(type.Bytes())` | -| `dateTime()` | `pg/timestamptz-temporal@1` | `TimestamptzTemporal` | `field.column(type.TimestamptzTemporal())` | +| `dateTime()` | `pg/timestamptz-temporal@1` | `DateTime` | `field.column(type.DateTime())` | | `uuidNative()` | `pg/uuid@1` | `Uuid` | `field.column(type.Uuid())` | | `id.uuidv4Native()` | `pg/uuid@1`, `@id`, `uuidv4` | `Uuid @id @onCreate(uuid())` | `field.column(type.Uuid()).id().onCreate(gen.uuid())` | | `id.uuidv7Native()` | `pg/uuid@1`, `@id`, `uuidv7` | `Uuid @id @onCreate(uuid(7))` | `field.column(type.Uuid()).id().onCreate(gen.uuid(7))` | -| `temporal.createdAt()` | `pg/timestamptz-temporal@1`, now on create | `TimestamptzTemporal @onCreate(now())` | `field.column(type.TimestamptzTemporal()).onCreate(gen.now())` | -| `temporal.updatedAt()` | the same, on create and update | `TimestamptzTemporal @onCreate(now()) @onUpdate(now())` | `field.column(type.TimestamptzTemporal()).onCreate(gen.now()).onUpdate(gen.now())` | -| `temporal.createdAtString()`, `temporal.updatedAtString()` | `pg/timestamptz-string@1` | `TimestamptzString` and the attributes | `field.column(type.TimestamptzString())` and the calls | -| `temporal.createdAtJsDate()`, `temporal.updatedAtJsDate()` | `pg/timestamptz-date@1` | `TimestamptzJsDate` and the attributes | `field.column(type.TimestamptzJsDate())` and the calls | +| `temporal.createdAt()` | `pg/timestamptz-temporal@1`, now on create | `temporal.CreatedAt` | `field.column(type.temporal.CreatedAt())` | +| `temporal.updatedAt()` | the same, on create and update | `temporal.UpdatedAt` | `field.column(type.temporal.UpdatedAt())` | +| `temporal.createdAtString()`, `temporal.updatedAtString()` | `pg/timestamptz-string@1` | `temporal.CreatedAtString`, `temporal.UpdatedAtString` | `field.column(type.temporal.CreatedAtString())`, `field.column(type.temporal.UpdatedAtString())` | +| `temporal.createdAtJsDate()`, `temporal.updatedAtJsDate()` | `pg/timestamptz-date@1` | `temporal.CreatedAtJsDate`, `temporal.UpdatedAtJsDate` | `field.column(type.temporal.CreatedAtJsDate())`, `field.column(type.temporal.UpdatedAtJsDate())` | | `temporal.timestamp(p, onCreate: now, onUpdate: now)` | `pg/timestamp-temporal@1`, `{ precision: p }` | `TimestampTemporal(p) @onCreate(now()) @onUpdate(now())` | `field.column(type.TimestampTemporal(p)).onCreate(gen.now()).onUpdate(gen.now())` | | `temporal.timestamptz(...)` | `pg/timestamptz-temporal@1` | `TimestamptzTemporal(p)` and the attributes | `field.column(type.TimestamptzTemporal(p))` and the calls | | `temporal.timestampString(...)` | `pg/timestamp-string@1` | `TimestampString(p)` and the attributes | `field.column(type.TimestampString(p))` and the calls | | `temporal.timestamptzString(...)` | `pg/timestamptz-string@1` | `TimestamptzString(p)` and the attributes | `field.column(type.TimestamptzString(p))` and the calls | | `temporal.timestamptzJsDate(...)` | `pg/timestamptz-date@1` | `TimestamptzJsDate(p)` and the attributes | `field.column(type.TimestamptzJsDate(p))` and the calls | -| `temporal.date(onCreate: now, onUpdate: now)` | `pg/date-temporal@1` | `DateTemporal` and the attributes | `field.column(type.DateTemporal())` and the calls | +| `temporal.date(onCreate: now, onUpdate: now)` | `pg/date-temporal@1` | `Date` and the attributes | `field.column(type.Date())` and the calls | | `temporal.dateString(...)` | `pg/date-string@1` | `DateString` and the attributes | `field.column(type.DateString())` and the calls | -| `temporal.time(p, ...)` | `pg/time-temporal@1` | `TimeTemporal(p)` and the attributes | `field.column(type.TimeTemporal(p))` and the calls | +| `temporal.time(p, ...)` | `pg/time-temporal@1` | `Time(p)` and the attributes | `field.column(type.Time(p))` and the calls | | `temporal.timeString(p, ...)` | `pg/time-string@1` | `TimeString(p)` and the attributes | `field.column(type.TimeString(p))` and the calls | | `temporal.timetz(p, ...)` | `pg/timetz@1` | `Timetz(p)` and the attributes | `field.column(type.Timetz(p))` and the calls | -The `temporal.(...)` presets take an optional precision `p` (the first argument, or `precision: p`) and the phases `onCreate: now` and `onUpdate: now`. Write the precision as the constructor's argument, and leave it out when the preset left it out: `temporal.timestamptz()` is `TimestamptzTemporal`. Write each phase the preset set as its attribute, `@onCreate(now())` or `@onUpdate(now())`, and leave out a phase it did not set: `temporal.time(3, onUpdate: now)` is `TimeTemporal(3) @onUpdate(now())`. A preset with no phase is the bare constructor. The `createdAt` and `updatedAt` presets of each kind set `@onCreate(now())`, and the `updatedAt` ones `@onUpdate(now())` too. +The `temporal.(...)` presets take an optional precision `p` (the first argument, or `precision: p`) and the phases `onCreate: now` and `onUpdate: now`. Write the precision as the constructor's argument, and leave it out when the preset left it out: `temporal.timestamptz()` is `TimestamptzTemporal`. Write each phase the preset set as its attribute, `@onCreate(now())` or `@onUpdate(now())`, and leave out a phase it did not set: `temporal.time(3, onUpdate: now)` is `Time(3) @onUpdate(now())`. A preset with no phase is the bare constructor. -On Postgres, `temporal.CreatedAt` and `temporal.UpdatedAt` are now built on `DateTime`, a `timestamptz(3)` column read as a JavaScript `Date`, so they do not store what `temporal.createdAt()` and `temporal.updatedAt()` stored; write the explicit type the table names to keep the contract. +In TypeScript the precision and the phases are positional, and a phase is `'now'` or `undefined`: `field.temporal.timestamp(3, 'now', 'now')` is `field.column(type.TimestampTemporal(3)).onCreate(gen.now()).onUpdate(gen.now())`, `field.temporal.time(3, undefined, 'now')` is `field.column(type.Time(3)).onUpdate(gen.now())`, and `field.temporal.date('now')` is `field.column(type.Date()).onCreate(gen.now())`. -In TypeScript the precision and the phases are positional, and a phase is `'now'` or `undefined`: `field.temporal.timestamp(3, 'now', 'now')` is `field.column(type.TimestampTemporal(3)).onCreate(gen.now()).onUpdate(gen.now())`, `field.temporal.time(3, undefined, 'now')` is `field.column(type.TimeTemporal(3)).onUpdate(gen.now())`, and `field.temporal.date('now')` is `field.column(type.DateTemporal()).onCreate(gen.now())`. - -Attributes and calls written after the preset stay after the replacement: `temporal.updatedAt() @map("updated_at")` is `TimestamptzTemporal @onCreate(now()) @onUpdate(now()) @map("updated_at")`, `field.temporal.createdAt().optional()` is `field.column(type.TimestamptzTemporal()).onCreate(gen.now()).optional()`, and `temporal.timestamptz(onUpdate: now) @onCreate(now())` is `TimestamptzTemporal @onCreate(now()) @onUpdate(now())`. +Attributes and calls written after the preset stay after the replacement: `temporal.updatedAt() @map("updated_at")` is `temporal.UpdatedAt @map("updated_at")`, `field.temporal.createdAt().optional()` is `field.column(type.temporal.CreatedAt()).optional()`, and `temporal.timestamptz(onUpdate: now) @onCreate(now())` is `TimestamptzTemporal @onCreate(now()) @onUpdate(now())`. ### SQLite presets @@ -226,7 +224,7 @@ Rewrite every preset call in your Prisma 8 schema files as the tables say. `pris ```text PSL_UNRESOLVED_REFERENCE: id.uuidv4String() was a field preset, and Prisma 8 removed field presets. Write UuidString @id @onCreate(uuid()). -PSL_UNRESOLVED_REFERENCE: temporal.time(3, onUpdate: now) was a field preset, and Prisma 8 removed field presets. Write TimeTemporal(3) @onUpdate(now()). +PSL_UNRESOLVED_REFERENCE: temporal.time(3, onUpdate: now) was a field preset, and Prisma 8 removed field presets. Write Time(3) @onUpdate(now()). ``` The message writes the alias as the schema's target names it: `UuidString` on Postgres and `sql.UuidString` on SQLite. On Postgres you may write `sql.UuidString` too; both store the same column. If a preset was called with an argument it did not take, the message gives the type and tells you which attributes to add. From 0a8b170c32525056e48c8948329dc614e1fe0d81 Mon Sep 17 00:00:00 2001 From: willbot Date: Wed, 28 Oct 2026 14:26:38 +0100 Subject: [PATCH 109/118] The docs say a stored infinity fails to read as a Date, and that infer prefers rows in declared order Co-Authored-By: Claude Opus 5.5 Signed-off-by: willbot Signed-off-by: Will Madden --- ...oose an explicit type from their arguments.md | 2 +- docs/reference/codec-authoring-guide.md | 2 +- docs/reference/scalar-types.md | 2 +- .../05-tools-write-convenience-types/spec.md | 5 +++-- .../app/instructions.md | 16 +++++++++++----- 5 files changed, 17 insertions(+), 10 deletions(-) diff --git a/docs/architecture docs/adrs/ADR 279 - Convenience types choose an explicit type from their arguments.md b/docs/architecture docs/adrs/ADR 279 - Convenience types choose an explicit type from their arguments.md index 6c72c1699e21..233c78c0c599 100644 --- a/docs/architecture docs/adrs/ADR 279 - Convenience types choose an explicit type from their arguments.md +++ b/docs/architecture docs/adrs/ADR 279 - Convenience types choose an explicit type from their arguments.md @@ -113,7 +113,7 @@ Each place that needs a field's codec resolves the field's call first, including ### What the tools write `contract infer` reads a database and `contract print` reads a contract, and neither knows which kind of type the user wrote. Both write a convenience call wherever one gives the column's explicit type, and the explicit type otherwise: -1. For each database type, one explicit type is marked `inferred`: the one its convenience default gives. Infer and the Prisma 7 schema reader pick that type for a column. If that type cannot read the column's own default, such as `infinity` for a `Date`, infer picks the type of the same convenience type that can, so it never writes a field whose default fails to read. +1. For each database type, one explicit type is marked `inferred`: the one its convenience default gives. Infer and the Prisma 7 schema reader pick that type for a column. If that type cannot read the column's own default, such as `infinity` for a `Date`, infer picks the first row, in the order the convenience type declares its rows, with the same storage whose type can, so it does not write a field whose default fails to read where some row reads it. The rows' order is therefore infer's order of preference. Infer reads only the default, not the rows, so a column whose rows hold such a value is still written with the default type. 2. To write the call for an explicit type, the tools find the row that names it, build the arguments from the row, and drop those equal to the defaults. 3. They resolve the call again and keep it only if it gives the same codec and parameters. diff --git a/docs/reference/codec-authoring-guide.md b/docs/reference/codec-authoring-guide.md index e457fda28542..c7677a1e2eab 100644 --- a/docs/reference/codec-authoring-guide.md +++ b/docs/reference/codec-authoring-guide.md @@ -591,7 +591,7 @@ A codec with parameters of its own sets `paramsSchema` to `dataType.params.and(o A type constructor maps its arguments onto the parameters and names only the codec; the data type follows from the codec. Every argument of a type constructor is a value argument, `{ kind: 'value', dataType }`, and assembly refuses any other kind with `CONTRACT.PACK_CONTRIBUTION_INVALID`. A value argument is a value of the named data type, read and cast as an attribute argument is (ADR 254), so `Vector(1536)` reads `1536` as a `pg/int2` and casts it to `pg/int4`, and `Vector("1536")` is refused. On Postgres every length, precision, scale and SRID is a `pg/int4`, the type of a typmod. A value argument carries no bounds, because the parameter's schema checks the value it gives. Assembly refuses a value argument whose data type no component registers (`CONTRACT.DATA_TYPE_UNREGISTERED`) or that no contract source can write (`CONTRACT.DATA_TYPE_NOT_WRITABLE`). In the TypeScript builder, the pack that registers a data type declares the TypeScript value an argument of it takes as `__dataTypeArguments` in its type, such as `{ 'pg/int4': { argument: number; parameter: number } }`: `argument` is what an author passes, and `parameter` is the type parameter the data type's reader gives for it, and `contract print` writes each argument as a literal of its data type. `inferred: true` marks the constructor written for a column of the data type when no schema names one: `contract infer` and the Prisma 7 reader write it. `contract print` writes it when it produces the column's codec, and otherwise the first constructor in the stack that does, so a `timestamptz` column with the `Temporal` codec prints `TimestamptzTemporal`. It is called with the fewest arguments that give the column's parameters in normal form, so a `numeric(10,0)` column is written `Numeric(10)`. The Prisma 7 reader builds a contract rather than printing PSL, so it passes every parameter the Prisma 7 column gives. At most one constructor per data type carries the mark. A data type with no marked constructor, such as `pg/tsquery`, is still recognised, but `contract infer` cannot write a column of it and fails with `CONTRACT.INFER_COLUMN_TYPE_UNCLAIMED`. -A type choice (`kind: 'typeChoice'`) is a second kind of type declaration, for a type named after what a field means rather than a column type ([ADR 279](../architecture%20docs/adrs/ADR%20279%20-%20Convenience%20types%20choose%20an%20explicit%20type%20from%20their%20arguments.md)). It names no codec. It declares option arguments, `{ kind: 'option', name, values, default, optional: true }`, written as bare names (the option kind exists only for type choice arguments), and value arguments it passes on by name, such as `precision`; and `choices`, a table whose rows map one value of each option to the name of a type constructor the same component contributes at the same level of the namespace, with `defaults` for passed-on arguments the call leaves out. A call resolves to its row's constructor, called with the passed-on arguments, so the contract records only that constructor's codec and parameters. A combination with no row, and a passed-on argument the chosen constructor does not take, are refused at the argument. Every argument is optional, so a type choice is also written bare, as its default row. Assembly refuses a table that names a constructor the component does not contribute beside the choice, a value or default an option does not list, defaults with no row, and a passed-on argument of another data type than its constructor's. A type choice is never marked `inferred`. Where a call of a type choice becomes the constructor that `contract print` or `contract infer` writes for a column, they write the type choice call instead, by name, with only the arguments that differ from its defaults, and only when that call gives the same column; otherwise they write the constructor. In the TypeScript builder its `type.*` helper takes the same arguments as one object keyed by name, such as `type.DateTime({ as: 'string', precision: 6 })`, and is typed as one overload per row. +A type choice (`kind: 'typeChoice'`) is a second kind of type declaration, for a type named after what a field means rather than a column type ([ADR 279](../architecture%20docs/adrs/ADR%20279%20-%20Convenience%20types%20choose%20an%20explicit%20type%20from%20their%20arguments.md)). It names no codec. It declares option arguments, `{ kind: 'option', name, values, default, optional: true }`, written as bare names (the option kind exists only for type choice arguments), and value arguments it passes on by name, such as `precision`; and `choices`, a table whose rows map one value of each option to the name of a type constructor the same component contributes at the same level of the namespace, with `defaults` for passed-on arguments the call leaves out. A call resolves to its row's constructor, called with the passed-on arguments, so the contract records only that constructor's codec and parameters. A combination with no row, and a passed-on argument the chosen constructor does not take, are refused at the argument. Every argument is optional, so a type choice is also written bare, as its default row. Assembly refuses a table that names a constructor the component does not contribute beside the choice, a value or default an option does not list, defaults with no row, a row default for an argument the choice does not pass on, a row whose constructor requires an argument that the row gives no default and the choice does not pass on, and a passed-on argument of another data type than its constructor's. The order of the rows matters in one place: when the default type of a column cannot read the column's own default, `contract infer` takes the first row with the same storage whose type can. A type choice is never marked `inferred`. Where a call of a type choice becomes the constructor that `contract print` or `contract infer` writes for a column, they write the type choice call instead, by name, with only the arguments that differ from its defaults, and only when that call gives the same column; otherwise they write the constructor. In the TypeScript builder its `type.*` helper takes the same arguments as one object keyed by name, such as `type.DateTime({ as: 'string', precision: 6 })`, and is typed as one overload per row. ```ts import type { AuthoringTypeNamespace } from '@internal/framework-components/authoring'; diff --git a/docs/reference/scalar-types.md b/docs/reference/scalar-types.md index 5e3d8e9de449..9396a8926087 100644 --- a/docs/reference/scalar-types.md +++ b/docs/reference/scalar-types.md @@ -129,7 +129,7 @@ A literal default of a date, time, interval or bytes type is written with the ta The tools that write a schema for you write the convenience types, with only the arguments that differ from the defaults, and the explicit type only where no convenience call becomes it, such as `TimestampTemporal` for a `timestamp` read as `Temporal.PlainDateTime`. - `prisma orm init` writes `createdAt DateTime @default(now()) @onCreate(now())` and `updatedAt temporal.UpdatedAt` in a PSL schema, and `field.column(type.DateTime()).default(now()).onCreate(gen.now())` and `field.column(type.temporal.UpdatedAt())` in a TypeScript contract. -- `prisma contract infer` takes the default path for each column: a `timestamptz(3)` column is `DateTime`, a `timestamp(3)` column `DateTime(storage: timestamp)`, a `date` column `Date`, a `time(0)` column `Time(precision: 0)`, a `timetz` column `Time(storage: timetz)` and an `interval` column `Duration`. A column with no declared precision holds microseconds, as precision 6 does, so a `timestamptz` column is `DateTime(precision: 6)`; `db verify` and the migration planner count an omitted precision on a date or time column as 6. A `Date` holds milliseconds, so a timestamp column with more digits reads with them dropped, and a timestamp default with digits below a millisecond is written as its raw `sql` expression. To keep the digits, change the field to `DateTime(as: temporal)` or `DateTime(as: string)`. When the default-path type cannot read the column's own default as a row at all, as a `Date` cannot read `infinity` or `-infinity`, infer writes the first row of the same convenience type whose type can, which for those values is `as: string`: a `timestamp(3)` column with `DEFAULT 'infinity'` is ``DateTime(as: string, storage: timestamp, precision: 3) @default(timestamp`infinity`)``. +- `prisma contract infer` takes the default path for each column: a `timestamptz(3)` column is `DateTime`, a `timestamp(3)` column `DateTime(storage: timestamp)`, a `date` column `Date`, a `time(0)` column `Time(precision: 0)`, a `timetz` column `Time(storage: timetz)` and an `interval` column `Duration`. A column with no declared precision holds microseconds, as precision 6 does, so a `timestamptz` column is `DateTime(precision: 6)`; `db verify` and the migration planner count an omitted precision on a date or time column as 6. A `Date` holds milliseconds, so a timestamp column with more digits reads with them dropped, and a timestamp default with digits below a millisecond is written as its raw `sql` expression. To keep the digits, change the field to `DateTime(as: temporal)` or `DateTime(as: string)`. When the default-path type cannot read the column's own default as a row at all, as a `Date` cannot read `infinity` or `-infinity`, infer writes the first row, in the order the convenience type declares its rows, of the same storage whose type can, which for those values is `as: string`: a `timestamp(3)` column with `DEFAULT 'infinity'` is ``DateTime(as: string, storage: timestamp, precision: 3) @default(timestamp`infinity`)``. Infer reads only the default, not the rows: a row that stores `infinity` or `-infinity` in a column read as a `Date` fails to read with `RUNTIME.DECODE_FAILED`, so change such a field to `DateTime(as: string)`; `as: temporal` cannot hold those values either. - `prisma contract print` writes each column as the convenience call that becomes its explicit type exactly, such as `DateTime(as: temporal)` for `TimestamptzTemporal`, and otherwise the explicit type. - `prisma7Schema(...)` reads Prisma 7's `DateTime` as `TimestampJsDate(3)`, a `timestamp(3)` column read as a `Date` as Prisma 7 read it, printed `DateTime(storage: timestamp)`, and `@db.Timestamptz(p)` as `TimestamptzJsDate(p)`. diff --git a/projects/datetime-convenience-types/slices/05-tools-write-convenience-types/spec.md b/projects/datetime-convenience-types/slices/05-tools-write-convenience-types/spec.md index 122c690a601b..cfc3936b2b85 100644 --- a/projects/datetime-convenience-types/slices/05-tools-write-convenience-types/spec.md +++ b/projects/datetime-convenience-types/slices/05-tools-write-convenience-types/spec.md @@ -39,7 +39,8 @@ A Prisma 7 schema read by `prisma7Schema(...)`: `DateTime` becomes `TimestampJsD ## Chosen design 1. **Infer always takes the default path where it can** (operator ruling, 2026-10-23: "infer should always choose the default path, where possible, since that's the simplest to see in your contract, as a user, and ought to represent the well-trodden path"). For each date and time column, infer picks the codec of the convenience type's default value for that column type, and writes the convenience call with only the arguments that differ from its defaults. The explicit type is written only where no convenience call reproduces the column. - - Consequence the docs state: a `Date` holds milliseconds, so a `timestamptz` or `timestamp` column with more than 3 digits of precision reads with the extra digits dropped, and `infinity` is refused on read. The upgrade and infer docs say to write `DateTime(as: temporal)` or `DateTime(as: string)` to keep them. + - Consequence the docs state: a `Date` holds milliseconds, so a `timestamptz` or `timestamp` column with more than 3 digits of precision reads with the extra digits dropped, and `infinity` is refused on read. The upgrade and infer docs say to write `DateTime(as: temporal)` or `DateTime(as: string)` to keep the digits, and `DateTime(as: string)` for a column whose rows hold `infinity`. + - The one exception (operator ruling, 2026-10-27): when the default-path type cannot read the column's own default, such as `infinity` for a `Date`, infer takes the first row of the same convenience type and storage whose type can, because every row created with that default would otherwise fail to read. 2. **An omitted precision equals precision 6** on `pg/timestamptz`, `pg/timestamp`, `pg/time`, `pg/timetz` and `pg/interval` when columns are compared (the data type's normal form, ADR 254), because PostgreSQL stores microseconds either way. That lets infer write `DateTime(precision: 6)` for a column with no declared precision without `db verify` or the planner seeing a difference. The contract keeps parameters as written; stored contracts and hashes do not change. If this normal-form change would change any existing contract, hash or plan in the repository's fixtures, stop and report. 3. **The `inferred` marks move to the default explicit types:** `pg/timestamptz` → `TimestamptzJsDate`, `pg/timestamp` → `TimestampJsDate`, `pg/interval` → `IntervalString`; `DateString`, `TimeString`, `Timetz` keep theirs. Both infer and the Prisma 7 reader follow the marks, which gives the Prisma 7 table above. The guard test that forbids Temporal codecs in these maps keeps passing and gains `pg/timestamp-instant@1`. 4. **Print and infer write convenience calls through one framework helper** beside `resolveAuthoringTypeChoice`: given an explicit constructor call, find the type choice row (at the same namespace level) that names that constructor, build named arguments from the row (options from its `when`, passed-on arguments by name), drop arguments equal to the defaults, re-resolve to prove it reproduces the same codec and parameters, and otherwise return nothing so the caller writes the explicit call. Arguments are always written by name (positional `DateTime(6)` would land in `as`). @@ -60,7 +61,7 @@ A Prisma 7 schema read by `prisma7Schema(...)`: `DateTime` becomes `TimestampJsD | --- | --- | | `timestamptz` with no declared precision | `DateTime(precision: 6)`, by decision 2 | | `timestamp` column read as `Temporal.PlainDateTime` | Explicit `TimestampTemporal` | -| An inferred `infinity` default on a `timestamptz` column | The `Date` codec cannot read it; infer prints it as a raw `sql` default and says so (the existing fallback), pinned by `infer-roundtrip-fidelity.prisma7-defaults.e2e.test.ts` | +| An inferred `infinity` default on a `timestamptz` or `timestamp` column | The `Date` codec cannot read it, so infer writes the first row that can: `DateTime(as: string, ...)` with the default as written (decision 1's exception) | | `time` with no precision | `Time` (string default; omitted precision equals 6 by decision 2) | | `examples/prisma7-adoption` reading `updatedAt.replace(' ', 'T')` | Code updated to read a `Date` | | Two type choices naming one constructor | The helper takes the first in namespace order; no PostgreSQL constructor is named twice | diff --git a/upgrade-instructions/pending/tools-write-convenience-types/app/instructions.md b/upgrade-instructions/pending/tools-write-convenience-types/app/instructions.md index 500f78c59c71..0a90a36b9060 100644 --- a/upgrade-instructions/pending/tools-write-convenience-types/app/instructions.md +++ b/upgrade-instructions/pending/tools-write-convenience-types/app/instructions.md @@ -66,23 +66,29 @@ For each project whose `prisma.config.ts` uses `prisma7Schema(...)`: A `Date` holds milliseconds. A `@db.Timestamptz(6)` or `@db.Timestamp(6)` column with values that use the extra digits reads with them dropped. To keep them, write the Prisma 8 schema yourself with `DateTime(as: string)` or `DateTime(as: temporal)` for that field. +A `Date` cannot hold `infinity` or `-infinity` either. A row that stores one now fails to read with `RUNTIME.DECODE_FAILED`, where it read as the string `infinity` before. For a column that can hold them, write the Prisma 8 schema yourself with `DateTime(as: string, storage: timestamp, precision: 3)` for that field; `DateTime(as: temporal)` cannot hold them either. + ## `temporal-type-aliases-on-datetime` `temporal.CreatedAt` is now `DateTime @onCreate(now())` and `temporal.UpdatedAt` is `DateTime @onCreate(now()) @onUpdate(now())`, a `timestamptz(3)` column read as a JavaScript `Date`. To keep each field exactly as it is, and its contract, write the type and the generators the alias stood for: | Before | After, in PSL | | --- | --- | -| `temporal.CreatedAt` | `TimestamptzTemporal @onCreate(now())` | -| `temporal.UpdatedAt` | `TimestamptzTemporal @onCreate(now()) @onUpdate(now())` | -| `temporal.CreatedAtString` | `TimestamptzString @onCreate(now())` | -| `temporal.UpdatedAtString` | `TimestamptzString @onCreate(now()) @onUpdate(now())` | +| `temporal.CreatedAt` | `DateTime(as: temporal) @onCreate(now())` | +| `temporal.UpdatedAt` | `DateTime(as: temporal) @onCreate(now()) @onUpdate(now())` | +| `temporal.CreatedAtString` | `DateTime(as: string) @onCreate(now())` | +| `temporal.UpdatedAtString` | `DateTime(as: string) @onCreate(now()) @onUpdate(now())` | | `temporal.CreatedAtJsDate` | `TimestamptzJsDate @onCreate(now())` | | `temporal.UpdatedAtJsDate` | `TimestamptzJsDate @onCreate(now()) @onUpdate(now())` | -In a TypeScript contract, `field.column(type.temporal.CreatedAtString())` becomes `field.column(type.TimestamptzString()).onCreate(gen.now())`, adding `.onUpdate(gen.now())` for an `UpdatedAt` alias, with `gen` taken from the `defineContract` callback beside `field` and `type`. Do the same for each alias in the table. Re-emit with `prisma contract emit`: the contract is the same as before. +`DateTime(as: temporal)` is exactly `TimestamptzTemporal`, and `DateTime(as: string)` exactly `TimestamptzString`; either form gives the same contract. `TimestamptzJsDate` keeps its explicit name, because `DateTime` gives `TimestamptzJsDate(3)`, which stores a precision the alias did not. + +In a TypeScript contract, `field.column(type.temporal.CreatedAtString())` becomes `field.column(type.DateTime({ as: 'string' })).onCreate(gen.now())`, adding `.onUpdate(gen.now())` for an `UpdatedAt` alias, with `gen` taken from the `defineContract` callback beside `field` and `type`. Do the same for each alias in the table. Re-emit with `prisma contract emit`: the contract is the same as before. To move a `temporal.CreatedAt` or `temporal.UpdatedAt` field to the new default instead, keep the alias and update the code that reads it to a `Date`. Its column becomes `timestamptz(3)`, which rounds stored values to milliseconds, so plan it with `prisma migration plan --delete .` and apply it with `prisma db migrate`, or run `prisma db update --delete .`. +If you followed the 8.0.0-rc.21 guide's `remove-field-presets` change, a field it rewrote from `temporal.createdAt()` or `temporal.updatedAt()` to `temporal.CreatedAt` or `temporal.UpdatedAt` is such a field: write it from the table above to keep its contract. + The detection also matches `temporal.CreatedAt` and `temporal.UpdatedAt` in SQLite and MongoDB projects. There they keep their meaning and need no change. ## `date-default-below-a-millisecond` From bb1e118e8bc3a4d82218c52e13e5546e1894a84d Mon Sep 17 00:00:00 2001 From: willbot Date: Wed, 28 Oct 2026 14:33:33 +0100 Subject: [PATCH 110/118] Infer looks each type choice row up in the namespace instead of carrying alternatives When the default-path type cannot read a column default, infer walks the type choice's rows. It now looks each row's constructor up in the authoring namespace and compares its codec's data type with the marked constructor's, so InferredTypeConstructor no longer carries the list of every other constructor of its data type. Behaviour is unchanged. The infer test fixture's dataTypeOfCodec now answers from the codec lookup, as the assembled stack does. Co-Authored-By: Claude Opus 5.5 Signed-off-by: willbot Signed-off-by: Will Madden --- .../src/core/psl-build/column-types.ts | 19 +++++------- .../psl-build/inferred-type-constructors.ts | 31 +++---------------- .../inferred-type-constructors.test.ts | 7 +++-- .../1-target/test/psl-infer/fixtures.ts | 6 ++-- 4 files changed, 21 insertions(+), 42 deletions(-) diff --git a/packages/2-sql-family/9-family/src/core/psl-build/column-types.ts b/packages/2-sql-family/9-family/src/core/psl-build/column-types.ts index cea4f079126e..65677688143c 100644 --- a/packages/2-sql-family/9-family/src/core/psl-build/column-types.ts +++ b/packages/2-sql-family/9-family/src/core/psl-build/column-types.ts @@ -3,6 +3,7 @@ import { authoringTypeConstructorArguments, type DataTypeSupport, getAuthoringTypeChoice, + getAuthoringTypeConstructor, } from '@internal/framework-components/authoring'; import type { DataType } from '@internal/framework-components/codec'; import { resolveReportedSqlType } from '@internal/sql-contract/data-type'; @@ -12,7 +13,6 @@ import { sqlFamilyError } from '../errors'; import { fewestArgumentsCall, type InferredTypeConstructor, - type InferredTypeConstructorPath, type InferredTypeConstructors, type PslTypeArgument, sameInNormalForm, @@ -56,7 +56,7 @@ function reportedTypeOf(column: SqlColumnIR, tableName: string): ReportedSqlType /** The call to the type choice that becomes `marked` with every parameter in `params`, compared in the normal form, as `db verify` compares the column, with the explicit call it becomes. */ function convenienceCall( - marked: InferredTypeConstructorPath & Pick, + marked: InferredTypeConstructor, params: Readonly>, authoringTypes: AuthoringTypeNamespace, support: DataTypeSupport, @@ -95,16 +95,13 @@ async function rowReadingDefault( ): Promise { const choice = getAuthoringTypeChoice({ type: authoringTypes }, written.path); for (const row of choice?.descriptor.choices ?? []) { - const path = [...written.path.slice(0, -1), row.type].join('.'); - const alternative = marked.alternatives.find((candidate) => candidate.path.join('.') === path); + const path = [...written.path.slice(0, -1), row.type]; + const descriptor = getAuthoringTypeConstructor({ type: authoringTypes }, path); const call = - alternative && - convenienceCall( - { ...alternative, dataType: marked.dataType }, - params, - authoringTypes, - support, - ); + descriptor !== undefined && + support.dataTypeOfCodec(descriptor.output.codecId) === marked.dataType.id + ? convenienceCall({ ...marked, path, descriptor }, params, authoringTypes, support) + : undefined; if (call !== undefined && (await readsDefault(call))) return call; } return undefined; diff --git a/packages/2-sql-family/9-family/src/core/psl-build/inferred-type-constructors.ts b/packages/2-sql-family/9-family/src/core/psl-build/inferred-type-constructors.ts index 1a677d7fa03b..0a426006934f 100644 --- a/packages/2-sql-family/9-family/src/core/psl-build/inferred-type-constructors.ts +++ b/packages/2-sql-family/9-family/src/core/psl-build/inferred-type-constructors.ts @@ -33,14 +33,6 @@ export interface InferredTypeConstructor { readonly path: readonly string[]; readonly descriptor: AuthoringTypeConstructorDescriptor; readonly dataType: SqlDataType; - /** The other type constructors whose codec represents the same data type, in namespace order. */ - readonly alternatives: readonly InferredTypeConstructorPath[]; -} - -/** A type constructor by its path in an authoring namespace. */ -export interface InferredTypeConstructorPath { - readonly path: readonly string[]; - readonly descriptor: AuthoringTypeConstructorDescriptor; } /** The constructors marked `inferred` in a stack, keyed by the id of the data type each produces. */ @@ -52,30 +44,15 @@ export function inferredTypeConstructors(input: { readonly codecLookup: Pick; readonly dataTypeLookup: DataTypeLookup; }): InferredTypeConstructors { - const byDataType = new Map(); const found = new Map(); - for (const typeConstructor of authoringTypeConstructors(input.authoringTypes)) { - const { path, descriptor } = typeConstructor; + for (const { path, descriptor } of authoringTypeConstructors(input.authoringTypes)) { + if (descriptor.inferred !== true) continue; const dataTypeId = input.codecLookup.descriptorFor(descriptor.output.codecId)?.dataType; const dataType = dataTypeId === undefined ? undefined : input.dataTypeLookup.get(dataTypeId); if (dataType === undefined || !isSqlDataType(dataType)) continue; - const ofDataType = byDataType.get(dataType.id) ?? []; - ofDataType.push(typeConstructor); - byDataType.set(dataType.id, ofDataType); - if (descriptor.inferred !== true) continue; - found.set(dataType.id, { path, descriptor, dataType, alternatives: [] }); + found.set(dataType.id, { path, descriptor, dataType }); } - return new Map( - [...found].map(([id, marked]) => [ - id, - { - ...marked, - alternatives: (byDataType.get(id) ?? []).filter( - ({ descriptor }) => descriptor !== marked.descriptor, - ), - }, - ]), - ); + return found; } function normalParams( diff --git a/packages/2-sql-family/9-family/test/psl-build/inferred-type-constructors.test.ts b/packages/2-sql-family/9-family/test/psl-build/inferred-type-constructors.test.ts index c9b22b38c1d8..dbf5d64bd607 100644 --- a/packages/2-sql-family/9-family/test/psl-build/inferred-type-constructors.test.ts +++ b/packages/2-sql-family/9-family/test/psl-build/inferred-type-constructors.test.ts @@ -3,7 +3,7 @@ import type { DataTypeSupport, } from '@internal/framework-components/authoring'; import type { AnyCodecDescriptor } from '@internal/framework-components/codec'; -import { createDataTypeLookup } from '@internal/framework-components/codec'; +import { createDataTypeLookup, dataTypeId } from '@internal/framework-components/codec'; import { sqlDataType } from '@internal/sql-contract/data-type'; import type { ReportedSqlType, SqlColumnIRInput } from '@internal/sql-schema-ir/types'; import { SqlColumnIR } from '@internal/sql-schema-ir/types'; @@ -177,7 +177,10 @@ const dataTypes: DataTypeSupport = { }, }, lookup: createDataTypeLookup([int, numeric, text, labelled, stamp, clock]), - dataTypeOfCodec: () => undefined, + dataTypeOfCodec: (codecId) => { + const id = dataTypeOfCodec[codecId]; + return id === undefined ? undefined : dataTypeId(id); + }, }; describe('inferredTypeConstructors', () => { diff --git a/packages/3-targets/postgres/1-target/test/psl-infer/fixtures.ts b/packages/3-targets/postgres/1-target/test/psl-infer/fixtures.ts index 0fc96b73b5e5..c05190341a10 100644 --- a/packages/3-targets/postgres/1-target/test/psl-infer/fixtures.ts +++ b/packages/3-targets/postgres/1-target/test/psl-infer/fixtures.ts @@ -219,6 +219,8 @@ export function treeFromFlat(schemaIR: SqlSchemaIR): PostgresDatabaseSchemaNode * The stack `contract emit` reads an inferred schema with: the type constructors the family and the * adapter contribute, the target's own, and the target's codecs and data types. */ +const inferCodecLookup = createPostgresBuiltinCodecLookup(); + export const inferBuildContext: SqlPslBuildContext = { authoringContributions: { type: { @@ -229,11 +231,11 @@ export const inferBuildContext: SqlPslBuildContext = { }, generatorFunctions: EMPTY_GENERATOR_FUNCTIONS, componentIdsByGenerator: new Map(), - codecLookup: createPostgresBuiltinCodecLookup(), + codecLookup: inferCodecLookup, dataTypes: { entries: postgresDataTypeEntries(), lookup: createDataTypeLookup(postgresDataTypes), - dataTypeOfCodec: () => undefined, + dataTypeOfCodec: (codecId) => inferCodecLookup.descriptorFor(codecId)?.dataType, }, }; From 57c0266469bba092de9e2630b892938e6c54b5f9 Mon Sep 17 00:00:00 2001 From: willbot Date: Wed, 28 Oct 2026 14:40:15 +0100 Subject: [PATCH 111/118] Hover writes the explicit type with the writer contract print uses Hover printed the explicit type a convenience call becomes with its own printer: the bare constructor name, without the type choice's namespace, and each argument through JSON.stringify, which quotes strings unlike PSL and throws on a bigint, so the whole hover disappeared. The literal writer that infer and print use moves from the SQL family to framework-components, so hover can use it: writeDataTypeValue, writtenTypeArguments, and writeTypedLiteral (formerly writeDefaultLiteral, now taking the receiving data type rather than a column codec, so the framework stays family-blind). A new writtenTypeConstructorCall writes a call with its arguments by name. Hover now writes the full path, such as pg.Stamp(precision: 3), in both the "is exactly" line and the table of choices, and names no explicit type when an argument has no literal. Signed-off-by: willbot Signed-off-by: Will Madden Co-Authored-By: Claude Opus 5.5 --- .../src/exports/authoring.ts | 7 + .../src/shared/data-type-literal.ts | 403 ++++++++++++++++++ .../test/data-type-literal.test.ts | 106 +++++ .../3-tooling/language-server/src/hover.ts | 48 ++- .../language-server/test/helpers/hover.ts | 19 + .../test/hover.contributed-types.test.ts | 27 +- .../src/core/psl-build/column-types.ts | 2 +- .../src/core/psl-build/default-mapping.ts | 335 +-------------- .../psl-build/inferred-type-constructors.ts | 28 +- .../9-family/src/exports/psl-build.ts | 7 +- .../inferred-type-constructors.test.ts | 19 - .../src/core/psl-print/column-types.ts | 2 +- .../src/core/psl-print/enum-blocks.ts | 6 +- 13 files changed, 607 insertions(+), 402 deletions(-) create mode 100644 packages/1-framework/1-core/framework-components/src/shared/data-type-literal.ts create mode 100644 packages/1-framework/1-core/framework-components/test/data-type-literal.test.ts diff --git a/packages/1-framework/1-core/framework-components/src/exports/authoring.ts b/packages/1-framework/1-core/framework-components/src/exports/authoring.ts index e1f6837044c2..1a6219d56f34 100644 --- a/packages/1-framework/1-core/framework-components/src/exports/authoring.ts +++ b/packages/1-framework/1-core/framework-components/src/exports/authoring.ts @@ -32,6 +32,13 @@ export { authoringTypeConstructors, findAuthoringTypeConstructorCall, } from '../shared/authoring-type-constructor-call'; +export { + type TypedLiteralOptions, + writeDataTypeValue, + writeTypedLiteral, + writtenTypeArguments, + writtenTypeConstructorCall, +} from '../shared/data-type-literal'; export type { AssembledDataTypeSupport, DataTypeComponent, diff --git a/packages/1-framework/1-core/framework-components/src/shared/data-type-literal.ts b/packages/1-framework/1-core/framework-components/src/shared/data-type-literal.ts new file mode 100644 index 000000000000..2463a20cd2c3 --- /dev/null +++ b/packages/1-framework/1-core/framework-components/src/shared/data-type-literal.ts @@ -0,0 +1,403 @@ +/** + * Writing a stored value back as the PSL literal its data type takes. + * + * The inverse of reading one. The stored form is read with the same functions a written value + * takes, `readWrittenValue` and `castTypedValue`: a number or numeral text through the target's + * number entry, a document or text through the entry that owns that syntax, each a value its type + * constructs, and the receiving data type must be that type or declare a cast from it. The text is + * then printed with that type's authoring entry and read straight back the same way, so a literal + * that would not come back as the stored value never reaches the contract source. ADR 254. + */ + +import type { JsonValue } from '@internal/contract/types'; +import { blindCast } from '@internal/utils/casts'; +import { isInternalError } from '@internal/utils/internal-error'; +import { + type DataTypeId, + type DataTypeLookup, + type DataTypeParams, + type DataTypeValue, + dataTypeId, + dataTypeParams, +} from './data-type'; +import type { + AuthoringArgumentDescriptor, + AuthoringTypeConstructorDescriptor, + DataTypeAuthoringEntry, +} from './framework-authoring'; +import { numeralText } from './numeral-text'; +import { printTaggedLiteral } from './tagged-literal'; +import { + castTypedValue, + type DataTypeSupport, + readWrittenValue, + type WrittenScalar, +} from './written-value'; + +/** What a value is written for: the data type that receives it, that type's parameters, and whether it is a list. */ +export interface TypedLiteralOptions { + /** The stack's data types, whose casts say which other types' values each one takes, with the authoring entries that write them. */ + readonly dataTypes?: DataTypeSupport | undefined; + /** The data type the value is written in. */ + readonly dataType?: DataTypeId | undefined; + /** The receiving type's parameters, under which a shorter literal can read back as the stored value, as `-0.5` does on `numeric(65,30)`. */ + readonly typeParams?: unknown; + /** + * Whether the value is a list whose elements each carry the receiving data type. Otherwise a + * written list goes through that type's list cast. + */ + readonly list?: boolean; +} + +/** A string as a contract source writes it, with the escapes its string reader resolves. */ +function escapePslString(value: string): string { + return value + .replace(/\\/g, '\\\\') + .replace(/"/g, '\\"') + .replace(/\n/g, '\\n') + .replace(/\r/g, '\\r'); +} + +/** + * The authoring entries the printer reads, arranged by what it has to look up: the entry that owns + * each type, and the types each tag writes. + */ +interface WritingSurface { + readonly support: DataTypeSupport; + readonly entryOf: ReadonlyMap; + readonly tagTypes: readonly DataTypeId[]; +} + +function writingSurface(support: DataTypeSupport): WritingSurface { + const entryOf = new Map(); + const tagTypes: DataTypeId[] = []; + for (const [key, entry] of Object.entries(support.entries)) { + const written = entry.written; + if (written.kind === 'tag') { + const type = dataTypeId(key); + if (!entryOf.has(type)) entryOf.set(type, entry); + tagTypes.push(type); + continue; + } + if (written.syntax === 'number') { + for (const type of written.types) entryOf.set(type, entry); + continue; + } + entryOf.set(key, entry); + } + return { support, entryOf, tagTypes }; +} + +function readValue(surface: WritingSurface, written: WrittenScalar): DataTypeValue | undefined { + const read = readWrittenValue(surface.support, written); + return read.ok ? read.value : undefined; +} + +/** The value of a tag's type that a stored document is, constructed by that type. */ +function tagValue( + surface: WritingSurface, + type: DataTypeId, + value: JsonValue, +): DataTypeValue | undefined { + try { + return surface.support.lookup.get(type)?.valueFrom(value, {}); + } catch (error) { + if (isInternalError(error)) throw error; + return undefined; + } +} + +/** + * Every value the stored form could be, in the order a written value would be read: a number or + * numeral text through the number entry first, then the plain syntax that matches the shape, then + * each tag, whose stored form is a whole document, which may itself be a number or a boolean. + */ +function classifications(value: JsonValue, surface: WritingSurface): readonly DataTypeValue[] { + const found: (DataTypeValue | undefined)[] = []; + if (typeof value === 'number') { + found.push(readValue(surface, { kind: 'number', text: numeralText(value) })); + } else if (typeof value === 'boolean') { + found.push(readValue(surface, { kind: 'boolean', value })); + } else if (typeof value === 'string') { + found.push(readValue(surface, { kind: 'number', text: value })); + found.push(readValue(surface, { kind: 'string', text: value })); + } + for (const type of surface.tagTypes) found.push(tagValue(surface, type, value)); + return found.filter((candidate) => candidate !== undefined); +} + +/** The receiving data type with the parameters a written value is read under. */ +interface ReceivingType { + readonly id: DataTypeId; + readonly params: DataTypeParams; +} + +/** What the receiving type stores for `json` under its parameters, or `undefined` when it holds no such value. */ +function storedUnder( + json: JsonValue, + receiver: ReceivingType, + lookup: DataTypeLookup, +): JsonValue | undefined { + const type = lookup.get(receiver.id); + if (type === undefined) return undefined; + try { + return type.toContract(type.valueFrom(json, receiver.params)); + } catch (error) { + if (isInternalError(error)) throw error; + return undefined; + } +} + +/** The stored form the receiving type takes this value as, spelled as its parameters give it, or `undefined` when its type takes no such value. */ +function admitted( + candidate: DataTypeValue, + receiver: ReceivingType, + surface: WritingSurface, +): JsonValue | undefined { + const cast = castTypedValue(surface.support, receiver.id, receiver.params, candidate); + return cast.ok ? cast.value.value : undefined; +} + +/** The text of the literal: what the entry prints, before the syntax that fences it. */ +function printedText(entry: DataTypeAuthoringEntry, value: JsonValue): string | undefined { + try { + return entry.print(value); + } catch { + return undefined; + } +} + +/** The printed text as the entry's syntax writes it, for reading back. */ +function writtenAs(entry: DataTypeAuthoringEntry, text: string): WrittenScalar | undefined { + const written = entry.written; + if (written.kind === 'tag') return { kind: 'tag', tag: written.tag, text }; + if (written.syntax !== 'boolean') return { kind: written.syntax, text }; + return text === 'true' || text === 'false' + ? { kind: 'boolean', value: text === 'true' } + : undefined; +} + +/** What the printed text reads back as, through the same read a written value takes. */ +function readBack( + surface: WritingSurface, + entry: DataTypeAuthoringEntry, + text: string, +): DataTypeValue | undefined { + const written = writtenAs(entry, text); + return written === undefined ? undefined : readValue(surface, written); +} + +function literalText(entry: DataTypeAuthoringEntry, text: string): string { + const written = entry.written; + if (written.kind === 'tag') return printTaggedLiteral(written.tag, text); + return written.syntax === 'string' ? `"${escapePslString(text)}"` : text; +} + +/** One element of a written list: its source text and the value the list cast receives. */ +interface WrittenElement { + readonly text: string; + readonly value: JsonValue; +} + +/** + * The value written as the literal of one of its types, proven by reading the text straight back: + * the same read, the same cast, and the stored form again. A number is tried first without the + * trailing fraction zeros the receiving type's scale spells it with. + */ +function writeScalar( + value: JsonValue, + receiver: ReceivingType, + surface: WritingSurface, +): string | undefined { + const shortest = withoutTrailingFractionZeros(value); + const candidates = [ + ...(shortest === undefined ? [] : classifications(shortest, surface)), + ...classifications(value, surface), + ]; + for (const candidate of candidates) { + const entry = surface.entryOf.get(candidate.type); + if (entry === undefined) continue; + const stored = admitted(candidate, receiver, surface); + if (stored === undefined || !sameForm(stored, value)) continue; + const text = printedText(entry, candidate.value); + if (text === undefined) continue; + const reread = readBack(surface, entry, text); + if (reread === undefined) continue; + const restored = admitted(reread, receiver, surface); + if (restored === undefined || !sameForm(restored, value)) continue; + return literalText(entry, text); + } + return undefined; +} + +/** + * `value`, a value of `dataType` as `contract.json` stores it, written as the literal of a type that `dataType` takes, proven to read back as `value`: `255` for `pg/int4`, ``sql`"MyType"[]` `` for `sql/expression`. `undefined` when no literal reads back as it. + */ +export function writeDataTypeValue( + value: JsonValue, + dataType: DataTypeId, + support: DataTypeSupport, +): string | undefined { + return writeScalar(value, { id: dataType, params: {} }, writingSurface(support)); +} + +const DECIMAL_WITH_FRACTION = /^-?\d+\.\d+$/; + +function withoutTrailingFractionZeros(value: JsonValue): string | undefined { + if (typeof value !== 'string' || !DECIMAL_WITH_FRACTION.test(value)) return undefined; + const shortened = value.replace(/0+$/, '').replace(/\.$/, ''); + return shortened === value ? undefined : shortened; +} + +/** One written element of a list, printed as a value of any type, with no receiving type to admit it. */ +function writeElement( + value: JsonValue, + of: readonly DataTypeId[], + surface: WritingSurface, +): WrittenElement | undefined { + for (const candidate of classifications(value, surface)) { + if (!of.includes(candidate.type)) continue; + const entry = surface.entryOf.get(candidate.type); + if (entry === undefined) continue; + const text = printedText(entry, candidate.value); + if (text === undefined) continue; + const reread = readBack(surface, entry, text); + if (reread === undefined || !of.includes(reread.type)) continue; + return { text: literalText(entry, text), value: reread.value }; + } + return undefined; +} + +/** + * A written list for a scalar data type, which that type's list cast turns into its one value, + * constructed by the type. Each element is classified on its own and must be of a type the + * cast takes. + */ +function writeListCast( + value: readonly JsonValue[], + receiver: ReceivingType, + surface: WritingSurface, +): string | undefined { + const listCast = surface.support.lookup.get(receiver.id)?.listCast; + if (listCast === undefined) return undefined; + const parts: string[] = []; + const elements: JsonValue[] = []; + for (const element of value) { + const written = writeElement(element, listCast.of, surface); + if (written === undefined) return undefined; + parts.push(written.text); + elements.push(written.value); + } + let cast: JsonValue; + try { + cast = listCast.cast(elements); + } catch (error) { + if (isInternalError(error)) throw error; + return undefined; + } + const stored = storedUnder(cast, receiver, surface.support.lookup); + return stored !== undefined && sameForm(stored, value) ? `[${parts.join(', ')}]` : undefined; +} + +/** The PSL literal a stored value is written as for the data type `options` describe, or `undefined` when it has no written form that reads back as that value. */ +export function writeTypedLiteral( + value: JsonValue | Date, + options: TypedLiteralOptions | undefined, +): string | undefined { + if (value instanceof Date) return undefined; + const { dataTypes, dataType } = options ?? {}; + if (dataTypes === undefined || dataType === undefined) return undefined; + const { lookup } = dataTypes; + const receivingType = lookup.get(dataType); + const receiver: ReceivingType = { + id: dataType, + params: receivingType === undefined ? {} : dataTypeParams(receivingType, options?.typeParams), + }; + const surface = writingSurface(dataTypes); + if (options?.list === true) { + if (!Array.isArray(value)) return undefined; + const parts: string[] = []; + for (const element of value) { + const written = writeScalar(element, receiver, surface); + if (written === undefined) return undefined; + parts.push(written); + } + return `[${parts.join(', ')}]`; + } + const written = writeScalar(value, receiver, surface); + if (written !== undefined) return written; + return Array.isArray(value) ? writeListCast(value, receiver, surface) : undefined; +} + +/** Whether two stored forms are the same JSON value, member by member and element by element. */ +function sameForm(left: JsonValue, right: JsonValue): boolean { + if (left === right) return true; + if (Array.isArray(left) !== Array.isArray(right)) return false; + if (typeof left !== 'object' || typeof right !== 'object' || left === null || right === null) { + return false; + } + const members = Object.entries(left); + const others = new Map(Object.entries(right)); + return ( + members.length === others.size && + members.every(([key, member]) => { + const other = others.get(key); + return other !== undefined && sameForm(member, other); + }) + ); +} + +/** A value argument of a type constructor, written as a literal of its data type. */ +function writtenArgument( + argument: AuthoringArgumentDescriptor, + value: unknown, + dataTypes: DataTypeSupport, +): string | undefined { + return writeDataTypeValue( + blindCast< + JsonValue, + 'a type constructor argument is a type parameter, which contract.json stores as JSON' + >(value), + dataTypeId(argument.dataType), + dataTypes, + ); +} + +/** + * Each argument of a call to `descriptor` as PSL writes it: a value argument as a literal of its data type that reads back as the same value. `undefined` when an argument is not a value argument or no literal reads back as it. + */ +export function writtenTypeArguments( + descriptor: AuthoringTypeConstructorDescriptor, + args: readonly unknown[], + dataTypes: DataTypeSupport, +): readonly string[] | undefined { + const written: string[] = []; + for (const [index, value] of args.entries()) { + const argument = descriptor.args?.[index]; + const text = argument && writtenArgument(argument, value, dataTypes); + if (text === undefined) return undefined; + written.push(text); + } + return written; +} + +/** + * The call to the type constructor `descriptor` at `path` as PSL writes it, with each argument that has a value written by its name as a literal of its data type, and the bare path when none has one. `undefined` when no literal reads back as one of the values. + */ +export function writtenTypeConstructorCall( + path: readonly string[], + descriptor: AuthoringTypeConstructorDescriptor, + args: readonly unknown[], + dataTypes: DataTypeSupport, +): string | undefined { + const written: string[] = []; + for (const [index, argument] of (descriptor.args ?? []).entries()) { + const value = args[index]; + if (value === undefined) continue; + const text = writtenArgument(argument, value, dataTypes); + if (text === undefined) return undefined; + written.push(argument.name === undefined ? text : `${argument.name}: ${text}`); + } + const name = path.join('.'); + return written.length === 0 ? name : `${name}(${written.join(', ')})`; +} diff --git a/packages/1-framework/1-core/framework-components/test/data-type-literal.test.ts b/packages/1-framework/1-core/framework-components/test/data-type-literal.test.ts new file mode 100644 index 000000000000..d3909c330bce --- /dev/null +++ b/packages/1-framework/1-core/framework-components/test/data-type-literal.test.ts @@ -0,0 +1,106 @@ +import { describe, expect, it } from 'vitest'; +import { createDataTypeLookup, dataType } from '../src/shared/data-type'; +import { + writeDataTypeValue, + writtenTypeArguments, + writtenTypeConstructorCall, +} from '../src/shared/data-type-literal'; +import type { AuthoringTypeConstructorDescriptor } from '../src/shared/framework-authoring'; +import type { DataTypeSupport } from '../src/shared/written-value'; + +const int = dataType('t/int', { read: (json) => json }); +const text = dataType('t/text', { read: (json) => json }); + +const dataTypes: DataTypeSupport = { + entries: { + [int.id]: { + written: { + kind: 'plain', + syntax: 'number', + types: [int.id], + classify: (written) => + /^\d+$/.test(written) ? { type: int.id, value: Number(written) } : undefined, + }, + print: String, + documentation: 'A whole number.', + }, + [text.id]: { + written: { kind: 'plain', syntax: 'string', parse: (written) => written }, + print: String, + documentation: 'Text.', + }, + }, + lookup: createDataTypeLookup([int, text]), + dataTypeOfCodec: () => undefined, +}; + +const numeric: AuthoringTypeConstructorDescriptor = { + kind: 'typeConstructor', + args: [ + { kind: 'value', dataType: 't/int', name: 'precision', optional: true }, + { kind: 'value', dataType: 't/int', name: 'scale', optional: true }, + ], + output: { codecId: 't/numeric@1' }, +}; + +const labelled: AuthoringTypeConstructorDescriptor = { + kind: 'typeConstructor', + args: [{ kind: 'value', dataType: 't/text', name: 'label' }], + output: { codecId: 't/labelled@1' }, +}; + +describe('writeDataTypeValue', () => { + it('writes a value as the literal of its data type that reads back as it', () => { + expect([ + writeDataTypeValue(10, int.id, dataTypes), + writeDataTypeValue('line\nbreak', text.id, dataTypes), + ]).toEqual(['10', '"line\\nbreak"']); + }); + + it('writes nothing for a value no literal reads back as, without throwing', () => { + expect([ + writeDataTypeValue(-1, int.id, dataTypes), + writeDataTypeValue({ precision: 1 }, int.id, dataTypes), + ]).toEqual([undefined, undefined]); + }); +}); + +describe('writtenTypeArguments', () => { + it('writes each value argument as a literal of its data type', () => { + expect([ + writtenTypeArguments(numeric, [10, 2], dataTypes), + writtenTypeArguments(labelled, ['say "hi"'], dataTypes), + ]).toEqual([['10', '2'], ['"say \\"hi\\""']]); + }); + + it('writes nothing for a value no literal of its data type reads back as', () => { + expect([ + writtenTypeArguments(numeric, [-1], dataTypes), + writtenTypeArguments(numeric, [{ precision: 1 }], dataTypes), + writtenTypeArguments(numeric, [1, 2, 3], dataTypes), + ]).toEqual([undefined, undefined, undefined]); + }); +}); + +describe('writtenTypeConstructorCall', () => { + it('writes the path and each argument that has a value, by name', () => { + expect([ + writtenTypeConstructorCall(['pg', 'Numeric'], numeric, [10, 2], dataTypes), + writtenTypeConstructorCall(['Numeric'], numeric, [undefined, 2], dataTypes), + writtenTypeConstructorCall(['ext', 'Numeric'], numeric, [], dataTypes), + ]).toEqual(['pg.Numeric(precision: 10, scale: 2)', 'Numeric(scale: 2)', 'ext.Numeric']); + }); + + it('writes an unnamed argument by position', () => { + const unnamed: AuthoringTypeConstructorDescriptor = { + kind: 'typeConstructor', + args: [{ kind: 'value', dataType: 't/text' }], + output: { codecId: 't/unnamed@1' }, + }; + expect(writtenTypeConstructorCall(['Unnamed'], unnamed, ['x'], dataTypes)).toBe('Unnamed("x")'); + }); + + it('writes nothing for a value no literal reads back as, such as a bigint', () => { + expect(writtenTypeConstructorCall(['Numeric'], numeric, [10n], dataTypes)).toBeUndefined(); + }); +}); diff --git a/packages/1-framework/3-tooling/language-server/src/hover.ts b/packages/1-framework/3-tooling/language-server/src/hover.ts index 88eb881a3df1..fa96c4c8c936 100644 --- a/packages/1-framework/3-tooling/language-server/src/hover.ts +++ b/packages/1-framework/3-tooling/language-server/src/hover.ts @@ -1,12 +1,12 @@ import { type AuthoringArgumentDescriptor, type AuthoringPslBlockDescriptorNamespace, - type AuthoringTypeChoiceDescriptor, type AuthoringTypeChoiceOption, type AuthoringTypeChoiceRow, type DataTypeSupport, describeAdmittedForms, isAuthoringTypeChoiceOption, + writtenTypeConstructorCall, } from '@internal/framework-components/authoring'; import { dataTypeId } from '@internal/framework-components/codec'; import { @@ -168,10 +168,24 @@ function renderContributedTypeContent( } const chosen = renderChosenType(symbol, result.annotation, input); const head = chosen === undefined ? fence : `${fence}\n\n${chosen}`; - return `${withDocumentation(head, symbol.descriptor.documentation)}\n\n${renderTypeChoices(symbol.descriptor)}`; + return `${withDocumentation(head, symbol.descriptor.documentation)}\n\n${renderTypeChoices(symbol, input.dataTypes)}`; } -/** The explicit type the call at `annotation` becomes, or `undefined` when its arguments choose none. */ +/** The explicit type `type`, at the level of the type choice `symbol`, called with `args` as the tools write it; `undefined` when the type choice names no such type or an argument has no literal. */ +function writtenChosenType( + symbol: ContributedTypeChoiceSymbol, + type: string, + args: readonly unknown[], + dataTypes: DataTypeSupport, +): string | undefined { + const descriptor = symbol.choiceTypes.get(type); + return ( + descriptor && + writtenTypeConstructorCall([...symbol.path.slice(0, -1), type], descriptor, args, dataTypes) + ); +} + +/** The explicit type the call at `annotation` becomes, or `undefined` when its arguments choose none or it cannot be written. */ function renderChosenType( symbol: ContributedTypeChoiceSymbol, annotation: TypeAnnotationAst | undefined, @@ -179,27 +193,31 @@ function renderChosenType( ): string | undefined { const call = readResolvedConstructorCall(annotation, input.sources); const chosen = chosenTypeConstructor(symbol, call, input.dataTypes); - if (!chosen.ok) return undefined; - const { type, descriptor, args } = chosen.value; - const written = (descriptor.args ?? []).flatMap((arg, index) => - args[index] === undefined - ? [] - : [`${arg.name ?? `arg${index + 1}`}: ${JSON.stringify(args[index])}`], - ); + const written = + chosen.ok && writtenChosenType(symbol, chosen.value.type, chosen.value.args, input.dataTypes); + if (written === false || written === undefined) return undefined; const callText = call === undefined ? symbol.path.join('.') : printTypeConstructorCall(call); - return `\`${callText}\` is exactly \`${type}${written.length === 0 ? '' : `(${written.join(', ')})`}\`.`; + return `\`${callText}\` is exactly \`${written}\`.`; } /** Each combination of a type choice's options, as written, and the explicit type it becomes. */ -function renderTypeChoices(descriptor: AuthoringTypeChoiceDescriptor): string { +function renderTypeChoices( + symbol: ContributedTypeChoiceSymbol, + dataTypes: DataTypeSupport, +): string { + const { descriptor } = symbol; const options = descriptor.args.filter(isAuthoringTypeChoiceOption); const cell = (value: string | undefined, option: AuthoringTypeChoiceOption) => value === option.default ? `${value} (default)` : (value ?? ''); const becomes = (row: AuthoringTypeChoiceRow) => { - const defaults = Object.entries(row.defaults ?? {}).map( - ([name, value]) => `${name}: ${JSON.stringify(value)}`, + const defaults = row.defaults ?? {}; + const args = (symbol.choiceTypes.get(row.type)?.args ?? []).map((argument) => + argument.name !== undefined && Object.hasOwn(defaults, argument.name) + ? defaults[argument.name] + : undefined, ); - return `\`${row.type}${defaults.length === 0 ? '' : `(${defaults.join(', ')})`}\``; + const written = writtenChosenType(symbol, row.type, args, dataTypes); + return `\`${written ?? `${[...symbol.path.slice(0, -1), row.type].join('.')}(...)`}\``; }; return [ `| ${[...options.map((option) => option.name), 'Becomes'].join(' | ')} |`, diff --git a/packages/1-framework/3-tooling/language-server/test/helpers/hover.ts b/packages/1-framework/3-tooling/language-server/test/helpers/hover.ts index e5850952c209..7a4a5d553962 100644 --- a/packages/1-framework/3-tooling/language-server/test/helpers/hover.ts +++ b/packages/1-framework/3-tooling/language-server/test/helpers/hover.ts @@ -175,6 +175,25 @@ const authoringContributions = assembleAuthoringContributions([ { when: { as: 'date', storage: 'narrow' }, type: 'Flag' }, ], }, + Shape: { + kind: 'typeChoice', + documentation: '', + args: [ + { + kind: 'option', + name: 'as', + values: ['raw', 'huge'], + default: 'raw', + optional: true, + }, + { kind: 'value', name: 'sql', dataType: 'fixture/expression', optional: true }, + { kind: 'value', name: 'precision', dataType: 'fixture/int', optional: true }, + ], + choices: [ + { when: { as: 'raw' }, type: 'Raw', defaults: { sql: 'text' } }, + { when: { as: 'huge' }, type: 'Stamp', defaults: { precision: { digits: 10 } } }, + ], + }, Stamp: { kind: 'typeConstructor', args: [{ name: 'precision', kind: 'value', dataType: 'fixture/int', optional: true }], diff --git a/packages/1-framework/3-tooling/language-server/test/hover.contributed-types.test.ts b/packages/1-framework/3-tooling/language-server/test/hover.contributed-types.test.ts index 752529e01fe0..011c8f5875d5 100644 --- a/packages/1-framework/3-tooling/language-server/test/hover.contributed-types.test.ts +++ b/packages/1-framework/3-tooling/language-server/test/hover.contributed-types.test.ts @@ -60,15 +60,15 @@ describe('providePslHover', () => { 'pg.Moment(as?: date | text, storage?: wide | narrow, precision?: a number)', '```', '', - '`pg.Moment(as: text)` is exactly `Text`.', + '`pg.Moment(as: text)` is exactly `pg.Text`.', '', 'A moment in time.', '', '| as | storage | Becomes |', '| --- | --- | --- |', - '| date (default) | wide (default) | `Stamp(precision: 3)` |', - '| text | wide (default) | `Text` |', - '| date (default) | narrow | `Flag` |', + '| date (default) | wide (default) | `pg.Stamp(precision: 3)` |', + '| text | wide (default) | `pg.Text` |', + '| date (default) | narrow | `pg.Flag` |', ].join('\n'), }); }); @@ -76,14 +76,16 @@ describe('providePslHover', () => { it('shows the explicit type a bare type choice is, with the arguments its row defaults', () => { const value = hover('model Product {\n at pg.Mo|ment\n}')?.contents; expect(value).toMatchObject({ - value: expect.stringContaining('`pg.Moment` is exactly `Stamp(precision: 3)`.'), + value: expect.stringContaining('`pg.Moment` is exactly `pg.Stamp(precision: 3)`.'), }); }); it('shows the explicit type with the arguments a call hands on', () => { const value = hover('model Product {\n at pg.Mo|ment(precision: 6)\n}')?.contents; expect(value).toMatchObject({ - value: expect.stringContaining('`pg.Moment(precision: 6)` is exactly `Stamp(precision: 6)`.'), + value: expect.stringContaining( + '`pg.Moment(precision: 6)` is exactly `pg.Stamp(precision: 6)`.', + ), }); }); @@ -91,4 +93,17 @@ describe('providePslHover', () => { const value = hover('model Product {\n at pg.Mo|ment(as: text, storage: narrow)\n}')?.contents; expect(value).toMatchObject({ value: expect.not.stringContaining('is exactly') }); }); + + it('writes each argument of the explicit type as a literal of its data type', () => { + const value = hover('model Product {\n shape pg.Sha|pe\n}')?.contents; + expect(value).toMatchObject({ + value: expect.stringContaining('`pg.Shape` is exactly `pg.Raw(sql: sql`text`)`.'), + }); + }); + + it('names no explicit type, and still hovers, when an argument has no literal', () => { + const value = hover('model Product {\n shape pg.Sha|pe(as: huge)\n}')?.contents; + expect(value).toMatchObject({ value: expect.not.stringContaining('is exactly') }); + expect(value).toMatchObject({ value: expect.stringContaining('| huge | `pg.Stamp(...)` |') }); + }); }); diff --git a/packages/2-sql-family/9-family/src/core/psl-build/column-types.ts b/packages/2-sql-family/9-family/src/core/psl-build/column-types.ts index 65677688143c..33e027448e98 100644 --- a/packages/2-sql-family/9-family/src/core/psl-build/column-types.ts +++ b/packages/2-sql-family/9-family/src/core/psl-build/column-types.ts @@ -4,6 +4,7 @@ import { type DataTypeSupport, getAuthoringTypeChoice, getAuthoringTypeConstructor, + writtenTypeArguments, } from '@internal/framework-components/authoring'; import type { DataType } from '@internal/framework-components/codec'; import { resolveReportedSqlType } from '@internal/sql-contract/data-type'; @@ -16,7 +17,6 @@ import { type InferredTypeConstructors, type PslTypeArgument, sameInNormalForm, - writtenTypeArguments, writtenTypeChoiceCall, } from './inferred-type-constructors'; diff --git a/packages/2-sql-family/9-family/src/core/psl-build/default-mapping.ts b/packages/2-sql-family/9-family/src/core/psl-build/default-mapping.ts index 8971971f04dc..324fb95c8418 100644 --- a/packages/2-sql-family/9-family/src/core/psl-build/default-mapping.ts +++ b/packages/2-sql-family/9-family/src/core/psl-build/default-mapping.ts @@ -1,40 +1,12 @@ -/** - * Writing a stored default back as the PSL literal the column takes. - * - * The inverse of reading one. The stored form is read with the same functions a written value - * takes, `readWrittenValue` and `castTypedValue`: a number or numeral text through the target's - * number entry, a document or text through the entry that owns that syntax, each a value its type - * constructs, and the column's data type must be that type or declare a cast from it. The text is - * then printed with that type's authoring entry and read straight back the same way, so a literal - * that would not come back as the stored value never reaches the contract source. ADR 254. - */ - -import type { - ColumnDefault, - ColumnDefaultLiteralInputValue, - JsonValue, -} from '@internal/contract/types'; -import type { - DataTypeAuthoringEntry, - DataTypeSupport, - WrittenScalar, -} from '@internal/framework-components/authoring'; +import type { ColumnDefault } from '@internal/contract/types'; import { - castTypedValue, + type DataTypeSupport, printTaggedLiteral, - readWrittenValue, + writeTypedLiteral, } from '@internal/framework-components/authoring'; -import type { - CodecDescriptor, - DataTypeId, - DataTypeLookup, - DataTypeParams, - DataTypeValue, -} from '@internal/framework-components/codec'; -import { dataTypeId, dataTypeParams, numeralText } from '@internal/framework-components/codec'; -import { escapePslString } from '@internal/sql-contract/data-type-support'; +import type { CodecDescriptor } from '@internal/framework-components/codec'; import { SQL_EXPRESSION_TAG } from '@internal/sql-contract/sql-expression'; -import { isInternalError } from '@internal/utils/internal-error'; +import { ifDefined } from '@internal/utils/defined'; const DEFAULT_FUNCTION_ATTRIBUTES: Readonly> = { 'autoincrement()': '@default(autoincrement())', @@ -71,7 +43,12 @@ export function mapDefault( ): DefaultMappingResult | undefined { switch (columnDefault.kind) { case 'literal': { - const text = writeDefaultLiteral(columnDefault.value, options); + const text = writeTypedLiteral(columnDefault.value, { + dataTypes: options?.dataTypes, + dataType: options?.columnCodec?.dataType, + typeParams: options?.typeParams, + ...ifDefined('list', options?.list), + }); return text === undefined ? undefined : { attribute: `@default(${text})` }; } case 'function': { @@ -83,293 +60,3 @@ export function mapDefault( } } } - -/** - * The authoring entries the printer reads, arranged by what it has to look up: the entry that owns - * each type, and the types each tag writes. - */ -interface WritingSurface { - readonly support: DataTypeSupport; - readonly entryOf: ReadonlyMap; - readonly tagTypes: readonly DataTypeId[]; -} - -function writingSurface(support: DataTypeSupport): WritingSurface { - const entryOf = new Map(); - const tagTypes: DataTypeId[] = []; - for (const [key, entry] of Object.entries(support.entries)) { - const written = entry.written; - if (written.kind === 'tag') { - const type = dataTypeId(key); - if (!entryOf.has(type)) entryOf.set(type, entry); - tagTypes.push(type); - continue; - } - if (written.syntax === 'number') { - for (const type of written.types) entryOf.set(type, entry); - continue; - } - entryOf.set(key, entry); - } - return { support, entryOf, tagTypes }; -} - -function readValue(surface: WritingSurface, written: WrittenScalar): DataTypeValue | undefined { - const read = readWrittenValue(surface.support, written); - return read.ok ? read.value : undefined; -} - -/** The value of a tag's type that a stored document is, constructed by that type. */ -function tagValue( - surface: WritingSurface, - type: DataTypeId, - value: JsonValue, -): DataTypeValue | undefined { - try { - return surface.support.lookup.get(type)?.valueFrom(value, {}); - } catch (error) { - if (isInternalError(error)) throw error; - return undefined; - } -} - -/** - * Every value the stored form could be, in the order a written value would be read: a number or - * numeral text through the number entry first, then the plain syntax that matches the shape, then - * each tag, whose stored form is a whole document, which may itself be a number or a boolean. - */ -function classifications(value: JsonValue, surface: WritingSurface): readonly DataTypeValue[] { - const found: (DataTypeValue | undefined)[] = []; - if (typeof value === 'number') { - found.push(readValue(surface, { kind: 'number', text: numeralText(value) })); - } else if (typeof value === 'boolean') { - found.push(readValue(surface, { kind: 'boolean', value })); - } else if (typeof value === 'string') { - found.push(readValue(surface, { kind: 'number', text: value })); - found.push(readValue(surface, { kind: 'string', text: value })); - } - for (const type of surface.tagTypes) found.push(tagValue(surface, type, value)); - return found.filter((candidate) => candidate !== undefined); -} - -/** The column's data type with the parameters a written value is read under. */ -interface ColumnType { - readonly id: DataTypeId; - readonly params: DataTypeParams; -} - -/** What the column's type stores for `json` under the column's parameters, or `undefined` when it holds no such value. */ -function storedUnder( - json: JsonValue, - column: ColumnType, - lookup: DataTypeLookup, -): JsonValue | undefined { - const type = lookup.get(column.id); - if (type === undefined) return undefined; - try { - return type.toContract(type.valueFrom(json, column.params)); - } catch (error) { - if (isInternalError(error)) throw error; - return undefined; - } -} - -/** The stored form the column takes this value as, spelled as its parameters give it, or `undefined` when its type takes no such value. */ -function admitted( - candidate: DataTypeValue, - column: ColumnType, - surface: WritingSurface, -): JsonValue | undefined { - const cast = castTypedValue(surface.support, column.id, column.params, candidate); - return cast.ok ? cast.value.value : undefined; -} - -/** The text of the literal: what the entry prints, before the syntax that fences it. */ -function printedText(entry: DataTypeAuthoringEntry, value: JsonValue): string | undefined { - try { - return entry.print(value); - } catch { - return undefined; - } -} - -/** The printed text as the entry's syntax writes it, for reading back. */ -function writtenAs(entry: DataTypeAuthoringEntry, text: string): WrittenScalar | undefined { - const written = entry.written; - if (written.kind === 'tag') return { kind: 'tag', tag: written.tag, text }; - if (written.syntax !== 'boolean') return { kind: written.syntax, text }; - return text === 'true' || text === 'false' - ? { kind: 'boolean', value: text === 'true' } - : undefined; -} - -/** What the printed text reads back as, through the same read a written value takes. */ -function readBack( - surface: WritingSurface, - entry: DataTypeAuthoringEntry, - text: string, -): DataTypeValue | undefined { - const written = writtenAs(entry, text); - return written === undefined ? undefined : readValue(surface, written); -} - -function literalText(entry: DataTypeAuthoringEntry, text: string): string { - const written = entry.written; - if (written.kind === 'tag') return printTaggedLiteral(written.tag, text); - return written.syntax === 'string' ? `"${escapePslString(text)}"` : text; -} - -/** One element of a written list: its source text and the value the list cast receives. */ -interface WrittenElement { - readonly text: string; - readonly value: JsonValue; -} - -/** - * The value written as the literal of one of its types, proven by reading the text straight back: - * the same read, the same cast, and the stored form again. A number is tried first without the - * trailing fraction zeros the column's scale spells it with. - */ -function writeScalar( - value: JsonValue, - column: ColumnType, - surface: WritingSurface, -): string | undefined { - const shortest = withoutTrailingFractionZeros(value); - const candidates = [ - ...(shortest === undefined ? [] : classifications(shortest, surface)), - ...classifications(value, surface), - ]; - for (const candidate of candidates) { - const entry = surface.entryOf.get(candidate.type); - if (entry === undefined) continue; - const stored = admitted(candidate, column, surface); - if (stored === undefined || !sameForm(stored, value)) continue; - const text = printedText(entry, candidate.value); - if (text === undefined) continue; - const reread = readBack(surface, entry, text); - if (reread === undefined) continue; - const restored = admitted(reread, column, surface); - if (restored === undefined || !sameForm(restored, value)) continue; - return literalText(entry, text); - } - return undefined; -} - -/** - * `value`, a value of `dataType` as `contract.json` stores it, written as the literal of a type that `dataType` takes, proven to read back as `value`: `255` for `pg/int4`, ``sql`"MyType"[]` `` for `sql/expression`. `undefined` when no literal reads back as it. - */ -export function writeDataTypeValue( - value: JsonValue, - dataType: DataTypeId, - support: DataTypeSupport, -): string | undefined { - return writeScalar(value, { id: dataType, params: {} }, writingSurface(support)); -} - -const DECIMAL_WITH_FRACTION = /^-?\d+\.\d+$/; - -function withoutTrailingFractionZeros(value: JsonValue): string | undefined { - if (typeof value !== 'string' || !DECIMAL_WITH_FRACTION.test(value)) return undefined; - const shortened = value.replace(/0+$/, '').replace(/\.$/, ''); - return shortened === value ? undefined : shortened; -} - -/** One written element of a list, printed as a value of any type, with no column type to admit it. */ -function writeElement( - value: JsonValue, - of: readonly DataTypeId[], - surface: WritingSurface, -): WrittenElement | undefined { - for (const candidate of classifications(value, surface)) { - if (!of.includes(candidate.type)) continue; - const entry = surface.entryOf.get(candidate.type); - if (entry === undefined) continue; - const text = printedText(entry, candidate.value); - if (text === undefined) continue; - const reread = readBack(surface, entry, text); - if (reread === undefined || !of.includes(reread.type)) continue; - return { text: literalText(entry, text), value: reread.value }; - } - return undefined; -} - -/** - * A written list on a scalar column, which the column type's list cast turns into its one value, - * constructed by the column's type. Each element is classified on its own and must be of a type the - * cast takes. - */ -function writeListCast( - value: readonly JsonValue[], - column: ColumnType, - surface: WritingSurface, -): string | undefined { - const listCast = surface.support.lookup.get(column.id)?.listCast; - if (listCast === undefined) return undefined; - const parts: string[] = []; - const elements: JsonValue[] = []; - for (const element of value) { - const written = writeElement(element, listCast.of, surface); - if (written === undefined) return undefined; - parts.push(written.text); - elements.push(written.value); - } - let cast: JsonValue; - try { - cast = listCast.cast(elements); - } catch (error) { - if (isInternalError(error)) throw error; - return undefined; - } - const stored = storedUnder(cast, column, surface.support.lookup); - return stored !== undefined && sameForm(stored, value) ? `[${parts.join(', ')}]` : undefined; -} - -/** The PSL literal a stored value is written as for the column `options` describe, or `undefined` when it has no written form that reads back as that value. */ -export function writeDefaultLiteral( - value: ColumnDefaultLiteralInputValue, - options: DefaultMappingOptions | undefined, -): string | undefined { - if (value instanceof Date) return undefined; - const { dataTypes, columnCodec } = options ?? {}; - if (dataTypes === undefined || columnCodec === undefined) return undefined; - const { lookup } = dataTypes; - const columnDataType = columnCodec.dataType; - const columnType = lookup.get(columnDataType); - const column: ColumnType = { - id: columnDataType, - params: columnType === undefined ? {} : dataTypeParams(columnType, options?.typeParams), - }; - const surface = writingSurface(dataTypes); - if (options?.list === true) { - if (!Array.isArray(value)) return undefined; - const parts: string[] = []; - for (const element of value) { - const written = writeScalar(element, column, surface); - if (written === undefined) return undefined; - parts.push(written); - } - return `[${parts.join(', ')}]`; - } - const written = writeScalar(value, column, surface); - if (written !== undefined) return written; - return Array.isArray(value) ? writeListCast(value, column, surface) : undefined; -} - -/** Whether two stored forms are the same JSON value, member by member and element by element. */ -function sameForm(left: JsonValue, right: JsonValue): boolean { - if (left === right) return true; - if (Array.isArray(left) !== Array.isArray(right)) return false; - if (typeof left !== 'object' || typeof right !== 'object' || left === null || right === null) { - return false; - } - const members = Object.entries(left); - const others = new Map(Object.entries(right)); - return ( - members.length === others.size && - members.every(([key, member]) => { - const other = others.get(key); - return other !== undefined && sameForm(member, other); - }) - ); -} diff --git a/packages/2-sql-family/9-family/src/core/psl-build/inferred-type-constructors.ts b/packages/2-sql-family/9-family/src/core/psl-build/inferred-type-constructors.ts index 0a426006934f..a7bf33fb53de 100644 --- a/packages/2-sql-family/9-family/src/core/psl-build/inferred-type-constructors.ts +++ b/packages/2-sql-family/9-family/src/core/psl-build/inferred-type-constructors.ts @@ -11,6 +11,7 @@ import { getAuthoringTypeChoice, instantiateAuthoringTypeConstructor, isAuthoringTypeChoiceOption, + writeDataTypeValue, } from '@internal/framework-components/authoring'; import type { CodecLookupWithDescriptors, @@ -26,7 +27,6 @@ import { import { blindCast } from '@internal/utils/casts'; import { ifDefined } from '@internal/utils/defined'; import { isInternalError } from '@internal/utils/internal-error'; -import { writeDataTypeValue } from './default-mapping'; /** The type constructor `contract infer` writes for a data type, with the data type it produces. */ export interface InferredTypeConstructor { @@ -90,32 +90,6 @@ export function fewestArgumentsCall( return { path: marked.path, args }; } -/** - * Each argument of a call to `descriptor` as PSL writes it: a value argument as a literal of its data type that reads back as the same value. `undefined` when an argument is not a value argument or no literal reads back as it. - */ -export function writtenTypeArguments( - descriptor: AuthoringTypeConstructorDescriptor, - args: readonly unknown[], - dataTypes: DataTypeSupport, -): readonly string[] | undefined { - const written: string[] = []; - for (const [index, value] of args.entries()) { - const argument = descriptor.args?.[index]; - if (argument === undefined) return undefined; - const text = writeDataTypeValue( - blindCast< - JsonValue, - 'a type constructor argument is a type parameter, which contract.json stores as JSON' - >(value), - dataTypeId(argument.dataType), - dataTypes, - ); - if (text === undefined) return undefined; - written.push(text); - } - return written; -} - /** An argument of a type call as PSL writes it: by name, or by position when `name` is undefined. */ export interface PslTypeArgument { readonly name: string | undefined; diff --git a/packages/2-sql-family/9-family/src/exports/psl-build.ts b/packages/2-sql-family/9-family/src/exports/psl-build.ts index 72bdfd673fe5..16a6d097cd8a 100644 --- a/packages/2-sql-family/9-family/src/exports/psl-build.ts +++ b/packages/2-sql-family/9-family/src/exports/psl-build.ts @@ -15,11 +15,7 @@ export type { DefaultMappingOptions, DefaultMappingResult, } from '../core/psl-build/default-mapping'; -export { - mapDefault, - writeDataTypeValue, - writeDefaultLiteral, -} from '../core/psl-build/default-mapping'; +export { mapDefault } from '../core/psl-build/default-mapping'; export type { GeneratorCallRefusal, GeneratorCallsResult, @@ -35,7 +31,6 @@ export { sameInNormalForm, type WrittenPslTypeCall, type WrittenTypeChoiceCall, - writtenTypeArguments, writtenTypeChoiceCall, } from '../core/psl-build/inferred-type-constructors'; export { toEnumMemberName } from '../core/psl-build/psl-names'; diff --git a/packages/2-sql-family/9-family/test/psl-build/inferred-type-constructors.test.ts b/packages/2-sql-family/9-family/test/psl-build/inferred-type-constructors.test.ts index dbf5d64bd607..e8f624c73122 100644 --- a/packages/2-sql-family/9-family/test/psl-build/inferred-type-constructors.test.ts +++ b/packages/2-sql-family/9-family/test/psl-build/inferred-type-constructors.test.ts @@ -17,7 +17,6 @@ import { import { fewestArgumentsCall, inferredTypeConstructors, - writtenTypeArguments, writtenTypeChoiceCall, } from '../../src/core/psl-build/inferred-type-constructors'; @@ -268,24 +267,6 @@ describe('writtenTypeChoiceCall', () => { }); }); -describe('writtenTypeArguments', () => { - const numericConstructor = authoringTypes.Numeric; - - it('writes each value argument as a literal of its data type', () => { - expect([ - writtenTypeArguments(numericConstructor, [10, 2], dataTypes), - writtenTypeArguments(authoringTypes.ext.Labelled, ['say "hi"'], dataTypes), - ]).toEqual([['10', '2'], ['"say \\"hi\\""']]); - }); - - it('writes nothing for a value no literal of its data type reads back as', () => { - expect([ - writtenTypeArguments(numericConstructor, [-1], dataTypes), - writtenTypeArguments(numericConstructor, [{ precision: 1 }], dataTypes), - ]).toEqual([undefined, undefined]); - }); -}); - describe('inferColumnTypes', () => { const infer = ( tables: readonly IntrospectedTable[], diff --git a/packages/3-targets/postgres/1-target/src/core/psl-print/column-types.ts b/packages/3-targets/postgres/1-target/src/core/psl-print/column-types.ts index 7bccf3021842..a2f1d37c5797 100644 --- a/packages/3-targets/postgres/1-target/src/core/psl-print/column-types.ts +++ b/packages/3-targets/postgres/1-target/src/core/psl-print/column-types.ts @@ -1,7 +1,6 @@ import { type InferredTypeConstructors, type PslTypeArgument, - writtenTypeArguments, writtenTypeChoiceCall, } from '@internal/family-sql/psl-build'; import { @@ -11,6 +10,7 @@ import { type DataTypeSupport, findAuthoringTypeConstructorCall, getAuthoringTypeConstructor, + writtenTypeArguments, } from '@internal/framework-components/authoring'; import type { DataTypeLookup } from '@internal/framework-components/codec'; import { dataTypeParams } from '@internal/framework-components/codec'; diff --git a/packages/3-targets/postgres/1-target/src/core/psl-print/enum-blocks.ts b/packages/3-targets/postgres/1-target/src/core/psl-print/enum-blocks.ts index 36726a1fd243..6cb4d9bcb16c 100644 --- a/packages/3-targets/postgres/1-target/src/core/psl-print/enum-blocks.ts +++ b/packages/3-targets/postgres/1-target/src/core/psl-print/enum-blocks.ts @@ -1,6 +1,6 @@ import type { ContractEnum } from '@internal/contract/types'; import type { SqlPslBuildContext } from '@internal/family-sql/control'; -import { writeDefaultLiteral } from '@internal/family-sql/psl-build'; +import { writeTypedLiteral } from '@internal/framework-components/authoring'; import type { PslExtensionBlock, PslExtensionBlockPrintEntry, @@ -31,9 +31,9 @@ export function buildDomainEnumBlocks( const parameters = Object.fromEntries( domainEnum.members.map((member): [string, PslExtensionBlockPrintEntry] => { refuseUnwritableName('enum member', member.name); - const written = writeDefaultLiteral(member.value, { + const written = writeTypedLiteral(member.value, { dataTypes: context.dataTypes, - columnCodec: context.codecLookup.descriptorFor(domainEnum.codecId), + dataType: context.codecLookup.descriptorFor(domainEnum.codecId)?.dataType, }); if (written === undefined) { refuseUnwritableEnumMember({ enumName: name, member: member.name, value: member.value }); From 218167c277631163e6b1ee17c9b67261abcff92c Mon Sep 17 00:00:00 2001 From: willbot Date: Wed, 28 Oct 2026 14:50:48 +0100 Subject: [PATCH 112/118] Removed-preset refusals write the convenience call that stores the preset's type ADR 279 says the tools write the convenience call wherever one gives a column's explicit type. The refusals for removed date and time presets named the explicit type instead: dateTime() said to write TimestamptzTemporal, which contract print then rewrites as DateTime(as: temporal). Each date and time row now writes the convenience call where one is exact: DateTime(as: temporal) for dateTime(), temporal.createdAt() and temporal.timestamptz(); DateTime(as: string) and DateTime(as: string, storage: timestamp) for the string presets; Date(as: temporal), Date, Time(as: temporal), Time and Time(storage: timetz) for the date and time presets. TypeScript writes type.DateTime({ as: 'temporal' }) and so on. A precision is written by name, as precision: 3, since a type choice takes it after its options. The JavaScript Date presets keep TimestamptzJsDate, because DateTime would add precision 3 the preset did not store. A row now names its type as a path and options, and applies where that call resolves, through the type choice resolution a column uses, to the row's codec. The parity tests take each rewrite from the refusal and check it stores what the preset stored. Co-Authored-By: Claude Opus 5.5 Signed-off-by: willbot Signed-off-by: Will Madden --- .../psl-parser/src/contributed-type-scope.ts | 13 +- .../psl-parser/src/exports/interpret.ts | 8 +- .../psl-parser/src/removed-field-presets.ts | 74 ++++++-- .../test/removed-field-presets.test.ts | 36 +++- .../contract-psl/src/removed-field-presets.ts | 70 +++++--- .../interpreter.removed-field-presets.test.ts | 103 ++++++++--- .../contract-ts/src/removed-field-presets.ts | 167 ++++++++++++------ .../test/removed-field-presets.test.ts | 55 +++++- .../typescript-preset-refusals.test.ts | 17 +- 9 files changed, 406 insertions(+), 137 deletions(-) diff --git a/packages/1-framework/2-authoring/psl-parser/src/contributed-type-scope.ts b/packages/1-framework/2-authoring/psl-parser/src/contributed-type-scope.ts index 4b104b1b4496..07a80f97f4d4 100644 --- a/packages/1-framework/2-authoring/psl-parser/src/contributed-type-scope.ts +++ b/packages/1-framework/2-authoring/psl-parser/src/contributed-type-scope.ts @@ -132,28 +132,29 @@ export function contributedTypeAt( return contributedTypeAt(value, rest); } -/** The codec a column typed by the type at `path` in `namespace`, written bare, is stored with, or `undefined` when none is contributed there or it is a type choice whose defaults choose no type. */ -export function bareContributedTypeCodecId( +/** The codec a column typed by `call` is stored with, reading the call's arguments as the column does, or `undefined` when no type is contributed at its path or it is a type choice whose arguments choose no type. */ +export function writtenContributedTypeCodecId( namespace: ContributedTypeNamespace, - path: readonly string[], + call: ResolvedTypeConstructorCall, ): string | undefined { - return codecIdAtPath(namespace, path, []); + return codecIdAtPath(namespace, call.path, [], call); } function codecIdAtPath( namespace: ContributedTypeNamespace, path: readonly string[], prefix: readonly string[], + call: ResolvedTypeConstructorCall, ): string | undefined { const [name, ...rest] = path; if (name === undefined || !Object.hasOwn(namespace, name)) return undefined; const value = namespace[name]; if (value === undefined) return undefined; const at = [...prefix, name]; - if (!isContributedTypeDescriptor(value)) return codecIdAtPath(value, rest, at); + if (!isContributedTypeDescriptor(value)) return codecIdAtPath(value, rest, at, call); if (rest.length > 0) return undefined; return contributedTypeCodecId( - { symbol: contributedTypeSymbol(name, at, value, namespace), call: undefined }, + { symbol: contributedTypeSymbol(name, at, value, namespace), call }, EMPTY_DATA_TYPES, ); } diff --git a/packages/1-framework/2-authoring/psl-parser/src/exports/interpret.ts b/packages/1-framework/2-authoring/psl-parser/src/exports/interpret.ts index be3b1881ff4e..c615cc7d34a4 100644 --- a/packages/1-framework/2-authoring/psl-parser/src/exports/interpret.ts +++ b/packages/1-framework/2-authoring/psl-parser/src/exports/interpret.ts @@ -8,11 +8,17 @@ export { fkRelationPairKey, requiredOneToOneBackrelationDiagnostic, } from '../relation-backrelations'; -export type { RemovedFieldPreset, RemovedFieldPresets } from '../removed-field-presets'; +export type { + RemovedFieldPreset, + RemovedFieldPresets, + RemovedPresetType, +} from '../removed-field-presets'; export { describeRemovedFieldPreset, + presetType, removedPresetWithPhases, removedPresetWrittenAs, + writtenPresetType, } from '../removed-field-presets'; export { argumentIndexOf, diff --git a/packages/1-framework/2-authoring/psl-parser/src/removed-field-presets.ts b/packages/1-framework/2-authoring/psl-parser/src/removed-field-presets.ts index a98341911069..0a83eed98514 100644 --- a/packages/1-framework/2-authoring/psl-parser/src/removed-field-presets.ts +++ b/packages/1-framework/2-authoring/psl-parser/src/removed-field-presets.ts @@ -1,15 +1,39 @@ import { argumentsByParameter, printTypeConstructorCall } from './call-arguments'; import { - bareContributedTypeCodecId, type ContributedTypeNamespace, + writtenContributedTypeCodecId, } from './contributed-type-scope'; import type { ResolvedTypeConstructorCall } from './resolve'; +/** A type as a removed preset's rewrite writes it: its path, and the options it is called with, each written as a bare name, such as `as: temporal`. */ +export interface RemovedPresetType { + readonly path: readonly string[]; + readonly options: Readonly>; +} + +/** The type at the dotted `path`, called with `options`. */ +export function presetType( + path: string, + options: Readonly> = {}, +): RemovedPresetType { + return { path: path.split('.'), options }; +} + +/** The type as PSL writes it, with `extra` after its options, such as `precision: 3`. */ +export function writtenPresetType(type: RemovedPresetType, extra?: string): string { + const args = [ + ...Object.entries(type.options).map(([name, value]) => `${name}: ${value}`), + ...(extra === undefined ? [] : [extra]), + ]; + const path = type.path.join('.'); + return args.length === 0 ? path : `${path}(${args.join(', ')})`; +} + /** * A field preset Prisma 8 removed, and how to write what it stored. The preset is named only where `type` resolves, and to `codecId` when one is given, so a row does not fire on a target the preset never existed on. */ export interface RemovedFieldPreset { - readonly type: readonly string[]; + readonly type: RemovedPresetType; readonly codecId?: string; readonly rewrite: (call: ResolvedTypeConstructorCall) => string; } @@ -19,34 +43,40 @@ export type RemovedFieldPresets = Readonly `Write ${written}.`, + rewrite: () => `Write ${writtenPresetType(written)}${suffix}.`, }, ]; } /** - * The row of a preset that took `onCreate: now` and `onUpdate: now`, and a precision first when `takesPrecision`. Its rewrite writes `type`, with the precision as its argument and `@onCreate(now())` and `@onUpdate(now())` for the phases the call set. + * The row of a preset that took `onCreate: now` and `onUpdate: now`, and a precision first when `takesPrecision`. Its rewrite writes `type`, with the precision as its `precision` argument and `@onCreate(now())` and `@onUpdate(now())` for the phases the call set. */ export function removedPresetWithPhases( - type: string, + type: RemovedPresetType | string, codecId: string, takesPrecision: boolean, ): readonly RemovedFieldPreset[] { + const written = asType(type); const parameters = takesPrecision ? ['precision', ...PHASES] : [...PHASES]; - const fallback = `Write ${type}${takesPrecision ? '(...)' : ''} with @onCreate(now()) and @onUpdate(now()) for the phases the preset set.`; return [ { - type: [type], + type: written, codecId, rewrite: (call) => { const args = argumentsByParameter(call.args, parameters); @@ -56,19 +86,20 @@ export function removedPresetWithPhases( (precision !== undefined && !/^\d+$/.test(precision)) || PHASES.some((phase) => args.has(phase) && args.get(phase) !== 'now') ) { - return fallback; + return `Write ${writtenPresetType(written, takesPrecision ? '...' : undefined)} with @onCreate(now()) and @onUpdate(now()) for the phases the preset set.`; } const attributes = PHASES.filter((phase) => args.has(phase)).map( (phase) => ` @${phase}(now())`, ); - return `Write ${type}${precision === undefined ? '' : `(${precision})`}${attributes.join('')}.`; + const extra = precision === undefined ? undefined : `precision: ${precision}`; + return `Write ${writtenPresetType(written, extra)}${attributes.join('')}.`; }, }, ]; } /** - * The refusal of a call to a field preset Prisma 8 removed, naming what to write instead, or `undefined` when `presets` has no row for the call that applies to a stack whose types `types` holds. A row is skipped when `isDeclared` says the schema declares the first name of its type, because the rewrite would then name the schema's declaration. + * The refusal of a call to a field preset Prisma 8 removed, naming what to write instead, or `undefined` when `presets` has no row for the call that applies to a stack whose types `types` holds. A row applies where its type, called as the rewrite writes it, resolves as a column would, to the row's codec. A row is skipped when `isDeclared` says the schema declares the first name of its type, because the rewrite would then name the schema's declaration. */ export function describeRemovedFieldPreset( presets: RemovedFieldPresets, @@ -79,8 +110,17 @@ export function describeRemovedFieldPreset( const path = call.path.join('.'); const rows = Object.hasOwn(presets, path) ? presets[path] : undefined; const preset = rows?.find((row) => { - const [name] = row.type; - const codecId = bareContributedTypeCodecId(types, row.type); + const [name] = row.type.path; + const codecId = writtenContributedTypeCodecId(types, { + path: row.type.path, + args: Object.entries(row.type.options).map(([option, value]) => ({ + kind: 'named', + name: option, + value, + span: call.span, + })), + span: call.span, + }); return ( codecId !== undefined && (name === undefined || !isDeclared(name)) && diff --git a/packages/1-framework/2-authoring/psl-parser/test/removed-field-presets.test.ts b/packages/1-framework/2-authoring/psl-parser/test/removed-field-presets.test.ts index ddaf56282cd1..fdbc2cee7a03 100644 --- a/packages/1-framework/2-authoring/psl-parser/test/removed-field-presets.test.ts +++ b/packages/1-framework/2-authoring/psl-parser/test/removed-field-presets.test.ts @@ -3,6 +3,7 @@ import type { ContributedTypeNamespace } from '../src/contributed-type-scope'; import { parse } from '../src/parse'; import { describeRemovedFieldPreset, + presetType, removedPresetWithPhases, removedPresetWrittenAs, } from '../src/removed-field-presets'; @@ -23,13 +24,25 @@ function callOf(type: string): ResolvedTypeConstructorCall { } const stamp = { kind: 'typeConstructor', output: { codecId: 'test/stamp@1' } } as const; +const text = { kind: 'typeConstructor', output: { codecId: 'test/text@1' } } as const; +const moment = { + kind: 'typeChoice', + args: [ + { kind: 'option', name: 'as', values: ['stamp', 'text'], default: 'text', optional: true }, + ], + choices: [ + { when: { as: 'stamp' }, type: 'Stamp' }, + { when: { as: 'text' }, type: 'Text' }, + ], +} as const; const presets = { 'ns.stamp': removedPresetWithPhases('Stamp', 'test/stamp@1', true), - plain: removedPresetWrittenAs('Stamp @id', 'test/stamp@1'), + plain: removedPresetWrittenAs('Stamp', 'test/stamp@1', '@id'), either: [ ...removedPresetWrittenAs('Stamp', 'test/stamp@1'), ...removedPresetWrittenAs('ns.Stamp', 'test/stamp@1'), ], + chosen: removedPresetWithPhases(presetType('Moment', { as: 'stamp' }), 'test/stamp@1', true), }; function describeCall( @@ -43,13 +56,32 @@ function describeCall( describe('describeRemovedFieldPreset', () => { it('names the call and its rewrite', () => { expect(describeCall(callOf('ns.stamp(3, onUpdate: now)'), { Stamp: stamp })).toBe( - 'ns.stamp(3, onUpdate: now) was a field preset, and Prisma 8 removed field presets. Write Stamp(3) @onUpdate(now()).', + 'ns.stamp(3, onUpdate: now) was a field preset, and Prisma 8 removed field presets. Write Stamp(precision: 3) @onUpdate(now()).', ); expect(describeCall(callOf('plain()'), { Stamp: stamp })).toBe( 'plain() was a field preset, and Prisma 8 removed field presets. Write Stamp @id.', ); }); + it('names a type choice called with options where that call resolves to the codec', () => { + const types = { Moment: moment, Stamp: stamp, Text: text }; + expect([ + describeCall(callOf('chosen(3, onCreate: now)'), types), + describeCall(callOf('chosen(onCreate: later)'), types), + ]).toEqual([ + 'chosen(3, onCreate: now) was a field preset, and Prisma 8 removed field presets. Write Moment(as: stamp, precision: 3) @onCreate(now()).', + 'chosen(onCreate: later) was a field preset, and Prisma 8 removed field presets. Write Moment(as: stamp, ...) with @onCreate(now()) and @onUpdate(now()) for the phases the preset set.', + ]); + }); + + it('names nothing where the type choice call resolves to another codec', () => { + const textOnly = { + ...moment, + choices: [{ when: { as: 'stamp' }, type: 'Text' }], + } as const; + expect(describeCall(callOf('chosen()'), { Moment: textOnly, Text: text })).toBeUndefined(); + }); + it('names the rewrite of the first row that applies to the stack', () => { expect( describeCall(callOf('either()'), { diff --git a/packages/2-sql-family/2-authoring/contract-psl/src/removed-field-presets.ts b/packages/2-sql-family/2-authoring/contract-psl/src/removed-field-presets.ts index 38ecbdbd99b8..0635d92bdb61 100644 --- a/packages/2-sql-family/2-authoring/contract-psl/src/removed-field-presets.ts +++ b/packages/2-sql-family/2-authoring/contract-psl/src/removed-field-presets.ts @@ -1,5 +1,6 @@ import type { ResolvedTypeConstructorCall } from '@internal/psl-parser'; import { + presetType, type RemovedFieldPreset, type RemovedFieldPresets, removedPresetWithPhases, @@ -33,8 +34,9 @@ function idAlias( ): readonly RemovedFieldPreset[] { return GENERATED_ID_COLUMN_ALIAS_NAMESPACES.flatMap((namespace) => removedPresetWrittenAs( - `${qualified(namespace, alias)}${attributes === undefined ? '' : ` ${attributes}`}`, + qualified(namespace, alias), generatedIdColumnAliases[alias].output.type.codecId, + attributes, ), ); } @@ -47,7 +49,7 @@ function nanoid(id: boolean): readonly RemovedFieldPreset[] { return id ? `${column} @id @onCreate(nanoid(${size ?? ''}))` : column; }; return { - type: [...namespace, 'Nanoid'], + type: presetType(qualified(namespace, 'Nanoid')), codecId: generatedIdColumnAliases.Nanoid.output.type.codecId, rewrite: (call) => { const options = nanoidSize(call); @@ -59,7 +61,12 @@ function nanoid(id: boolean): readonly RemovedFieldPreset[] { }); } -/** The SQL family's removed field presets: each row applies only where its rewrite's type stores the preset's codec, and a row with no codec id writes a type alias only the targets with the preset contribute. A generated id preset is written with the target's top-level alias where it has one, and with the `sql.` alias elsewhere. */ +const TEMPORAL_DATE_TIME = presetType('DateTime', { as: 'temporal' }); +const STRING_DATE_TIME = presetType('DateTime', { as: 'string' }); +const CREATED = '@onCreate(now())'; +const UPDATED = '@onCreate(now()) @onUpdate(now())'; + +/** The SQL family's removed field presets: each row applies only where its rewrite's type stores the preset's codec, and a row with no codec id writes a type alias only the targets with the preset contribute. A generated id preset is written with the target's top-level alias where it has one, and with the `sql.` alias elsewhere. A date and time preset is written with the convenience call that gives the explicit type it stored, where one does (ADR 279). */ export const removedSqlFieldPresets: RemovedFieldPresets = { uuidString: idAlias('UuidString'), ulid: idAlias('Ulid'), @@ -80,36 +87,37 @@ export const removedSqlFieldPresets: RemovedFieldPresets = { boolean: removedPresetWrittenAs('Boolean', 'pg/bool@1'), json: removedPresetWrittenAs('Jsonb', 'pg/jsonb@1'), bytes: removedPresetWrittenAs('Bytes', 'pg/bytea@1'), - dateTime: removedPresetWrittenAs('TimestamptzTemporal', 'pg/timestamptz-temporal@1'), + dateTime: removedPresetWrittenAs(TEMPORAL_DATE_TIME, 'pg/timestamptz-temporal@1'), uuidNative: removedPresetWrittenAs('Uuid', 'pg/uuid@1'), - 'id.uuidv4Native': removedPresetWrittenAs('Uuid @id @onCreate(uuid())', 'pg/uuid@1'), - 'id.uuidv7Native': removedPresetWrittenAs('Uuid @id @onCreate(uuid(7))', 'pg/uuid@1'), + 'id.uuidv4Native': removedPresetWrittenAs('Uuid', 'pg/uuid@1', '@id @onCreate(uuid())'), + 'id.uuidv7Native': removedPresetWrittenAs('Uuid', 'pg/uuid@1', '@id @onCreate(uuid(7))'), 'temporal.createdAt': [ - ...removedPresetWrittenAs('TimestamptzTemporal @onCreate(now())', 'pg/timestamptz-temporal@1'), - ...removedPresetWrittenAs('temporal.CreatedAt'), + ...removedPresetWrittenAs(TEMPORAL_DATE_TIME, 'pg/timestamptz-temporal@1', CREATED), + ...removedPresetWrittenAs('temporal.CreatedAt', undefined), ], 'temporal.updatedAt': [ - ...removedPresetWrittenAs( - 'TimestamptzTemporal @onCreate(now()) @onUpdate(now())', - 'pg/timestamptz-temporal@1', - ), - ...removedPresetWrittenAs('temporal.UpdatedAt'), + ...removedPresetWrittenAs(TEMPORAL_DATE_TIME, 'pg/timestamptz-temporal@1', UPDATED), + ...removedPresetWrittenAs('temporal.UpdatedAt', undefined), ], 'temporal.createdAtString': removedPresetWrittenAs( - 'TimestamptzString @onCreate(now())', + STRING_DATE_TIME, 'pg/timestamptz-string@1', + CREATED, ), 'temporal.updatedAtString': removedPresetWrittenAs( - 'TimestamptzString @onCreate(now()) @onUpdate(now())', + STRING_DATE_TIME, 'pg/timestamptz-string@1', + UPDATED, ), 'temporal.createdAtJsDate': removedPresetWrittenAs( - 'TimestamptzJsDate @onCreate(now())', + 'TimestamptzJsDate', 'pg/timestamptz-date@1', + CREATED, ), 'temporal.updatedAtJsDate': removedPresetWrittenAs( - 'TimestamptzJsDate @onCreate(now()) @onUpdate(now())', + 'TimestamptzJsDate', 'pg/timestamptz-date@1', + UPDATED, ), 'temporal.timestamp': removedPresetWithPhases( 'TimestampTemporal', @@ -117,17 +125,17 @@ export const removedSqlFieldPresets: RemovedFieldPresets = { true, ), 'temporal.timestamptz': removedPresetWithPhases( - 'TimestamptzTemporal', + TEMPORAL_DATE_TIME, 'pg/timestamptz-temporal@1', true, ), 'temporal.timestampString': removedPresetWithPhases( - 'TimestampString', + presetType('DateTime', { as: 'string', storage: 'timestamp' }), 'pg/timestamp-string@1', true, ), 'temporal.timestamptzString': removedPresetWithPhases( - 'TimestamptzString', + STRING_DATE_TIME, 'pg/timestamptz-string@1', true, ), @@ -136,10 +144,22 @@ export const removedSqlFieldPresets: RemovedFieldPresets = { 'pg/timestamptz-date@1', true, ), - 'temporal.date': removedPresetWithPhases('DateTemporal', 'pg/date-temporal@1', false), - 'temporal.dateString': removedPresetWithPhases('DateString', 'pg/date-string@1', false), - 'temporal.time': removedPresetWithPhases('TimeTemporal', 'pg/time-temporal@1', true), - 'temporal.timeString': removedPresetWithPhases('TimeString', 'pg/time-string@1', true), - 'temporal.timetz': removedPresetWithPhases('Timetz', 'pg/timetz@1', true), + 'temporal.date': removedPresetWithPhases( + presetType('Date', { as: 'temporal' }), + 'pg/date-temporal@1', + false, + ), + 'temporal.dateString': removedPresetWithPhases('Date', 'pg/date-string@1', false), + 'temporal.time': removedPresetWithPhases( + presetType('Time', { as: 'temporal' }), + 'pg/time-temporal@1', + true, + ), + 'temporal.timeString': removedPresetWithPhases('Time', 'pg/time-string@1', true), + 'temporal.timetz': removedPresetWithPhases( + presetType('Time', { storage: 'timetz' }), + 'pg/timetz@1', + true, + ), 'temporal.datetime': removedPresetWithPhases('DateTime', 'sqlite/datetime@1', false), }; diff --git a/packages/2-sql-family/2-authoring/contract-psl/test/interpreter.removed-field-presets.test.ts b/packages/2-sql-family/2-authoring/contract-psl/test/interpreter.removed-field-presets.test.ts index 6301bafbec38..2385af2b627b 100644 --- a/packages/2-sql-family/2-authoring/contract-psl/test/interpreter.removed-field-presets.test.ts +++ b/packages/2-sql-family/2-authoring/contract-psl/test/interpreter.removed-field-presets.test.ts @@ -50,8 +50,58 @@ function temporalAliases(codecId: string) { } as const; } +const precision = { + kind: 'value', + dataType: 'pg/int4', + name: 'precision', + optional: true, +} as const; + +function option(name: string, values: readonly [string, ...string[]]) { + return { kind: 'option', name, values, default: values[0], optional: true } as const; +} + +const convenienceTypes = { + DateTime: { + kind: 'typeChoice', + args: [ + option('as', ['jsDate', 'temporal', 'string']), + option('storage', ['timestamptz', 'timestamp']), + precision, + ], + choices: [ + { + when: { as: 'jsDate', storage: 'timestamptz' }, + type: 'TimestamptzJsDate', + defaults: { precision: 3 }, + }, + { when: { as: 'temporal', storage: 'timestamptz' }, type: 'TimestamptzTemporal' }, + { when: { as: 'string', storage: 'timestamptz' }, type: 'TimestamptzString' }, + { when: { as: 'string', storage: 'timestamp' }, type: 'TimestampString' }, + ], + }, + Date: { + kind: 'typeChoice', + args: [option('as', ['string', 'temporal'])], + choices: [ + { when: { as: 'string' }, type: 'DateString' }, + { when: { as: 'temporal' }, type: 'DateTemporal' }, + ], + }, + Time: { + kind: 'typeChoice', + args: [option('as', ['string', 'temporal']), option('storage', ['time', 'timetz']), precision], + choices: [ + { when: { as: 'string', storage: 'time' }, type: 'TimeString' }, + { when: { as: 'temporal', storage: 'time' }, type: 'TimeTemporal' }, + { when: { as: 'string', storage: 'timetz' }, type: 'Timetz' }, + ], + }, +} as const satisfies AuthoringTypeNamespace; + const postgresTypes: AuthoringTypeNamespace = { ...postgresScalarAuthoringTypes, + ...convenienceTypes, Char: char, ...generatedIdAliases, DateString: { kind: 'typeConstructor', output: { codecId: 'pg/date-string@1' } }, @@ -138,53 +188,62 @@ describe('a removed field preset in a SQL schema', () => { ['boolean()', 'Write Boolean.'], ['json()', 'Write Jsonb.'], ['bytes()', 'Write Bytes.'], - ['dateTime()', 'Write TimestamptzTemporal.'], + ['dateTime()', 'Write DateTime(as: temporal).'], ['uuidNative()', 'Write Uuid.'], ['id.uuidv4Native()', 'Write Uuid @id @onCreate(uuid()).'], ['id.uuidv7Native()', 'Write Uuid @id @onCreate(uuid(7)).'], - ['temporal.createdAt()', 'Write TimestamptzTemporal @onCreate(now()).'], - ['temporal.updatedAt()', 'Write TimestamptzTemporal @onCreate(now()) @onUpdate(now()).'], - ['temporal.createdAtString()', 'Write TimestamptzString @onCreate(now()).'], - ['temporal.updatedAtString()', 'Write TimestamptzString @onCreate(now()) @onUpdate(now()).'], + ['temporal.createdAt()', 'Write DateTime(as: temporal) @onCreate(now()).'], + ['temporal.updatedAt()', 'Write DateTime(as: temporal) @onCreate(now()) @onUpdate(now()).'], + ['temporal.createdAtString()', 'Write DateTime(as: string) @onCreate(now()).'], + [ + 'temporal.updatedAtString()', + 'Write DateTime(as: string) @onCreate(now()) @onUpdate(now()).', + ], ['temporal.createdAtJsDate()', 'Write TimestamptzJsDate @onCreate(now()).'], ['temporal.updatedAtJsDate()', 'Write TimestamptzJsDate @onCreate(now()) @onUpdate(now()).'], [ 'temporal.timestamp(3, onCreate: now, onUpdate: now)', - 'Write TimestampTemporal(3) @onCreate(now()) @onUpdate(now()).', + 'Write TimestampTemporal(precision: 3) @onCreate(now()) @onUpdate(now()).', ], [ 'temporal.timestamp(precision: 6, onCreate: now)', - 'Write TimestampTemporal(6) @onCreate(now()).', + 'Write TimestampTemporal(precision: 6) @onCreate(now()).', ], [ 'temporal.timestamp(3, now, now)', - 'Write TimestampTemporal(3) @onCreate(now()) @onUpdate(now()).', + 'Write TimestampTemporal(precision: 3) @onCreate(now()) @onUpdate(now()).', + ], + ['temporal.timestamptz()', 'Write DateTime(as: temporal).'], + ['temporal.timestamptz(onUpdate: now)', 'Write DateTime(as: temporal) @onUpdate(now()).'], + [ + 'temporal.timestampString(6, onUpdate: now)', + 'Write DateTime(as: string, storage: timestamp, precision: 6) @onUpdate(now()).', ], - ['temporal.timestamptz()', 'Write TimestamptzTemporal.'], - ['temporal.timestamptz(onUpdate: now)', 'Write TimestamptzTemporal @onUpdate(now()).'], - ['temporal.timestampString(6, onUpdate: now)', 'Write TimestampString(6) @onUpdate(now()).'], [ 'temporal.timestamptzString(3, onCreate: now, onUpdate: now)', - 'Write TimestamptzString(3) @onCreate(now()) @onUpdate(now()).', + 'Write DateTime(as: string, precision: 3) @onCreate(now()) @onUpdate(now()).', ], [ 'temporal.timestamptzJsDate(3, onCreate: now)', - 'Write TimestamptzJsDate(3) @onCreate(now()).', + 'Write TimestamptzJsDate(precision: 3) @onCreate(now()).', ], - ['temporal.date(onCreate: now)', 'Write DateTemporal @onCreate(now()).'], + ['temporal.date(onCreate: now)', 'Write Date(as: temporal) @onCreate(now()).'], [ 'temporal.dateString(onCreate: now, onUpdate: now)', - 'Write DateString @onCreate(now()) @onUpdate(now()).', + 'Write Date @onCreate(now()) @onUpdate(now()).', + ], + [ + 'temporal.time(3, onUpdate: now)', + 'Write Time(as: temporal, precision: 3) @onUpdate(now()).', ], - ['temporal.time(3, onUpdate: now)', 'Write TimeTemporal(3) @onUpdate(now()).'], [ 'temporal.time(3, onUpdate: now, now)', - 'Write TimeTemporal(3) @onCreate(now()) @onUpdate(now()).', + 'Write Time(as: temporal, precision: 3) @onCreate(now()) @onUpdate(now()).', ], - ['temporal.timeString(3, onCreate: now)', 'Write TimeString(3) @onCreate(now()).'], + ['temporal.timeString(3, onCreate: now)', 'Write Time(precision: 3) @onCreate(now()).'], [ 'temporal.timetz(6, onCreate: now, onUpdate: now)', - 'Write Timetz(6) @onCreate(now()) @onUpdate(now()).', + 'Write Time(storage: timetz, precision: 6) @onCreate(now()) @onUpdate(now()).', ], ])('refuses %s with its rewrite', (call, rewrite) => { expect(diagnosticsOf(call, postgresInput)).toEqual(removed(call, rewrite)); @@ -196,8 +255,8 @@ describe('a removed field preset in a SQL schema', () => { ['temporal.timestamp(3, precision: 4)', 'TimestampTemporal(...)'], ['temporal.timestamp(frequency: now)', 'TimestampTemporal(...)'], ['temporal.timestamp(-1)', 'TimestampTemporal(...)'], - ['temporal.time(onCreate: now, 3, now)', 'TimeTemporal(...)'], - ['temporal.date(3)', 'DateTemporal'], + ['temporal.time(onCreate: now, 3, now)', 'Time(as: temporal, ...)'], + ['temporal.date(3)', 'Date(as: temporal)'], ])( 'names the rewrite without the arguments of %s, which the preset did not take', (call, type) => { @@ -251,7 +310,7 @@ describe('a removed field preset in a SQL schema', () => { it('refuses each preset field once, keeping the field modifiers out of the call', () => { expect(diagnosticsOf('temporal.createdAt()?', postgresInput)).toEqual( - removed('temporal.createdAt()', 'Write TimestamptzTemporal @onCreate(now()).'), + removed('temporal.createdAt()', 'Write DateTime(as: temporal) @onCreate(now()).'), ); }); diff --git a/packages/2-sql-family/2-authoring/contract-ts/src/removed-field-presets.ts b/packages/2-sql-family/2-authoring/contract-ts/src/removed-field-presets.ts index e3339107c072..cbbebbb3a77c 100644 --- a/packages/2-sql-family/2-authoring/contract-ts/src/removed-field-presets.ts +++ b/packages/2-sql-family/2-authoring/contract-ts/src/removed-field-presets.ts @@ -11,21 +11,51 @@ import { type GeneratedIdColumnAlias, generatedIdColumnAliases, } from '@internal/sql-contract/generated-id-column-aliases'; +import { isInternalError } from '@internal/utils/internal-error'; import { contractError } from './contract-errors'; const GUIDE = 'The upgrade guide "Field presets are removed" lists every rewrite.'; -/** A removed field preset: its rewrite, named only where the type the rewrite writes resolves, and to `codecId` when one is given, so a row does not fire on a target the preset never existed on. */ +/** The type a rewrite writes: its path, and for a type choice the options it is called with, such as `{ as: 'temporal' }`. */ +interface RewriteType { + readonly path: readonly string[]; + readonly options: Readonly> | undefined; +} + +/** A removed field preset: its rewrite, named only where `type` resolves, and to `codecId` when one is given, so a row does not fire on a target the preset never existed on. */ interface RemovedFieldPreset { - readonly type: readonly string[]; + readonly type: RewriteType; readonly codecId: string | undefined; readonly rewrite: string; } -/** The row of a preset whose rewrite is `rewrite`; its type is the first `type.X(` that `rewrite` writes. */ -function writtenAs(rewrite: string, codecId?: string): readonly RemovedFieldPreset[] { - const type = /type\.([\w.]+)\(/.exec(rewrite)?.[1] ?? ''; - return [{ type: type.split('.'), codecId, rewrite }]; +/** The explicit type constructor at the dotted `path`. */ +const explicit = (path: string): RewriteType => ({ path: path.split('.'), options: undefined }); + +/** The type choice at `path`, called with `options`. */ +const choice = (path: string, options: Readonly> = {}): RewriteType => ({ + path: path.split('.'), + options, +}); + +/** The call to `type` as TypeScript writes it, with `precision` when given: one object of arguments for a type choice, and a positional argument for an explicit type. */ +function typeCall({ path, options }: RewriteType, precision?: string): string { + const name = `type.${path.join('.')}`; + if (options === undefined) return `${name}(${precision ?? ''})`; + const members = [ + ...Object.entries(options).map(([option, value]) => `${option}: '${value}'`), + ...(precision === undefined ? [] : [`precision: ${precision}`]), + ]; + return members.length === 0 ? `${name}()` : `${name}({ ${members.join(', ')} })`; +} + +/** The row of a preset whose rewrite is the column of `type`, then `chain`. */ +function writtenAs( + type: RewriteType, + codecId: string | undefined, + chain = '', +): readonly RemovedFieldPreset[] { + return [{ type, codecId, rewrite: `field.column(${typeCall(type)})${chain}` }]; } /** The `type.` prefix of a namespace, such as `sql.` for `['sql']`. */ @@ -37,7 +67,7 @@ function idAlias( write: (prefix: string) => string, ): readonly RemovedFieldPreset[] { return GENERATED_ID_COLUMN_ALIAS_NAMESPACES.map((namespace) => ({ - type: [...namespace, alias], + type: { path: [...namespace, alias], options: undefined }, codecId: generatedIdColumnAliases[alias].output.type.codecId, rewrite: write(prefixOf(namespace)), })); @@ -50,12 +80,20 @@ const aliasColumn = (alias: GeneratedIdColumnAlias) => idAlias(alias, (prefix) => column(prefix, `${alias}()`)); const aliasId = (alias: GeneratedIdColumnAlias, generator: string) => idAlias(alias, (prefix) => id(column(prefix, `${alias}()`), generator)); -const withPhases = (type: string, codecId: string, precision: boolean) => - writtenAs( - `field.column(type.${type}(${precision ? 'p' : ''})), with .onCreate(gen.now()) and .onUpdate(gen.now()) for the phases the preset set${precision ? ', and without p when it set no precision' : ''}`, +const withPhases = (type: RewriteType, codecId: string, precision: boolean) => [ + { + type, codecId, - ); + rewrite: `field.column(${typeCall(type, precision ? 'p' : undefined)}), with .onCreate(gen.now()) and .onUpdate(gen.now()) for the phases the preset set${precision ? `, and without ${type.options === undefined ? 'p' : 'precision: p'} when it set no precision` : ''}`, + }, +]; +const CREATED = '.onCreate(gen.now())'; +const UPDATED = '.onCreate(gen.now()).onUpdate(gen.now())'; +const TEMPORAL_DATE_TIME = choice('DateTime', { as: 'temporal' }); +const STRING_DATE_TIME = choice('DateTime', { as: 'string' }); + +/** Each removed SQL preset and its rewrites. A date and time preset is written with the convenience call that gives the explicit type it stored, where one does (ADR 279). */ export const PRESETS: Readonly> = { uuidString: aliasColumn('UuidString'), ulid: aliasColumn('Ulid'), @@ -66,16 +104,16 @@ export const PRESETS: Readonly> = ), cuid2: aliasColumn('Cuid2'), ksuid: aliasColumn('Ksuid'), - text: writtenAs('field.column(type.String())', 'pg/text@1'), - int: writtenAs('field.column(type.Int())', 'pg/int4@1'), - bigint: writtenAs('field.column(type.BigInt())', 'pg/int8@1'), - float: writtenAs('field.column(type.Float())', 'pg/float8@1'), - decimal: writtenAs('field.column(type.Decimal())', 'pg/numeric@1'), - boolean: writtenAs('field.column(type.Boolean())', 'pg/bool@1'), - json: writtenAs('field.column(type.Jsonb())', 'pg/jsonb@1'), - bytes: writtenAs('field.column(type.Bytes())', 'pg/bytea@1'), - dateTime: writtenAs('field.column(type.TimestamptzTemporal())', 'pg/timestamptz-temporal@1'), - uuidNative: writtenAs('field.column(type.Uuid())', 'pg/uuid@1'), + text: writtenAs(explicit('String'), 'pg/text@1'), + int: writtenAs(explicit('Int'), 'pg/int4@1'), + bigint: writtenAs(explicit('BigInt'), 'pg/int8@1'), + float: writtenAs(explicit('Float'), 'pg/float8@1'), + decimal: writtenAs(explicit('Decimal'), 'pg/numeric@1'), + boolean: writtenAs(explicit('Boolean'), 'pg/bool@1'), + json: writtenAs(explicit('Jsonb'), 'pg/jsonb@1'), + bytes: writtenAs(explicit('Bytes'), 'pg/bytea@1'), + dateTime: writtenAs(TEMPORAL_DATE_TIME, 'pg/timestamptz-temporal@1'), + uuidNative: writtenAs(explicit('Uuid'), 'pg/uuid@1'), generated: idAlias( 'UuidString', (prefix) => @@ -91,66 +129,79 @@ export const PRESETS: Readonly> = ), 'id.cuid2': aliasId('Cuid2', 'cuid(2)'), 'id.ksuid': aliasId('Ksuid', 'ksuid()'), - 'id.uuidv4Native': writtenAs(id('field.column(type.Uuid())', 'uuid()'), 'pg/uuid@1'), - 'id.uuidv7Native': writtenAs(id('field.column(type.Uuid())', 'uuid(7)'), 'pg/uuid@1'), + 'id.uuidv4Native': writtenAs(explicit('Uuid'), 'pg/uuid@1', '.id().onCreate(gen.uuid())'), + 'id.uuidv7Native': writtenAs(explicit('Uuid'), 'pg/uuid@1', '.id().onCreate(gen.uuid(7))'), 'temporal.createdAt': [ - ...writtenAs( - 'field.column(type.TimestamptzTemporal()).onCreate(gen.now())', - 'pg/timestamptz-temporal@1', - ), - ...writtenAs('field.column(type.temporal.CreatedAt())'), + ...writtenAs(TEMPORAL_DATE_TIME, 'pg/timestamptz-temporal@1', CREATED), + ...writtenAs(explicit('temporal.CreatedAt'), undefined), ], 'temporal.updatedAt': [ - ...writtenAs( - 'field.column(type.TimestamptzTemporal()).onCreate(gen.now()).onUpdate(gen.now())', - 'pg/timestamptz-temporal@1', - ), - ...writtenAs('field.column(type.temporal.UpdatedAt())'), + ...writtenAs(TEMPORAL_DATE_TIME, 'pg/timestamptz-temporal@1', UPDATED), + ...writtenAs(explicit('temporal.UpdatedAt'), undefined), ], - 'temporal.createdAtString': writtenAs( - 'field.column(type.TimestamptzString()).onCreate(gen.now())', - 'pg/timestamptz-string@1', - ), - 'temporal.updatedAtString': writtenAs( - 'field.column(type.TimestamptzString()).onCreate(gen.now()).onUpdate(gen.now())', - 'pg/timestamptz-string@1', - ), + 'temporal.createdAtString': writtenAs(STRING_DATE_TIME, 'pg/timestamptz-string@1', CREATED), + 'temporal.updatedAtString': writtenAs(STRING_DATE_TIME, 'pg/timestamptz-string@1', UPDATED), 'temporal.createdAtJsDate': writtenAs( - 'field.column(type.TimestamptzJsDate()).onCreate(gen.now())', + explicit('TimestamptzJsDate'), 'pg/timestamptz-date@1', + CREATED, ), 'temporal.updatedAtJsDate': writtenAs( - 'field.column(type.TimestamptzJsDate()).onCreate(gen.now()).onUpdate(gen.now())', + explicit('TimestamptzJsDate'), 'pg/timestamptz-date@1', + UPDATED, + ), + 'temporal.timestamp': withPhases(explicit('TimestampTemporal'), 'pg/timestamp-temporal@1', true), + 'temporal.timestamptz': withPhases(TEMPORAL_DATE_TIME, 'pg/timestamptz-temporal@1', true), + 'temporal.timestampString': withPhases( + choice('DateTime', { as: 'string', storage: 'timestamp' }), + 'pg/timestamp-string@1', + true, ), - 'temporal.timestamp': withPhases('TimestampTemporal', 'pg/timestamp-temporal@1', true), - 'temporal.timestamptz': withPhases('TimestamptzTemporal', 'pg/timestamptz-temporal@1', true), - 'temporal.timestampString': withPhases('TimestampString', 'pg/timestamp-string@1', true), - 'temporal.timestamptzString': withPhases('TimestamptzString', 'pg/timestamptz-string@1', true), - 'temporal.timestamptzJsDate': withPhases('TimestamptzJsDate', 'pg/timestamptz-date@1', true), - 'temporal.date': withPhases('DateTemporal', 'pg/date-temporal@1', false), - 'temporal.dateString': withPhases('DateString', 'pg/date-string@1', false), - 'temporal.time': withPhases('TimeTemporal', 'pg/time-temporal@1', true), - 'temporal.timeString': withPhases('TimeString', 'pg/time-string@1', true), - 'temporal.timetz': withPhases('Timetz', 'pg/timetz@1', true), - 'temporal.datetime': withPhases('DateTime', 'sqlite/datetime@1', false), + 'temporal.timestamptzString': withPhases(STRING_DATE_TIME, 'pg/timestamptz-string@1', true), + 'temporal.timestamptzJsDate': withPhases( + explicit('TimestamptzJsDate'), + 'pg/timestamptz-date@1', + true, + ), + 'temporal.date': withPhases(choice('Date', { as: 'temporal' }), 'pg/date-temporal@1', false), + 'temporal.dateString': withPhases(choice('Date'), 'pg/date-string@1', false), + 'temporal.time': withPhases(choice('Time', { as: 'temporal' }), 'pg/time-temporal@1', true), + 'temporal.timeString': withPhases(choice('Time'), 'pg/time-string@1', true), + 'temporal.timetz': withPhases(choice('Time', { storage: 'timetz' }), 'pg/timetz@1', true), + 'temporal.datetime': withPhases(explicit('DateTime'), 'sqlite/datetime@1', false), }; -function codecIdAt(types: AuthoringTypeNamespace, path: readonly string[]): string | undefined { +/** The codec a column of `type` is stored with in `types`, a type choice resolved with its options as a column resolves it; `undefined` when `types` has no such type or the options choose none. */ +function codecIdAt( + types: AuthoringTypeNamespace, + path: readonly string[], + options: Readonly> | undefined, +): string | undefined { const [name, ...rest] = path; if (name === undefined || !Object.hasOwn(types, name)) return undefined; const value = types[name]; if (value === undefined) return undefined; - if (!isAuthoringTypeDescriptor(value)) return codecIdAt(value, rest); + if (!isAuthoringTypeDescriptor(value)) return codecIdAt(value, rest, options); if (rest.length > 0) return undefined; if (isAuthoringTypeChoiceDescriptor(value)) { - return instantiateAuthoringTypeCall(types, name, []).codecId; + const args = value.args.map((argument) => + argument.name !== undefined && options !== undefined && Object.hasOwn(options, argument.name) + ? options[argument.name] + : undefined, + ); + try { + return instantiateAuthoringTypeCall(types, name, args).codecId; + } catch (error) { + if (isInternalError(error) || !(error instanceof Error)) throw error; + return undefined; + } } return isAuthoringTypeAliasDescriptor(value) ? value.output.type.codecId : value.output.codecId; } function appliesTo(preset: RemovedFieldPreset, types: AuthoringTypeNamespace): boolean { - const codecId = codecIdAt(types, preset.type); + const codecId = codecIdAt(types, preset.type.path, preset.type.options); return codecId !== undefined && (preset.codecId === undefined || codecId === preset.codecId); } diff --git a/packages/2-sql-family/2-authoring/contract-ts/test/removed-field-presets.test.ts b/packages/2-sql-family/2-authoring/contract-ts/test/removed-field-presets.test.ts index 0e7f15a1268f..0a0c395ea2b8 100644 --- a/packages/2-sql-family/2-authoring/contract-ts/test/removed-field-presets.test.ts +++ b/packages/2-sql-family/2-authoring/contract-ts/test/removed-field-presets.test.ts @@ -33,12 +33,41 @@ const postgresLike = { Jsonb: typeConstructor('pg/jsonb@1'), Uuid: typeConstructor('pg/uuid@1'), TimeTemporal: typeConstructor('pg/time-temporal@1'), + TimeString: typeConstructor('pg/time-string@1'), TimestamptzTemporal: typeConstructor('pg/timestamptz-temporal@1'), TimestamptzJsDate: typeConstructor('pg/timestamptz-date@1'), DateTime: { kind: 'typeChoice', - args: [{ kind: 'option', name: 'as', values: ['date'], default: 'date', optional: true }], - choices: [{ when: { as: 'date' }, type: 'TimestamptzJsDate' }], + args: [ + { + kind: 'option', + name: 'as', + values: ['date', 'temporal'], + default: 'date', + optional: true, + }, + ], + choices: [ + { when: { as: 'date' }, type: 'TimestamptzJsDate' }, + { when: { as: 'temporal' }, type: 'TimestamptzTemporal' }, + ], + }, + Time: { + kind: 'typeChoice', + args: [ + { + kind: 'option', + name: 'as', + values: ['string', 'temporal'], + default: 'string', + optional: true, + }, + { kind: 'value', name: 'precision', dataType: 'pg/int4', optional: true }, + ], + choices: [ + { when: { as: 'string' }, type: 'TimeString' }, + { when: { as: 'temporal' }, type: 'TimeTemporal' }, + ], }, sql: { Char: typeConstructor('sql/char@1'), ...generatedIdAliases }, temporal: { @@ -108,11 +137,15 @@ describe('a read of a removed field preset from the SQL field helpers', () => { ['id.uuidv7Native', 'field.column(type.Uuid()).id().onCreate(gen.uuid(7))'], [ 'temporal.updatedAt', - 'field.column(type.TimestamptzTemporal()).onCreate(gen.now()).onUpdate(gen.now())', + "field.column(type.DateTime({ as: 'temporal' })).onCreate(gen.now()).onUpdate(gen.now())", ], [ 'temporal.time', - 'field.column(type.TimeTemporal(p)), with .onCreate(gen.now()) and .onUpdate(gen.now()) for the phases the preset set, and without p when it set no precision', + "field.column(type.Time({ as: 'temporal', precision: p })), with .onCreate(gen.now()) and .onUpdate(gen.now()) for the phases the preset set, and without precision: p when it set no precision", + ], + [ + 'temporal.timeString', + 'field.column(type.Time({ precision: p })), with .onCreate(gen.now()) and .onUpdate(gen.now()) for the phases the preset set, and without precision: p when it set no precision', ], [ 'generated', @@ -130,6 +163,20 @@ describe('a read of a removed field preset from the SQL field helpers', () => { expect(refusalOf(sqliteField, path)).toMatchObject(unknown(path)); }); + it('names the forms that replace presets where the type choice call stores another codec', () => { + const jsDateOnly = { + ...postgresLike, + DateTime: { + ...postgresLike.DateTime, + choices: [{ when: { as: 'date' }, type: 'TimestamptzJsDate' }], + }, + temporal: {}, + } as const; + expect( + refusalOf(refusingRemovedFieldPresets(field, jsDateOnly), 'temporal.updatedAt'), + ).toMatchObject(unknown('temporal.updatedAt')); + }); + it('names the forms that replace presets where the rewrite type is a type choice that stores another codec', () => { expect(refusalOf(postgresField, 'temporal.datetime')).toMatchObject( unknown('temporal.datetime'), diff --git a/test/integration/test/authoring/preset-replacements/typescript-preset-refusals.test.ts b/test/integration/test/authoring/preset-replacements/typescript-preset-refusals.test.ts index 60ee79eddd78..32a8e0237739 100644 --- a/test/integration/test/authoring/preset-replacements/typescript-preset-refusals.test.ts +++ b/test/integration/test/authoring/preset-replacements/typescript-preset-refusals.test.ts @@ -56,11 +56,24 @@ const generated = ([code = '']) => code.replace('(n)', `(${length})`).replace('gen.fn()', `gen.${generator}`); +/** The column with `precision` for `p`, or without the precision when it is empty: `type.X(p)` for an explicit type, `precision: p` in the options of a type choice. */ +function withPrecision(column: string, precision: string): string { + if (precision !== '') { + return column + .replace('(p)', `(${precision})`) + .replace('precision: p', `precision: ${precision}`); + } + return column + .replace('(p)', '()') + .replace('({ precision: p })', '()') + .replace(', precision: p', ''); +} + /** The column the rewrite names, with `precision` for `p` (`undefined` when the preset took no precision, so the rewrite must name none), and the rewrite's `.onCreate(...)` and `.onUpdate(...)` for the phases the preset set. */ const withPhases = (precision: string | undefined, phases: readonly ('onCreate' | 'onUpdate')[]): Write => ([column = '', ...modifiers]) => { - if (precision === undefined && column.includes('(p)')) { + if (precision === undefined && /\(p\)|precision: p/.test(column)) { throw new Error(`the rewrite ${column} names a precision the preset did not take`); } const modifiersOfPhases = phases.map((phase) => { @@ -68,7 +81,7 @@ const withPhases = if (modifier === undefined) throw new Error(`the rewrite names no .${phase}(...)`); return modifier; }); - return column.replace('(p)', `(${precision ?? ''})`) + modifiersOfPhases.join(''); + return withPrecision(column, precision ?? '') + modifiersOfPhases.join(''); }; const BOTH = ['onCreate', 'onUpdate'] as const; From b3ffec44724a059c5abb3e314df9cef2a738821c Mon Sep 17 00:00:00 2001 From: willbot Date: Wed, 28 Oct 2026 14:54:02 +0100 Subject: [PATCH 113/118] Examples write DateTime(as: temporal) where the merge wrote TimestamptzTemporal Merging the field-preset removal rewrote each old DateTime in the examples and the telemetry backend to TimestamptzTemporal, so the contracts stayed the same. ADR 279 says users start from the convenience type and the tools write it back, and contract print on these schemas writes DateTime(as: temporal). The sources, the migration snapshots, and the TypeScript contracts and fragments now say DateTime(as: temporal) or type.DateTime({ as: 'temporal' }). No contract.json or hash changes. Co-Authored-By: Claude Opus 5.5 Signed-off-by: willbot Signed-off-by: Will Madden --- apps/telemetry-backend/src/prisma/contract.prisma | 2 +- .../src/prisma/contract.prisma | 6 +++--- .../app/20260422T0720_initial/contract.prisma | 6 +++--- .../contract.prisma | 6 +++--- .../contract.prisma | 8 ++++---- .../contract.prisma | 8 ++++---- .../contract.prisma | 8 ++++---- examples/prisma-8-demo/src/orm-client/fragments.ts | 2 +- examples/prisma-8-demo/src/prisma/contract.prisma | 8 ++++---- .../react-router-demo/src/prisma/contract.prisma | 4 ++-- examples/react-router-demo/src/prisma/contract.ts | 14 ++++++++++---- 11 files changed, 39 insertions(+), 33 deletions(-) diff --git a/apps/telemetry-backend/src/prisma/contract.prisma b/apps/telemetry-backend/src/prisma/contract.prisma index c90daf667202..2b3ee799173a 100644 --- a/apps/telemetry-backend/src/prisma/contract.prisma +++ b/apps/telemetry-backend/src/prisma/contract.prisma @@ -2,7 +2,7 @@ model TelemetryEvent { id BigInt @id @default(autoincrement()) - ingestedAt TimestamptzTemporal @default(now()) + ingestedAt DateTime(as: temporal) @default(now()) installationId String version String command String diff --git a/examples/prisma-8-cloudflare-worker/src/prisma/contract.prisma b/examples/prisma-8-cloudflare-worker/src/prisma/contract.prisma index c2e2e927af44..109b7b4aa493 100644 --- a/examples/prisma-8-cloudflare-worker/src/prisma/contract.prisma +++ b/examples/prisma-8-cloudflare-worker/src/prisma/contract.prisma @@ -17,7 +17,7 @@ model User { id String @id @onCreate(uuid()) email String displayName String - createdAt TimestamptzTemporal @default(now()) + createdAt DateTime(as: temporal) @default(now()) kind user_type address Address? posts Post[] @@ -30,7 +30,7 @@ model Post { id String @id @onCreate(uuid()) title String userId String - createdAt TimestamptzTemporal @default(now()) + createdAt DateTime(as: temporal) @default(now()) user User @relation(fields: [userId], references: [id]) @@ -44,7 +44,7 @@ model Task { status String @default("open") type String userId String - createdAt TimestamptzTemporal @default(now()) + createdAt DateTime(as: temporal) @default(now()) user User @relation(fields: [userId], references: [id]) diff --git a/examples/prisma-8-demo/migrations/app/20260422T0720_initial/contract.prisma b/examples/prisma-8-demo/migrations/app/20260422T0720_initial/contract.prisma index 93051368ceb8..526dc39d7964 100644 --- a/examples/prisma-8-demo/migrations/app/20260422T0720_initial/contract.prisma +++ b/examples/prisma-8-demo/migrations/app/20260422T0720_initial/contract.prisma @@ -28,7 +28,7 @@ model User { id Uuid @id @onCreate(uuid()) email String displayName String - createdAt TimestamptzTemporal @default(now()) + createdAt DateTime(as: temporal) @default(now()) kind user_type address Address? posts Post[] @@ -42,7 +42,7 @@ model Post { title String userId Uuid priority Priority @default(Low) - createdAt TimestamptzTemporal @default(now()) + createdAt DateTime(as: temporal) @default(now()) embedding Embedding1536? user User @relation(fields: [userId], references: [id]) @@ -78,7 +78,7 @@ model Task { status String @default("open") type String userId Uuid - createdAt TimestamptzTemporal @default(now()) + createdAt DateTime(as: temporal) @default(now()) user User @relation(fields: [userId], references: [id]) diff --git a/examples/prisma-8-demo/migrations/app/20260810T1108_add_post_engagement_counters/contract.prisma b/examples/prisma-8-demo/migrations/app/20260810T1108_add_post_engagement_counters/contract.prisma index 5abc32952db2..14c27660787a 100644 --- a/examples/prisma-8-demo/migrations/app/20260810T1108_add_post_engagement_counters/contract.prisma +++ b/examples/prisma-8-demo/migrations/app/20260810T1108_add_post_engagement_counters/contract.prisma @@ -28,7 +28,7 @@ model User { id Uuid @id @onCreate(uuid()) email String displayName String - createdAt TimestamptzTemporal @default(now()) + createdAt DateTime(as: temporal) @default(now()) kind user_type address Address? posts Post[] @@ -42,7 +42,7 @@ model Post { title String userId Uuid priority Priority @default(Low) - createdAt TimestamptzTemporal @default(now()) + createdAt DateTime(as: temporal) @default(now()) embedding Embedding1536? // Three engagement counters, one per integer representation. They are @@ -94,7 +94,7 @@ model Task { status String @default("open") type String userId Uuid - createdAt TimestamptzTemporal @default(now()) + createdAt DateTime(as: temporal) @default(now()) user User @relation(fields: [userId], references: [id]) diff --git a/examples/prisma-8-demo/migrations/app/20260917T0818_add_post_expires_at/contract.prisma b/examples/prisma-8-demo/migrations/app/20260917T0818_add_post_expires_at/contract.prisma index 07dbc8df3d69..d0d97622dbe5 100644 --- a/examples/prisma-8-demo/migrations/app/20260917T0818_add_post_expires_at/contract.prisma +++ b/examples/prisma-8-demo/migrations/app/20260917T0818_add_post_expires_at/contract.prisma @@ -28,7 +28,7 @@ model User { id Uuid @id @onCreate(uuid()) email String displayName String - createdAt TimestamptzTemporal @default(now()) + createdAt DateTime(as: temporal) @default(now()) kind user_type address Address? posts Post[] @@ -42,8 +42,8 @@ model Post { title String userId Uuid priority Priority @default(Low) - createdAt TimestamptzTemporal @default(now()) - expiresAt TimestamptzTemporal @default(sql`(now() + '7 days'::interval)`) + createdAt DateTime(as: temporal) @default(now()) + expiresAt DateTime(as: temporal) @default(sql`(now() + '7 days'::interval)`) embedding Embedding1536? // Three engagement counters, one per integer representation. They are @@ -95,7 +95,7 @@ model Task { status String @default("open") type String userId Uuid - createdAt TimestamptzTemporal @default(now()) + createdAt DateTime(as: temporal) @default(now()) user User @relation(fields: [userId], references: [id]) diff --git a/examples/prisma-8-demo/migrations/app/20260922T1218_add_post_title_search/contract.prisma b/examples/prisma-8-demo/migrations/app/20260922T1218_add_post_title_search/contract.prisma index e20083f1f09f..cd7a0815797a 100644 --- a/examples/prisma-8-demo/migrations/app/20260922T1218_add_post_title_search/contract.prisma +++ b/examples/prisma-8-demo/migrations/app/20260922T1218_add_post_title_search/contract.prisma @@ -28,7 +28,7 @@ model User { id Uuid @id @onCreate(uuid()) email String displayName String - createdAt TimestamptzTemporal @default(now()) + createdAt DateTime(as: temporal) @default(now()) kind user_type address Address? posts Post[] @@ -42,8 +42,8 @@ model Post { title String userId Uuid priority Priority @default(Low) - createdAt TimestamptzTemporal @default(now()) - expiresAt TimestamptzTemporal @default(sql`(now() + '7 days'::interval)`) + createdAt DateTime(as: temporal) @default(now()) + expiresAt DateTime(as: temporal) @default(sql`(now() + '7 days'::interval)`) embedding Embedding1536? // Three engagement counters, one per integer representation. They are @@ -96,7 +96,7 @@ model Task { status String @default("open") type String userId Uuid - createdAt TimestamptzTemporal @default(now()) + createdAt DateTime(as: temporal) @default(now()) user User @relation(fields: [userId], references: [id]) diff --git a/examples/prisma-8-demo/migrations/app/20261009T1354_add_post_body_search/contract.prisma b/examples/prisma-8-demo/migrations/app/20261009T1354_add_post_body_search/contract.prisma index 67dc00cb3245..26cdbfd3a008 100644 --- a/examples/prisma-8-demo/migrations/app/20261009T1354_add_post_body_search/contract.prisma +++ b/examples/prisma-8-demo/migrations/app/20261009T1354_add_post_body_search/contract.prisma @@ -28,7 +28,7 @@ model User { id Uuid @id @onCreate(uuid()) email String displayName String - createdAt TimestamptzTemporal @default(now()) + createdAt DateTime(as: temporal) @default(now()) kind user_type address Address? posts Post[] @@ -43,8 +43,8 @@ model Post { body String? userId Uuid priority Priority @default(Low) - createdAt TimestamptzTemporal @default(now()) - expiresAt TimestamptzTemporal @default(sql`(now() + '7 days'::interval)`) + createdAt DateTime(as: temporal) @default(now()) + expiresAt DateTime(as: temporal) @default(sql`(now() + '7 days'::interval)`) embedding Embedding1536? // Three engagement counters, one per integer representation. They are @@ -100,7 +100,7 @@ model Task { status String @default("open") type String userId Uuid - createdAt TimestamptzTemporal @default(now()) + createdAt DateTime(as: temporal) @default(now()) user User @relation(fields: [userId], references: [id]) diff --git a/examples/prisma-8-demo/src/orm-client/fragments.ts b/examples/prisma-8-demo/src/orm-client/fragments.ts index f5ac1b1dccaf..eb8b1d281b37 100644 --- a/examples/prisma-8-demo/src/orm-client/fragments.ts +++ b/examples/prisma-8-demo/src/orm-client/fragments.ts @@ -7,7 +7,7 @@ import { db } from '../prisma/db'; * A scope for any model with a `createdAt` timestamp, such as `User`, `Post` and `Task`: rows created since `since`. */ export function createdSince(since: Temporal.Instant) { - return db.orm.fragment({ createdAt: field.column(type.TimestamptzTemporal()) }, (rows) => + return db.orm.fragment({ createdAt: field.column(type.DateTime({ as: 'temporal' })) }, (rows) => rows.where((row) => row.createdAt.gte(since)), ); } diff --git a/examples/prisma-8-demo/src/prisma/contract.prisma b/examples/prisma-8-demo/src/prisma/contract.prisma index 7ca2ccd41957..93465e9bafe2 100644 --- a/examples/prisma-8-demo/src/prisma/contract.prisma +++ b/examples/prisma-8-demo/src/prisma/contract.prisma @@ -28,7 +28,7 @@ model User { id Uuid @id @onCreate(uuid()) email String displayName String - createdAt TimestamptzTemporal @default(now()) + createdAt DateTime(as: temporal) @default(now()) kind user_type address Address? posts Post[] @@ -43,8 +43,8 @@ model Post { body String? userId Uuid priority Priority @default(Low) - createdAt TimestamptzTemporal @default(now()) - expiresAt TimestamptzTemporal @default(sql`(now() + '7 days'::interval)`) + createdAt DateTime(as: temporal) @default(now()) + expiresAt DateTime(as: temporal) @default(sql`(now() + '7 days'::interval)`) embedding Embedding1536? // Three engagement counters, one per integer representation. They are @@ -100,7 +100,7 @@ model Task { status String @default("open") type String userId Uuid - createdAt TimestamptzTemporal @default(now()) + createdAt DateTime(as: temporal) @default(now()) user User @relation(fields: [userId], references: [id]) diff --git a/examples/react-router-demo/src/prisma/contract.prisma b/examples/react-router-demo/src/prisma/contract.prisma index f4f18eefde3d..b7eee4922a9a 100644 --- a/examples/react-router-demo/src/prisma/contract.prisma +++ b/examples/react-router-demo/src/prisma/contract.prisma @@ -3,7 +3,7 @@ model User { id Char(36) @id @onCreate(uuid()) email String - createdAt TimestamptzTemporal @default(now()) + createdAt DateTime(as: temporal) @default(now()) posts Post[] @@map("user") @@ -13,7 +13,7 @@ model Post { id Char(36) @id @onCreate(uuid()) title String userId String - createdAt TimestamptzTemporal @default(now()) + createdAt DateTime(as: temporal) @default(now()) user User @relation(fields: [userId], references: [id]) diff --git a/examples/react-router-demo/src/prisma/contract.ts b/examples/react-router-demo/src/prisma/contract.ts index ae83baf4607b..1737bd266ad8 100644 --- a/examples/react-router-demo/src/prisma/contract.ts +++ b/examples/react-router-demo/src/prisma/contract.ts @@ -5,8 +5,11 @@ export const contract = defineContract({}, ({ field, model, gen, type }) => { fields: { id: field.column(type.Char(36)).id().onCreate(gen.uuid()), email: field.column(type.String()), - createdAt: field.column(type.TimestamptzTemporal()).onCreate(gen.now()), - updatedAt: field.column(type.TimestamptzTemporal()).onCreate(gen.now()).onUpdate(gen.now()), + createdAt: field.column(type.DateTime({ as: 'temporal' })).onCreate(gen.now()), + updatedAt: field + .column(type.DateTime({ as: 'temporal' })) + .onCreate(gen.now()) + .onUpdate(gen.now()), }, }); @@ -15,8 +18,11 @@ export const contract = defineContract({}, ({ field, model, gen, type }) => { id: field.column(type.Char(36)).id().onCreate(gen.uuid()), title: field.column(type.String()), userId: field.column(type.Char(36)), - createdAt: field.column(type.TimestamptzTemporal()).onCreate(gen.now()), - updatedAt: field.column(type.TimestamptzTemporal()).onCreate(gen.now()).onUpdate(gen.now()), + createdAt: field.column(type.DateTime({ as: 'temporal' })).onCreate(gen.now()), + updatedAt: field + .column(type.DateTime({ as: 'temporal' })) + .onCreate(gen.now()) + .onUpdate(gen.now()), }, }); From 95396a6baafcf2cb14f8820ce60c4a4df1cc90e6 Mon Sep 17 00:00:00 2001 From: willbot Date: Wed, 28 Oct 2026 14:55:19 +0100 Subject: [PATCH 114/118] A removed-preset refusal writes the field's ? or [] right after the type The refusal names the call and what to write in its place. For an optional field, temporal.createdAt()? got "Write DateTime(as: temporal) @onCreate(now())", and putting that in place of the call gives DateTime(as: temporal) @onCreate(now())?, which does not parse. describeRemovedFieldPreset now takes the field, and every rewrite writes the field's ? or [] right after the type and before any attribute: "Write DateTime(as: temporal)? @onCreate(now())". This covers every row, including the id, nanoid and phase rows and their fallback sentences. The OptionalCreatedAt parity row has its preset again, so its refusal is checked. Co-Authored-By: Claude Opus 5.5 Signed-off-by: willbot Signed-off-by: Will Madden --- .../psl-parser/src/removed-field-presets.ts | 26 +++++++++----- .../test/removed-field-presets.test.ts | 35 ++++++++++++++----- .../src/describe-unresolved-type.ts | 7 +--- .../contract-psl/src/removed-field-presets.ts | 10 +++--- .../contract-psl/src/unresolved-type.ts | 2 +- .../interpreter.removed-field-presets.test.ts | 31 +++++++++++++--- .../preset-replacement-rows.ts | 2 +- .../preset-replacements.test.ts | 10 +++++- 8 files changed, 88 insertions(+), 35 deletions(-) diff --git a/packages/1-framework/2-authoring/psl-parser/src/removed-field-presets.ts b/packages/1-framework/2-authoring/psl-parser/src/removed-field-presets.ts index 0a83eed98514..d47db338d3aa 100644 --- a/packages/1-framework/2-authoring/psl-parser/src/removed-field-presets.ts +++ b/packages/1-framework/2-authoring/psl-parser/src/removed-field-presets.ts @@ -4,6 +4,7 @@ import { writtenContributedTypeCodecId, } from './contributed-type-scope'; import type { ResolvedTypeConstructorCall } from './resolve'; +import type { FieldSymbol } from './symbol-table'; /** A type as a removed preset's rewrite writes it: its path, and the options it is called with, each written as a bare name, such as `as: temporal`. */ export interface RemovedPresetType { @@ -35,7 +36,8 @@ export function writtenPresetType(type: RemovedPresetType, extra?: string): stri export interface RemovedFieldPreset { readonly type: RemovedPresetType; readonly codecId?: string; - readonly rewrite: (call: ResolvedTypeConstructorCall) => string; + /** The rewrite of `call`, with `modifier`, the field's `?` or `[]`, written right after the type. */ + readonly rewrite: (call: ResolvedTypeConstructorCall, modifier: string) => string; } /** A family's removed field presets, keyed by the path a schema called: the rows of each, of which the first that applies names the rewrite, such as a target's own type before the family's. */ @@ -59,7 +61,7 @@ export function removedPresetWrittenAs( { type: written, ...(codecId === undefined ? {} : { codecId }), - rewrite: () => `Write ${writtenPresetType(written)}${suffix}.`, + rewrite: (_, modifier) => `Write ${writtenPresetType(written)}${modifier}${suffix}.`, }, ]; } @@ -78,7 +80,7 @@ export function removedPresetWithPhases( { type: written, codecId, - rewrite: (call) => { + rewrite: (call, modifier) => { const args = argumentsByParameter(call.args, parameters); const precision = args?.get('precision'); if ( @@ -86,27 +88,35 @@ export function removedPresetWithPhases( (precision !== undefined && !/^\d+$/.test(precision)) || PHASES.some((phase) => args.has(phase) && args.get(phase) !== 'now') ) { - return `Write ${writtenPresetType(written, takesPrecision ? '...' : undefined)} with @onCreate(now()) and @onUpdate(now()) for the phases the preset set.`; + return `Write ${writtenPresetType(written, takesPrecision ? '...' : undefined)}${modifier} with @onCreate(now()) and @onUpdate(now()) for the phases the preset set.`; } const attributes = PHASES.filter((phase) => args.has(phase)).map( (phase) => ` @${phase}(now())`, ); const extra = precision === undefined ? undefined : `precision: ${precision}`; - return `Write ${writtenPresetType(written, extra)}${attributes.join('')}.`; + return `Write ${writtenPresetType(written, extra)}${modifier}${attributes.join('')}.`; }, }, ]; } +/** The `?` or `[]` a field writes right after its type. */ +function typeModifier(field: FieldSymbol): string { + if (field.list) return field.elementOptional ? '?[]' : '[]'; + return field.optional ? '?' : ''; +} + /** - * The refusal of a call to a field preset Prisma 8 removed, naming what to write instead, or `undefined` when `presets` has no row for the call that applies to a stack whose types `types` holds. A row applies where its type, called as the rewrite writes it, resolves as a column would, to the row's codec. A row is skipped when `isDeclared` says the schema declares the first name of its type, because the rewrite would then name the schema's declaration. + * The refusal of a field typed by a call to a field preset Prisma 8 removed, naming what to write instead, with the field's `?` or `[]` right after the type and before any attribute. `undefined` when the field is not typed by a call, or `presets` has no row for the call that applies to a stack whose types `types` holds. A row applies where its type, called as the rewrite writes it, resolves as a column would, to the row's codec. A row is skipped when `isDeclared` says the schema declares the first name of its type, because the rewrite would then name the schema's declaration. */ export function describeRemovedFieldPreset( presets: RemovedFieldPresets, - call: ResolvedTypeConstructorCall, + field: FieldSymbol, types: ContributedTypeNamespace, isDeclared: (name: string) => boolean, ): string | undefined { + const call = field.typeConstructor; + if (call === undefined) return undefined; const path = call.path.join('.'); const rows = Object.hasOwn(presets, path) ? presets[path] : undefined; const preset = rows?.find((row) => { @@ -128,5 +138,5 @@ export function describeRemovedFieldPreset( ); }); if (preset === undefined) return undefined; - return `${printTypeConstructorCall(call)} was a field preset, and Prisma 8 removed field presets. ${preset.rewrite(call)}`; + return `${printTypeConstructorCall(call)} was a field preset, and Prisma 8 removed field presets. ${preset.rewrite(call, typeModifier(field))}`; } diff --git a/packages/1-framework/2-authoring/psl-parser/test/removed-field-presets.test.ts b/packages/1-framework/2-authoring/psl-parser/test/removed-field-presets.test.ts index fdbc2cee7a03..c0750e60cfd6 100644 --- a/packages/1-framework/2-authoring/psl-parser/test/removed-field-presets.test.ts +++ b/packages/1-framework/2-authoring/psl-parser/test/removed-field-presets.test.ts @@ -7,20 +7,19 @@ import { removedPresetWithPhases, removedPresetWrittenAs, } from '../src/removed-field-presets'; -import type { ResolvedTypeConstructorCall } from '../src/resolve'; import { PslSources } from '../src/source-file'; -import { buildSymbolTable } from '../src/symbol-table'; +import { buildSymbolTable, type FieldSymbol } from '../src/symbol-table'; -function callOf(type: string): ResolvedTypeConstructorCall { +function callOf(type: string): FieldSymbol { const { document, sources } = parse(`model M {\n f ${type}\n}\n`, 'schema.prisma'); const { symbolTable } = buildSymbolTable({ documents: [document], sources: new PslSources([[document.syntax, sources.sourceFileFor(document.syntax)]]), defaultNamespaceId: 'public', }); - const call = symbolTable.defaultNamespace.models['M']?.fields['f']?.typeConstructor; - if (call === undefined) throw new Error(`${type} is not a call`); - return call; + const field = symbolTable.defaultNamespace.models['M']?.fields['f']; + if (field?.typeConstructor === undefined) throw new Error(`${type} is not a call`); + return field; } const stamp = { kind: 'typeConstructor', output: { codecId: 'test/stamp@1' } } as const; @@ -46,11 +45,11 @@ const presets = { }; function describeCall( - call: ResolvedTypeConstructorCall, + field: FieldSymbol, types: ContributedTypeNamespace, isDeclared: (name: string) => boolean = () => false, ) { - return describeRemovedFieldPreset(presets, call, types, isDeclared); + return describeRemovedFieldPreset(presets, field, types, isDeclared); } describe('describeRemovedFieldPreset', () => { @@ -82,6 +81,26 @@ describe('describeRemovedFieldPreset', () => { expect(describeCall(callOf('chosen()'), { Moment: textOnly, Text: text })).toBeUndefined(); }); + it.each([ + ['plain()?', 'Write Stamp? @id.'], + ['plain()[]', 'Write Stamp[] @id.'], + ['ns.stamp(3, onCreate: now)?', 'Write Stamp(precision: 3)? @onCreate(now()).'], + [ + 'ns.stamp(onCreate: later)[]', + 'Write Stamp(...)[] with @onCreate(now()) and @onUpdate(now()) for the phases the preset set.', + ], + ])('writes the modifier of %s right after the type, before the attributes', (type, rewrite) => { + const call = type.replace(/(\?|\[\])$/, ''); + expect(describeCall(callOf(type), { Stamp: stamp })).toBe( + `${call} was a field preset, and Prisma 8 removed field presets. ${rewrite}`, + ); + }); + + it('names nothing for a field not typed by a call', () => { + const { typeConstructor: _, ...field } = callOf('plain()'); + expect(describeCall(field, { Stamp: stamp })).toBeUndefined(); + }); + it('names the rewrite of the first row that applies to the stack', () => { expect( describeCall(callOf('either()'), { diff --git a/packages/2-mongo-family/2-authoring/contract-psl/src/describe-unresolved-type.ts b/packages/2-mongo-family/2-authoring/contract-psl/src/describe-unresolved-type.ts index bcb55842863d..7e3c2f10028c 100644 --- a/packages/2-mongo-family/2-authoring/contract-psl/src/describe-unresolved-type.ts +++ b/packages/2-mongo-family/2-authoring/contract-psl/src/describe-unresolved-type.ts @@ -39,12 +39,7 @@ export function describeUnresolvedMongoType( const scalars = collectScalarTypeConstructors(types); return ({ field, owner, written, isDeclared }) => { if (field.typeConstructor !== undefined) { - return describeRemovedFieldPreset( - removedMongoFieldPresets, - field.typeConstructor, - types, - isDeclared, - ); + return describeRemovedFieldPreset(removedMongoFieldPresets, field, types, isDeclared); } if (field.typeNamespaceId !== undefined) { return undefined; diff --git a/packages/2-sql-family/2-authoring/contract-psl/src/removed-field-presets.ts b/packages/2-sql-family/2-authoring/contract-psl/src/removed-field-presets.ts index 0635d92bdb61..f7d0542db40d 100644 --- a/packages/2-sql-family/2-authoring/contract-psl/src/removed-field-presets.ts +++ b/packages/2-sql-family/2-authoring/contract-psl/src/removed-field-presets.ts @@ -43,19 +43,19 @@ function idAlias( function nanoid(id: boolean): readonly RemovedFieldPreset[] { return GENERATED_ID_COLUMN_ALIAS_NAMESPACES.map((namespace) => { - const write = (size: string | undefined) => { + const write = (size: string | undefined, modifier: string) => { const column = size === undefined ? qualified(namespace, 'Nanoid') : qualified(namespace, `Char(${size})`); - return id ? `${column} @id @onCreate(nanoid(${size ?? ''}))` : column; + return `${column}${modifier}${id ? ` @id @onCreate(nanoid(${size ?? ''}))` : ''}`; }; return { type: presetType(qualified(namespace, 'Nanoid')), codecId: generatedIdColumnAliases.Nanoid.output.type.codecId, - rewrite: (call) => { + rewrite: (call, modifier) => { const options = nanoidSize(call); return options === undefined - ? `Write ${write(undefined)}, or ${write('n')} for a size of n.` - : `Write ${write(options.size)}.`; + ? `Write ${write(undefined, modifier)}, or ${write('n', modifier)} for a size of n.` + : `Write ${write(options.size, modifier)}.`; }, }; }); diff --git a/packages/2-sql-family/2-authoring/contract-psl/src/unresolved-type.ts b/packages/2-sql-family/2-authoring/contract-psl/src/unresolved-type.ts index 3cb796df22e8..b2921f736678 100644 --- a/packages/2-sql-family/2-authoring/contract-psl/src/unresolved-type.ts +++ b/packages/2-sql-family/2-authoring/contract-psl/src/unresolved-type.ts @@ -46,7 +46,7 @@ export function describeUnresolvedSqlType( if (written !== 'Unsupported') return describeRemovedFieldPreset( removedSqlFieldPresets, - call, + field, contributions.type, isDeclared, ); diff --git a/packages/2-sql-family/2-authoring/contract-psl/test/interpreter.removed-field-presets.test.ts b/packages/2-sql-family/2-authoring/contract-psl/test/interpreter.removed-field-presets.test.ts index 2385af2b627b..0e6e1b5df5e9 100644 --- a/packages/2-sql-family/2-authoring/contract-psl/test/interpreter.removed-field-presets.test.ts +++ b/packages/2-sql-family/2-authoring/contract-psl/test/interpreter.removed-field-presets.test.ts @@ -308,11 +308,32 @@ describe('a removed field preset in a SQL schema', () => { ]); }); - it('refuses each preset field once, keeping the field modifiers out of the call', () => { - expect(diagnosticsOf('temporal.createdAt()?', postgresInput)).toEqual( - removed('temporal.createdAt()', 'Write DateTime(as: temporal) @onCreate(now()).'), - ); - }); + it.each([ + ['temporal.createdAt()?', 'Write DateTime(as: temporal)? @onCreate(now()).'], + ['temporal.timestamptz(onCreate: now)?', 'Write DateTime(as: temporal)? @onCreate(now()).'], + [ + 'temporal.time(3, onUpdate: now)[]', + 'Write Time(as: temporal, precision: 3)[] @onUpdate(now()).', + ], + ['id.ulid()?', 'Write Ulid? @id @onCreate(ulid()).'], + ['nanoid()[]', 'Write Nanoid[].'], + [ + 'id.nanoid({ length: 16 })?', + 'Write Nanoid? @id @onCreate(nanoid()), or Char(n)? @id @onCreate(nanoid(n)) for a size of n.', + ], + [ + 'temporal.timestamp(onCreate: later)?', + 'Write TimestampTemporal(...)? with @onCreate(now()) and @onUpdate(now()) for the phases the preset set.', + ], + ])( + 'refuses %s once, writing the modifier right after the type and before the attributes', + (field, rewrite) => { + const refusals = diagnosticsOf(field, postgresInput).filter( + ({ code }) => code === 'PSL_UNRESOLVED_REFERENCE', + ); + expect(refusals).toEqual(removed(field.replace(/(\?|\[\])$/, ''), rewrite)); + }, + ); it('keeps the binder message for a SQLite preset', () => { expect(diagnosticsOf('temporal.datetime(onCreate: now)', postgresInput)).toEqual([ diff --git a/test/integration/test/authoring/preset-replacements/preset-replacement-rows.ts b/test/integration/test/authoring/preset-replacements/preset-replacement-rows.ts index 3d3ca680f57e..f339dfb835f7 100644 --- a/test/integration/test/authoring/preset-replacements/preset-replacement-rows.ts +++ b/test/integration/test/authoring/preset-replacements/preset-replacement-rows.ts @@ -446,7 +446,7 @@ export const postgresRows: readonly SqlRow[] = [ }, { model: 'OptionalCreatedAt', - psl: { replacement: 'TimestamptzTemporal? @onCreate(now())' }, + psl: { preset: 'temporal.createdAt()?', replacement: 'TimestamptzTemporal? @onCreate(now())' }, ts: ({ field, type, gen }) => field.column(type.TimestamptzTemporal()).onCreate(gen.now()).optional(), }, diff --git a/test/integration/test/authoring/preset-replacements/preset-replacements.test.ts b/test/integration/test/authoring/preset-replacements/preset-replacements.test.ts index 67b777cfb9e5..1220c7346856 100644 --- a/test/integration/test/authoring/preset-replacements/preset-replacements.test.ts +++ b/test/integration/test/authoring/preset-replacements/preset-replacements.test.ts @@ -76,6 +76,14 @@ function refusalOf(targetCase: TargetCase, row: Row) { }; } +/** The field written with `rewrite` in place of `call` and the `?` or `[]` after it, which the rewrite writes itself. */ +function withRewrite(preset: string, call: string, rewrite: string): string { + const start = preset.indexOf(call); + const end = start + call.length; + const modifier = /^(?:\?|\[\])?/.exec(preset.slice(end))?.[0] ?? ''; + return `${preset.slice(0, start)}${rewrite}${preset.slice(end + modifier.length)}`; +} + function describeTarget>( targetCase: TargetCase, ): void { @@ -91,7 +99,7 @@ function describeTarget>( ); const rewritten = { ...row, - psl: { replacement: preset.replace(call ?? '', rewrite ?? '') }, + psl: { replacement: withRewrite(preset, call ?? '', rewrite ?? '') }, }; expect(await emitted(targetCase, 'psl', [rewritten], 'replacement')).toEqual( await emitted(targetCase, 'psl', [row], 'replacement'), From 940abb017b6079f4b56b3b2a65ff477d171ca7af Mon Sep 17 00:00:00 2001 From: willbot Date: Wed, 28 Oct 2026 14:58:28 +0100 Subject: [PATCH 115/118] Removed-preset fallback rows apply only where they store the preset's codec temporal.createdAt() and temporal.updatedAt() each have a PostgreSQL row and a fallback row for SQLite that writes temporal.CreatedAt or temporal.UpdatedAt. The fallback had no codec id, so it applied wherever the alias exists. On PostgreSQL, with a schema that declares DateTime, the first row is skipped and the fallback told the user to write temporal.CreatedAt, which there stores TimestamptzJsDate(3), not what the preset stored. The fallback rows now carry SQLite's sqlite/datetime@1, in PSL and TypeScript. Every row has a codec id now, so the codec id is required and a row applies only where its type stores exactly that codec. Co-Authored-By: Claude Opus 5.5 Signed-off-by: willbot Signed-off-by: Will Madden --- .../psl-parser/src/removed-field-presets.ts | 14 +++++-------- .../contract-psl/src/removed-field-presets.ts | 9 +++++---- .../interpreter.removed-field-presets.test.ts | 12 +++++++++++ .../contract-ts/src/removed-field-presets.ts | 20 ++++++++----------- .../test/removed-field-presets.test.ts | 3 +-- 5 files changed, 31 insertions(+), 27 deletions(-) diff --git a/packages/1-framework/2-authoring/psl-parser/src/removed-field-presets.ts b/packages/1-framework/2-authoring/psl-parser/src/removed-field-presets.ts index d47db338d3aa..5d78fafe2a3c 100644 --- a/packages/1-framework/2-authoring/psl-parser/src/removed-field-presets.ts +++ b/packages/1-framework/2-authoring/psl-parser/src/removed-field-presets.ts @@ -31,11 +31,11 @@ export function writtenPresetType(type: RemovedPresetType, extra?: string): stri } /** - * A field preset Prisma 8 removed, and how to write what it stored. The preset is named only where `type` resolves, and to `codecId` when one is given, so a row does not fire on a target the preset never existed on. + * A field preset Prisma 8 removed, and how to write what it stored. The preset is named only where `type` resolves to `codecId`, the codec the preset stored, so a row does not fire on a target the preset never existed on, or name a type that stores something else. */ export interface RemovedFieldPreset { readonly type: RemovedPresetType; - readonly codecId?: string; + readonly codecId: string; /** The rewrite of `call`, with `modifier`, the field's `?` or `[]`, written right after the type. */ readonly rewrite: (call: ResolvedTypeConstructorCall, modifier: string) => string; } @@ -52,7 +52,7 @@ function asType(type: RemovedPresetType | string): RemovedPresetType { /** The row of a preset whose rewrite always writes `type`, then `attributes` when given, such as `Ulid @id @onCreate(ulid())`. */ export function removedPresetWrittenAs( type: RemovedPresetType | string, - codecId: string | undefined, + codecId: string, attributes?: string, ): readonly RemovedFieldPreset[] { const written = asType(type); @@ -60,7 +60,7 @@ export function removedPresetWrittenAs( return [ { type: written, - ...(codecId === undefined ? {} : { codecId }), + codecId, rewrite: (_, modifier) => `Write ${writtenPresetType(written)}${modifier}${suffix}.`, }, ]; @@ -131,11 +131,7 @@ export function describeRemovedFieldPreset( })), span: call.span, }); - return ( - codecId !== undefined && - (name === undefined || !isDeclared(name)) && - (row.codecId === undefined || codecId === row.codecId) - ); + return codecId === row.codecId && (name === undefined || !isDeclared(name)); }); if (preset === undefined) return undefined; return `${printTypeConstructorCall(call)} was a field preset, and Prisma 8 removed field presets. ${preset.rewrite(call, typeModifier(field))}`; diff --git a/packages/2-sql-family/2-authoring/contract-psl/src/removed-field-presets.ts b/packages/2-sql-family/2-authoring/contract-psl/src/removed-field-presets.ts index f7d0542db40d..7f6f8b31f7f8 100644 --- a/packages/2-sql-family/2-authoring/contract-psl/src/removed-field-presets.ts +++ b/packages/2-sql-family/2-authoring/contract-psl/src/removed-field-presets.ts @@ -63,10 +63,11 @@ function nanoid(id: boolean): readonly RemovedFieldPreset[] { const TEMPORAL_DATE_TIME = presetType('DateTime', { as: 'temporal' }); const STRING_DATE_TIME = presetType('DateTime', { as: 'string' }); +const SQLITE_DATETIME = 'sqlite/datetime@1'; const CREATED = '@onCreate(now())'; const UPDATED = '@onCreate(now()) @onUpdate(now())'; -/** The SQL family's removed field presets: each row applies only where its rewrite's type stores the preset's codec, and a row with no codec id writes a type alias only the targets with the preset contribute. A generated id preset is written with the target's top-level alias where it has one, and with the `sql.` alias elsewhere. A date and time preset is written with the convenience call that gives the explicit type it stored, where one does (ADR 279). */ +/** The SQL family's removed field presets: each row applies only where its rewrite's type stores the preset's codec. A generated id preset is written with the target's top-level alias where it has one, and with the `sql.` alias elsewhere. A date and time preset is written with the convenience call that gives the explicit type it stored, where one does (ADR 279). */ export const removedSqlFieldPresets: RemovedFieldPresets = { uuidString: idAlias('UuidString'), ulid: idAlias('Ulid'), @@ -93,11 +94,11 @@ export const removedSqlFieldPresets: RemovedFieldPresets = { 'id.uuidv7Native': removedPresetWrittenAs('Uuid', 'pg/uuid@1', '@id @onCreate(uuid(7))'), 'temporal.createdAt': [ ...removedPresetWrittenAs(TEMPORAL_DATE_TIME, 'pg/timestamptz-temporal@1', CREATED), - ...removedPresetWrittenAs('temporal.CreatedAt', undefined), + ...removedPresetWrittenAs('temporal.CreatedAt', SQLITE_DATETIME), ], 'temporal.updatedAt': [ ...removedPresetWrittenAs(TEMPORAL_DATE_TIME, 'pg/timestamptz-temporal@1', UPDATED), - ...removedPresetWrittenAs('temporal.UpdatedAt', undefined), + ...removedPresetWrittenAs('temporal.UpdatedAt', SQLITE_DATETIME), ], 'temporal.createdAtString': removedPresetWrittenAs( STRING_DATE_TIME, @@ -161,5 +162,5 @@ export const removedSqlFieldPresets: RemovedFieldPresets = { 'pg/timetz@1', true, ), - 'temporal.datetime': removedPresetWithPhases('DateTime', 'sqlite/datetime@1', false), + 'temporal.datetime': removedPresetWithPhases('DateTime', SQLITE_DATETIME, false), }; diff --git a/packages/2-sql-family/2-authoring/contract-psl/test/interpreter.removed-field-presets.test.ts b/packages/2-sql-family/2-authoring/contract-psl/test/interpreter.removed-field-presets.test.ts index 0e6e1b5df5e9..0b994d4e8abf 100644 --- a/packages/2-sql-family/2-authoring/contract-psl/test/interpreter.removed-field-presets.test.ts +++ b/packages/2-sql-family/2-authoring/contract-psl/test/interpreter.removed-field-presets.test.ts @@ -302,6 +302,18 @@ describe('a removed field preset in a SQL schema', () => { ).toEqual(removed('id.ulid()', 'Write sql.Ulid @id @onCreate(ulid()).')); }); + it('names no temporal alias where the schema declares DateTime, since the alias stores another codec than the preset', () => { + const result = interpretSqlContract( + 'model DateTime {\n id Int @id\n}\n\nmodel Event {\n id Int @id\n value temporal.createdAt()\n}\n', + postgresInput, + ); + expect( + result.ok ? [] : result.failure.diagnostics.map(({ code, message }) => ({ code, message })), + ).toEqual([ + { code: 'PSL_UNRESOLVED_REFERENCE', message: 'Cannot find type "temporal.createdAt"' }, + ]); + }); + it('keeps the binder message for a preset written without a call', () => { expect(diagnosticsOf('temporal.createdAt', postgresInput)).toEqual([ { code: 'PSL_UNRESOLVED_REFERENCE', message: 'Cannot find type "temporal.createdAt"' }, diff --git a/packages/2-sql-family/2-authoring/contract-ts/src/removed-field-presets.ts b/packages/2-sql-family/2-authoring/contract-ts/src/removed-field-presets.ts index cbbebbb3a77c..52a686e72866 100644 --- a/packages/2-sql-family/2-authoring/contract-ts/src/removed-field-presets.ts +++ b/packages/2-sql-family/2-authoring/contract-ts/src/removed-field-presets.ts @@ -22,10 +22,10 @@ interface RewriteType { readonly options: Readonly> | undefined; } -/** A removed field preset: its rewrite, named only where `type` resolves, and to `codecId` when one is given, so a row does not fire on a target the preset never existed on. */ +/** A removed field preset: its rewrite, named only where `type` resolves to `codecId`, the codec the preset stored, so a row does not fire on a target the preset never existed on. */ interface RemovedFieldPreset { readonly type: RewriteType; - readonly codecId: string | undefined; + readonly codecId: string; readonly rewrite: string; } @@ -50,11 +50,7 @@ function typeCall({ path, options }: RewriteType, precision?: string): string { } /** The row of a preset whose rewrite is the column of `type`, then `chain`. */ -function writtenAs( - type: RewriteType, - codecId: string | undefined, - chain = '', -): readonly RemovedFieldPreset[] { +function writtenAs(type: RewriteType, codecId: string, chain = ''): readonly RemovedFieldPreset[] { return [{ type, codecId, rewrite: `field.column(${typeCall(type)})${chain}` }]; } @@ -88,6 +84,7 @@ const withPhases = (type: RewriteType, codecId: string, precision: boolean) => [ }, ]; +const SQLITE_DATETIME = 'sqlite/datetime@1'; const CREATED = '.onCreate(gen.now())'; const UPDATED = '.onCreate(gen.now()).onUpdate(gen.now())'; const TEMPORAL_DATE_TIME = choice('DateTime', { as: 'temporal' }); @@ -133,11 +130,11 @@ export const PRESETS: Readonly> = 'id.uuidv7Native': writtenAs(explicit('Uuid'), 'pg/uuid@1', '.id().onCreate(gen.uuid(7))'), 'temporal.createdAt': [ ...writtenAs(TEMPORAL_DATE_TIME, 'pg/timestamptz-temporal@1', CREATED), - ...writtenAs(explicit('temporal.CreatedAt'), undefined), + ...writtenAs(explicit('temporal.CreatedAt'), SQLITE_DATETIME), ], 'temporal.updatedAt': [ ...writtenAs(TEMPORAL_DATE_TIME, 'pg/timestamptz-temporal@1', UPDATED), - ...writtenAs(explicit('temporal.UpdatedAt'), undefined), + ...writtenAs(explicit('temporal.UpdatedAt'), SQLITE_DATETIME), ], 'temporal.createdAtString': writtenAs(STRING_DATE_TIME, 'pg/timestamptz-string@1', CREATED), 'temporal.updatedAtString': writtenAs(STRING_DATE_TIME, 'pg/timestamptz-string@1', UPDATED), @@ -169,7 +166,7 @@ export const PRESETS: Readonly> = 'temporal.time': withPhases(choice('Time', { as: 'temporal' }), 'pg/time-temporal@1', true), 'temporal.timeString': withPhases(choice('Time'), 'pg/time-string@1', true), 'temporal.timetz': withPhases(choice('Time', { storage: 'timetz' }), 'pg/timetz@1', true), - 'temporal.datetime': withPhases(explicit('DateTime'), 'sqlite/datetime@1', false), + 'temporal.datetime': withPhases(explicit('DateTime'), SQLITE_DATETIME, false), }; /** The codec a column of `type` is stored with in `types`, a type choice resolved with its options as a column resolves it; `undefined` when `types` has no such type or the options choose none. */ @@ -201,8 +198,7 @@ function codecIdAt( } function appliesTo(preset: RemovedFieldPreset, types: AuthoringTypeNamespace): boolean { - const codecId = codecIdAt(types, preset.type.path, preset.type.options); - return codecId !== undefined && (preset.codecId === undefined || codecId === preset.codecId); + return codecIdAt(types, preset.type.path, preset.type.options) === preset.codecId; } function refuse(path: string, types: AuthoringTypeNamespace): never { diff --git a/packages/2-sql-family/2-authoring/contract-ts/test/removed-field-presets.test.ts b/packages/2-sql-family/2-authoring/contract-ts/test/removed-field-presets.test.ts index 0a0c395ea2b8..358a5a20020a 100644 --- a/packages/2-sql-family/2-authoring/contract-ts/test/removed-field-presets.test.ts +++ b/packages/2-sql-family/2-authoring/contract-ts/test/removed-field-presets.test.ts @@ -163,14 +163,13 @@ describe('a read of a removed field preset from the SQL field helpers', () => { expect(refusalOf(sqliteField, path)).toMatchObject(unknown(path)); }); - it('names the forms that replace presets where the type choice call stores another codec', () => { + it('names the forms that replace presets where the type choice call stores another codec, and temporal.UpdatedAt stores another codec than the preset', () => { const jsDateOnly = { ...postgresLike, DateTime: { ...postgresLike.DateTime, choices: [{ when: { as: 'date' }, type: 'TimestamptzJsDate' }], }, - temporal: {}, } as const; expect( refusalOf(refusingRemovedFieldPresets(field, jsDateOnly), 'temporal.updatedAt'), From fbae01c2214e0bbe7524539b827d8a97dad4469e Mon Sep 17 00:00:00 2001 From: willbot Date: Wed, 28 Oct 2026 15:35:43 +0100 Subject: [PATCH 116/118] A removed JS-Date preset with a precision is refused with DateTime, not its explicit type temporal.timestamptzJsDate(3, ...) is exactly DateTime, and (6, ...) DateTime(precision: 6); with no precision it stays TimestamptzJsDate, which DateTime cannot give. Co-Authored-By: Claude Opus 5.5 Signed-off-by: willbot Signed-off-by: Will Madden --- .../psl-parser/src/removed-field-presets.ts | 23 ++++++++++++++++--- .../contract-psl/src/removed-field-presets.ts | 1 + .../interpreter.removed-field-presets.test.ts | 6 +++-- .../9-facade/test/native-type-parity.test.ts | 2 +- .../preset-replacement-rows.ts | 2 +- 5 files changed, 27 insertions(+), 7 deletions(-) diff --git a/packages/1-framework/2-authoring/psl-parser/src/removed-field-presets.ts b/packages/1-framework/2-authoring/psl-parser/src/removed-field-presets.ts index 5d78fafe2a3c..2b8923eee474 100644 --- a/packages/1-framework/2-authoring/psl-parser/src/removed-field-presets.ts +++ b/packages/1-framework/2-authoring/psl-parser/src/removed-field-presets.ts @@ -66,13 +66,20 @@ export function removedPresetWrittenAs( ]; } +/** A convenience type that gives a row's explicit type for every written precision, and the precision it gives when none is written. */ +export interface RemovedPresetPrecisionType { + readonly type: RemovedPresetType; + readonly defaultPrecision: string; +} + /** - * The row of a preset that took `onCreate: now` and `onUpdate: now`, and a precision first when `takesPrecision`. Its rewrite writes `type`, with the precision as its `precision` argument and `@onCreate(now())` and `@onUpdate(now())` for the phases the call set. + * The row of a preset that took `onCreate: now` and `onUpdate: now`, and a precision first when `takesPrecision`. Its rewrite writes `type`, with the precision as its `precision` argument and `@onCreate(now())` and `@onUpdate(now())` for the phases the call set. When the call writes a precision and `withPrecision` is given, it writes `withPrecision.type` instead, leaving out a precision equal to its default. */ export function removedPresetWithPhases( type: RemovedPresetType | string, codecId: string, takesPrecision: boolean, + withPrecision?: RemovedPresetPrecisionType, ): readonly RemovedFieldPreset[] { const written = asType(type); const parameters = takesPrecision ? ['precision', ...PHASES] : [...PHASES]; @@ -93,8 +100,18 @@ export function removedPresetWithPhases( const attributes = PHASES.filter((phase) => args.has(phase)).map( (phase) => ` @${phase}(now())`, ); - const extra = precision === undefined ? undefined : `precision: ${precision}`; - return `Write ${writtenPresetType(written, extra)}${modifier}${attributes.join('')}.`; + const typeText = + precision === undefined + ? writtenPresetType(written) + : withPrecision === undefined + ? writtenPresetType(written, `precision: ${precision}`) + : writtenPresetType( + withPrecision.type, + precision === withPrecision.defaultPrecision + ? undefined + : `precision: ${precision}`, + ); + return `Write ${typeText}${modifier}${attributes.join('')}.`; }, }, ]; diff --git a/packages/2-sql-family/2-authoring/contract-psl/src/removed-field-presets.ts b/packages/2-sql-family/2-authoring/contract-psl/src/removed-field-presets.ts index 7f6f8b31f7f8..33d9287751d3 100644 --- a/packages/2-sql-family/2-authoring/contract-psl/src/removed-field-presets.ts +++ b/packages/2-sql-family/2-authoring/contract-psl/src/removed-field-presets.ts @@ -144,6 +144,7 @@ export const removedSqlFieldPresets: RemovedFieldPresets = { 'TimestamptzJsDate', 'pg/timestamptz-date@1', true, + { type: presetType('DateTime'), defaultPrecision: '3' }, ), 'temporal.date': removedPresetWithPhases( presetType('Date', { as: 'temporal' }), diff --git a/packages/2-sql-family/2-authoring/contract-psl/test/interpreter.removed-field-presets.test.ts b/packages/2-sql-family/2-authoring/contract-psl/test/interpreter.removed-field-presets.test.ts index 0b994d4e8abf..fad0862ddc4a 100644 --- a/packages/2-sql-family/2-authoring/contract-psl/test/interpreter.removed-field-presets.test.ts +++ b/packages/2-sql-family/2-authoring/contract-psl/test/interpreter.removed-field-presets.test.ts @@ -223,10 +223,12 @@ describe('a removed field preset in a SQL schema', () => { 'temporal.timestamptzString(3, onCreate: now, onUpdate: now)', 'Write DateTime(as: string, precision: 3) @onCreate(now()) @onUpdate(now()).', ], + ['temporal.timestamptzJsDate(3, onCreate: now)', 'Write DateTime @onCreate(now()).'], [ - 'temporal.timestamptzJsDate(3, onCreate: now)', - 'Write TimestamptzJsDate(precision: 3) @onCreate(now()).', + 'temporal.timestamptzJsDate(6, onCreate: now)', + 'Write DateTime(precision: 6) @onCreate(now()).', ], + ['temporal.timestamptzJsDate(onCreate: now)', 'Write TimestamptzJsDate @onCreate(now()).'], ['temporal.date(onCreate: now)', 'Write Date(as: temporal) @onCreate(now()).'], [ 'temporal.dateString(onCreate: now, onUpdate: now)', diff --git a/packages/3-targets/postgres/9-facade/test/native-type-parity.test.ts b/packages/3-targets/postgres/9-facade/test/native-type-parity.test.ts index f6cc9201abcb..10199b5ef6b7 100644 --- a/packages/3-targets/postgres/9-facade/test/native-type-parity.test.ts +++ b/packages/3-targets/postgres/9-facade/test/native-type-parity.test.ts @@ -413,7 +413,7 @@ describe('native types as bare scalar types — parity with the live bare-type p { code: 'PSL_UNRESOLVED_REFERENCE', message: - 'temporal.timestamptzJsDate(3, onCreate: now) was a field preset, and Prisma 8 removed field presets. Write TimestamptzJsDate(3) @onCreate(now()).', + 'temporal.timestamptzJsDate(3, onCreate: now) was a field preset, and Prisma 8 removed field presets. Write DateTime @onCreate(now()).', }, ]); }); diff --git a/test/integration/test/authoring/preset-replacements/preset-replacement-rows.ts b/test/integration/test/authoring/preset-replacements/preset-replacement-rows.ts index f339dfb835f7..21067fdea29c 100644 --- a/test/integration/test/authoring/preset-replacements/preset-replacement-rows.ts +++ b/test/integration/test/authoring/preset-replacements/preset-replacement-rows.ts @@ -396,7 +396,7 @@ export const postgresRows: readonly SqlRow[] = [ model: 'TimestamptzJsDateOnCreate', psl: { preset: 'temporal.timestamptzJsDate(3, onCreate: now)', - replacement: 'TimestamptzJsDate(3) @onCreate(now())', + replacement: 'DateTime @onCreate(now())', }, ts: ({ field, type, gen }) => field.column(type.TimestamptzJsDate(3)).onCreate(gen.now()), }, From 56e4aa49a03dae10f0a5133d9f31e089ed1e751f Mon Sep 17 00:00:00 2001 From: willbot Date: Wed, 28 Oct 2026 16:01:29 +0100 Subject: [PATCH 117/118] Hover fences a code span with more backticks than it holds, and the infinity advice keeps the column's other arguments Also moves a fixture doc comment back above its declaration, and the demo fixture writes DateTime(as: temporal). Co-Authored-By: Claude Opus 5.5 Signed-off-by: willbot Signed-off-by: Will Madden --- docs/reference/scalar-types.md | 2 +- .../test/fixtures/declaration-library.ts | 2 +- .../3-tooling/language-server/src/hover.ts | 12 ++++++++++-- .../test/hover.contributed-types.test.ts | 2 +- .../postgres/1-target/test/psl-infer/fixtures.ts | 4 ++-- 5 files changed, 15 insertions(+), 7 deletions(-) diff --git a/docs/reference/scalar-types.md b/docs/reference/scalar-types.md index 9396a8926087..802cf075e0c0 100644 --- a/docs/reference/scalar-types.md +++ b/docs/reference/scalar-types.md @@ -129,7 +129,7 @@ A literal default of a date, time, interval or bytes type is written with the ta The tools that write a schema for you write the convenience types, with only the arguments that differ from the defaults, and the explicit type only where no convenience call becomes it, such as `TimestampTemporal` for a `timestamp` read as `Temporal.PlainDateTime`. - `prisma orm init` writes `createdAt DateTime @default(now()) @onCreate(now())` and `updatedAt temporal.UpdatedAt` in a PSL schema, and `field.column(type.DateTime()).default(now()).onCreate(gen.now())` and `field.column(type.temporal.UpdatedAt())` in a TypeScript contract. -- `prisma contract infer` takes the default path for each column: a `timestamptz(3)` column is `DateTime`, a `timestamp(3)` column `DateTime(storage: timestamp)`, a `date` column `Date`, a `time(0)` column `Time(precision: 0)`, a `timetz` column `Time(storage: timetz)` and an `interval` column `Duration`. A column with no declared precision holds microseconds, as precision 6 does, so a `timestamptz` column is `DateTime(precision: 6)`; `db verify` and the migration planner count an omitted precision on a date or time column as 6. A `Date` holds milliseconds, so a timestamp column with more digits reads with them dropped, and a timestamp default with digits below a millisecond is written as its raw `sql` expression. To keep the digits, change the field to `DateTime(as: temporal)` or `DateTime(as: string)`. When the default-path type cannot read the column's own default as a row at all, as a `Date` cannot read `infinity` or `-infinity`, infer writes the first row, in the order the convenience type declares its rows, of the same storage whose type can, which for those values is `as: string`: a `timestamp(3)` column with `DEFAULT 'infinity'` is ``DateTime(as: string, storage: timestamp, precision: 3) @default(timestamp`infinity`)``. Infer reads only the default, not the rows: a row that stores `infinity` or `-infinity` in a column read as a `Date` fails to read with `RUNTIME.DECODE_FAILED`, so change such a field to `DateTime(as: string)`; `as: temporal` cannot hold those values either. +- `prisma contract infer` takes the default path for each column: a `timestamptz(3)` column is `DateTime`, a `timestamp(3)` column `DateTime(storage: timestamp)`, a `date` column `Date`, a `time(0)` column `Time(precision: 0)`, a `timetz` column `Time(storage: timetz)` and an `interval` column `Duration`. A column with no declared precision holds microseconds, as precision 6 does, so a `timestamptz` column is `DateTime(precision: 6)`; `db verify` and the migration planner count an omitted precision on a date or time column as 6. A `Date` holds milliseconds, so a timestamp column with more digits reads with them dropped, and a timestamp default with digits below a millisecond is written as its raw `sql` expression. To keep the digits, change the field to `DateTime(as: temporal)` or `DateTime(as: string)`. When the default-path type cannot read the column's own default as a row at all, as a `Date` cannot read `infinity` or `-infinity`, infer writes the first row, in the order the convenience type declares its rows, of the same storage whose type can, which for those values is `as: string`: a `timestamp(3)` column with `DEFAULT 'infinity'` is ``DateTime(as: string, storage: timestamp, precision: 3) @default(timestamp`infinity`)``. Infer reads only the default, not the rows: a row that stores `infinity` or `-infinity` in a column read as a `Date` fails to read with `RUNTIME.DECODE_FAILED`, so add `as: string` to such a field and keep its other arguments, such as `DateTime(as: string, storage: timestamp, precision: 3)` for a `timestamp(3)` column; `as: temporal` cannot hold those values either. - `prisma contract print` writes each column as the convenience call that becomes its explicit type exactly, such as `DateTime(as: temporal)` for `TimestamptzTemporal`, and otherwise the explicit type. - `prisma7Schema(...)` reads Prisma 7's `DateTime` as `TimestampJsDate(3)`, a `timestamp(3)` column read as a `Date` as Prisma 7 read it, printed `DateTime(storage: timestamp)`, and `@db.Timestamptz(p)` as `TimestamptzJsDate(p)`. diff --git a/examples/prisma-8-demo/test/fixtures/declaration-library.ts b/examples/prisma-8-demo/test/fixtures/declaration-library.ts index ea51d0756c36..07701eeb6198 100644 --- a/examples/prisma-8-demo/test/fixtures/declaration-library.ts +++ b/examples/prisma-8-demo/test/fixtures/declaration-library.ts @@ -45,7 +45,7 @@ export const labelled = (labels: readonly string[]) => ); export const unexpired = (now: Temporal.Instant) => - client.fragment({ expiresAt: field.column(type.TimestamptzTemporal()) }, (rows) => + client.fragment({ expiresAt: field.column(type.DateTime({ as: 'temporal' })) }, (rows) => rows.where((row) => row.expiresAt.gt(now)).orderBy((row) => row.expiresAt.asc()), ); diff --git a/packages/1-framework/3-tooling/language-server/src/hover.ts b/packages/1-framework/3-tooling/language-server/src/hover.ts index fa96c4c8c936..78e2866c2d65 100644 --- a/packages/1-framework/3-tooling/language-server/src/hover.ts +++ b/packages/1-framework/3-tooling/language-server/src/hover.ts @@ -197,7 +197,7 @@ function renderChosenType( chosen.ok && writtenChosenType(symbol, chosen.value.type, chosen.value.args, input.dataTypes); if (written === false || written === undefined) return undefined; const callText = call === undefined ? symbol.path.join('.') : printTypeConstructorCall(call); - return `\`${callText}\` is exactly \`${written}\`.`; + return `${inlineCode(callText)} is exactly ${inlineCode(written)}.`; } /** Each combination of a type choice's options, as written, and the explicit type it becomes. */ @@ -217,7 +217,7 @@ function renderTypeChoices( : undefined, ); const written = writtenChosenType(symbol, row.type, args, dataTypes); - return `\`${written ?? `${[...symbol.path.slice(0, -1), row.type].join('.')}(...)`}\``; + return inlineCode(written ?? `${[...symbol.path.slice(0, -1), row.type].join('.')}(...)`); }; return [ `| ${[...options.map((option) => option.name), 'Becomes'].join(' | ')} |`, @@ -229,6 +229,14 @@ function renderTypeChoices( ].join('\n'); } +/** `text` as a Markdown code span, fenced by more backticks than any run inside it. */ +function inlineCode(text: string): string { + const longestRun = Math.max(0, ...(text.match(/`+/g) ?? []).map((run) => run.length)); + const fence = '`'.repeat(longestRun + 1); + const padding = text.startsWith('`') || text.endsWith('`') || longestRun > 0 ? ' ' : ''; + return `${fence}${padding}${text}${padding}${fence}`; +} + function renderParameterContent(symbol: ParameterSymbol): string { const fence = ['```prisma', namedParameterText(symbol.name, symbol.param.type), '```'].join('\n'); return withDocumentation(fence, symbol.param.documentation); diff --git a/packages/1-framework/3-tooling/language-server/test/hover.contributed-types.test.ts b/packages/1-framework/3-tooling/language-server/test/hover.contributed-types.test.ts index 011c8f5875d5..59862a268d13 100644 --- a/packages/1-framework/3-tooling/language-server/test/hover.contributed-types.test.ts +++ b/packages/1-framework/3-tooling/language-server/test/hover.contributed-types.test.ts @@ -97,7 +97,7 @@ describe('providePslHover', () => { it('writes each argument of the explicit type as a literal of its data type', () => { const value = hover('model Product {\n shape pg.Sha|pe\n}')?.contents; expect(value).toMatchObject({ - value: expect.stringContaining('`pg.Shape` is exactly `pg.Raw(sql: sql`text`)`.'), + value: expect.stringContaining('`pg.Shape` is exactly `` pg.Raw(sql: sql`text`) ``.'), }); }); diff --git a/packages/3-targets/postgres/1-target/test/psl-infer/fixtures.ts b/packages/3-targets/postgres/1-target/test/psl-infer/fixtures.ts index c05190341a10..56d3b5127b89 100644 --- a/packages/3-targets/postgres/1-target/test/psl-infer/fixtures.ts +++ b/packages/3-targets/postgres/1-target/test/psl-infer/fixtures.ts @@ -215,12 +215,12 @@ export function treeFromFlat(schemaIR: SqlSchemaIR): PostgresDatabaseSchemaNode }); } +const inferCodecLookup = createPostgresBuiltinCodecLookup(); + /** * The stack `contract emit` reads an inferred schema with: the type constructors the family and the * adapter contribute, the target's own, and the target's codecs and data types. */ -const inferCodecLookup = createPostgresBuiltinCodecLookup(); - export const inferBuildContext: SqlPslBuildContext = { authoringContributions: { type: { From 6ef1cbd92ba02b8108d195bc8e5504254efcc748 Mon Sep 17 00:00:00 2001 From: willbot Date: Wed, 28 Oct 2026 16:49:26 +0100 Subject: [PATCH 118/118] The emit parity fixture writes DateTime(as: temporal), and the move journey plans a migration with no operations Main now writes and applies a plan with no operations instead of refusing it, so the codec-only move needs no migration new step. Co-Authored-By: Claude Opus 5.5 Signed-off-by: willbot Signed-off-by: Will Madden --- .../convenience-date-time-types-move.e2e.test.ts | 11 ++--------- .../fixtures/emit-command/schema.parity.psl | 2 +- .../convenience-date-time-types/app/instructions.md | 2 +- 3 files changed, 4 insertions(+), 11 deletions(-) diff --git a/test/integration/test/cli-journeys/convenience-date-time-types-move.e2e.test.ts b/test/integration/test/cli-journeys/convenience-date-time-types-move.e2e.test.ts index 52618f2ce900..138d6d27585f 100644 --- a/test/integration/test/cli-journeys/convenience-date-time-types-move.e2e.test.ts +++ b/test/integration/test/cli-journeys/convenience-date-time-types-move.e2e.test.ts @@ -1,5 +1,5 @@ /** - * Journey: a schema that wrote the Temporal types under their explicit names moves its fields to the convenience types' defaults, as the upgrade instruction describes. `Date` and `Time` keep the `date` and `time(0)` columns and change only the codec: `migration plan` refuses to plan nothing and names `migration new --from `, which writes a migration with no operations, and `db update` applies no operations and moves the marker. `DateTime` makes the `timestamptz` column `timestamptz(3)`, which can round stored values, so both `migration plan` and `db update` ask for `--delete Event.at` before they alter it. + * Journey: a schema that wrote the Temporal types under their explicit names moves its fields to the convenience types' defaults, as the upgrade instruction describes. `Date` and `Time` keep the `date` and `time(0)` columns and change only the codec: `migration plan` writes a migration with no operations, and `db update` applies no operations and moves the marker. `DateTime` makes the `timestamptz` column `timestamptz(3)`, which can round stored values, so both `migration plan` and `db update` ask for `--delete Event.at` before they alter it. */ import { readFileSync, writeFileSync } from 'node:fs'; import { join } from 'pathe'; @@ -13,7 +13,6 @@ import { runDbUpdate, runDbVerify, runMigrate, - runMigrationNew, runMigrationPlan, setupJourney, timeouts, @@ -75,13 +74,7 @@ withTempDir(({ createTempDir }) => { expect(applied.exitCode, output(applied)).toBe(0); const codecOnly = await emit(ctx, CODEC_ONLY); - const refused = await runMigrationPlan(ctx, ['--from', explicit, '--name', 'codecs']); - expect(refused.exitCode).toBe(2); - expect(output(refused)).toContain( - 'The contract changed, but migration plan found nothing to change in the database.', - ); - expect(output(refused)).toContain(`prisma migration new --from ${explicit}`); - const empty = await runMigrationNew(ctx, ['--from', explicit, '--name', 'codecs']); + const empty = await runMigrationPlan(ctx, ['--from', explicit, '--name', 'codecs']); expect(empty.exitCode, output(empty)).toBe(0); const appliedEmpty = await runMigrate(ctx); expect(appliedEmpty.exitCode, output(appliedEmpty)).toBe(0); diff --git a/test/integration/test/fixtures/cli/cli-integration-test-app/fixtures/emit-command/schema.parity.psl b/test/integration/test/fixtures/cli/cli-integration-test-app/fixtures/emit-command/schema.parity.psl index 5407e408d2b0..0964438a54de 100644 --- a/test/integration/test/fixtures/cli/cli-integration-test-app/fixtures/emit-command/schema.parity.psl +++ b/test/integration/test/fixtures/cli/cli-integration-test-app/fixtures/emit-command/schema.parity.psl @@ -14,7 +14,7 @@ model User { id Int @id @default(autoincrement()) email Email @unique role Role - createdAt DateTime @default(now()) + createdAt DateTime(as: temporal) @default(now()) isActive Boolean @default(true) profile Jsonb? @@map("user") diff --git a/upgrade-instructions/pending/convenience-date-time-types/app/instructions.md b/upgrade-instructions/pending/convenience-date-time-types/app/instructions.md index 2c26d5cc131e..37b3ccc45ab0 100644 --- a/upgrade-instructions/pending/convenience-date-time-types/app/instructions.md +++ b/upgrade-instructions/pending/convenience-date-time-types/app/instructions.md @@ -55,7 +55,7 @@ A type alias written with one of the names, such as `types { Stamp = DateTime @o To move a field to the new defaults instead, keep the name, update the application code that reads the field, and run `prisma contract emit`. What the database needs then depends on the type: - **`Date` and `Time`** keep their `date` and `time(p)` columns, when a precision is written by name (`Time(precision: 0)` for `Time(0)`), and change only the codec, so the database does not change, but the contract hash does. - - With migrations: `prisma migration plan` refuses, because it finds nothing to change, and names the command to run instead. Run `prisma migration new --from ` with the hash it gives, which writes a migration with no operations, then `prisma db migrate`. + - With migrations: `prisma migration plan` writes a migration with no operations, then `prisma db migrate` applies it and moves the marker. - With `prisma db update`: run it. It applies no operations and records the new contract on the database. - **`DateTime`** makes the column `timestamptz(3)`, and a schema that wrote `DateTime` before had a `timestamptz` column, which holds microseconds. Lowering the precision rounds stored values to milliseconds, so both commands ask before they change it. Run `prisma migration plan --delete .` and then `prisma db migrate`, or `prisma db update --delete .`, which accepts the rounding. A JavaScript `Date` holds milliseconds only, so to keep microseconds, keep the `Temporal.Instant` field: write `DateTime(as: temporal)`, which is exactly `TimestamptzTemporal`.