@stacksjs/database 0.70.106 → 0.70.107

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.
Files changed (2) hide show
  1. package/dist/migrations.js +65 -4
  2. package/package.json +10 -10
@@ -537,16 +537,76 @@ ${readFileSync(join(migrationsDir, f), "utf8")}`;
537
537
  }
538
538
  return written;
539
539
  }
540
+ function normalizeCreateStatements(sqlStatements) {
541
+ const creates = [], constraints = [], passthrough = [];
542
+ for (const raw of sqlStatements) {
543
+ const statement = raw.trim();
544
+ if (!statement)
545
+ continue;
546
+ const create = statement.match(/^CREATE\s+TABLE\s+(?:IF\s+NOT\s+EXISTS\s+)?["`]?(\w+)["`]?/i);
547
+ if (create?.[1]) {
548
+ creates.push({ statement, table: create[1] });
549
+ continue;
550
+ }
551
+ const constraint = statement.match(/^ALTER\s+TABLE\s+["`]?(\w+)["`]?\s+ADD\s+CONSTRAINT\s+([\s\S]+?);?$/i);
552
+ if (constraint?.[1] && constraint[2]) {
553
+ const references = [...constraint[2].matchAll(/REFERENCES\s+["`]?(\w+)["`]?/gi)].flatMap((match) => match[1] ? [match[1]] : []);
554
+ constraints.push({ body: `CONSTRAINT ${constraint[2].replace(/;\s*$/, "")}`, references, statement, table: constraint[1] });
555
+ continue;
556
+ }
557
+ passthrough.push(statement);
558
+ }
559
+ if (creates.length === 0)
560
+ return sqlStatements.map((statement) => statement.trim()).filter(Boolean);
561
+ const createdTables = new Set(creates.map((create) => create.table)), createOrder = new Map(creates.map((create, index) => [create.table, index])), relevantConstraints = constraints.filter((constraint) => createdTables.has(constraint.table)), unrelatedConstraints = constraints.filter((constraint) => !createdTables.has(constraint.table)), dependencies = new Map(creates.map((create) => [
562
+ create.table,
563
+ new Set(relevantConstraints.filter((constraint) => constraint.table === create.table).flatMap((constraint) => constraint.references).filter((reference) => reference !== create.table && createdTables.has(reference)))
564
+ ])), sortTables = (ignoredEdges = new Set) => {
565
+ const remaining = new Set(createdTables), sorted = [];
566
+ while (remaining.size > 0) {
567
+ const ready = [...remaining].filter((table) => [...dependencies.get(table) ?? []].every((dependency) => {
568
+ return !remaining.has(dependency) || ignoredEdges.has(`${table}->${dependency}`);
569
+ })).sort((a, b) => (createOrder.get(a) ?? 0) - (createOrder.get(b) ?? 0));
570
+ if (ready.length === 0)
571
+ break;
572
+ for (const table of ready) {
573
+ remaining.delete(table);
574
+ sorted.push(table);
575
+ }
576
+ }
577
+ return sorted;
578
+ }, initiallySorted = sortTables(), cyclicTables = new Set([...createdTables].filter((table) => !initiallySorted.includes(table))), deferred = relevantConstraints.filter((constraint) => constraint.references.some((reference) => {
579
+ return reference !== constraint.table && cyclicTables.has(constraint.table) && cyclicTables.has(reference);
580
+ })), deferredStatements = new Set(deferred.map((constraint) => constraint.statement)), ignoredEdges = new Set(deferred.flatMap((constraint) => constraint.references.map((reference) => `${constraint.table}->${reference}`))), orderedTables = sortTables(ignoredEdges), byTable = new Map(creates.map((create) => [create.table, create]));
581
+ return [
582
+ ...orderedTables.map((table) => {
583
+ const create = byTable.get(table), inline = relevantConstraints.filter((constraint) => constraint.table === table && !deferredStatements.has(constraint.statement));
584
+ if (inline.length === 0)
585
+ return create.statement;
586
+ const closing = create.statement.lastIndexOf(")");
587
+ if (closing < 0)
588
+ return create.statement;
589
+ const before = create.statement.slice(0, closing).trimEnd(), after = create.statement.slice(closing);
590
+ return `${before},
591
+ ${inline.map((constraint) => constraint.body).join(`,
592
+ `)}
593
+ ${after}`;
594
+ }),
595
+ ...passthrough,
596
+ ...unrelatedConstraints.map((constraint) => constraint.statement),
597
+ ...deferred.map((constraint) => constraint.statement)
598
+ ];
599
+ }
540
600
  export function groupGeneratedStatements(sqlStatements) {
541
- const groups = new Map, push = (label, stmt) => {
601
+ const normalizedStatements = normalizeCreateStatements(sqlStatements), groups = new Map, push = (label, stmt) => {
542
602
  const list = groups.get(label) ?? [];
543
603
  list.push(stmt);
544
604
  groups.set(label, list);
545
- }, createdTables = new Set(sqlStatements.flatMap((raw) => {
605
+ }, createdTables = new Set(normalizedStatements.flatMap((raw) => {
546
606
  const match = raw.trim().match(/^\s*CREATE\s+TABLE\s+(?:IF\s+NOT\s+EXISTS\s+)?["`]?(\w+)["`]?/i);
547
607
  return match?.[1] ? [match[1]] : [];
548
608
  }));
549
- for (const raw of sqlStatements) {
609
+ for (const raw of normalizedStatements) {
550
610
  const stmt = raw.trim();
551
611
  if (!stmt)
552
612
  continue;
@@ -557,7 +617,8 @@ export function groupGeneratedStatements(sqlStatements) {
557
617
  }
558
618
  const alter = stmt.match(/^\s*ALTER\s+TABLE\s+["`]?(\w+)["`]?\s+(?:ADD\s+COLUMN\s+["`]?(\w+)["`]?|DROP\s+COLUMN\s+["`]?(\w+)["`]?|ADD\s+CONSTRAINT)/i);
559
619
  if (alter) {
560
- push(`alter-${alter[1]}-${alter[2] || alter[3] || "constraint"}`, stmt);
620
+ const isCreateTimeConstraint = createdTables.has(alter[1]) && !alter[2] && !alter[3];
621
+ push(isCreateTimeConstraint ? "create-foreign-key-constraints" : `alter-${alter[1]}-${alter[2] || alter[3] || "constraint"}`, stmt);
561
622
  continue;
562
623
  }
563
624
  const idx = stmt.match(/^\s*CREATE\s+(?:UNIQUE\s+)?INDEX\s+(?:IF\s+NOT\s+EXISTS\s+)?["`]?(\w+)["`]?\s+ON\s+["`]?(\w+)["`]?/i), idxName = idx?.[1], idxTable = idx?.[2];
package/package.json CHANGED
@@ -2,7 +2,7 @@
2
2
  "name": "@stacksjs/database",
3
3
  "type": "module",
4
4
  "sideEffects": false,
5
- "version": "0.70.106",
5
+ "version": "0.70.107",
6
6
  "description": "The Stacks database integration.",
7
7
  "author": "Chris Breuer",
8
8
  "contributors": [
@@ -58,15 +58,15 @@
58
58
  "bun-query-builder": "^0.1.54"
59
59
  },
60
60
  "devDependencies": {
61
- "@stacksjs/cli": "0.70.106",
62
- "@stacksjs/config": "0.70.106",
63
- "@stacksjs/logging": "0.70.106",
64
- "@stacksjs/router": "0.70.106",
61
+ "@stacksjs/cli": "0.70.107",
62
+ "@stacksjs/config": "0.70.107",
63
+ "@stacksjs/logging": "0.70.107",
64
+ "@stacksjs/router": "0.70.107",
65
65
  "better-dx": "^0.2.16",
66
- "@stacksjs/path": "0.70.106",
67
- "@stacksjs/query-builder": "0.70.106",
68
- "@stacksjs/storage": "0.70.106",
69
- "@stacksjs/strings": "0.70.106",
70
- "@stacksjs/utils": "0.70.106"
66
+ "@stacksjs/path": "0.70.107",
67
+ "@stacksjs/query-builder": "0.70.107",
68
+ "@stacksjs/storage": "0.70.107",
69
+ "@stacksjs/strings": "0.70.107",
70
+ "@stacksjs/utils": "0.70.107"
71
71
  }
72
72
  }