@vibeorm/migrate 2.0.0-alpha.4 → 2.0.0-alpha.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.
@@ -1 +1 @@
1
- {"version":3,"file":"sqlite.d.ts","sourceRoot":"","sources":["../../src/ddl/sqlite.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAgDG;AAGH,OAAO,KAAK,EAAmB,QAAQ,EAAE,MAAM,iBAAiB,CAAC;AAajE,OAAO,KAAK,EAAe,aAAa,EAAE,MAAM,aAAa,CAAC;AAI9D;;;;GAIG;AACH,wBAAgB,iBAAiB,CAAC,MAAM,EAAE;IACxC,KAAK,EAAE,SAAS,aAAa,EAAE,CAAC;IAChC,IAAI,CAAC,EAAE,QAAQ,CAAC;IAChB,EAAE,CAAC,EAAE,QAAQ,CAAC;CACf,GAAG,MAAM,EAAE,CAkLX"}
1
+ {"version":3,"file":"sqlite.d.ts","sourceRoot":"","sources":["../../src/ddl/sqlite.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAgDG;AAGH,OAAO,KAAK,EAAmB,QAAQ,EAAE,MAAM,iBAAiB,CAAC;AAajE,OAAO,KAAK,EAAe,aAAa,EAAE,MAAM,aAAa,CAAC;AAI9D;;;;GAIG;AACH,wBAAgB,iBAAiB,CAAC,MAAM,EAAE;IACxC,KAAK,EAAE,SAAS,aAAa,EAAE,CAAC;IAChC,IAAI,CAAC,EAAE,QAAQ,CAAC;IAChB,EAAE,CAAC,EAAE,QAAQ,CAAC;CACf,GAAG,MAAM,EAAE,CA0LX"}
@@ -1 +1 @@
1
- {"version":3,"file":"differ.d.ts","sourceRoot":"","sources":["../src/differ.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AAGH,OAAO,KAAK,EAAE,OAAO,EAAU,QAAQ,EAAE,MAAM,iBAAiB,CAAC;AAKjE,OAAO,KAAK,EAAqB,aAAa,EAAE,MAAM,YAAY,CAAC;AAkCnE;;;;;;;;;;GAUG;AACH,wBAAgB,WAAW,CAAC,MAAM,EAAE;IAClC,IAAI,EAAE,QAAQ,CAAC;IACf,EAAE,EAAE,QAAQ,CAAC;IACb,OAAO,CAAC,EAAE,OAAO,CAAC;CACnB,GAAG,aAAa,EAAE,CAsDlB"}
1
+ {"version":3,"file":"differ.d.ts","sourceRoot":"","sources":["../src/differ.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AAGH,OAAO,KAAK,EAAE,OAAO,EAAU,QAAQ,EAAE,MAAM,iBAAiB,CAAC;AAKjE,OAAO,KAAK,EAAqB,aAAa,EAAE,MAAM,YAAY,CAAC;AAoCnE;;;;;;;;;;GAUG;AACH,wBAAgB,WAAW,CAAC,MAAM,EAAE;IAClC,IAAI,EAAE,QAAQ,CAAC;IACf,EAAE,EAAE,QAAQ,CAAC;IACb,OAAO,CAAC,EAAE,OAAO,CAAC;CACnB,GAAG,aAAa,EAAE,CA0DlB"}
package/dist/index.js CHANGED
@@ -21,6 +21,10 @@ function describeStep(params) {
21
21
  return `alter column "${step.table}"."${step.column}" (${step.changes.join(", ")})`;
22
22
  case "createIndex":
23
23
  return `create ${step.unique ? "unique " : ""}index "${step.name}" on "${step.table}"`;
24
+ case "addCheck":
25
+ return `add check "${step.name}" on "${step.table}"`;
26
+ case "dropCheck":
27
+ return `drop check "${step.name}" on "${step.table}"`;
24
28
  case "dropIndex":
25
29
  return `drop index "${step.name}"`;
26
30
  case "addForeignKey":
@@ -389,6 +393,32 @@ function sqliteEnumCheckName(params) {
389
393
  return `${params.table}_${params.column}_enum_${params.enumName}`;
390
394
  }
391
395
  var ENUM_CHECK_PATTERN = /CONSTRAINT "((?:[^"]|"")+)_enum_((?:[^"]|"")+)" CHECK \("((?:[^"]|"")+)" IN \(([^)]*)\)\)/g;
396
+ function parseSqliteNamedChecks(params) {
397
+ const { tableSql } = params;
398
+ const result = [];
399
+ for (const match of tableSql.matchAll(/CONSTRAINT "((?:[^"]|"")+)" CHECK\s*\(/g)) {
400
+ const name = match[1].replaceAll('""', '"');
401
+ const open = (match.index ?? 0) + match[0].length - 1;
402
+ let depth = 0;
403
+ let close = -1;
404
+ for (let index = open;index < tableSql.length; index++) {
405
+ const char = tableSql[index];
406
+ if (char === "(")
407
+ depth += 1;
408
+ else if (char === ")") {
409
+ depth -= 1;
410
+ if (depth === 0) {
411
+ close = index;
412
+ break;
413
+ }
414
+ }
415
+ }
416
+ if (close === -1)
417
+ continue;
418
+ result.push({ name, expression: tableSql.slice(open + 1, close) });
419
+ }
420
+ return result;
421
+ }
392
422
  function parseSqliteEnumChecks(params) {
393
423
  const result = [];
394
424
  for (const match of params.tableSql.matchAll(ENUM_CHECK_PATTERN)) {
@@ -599,7 +629,10 @@ function normalizeModel(params) {
599
629
  const pk = normalizePk({ model });
600
630
  const indexes = normalizeIndexes({ model, table });
601
631
  const fks = normalizeFks({ model, table, schema });
602
- return { model, table, columns, pk, indexes, fks };
632
+ const checks = new Map;
633
+ for (const check of model.checks ?? [])
634
+ checks.set(check.name, check.expression);
635
+ return { model, table, columns, pk, indexes, fks, checks };
603
636
  }
604
637
  function normalizePk(params) {
605
638
  const { model } = params;
@@ -624,7 +657,8 @@ function normalizeIndexes(params) {
624
657
  };
625
658
  });
626
659
  const where = index.where;
627
- const key = indexComparisonKey({ unique, kind, columns, where });
660
+ const nullsNotDistinct = unique && index.nullsNotDistinct === true;
661
+ const key = indexComparisonKey({ unique, kind, columns, where, nullsNotDistinct });
628
662
  if (result.has(key))
629
663
  return;
630
664
  const name = index.name ?? `${table}_${columns.map((column) => column.name).join("_")}_${unique ? "key" : "idx"}`;
@@ -634,7 +668,8 @@ function normalizeIndexes(params) {
634
668
  kind,
635
669
  unique,
636
670
  columns,
637
- ...where === undefined ? {} : { where }
671
+ ...where === undefined ? {} : { where },
672
+ ...nullsNotDistinct ? { nullsNotDistinct: true } : {}
638
673
  });
639
674
  };
640
675
  for (const index of model.indexes)
@@ -647,9 +682,9 @@ function normalizeIndexes(params) {
647
682
  return result;
648
683
  }
649
684
  function indexComparisonKey(params) {
650
- const { unique, kind, columns, where } = params;
685
+ const { unique, kind, columns, where, nullsNotDistinct } = params;
651
686
  const cols = columns.map((column) => `${column.name}${column.desc ? " desc" : ""}`).join(",");
652
- return `${unique ? "u" : "i"}:${kind}:${cols}:${normalizeWhere({ where })}`;
687
+ return `${unique ? "u" : "i"}${nullsNotDistinct === true ? "n" : ""}:${kind}:${cols}:${normalizeWhere({ where })}`;
653
688
  }
654
689
  function normalizeWhere(params) {
655
690
  const { where } = params;
@@ -748,6 +783,8 @@ function diffSchemas(params) {
748
783
  dropColumn: [],
749
784
  dropPrimaryKey: [],
750
785
  addPrimaryKey: [],
786
+ dropCheck: [],
787
+ addCheck: [],
751
788
  createIndex: [],
752
789
  addForeignKey: [],
753
790
  createJoinTable: [],
@@ -772,6 +809,8 @@ function diffSchemas(params) {
772
809
  ...buckets.dropColumn,
773
810
  ...buckets.dropPrimaryKey,
774
811
  ...buckets.addPrimaryKey,
812
+ ...buckets.dropCheck,
813
+ ...buckets.addCheck,
775
814
  ...buckets.createIndex,
776
815
  ...buckets.addForeignKey,
777
816
  ...buckets.createJoinTable,
@@ -881,6 +920,7 @@ function createWholeTable(params) {
881
920
  table: table.table,
882
921
  columns: [...table.columns.values()].map((column) => column.field),
883
922
  primaryKey: table.pk,
923
+ checks: [...table.checks].map(([name, expression]) => ({ name, expression })),
884
924
  destructive: false
885
925
  });
886
926
  for (const index of table.indexes.values()) {
@@ -892,6 +932,7 @@ function createWholeTable(params) {
892
932
  unique: index.unique,
893
933
  columns: index.columns,
894
934
  ...index.where === undefined ? {} : { where: index.where },
935
+ ...index.nullsNotDistinct === true ? { nullsNotDistinct: true } : {},
895
936
  destructive: false
896
937
  });
897
938
  }
@@ -973,6 +1014,16 @@ function diffOneTable(params) {
973
1014
  });
974
1015
  }
975
1016
  }
1017
+ for (const [name, expression] of current.checks) {
1018
+ if (previous.checks.has(name))
1019
+ continue;
1020
+ buckets.addCheck.push({ kind: "addCheck", table, name, expression, destructive: false });
1021
+ }
1022
+ for (const [name] of previous.checks) {
1023
+ if (current.checks.has(name))
1024
+ continue;
1025
+ buckets.dropCheck.push({ kind: "dropCheck", table, name, destructive: false });
1026
+ }
976
1027
  for (const [key, index] of current.indexes) {
977
1028
  if (previous.indexes.has(key))
978
1029
  continue;
@@ -984,6 +1035,7 @@ function diffOneTable(params) {
984
1035
  unique: index.unique,
985
1036
  columns: index.columns,
986
1037
  ...index.where === undefined ? {} : { where: index.where },
1038
+ ...index.nullsNotDistinct === true ? { nullsNotDistinct: true } : {},
987
1039
  destructive: false
988
1040
  });
989
1041
  }
@@ -1065,6 +1117,9 @@ function renderStep(params) {
1065
1117
  const pkCols = step.primaryKey.map((column) => q(column)).join(", ");
1066
1118
  lines.push(` CONSTRAINT ${q(`${step.table}_pkey`)} PRIMARY KEY (${pkCols})`);
1067
1119
  }
1120
+ for (const check of step.checks ?? []) {
1121
+ lines.push(` CONSTRAINT ${q(check.name)} CHECK (${check.expression})`);
1122
+ }
1068
1123
  return [`CREATE TABLE ${q(step.table)} (
1069
1124
  ${lines.join(`,
1070
1125
  `)}
@@ -1085,13 +1140,20 @@ ${lines.join(`,
1085
1140
  const unique = step.unique ? "UNIQUE " : "";
1086
1141
  const using = !step.unique && step.indexKind !== "btree" ? ` USING ${step.indexKind}` : "";
1087
1142
  const cols = step.columns.map((column) => `${q(column.name)}${column.desc ? " DESC" : ""}`).join(", ");
1143
+ const nulls = step.nullsNotDistinct === true ? " NULLS NOT DISTINCT" : "";
1088
1144
  const where = step.where === undefined ? "" : ` WHERE ${step.where}`;
1089
1145
  return [
1090
- `CREATE ${unique}INDEX ${q(step.name)} ON ${q(step.table)}${using} (${cols})${where};`
1146
+ `CREATE ${unique}INDEX ${q(step.name)} ON ${q(step.table)}${using} (${cols})${nulls}${where};`
1091
1147
  ];
1092
1148
  }
1093
1149
  case "dropIndex":
1094
1150
  return [`DROP INDEX ${q(step.name)};`];
1151
+ case "addCheck":
1152
+ return [
1153
+ `ALTER TABLE ${q(step.table)} ADD CONSTRAINT ${q(step.name)} CHECK (${step.expression});`
1154
+ ];
1155
+ case "dropCheck":
1156
+ return [`ALTER TABLE ${q(step.table)} DROP CONSTRAINT ${q(step.name)};`];
1095
1157
  case "addForeignKey": {
1096
1158
  const cols = step.columns.map((column) => q(column)).join(", ");
1097
1159
  const refCols = step.refColumns.map((column) => q(column)).join(", ");
@@ -1245,6 +1307,8 @@ function renderSqliteSteps(params) {
1245
1307
  case "dropForeignKey":
1246
1308
  case "addPrimaryKey":
1247
1309
  case "dropPrimaryKey":
1310
+ case "addCheck":
1311
+ case "dropCheck":
1248
1312
  markRebuild(step.table);
1249
1313
  break;
1250
1314
  case "addColumn":
@@ -1288,6 +1352,7 @@ function renderSqliteSteps(params) {
1288
1352
  constraintTable: step.table,
1289
1353
  columns: step.columns,
1290
1354
  primaryKey: step.primaryKey,
1355
+ checks: step.checks ?? [],
1291
1356
  fks: (foldedFks.get(step.table) ?? []).map((fk) => ({
1292
1357
  constraintName: fk.constraintName,
1293
1358
  columns: fk.columns,
@@ -1299,6 +1364,9 @@ function renderSqliteSteps(params) {
1299
1364
  enums
1300
1365
  }));
1301
1366
  break;
1367
+ case "addCheck":
1368
+ case "dropCheck":
1369
+ break;
1302
1370
  case "createJoinTable": {
1303
1371
  const aType = sqliteStorageType({ field: { ...step.a.field, isList: false } });
1304
1372
  const bType = sqliteStorageType({ field: { ...step.b.field, isList: false } });
@@ -1333,7 +1401,8 @@ function renderSqliteSteps(params) {
1333
1401
  unique: step.unique,
1334
1402
  kind: step.indexKind,
1335
1403
  columns: step.columns,
1336
- where: step.where
1404
+ where: step.where,
1405
+ nullsNotDistinct: step.nullsNotDistinct
1337
1406
  }));
1338
1407
  }
1339
1408
  break;
@@ -1389,6 +1458,7 @@ function renderTableRebuild(params) {
1389
1458
  constraintTable: table,
1390
1459
  columns: [...target.columns.values()].map((column) => column.field),
1391
1460
  primaryKey: target.pk,
1461
+ checks: [...target.checks].map(([name, expression]) => ({ name, expression })),
1392
1462
  fks: [...target.fks.values()].map((fk) => ({
1393
1463
  constraintName: fk.constraintName,
1394
1464
  columns: fk.columns,
@@ -1412,7 +1482,8 @@ function renderTableRebuild(params) {
1412
1482
  unique: index.unique,
1413
1483
  kind: index.kind,
1414
1484
  columns: index.columns,
1415
- where: index.where
1485
+ where: index.where,
1486
+ nullsNotDistinct: index.nullsNotDistinct
1416
1487
  }));
1417
1488
  }
1418
1489
  statements.push("PRAGMA foreign_key_check;", "COMMIT;", "PRAGMA foreign_keys=ON;");
@@ -1470,6 +1541,9 @@ function createTableSql(params) {
1470
1541
  if (primaryKey.length > 0 && autoincrementPk === undefined) {
1471
1542
  lines.push(` PRIMARY KEY (${primaryKey.map((column) => q2(column)).join(", ")})`);
1472
1543
  }
1544
+ for (const check of params.checks) {
1545
+ lines.push(` CONSTRAINT ${q2(check.name)} CHECK (${check.expression})`);
1546
+ }
1473
1547
  for (const fk of fks) {
1474
1548
  lines.push(` CONSTRAINT ${q2(fk.constraintName)} FOREIGN KEY (${fk.columns.map((column) => q2(column)).join(", ")}) REFERENCES ${q2(fk.refTable)} (${fk.refColumns.map((column) => q2(column)).join(", ")}) ON DELETE ${fk.onDelete} ON UPDATE ${fk.onUpdate}`);
1475
1549
  }
@@ -1525,6 +1599,13 @@ function columnDefSql2(params) {
1525
1599
  }
1526
1600
  function createIndexSql(params) {
1527
1601
  const { table, name, unique, kind, columns, where } = params;
1602
+ if (params.nullsNotDistinct === true) {
1603
+ throw unsupportedCapability4({
1604
+ dialect: "sqlite",
1605
+ feature: `UNIQUE NULLS NOT DISTINCT (index "${name}")`,
1606
+ hint: "sqlite treats NULLs as distinct in unique indexes and has no switch \u2014 gate the flag per dialect"
1607
+ });
1608
+ }
1528
1609
  if (!unique && kind !== "btree") {
1529
1610
  throw unsupportedCapability4({
1530
1611
  dialect: "sqlite",
@@ -1583,6 +1664,9 @@ function renderStep2(params) {
1583
1664
  if (step.primaryKey.length > 0) {
1584
1665
  lines.push(` PRIMARY KEY (${step.primaryKey.map((column) => q3(column)).join(", ")})`);
1585
1666
  }
1667
+ for (const check of step.checks ?? []) {
1668
+ lines.push(` CONSTRAINT ${q3(check.name)} CHECK (${check.expression})`);
1669
+ }
1586
1670
  return [`CREATE TABLE ${q3(step.table)} (
1587
1671
  ${lines.join(`,
1588
1672
  `)}
@@ -1609,6 +1693,13 @@ ${lines.join(`,
1609
1693
  hint: "mysql has no partial indexes \u2014 drop the WHERE predicate or gate the index per dialect"
1610
1694
  });
1611
1695
  }
1696
+ if (step.nullsNotDistinct === true) {
1697
+ throw unsupportedCapability5({
1698
+ dialect: "mysql",
1699
+ feature: `UNIQUE NULLS NOT DISTINCT (index "${step.name}")`,
1700
+ hint: "mysql treats NULLs as distinct in unique indexes and has no switch \u2014 gate the flag per dialect"
1701
+ });
1702
+ }
1612
1703
  if (!step.unique && step.indexKind !== "btree") {
1613
1704
  throw unsupportedCapability5({
1614
1705
  dialect: "mysql",
@@ -1621,6 +1712,12 @@ ${lines.join(`,
1621
1712
  `CREATE ${step.unique ? "UNIQUE " : ""}INDEX ${q3(step.name)} ON ${q3(step.table)} (${cols});`
1622
1713
  ];
1623
1714
  }
1715
+ case "addCheck":
1716
+ return [
1717
+ `ALTER TABLE ${q3(step.table)} ADD CONSTRAINT ${q3(step.name)} CHECK (${step.expression});`
1718
+ ];
1719
+ case "dropCheck":
1720
+ return [`ALTER TABLE ${q3(step.table)} DROP CHECK ${q3(step.name)};`];
1624
1721
  case "dropIndex": {
1625
1722
  if (step.table === undefined) {
1626
1723
  throw new VibeError7({
@@ -2561,6 +2658,18 @@ var CONSTRAINTS_QUERY = `
2561
2658
  AND n.nspname = 'public'
2562
2659
  ORDER BY cl.relname, c.conname, k.ord;
2563
2660
  `;
2661
+ var CHECKS_QUERY = `
2662
+ SELECT
2663
+ cl.relname AS table_name,
2664
+ c.conname AS constraint_name,
2665
+ pg_get_constraintdef(c.oid) AS definition
2666
+ FROM pg_catalog.pg_constraint c
2667
+ JOIN pg_catalog.pg_class cl ON cl.oid = c.conrelid
2668
+ JOIN pg_catalog.pg_namespace n ON n.oid = cl.relnamespace
2669
+ WHERE c.contype = 'c'
2670
+ AND n.nspname = 'public'
2671
+ ORDER BY cl.relname, c.conname;
2672
+ `;
2564
2673
  var FOREIGN_KEYS_QUERY = `
2565
2674
  SELECT
2566
2675
  c.conname AS constraint_name,
@@ -2606,14 +2715,15 @@ var INDEXES_QUERY = `
2606
2715
  `;
2607
2716
  async function introspectPostgres(params) {
2608
2717
  const { executor } = params;
2609
- const [tableRows, viewRows, columnRows, constraintRows, fkRows, enumRows, indexRows] = await Promise.all([
2718
+ const [tableRows, viewRows, columnRows, constraintRows, fkRows, enumRows, indexRows, checkRows] = await Promise.all([
2610
2719
  executor({ text: TABLES_QUERY }),
2611
2720
  executor({ text: VIEWS_QUERY }),
2612
2721
  executor({ text: COLUMNS_QUERY }),
2613
2722
  executor({ text: CONSTRAINTS_QUERY }),
2614
2723
  executor({ text: FOREIGN_KEYS_QUERY }),
2615
2724
  executor({ text: ENUMS_QUERY }),
2616
- executor({ text: INDEXES_QUERY })
2725
+ executor({ text: INDEXES_QUERY }),
2726
+ executor({ text: CHECKS_QUERY })
2617
2727
  ]);
2618
2728
  const tables = tableRows.map((row) => row.table_name);
2619
2729
  const views = viewRows.map((row) => row.table_name);
@@ -2668,6 +2778,17 @@ async function introspectPostgres(params) {
2668
2778
  entry.columns.push(columnName);
2669
2779
  }
2670
2780
  }
2781
+ const checksByTable = new Map;
2782
+ for (const row of checkRows) {
2783
+ const table = row.table_name;
2784
+ const name = row.constraint_name;
2785
+ const expression = stripCheckDefinition({ definition: String(row.definition ?? "") });
2786
+ const list = checksByTable.get(table);
2787
+ if (list === undefined)
2788
+ checksByTable.set(table, [{ name, expression }]);
2789
+ else
2790
+ list.push({ name, expression });
2791
+ }
2671
2792
  const fkGroupsByName = new Map;
2672
2793
  for (const row of fkRows) {
2673
2794
  const constraintName = row.constraint_name;
@@ -2727,7 +2848,8 @@ async function introspectPostgres(params) {
2727
2848
  uniquesByConstraint,
2728
2849
  constraintNames,
2729
2850
  indexes,
2730
- enumNames
2851
+ enumNames,
2852
+ checks: checksByTable.get(table) ?? []
2731
2853
  }));
2732
2854
  }
2733
2855
  for (const view of views) {
@@ -2739,7 +2861,8 @@ async function introspectPostgres(params) {
2739
2861
  uniquesByConstraint,
2740
2862
  constraintNames,
2741
2863
  indexes: [],
2742
- enumNames
2864
+ enumNames,
2865
+ checks: []
2743
2866
  }));
2744
2867
  }
2745
2868
  addFkRelations({ models, fkGroups, joinTables });
@@ -2769,8 +2892,9 @@ function buildModel(params) {
2769
2892
  for (const index of tableIndexes) {
2770
2893
  if (!index.unique)
2771
2894
  continue;
2772
- if (index.columns.length === 1)
2895
+ if (index.columns.length === 1 && index.where === undefined && index.nullsNotDistinct !== true) {
2773
2896
  uniqueSingles.add(index.columns[0].name);
2897
+ }
2774
2898
  }
2775
2899
  const fields = tableColumns.map((column) => buildField({ column, pk, uniqueSingles, enumNames }));
2776
2900
  const modelIndexes = [];
@@ -2782,8 +2906,9 @@ function buildModel(params) {
2782
2906
  });
2783
2907
  }
2784
2908
  for (const index of tableIndexes) {
2785
- if (index.unique && index.columns.length === 1)
2909
+ if (index.unique && index.columns.length === 1 && index.where === undefined && index.nullsNotDistinct !== true) {
2786
2910
  continue;
2911
+ }
2787
2912
  modelIndexes.push({
2788
2913
  name: index.name,
2789
2914
  kind: index.unique ? "unique" : index.kind,
@@ -2791,7 +2916,8 @@ function buildModel(params) {
2791
2916
  name: column.name,
2792
2917
  ...column.desc ? { sort: "desc" } : {}
2793
2918
  })),
2794
- ...index.where === undefined ? {} : { where: index.where }
2919
+ ...index.where === undefined ? {} : { where: index.where },
2920
+ ...index.nullsNotDistinct === true ? { nullsNotDistinct: true } : {}
2795
2921
  });
2796
2922
  }
2797
2923
  return {
@@ -2799,10 +2925,16 @@ function buildModel(params) {
2799
2925
  fields,
2800
2926
  relations: [],
2801
2927
  indexes: modelIndexes,
2928
+ ...params.checks.length === 0 ? {} : { checks: params.checks },
2802
2929
  isView,
2803
2930
  ...pk.length > 1 ? { primaryKey: { fields: pk } } : {}
2804
2931
  };
2805
2932
  }
2933
+ function stripCheckDefinition(params) {
2934
+ const trimmed = params.definition.trim();
2935
+ const match = trimmed.match(/^CHECK\s*\((.*)\)$/is);
2936
+ return match === null ? trimmed : match[1].trim();
2937
+ }
2806
2938
  function buildField(params) {
2807
2939
  const { column, pk, uniqueSingles, enumNames } = params;
2808
2940
  const mapped = reverseMapType({ column, enumNames });
@@ -2953,7 +3085,10 @@ function parseIndexDef(params) {
2953
3085
  const nameOnly = trimmed.replace(/ (DESC|ASC)( NULLS (FIRST|LAST))?$/i, "");
2954
3086
  return { name: nameOnly.replaceAll('"', ""), desc };
2955
3087
  });
2956
- const tail = def.slice(end + 1).trim();
3088
+ let tail = def.slice(end + 1).trim();
3089
+ const nullsNotDistinct = /^NULLS NOT DISTINCT\b/i.test(tail);
3090
+ if (nullsNotDistinct)
3091
+ tail = tail.slice("NULLS NOT DISTINCT".length).trim();
2957
3092
  const whereMatch = tail.match(/^WHERE (.*)$/);
2958
3093
  const whereRaw = whereMatch?.[1];
2959
3094
  return {
@@ -2962,7 +3097,8 @@ function parseIndexDef(params) {
2962
3097
  unique: head[1] !== undefined,
2963
3098
  kind: head[2].toLowerCase(),
2964
3099
  columns,
2965
- ...whereRaw === undefined ? {} : { where: whereRaw }
3100
+ ...whereRaw === undefined ? {} : { where: whereRaw },
3101
+ ...nullsNotDistinct ? { nullsNotDistinct: true } : {}
2966
3102
  };
2967
3103
  }
2968
3104
  function splitTopLevel(params) {
@@ -3026,6 +3162,7 @@ async function introspectSqlite(params) {
3026
3162
  const indexes = [];
3027
3163
  const uniqueSinglesByTable = new Map;
3028
3164
  const modelIndexesByTable = new Map;
3165
+ const checksByTable = new Map;
3029
3166
  const fkGroups = [];
3030
3167
  const enumsByColumn = new Map;
3031
3168
  const enumRegistry = new Map;
@@ -3033,15 +3170,18 @@ async function introspectSqlite(params) {
3033
3170
  for (const table of tables) {
3034
3171
  const columns = await readColumns({ executor, table: table.name });
3035
3172
  columnsByTable.set(table.name, columns);
3173
+ const enumCheckNames = new Set;
3036
3174
  for (const check of parseSqliteEnumChecks({ tableSql: table.sql ?? "" })) {
3037
3175
  enumsByColumn.set(`${table.name}.${check.column}`, {
3038
3176
  enumName: check.enumName,
3039
3177
  values: check.values
3040
3178
  });
3179
+ enumCheckNames.add(sqliteEnumCheckName({ table: table.name, column: check.column, enumName: check.enumName }));
3041
3180
  if (!enumRegistry.has(check.enumName)) {
3042
3181
  enumRegistry.set(check.enumName, { name: check.enumName, values: check.values });
3043
3182
  }
3044
3183
  }
3184
+ checksByTable.set(table.name, parseSqliteNamedChecks({ tableSql: table.sql ?? "" }).filter((check) => !enumCheckNames.has(check.name)));
3045
3185
  if (/PRIMARY KEY AUTOINCREMENT/i.test(table.sql ?? "")) {
3046
3186
  autoincrementTables.add(table.name);
3047
3187
  }
@@ -3077,7 +3217,8 @@ async function introspectSqlite(params) {
3077
3217
  uniqueSingles: uniqueSinglesByTable.get(table.name) ?? new Set,
3078
3218
  modelIndexes: modelIndexesByTable.get(table.name) ?? [],
3079
3219
  enumsByColumn,
3080
- autoincrement: autoincrementTables.has(table.name)
3220
+ autoincrement: autoincrementTables.has(table.name),
3221
+ checks: checksByTable.get(table.name) ?? []
3081
3222
  }));
3082
3223
  }
3083
3224
  for (const view of views) {
@@ -3088,7 +3229,8 @@ async function introspectSqlite(params) {
3088
3229
  uniqueSingles: new Set,
3089
3230
  modelIndexes: [],
3090
3231
  enumsByColumn,
3091
- autoincrement: false
3232
+ autoincrement: false,
3233
+ checks: []
3092
3234
  }));
3093
3235
  }
3094
3236
  addFkRelations({ models, fkGroups, joinTables });
@@ -3289,6 +3431,7 @@ function buildModel2(params) {
3289
3431
  fields,
3290
3432
  relations: [],
3291
3433
  indexes: [...modelIndexes],
3434
+ ...params.checks.length === 0 ? {} : { checks: params.checks },
3292
3435
  isView,
3293
3436
  ...pk.length > 1 ? { primaryKey: { fields: pk } } : {}
3294
3437
  };
@@ -3394,6 +3537,19 @@ var REFERENTIAL_QUERY = `
3394
3537
  FROM information_schema.referential_constraints
3395
3538
  WHERE constraint_schema = DATABASE();
3396
3539
  `;
3540
+ var CHECKS_QUERY2 = `
3541
+ SELECT
3542
+ tc.table_name AS table_name,
3543
+ tc.constraint_name AS constraint_name,
3544
+ cc.check_clause AS definition
3545
+ FROM information_schema.table_constraints tc
3546
+ JOIN information_schema.check_constraints cc
3547
+ ON cc.constraint_schema = tc.constraint_schema
3548
+ AND cc.constraint_name = tc.constraint_name
3549
+ WHERE tc.table_schema = DATABASE()
3550
+ AND tc.constraint_type = 'CHECK'
3551
+ ORDER BY tc.table_name, tc.constraint_name;
3552
+ `;
3397
3553
  var MYSQL_RULE_TO_CASCADE = {
3398
3554
  "NO ACTION": "NoAction",
3399
3555
  RESTRICT: "Restrict",
@@ -3407,14 +3563,26 @@ function catalogField(params) {
3407
3563
  }
3408
3564
  async function introspectMysql(params) {
3409
3565
  const { executor } = params;
3410
- const [tableRows, viewRows, columnRows, statisticRows, fkRows, refRows] = await Promise.all([
3566
+ const [tableRows, viewRows, columnRows, statisticRows, fkRows, refRows, checkRows] = await Promise.all([
3411
3567
  executor({ text: TABLES_QUERY2 }),
3412
3568
  executor({ text: VIEWS_QUERY2 }),
3413
3569
  executor({ text: COLUMNS_QUERY2 }),
3414
3570
  executor({ text: STATISTICS_QUERY }),
3415
3571
  executor({ text: FOREIGN_KEYS_QUERY2 }),
3416
- executor({ text: REFERENTIAL_QUERY })
3572
+ executor({ text: REFERENTIAL_QUERY }),
3573
+ executor({ text: CHECKS_QUERY2 })
3417
3574
  ]);
3575
+ const checksByTable = new Map;
3576
+ for (const row of checkRows) {
3577
+ const table = catalogField({ row, name: "table_name" });
3578
+ const name = catalogField({ row, name: "constraint_name" });
3579
+ const expression = stripBalancedOuterParens(String(catalogField({ row, name: "definition" }) ?? ""));
3580
+ const list = checksByTable.get(table);
3581
+ if (list === undefined)
3582
+ checksByTable.set(table, [{ name, expression }]);
3583
+ else
3584
+ list.push({ name, expression });
3585
+ }
3418
3586
  const tables = tableRows.map((row) => catalogField({ row, name: "table_name" }));
3419
3587
  const views = viewRows.map((row) => catalogField({ row, name: "table_name" }));
3420
3588
  const columns = columnRows.map((row) => ({
@@ -3524,10 +3692,26 @@ async function introspectMysql(params) {
3524
3692
  for (const table of tables) {
3525
3693
  if (joinTables.has(table))
3526
3694
  continue;
3527
- models.push(buildModel3({ table, isView: false, columns, pkByTable, indexes, enums }));
3695
+ models.push(buildModel3({
3696
+ table,
3697
+ isView: false,
3698
+ columns,
3699
+ pkByTable,
3700
+ indexes,
3701
+ enums,
3702
+ checks: checksByTable.get(table) ?? []
3703
+ }));
3528
3704
  }
3529
3705
  for (const view of views) {
3530
- models.push(buildModel3({ table: view, isView: true, columns, pkByTable: new Map, indexes: [], enums }));
3706
+ models.push(buildModel3({
3707
+ table: view,
3708
+ isView: true,
3709
+ columns,
3710
+ pkByTable: new Map,
3711
+ indexes: [],
3712
+ enums,
3713
+ checks: []
3714
+ }));
3531
3715
  }
3532
3716
  addFkRelations({ models, fkGroups, joinTables });
3533
3717
  addJoinTableRelations({ models, joinTables });
@@ -3584,10 +3768,28 @@ function buildModel3(params) {
3584
3768
  fields,
3585
3769
  relations: [],
3586
3770
  indexes: modelIndexes,
3771
+ ...params.checks.length === 0 ? {} : { checks: params.checks },
3587
3772
  isView,
3588
3773
  ...pk.length > 1 ? { primaryKey: { fields: pk } } : {}
3589
3774
  };
3590
3775
  }
3776
+ function stripBalancedOuterParens(text) {
3777
+ const trimmed = text.trim();
3778
+ if (!trimmed.startsWith("(") || !trimmed.endsWith(")"))
3779
+ return trimmed;
3780
+ let depth = 0;
3781
+ for (let index = 0;index < trimmed.length; index++) {
3782
+ const char = trimmed[index];
3783
+ if (char === "(")
3784
+ depth += 1;
3785
+ else if (char === ")") {
3786
+ depth -= 1;
3787
+ if (depth === 0 && index < trimmed.length - 1)
3788
+ return trimmed;
3789
+ }
3790
+ }
3791
+ return trimmed.slice(1, -1).trim();
3792
+ }
3591
3793
  var CANONICAL_MYSQL = {
3592
3794
  "varchar(191)": "String",
3593
3795
  "tinyint(1)": "Boolean",
@@ -3855,4 +4057,4 @@ export {
3855
4057
  MIGRATIONS_TABLE_NAME
3856
4058
  };
3857
4059
 
3858
- //# debugId=5088DA7EA679FA1464756E2164756E21
4060
+ //# debugId=CA4C1473A398313F64756E2164756E21