@memberjunction/codegen-lib 6.1.0-edge.3 → 6.1.0-edge.5

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 (107) hide show
  1. package/README.md +50 -21
  2. package/dist/Config/config.d.ts +290 -11
  3. package/dist/Config/config.d.ts.map +1 -1
  4. package/dist/Config/config.js +259 -9
  5. package/dist/Config/config.js.map +1 -1
  6. package/dist/Config/schema-output.d.ts +33 -0
  7. package/dist/Config/schema-output.d.ts.map +1 -0
  8. package/dist/Config/schema-output.js +52 -0
  9. package/dist/Config/schema-output.js.map +1 -0
  10. package/dist/Database/__tests__/exclude-schema-permissions.test.d.ts +2 -0
  11. package/dist/Database/__tests__/exclude-schema-permissions.test.d.ts.map +1 -0
  12. package/dist/Database/__tests__/exclude-schema-permissions.test.js +32 -0
  13. package/dist/Database/__tests__/exclude-schema-permissions.test.js.map +1 -0
  14. package/dist/Database/codeGenDatabaseProvider.d.ts +126 -2
  15. package/dist/Database/codeGenDatabaseProvider.d.ts.map +1 -1
  16. package/dist/Database/codeGenDatabaseProvider.js +203 -0
  17. package/dist/Database/codeGenDatabaseProvider.js.map +1 -1
  18. package/dist/Database/exclude-tables.d.ts +26 -0
  19. package/dist/Database/exclude-tables.d.ts.map +1 -0
  20. package/dist/Database/exclude-tables.js +38 -0
  21. package/dist/Database/exclude-tables.js.map +1 -0
  22. package/dist/Database/heal-schema-params.d.ts +33 -0
  23. package/dist/Database/heal-schema-params.d.ts.map +1 -0
  24. package/dist/Database/heal-schema-params.js +51 -0
  25. package/dist/Database/heal-schema-params.js.map +1 -0
  26. package/dist/Database/manage-metadata.d.ts +59 -0
  27. package/dist/Database/manage-metadata.d.ts.map +1 -1
  28. package/dist/Database/manage-metadata.js +157 -61
  29. package/dist/Database/manage-metadata.js.map +1 -1
  30. package/dist/Database/providers/postgresql/PostgreSQLCodeGenProvider.d.ts +25 -35
  31. package/dist/Database/providers/postgresql/PostgreSQLCodeGenProvider.d.ts.map +1 -1
  32. package/dist/Database/providers/postgresql/PostgreSQLCodeGenProvider.js +97 -51
  33. package/dist/Database/providers/postgresql/PostgreSQLCodeGenProvider.js.map +1 -1
  34. package/dist/Database/providers/postgresql/__tests__/SoftPrimaryKeyIndex.test.d.ts +2 -0
  35. package/dist/Database/providers/postgresql/__tests__/SoftPrimaryKeyIndex.test.d.ts.map +1 -0
  36. package/dist/Database/providers/postgresql/__tests__/SoftPrimaryKeyIndex.test.js +110 -0
  37. package/dist/Database/providers/postgresql/__tests__/SoftPrimaryKeyIndex.test.js.map +1 -0
  38. package/dist/Database/providers/postgresql/metadataSupportObjects.js +78 -10
  39. package/dist/Database/providers/postgresql/metadataSupportObjects.js.map +1 -1
  40. package/dist/Database/providers/sqlserver/SQLServerCodeGenProvider.d.ts +14 -0
  41. package/dist/Database/providers/sqlserver/SQLServerCodeGenProvider.d.ts.map +1 -1
  42. package/dist/Database/providers/sqlserver/SQLServerCodeGenProvider.js +38 -0
  43. package/dist/Database/providers/sqlserver/SQLServerCodeGenProvider.js.map +1 -1
  44. package/dist/Database/providers/sqlserver/__tests__/SQLServerCodeGenProvider.test.js +17 -0
  45. package/dist/Database/providers/sqlserver/__tests__/SQLServerCodeGenProvider.test.js.map +1 -1
  46. package/dist/Database/providers/sqlserver/__tests__/SoftPrimaryKeyIndex.test.d.ts +2 -0
  47. package/dist/Database/providers/sqlserver/__tests__/SoftPrimaryKeyIndex.test.d.ts.map +1 -0
  48. package/dist/Database/providers/sqlserver/__tests__/SoftPrimaryKeyIndex.test.js +195 -0
  49. package/dist/Database/providers/sqlserver/__tests__/SoftPrimaryKeyIndex.test.js.map +1 -0
  50. package/dist/Database/schema-filters.d.ts +23 -0
  51. package/dist/Database/schema-filters.d.ts.map +1 -0
  52. package/dist/Database/schema-filters.js +42 -0
  53. package/dist/Database/schema-filters.js.map +1 -0
  54. package/dist/Database/schema-scope.d.ts.map +1 -1
  55. package/dist/Database/schema-scope.js +4 -0
  56. package/dist/Database/schema-scope.js.map +1 -1
  57. package/dist/Database/sql_codegen.d.ts +33 -0
  58. package/dist/Database/sql_codegen.d.ts.map +1 -1
  59. package/dist/Database/sql_codegen.js +147 -56
  60. package/dist/Database/sql_codegen.js.map +1 -1
  61. package/dist/EntityNameScanner/EntityNameScanner.d.ts.map +1 -1
  62. package/dist/EntityNameScanner/EntityNameScanner.js +33 -2
  63. package/dist/EntityNameScanner/EntityNameScanner.js.map +1 -1
  64. package/dist/Manifest/GenerateClassRegistrationsManifest.d.ts +30 -4
  65. package/dist/Manifest/GenerateClassRegistrationsManifest.d.ts.map +1 -1
  66. package/dist/Manifest/GenerateClassRegistrationsManifest.js +175 -25
  67. package/dist/Manifest/GenerateClassRegistrationsManifest.js.map +1 -1
  68. package/dist/Misc/codegen-reporter.d.ts +3 -0
  69. package/dist/Misc/codegen-reporter.d.ts.map +1 -1
  70. package/dist/Misc/codegen-reporter.js +3 -0
  71. package/dist/Misc/codegen-reporter.js.map +1 -1
  72. package/dist/Misc/emit-stats.d.ts +28 -0
  73. package/dist/Misc/emit-stats.d.ts.map +1 -0
  74. package/dist/Misc/emit-stats.js +43 -0
  75. package/dist/Misc/emit-stats.js.map +1 -0
  76. package/dist/Misc/entity_subclasses_codegen.d.ts +42 -7
  77. package/dist/Misc/entity_subclasses_codegen.d.ts.map +1 -1
  78. package/dist/Misc/entity_subclasses_codegen.js +177 -52
  79. package/dist/Misc/entity_subclasses_codegen.js.map +1 -1
  80. package/dist/Misc/file-write.d.ts +15 -0
  81. package/dist/Misc/file-write.d.ts.map +1 -0
  82. package/dist/Misc/file-write.js +33 -0
  83. package/dist/Misc/file-write.js.map +1 -0
  84. package/dist/Misc/graphql_server_codegen.d.ts +12 -2
  85. package/dist/Misc/graphql_server_codegen.d.ts.map +1 -1
  86. package/dist/Misc/graphql_server_codegen.js +61 -12
  87. package/dist/Misc/graphql_server_codegen.js.map +1 -1
  88. package/dist/Misc/runCommand.d.ts +5 -0
  89. package/dist/Misc/runCommand.d.ts.map +1 -1
  90. package/dist/Misc/runCommand.js +43 -17
  91. package/dist/Misc/runCommand.js.map +1 -1
  92. package/dist/Misc/schema-emit.d.ts +123 -0
  93. package/dist/Misc/schema-emit.d.ts.map +1 -0
  94. package/dist/Misc/schema-emit.js +220 -0
  95. package/dist/Misc/schema-emit.js.map +1 -0
  96. package/dist/Misc/system_integrity.d.ts.map +1 -1
  97. package/dist/Misc/system_integrity.js +5 -0
  98. package/dist/Misc/system_integrity.js.map +1 -1
  99. package/dist/index.d.ts +6 -0
  100. package/dist/index.d.ts.map +1 -1
  101. package/dist/index.js +6 -0
  102. package/dist/index.js.map +1 -1
  103. package/dist/runCodeGen.d.ts +20 -2
  104. package/dist/runCodeGen.d.ts.map +1 -1
  105. package/dist/runCodeGen.js +147 -21
  106. package/dist/runCodeGen.js.map +1 -1
  107. package/package.json +29 -29
@@ -0,0 +1 @@
1
+ {"version":3,"file":"schema-filters.d.ts","sourceRoot":"","sources":["../../src/Database/schema-filters.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,kEAAkE;AAClE,wBAAgB,gBAAgB,CAAC,MAAM,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,GAAG,SAAS,GAAG,IAAI,GAAG,OAAO,CAM3F;AAED;;;;;GAKG;AACH,wBAAgB,iCAAiC,CAC7C,YAAY,EAAE,MAAM,EACpB,aAAa,EAAE,MAAM,EACrB,8BAA8B,EAAE,OAAO,GACxC,OAAO,CAKT;AAED;;;;;;;GAOG;AACH,wBAAgB,kCAAkC,CAC9C,UAAU,EAAE,MAAM,EAClB,cAAc,EAAE,MAAM,EAAE,EACxB,cAAc,CAAC,EAAE,MAAM,EAAE,GAAG,IAAI,GACjC,OAAO,CAQT"}
@@ -0,0 +1,42 @@
1
+ /**
2
+ * Pure schema-scope predicates used by CodeGen SQL generation.
3
+ * Kept out of sql_codegen.ts so unit tests do not load the full generator.
4
+ */
5
+ /** Case-insensitive, trimmed membership in a schema-name list. */
6
+ export function schemaNameInList(schema, list) {
7
+ if (!list || list.length === 0) {
8
+ return false;
9
+ }
10
+ const key = schema.trim().toLowerCase();
11
+ return list.some((s) => s.trim().toLowerCase() === key);
12
+ }
13
+ /**
14
+ * Cascade SQL for a related entity whose FK points at `parentSchema`.
15
+ * Default is intra-schema only. `allowCrossSchemaCascadeDeletes` restores the
16
+ * historical walk across every schema in metadata — that is a dangerous
17
+ * escape hatch and must stay off for Open Apps.
18
+ */
19
+ export function shouldEmitCascadeForRelatedEntity(parentSchema, relatedSchema, allowCrossSchemaCascadeDeletes) {
20
+ if (allowCrossSchemaCascadeDeletes) {
21
+ return true;
22
+ }
23
+ return parentSchema.trim().toLowerCase() === relatedSchema.trim().toLowerCase();
24
+ }
25
+ /**
26
+ * Whether a custom/layered base view for `schemaName` may be `sp_refreshview`'d
27
+ * into the migration log (STEP 4.5).
28
+ *
29
+ * Always drop `excludeSchemas`. When `includeSchemas` is non-empty, keep only
30
+ * that positive list. When it is empty/unset, keep current "all modified
31
+ * custom views" behavior minus excludes.
32
+ */
33
+ export function entityInCustomBaseViewRefreshScope(schemaName, excludeSchemas, includeSchemas) {
34
+ if (schemaNameInList(schemaName, excludeSchemas)) {
35
+ return false;
36
+ }
37
+ if (includeSchemas && includeSchemas.length > 0) {
38
+ return schemaNameInList(schemaName, includeSchemas);
39
+ }
40
+ return true;
41
+ }
42
+ //# sourceMappingURL=schema-filters.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"schema-filters.js","sourceRoot":"","sources":["../../src/Database/schema-filters.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,kEAAkE;AAClE,MAAM,UAAU,gBAAgB,CAAC,MAAc,EAAE,IAAiC;IAC9E,IAAI,CAAC,IAAI,IAAI,IAAI,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QAC7B,OAAO,KAAK,CAAC;IACjB,CAAC;IACD,MAAM,GAAG,GAAG,MAAM,CAAC,IAAI,EAAE,CAAC,WAAW,EAAE,CAAC;IACxC,OAAO,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC,WAAW,EAAE,KAAK,GAAG,CAAC,CAAC;AAC5D,CAAC;AAED;;;;;GAKG;AACH,MAAM,UAAU,iCAAiC,CAC7C,YAAoB,EACpB,aAAqB,EACrB,8BAAuC;IAEvC,IAAI,8BAA8B,EAAE,CAAC;QACjC,OAAO,IAAI,CAAC;IAChB,CAAC;IACD,OAAO,YAAY,CAAC,IAAI,EAAE,CAAC,WAAW,EAAE,KAAK,aAAa,CAAC,IAAI,EAAE,CAAC,WAAW,EAAE,CAAC;AACpF,CAAC;AAED;;;;;;;GAOG;AACH,MAAM,UAAU,kCAAkC,CAC9C,UAAkB,EAClB,cAAwB,EACxB,cAAgC;IAEhC,IAAI,gBAAgB,CAAC,UAAU,EAAE,cAAc,CAAC,EAAE,CAAC;QAC/C,OAAO,KAAK,CAAC;IACjB,CAAC;IACD,IAAI,cAAc,IAAI,cAAc,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;QAC9C,OAAO,gBAAgB,CAAC,UAAU,EAAE,cAAc,CAAC,CAAC;IACxD,CAAC;IACD,OAAO,IAAI,CAAC;AAChB,CAAC"}
@@ -1 +1 @@
1
- {"version":3,"file":"schema-scope.d.ts","sourceRoot":"","sources":["../../src/Database/schema-scope.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAEH;;;;;;;;;;;;;GAaG;AACH,wBAAgB,qCAAqC,CACnD,UAAU,EAAE,MAAM,EAAE,EACpB,cAAc,EAAE,MAAM,EAAE,EACxB,sBAAsB,EAAE,MAAM,EAAE,GAC/B,MAAM,EAAE,CAYV;AAED,sEAAsE;AACtE,MAAM,WAAW,iBAAiB;IAChC,cAAc,CAAC,EAAE,MAAM,EAAE,CAAC;IAC1B,cAAc,CAAC,EAAE,MAAM,EAAE,CAAC;CAC3B;AAED;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,wBAAgB,uBAAuB,CAAC,UAAU,EAAE,MAAM,EAAE,EAAE,MAAM,EAAE,iBAAiB,GAAG,MAAM,EAAE,CAUjG"}
1
+ {"version":3,"file":"schema-scope.d.ts","sourceRoot":"","sources":["../../src/Database/schema-scope.ts"],"names":[],"mappings":"AAEA;;;;;;;;;;GAUG;AAEH;;;;;;;;;;;;;GAaG;AACH,wBAAgB,qCAAqC,CACnD,UAAU,EAAE,MAAM,EAAE,EACpB,cAAc,EAAE,MAAM,EAAE,EACxB,sBAAsB,EAAE,MAAM,EAAE,GAC/B,MAAM,EAAE,CAYV;AAED,sEAAsE;AACtE,MAAM,WAAW,iBAAiB;IAChC,cAAc,CAAC,EAAE,MAAM,EAAE,CAAC;IAC1B,cAAc,CAAC,EAAE,MAAM,EAAE,CAAC;CAC3B;AAED;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,wBAAgB,uBAAuB,CAAC,UAAU,EAAE,MAAM,EAAE,EAAE,MAAM,EAAE,iBAAiB,GAAG,MAAM,EAAE,CAajG"}
@@ -1,3 +1,4 @@
1
+ import { snapshotAuthoredExcludeSchemas } from './heal-schema-params.js';
1
2
  /**
2
3
  * Schema-scope resolution helpers for CodeGen.
3
4
  *
@@ -58,6 +59,9 @@ export function computeSchemasToExcludeForIncludeList(allSchemas, includeSchemas
58
59
  * @returns the schema names that were newly appended (empty when the scope is unused or already applied)
59
60
  */
60
61
  export function applyIncludeSchemaScope(allSchemas, config) {
62
+ // Capture authored excludes before this run mutates excludeSchemas. Heal EXEC
63
+ // statements must serialize that original list, not the sibling snapshot.
64
+ snapshotAuthoredExcludeSchemas(config.excludeSchemas);
61
65
  if (!config.includeSchemas || config.includeSchemas.length === 0) {
62
66
  return []; // classic exclude-only behavior, unchanged
63
67
  }
@@ -1 +1 @@
1
- {"version":3,"file":"schema-scope.js","sourceRoot":"","sources":["../../src/Database/schema-scope.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAEH;;;;;;;;;;;;;GAaG;AACH,MAAM,UAAU,qCAAqC,CACnD,UAAoB,EACpB,cAAwB,EACxB,sBAAgC;IAEhC,MAAM,UAAU,GAAG,IAAI,GAAG,CAAC,cAAc,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC,WAAW,EAAE,CAAC,CAAC,CAAC;IAC9E,MAAM,YAAY,GAAG,IAAI,GAAG,CAAC,sBAAsB,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC,WAAW,EAAE,CAAC,CAAC,CAAC;IACxF,MAAM,SAAS,GAAa,EAAE,CAAC;IAC/B,KAAK,MAAM,MAAM,IAAI,UAAU,EAAE,CAAC;QAChC,MAAM,GAAG,GAAG,MAAM,CAAC,IAAI,EAAE,CAAC,WAAW,EAAE,CAAC;QACxC,IAAI,CAAC,UAAU,CAAC,GAAG,CAAC,GAAG,CAAC,IAAI,CAAC,YAAY,CAAC,GAAG,CAAC,GAAG,CAAC,EAAE,CAAC;YACnD,SAAS,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;YACvB,YAAY,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC,CAAC,6CAA6C;QACtE,CAAC;IACH,CAAC;IACD,OAAO,SAAS,CAAC;AACnB,CAAC;AAQD;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,MAAM,UAAU,uBAAuB,CAAC,UAAoB,EAAE,MAAyB;IACrF,IAAI,CAAC,MAAM,CAAC,cAAc,IAAI,MAAM,CAAC,cAAc,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QACjE,OAAO,EAAE,CAAC,CAAC,2CAA2C;IACxD,CAAC;IACD,IAAI,CAAC,MAAM,CAAC,cAAc,EAAE,CAAC;QAC3B,MAAM,CAAC,cAAc,GAAG,EAAE,CAAC;IAC7B,CAAC;IACD,MAAM,SAAS,GAAG,qCAAqC,CAAC,UAAU,EAAE,MAAM,CAAC,cAAc,EAAE,MAAM,CAAC,cAAc,CAAC,CAAC;IAClH,MAAM,CAAC,cAAc,CAAC,IAAI,CAAC,GAAG,SAAS,CAAC,CAAC;IACzC,OAAO,SAAS,CAAC;AACnB,CAAC"}
1
+ {"version":3,"file":"schema-scope.js","sourceRoot":"","sources":["../../src/Database/schema-scope.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,8BAA8B,EAAE,MAAM,sBAAsB,CAAC;AAEtE;;;;;;;;;;GAUG;AAEH;;;;;;;;;;;;;GAaG;AACH,MAAM,UAAU,qCAAqC,CACnD,UAAoB,EACpB,cAAwB,EACxB,sBAAgC;IAEhC,MAAM,UAAU,GAAG,IAAI,GAAG,CAAC,cAAc,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC,WAAW,EAAE,CAAC,CAAC,CAAC;IAC9E,MAAM,YAAY,GAAG,IAAI,GAAG,CAAC,sBAAsB,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC,WAAW,EAAE,CAAC,CAAC,CAAC;IACxF,MAAM,SAAS,GAAa,EAAE,CAAC;IAC/B,KAAK,MAAM,MAAM,IAAI,UAAU,EAAE,CAAC;QAChC,MAAM,GAAG,GAAG,MAAM,CAAC,IAAI,EAAE,CAAC,WAAW,EAAE,CAAC;QACxC,IAAI,CAAC,UAAU,CAAC,GAAG,CAAC,GAAG,CAAC,IAAI,CAAC,YAAY,CAAC,GAAG,CAAC,GAAG,CAAC,EAAE,CAAC;YACnD,SAAS,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;YACvB,YAAY,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC,CAAC,6CAA6C;QACtE,CAAC;IACH,CAAC;IACD,OAAO,SAAS,CAAC;AACnB,CAAC;AAQD;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,MAAM,UAAU,uBAAuB,CAAC,UAAoB,EAAE,MAAyB;IACrF,8EAA8E;IAC9E,0EAA0E;IAC1E,8BAA8B,CAAC,MAAM,CAAC,cAAc,CAAC,CAAC;IACtD,IAAI,CAAC,MAAM,CAAC,cAAc,IAAI,MAAM,CAAC,cAAc,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QACjE,OAAO,EAAE,CAAC,CAAC,2CAA2C;IACxD,CAAC;IACD,IAAI,CAAC,MAAM,CAAC,cAAc,EAAE,CAAC;QAC3B,MAAM,CAAC,cAAc,GAAG,EAAE,CAAC;IAC7B,CAAC;IACD,MAAM,SAAS,GAAG,qCAAqC,CAAC,UAAU,EAAE,MAAM,CAAC,cAAc,EAAE,MAAM,CAAC,cAAc,CAAC,CAAC;IAClH,MAAM,CAAC,cAAc,CAAC,IAAI,CAAC,GAAG,SAAS,CAAC,CAAC;IACzC,OAAO,SAAS,CAAC;AACnB,CAAC"}
@@ -8,6 +8,14 @@ export declare const SPType: {
8
8
  readonly Delete: "Delete";
9
9
  };
10
10
  export type SPType = typeof SPType[keyof typeof SPType];
11
+ /**
12
+ * Entities whose SchemaName is not in excludeSchemas (case-insensitive).
13
+ * This is the CodeGen run's in-scope set: SQL objects and GRANTs are
14
+ * generated and executed only for these. excludeSchemas means "not this run".
15
+ */
16
+ export declare function entitiesNotInExcludedSchemas<T extends {
17
+ SchemaName: string;
18
+ }>(entities: T[], excludeSchemas: string[]): T[];
11
19
  /**
12
20
  * This class is responsible for generating database level objects like views, stored procedures, permissions, etc. You can sub-class this class to create your own SQL generation logic or implement support for other
13
21
  * databases. The base class implements support for SQL Server. In future versions of MJ, we will break out an abstract base class that has the skeleton of the logic and then the SQL Server version will be a sub-class
@@ -51,6 +59,13 @@ export declare class SQLCodeGenBase {
51
59
  * Ordered list of entity IDs for delete SP regeneration (dependency order)
52
60
  */
53
61
  protected orderedEntitiesForDeleteSPRegeneration: string[];
62
+ /**
63
+ * `schema.routine` keys (lowercased) present in the database at the start of
64
+ * this SQL-generation pass. Used to force-log CREATE PROC for routines that
65
+ * were dropped out-of-band (BigSchemaDemo recreate, a failed prior batch)
66
+ * while the entity itself is not in newEntityList/modifiedEntityList.
67
+ */
68
+ protected existingRoutines: Set<string> | null;
54
69
  manageSQLScriptsAndExecution(pool: CodeGenConnection, entities: EntityInfo[], directory: string, currentUser: UserInfo): Promise<boolean>;
55
70
  /**
56
71
  * Apply reason-specific fixups after late-phase view regeneration.
@@ -205,6 +220,14 @@ export declare class SQLCodeGenBase {
205
220
  files: string[];
206
221
  }>;
207
222
  getSPName(entity: EntityInfo, type: SPType): string;
223
+ /**
224
+ * Snapshot every CRUD routine currently in the database (one query) so
225
+ * generate-and-log can force-emit CREATE PROC for objects that vanished
226
+ * without the entity landing on newEntityList/modifiedEntityList.
227
+ */
228
+ protected loadExistingRoutines(pool: CodeGenConnection, entities: EntityInfo[]): Promise<void>;
229
+ protected isRoutineMissing(schema: string, routineName: string): boolean;
230
+ protected forceLogForRoutine(entity: EntityInfo, routineName: string, baseViewChanged: boolean): boolean;
208
231
  getEntityPermissionFileNames(entity: EntityInfo): string[];
209
232
  /**
210
233
  * Deprecated - do not use
@@ -226,6 +249,16 @@ export declare class SQLCodeGenBase {
226
249
  * Delegates to the database provider for platform-specific index DDL.
227
250
  */
228
251
  generateIndexesForForeignKeys(pool: CodeGenConnection, entity: EntityInfo): string;
252
+ /**
253
+ * Generates the composite index covering the entity's SOFT primary key. Empty for every
254
+ * entity with a real `PRIMARY KEY` constraint, which is nearly all of them.
255
+ *
256
+ * Kept separate from {@link generateIndexesForForeignKeys} and gated by its own setting: a
257
+ * soft-PK index serves MJ's own per-record existence check on the create path, which is a
258
+ * different concern from indexing foreign keys for joins and filters, and an operator's
259
+ * opinion about one is not an opinion about the other.
260
+ */
261
+ generateIndexForSoftPrimaryKey(entity: EntityInfo): string;
229
262
  /**
230
263
  * Detects self-referential foreign keys in an entity (e.g., ParentTaskID pointing back to Task table)
231
264
  * Returns array of field info objects representing recursive relationships
@@ -1 +1 @@
1
- {"version":3,"file":"sql_codegen.d.ts","sourceRoot":"","sources":["../../src/Database/sql_codegen.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"sql_codegen.d.ts","sourceRoot":"","sources":["../../src/Database/sql_codegen.ts"],"names":[],"mappings":"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"}
@@ -4,18 +4,29 @@ import * as fs from 'fs';
4
4
  import path from 'path';
5
5
  import { SQLUtilityBase } from './sql.js';
6
6
  import { CodeGenDatabaseProvider } from './codeGenDatabaseProvider.js';
7
- import { autoIndexForeignKeys, configInfo, customSqlScripts, mjCoreSchema } from '../Config/config.js';
7
+ import { autoIndexForeignKeys, autoIndexSoftPrimaryKeys, configInfo, customSqlScripts, mjCoreSchema } from '../Config/config.js';
8
+ import { entityInCustomBaseViewRefreshScope, shouldEmitCascadeForRelatedEntity } from './schema-filters.js';
8
9
  import { ManageMetadataBase } from './manage-metadata.js';
9
10
  import { UserCache } from '@memberjunction/generic-database-provider';
10
11
  import { combineFiles, logIf } from '../Misc/util.js';
11
12
  import { MJGlobal, UUIDsEqual } from '@memberjunction/global';
12
13
  import { SQLLogging } from '../Misc/sql_logging.js';
13
14
  import { TempBatchFile } from '../Misc/temp_batch_file.js';
15
+ import { writeFileIfChanged as writeFileIfChangedShared } from '../Misc/file-write.js';
14
16
  export const SPType = {
15
17
  Create: 'Create',
16
18
  Update: 'Update',
17
19
  Delete: 'Delete',
18
20
  };
21
+ /**
22
+ * Entities whose SchemaName is not in excludeSchemas (case-insensitive).
23
+ * This is the CodeGen run's in-scope set: SQL objects and GRANTs are
24
+ * generated and executed only for these. excludeSchemas means "not this run".
25
+ */
26
+ export function entitiesNotInExcludedSchemas(entities, excludeSchemas) {
27
+ const exclude = new Set(excludeSchemas.map((s) => s.toLowerCase()));
28
+ return entities.filter((e) => !exclude.has(e.SchemaName.toLowerCase()));
29
+ }
19
30
  /**
20
31
  * This class is responsible for generating database level objects like views, stored procedures, permissions, etc. You can sub-class this class to create your own SQL generation logic or implement support for other
21
32
  * databases. The base class implements support for SQL Server. In future versions of MJ, we will break out an abstract base class that has the skeleton of the logic and then the SQL Server version will be a sub-class
@@ -57,6 +68,13 @@ export class SQLCodeGenBase {
57
68
  * Ordered list of entity IDs for delete SP regeneration (dependency order)
58
69
  */
59
70
  this.orderedEntitiesForDeleteSPRegeneration = [];
71
+ /**
72
+ * `schema.routine` keys (lowercased) present in the database at the start of
73
+ * this SQL-generation pass. Used to force-log CREATE PROC for routines that
74
+ * were dropped out-of-band (BigSchemaDemo recreate, a failed prior batch)
75
+ * while the entity itself is not in newEntityList/modifiedEntityList.
76
+ */
77
+ this.existingRoutines = null;
60
78
  }
61
79
  get SQLUtilityObject() {
62
80
  return this._sqlUtilityObject;
@@ -69,6 +87,7 @@ export class SQLCodeGenBase {
69
87
  }
70
88
  async manageSQLScriptsAndExecution(pool, entities, directory, currentUser) {
71
89
  try {
90
+ this.existingRoutines = null;
72
91
  // Build list of entities qualified for forced regeneration if entityWhereClause is provided
73
92
  if (configInfo.forceRegeneration?.enabled && configInfo.forceRegeneration?.entityWhereClause) {
74
93
  this.filterEntitiesQualifiedForRegeneration = true; // Enable filtering
@@ -104,13 +123,12 @@ export class SQLCodeGenBase {
104
123
  return false;
105
124
  }
106
125
  succeedSpinner(`Custom SQL scripts completed (${(new Date().getTime() - startTime.getTime()) / 1000}s)`);
126
+ await this.loadExistingRoutines(pool, entities);
107
127
  // ALWAYS use the first filter where we only include entities that have IncludeInAPI = 1
108
128
  // Entities are already sorted by name in PostProcessEntityMetadata (see providerBase.ts)
109
129
  const baselineEntities = entities.filter(e => e.IncludeInAPI);
110
130
  // OPTIMIZATION: Use a Set for O(1) lookups instead of O(n) array finds to improve performance
111
- const excludeSchemasSet = new Set(configInfo.excludeSchemas.map(s => s.toLowerCase()));
112
- const includedEntities = baselineEntities.filter(e => !excludeSchemasSet.has(e.SchemaName.toLowerCase())); //only include entities that are NOT in the excludeSchemas list
113
- const excludedEntities = baselineEntities.filter(e => excludeSchemasSet.has(e.SchemaName.toLowerCase())); //only include entities that ARE in the excludeSchemas list in this array
131
+ const includedEntities = entitiesNotInExcludedSchemas(baselineEntities, configInfo.excludeSchemas);
114
132
  // Initialize temp batch files for each schema
115
133
  // These will be populated as SQL is generated and will be used for actual execution
116
134
  const schemas = Array.from(new Set(baselineEntities.map(e => e.SchemaName))).sort();
@@ -140,17 +158,17 @@ export class SQLCodeGenBase {
140
158
  .filter(e => e !== undefined);
141
159
  // Track per-batch success without short-circuiting. Historically,
142
160
  // any single batch failure here returned `false` immediately, which
143
- // skipped the cascade-regen and excluded-perms batches and left
144
- // their entities with no SQL emitted at all. The post-run CRUD
145
- // validator (in runCodeGen.ts) is the authoritative ship-gate now,
146
- // so we want every batch to attempt its work and surface the
147
- // complete picture of what's missing — partial generation is
148
- // strictly more useful than nothing.
161
+ // skipped the cascade-regen batch and left those entities with no
162
+ // SQL emitted at all. The post-run CRUD validator (in runCodeGen.ts)
163
+ // is the authoritative ship-gate now, so we want every in-scope
164
+ // batch to attempt its work and surface the complete picture of
165
+ // what's missing — partial generation is strictly more useful than
166
+ // nothing.
149
167
  let entityGenSuccess = true;
150
168
  // Build a UNION will-regenerate set across every batch we're about
151
- // to launch (main + cascade-regen + excluded-perms). The PG view
152
- // fallback's restoreDependents path uses this to skip restoring
153
- // captured dependents that codegen will recreate later in the run.
169
+ // to launch (main + cascade-regen). The PG view fallback's
170
+ // restoreDependents path uses this to skip restoring captured
171
+ // dependents that codegen will recreate later in the run.
154
172
  // Without the union, batch 1's fallback only sees batch 1's
155
173
  // entities — so a dependent like vwAIAgentRuns (which lives in
156
174
  // batch 2 cascade-regen) is "missing from willRegenerate", the
@@ -162,7 +180,6 @@ export class SQLCodeGenBase {
162
180
  const allBatchEntities = [
163
181
  ...entitiesWithoutCascadeRegeneration,
164
182
  ...entitiesForCascadeRegeneration,
165
- ...excludedEntities,
166
183
  ];
167
184
  const globalWillRegenerate = new Set(allBatchEntities
168
185
  .filter(e => !!e.BaseView)
@@ -180,7 +197,8 @@ export class SQLCodeGenBase {
180
197
  // AI Engine init because vwAIModels is gone. SQL Server doesn't
181
198
  // hit this because its execution path is bulk-monolithic, not
182
199
  // phased per-entity.
183
- const perEntityBatchSize = configInfo.dbPlatform === 'postgresql' ? 1 : 5;
200
+ const configuredBatch = configInfo.fileEmit?.sqlEntityBatchSize ?? 8;
201
+ const perEntityBatchSize = configInfo.dbPlatform === 'postgresql' ? 1 : configuredBatch;
184
202
  // Generate SQL for entities that don't need cascade delete regeneration
185
203
  const genResult = await this.generateAndExecuteEntitySQLToSeparateFiles({
186
204
  pool,
@@ -196,7 +214,7 @@ export class SQLCodeGenBase {
196
214
  willRegenerate: globalWillRegenerate
197
215
  }); // enable sql logging for NEW entities....
198
216
  if (!genResult.Success) {
199
- logError('Main entity SQL generation batch had failures — continuing with cascade-regen and excluded-perms batches; validator will report any missing routines.');
217
+ logError('Main entity SQL generation batch had failures — continuing with cascade-regen; validator will report any missing routines.');
200
218
  entityGenSuccess = false;
201
219
  }
202
220
  // Generate SQL for cascade delete regenerations in dependency order (sequentially)
@@ -219,23 +237,19 @@ export class SQLCodeGenBase {
219
237
  }
220
238
  genResult.Files.push(...cascadeGenResult.Files);
221
239
  }
222
- // STEP 2(c) - for the excludedEntities, while we don't want to generate SQL, we do want to generate the permissions files for them
223
- updateSpinner(`Generating permissions for ${excludedEntities.length} excluded entities...`);
224
- const genResult2 = await this.generateAndExecuteEntitySQLToSeparateFiles({
225
- pool,
226
- entities: excludedEntities,
227
- directory,
228
- onlyPermissions: true,
229
- skipExecution: true, // skip execution because we execute it all in a giant batch below
230
- batchSize: perEntityBatchSize,
231
- writeFiles: true,
232
- enableSQLLoggingForNewOrModifiedEntities: false, /*don't log this stuff, it is just permissions for excluded entities*/
233
- willRegenerate: globalWillRegenerate
234
- });
235
- if (!genResult2.Success) {
236
- logError('Excluded-entities permissions batch had failures — continuing; validator will report any missing routines.');
237
- entityGenSuccess = false;
238
- }
240
+ // STEP 2(c) used to generate+apply GRANT files for excludeSchemas entities
241
+ // ("we don't want to generate SQL, we do want to generate the permissions").
242
+ // That is wrong once excludeSchemas means "not this CodeGen run":
243
+ //
244
+ // - Open Apps exclude sibling schemas (__mj_BizAppsCommon, …) and __mj.
245
+ // GRANT SELECT ON [__mj_BizAppsCommon].[vwContactMethods] then runs from
246
+ // the orders (or other) repo, pollutes SQL Scripts/generated/<sibling>/,
247
+ // and fails the whole run (exit 1) when that object is missing or lives
248
+ // in a schema the executing login does not resolve.
249
+ // - Core (__mj) permissions already ship in MJ migrations. Refreshing them
250
+ // from an app CodeGen is not this app's job.
251
+ //
252
+ // Permissions are applied in STEP 4 for includedEntities only.
239
253
  if (entityGenSuccess) {
240
254
  succeedSpinner(`Entity generation completed (${(new Date().getTime() - step2StartTime.getTime()) / 1000}s)`);
241
255
  }
@@ -244,15 +258,15 @@ export class SQLCodeGenBase {
244
258
  }
245
259
  // STEP 2(d) now that we've generated the SQL, let's create a combined file in each schema sub-directory for convenience for a DBA
246
260
  startSpinner('Creating combined SQL files...');
247
- const allEntityFiles = this.createCombinedEntitySQLFiles(directory, baselineEntities);
248
- succeedSpinner(`Created combined SQL files for ${allEntityFiles.length} schemas`);
261
+ const allEntityFiles = this.createCombinedEntitySQLFiles(directory, includedEntities);
262
+ succeedSpinner(`Created combined SQL files for ${allEntityFiles.length} file(s)`);
249
263
  // STEP 2(e) ---- FINALLY, we execute SQL in proper dependency order
250
264
  // Use temp batch files (which maintain CodeGen log order) if available, otherwise fall back to combined files
251
265
  startSpinner('Executing entity SQL files...');
252
266
  const step2eStartTime = new Date();
253
267
  let executionSuccess = false;
254
268
  if (useProviderPhasedExecution) {
255
- // Per-entity phased execution already ran during steps 2(b)/(c).
269
+ // Per-entity phased execution already ran during step 2(b).
256
270
  // Skip the bulk file re-execution — replaying the same SQL here
257
271
  // would re-trigger 42P16 without the phased recovery wrapper and
258
272
  // undo the work the phased executor just did.
@@ -363,10 +377,13 @@ export class SQLCodeGenBase {
363
377
  succeedSpinner('No valid entities to regenerate');
364
378
  }
365
379
  }
366
- // STEP 4- Apply permissions, executing all .permissions files
380
+ // STEP 4- Apply permissions for THIS run's schemas only. Excluded-schema
381
+ // GRANT files are not generated (see STEP 2(c) note) and must not be
382
+ // executed: a missing sibling object would fail CodeGen with
383
+ // "Cannot find the object 'vw…'" even though this app's work succeeded.
367
384
  startSpinner('Applying permissions...');
368
385
  const step4StartTime = new Date();
369
- if (!await this.applyPermissions(pool, directory, baselineEntities)) {
386
+ if (!await this.applyPermissions(pool, directory, includedEntities)) {
370
387
  failSpinner('Failed to apply permissions');
371
388
  overallSuccess = false;
372
389
  }
@@ -713,14 +730,7 @@ export class SQLCodeGenBase {
713
730
  * This avoids false timestamp updates and unnecessary I/O.
714
731
  */
715
732
  writeFileIfChanged(filePath, newContent) {
716
- if (fs.existsSync(filePath)) {
717
- const existing = fs.readFileSync(filePath, 'utf-8');
718
- if (existing === newContent) {
719
- return false;
720
- }
721
- }
722
- fs.writeFileSync(filePath, newContent);
723
- return true;
733
+ return writeFileIfChangedShared(filePath, newContent);
724
734
  }
725
735
  /**
726
736
  * Fetches the current base view definition from SQL Server and compares it against newly generated SQL.
@@ -925,7 +935,8 @@ export class SQLCodeGenBase {
925
935
  return md.Entities.filter(e => modifiedOrNewNames.includes(e.Name) &&
926
936
  !e.BaseViewGenerated &&
927
937
  e.IncludeInAPI &&
928
- !e.VirtualEntity);
938
+ !e.VirtualEntity &&
939
+ entityInCustomBaseViewRefreshScope(e.SchemaName, configInfo.excludeSchemas ?? [], configInfo.includeSchemas));
929
940
  }
930
941
  /**
931
942
  * Builds a combined SQL string of sp_refreshview statements for the given entities.
@@ -951,7 +962,14 @@ export class SQLCodeGenBase {
951
962
  return outer; // fully custom — one view, and it is a standing prerequisite
952
963
  }
953
964
  const inner = this._dbProvider.generateViewRefreshSQL(e.SchemaName, e.GeneratedViewName);
954
- return inner + '\n' + this.guardOnApplicationOwnedView(e, outer);
965
+ const rebind = this._dbProvider.generateLayeredOuterRebindSQL(e);
966
+ const outerSql = outer.trim().length > 0
967
+ ? this.guardOnApplicationOwnedView(e, outer)
968
+ : '';
969
+ const rebindSql = rebind.trim().length > 0
970
+ ? this.guardOnApplicationOwnedView(e, rebind.trim())
971
+ : '';
972
+ return [inner, outerSql, rebindSql].filter((s) => s && s.trim().length > 0).join('\n');
955
973
  })
956
974
  .join('\n');
957
975
  }
@@ -981,9 +999,21 @@ export class SQLCodeGenBase {
981
999
  let baseViewChanged = false;
982
1000
  // Indexes for Fkeys for the table (skip for virtual entities — views can't have indexes)
983
1001
  if (!options.onlyPermissions && !options.entity.VirtualEntity) {
984
- const shouldGenerateIndexes = autoIndexForeignKeys() || (configInfo.forceRegeneration?.enabled && configInfo.forceRegeneration?.indexes);
1002
+ const forceIndexes = !!(configInfo.forceRegeneration?.enabled && configInfo.forceRegeneration?.indexes);
1003
+ const shouldGenerateIndexes = autoIndexForeignKeys() || forceIndexes;
985
1004
  const indexSQL = shouldGenerateIndexes ? this.generateIndexesForForeignKeys(options.pool, options.entity) : ''; // generate indexes if auto-indexing is on OR force regeneration is enabled
986
- const s = this.generateSingleEntitySQLFileHeader(options.entity, 'Index for Foreign Keys') + indexSQL;
1005
+ // The soft-PK index rides in the same generated file (and therefore the same
1006
+ // migration log), but on its own setting — see generateIndexForSoftPrimaryKey.
1007
+ // Appended rather than prepended so the file's existing content stays
1008
+ // byte-identical for every entity that has no soft PK, which is nearly all of
1009
+ // them: writeFileIfChanged compares content, and reordering would rewrite the
1010
+ // whole tree for no functional gain.
1011
+ const softPKSQL = (autoIndexSoftPrimaryKeys() || forceIndexes)
1012
+ ? this.generateIndexForSoftPrimaryKey(options.entity)
1013
+ : '';
1014
+ const s = this.generateSingleEntitySQLFileHeader(options.entity, 'Index for Foreign Keys')
1015
+ + indexSQL
1016
+ + (softPKSQL ? (indexSQL ? '\n\n' : '') + softPKSQL : '');
987
1017
  if (options.writeFiles) {
988
1018
  const filePath = path.join(options.directory, this.SQLUtilityObject.getDBObjectFileName('index', options.entity.SchemaName, options.entity.BaseTable, false, true));
989
1019
  this.writeFileIfChanged(filePath, s);
@@ -1080,7 +1110,7 @@ export class SQLCodeGenBase {
1080
1110
  if (options.writeFiles) {
1081
1111
  const filePath = path.join(options.directory, this.SQLUtilityObject.getDBObjectFileName('sp', options.entity.SchemaName, spName, false, true));
1082
1112
  this.writeFileIfChanged(filePath, s);
1083
- this.logSQLForNewOrModifiedEntity(options.entity, s, `spCreate SQL for ${options.entity.Name}`, options.enableSQLLoggingForNewOrModifiedEntities, baseViewChanged);
1113
+ this.logSQLForNewOrModifiedEntity(options.entity, s, `spCreate SQL for ${options.entity.Name}`, options.enableSQLLoggingForNewOrModifiedEntities, this.forceLogForRoutine(options.entity, spName, baseViewChanged));
1084
1114
  files.push(filePath);
1085
1115
  }
1086
1116
  sRet += s + '\n' + this._dbProvider.BatchSeparator + '\n';
@@ -1091,7 +1121,7 @@ export class SQLCodeGenBase {
1091
1121
  if (options.writeFiles) {
1092
1122
  const filePath = path.join(options.directory, this.SQLUtilityObject.getDBObjectFileName('sp', options.entity.SchemaName, spName, true, true));
1093
1123
  this.writeFileIfChanged(filePath, s);
1094
- this.logSQLForNewOrModifiedEntity(options.entity, s, `spCreate Permissions for ${options.entity.Name}`, options.enableSQLLoggingForNewOrModifiedEntities, baseViewChanged);
1124
+ this.logSQLForNewOrModifiedEntity(options.entity, s, `spCreate Permissions for ${options.entity.Name}`, options.enableSQLLoggingForNewOrModifiedEntities, this.forceLogForRoutine(options.entity, spName, baseViewChanged));
1095
1125
  files.push(filePath);
1096
1126
  }
1097
1127
  // now, append the permissions to the return string IF we did NOT generate the proc - because if we generated the proc, that
@@ -1110,7 +1140,7 @@ export class SQLCodeGenBase {
1110
1140
  if (options.writeFiles) {
1111
1141
  const filePath = path.join(options.directory, this.SQLUtilityObject.getDBObjectFileName('sp', options.entity.SchemaName, spName, false, true));
1112
1142
  this.writeFileIfChanged(filePath, s);
1113
- this.logSQLForNewOrModifiedEntity(options.entity, s, `spUpdate SQL for ${options.entity.Name}`, options.enableSQLLoggingForNewOrModifiedEntities, baseViewChanged);
1143
+ this.logSQLForNewOrModifiedEntity(options.entity, s, `spUpdate SQL for ${options.entity.Name}`, options.enableSQLLoggingForNewOrModifiedEntities, this.forceLogForRoutine(options.entity, spName, baseViewChanged));
1114
1144
  files.push(filePath);
1115
1145
  }
1116
1146
  sRet += s + '\n' + this._dbProvider.BatchSeparator + '\n';
@@ -1121,7 +1151,7 @@ export class SQLCodeGenBase {
1121
1151
  if (options.writeFiles) {
1122
1152
  const filePath = path.join(options.directory, this.SQLUtilityObject.getDBObjectFileName('sp', options.entity.SchemaName, spName, true, true));
1123
1153
  this.writeFileIfChanged(filePath, s);
1124
- this.logSQLForNewOrModifiedEntity(options.entity, s, `spUpdate Permissions for ${options.entity.Name}`, options.enableSQLLoggingForNewOrModifiedEntities, baseViewChanged);
1154
+ this.logSQLForNewOrModifiedEntity(options.entity, s, `spUpdate Permissions for ${options.entity.Name}`, options.enableSQLLoggingForNewOrModifiedEntities, this.forceLogForRoutine(options.entity, spName, baseViewChanged));
1125
1155
  files.push(filePath);
1126
1156
  }
1127
1157
  // now, append the permissions to the return string IF we did NOT generate the proc - because if we generated the proc, that
@@ -1146,7 +1176,7 @@ export class SQLCodeGenBase {
1146
1176
  if (options.writeFiles) {
1147
1177
  const filePath = path.join(options.directory, this.SQLUtilityObject.getDBObjectFileName('sp', options.entity.SchemaName, spName, false, true));
1148
1178
  this.writeFileIfChanged(filePath, s);
1149
- this.logSQLForNewOrModifiedEntity(options.entity, s, `spDelete SQL for ${options.entity.Name}`, options.enableSQLLoggingForNewOrModifiedEntities, baseViewChanged);
1179
+ this.logSQLForNewOrModifiedEntity(options.entity, s, `spDelete SQL for ${options.entity.Name}`, options.enableSQLLoggingForNewOrModifiedEntities, this.forceLogForRoutine(options.entity, spName, baseViewChanged));
1150
1180
  files.push(filePath);
1151
1181
  }
1152
1182
  sRet += s + '\n' + this._dbProvider.BatchSeparator + '\n';
@@ -1157,7 +1187,7 @@ export class SQLCodeGenBase {
1157
1187
  if (options.writeFiles) {
1158
1188
  const filePath = path.join(options.directory, this.SQLUtilityObject.getDBObjectFileName('sp', options.entity.SchemaName, spName, true, true));
1159
1189
  this.writeFileIfChanged(filePath, s);
1160
- this.logSQLForNewOrModifiedEntity(options.entity, s, `spDelete Permissions for ${options.entity.Name}`, options.enableSQLLoggingForNewOrModifiedEntities, baseViewChanged);
1190
+ this.logSQLForNewOrModifiedEntity(options.entity, s, `spDelete Permissions for ${options.entity.Name}`, options.enableSQLLoggingForNewOrModifiedEntities, this.forceLogForRoutine(options.entity, spName, baseViewChanged));
1161
1191
  files.push(filePath);
1162
1192
  }
1163
1193
  // now, append the permissions to the return string IF we did NOT generate the proc - because if we generated the proc, that
@@ -1203,6 +1233,42 @@ export class SQLCodeGenBase {
1203
1233
  getSPName(entity, type) {
1204
1234
  return this._dbProvider.getCRUDRoutineName(entity, type);
1205
1235
  }
1236
+ /**
1237
+ * Snapshot every CRUD routine currently in the database (one query) so
1238
+ * generate-and-log can force-emit CREATE PROC for objects that vanished
1239
+ * without the entity landing on newEntityList/modifiedEntityList.
1240
+ */
1241
+ async loadExistingRoutines(pool, entities) {
1242
+ this.existingRoutines = new Set();
1243
+ const schemas = [...new Set(entities.map((e) => e.SchemaName).filter((s) => !!s))];
1244
+ const sql = this._dbProvider.getRoutineNamesBySchemaSQL(schemas);
1245
+ if (!sql || !sql.trim()) {
1246
+ return;
1247
+ }
1248
+ try {
1249
+ const result = await pool.query(sql);
1250
+ for (const row of result.recordset ?? []) {
1251
+ const schemaName = String(row.schema_name ?? '').toLowerCase();
1252
+ const routineName = String(row.routine_name ?? '').toLowerCase();
1253
+ if (schemaName && routineName) {
1254
+ this.existingRoutines.add(`${schemaName}.${routineName}`);
1255
+ }
1256
+ }
1257
+ }
1258
+ catch (e) {
1259
+ logWarning(`Could not load existing CRUD routines (missing-proc self-heal disabled this run): ${e instanceof Error ? e.message : String(e)}`);
1260
+ this.existingRoutines = null;
1261
+ }
1262
+ }
1263
+ isRoutineMissing(schema, routineName) {
1264
+ if (!this.existingRoutines) {
1265
+ return false;
1266
+ }
1267
+ return !this.existingRoutines.has(`${schema}.${routineName}`.toLowerCase());
1268
+ }
1269
+ forceLogForRoutine(entity, routineName, baseViewChanged) {
1270
+ return baseViewChanged || this.isRoutineMissing(entity.SchemaName, routineName);
1271
+ }
1206
1272
  getEntityPermissionFileNames(entity) {
1207
1273
  const files = [];
1208
1274
  // all entities have a base view - and we always generate permissions for the base view even if not generated base view
@@ -1324,6 +1390,19 @@ export class SQLCodeGenBase {
1324
1390
  const indexStatements = this._dbProvider.generateForeignKeyIndexes(entity);
1325
1391
  return indexStatements.join('\n\n');
1326
1392
  }
1393
+ /**
1394
+ * Generates the composite index covering the entity's SOFT primary key. Empty for every
1395
+ * entity with a real `PRIMARY KEY` constraint, which is nearly all of them.
1396
+ *
1397
+ * Kept separate from {@link generateIndexesForForeignKeys} and gated by its own setting: a
1398
+ * soft-PK index serves MJ's own per-record existence check on the create path, which is a
1399
+ * different concern from indexing foreign keys for joins and filters, and an operator's
1400
+ * opinion about one is not an opinion about the other.
1401
+ */
1402
+ generateIndexForSoftPrimaryKey(entity) {
1403
+ const indexStatements = this._dbProvider.generateSoftPrimaryKeyIndex(entity);
1404
+ return indexStatements.join('\n\n');
1405
+ }
1327
1406
  /**
1328
1407
  * Detects self-referential foreign keys in an entity (e.g., ParentTaskID pointing back to Task table)
1329
1408
  * Returns array of field info objects representing recursive relationships
@@ -1597,6 +1676,12 @@ export class SQLCodeGenBase {
1597
1676
  const refreshSQL = this._dbProvider.generateViewRefreshSQL(entity.SchemaName, entity.BaseView);
1598
1677
  sOutput += this.guardOnApplicationOwnedView(entity, refreshSQL) + separator;
1599
1678
  }
1679
+ if (entity.HasLayeredBaseView && !entity.VirtualEntity) {
1680
+ const rebindSQL = this._dbProvider.generateLayeredOuterRebindSQL(entity);
1681
+ if (rebindSQL.trim().length > 0) {
1682
+ sOutput += this.guardOnApplicationOwnedView(entity, rebindSQL.trim()) + separator;
1683
+ }
1684
+ }
1600
1685
  return sOutput + permissionsHeader + this.guardOnApplicationOwnedView(entity, permissionsBody) + separator;
1601
1686
  }
1602
1687
  /**
@@ -1949,6 +2034,9 @@ export class SQLCodeGenBase {
1949
2034
  const md = new Metadata(); // global-provider-ok: codegen runs offline against a single provider
1950
2035
  // Find all fields in other entities that are foreign keys to this entity
1951
2036
  for (const e of md.Entities) {
2037
+ if (!shouldEmitCascadeForRelatedEntity(entity.SchemaName, e.SchemaName, configInfo.allowCrossSchemaCascadeDeletes === true)) {
2038
+ continue;
2039
+ }
1952
2040
  for (const ef of e.Fields) {
1953
2041
  if (UUIDsEqual(ef.RelatedEntityID, entity.ID) && ef.IsVirtual === false) {
1954
2042
  const cascadeSql = await this.generateSingleCascadeOperation(entity, e, ef, pool);
@@ -2059,6 +2147,9 @@ export class SQLCodeGenBase {
2059
2147
  const md = new Metadata(); // global-provider-ok: codegen runs offline against a single provider
2060
2148
  // Find all fields in other entities that are foreign keys to this entity
2061
2149
  for (const e of md.Entities) {
2150
+ if (!shouldEmitCascadeForRelatedEntity(entity.SchemaName, e.SchemaName, configInfo.allowCrossSchemaCascadeDeletes === true)) {
2151
+ continue;
2152
+ }
2062
2153
  for (const ef of e.Fields) {
2063
2154
  if (UUIDsEqual(ef.RelatedEntityID, entity.ID) && ef.IsVirtual === false) {
2064
2155
  // Skip self-referential foreign keys (e.g., ParentID pointing to same entity)