@drzl/analyzer 1.18.0 → 1.20.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.d.cts CHANGED
@@ -8,28 +8,91 @@ interface Issue {
8
8
  level: 'info' | 'warn' | 'error';
9
9
  message: string;
10
10
  hint?: string;
11
+ /**
12
+ * Where the issue is, as `table` or `table.column`.
13
+ *
14
+ * Declared since this interface existed and set by nothing, so every consumer wanting to group
15
+ * warnings by table had to read the names back out of the English in `message`. `drzl doctor` is
16
+ * the first such consumer and a report built by regex over prose breaks the first time a message
17
+ * is reworded, so the names are stated here instead.
18
+ *
19
+ * Still optional: an issue about the schema as a whole, such as an unidentifiable dialect, is
20
+ * about no table and says so by omitting this.
21
+ */
11
22
  path?: string;
12
23
  }
24
+ /**
25
+ * Why a column has no type, where "nobody has modelled it" is not the answer.
26
+ *
27
+ * The distinction exists because it changes the advice. A column class this file has no arm for is
28
+ * a gap someone can close, and the warning tells its author to say so. A Gel temporal column is
29
+ * not: the value is an instance of a class from the `gel` package, DRZL cannot import that package,
30
+ * and no generator could emit a check for it even knowing the name. Leaving it `unknown` is the
31
+ * measured answer rather than an omission, and its warning should say that instead of asking for a
32
+ * bug report that is already closed.
33
+ */
34
+ type UnnameableReason = 'gel-temporal';
13
35
  interface ColumnRef {
14
36
  table: string;
15
37
  column: string;
16
38
  }
39
+ /**
40
+ * A link between two tables, each end named by `qualifiedTableName`.
41
+ *
42
+ * Qualified rather than bare, because a bare database name identifies a table only while no two
43
+ * SQL schemas hold it: `to: 'users'` cannot say whether it means `public.users` or
44
+ * `reporting.users`, and every consumer resolves these strings back to a table object. A table in
45
+ * the default schema has no prefix, so nothing about a single-schema analysis changes.
46
+ */
17
47
  interface Relation {
18
48
  kind: 'one' | 'many' | 'manyToMany';
49
+ /** Qualified table name: `users`, or `reporting.users`. */
19
50
  from: string;
51
+ /** Qualified table name: `users`, or `reporting.users`. */
20
52
  to: string;
53
+ /** Qualified name of the join table, for m2m. */
21
54
  via?: string;
22
55
  }
23
56
  interface Column {
24
57
  name: string;
25
58
  tsType: string;
59
+ /**
60
+ * A coarse label for the column's kind, not its type.
61
+ *
62
+ * `varchar`, `char` and `text` are all `TEXT` here, deliberately: exactly one consumer reads
63
+ * this, `isIntegerColumn`, and it only asks whether a number is whole. Use `sqlType` for the
64
+ * question this name suggests it answers.
65
+ */
26
66
  dbType: string;
67
+ /**
68
+ * The column's type as the database declares it, from Drizzle's own `getSQLType()`:
69
+ * `varchar(255)`, `numeric(10, 2)`, `timestamp with time zone`, `text[]`, or an enum's type
70
+ * name.
71
+ *
72
+ * The one fact about a column that no validator schema can carry, and the one every other fact
73
+ * here is a consequence of. A generator that emits metadata beside its schemas has nothing else
74
+ * to put in it: `dbType` is a label rather than a type, and the declared width lives in
75
+ * `maxLength`, the precision in `min`/`max`, and neither of those says which type produced it.
76
+ *
77
+ * The two Drizzle majors disagree about an array and are reconciled here, measured rather than
78
+ * assumed: 0.4x wraps the column in a `PgArray` whose own answer is already `text[]`, while v1
79
+ * leaves the class alone and raises `dimensions`, so its answer is the bare `text`. The suffix
80
+ * is added from `arrayDimensions` when the type does not already carry one, so a consumer
81
+ * cannot tell which major produced its metadata.
82
+ *
83
+ * Absent where the builder has no `getSQLType` or it throws. Nothing is guessed from the class
84
+ * name: an invented type string reads exactly like a real one, and there is no way for a
85
+ * consumer to tell them apart.
86
+ */
87
+ sqlType?: string;
27
88
  nullable: boolean;
28
89
  hasDefault: boolean;
29
90
  isGenerated: boolean;
30
91
  defaultExpression?: string;
31
92
  references?: {
32
93
  table: string;
94
+ /** SQL schema of the referenced table, absent for the default one. See `ForeignKey`. */
95
+ schema?: string;
33
96
  column: string;
34
97
  onDelete?: string;
35
98
  onUpdate?: string;
@@ -48,8 +111,16 @@ interface Column {
48
111
  *
49
112
  * Strings rather than numbers because a 64 bit bound is not representable as a JS number:
50
113
  * `9223372036854775807` rounds to `9223372036854775808` the moment it becomes one, so a
51
- * numeric field here would silently emit a wrong bound. Absent for floats and for `numeric`,
52
- * which have no integer range.
114
+ * numeric field here would silently emit a wrong bound. A 20 digit `numeric(20,0)` bound is
115
+ * further past that again.
116
+ *
117
+ * Not an integer range, despite the name this field used to be described by. An inexact column
118
+ * carries one too: a `real` is bounded by the magnitude the database refuses past, and a
119
+ * `numeric(10,2)` by the width its own declaration states. `integer` says which kind it is, and
120
+ * saying it is what stops a bounded float schema refusing 1.5.
121
+ *
122
+ * Absent where nothing declares a bound: an 8 byte float holds every finite JS number, and a
123
+ * `numeric` with no precision holds arbitrary precision.
53
124
  */
54
125
  min?: string;
55
126
  max?: string;
@@ -62,6 +133,42 @@ interface Column {
62
133
  * generators fall back to the old inference there.
63
134
  */
64
135
  integer?: boolean;
136
+ /**
137
+ * Whether the column stores and returns `NaN`, and whether it does the same for an infinity.
138
+ *
139
+ * A range cannot say this. `>=`/`<=` refuses `Infinity` whatever the two numbers are, and `NaN`
140
+ * compares false against both ends, so a bounded float column described by its range alone
141
+ * refused three values Postgres stores in it and hands back on SELECT. That is a read-path
142
+ * defect: every read of such a row fails validation on a column behaving exactly as documented.
143
+ * The generators render these as a union beside the range rather than as a wider range.
144
+ *
145
+ * Measured against PostgreSQL 18.3 through PGlite, on the bound-parameter path a validator
146
+ * guards. `real` and `double precision` return all three unchanged. An unconstrained `numeric`
147
+ * does too, but a `numeric(10,2)` refuses either infinity with `22003 numeric field overflow`
148
+ * while still taking `NaN`, and `integer`/`bigint` refuse all three.
149
+ *
150
+ * So `numeric` in `{ mode: 'number' }` answers each of the two separately: `allowsNaN` at any
151
+ * width, and `allowsInfinity` only where the declaration carries no precision. That used to be a
152
+ * flat `false`, and the recorded reason was that nothing here read a column's precision or scale,
153
+ * so the two declarations were indistinguishable and the narrower answer was the safer one.
154
+ * `declaredDecimalRange` reads both numbers now, the two are distinguishable, and each says what
155
+ * its own server does.
156
+ *
157
+ * Postgres and Gel. MySQL refuses all three on a `float`/`double`, and on a `decimal` refuses
158
+ * them outright rather than storing `0.00`: measured on MySQL 8.4.11 in `STRICT_TRANS_TABLES`,
159
+ * all three answer `Incorrect decimal value`. SQLite returns both infinities and silently turns
160
+ * `NaN` into NULL, which is real and is filed on its own: a column needs both halves of that
161
+ * answer or none.
162
+ *
163
+ * Gel joined on a measurement of its own rather than on being Postgres-backed: a live Gel 7.1
164
+ * stored `nan`, `inf` and `-inf` in both `std::float32` and `std::float64` and handed all three
165
+ * back unchanged, through a cast and through a stored property.
166
+ *
167
+ * Absent on every other column, including the string mode of `numeric`, which already carries the
168
+ * same fact as a pattern; see `COLUMN_FORMATS.numeric` in `@drzl/validation-core`.
169
+ */
170
+ allowsNaN?: boolean;
171
+ allowsInfinity?: boolean;
65
172
  /**
66
173
  * A string column whose contents have a shape the database enforces.
67
174
  *
@@ -228,6 +335,17 @@ interface ForeignKey {
228
335
  name?: string;
229
336
  columns: string[];
230
337
  foreignTable: string;
338
+ /**
339
+ * The SQL schema the referenced table lives in, absent for the default one, exactly as
340
+ * `Table.schema` is.
341
+ *
342
+ * `foreignTable` is a bare database name and Postgres lets two schemas hold the same one, so a
343
+ * key pointing at `reporting.users` recorded the identical string a key pointing at
344
+ * `public.users` records. Every consumer that resolves a key back to a table object did so by
345
+ * that string, and therefore resolved to whichever of the two it saw first. Use
346
+ * `qualifiedForeignTable` rather than reading the two fields apart.
347
+ */
348
+ foreignSchema?: string;
231
349
  foreignColumns: string[];
232
350
  onDelete?: string;
233
351
  onUpdate?: string;
@@ -235,6 +353,15 @@ interface ForeignKey {
235
353
  interface Table {
236
354
  name: string;
237
355
  tsName: string;
356
+ /**
357
+ * The SQL schema the table was declared in, from `pgSchema('reporting').table(...)` and the
358
+ * MySQL and SingleStore equivalents. Absent for a table declared with plain `pgTable`, which is
359
+ * the only spelling of the default schema there is: Drizzle refuses `pgSchema('public')`
360
+ * outright, with "Postgres is using public schema by default".
361
+ *
362
+ * `name` stays bare, so two tables in two schemas share one. `qualifiedTableName` is what tells
363
+ * them apart, and is what every name-addressed surface in DRZL matches against.
364
+ */
238
365
  schema?: string;
239
366
  columns: Column[];
240
367
  primaryKey?: Key;
@@ -254,6 +381,28 @@ interface Enum {
254
381
  name: string;
255
382
  values: string[];
256
383
  }
384
+ /**
385
+ * The one name that identifies a table across every SQL schema in an analysis.
386
+ *
387
+ * `reporting.users` where the table names a schema, and the bare `users` where it does not. The
388
+ * bare form for the default schema is deliberate and is what makes this safe to reach for
389
+ * everywhere: on a schema module that never calls `pgSchema`, and that is nearly all of them, this
390
+ * returns exactly `table.name`, so every file name, every export, every config pattern and every
391
+ * emitted path is byte for byte what it was.
392
+ *
393
+ * `public.users` is not produced here. Drizzle refuses `pgSchema('public')`, so no analysis can
394
+ * ever carry `schema: 'public'`, and a table with no schema *is* the public one. `public.` exists
395
+ * only as a spelling a config may use, resolved by `@drzl/cli`.
396
+ */
397
+ declare function qualifiedTableName(table: {
398
+ name: string;
399
+ schema?: string;
400
+ }): string;
401
+ /** The same name, for the far end of a foreign key. */
402
+ declare function qualifiedForeignTable(fk: {
403
+ foreignTable: string;
404
+ foreignSchema?: string;
405
+ }): string;
257
406
  interface Analysis {
258
407
  drizzleVersion?: string;
259
408
  dialect: Dialect;
@@ -451,6 +600,31 @@ declare class SchemaAnalyzer {
451
600
  * `number double` on drizzle v1, which is where these pairings come from.
452
601
  */
453
602
  private static readonly INEXACT_RANGES;
603
+ /**
604
+ * The Postgres number columns that hold a non-finite double, and which of the three each holds.
605
+ *
606
+ * The class-name half of what `describeV1Column` reads off the codec, and the two must agree: a
607
+ * fact stated on one path and not the other is a schema that changes when the user upgrades
608
+ * drizzle, which the cross-major diff in `verify-packed.sh` fails on. These three class names are
609
+ * the same on both majors, read off real `pgTable` columns on 0.45.2 and on 1.0.0-rc.4, so this
610
+ * table also answers for a v1 column and the two answers are identical rather than merely
611
+ * compatible. non-finite-numbers.spec.ts asserts that agreement through the real analyzer.
612
+ *
613
+ * No MySQL, SingleStore or SQLite class belongs here: MySQL refuses all three on a `float`/
614
+ * `double` and stores `0.00` for a `decimal`, and SQLite returns both infinities while silently
615
+ * turning `NaN` into NULL, which is a different answer that has to arrive whole.
616
+ *
617
+ * Gel does belong, and is the fourth and fifth entries. Measured on a live Gel 7.1 rather than
618
+ * inferred from it being Postgres-backed: both `std::float32` and `std::float64` stored `nan`,
619
+ * `inf` and `-inf` and handed all three back as `NaN`, `Infinity` and `-Infinity`, through a cast
620
+ * and again through a stored property. Without them every row of such a column failed validation.
621
+ *
622
+ * `PgNumeric` is absent because its value is a string, and its pattern already accepts `NaN` and
623
+ * `Infinity`. `PgNumericNumber` is absent because its answer is no longer flat: it takes `NaN` at
624
+ * any width and an infinity only where no precision is declared, which is a per-column question
625
+ * this table cannot ask. `columnConstraints` answers it beside the bound that decides it.
626
+ */
627
+ private static readonly PG_NON_FINITE;
454
628
  /**
455
629
  * Constraints the column definition already carries, which the analysis used to throw away.
456
630
  *
@@ -463,4 +637,4 @@ declare class SchemaAnalyzer {
463
637
  analyze(opts?: AnalyzeOptions): Promise<Analysis>;
464
638
  }
465
639
 
466
- export { type Analysis, type AnalyzeOptions, type Check, type Column, type ColumnRef, type ColumnShape, type Dialect, type Enum, type ForeignKey, type Index, type Issue, type Key, type Relation, SchemaAnalyzer, type Table, SchemaAnalyzer as default, describeV1Column, isDrizzleView, isReadOnlyRelation, isRelationsV2, readRelationsV2 };
640
+ export { type Analysis, type AnalyzeOptions, type Check, type Column, type ColumnRef, type ColumnShape, type Dialect, type Enum, type ForeignKey, type Index, type Issue, type Key, type Relation, SchemaAnalyzer, type Table, type UnnameableReason, SchemaAnalyzer as default, describeV1Column, isDrizzleView, isReadOnlyRelation, isRelationsV2, qualifiedForeignTable, qualifiedTableName, readRelationsV2 };
package/dist/index.d.ts CHANGED
@@ -8,28 +8,91 @@ interface Issue {
8
8
  level: 'info' | 'warn' | 'error';
9
9
  message: string;
10
10
  hint?: string;
11
+ /**
12
+ * Where the issue is, as `table` or `table.column`.
13
+ *
14
+ * Declared since this interface existed and set by nothing, so every consumer wanting to group
15
+ * warnings by table had to read the names back out of the English in `message`. `drzl doctor` is
16
+ * the first such consumer and a report built by regex over prose breaks the first time a message
17
+ * is reworded, so the names are stated here instead.
18
+ *
19
+ * Still optional: an issue about the schema as a whole, such as an unidentifiable dialect, is
20
+ * about no table and says so by omitting this.
21
+ */
11
22
  path?: string;
12
23
  }
24
+ /**
25
+ * Why a column has no type, where "nobody has modelled it" is not the answer.
26
+ *
27
+ * The distinction exists because it changes the advice. A column class this file has no arm for is
28
+ * a gap someone can close, and the warning tells its author to say so. A Gel temporal column is
29
+ * not: the value is an instance of a class from the `gel` package, DRZL cannot import that package,
30
+ * and no generator could emit a check for it even knowing the name. Leaving it `unknown` is the
31
+ * measured answer rather than an omission, and its warning should say that instead of asking for a
32
+ * bug report that is already closed.
33
+ */
34
+ type UnnameableReason = 'gel-temporal';
13
35
  interface ColumnRef {
14
36
  table: string;
15
37
  column: string;
16
38
  }
39
+ /**
40
+ * A link between two tables, each end named by `qualifiedTableName`.
41
+ *
42
+ * Qualified rather than bare, because a bare database name identifies a table only while no two
43
+ * SQL schemas hold it: `to: 'users'` cannot say whether it means `public.users` or
44
+ * `reporting.users`, and every consumer resolves these strings back to a table object. A table in
45
+ * the default schema has no prefix, so nothing about a single-schema analysis changes.
46
+ */
17
47
  interface Relation {
18
48
  kind: 'one' | 'many' | 'manyToMany';
49
+ /** Qualified table name: `users`, or `reporting.users`. */
19
50
  from: string;
51
+ /** Qualified table name: `users`, or `reporting.users`. */
20
52
  to: string;
53
+ /** Qualified name of the join table, for m2m. */
21
54
  via?: string;
22
55
  }
23
56
  interface Column {
24
57
  name: string;
25
58
  tsType: string;
59
+ /**
60
+ * A coarse label for the column's kind, not its type.
61
+ *
62
+ * `varchar`, `char` and `text` are all `TEXT` here, deliberately: exactly one consumer reads
63
+ * this, `isIntegerColumn`, and it only asks whether a number is whole. Use `sqlType` for the
64
+ * question this name suggests it answers.
65
+ */
26
66
  dbType: string;
67
+ /**
68
+ * The column's type as the database declares it, from Drizzle's own `getSQLType()`:
69
+ * `varchar(255)`, `numeric(10, 2)`, `timestamp with time zone`, `text[]`, or an enum's type
70
+ * name.
71
+ *
72
+ * The one fact about a column that no validator schema can carry, and the one every other fact
73
+ * here is a consequence of. A generator that emits metadata beside its schemas has nothing else
74
+ * to put in it: `dbType` is a label rather than a type, and the declared width lives in
75
+ * `maxLength`, the precision in `min`/`max`, and neither of those says which type produced it.
76
+ *
77
+ * The two Drizzle majors disagree about an array and are reconciled here, measured rather than
78
+ * assumed: 0.4x wraps the column in a `PgArray` whose own answer is already `text[]`, while v1
79
+ * leaves the class alone and raises `dimensions`, so its answer is the bare `text`. The suffix
80
+ * is added from `arrayDimensions` when the type does not already carry one, so a consumer
81
+ * cannot tell which major produced its metadata.
82
+ *
83
+ * Absent where the builder has no `getSQLType` or it throws. Nothing is guessed from the class
84
+ * name: an invented type string reads exactly like a real one, and there is no way for a
85
+ * consumer to tell them apart.
86
+ */
87
+ sqlType?: string;
27
88
  nullable: boolean;
28
89
  hasDefault: boolean;
29
90
  isGenerated: boolean;
30
91
  defaultExpression?: string;
31
92
  references?: {
32
93
  table: string;
94
+ /** SQL schema of the referenced table, absent for the default one. See `ForeignKey`. */
95
+ schema?: string;
33
96
  column: string;
34
97
  onDelete?: string;
35
98
  onUpdate?: string;
@@ -48,8 +111,16 @@ interface Column {
48
111
  *
49
112
  * Strings rather than numbers because a 64 bit bound is not representable as a JS number:
50
113
  * `9223372036854775807` rounds to `9223372036854775808` the moment it becomes one, so a
51
- * numeric field here would silently emit a wrong bound. Absent for floats and for `numeric`,
52
- * which have no integer range.
114
+ * numeric field here would silently emit a wrong bound. A 20 digit `numeric(20,0)` bound is
115
+ * further past that again.
116
+ *
117
+ * Not an integer range, despite the name this field used to be described by. An inexact column
118
+ * carries one too: a `real` is bounded by the magnitude the database refuses past, and a
119
+ * `numeric(10,2)` by the width its own declaration states. `integer` says which kind it is, and
120
+ * saying it is what stops a bounded float schema refusing 1.5.
121
+ *
122
+ * Absent where nothing declares a bound: an 8 byte float holds every finite JS number, and a
123
+ * `numeric` with no precision holds arbitrary precision.
53
124
  */
54
125
  min?: string;
55
126
  max?: string;
@@ -62,6 +133,42 @@ interface Column {
62
133
  * generators fall back to the old inference there.
63
134
  */
64
135
  integer?: boolean;
136
+ /**
137
+ * Whether the column stores and returns `NaN`, and whether it does the same for an infinity.
138
+ *
139
+ * A range cannot say this. `>=`/`<=` refuses `Infinity` whatever the two numbers are, and `NaN`
140
+ * compares false against both ends, so a bounded float column described by its range alone
141
+ * refused three values Postgres stores in it and hands back on SELECT. That is a read-path
142
+ * defect: every read of such a row fails validation on a column behaving exactly as documented.
143
+ * The generators render these as a union beside the range rather than as a wider range.
144
+ *
145
+ * Measured against PostgreSQL 18.3 through PGlite, on the bound-parameter path a validator
146
+ * guards. `real` and `double precision` return all three unchanged. An unconstrained `numeric`
147
+ * does too, but a `numeric(10,2)` refuses either infinity with `22003 numeric field overflow`
148
+ * while still taking `NaN`, and `integer`/`bigint` refuse all three.
149
+ *
150
+ * So `numeric` in `{ mode: 'number' }` answers each of the two separately: `allowsNaN` at any
151
+ * width, and `allowsInfinity` only where the declaration carries no precision. That used to be a
152
+ * flat `false`, and the recorded reason was that nothing here read a column's precision or scale,
153
+ * so the two declarations were indistinguishable and the narrower answer was the safer one.
154
+ * `declaredDecimalRange` reads both numbers now, the two are distinguishable, and each says what
155
+ * its own server does.
156
+ *
157
+ * Postgres and Gel. MySQL refuses all three on a `float`/`double`, and on a `decimal` refuses
158
+ * them outright rather than storing `0.00`: measured on MySQL 8.4.11 in `STRICT_TRANS_TABLES`,
159
+ * all three answer `Incorrect decimal value`. SQLite returns both infinities and silently turns
160
+ * `NaN` into NULL, which is real and is filed on its own: a column needs both halves of that
161
+ * answer or none.
162
+ *
163
+ * Gel joined on a measurement of its own rather than on being Postgres-backed: a live Gel 7.1
164
+ * stored `nan`, `inf` and `-inf` in both `std::float32` and `std::float64` and handed all three
165
+ * back unchanged, through a cast and through a stored property.
166
+ *
167
+ * Absent on every other column, including the string mode of `numeric`, which already carries the
168
+ * same fact as a pattern; see `COLUMN_FORMATS.numeric` in `@drzl/validation-core`.
169
+ */
170
+ allowsNaN?: boolean;
171
+ allowsInfinity?: boolean;
65
172
  /**
66
173
  * A string column whose contents have a shape the database enforces.
67
174
  *
@@ -228,6 +335,17 @@ interface ForeignKey {
228
335
  name?: string;
229
336
  columns: string[];
230
337
  foreignTable: string;
338
+ /**
339
+ * The SQL schema the referenced table lives in, absent for the default one, exactly as
340
+ * `Table.schema` is.
341
+ *
342
+ * `foreignTable` is a bare database name and Postgres lets two schemas hold the same one, so a
343
+ * key pointing at `reporting.users` recorded the identical string a key pointing at
344
+ * `public.users` records. Every consumer that resolves a key back to a table object did so by
345
+ * that string, and therefore resolved to whichever of the two it saw first. Use
346
+ * `qualifiedForeignTable` rather than reading the two fields apart.
347
+ */
348
+ foreignSchema?: string;
231
349
  foreignColumns: string[];
232
350
  onDelete?: string;
233
351
  onUpdate?: string;
@@ -235,6 +353,15 @@ interface ForeignKey {
235
353
  interface Table {
236
354
  name: string;
237
355
  tsName: string;
356
+ /**
357
+ * The SQL schema the table was declared in, from `pgSchema('reporting').table(...)` and the
358
+ * MySQL and SingleStore equivalents. Absent for a table declared with plain `pgTable`, which is
359
+ * the only spelling of the default schema there is: Drizzle refuses `pgSchema('public')`
360
+ * outright, with "Postgres is using public schema by default".
361
+ *
362
+ * `name` stays bare, so two tables in two schemas share one. `qualifiedTableName` is what tells
363
+ * them apart, and is what every name-addressed surface in DRZL matches against.
364
+ */
238
365
  schema?: string;
239
366
  columns: Column[];
240
367
  primaryKey?: Key;
@@ -254,6 +381,28 @@ interface Enum {
254
381
  name: string;
255
382
  values: string[];
256
383
  }
384
+ /**
385
+ * The one name that identifies a table across every SQL schema in an analysis.
386
+ *
387
+ * `reporting.users` where the table names a schema, and the bare `users` where it does not. The
388
+ * bare form for the default schema is deliberate and is what makes this safe to reach for
389
+ * everywhere: on a schema module that never calls `pgSchema`, and that is nearly all of them, this
390
+ * returns exactly `table.name`, so every file name, every export, every config pattern and every
391
+ * emitted path is byte for byte what it was.
392
+ *
393
+ * `public.users` is not produced here. Drizzle refuses `pgSchema('public')`, so no analysis can
394
+ * ever carry `schema: 'public'`, and a table with no schema *is* the public one. `public.` exists
395
+ * only as a spelling a config may use, resolved by `@drzl/cli`.
396
+ */
397
+ declare function qualifiedTableName(table: {
398
+ name: string;
399
+ schema?: string;
400
+ }): string;
401
+ /** The same name, for the far end of a foreign key. */
402
+ declare function qualifiedForeignTable(fk: {
403
+ foreignTable: string;
404
+ foreignSchema?: string;
405
+ }): string;
257
406
  interface Analysis {
258
407
  drizzleVersion?: string;
259
408
  dialect: Dialect;
@@ -451,6 +600,31 @@ declare class SchemaAnalyzer {
451
600
  * `number double` on drizzle v1, which is where these pairings come from.
452
601
  */
453
602
  private static readonly INEXACT_RANGES;
603
+ /**
604
+ * The Postgres number columns that hold a non-finite double, and which of the three each holds.
605
+ *
606
+ * The class-name half of what `describeV1Column` reads off the codec, and the two must agree: a
607
+ * fact stated on one path and not the other is a schema that changes when the user upgrades
608
+ * drizzle, which the cross-major diff in `verify-packed.sh` fails on. These three class names are
609
+ * the same on both majors, read off real `pgTable` columns on 0.45.2 and on 1.0.0-rc.4, so this
610
+ * table also answers for a v1 column and the two answers are identical rather than merely
611
+ * compatible. non-finite-numbers.spec.ts asserts that agreement through the real analyzer.
612
+ *
613
+ * No MySQL, SingleStore or SQLite class belongs here: MySQL refuses all three on a `float`/
614
+ * `double` and stores `0.00` for a `decimal`, and SQLite returns both infinities while silently
615
+ * turning `NaN` into NULL, which is a different answer that has to arrive whole.
616
+ *
617
+ * Gel does belong, and is the fourth and fifth entries. Measured on a live Gel 7.1 rather than
618
+ * inferred from it being Postgres-backed: both `std::float32` and `std::float64` stored `nan`,
619
+ * `inf` and `-inf` and handed all three back as `NaN`, `Infinity` and `-Infinity`, through a cast
620
+ * and again through a stored property. Without them every row of such a column failed validation.
621
+ *
622
+ * `PgNumeric` is absent because its value is a string, and its pattern already accepts `NaN` and
623
+ * `Infinity`. `PgNumericNumber` is absent because its answer is no longer flat: it takes `NaN` at
624
+ * any width and an infinity only where no precision is declared, which is a per-column question
625
+ * this table cannot ask. `columnConstraints` answers it beside the bound that decides it.
626
+ */
627
+ private static readonly PG_NON_FINITE;
454
628
  /**
455
629
  * Constraints the column definition already carries, which the analysis used to throw away.
456
630
  *
@@ -463,4 +637,4 @@ declare class SchemaAnalyzer {
463
637
  analyze(opts?: AnalyzeOptions): Promise<Analysis>;
464
638
  }
465
639
 
466
- export { type Analysis, type AnalyzeOptions, type Check, type Column, type ColumnRef, type ColumnShape, type Dialect, type Enum, type ForeignKey, type Index, type Issue, type Key, type Relation, SchemaAnalyzer, type Table, SchemaAnalyzer as default, describeV1Column, isDrizzleView, isReadOnlyRelation, isRelationsV2, readRelationsV2 };
640
+ export { type Analysis, type AnalyzeOptions, type Check, type Column, type ColumnRef, type ColumnShape, type Dialect, type Enum, type ForeignKey, type Index, type Issue, type Key, type Relation, SchemaAnalyzer, type Table, type UnnameableReason, SchemaAnalyzer as default, describeV1Column, isDrizzleView, isReadOnlyRelation, isRelationsV2, qualifiedForeignTable, qualifiedTableName, readRelationsV2 };