Skip to content

Commit

Permalink
chore: Break up codegen/index.ts.
Browse files Browse the repository at this point in the history
  • Loading branch information
stephenh committed May 30, 2024
1 parent ffe1fd1 commit 1119131
Show file tree
Hide file tree
Showing 5 changed files with 294 additions and 266 deletions.
2 changes: 1 addition & 1 deletion packages/codegen/src/config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import { promises as fs } from "fs";
import { groupBy } from "joist-utils";
import { z } from "zod";
import { getThisVersion } from "./codemods";
import { getStiEntities } from "./index";
import { getStiEntities } from "./inheritance";
import { fail, sortKeys, trueIfResolved } from "./utils";

const jsonFormatter = createFromBuffer(getBuffer());
Expand Down
72 changes: 72 additions & 0 deletions packages/codegen/src/foreignKeyOrdering.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
import { promises as fs } from "fs";
import { ManyToOneField, PolymorphicFieldComponent } from "./EntityDbMetadata";
import { Config, EntityDbMetadata } from "./index";
import { sortByNonDeferredForeignKeys } from "./sortForeignKeys";
import { assertNever } from "./utils";

export async function maybeSetForeignKeyOrdering(config: Config, entities: EntityDbMetadata[]): Promise<boolean> {
// Hopefully all FKs are deferred, but if not...
const hasAnyNonDeferredFks = entities.some((e) => e.nonDeferredFks.length > 0);
if (!hasAnyNonDeferredFks) {
// Great, nothing to do
for (const entity of entities) entity.nonDeferredFkOrder = 0;
return false;
}

let hasError = false;

// Topo sort the non-deferred FKs into a DAG to establish insert/flush order
const { notNullCycles } = sortByNonDeferredForeignKeys(entities);

const { nonDeferredForeignKeys: setting = "warn" } = config;
const nonDeferredFks = entities.flatMap((e) => e.nonDeferredFks.map((m2o) => ({ entity: e, m2o })));

if (setting === "error" || setting === "warn") {
const flag = setting === "error" ? "ERROR" : "WARNING";
console.log(`${flag}: Found ${nonDeferredFks.length} foreign keys that are not DEFERRABLE/INITIALLY DEFERRED`);
for (const { entity, m2o } of nonDeferredFks) console.log(`${entity.tableName}.${m2o.columnName}`);
console.log("");

console.log("Please either:");
console.log(" - Alter your migration to create the FKs as deferred (ideal)");
console.log(" - Execute the generated alter-foreign-keys.sql file (one-time fix)");
console.log(" - Set 'nonDeferredFks: ignore' in joist-config.json");
console.log("");

console.log("See https://joist-orm.io/docs/getting-started/schema-assumptions#deferred-constraints");
console.log("");

await writeAlterTables(nonDeferredFks);

if (setting === "error") hasError = true;
} else if (setting === "ignore") {
// We trust the user to know what they're doing
} else {
assertNever(setting);
}

// Always treat these as errors, even if `ignore` is set; maybe we need a `ignore-even-not-null-cycles`?
// But they would still probably break flush_test_database. Unless we tell it to disable constraints,
// like `ALTER TABLE your_table_name DISABLE TRIGGER ALL;`. But still unsure how we'd insert new rows.
if (notNullCycles.length > 0) {
console.log(`ERROR: Found a schema cycle of not-null foreign keys:`);
notNullCycles.forEach((cycle) => console.log(cycle));
console.log("");
console.log("These cycles can cause fatal em.flush & flush_test_database errors.");
console.log("");
console.log("Please make one of the FKs involved in the cycle nullable.");
console.log("");
hasError ??= true;
}

return hasError;
}

async function writeAlterTables(
nonDeferredFks: Array<{ entity: EntityDbMetadata; m2o: ManyToOneField | PolymorphicFieldComponent }>,
): Promise<void> {
const queries = nonDeferredFks.map(({ entity, m2o }) => {
return `ALTER TABLE ${entity.tableName} ALTER CONSTRAINT ${m2o.constraintName} DEFERRABLE INITIALLY DEFERRED;`;
});
await fs.writeFile("./alter-foreign-keys.sql", queries.join("\n"));
}
2 changes: 1 addition & 1 deletion packages/codegen/src/generateEntityCodegenFile.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ import {
PrimitiveTypescriptType,
} from "./EntityDbMetadata";
import { Config, hasConfigDefault } from "./config";
import { getStiEntities } from "./index";
import { getStiEntities } from "./inheritance";
import { keywords } from "./keywords";
import {
BaseEntity,
Expand Down

0 comments on commit 1119131

Please sign in to comment.