@drzl/analyzer 1.17.5 → 1.17.6

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.cjs CHANGED
@@ -112,6 +112,14 @@ function describeV1Column(column) {
112
112
  case "int53":
113
113
  case "uint53":
114
114
  case "int64": {
115
+ if (DECIMAL_BIGINT_MODE.test(entityKind)) {
116
+ out.tsType = "bigint";
117
+ out.dbType = "NUMERIC";
118
+ out.integer = true;
119
+ const range2 = decimalModeRange(column, entityKind, "bigint");
120
+ if (range2) [out.min, out.max] = range2;
121
+ break;
122
+ }
115
123
  const range = {
116
124
  int8: ["-128", "127"],
117
125
  int16: ["-32768", "32767"],
@@ -254,10 +262,11 @@ function describeV1Column(column) {
254
262
  out.tsType = "number";
255
263
  out.dbType = "NUMERIC";
256
264
  out.integer = false;
257
- [out.min, out.max] = JS_SAFE_INTEGER_BOUNDS;
265
+ const range = decimalModeRange(column, entityKind, "number");
266
+ if (range) [out.min, out.max] = range;
258
267
  if (codec === "numeric:number") {
259
268
  out.allowsNaN = true;
260
- out.allowsInfinity = false;
269
+ out.allowsInfinity = !declaredDecimalRange(column);
261
270
  }
262
271
  } else if (js === "string") {
263
272
  out.tsType = "string";
@@ -286,6 +295,28 @@ function declaredLength(column) {
286
295
  const n = column?.length ?? column?.config?.length ?? column?.config?.dimensions;
287
296
  return typeof n === "number" && Number.isFinite(n) && n > 0 ? n : void 0;
288
297
  }
298
+ function declaredDecimalRange(column) {
299
+ const cfg = column?.config ?? {};
300
+ const precision = column?.precision ?? cfg.precision;
301
+ const scale = column?.scale ?? cfg.scale ?? 0;
302
+ if (typeof precision !== "number" || !Number.isInteger(precision) || precision < 1)
303
+ return void 0;
304
+ if (typeof scale !== "number" || !Number.isInteger(scale) || scale < 0) return void 0;
305
+ const nines = "9".repeat(precision);
306
+ const max = scale === 0 ? nines : scale < precision ? `${nines.slice(0, precision - scale)}.${nines.slice(precision - scale)}` : `0.${"0".repeat(scale - precision)}${nines}`;
307
+ return [`-${max}`, max];
308
+ }
309
+ var DECIMAL_NUMBER_MODE = /(?:Numeric|Decimal)Number$/;
310
+ var DECIMAL_BIGINT_MODE = /(?:Numeric|Decimal)BigInt$/;
311
+ var MYSQL_IMPLICIT_DECIMAL_RANGE = ["-9999999999", "9999999999"];
312
+ function decimalModeRange(column, kind, mode) {
313
+ const declared = declaredDecimalRange(column);
314
+ if (declared) return declared;
315
+ if (kind.startsWith("MySql") || kind.startsWith("SingleStore"))
316
+ return MYSQL_IMPLICIT_DECIMAL_RANGE;
317
+ if (kind.startsWith("SQLite")) return void 0;
318
+ return mode === "number" ? JS_SAFE_INTEGER_BOUNDS : void 0;
319
+ }
289
320
  var VIEW_CONFIG_FIELDS = {
290
321
  "drizzle:Columns": "selectedFields",
291
322
  "drizzle:Name": "name",
@@ -583,6 +614,19 @@ var _SchemaAnalyzer = class _SchemaAnalyzer {
583
614
  out.allowsNaN = nonFinite.nan;
584
615
  out.allowsInfinity = nonFinite.infinity;
585
616
  }
617
+ if (DECIMAL_NUMBER_MODE.test(ctor)) {
618
+ const range2 = decimalModeRange(column, ctor, "number");
619
+ if (range2) [out.min, out.max] = range2;
620
+ out.integer = false;
621
+ if (ctor === "PgNumericNumber") {
622
+ out.allowsNaN = true;
623
+ out.allowsInfinity = !declaredDecimalRange(column);
624
+ }
625
+ } else if (DECIMAL_BIGINT_MODE.test(ctor)) {
626
+ const range2 = decimalModeRange(column, ctor, "bigint");
627
+ if (range2) [out.min, out.max] = range2;
628
+ out.integer = true;
629
+ }
586
630
  if (/^(Pg)?UUID$/i.test(ctor) || /Uuid$/i.test(ctor)) out.format = "uuid";
587
631
  return out;
588
632
  }
@@ -850,7 +894,8 @@ var _SchemaAnalyzer = class _SchemaAnalyzer {
850
894
  if (/^Gel/i.test(ctor)) {
851
895
  if (/BigInt64/i.test(ctor)) return { tsType: "bigint", dbType: "BIGINT" };
852
896
  if (/Int53|Integer|SmallInt/i.test(ctor)) return { tsType: "number", dbType: "INTEGER" };
853
- if (/Real|DoublePrecision/i.test(ctor)) return { tsType: "number", dbType: "NUMERIC" };
897
+ if (/DoublePrecision/i.test(ctor)) return { tsType: "number", dbType: "DOUBLE" };
898
+ if (/Real/i.test(ctor)) return { tsType: "number", dbType: "REAL" };
854
899
  if (/Decimal/i.test(ctor)) return { tsType: "string", dbType: "NUMERIC" };
855
900
  if (/UUID/i.test(ctor)) return { tsType: "string", dbType: "UUID" };
856
901
  if (/Json/i.test(ctor)) return { tsType: "any", dbType: "JSON" };
@@ -901,7 +946,9 @@ var _SchemaAnalyzer = class _SchemaAnalyzer {
901
946
  const v1 = describeV1Column(col);
902
947
  const constraints = this.columnConstraints(col);
903
948
  if (v1?.shape) delete constraints.maxLength;
904
- const sqlKind = String(outerCol?.constructor?.[/* @__PURE__ */ Symbol.for("drizzle:entityKind")] ?? "");
949
+ const sqlKind = String(
950
+ outerCol?.constructor?.[/* @__PURE__ */ Symbol.for("drizzle:entityKind")] ?? ""
951
+ );
905
952
  const sqlType = sqlKind.startsWith("MySql") && typeof col?.getSQLType === "function" ? String(col.getSQLType()).toLowerCase() : void 0;
906
953
  const byteCap = sqlType ? MYSQL_TEXT_CAPS[sqlType] : void 0;
907
954
  const ctorName = String(col?.constructor?.name ?? "");
@@ -1268,11 +1315,29 @@ _SchemaAnalyzer.INEXACT_RANGES = {
1268
1315
  SQLiteReal: null,
1269
1316
  SingleStoreDouble: null,
1270
1317
  SingleStoreReal: null,
1271
- // `numeric({ mode: 'number' })`, which v1 reaches through the bare-number arm of
1272
- // `describeV1Column`. This one is about what a JS number can carry rather than about the
1273
- // column, which Postgres caps far lower: it refuses 2147483648 into a `numeric(10,2)`.
1274
- PgNumericNumber: JS_SAFE_INTEGER_BOUNDS
1318
+ // Gel, whose `real` is a `std::float32` and whose `doublePrecision` is a `std::float64`. Both
1319
+ // used to be answered by a `/Real|DoublePrecision/i` arm that said NUMERIC and stated nothing
1320
+ // else at all, so a `real` column accepted 1e300 and the server refused it.
1321
+ //
1322
+ // Measured on a live Gel 7.1 (`geldata/gel:7`, sys::get_version_as_str() -> 7.1+08db576)
1323
+ // through the `gel` client, casting each literal so the server parses it, and again through a
1324
+ // stored property on a real object type. The float32 edge is Postgres's exactly, to the double:
1325
+ //
1326
+ // 3.4028234663852886e38 accepted, returned unchanged
1327
+ // 3.4028235677973366e38 accepted, and stored as 3.4028234663852886e38
1328
+ // 3.402823567797337e38 refused, "is out of range for type std::float32"
1329
+ // 1e300 refused, the same way
1330
+ //
1331
+ // The same value accepted, the same next double up refused, and the same rounding down of the
1332
+ // midpoint, so it takes the constant already here rather than a second name for one number.
1333
+ // float64 took 1e300 and Number.MAX_VALUE faithfully, for the reason no 8 byte float has a
1334
+ // truthful finite bound.
1335
+ GelReal: PG_FLOAT4_RANGE,
1336
+ GelDoublePrecision: null
1275
1337
  };
1338
+ // `numeric`/`decimal` in either of its two numeric modes is deliberately not in the table above.
1339
+ // Its bound is not a fixed magnitude per class but the precision each column declares for itself,
1340
+ // which no table keyed on a class name can hold; see `declaredDecimalRange`.
1276
1341
  /**
1277
1342
  * The Postgres number columns that hold a non-finite double, and which of the three each holds.
1278
1343
  *
@@ -1283,17 +1348,25 @@ _SchemaAnalyzer.INEXACT_RANGES = {
1283
1348
  * table also answers for a v1 column and the two answers are identical rather than merely
1284
1349
  * compatible. non-finite-numbers.spec.ts asserts that agreement through the real analyzer.
1285
1350
  *
1286
- * Postgres only. No MySQL, SingleStore or SQLite class belongs here: MySQL refuses all three on a
1287
- * `float`/`double` and stores `0.00` for a `decimal`, and SQLite returns both infinities while
1288
- * silently turning `NaN` into NULL, which is a different answer that has to arrive whole.
1351
+ * No MySQL, SingleStore or SQLite class belongs here: MySQL refuses all three on a `float`/
1352
+ * `double` and stores `0.00` for a `decimal`, and SQLite returns both infinities while silently
1353
+ * turning `NaN` into NULL, which is a different answer that has to arrive whole.
1354
+ *
1355
+ * Gel does belong, and is the fourth and fifth entries. Measured on a live Gel 7.1 rather than
1356
+ * inferred from it being Postgres-backed: both `std::float32` and `std::float64` stored `nan`,
1357
+ * `inf` and `-inf` and handed all three back as `NaN`, `Infinity` and `-Infinity`, through a cast
1358
+ * and again through a stored property. Without them every row of such a column failed validation.
1289
1359
  *
1290
1360
  * `PgNumeric` is absent because its value is a string, and its pattern already accepts `NaN` and
1291
- * `Infinity`. `PgNumericNumber` states `infinity: false` on purpose; see `Column.allowsNaN`.
1361
+ * `Infinity`. `PgNumericNumber` is absent because its answer is no longer flat: it takes `NaN` at
1362
+ * any width and an infinity only where no precision is declared, which is a per-column question
1363
+ * this table cannot ask. `columnConstraints` answers it beside the bound that decides it.
1292
1364
  */
1293
1365
  _SchemaAnalyzer.PG_NON_FINITE = {
1294
1366
  PgReal: { nan: true, infinity: true },
1295
1367
  PgDoublePrecision: { nan: true, infinity: true },
1296
- PgNumericNumber: { nan: true, infinity: false }
1368
+ GelReal: { nan: true, infinity: true },
1369
+ GelDoublePrecision: { nan: true, infinity: true }
1297
1370
  };
1298
1371
  var SchemaAnalyzer = _SchemaAnalyzer;
1299
1372
  var index_default = SchemaAnalyzer;
package/dist/index.d.cts CHANGED
@@ -48,8 +48,16 @@ interface Column {
48
48
  *
49
49
  * Strings rather than numbers because a 64 bit bound is not representable as a JS number:
50
50
  * `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.
51
+ * numeric field here would silently emit a wrong bound. A 20 digit `numeric(20,0)` bound is
52
+ * further past that again.
53
+ *
54
+ * Not an integer range, despite the name this field used to be described by. An inexact column
55
+ * carries one too: a `real` is bounded by the magnitude the database refuses past, and a
56
+ * `numeric(10,2)` by the width its own declaration states. `integer` says which kind it is, and
57
+ * saying it is what stops a bounded float schema refusing 1.5.
58
+ *
59
+ * Absent where nothing declares a bound: an 8 byte float holds every finite JS number, and a
60
+ * `numeric` with no precision holds arbitrary precision.
53
61
  */
54
62
  min?: string;
55
63
  max?: string;
@@ -76,14 +84,22 @@ interface Column {
76
84
  * does too, but a `numeric(10,2)` refuses either infinity with `22003 numeric field overflow`
77
85
  * while still taking `NaN`, and `integer`/`bigint` refuse all three.
78
86
  *
79
- * So `numeric` in `{ mode: 'number' }` states `allowsInfinity: false` deliberately, and it is a
80
- * narrowing rather than an oversight: nothing here reads a column's precision or scale, so the
81
- * two `numeric` declarations are indistinguishable, and admitting the infinities would make the
82
- * schema promise what the server refuses for the commoner of the two.
87
+ * So `numeric` in `{ mode: 'number' }` answers each of the two separately: `allowsNaN` at any
88
+ * width, and `allowsInfinity` only where the declaration carries no precision. That used to be a
89
+ * flat `false`, and the recorded reason was that nothing here read a column's precision or scale,
90
+ * so the two declarations were indistinguishable and the narrower answer was the safer one.
91
+ * `declaredDecimalRange` reads both numbers now, the two are distinguishable, and each says what
92
+ * its own server does.
83
93
  *
84
- * Postgres only, today. MySQL refuses all three on a `float`/`double` and silently stores `0.00`
85
- * for a `decimal`. SQLite returns both infinities and silently turns `NaN` into NULL, which is
86
- * real and is filed on its own: a column needs both halves of that answer or none.
94
+ * Postgres and Gel. MySQL refuses all three on a `float`/`double`, and on a `decimal` refuses
95
+ * them outright rather than storing `0.00`: measured on MySQL 8.4.11 in `STRICT_TRANS_TABLES`,
96
+ * all three answer `Incorrect decimal value`. SQLite returns both infinities and silently turns
97
+ * `NaN` into NULL, which is real and is filed on its own: a column needs both halves of that
98
+ * answer or none.
99
+ *
100
+ * Gel joined on a measurement of its own rather than on being Postgres-backed: a live Gel 7.1
101
+ * stored `nan`, `inf` and `-inf` in both `std::float32` and `std::float64` and handed all three
102
+ * back unchanged, through a cast and through a stored property.
87
103
  *
88
104
  * Absent on every other column, including the string mode of `numeric`, which already carries the
89
105
  * same fact as a pattern; see `COLUMN_FORMATS.numeric` in `@drzl/validation-core`.
@@ -489,12 +505,19 @@ declare class SchemaAnalyzer {
489
505
  * table also answers for a v1 column and the two answers are identical rather than merely
490
506
  * compatible. non-finite-numbers.spec.ts asserts that agreement through the real analyzer.
491
507
  *
492
- * Postgres only. No MySQL, SingleStore or SQLite class belongs here: MySQL refuses all three on a
493
- * `float`/`double` and stores `0.00` for a `decimal`, and SQLite returns both infinities while
494
- * silently turning `NaN` into NULL, which is a different answer that has to arrive whole.
508
+ * No MySQL, SingleStore or SQLite class belongs here: MySQL refuses all three on a `float`/
509
+ * `double` and stores `0.00` for a `decimal`, and SQLite returns both infinities while silently
510
+ * turning `NaN` into NULL, which is a different answer that has to arrive whole.
511
+ *
512
+ * Gel does belong, and is the fourth and fifth entries. Measured on a live Gel 7.1 rather than
513
+ * inferred from it being Postgres-backed: both `std::float32` and `std::float64` stored `nan`,
514
+ * `inf` and `-inf` and handed all three back as `NaN`, `Infinity` and `-Infinity`, through a cast
515
+ * and again through a stored property. Without them every row of such a column failed validation.
495
516
  *
496
517
  * `PgNumeric` is absent because its value is a string, and its pattern already accepts `NaN` and
497
- * `Infinity`. `PgNumericNumber` states `infinity: false` on purpose; see `Column.allowsNaN`.
518
+ * `Infinity`. `PgNumericNumber` is absent because its answer is no longer flat: it takes `NaN` at
519
+ * any width and an infinity only where no precision is declared, which is a per-column question
520
+ * this table cannot ask. `columnConstraints` answers it beside the bound that decides it.
498
521
  */
499
522
  private static readonly PG_NON_FINITE;
500
523
  /**
package/dist/index.d.ts CHANGED
@@ -48,8 +48,16 @@ interface Column {
48
48
  *
49
49
  * Strings rather than numbers because a 64 bit bound is not representable as a JS number:
50
50
  * `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.
51
+ * numeric field here would silently emit a wrong bound. A 20 digit `numeric(20,0)` bound is
52
+ * further past that again.
53
+ *
54
+ * Not an integer range, despite the name this field used to be described by. An inexact column
55
+ * carries one too: a `real` is bounded by the magnitude the database refuses past, and a
56
+ * `numeric(10,2)` by the width its own declaration states. `integer` says which kind it is, and
57
+ * saying it is what stops a bounded float schema refusing 1.5.
58
+ *
59
+ * Absent where nothing declares a bound: an 8 byte float holds every finite JS number, and a
60
+ * `numeric` with no precision holds arbitrary precision.
53
61
  */
54
62
  min?: string;
55
63
  max?: string;
@@ -76,14 +84,22 @@ interface Column {
76
84
  * does too, but a `numeric(10,2)` refuses either infinity with `22003 numeric field overflow`
77
85
  * while still taking `NaN`, and `integer`/`bigint` refuse all three.
78
86
  *
79
- * So `numeric` in `{ mode: 'number' }` states `allowsInfinity: false` deliberately, and it is a
80
- * narrowing rather than an oversight: nothing here reads a column's precision or scale, so the
81
- * two `numeric` declarations are indistinguishable, and admitting the infinities would make the
82
- * schema promise what the server refuses for the commoner of the two.
87
+ * So `numeric` in `{ mode: 'number' }` answers each of the two separately: `allowsNaN` at any
88
+ * width, and `allowsInfinity` only where the declaration carries no precision. That used to be a
89
+ * flat `false`, and the recorded reason was that nothing here read a column's precision or scale,
90
+ * so the two declarations were indistinguishable and the narrower answer was the safer one.
91
+ * `declaredDecimalRange` reads both numbers now, the two are distinguishable, and each says what
92
+ * its own server does.
83
93
  *
84
- * Postgres only, today. MySQL refuses all three on a `float`/`double` and silently stores `0.00`
85
- * for a `decimal`. SQLite returns both infinities and silently turns `NaN` into NULL, which is
86
- * real and is filed on its own: a column needs both halves of that answer or none.
94
+ * Postgres and Gel. MySQL refuses all three on a `float`/`double`, and on a `decimal` refuses
95
+ * them outright rather than storing `0.00`: measured on MySQL 8.4.11 in `STRICT_TRANS_TABLES`,
96
+ * all three answer `Incorrect decimal value`. SQLite returns both infinities and silently turns
97
+ * `NaN` into NULL, which is real and is filed on its own: a column needs both halves of that
98
+ * answer or none.
99
+ *
100
+ * Gel joined on a measurement of its own rather than on being Postgres-backed: a live Gel 7.1
101
+ * stored `nan`, `inf` and `-inf` in both `std::float32` and `std::float64` and handed all three
102
+ * back unchanged, through a cast and through a stored property.
87
103
  *
88
104
  * Absent on every other column, including the string mode of `numeric`, which already carries the
89
105
  * same fact as a pattern; see `COLUMN_FORMATS.numeric` in `@drzl/validation-core`.
@@ -489,12 +505,19 @@ declare class SchemaAnalyzer {
489
505
  * table also answers for a v1 column and the two answers are identical rather than merely
490
506
  * compatible. non-finite-numbers.spec.ts asserts that agreement through the real analyzer.
491
507
  *
492
- * Postgres only. No MySQL, SingleStore or SQLite class belongs here: MySQL refuses all three on a
493
- * `float`/`double` and stores `0.00` for a `decimal`, and SQLite returns both infinities while
494
- * silently turning `NaN` into NULL, which is a different answer that has to arrive whole.
508
+ * No MySQL, SingleStore or SQLite class belongs here: MySQL refuses all three on a `float`/
509
+ * `double` and stores `0.00` for a `decimal`, and SQLite returns both infinities while silently
510
+ * turning `NaN` into NULL, which is a different answer that has to arrive whole.
511
+ *
512
+ * Gel does belong, and is the fourth and fifth entries. Measured on a live Gel 7.1 rather than
513
+ * inferred from it being Postgres-backed: both `std::float32` and `std::float64` stored `nan`,
514
+ * `inf` and `-inf` and handed all three back as `NaN`, `Infinity` and `-Infinity`, through a cast
515
+ * and again through a stored property. Without them every row of such a column failed validation.
495
516
  *
496
517
  * `PgNumeric` is absent because its value is a string, and its pattern already accepts `NaN` and
497
- * `Infinity`. `PgNumericNumber` states `infinity: false` on purpose; see `Column.allowsNaN`.
518
+ * `Infinity`. `PgNumericNumber` is absent because its answer is no longer flat: it takes `NaN` at
519
+ * any width and an infinity only where no precision is declared, which is a per-column question
520
+ * this table cannot ask. `columnConstraints` answers it beside the bound that decides it.
498
521
  */
499
522
  private static readonly PG_NON_FINITE;
500
523
  /**
package/dist/index.js CHANGED
@@ -71,6 +71,14 @@ function describeV1Column(column) {
71
71
  case "int53":
72
72
  case "uint53":
73
73
  case "int64": {
74
+ if (DECIMAL_BIGINT_MODE.test(entityKind)) {
75
+ out.tsType = "bigint";
76
+ out.dbType = "NUMERIC";
77
+ out.integer = true;
78
+ const range2 = decimalModeRange(column, entityKind, "bigint");
79
+ if (range2) [out.min, out.max] = range2;
80
+ break;
81
+ }
74
82
  const range = {
75
83
  int8: ["-128", "127"],
76
84
  int16: ["-32768", "32767"],
@@ -213,10 +221,11 @@ function describeV1Column(column) {
213
221
  out.tsType = "number";
214
222
  out.dbType = "NUMERIC";
215
223
  out.integer = false;
216
- [out.min, out.max] = JS_SAFE_INTEGER_BOUNDS;
224
+ const range = decimalModeRange(column, entityKind, "number");
225
+ if (range) [out.min, out.max] = range;
217
226
  if (codec === "numeric:number") {
218
227
  out.allowsNaN = true;
219
- out.allowsInfinity = false;
228
+ out.allowsInfinity = !declaredDecimalRange(column);
220
229
  }
221
230
  } else if (js === "string") {
222
231
  out.tsType = "string";
@@ -245,6 +254,28 @@ function declaredLength(column) {
245
254
  const n = column?.length ?? column?.config?.length ?? column?.config?.dimensions;
246
255
  return typeof n === "number" && Number.isFinite(n) && n > 0 ? n : void 0;
247
256
  }
257
+ function declaredDecimalRange(column) {
258
+ const cfg = column?.config ?? {};
259
+ const precision = column?.precision ?? cfg.precision;
260
+ const scale = column?.scale ?? cfg.scale ?? 0;
261
+ if (typeof precision !== "number" || !Number.isInteger(precision) || precision < 1)
262
+ return void 0;
263
+ if (typeof scale !== "number" || !Number.isInteger(scale) || scale < 0) return void 0;
264
+ const nines = "9".repeat(precision);
265
+ const max = scale === 0 ? nines : scale < precision ? `${nines.slice(0, precision - scale)}.${nines.slice(precision - scale)}` : `0.${"0".repeat(scale - precision)}${nines}`;
266
+ return [`-${max}`, max];
267
+ }
268
+ var DECIMAL_NUMBER_MODE = /(?:Numeric|Decimal)Number$/;
269
+ var DECIMAL_BIGINT_MODE = /(?:Numeric|Decimal)BigInt$/;
270
+ var MYSQL_IMPLICIT_DECIMAL_RANGE = ["-9999999999", "9999999999"];
271
+ function decimalModeRange(column, kind, mode) {
272
+ const declared = declaredDecimalRange(column);
273
+ if (declared) return declared;
274
+ if (kind.startsWith("MySql") || kind.startsWith("SingleStore"))
275
+ return MYSQL_IMPLICIT_DECIMAL_RANGE;
276
+ if (kind.startsWith("SQLite")) return void 0;
277
+ return mode === "number" ? JS_SAFE_INTEGER_BOUNDS : void 0;
278
+ }
248
279
  var VIEW_CONFIG_FIELDS = {
249
280
  "drizzle:Columns": "selectedFields",
250
281
  "drizzle:Name": "name",
@@ -542,6 +573,19 @@ var _SchemaAnalyzer = class _SchemaAnalyzer {
542
573
  out.allowsNaN = nonFinite.nan;
543
574
  out.allowsInfinity = nonFinite.infinity;
544
575
  }
576
+ if (DECIMAL_NUMBER_MODE.test(ctor)) {
577
+ const range2 = decimalModeRange(column, ctor, "number");
578
+ if (range2) [out.min, out.max] = range2;
579
+ out.integer = false;
580
+ if (ctor === "PgNumericNumber") {
581
+ out.allowsNaN = true;
582
+ out.allowsInfinity = !declaredDecimalRange(column);
583
+ }
584
+ } else if (DECIMAL_BIGINT_MODE.test(ctor)) {
585
+ const range2 = decimalModeRange(column, ctor, "bigint");
586
+ if (range2) [out.min, out.max] = range2;
587
+ out.integer = true;
588
+ }
545
589
  if (/^(Pg)?UUID$/i.test(ctor) || /Uuid$/i.test(ctor)) out.format = "uuid";
546
590
  return out;
547
591
  }
@@ -809,7 +853,8 @@ var _SchemaAnalyzer = class _SchemaAnalyzer {
809
853
  if (/^Gel/i.test(ctor)) {
810
854
  if (/BigInt64/i.test(ctor)) return { tsType: "bigint", dbType: "BIGINT" };
811
855
  if (/Int53|Integer|SmallInt/i.test(ctor)) return { tsType: "number", dbType: "INTEGER" };
812
- if (/Real|DoublePrecision/i.test(ctor)) return { tsType: "number", dbType: "NUMERIC" };
856
+ if (/DoublePrecision/i.test(ctor)) return { tsType: "number", dbType: "DOUBLE" };
857
+ if (/Real/i.test(ctor)) return { tsType: "number", dbType: "REAL" };
813
858
  if (/Decimal/i.test(ctor)) return { tsType: "string", dbType: "NUMERIC" };
814
859
  if (/UUID/i.test(ctor)) return { tsType: "string", dbType: "UUID" };
815
860
  if (/Json/i.test(ctor)) return { tsType: "any", dbType: "JSON" };
@@ -860,7 +905,9 @@ var _SchemaAnalyzer = class _SchemaAnalyzer {
860
905
  const v1 = describeV1Column(col);
861
906
  const constraints = this.columnConstraints(col);
862
907
  if (v1?.shape) delete constraints.maxLength;
863
- const sqlKind = String(outerCol?.constructor?.[/* @__PURE__ */ Symbol.for("drizzle:entityKind")] ?? "");
908
+ const sqlKind = String(
909
+ outerCol?.constructor?.[/* @__PURE__ */ Symbol.for("drizzle:entityKind")] ?? ""
910
+ );
864
911
  const sqlType = sqlKind.startsWith("MySql") && typeof col?.getSQLType === "function" ? String(col.getSQLType()).toLowerCase() : void 0;
865
912
  const byteCap = sqlType ? MYSQL_TEXT_CAPS[sqlType] : void 0;
866
913
  const ctorName = String(col?.constructor?.name ?? "");
@@ -1227,11 +1274,29 @@ _SchemaAnalyzer.INEXACT_RANGES = {
1227
1274
  SQLiteReal: null,
1228
1275
  SingleStoreDouble: null,
1229
1276
  SingleStoreReal: null,
1230
- // `numeric({ mode: 'number' })`, which v1 reaches through the bare-number arm of
1231
- // `describeV1Column`. This one is about what a JS number can carry rather than about the
1232
- // column, which Postgres caps far lower: it refuses 2147483648 into a `numeric(10,2)`.
1233
- PgNumericNumber: JS_SAFE_INTEGER_BOUNDS
1277
+ // Gel, whose `real` is a `std::float32` and whose `doublePrecision` is a `std::float64`. Both
1278
+ // used to be answered by a `/Real|DoublePrecision/i` arm that said NUMERIC and stated nothing
1279
+ // else at all, so a `real` column accepted 1e300 and the server refused it.
1280
+ //
1281
+ // Measured on a live Gel 7.1 (`geldata/gel:7`, sys::get_version_as_str() -> 7.1+08db576)
1282
+ // through the `gel` client, casting each literal so the server parses it, and again through a
1283
+ // stored property on a real object type. The float32 edge is Postgres's exactly, to the double:
1284
+ //
1285
+ // 3.4028234663852886e38 accepted, returned unchanged
1286
+ // 3.4028235677973366e38 accepted, and stored as 3.4028234663852886e38
1287
+ // 3.402823567797337e38 refused, "is out of range for type std::float32"
1288
+ // 1e300 refused, the same way
1289
+ //
1290
+ // The same value accepted, the same next double up refused, and the same rounding down of the
1291
+ // midpoint, so it takes the constant already here rather than a second name for one number.
1292
+ // float64 took 1e300 and Number.MAX_VALUE faithfully, for the reason no 8 byte float has a
1293
+ // truthful finite bound.
1294
+ GelReal: PG_FLOAT4_RANGE,
1295
+ GelDoublePrecision: null
1234
1296
  };
1297
+ // `numeric`/`decimal` in either of its two numeric modes is deliberately not in the table above.
1298
+ // Its bound is not a fixed magnitude per class but the precision each column declares for itself,
1299
+ // which no table keyed on a class name can hold; see `declaredDecimalRange`.
1235
1300
  /**
1236
1301
  * The Postgres number columns that hold a non-finite double, and which of the three each holds.
1237
1302
  *
@@ -1242,17 +1307,25 @@ _SchemaAnalyzer.INEXACT_RANGES = {
1242
1307
  * table also answers for a v1 column and the two answers are identical rather than merely
1243
1308
  * compatible. non-finite-numbers.spec.ts asserts that agreement through the real analyzer.
1244
1309
  *
1245
- * Postgres only. No MySQL, SingleStore or SQLite class belongs here: MySQL refuses all three on a
1246
- * `float`/`double` and stores `0.00` for a `decimal`, and SQLite returns both infinities while
1247
- * silently turning `NaN` into NULL, which is a different answer that has to arrive whole.
1310
+ * No MySQL, SingleStore or SQLite class belongs here: MySQL refuses all three on a `float`/
1311
+ * `double` and stores `0.00` for a `decimal`, and SQLite returns both infinities while silently
1312
+ * turning `NaN` into NULL, which is a different answer that has to arrive whole.
1313
+ *
1314
+ * Gel does belong, and is the fourth and fifth entries. Measured on a live Gel 7.1 rather than
1315
+ * inferred from it being Postgres-backed: both `std::float32` and `std::float64` stored `nan`,
1316
+ * `inf` and `-inf` and handed all three back as `NaN`, `Infinity` and `-Infinity`, through a cast
1317
+ * and again through a stored property. Without them every row of such a column failed validation.
1248
1318
  *
1249
1319
  * `PgNumeric` is absent because its value is a string, and its pattern already accepts `NaN` and
1250
- * `Infinity`. `PgNumericNumber` states `infinity: false` on purpose; see `Column.allowsNaN`.
1320
+ * `Infinity`. `PgNumericNumber` is absent because its answer is no longer flat: it takes `NaN` at
1321
+ * any width and an infinity only where no precision is declared, which is a per-column question
1322
+ * this table cannot ask. `columnConstraints` answers it beside the bound that decides it.
1251
1323
  */
1252
1324
  _SchemaAnalyzer.PG_NON_FINITE = {
1253
1325
  PgReal: { nan: true, infinity: true },
1254
1326
  PgDoublePrecision: { nan: true, infinity: true },
1255
- PgNumericNumber: { nan: true, infinity: false }
1327
+ GelReal: { nan: true, infinity: true },
1328
+ GelDoublePrecision: { nan: true, infinity: true }
1256
1329
  };
1257
1330
  var SchemaAnalyzer = _SchemaAnalyzer;
1258
1331
  var index_default = SchemaAnalyzer;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@drzl/analyzer",
3
- "version": "1.17.5",
3
+ "version": "1.17.6",
4
4
  "private": false,
5
5
  "license": "Apache-2.0",
6
6
  "type": "module",