Member Junction
    Preparing search index...

    PostgreSQL implementation of the CodeGen database provider. Generates PostgreSQL-native DDL for views, CRUD functions, triggers, indexes, full-text search, permissions, and other database objects.

    Registered with MJGlobal.ClassFactory against the canonical 'postgresql' platform key — SQLCodeGenBase resolves this provider via ClassFactory.CreateInstance(CodeGenDatabaseProvider, configInfo.dbPlatform).

    Hierarchy (View Summary)

    Index

    Constructors

    Properties

    Accessors

    Methods

    addColumnSQL addDefaultConstraintSQL alterColumnTypeAndNullabilitySQL baseTableSelectList buildCRUDReturnSelect buildCRUDReturnType buildExecParamForField buildPrimaryKeyComponents callRoutineSQL canSelfJoinViewForVirtualNameField columnToken compareDataTypes conditionalInsertSQL dropDefaultConstraintSQL dropObjectSQL executeEntityPhased executeSQLFileViaShell foreignKeyIndexName formatCompositeIndexStatement formatDefaultValue formatIndexStatement formatInsertDefaultValue FullTextSearchCostDisclosure generateAllEntitiesSQLFileHeader generateAncestorsFunction generateBaseView generateCreateOrReplaceViewSQL generateCRUDCreate generateCRUDDelete generateCRUDParamString generateCRUDPermissions generateCRUDUpdate generateDescendantsFunction generateDropGuard generateForeignKeyIndexes GenerateForeignKeyIndexes generateFullTextSearch generateFullTextSearchPermissions generateHierarchyFieldJoin generateHierarchyFieldSelect generateHierarchyMetaFunction generateIfViewExistsSQL generateInsertFieldString GenerateInsertFieldString generateLayeredOuterRebindSQL generateMaterializedTableSQL generateMaterializedWrapperViewSQL generateRootFieldJoin generateRootFieldSelect generateRootIDFunction generateSingleCascadeOperation generateSoftPrimaryKeyIndex GenerateSoftPrimaryKeyIndex generateSQLFileHeader generateTimestampColumns generateTimestampTrigger generateUpdateFieldString GenerateUpdateFieldString generateViewPermissions generateViewRefreshSQL generateViewTestQuerySQL getAncestorsFunctionName getCheckConstraintsSchemaFilter getCompositeUniqueConstraintCheckSQL getCRUDRoutineName getDescendantsFunctionName getEntitiesWithMissingBaseTablesFilter getFixVirtualFieldNullabilitySQL getForeignKeyIndexExistsSQL getHierarchyMetaFunctionName getMaterializedHashSurrogateColumnType getMaterializedSurrogateColumnType getMetadataSupportObjectsSQL getPendingEntityFieldsSQL getPrimaryKeyIndexNameSQL getRootIDFunctionName getRoutineNamesBySchemaSQL getSystemSchemasToExclude GetUniqueIndexMembershipSQL getViewColumnsBySchemaSQL getViewColumnsSQL getViewDefinitionSQL getViewExistsSQL indexPrefix isIndexableForeignKey isIndexableKeyColumn isParamRequired isUnindexedVerifiedJoinColumn maxIdentifierLength needsClearCompanion parseColumnDefaultValue quoteSQLForExecution regenerateBaseView renderParameterType resolveCascadeParentKeyField SetFieldSecurityRunContext SetupDataSource shouldIncludeFieldInParams softPrimaryKeyFields softPrimaryKeyIndexName softPrimaryKeyIndexPrefix tableToken trimStatementTerminator unindexableOrganicKeyDisclosure unresolvedCascadeKeyComment validateEntityFieldsResolve ValidateEntityFieldsResolve validateExpectedCRUDFunctions ValidateExpectedCRUDFunctions verifiedJoinColumnNames wrapInsertWithConflictGuard

    Constructors

    Properties

    _fieldSecurityRunContext: FieldSecurityRunContext | null = null

    Field-security run context (catalog snapshot + protected-role set), set once per run by the orchestrator before entity generation begins. Null when the platform emits no DB-tier field security (PostgreSQL, per decision D2) or on runs that could not read the catalog — emitters must degrade to grants-only emission in that case.

    Accessors

    • get BatchSeparator(): string

      Returns the batch separator for the database platform. SQL Server: 'GO' PostgreSQL: '' (empty string, uses semicolons)

      Returns string

    • get NeedsVirtualFieldNullabilityFix(): boolean

      PostgreSQL requires a nullability fix for virtual (computed) fields in views. View columns derived from expressions may report incorrect nullability in information_schema.columns, so CodeGen must correct these after view creation.

      Returns boolean

    Methods

    • Generates ALTER TABLE ... ADD COLUMN SQL. SQL Server: ALTER TABLE [schema].[table] ADD colName TYPE [NOT] NULL [DEFAULT expr] PostgreSQL: ALTER TABLE schema."table" ADD COLUMN "colName" TYPE [NOT] NULL [DEFAULT expr]

      Parameters

      • schema: string
      • tableName: string
      • columnName: string
      • dataType: string
      • nullable: boolean
      • OptionaldefaultExpression: string

      Returns string

    • The base-table part of a base view's SELECT list: <alias>.*, or an explicit column list when this entity has at least one excluded column.

      <alias>.* UNLESS SOMETHING IS ACTUALLY EXCLUDED, deliberately. Enumerating columns unconditionally would rewrite every base view in every tenant on the next CodeGen run for no behavioural gain, churn a large generated diff, and hand the golden-master suite a rebaseline that hides any real change inside it. Emitting the star whenever the exclusion set is empty keeps the output byte-identical for every entity nobody has named — which, with the default empty config, is all of them.

      The enumerated list is BASE-TABLE columns only, in Sequence order. Virtual fields are the view's own joined columns and are appended by the caller from BaseViewGenerationContext; including them here would project them twice.

      Parameters

      • entity: EntityInfo
      • alias: string
      • excludedFieldNames: ReadonlySet<string>

      Returns string

    • Builds the post-insert / post-update SELECT statement returned by a PostgreSQL CRUD function.

      • When entity.SaveReturnMode is 'full' (default), queries SELECT * FROM "schema"."vwBaseView".
      • When entity.SaveReturnMode is 'datestamps_only', queries only the primary key column(s) plus __mj_CreatedAt and __mj_UpdatedAt (via EntityInfo.DatestampReturnFields) directly from "schema"."BaseTable", avoiding the post-write BaseView joins and large payload columns while still returning DB-generated key and timestamp values.

      Parameters

      • entity: EntityInfo
      • viewName: string
      • whereClause: string

      Returns string

    • Builds the EXEC parameter fragment(s) for a single field when calling a tolerant update SP. Returns an array of @ParamName = value strings.

      For most fields this is just ['@FieldName = @variable']. But when clearValue is true and the field has a _Clear companion (see needsClearCompanion), an additional @FieldName_Clear = 1 is prepended so the tolerant SP actually sets the column to NULL instead of treating the NULL parameter as "leave unchanged".

      This is the single source of truth for the calling convention of tolerant update SPs. All codepaths that generate EXEC calls to spUpdate — cascade-update cursors, future SP-to-SP calls, etc. — should use this method to stay in sync with the SP declaration logic in generateCRUDParamString.

      Parameters

      • ef: EntityFieldInfo

        The entity field being passed

      • valueExpr: string

        The SQL expression for the value (e.g. @prefixed_var)

      • clearValue: boolean = false

        Whether this field is being explicitly set to NULL

      Returns string[]

    • Builds a set of PL/pgSQL components for working with an entity's primary key(s) in cascade operations: variable declarations, SELECT field list, FETCH INTO variable list, and named routine parameter assignments. Used by cascade delete and update-to-NULL generators to construct cursor-based loops.

      Parameters

      Returns {
          fetchInto: string;
          routineParams: string;
          selectFields: string;
          varDeclarations: string;
      }

    • Generates SQL to invoke a stored procedure or function. SQL Server: EXEC [schema].[spName] @Param1='val1', @Param2='val2' PostgreSQL: SELECT * FROM schema."spName"('val1', 'val2')

      Parameters

      • schema: string

        The schema containing the routine.

      • routineName: string

        The routine name (e.g., spUpdateExistingEntitiesFromSchema).

      • params: string[]

        Ordered array of parameter values as pre-formatted SQL strings. For SQL Server these become @ParamName='value' pairs; for PostgreSQL they become positional arguments.

      • OptionalparamNames: string[]

        Optional parameter names for SQL Server's @Name=value syntax. Ignored on PostgreSQL.

      • OptionaldiscardResult: boolean

        Set for routines whose rows the caller never reads. PostgreSQL needs to know: its default SELECT * FROM routine(...) form is rejected outright for a function returning SETOF record ("a column definition list is required for functions returning record"), and a routine that only performs work has no column list to give. SQL Server's EXEC is unaffected and ignores this.

      Returns string

    • Returns boolean

      NOT supported. PG's strict CREATE OR REPLACE VIEW parser resolves view names against the existing catalog state at parse time, so a body that LEFT-JOINs the view-being-created to itself (e.g. for a self-FK's virtual NameField) raises 42P01 undefined_table on first creation. A CREATE OR REPLACE retry against a NULL-typed stub then fails with cannot change data type of view column ... from text to character varying(N) because PG enforces strict column-type compat in CREATE OR REPLACE.

      Returning false tells sql_codegen.ts to skip the self-join entirely for self-FK + virtual-NameField cases. The trade-off: the corresponding virtual column (e.g. RestoredFrom on vwRecordChanges) is not emitted. Matches the baseline-shipped vwRecordChanges shape, which never had this column either.

    • Generates a conditional INSERT statement (insert only if not exists). SQL Server: IF NOT EXISTS (checkQuery) BEGIN insertSQL END PostgreSQL: DO $$ BEGIN IF NOT EXISTS (checkQuery) THEN insertSQL; END IF; END $$

      Both arguments must already be identifier-quoted by the caller (qi()/qs()). On PostgreSQL the result is a DO $$ ... $$ block, and the identifier auto-quoter (quoteSQLForExecution, applied later by runQuery/LogSQLAndExecute) skips dollar-quoted blocks wholesale — it cannot know whether their contents are SQL or literal text. So this is the one SQL-building path where the usual "write it bare, the quoter handles it" convention does not hold: a bare ID survives to PG folded as id and the statement fails every run.

      Parameters

      • checkQuery: string

        The SELECT query to check for existence. Identifiers must be pre-quoted.

      • insertSQL: string

        The INSERT statement to execute if the check returns no rows. Identifiers must be pre-quoted.

      Returns string

    • Generates a PL/pgSQL DO $$ block that drops both a named CHECK constraint (if one exists on the column, found via pg_catalog.pg_constraint) and the column's default value. Uses dynamic SQL (EXECUTE format(...)) to drop the constraint by name, then unconditionally runs ALTER COLUMN ... DROP DEFAULT.

      Parameters

      • schema: string
      • tableName: string
      • columnName: string

      Returns string

    • Generates a DROP statement for a database object (view/procedure/function). SQL Server: IF OBJECT_ID('...', 'P') IS NOT NULL DROP PROCEDURE ... PostgreSQL: DROP FUNCTION IF EXISTS ... CASCADE

      Note: This differs from generateDropGuard() which is used for CREATE OR REPLACE patterns. This method is used for cleanup operations.

      Parameters

      • objectType: "VIEW" | "PROCEDURE" | "FUNCTION"
      • schema: string
      • name: string

      Returns string

    • Phased per-entity execution for PG. Runs view → CRUD functions → view permissions against the target DB, guaranteeing phase 2 is skipped if phase 1 failed (so we never leave fn_create_* functions pointing at a missing or stale view's rowtype).

      Phase 1 routes through executeWithFallback so a 42P16 triggers the capture/drop/recreate/restore flow rather than blowing up. Phase 2 runs each CRUD function's CREATE individually — we do NOT concatenate them because node-pg's simple query protocol would then abort the whole batch on the first failure; running them separately gives a per-routine error signal. Phase 3 applies view-level GRANTs.

      Parameters

      • opts: {
            crudCreateSQL: string;
            crudDeleteSQL: string;
            crudUpdateSQL: string;
            entity: EntityInfo;
            tvfSQL: string;
            viewPermSQL: string;
            viewSQL: string;
            willRegenerate?: Set<string>;
        }

      Returns Promise<PhasedExecutionResult>

    • Composes the automatic foreign-key index name and enforces the dialect's identifier length limit. Dialect-independent in shape — {prefix}{table}_{column} — while the casing of the prefix, the table/column token spelling, and the length cap come from the dialect hooks.

      NOTE: the resulting names are deliberately NOT unified across dialects. SQL Server names from BaseTableCodeName/CodeName, PostgreSQL from snake-cased BaseTable/Name. Changing either would orphan every existing index in deployed databases (CodeGen would create new ones alongside the old), so the token hooks preserve each dialect's historical spelling exactly.

      Parameters

      Returns string

    • Generates a PostgreSQL view-regeneration block for an entity's base view.

      Includes all base table columns, parent/related field joins, and root field lateral joins. Applies a soft-delete WHERE filter when the entity uses soft deletes.

      Two-path emission (try-then-fallback). The output wraps CREATE OR REPLACE VIEW in a DO $$ ... EXCEPTION WHEN invalid_table_definition THEN DROP VIEW ... CASCADE; EXECUTE vsql; END $$ block. Why:

      • Happy path: CREATE OR REPLACE VIEW succeeds (new column list is a prefix of the existing one plus optional trailing additions). Zero destruction. No dependent views, functions, triggers, or grants are touched.

      • Sad path: PG raises SQLSTATE 42P16 invalid_table_definition for any column rename / reorder / type change / removal. The exception handler runs DROP VIEW ... CASCADE and re-executes the CREATE. Dependent codegen-managed functions (spCreate/spUpdate/spDelete returning SETOF vwFoo) and dependent views are CASCADE-dropped — they are regenerated later in the same codegen output stream, so by the end of the run all dependents are restored to the new shape. GRANTs on the view itself are also lost on the CASCADE; codegen always re-emits permissions immediately after the view, so they come back too.

      The runtime-apply path also calls this, so the live DB applies the same DO block. executeWithFallback (the runtime helper) becomes a no-op for these statements because the DO block handles 42P16 internally — but it still runs as a safety net for any other failure modes.

      What this DOES NOT preserve on the sad path: non-codegen-managed dependent objects (e.g. a hand-written sproc against this view that codegen doesn't know about). Those would be CASCADE-dropped and not restored. MJ codegen-generated sprocs cover all standard CRUD pathways; bespoke sprocs against base views are extremely rare in practice. If a project does have them, they need to be re-applied after a 42P16 fallback fires.

      This pattern matches the v5.30.x fix migration V202604282300 — which used the same DO/EXCEPTION construct to recreate vwEntityPermissions after the unquoted RoleName alias bug — proving the pattern is production-tested.

      Permissions are handled separately by sql_codegen.ts via generateViewPermissions().

      Parameters

      Returns string

    • PostgreSQL create-or-replace for a config-declared view.

      CREATE OR REPLACE VIEW alone is not a full equivalent of SQL Server's CREATE OR ALTER VIEW: PG raises SQLSTATE 42P16 invalid_table_definition when the new body renames, reorders, retypes or drops an existing column. So the statement runs inside a DO block that, on 42P16 only, drops the view and re-runs the create.

      The drop is deliberately not CASCADE. Unlike a base view, nothing CodeGen generates depends on a config-declared view, so any dependent is user-owned and CodeGen could not restore it — PG refuses the drop (2BP01) and the run fails loudly rather than silently destroying it. Grants on the view are lost on that path (a NOTICE names the view so an operator can re-apply them); the happy path keeps them.

      The body travels inside dollar quotes, which quoteSQLForExecution skips — it must be valid PostgreSQL as written, with mixed-case identifiers already double-quoted.

      Parameters

      • schema: string
      • viewName: string
      • selectSQL: string

      Returns string

    • Generates a PostgreSQL CREATE OR REPLACE FUNCTION for inserting a new record. The function accepts typed parameters for each writable field, performs an INSERT into the base table, and returns the newly created row from the base view via RETURN QUERY SELECT. Handles auto-increment PKs (using RETURNING ... INTO), UUID PKs (with COALESCE to gen_random_uuid()), and composite PKs. Also emits GRANT EXECUTE permissions for authorized roles.

      Prepends a DROP-all-overloads block (see generateDropAllOverloadsBlock) so adding/removing a column doesn't trigger PG's overload-ambiguity error.

      Wide entities (where useJsonArgShape returns true) emit a JSON-arg variant via generateCRUDCreateJsonArg — single p_data JSONB parameter, dynamic INSERT built from keys present in the payload. Same semantics; different wire shape needed because of PostgreSQL's 100-arg function ceiling.

      Parameters

      Returns string

    • PostgreSQL override: same tolerant-SP shape as the base class, but with PG's "all params after the first DEFAULT must also have DEFAULTs" rule enforced. Once any parameter becomes optional (or a _Clear companion is emitted), every subsequent parameter is forced to DEFAULT NULL even if it would otherwise be required, because PG function signatures don't allow gaps between defaulted params.

      All decision logic and dialect-syntax bits route through the same base-class helpers / dialect methods used by SQL Server — the only thing that differs is the sticky-defaults walk.

      Parameters

      Returns string

    • Generates a PostgreSQL CREATE OR REPLACE FUNCTION for updating an existing record. The function accepts typed parameters for all updatable fields plus primary key(s), performs an UPDATE ... SET ... WHERE PK = param, checks ROW_COUNT to detect missing rows, and returns the updated record from the base view (or PK + datestamp columns from the base table when entity.SaveReturnMode === 'datestamps_only') via RETURN QUERY SELECT. Also generates the __mj_UpdatedAt timestamp trigger for the entity and emits GRANT EXECUTE permissions.

      Parameters

      Returns string

    • Generates a PostgreSQL DROP ... IF EXISTS ... CASCADE statement as a guard before creating or replacing a database object. For triggers, PostgreSQL relies on CREATE OR REPLACE on the trigger function, so a comment is emitted instead.

      Parameters

      • objectType: "VIEW" | "PROCEDURE" | "FUNCTION" | "TRIGGER"
      • schema: string
      • name: string

      Returns string

    • Wraps innerSQL so it runs only when the named view already exists.

      Used for objects CodeGen refreshes or grants on but does NOT create — specifically the application-owned outer view of a layered entity. On the first CodeGen pass after layering is enabled, that view legitimately does not exist yet: it selects from the inner view that this very pass is creating, so it cannot have been created earlier. Emitting an unguarded sp_refreshview/GRANT against it fails the run and blocks the only path to setting layering up. Every later pass finds the view present and behaves identically to the unguarded form.

      Parameters

      • schema: string

        Schema of the view whose existence gates innerSQL

      • viewName: string

        View whose existence gates innerSQL

      • innerSQL: string

        Statements to run when the view exists. Must be a complete statement batch.

      Returns string

    • Generates the column-list or value-list portion of an INSERT statement, depending on whether prefix is empty (column names) or non-empty (parameter values).

      Empty prefix — produces dialect-quoted column names suitable for the INSERT INTO ... (col1, col2) clause.

      Non-empty prefix — produces the parameter-value list suitable for the VALUES (...) clause, with tolerant-SP behavior:

      • Special-date fields are substituted with the dialect's CurrentTimestampUTC() (created/updated) or NullLiteral (deleted-at).
      • GUID fields with database defaults emit a CASE that detects the empty-GUID sentinel and falls back to the database default; otherwise wraps with IsNull.
      • Non-nullable fields with defaults are wrapped in IsNull(@Param, default).
      • Nullable fields with non-NULL defaults emit a _Clear companion CASE so callers can distinguish "leave default" from "explicitly NULL."
      • Plain nullable fields with no default pass the parameter reference through directly (NULL flows through).

      Skips auto-increment, virtual, and non-updatable fields. The PK column can be optionally excluded (used by the two-branch GUID-PK insert pattern in generateCRUDCreate).

      Dialect-specific syntax is fully delegated to:

      • Dialect.QuoteIdentifier, Dialect.ParameterRef,
      • Dialect.IsNull, Dialect.NullLiteral,
      • Dialect.CurrentTimestampUTC, Dialect.EmptyUUIDLiteral,
      • formatInsertDefaultValue(ef) render hook (for type-strict dialects that need to massage default values).

      Parameters

      Returns string

    • PostgreSQL wrapper-view DDL — the stable read contract over the materialized table. Uses CREATE OR REPLACE VIEW so the same statement both creates the view and atomically repoints it at a freshly-built table during refresh (plan §11.2). The body is always SELECT * FROM <table> and the shadow table shares the column shape, so PG's CREATE OR REPLACE column-compatibility rule is satisfied on the swap.

      Parameters

      • schema: string
      • viewName: string
      • tableName: string

      Returns string

    • Generates the composite index covering an entity's SOFT primary key, or an empty array when the entity has no soft PK (which is every ordinary entity — this is a no-op for anything with a real PRIMARY KEY constraint).

      WHY THIS EXISTS. A soft primary key lives only in metadata: IsPrimaryKey and IsSoftPrimaryKey are both set, and the table carries no PRIMARY KEY and no unique index. Integration tables are built that way on purpose — their keys are inferred, so enforcing one would reject valid rows whenever an inference is wrong.

      The consequence is a heap that MJ's own write path scans on every record. A create calls InnerLoad on the key to check for an existing row; a genuinely new record matches nothing; and a not-found lookup cannot short-circuit, so it reads the whole table before concluding the row is absent. The scan grows with the table, so a sync gets slower the longer it runs — measured live at 345 → 574 → 864 ms per record across consecutive batches of one connector, with nothing saturated (DB CPU 57%, log write 13%, sessions 0, app CPU 5.7%, memory flat).

      Note that isIndexableForeignKey excludes primary keys with the comment "a primary key is already covered by its own index". That is true for a real PK and false, by definition, for a soft one — which is precisely how these tables fell through every existing mechanism.

      ONE COMPOSITE INDEX, not one per column: the lookup is always an equality match on the whole key, so a single index in ordinal order serves it. Non-unique, because uniqueness is exactly what the soft-PK design refuses to assert.

      IDEMPOTENT BY NAME, like the FK indexes. An index someone created by hand over the same columns under a different name will not be recognised, and this will add a second one — drop the hand-made one rather than disabling this.

      Parameters

      Returns string[]

    • Generates the SET clause body for an UPDATE statement with tolerant merge semantics. Each non-PK column wraps the parameter with the dialect's null-coalescing call against the column's existing value (SET [Col] = ISNULL(@Param, [Col]) on SQL Server, SET "col" = COALESCE(p_col, "col") on PostgreSQL) so omitting a parameter preserves the existing row value. Nullable columns whose database default is non-NULL also emit a _Clear companion branch that lets callers distinguish "leave unchanged" from "explicitly NULL."

      The full structural logic lives here in the base class; only the per-line render is dialect-specific and that's resolved through the Dialect accessor's helpers (QuoteIdentifier, ParameterRef, IsNull, NullLiteral). Subclasses can override to customize line formatting if a future dialect needs something different.

      Parameters

      Returns string

    • Returns DDL that creates/replaces the platform's metadata-management support objects (introspection views and the routines manage-metadata invokes via callRoutineSQL), or null when the platform ships them through migrations instead.

      These objects are CodeGen's own machinery — only CodeGen calls them — so platforms that return DDL here get it executed (idempotently) at the start of every manageMetadata run. That guarantees the objects can never be missing or version-skewed relative to the CodeGenLib code that calls them.

      SQL Server: returns null (objects ship in the baseline migrations). PostgreSQL: returns the full support-object DDL.

      Parameters

      • mjCoreSchema: string

      Returns string | null

    • Generates the SQL to retrieve pending entity fields that exist in the database but not yet in the MJ metadata. This is a large, platform-specific query.

      Parameters

      • mjCoreSchema: string

        The MJ core schema name (e.g., __mj).

      • OptionalentityIDs: string[]

        Optional list of entity UUIDs to scope the query to. When provided, the query filters to fields belonging to those entities only — used by Pass 2 to avoid re-scanning the entire schema for entities that haven't changed. undefined or empty preserves the prior unscoped behavior.

      • OptionalexcludeSchemas: string[]

      Returns string

    • Produces the canonical name for the recursive root-finder helper function generated for self-referencing fields. The function definition and the view's LATERAL-JOIN reference must agree on this name (caller side calls via generateRootFieldJoin, definition via generateRootIDFunction).

      Note: this intentionally does NOT match the baseline-shipped PascalCase fn{Table}{Field}_GetRootID form, because the baseline returns TABLE("RootID" type) and the view callers expect a scalar — using the baseline name would clash with cannot change return type of existing function. The snake_case scalar form is codegen's own naming space and is consistent with how downstream views are emitted.

      Parameters

      Returns string

    • Returns a SQL query that lists every stored procedure / function name in the given schemas. Used by the post-run CRUD validator to diff expected vs actual routine presence in one round trip.

      The result set must contain exactly two columns: schema_name — the schema the routine lives in routine_name — the proc/function name as stored in the catalog

      SQL Server: queries sys.objects for procedures and functions. PostgreSQL: queries pg_proc joined to pg_namespace.

      Default: returns empty string. Providers that don't implement this opt out of the post-run CRUD validator (the validator returns no missing when this returns empty), preserving backwards compatibility for downstream subclasses that haven't been updated.

      Parameters

      • schemas: string[]

      Returns string

    • Returns a query listing every non-primary-key unique index (or unique constraint) that contains a column, one row per index, with two columns:

      • KeyColumnCount: the number of key columns in the index, not counting INCLUDE columns
      • IsIncludedColumn: true when the column is only an INCLUDE column of that index

      A column that is unique on its own has a row with KeyColumnCount = 1 and IsIncludedColumn = false. EntityField.IsUnique cannot tell that apart: schema sync sets it on every column of a composite unique constraint.

      The default returns null, meaning the platform cannot answer; callers then keep relying on EntityField.IsUnique.

      Parameters

      • schema: string
      • tableName: string
      • columnName: string

      Returns string | null

    • Decides whether a field should get an automatic foreign-key index. Dialect-independent.

      A field qualifies when it points at another entity and is a real, materialized column:

      • RelatedEntityID — the field is a foreign key. This is the base-table column that actually stores the relationship. (The sibling RelatedEntity field is a view join on that ID, not a base column; the two never disagree in practice — verified 0/793 divergence across the FK fields in the reference database — so keying off the ID is both equivalent and more direct.)
      • !IsPrimaryKey — a primary key is already covered by its own index. In 1:1 extension-table patterns the child's PK is also an FK to the parent, and indexing it again would be pure overhead.
      • !IsVirtual — virtual fields have no underlying column, so CREATE INDEX on one would reference a column that does not exist.

      The PK/virtual exclusions previously existed only on the PostgreSQL side; hoisting them here applies them to every dialect by construction. Current real-world exposure is zero (no FK field in the reference database is also a primary key or virtual), so this is a guard against a future case rather than a change in today's generated output.

      Parameters

      Returns boolean

    • Tolerant-SP semantics: returns true when this parameter must be provided by every caller (no default, no fallback). Pure decision logic — Pillar 1 of the cross-app migration architecture.

      • PKs: required on update, optional on non-AutoIncrement create (codegen emits a default that lets the database supply the value).
      • Non-PK on update: never required (merge semantics).
      • Non-PK on create: required only when the column is NOT NULL with no database default — i.e. a value the DB has no way to fill in.

      Parameters

      Returns boolean

    • Whether f is a verified join column (see verifiedJoinColumnNames) that still needs an index of its own.

      A column that is already IsUnique has one: the unique constraint's index serves the join. A declared FK can only reference a column with a unique constraint or index, on both dialects, so every EntityKeyField set from a declared non-PK FK lands here; indexing it again would add a second index on the same column, charged on every write and never chosen over the first. Only a soft FK or organic-key target with no unique index of its own gains anything from one.

      Parameters

      Returns boolean

    • Returns true when codegen should emit a <Param>_Clear companion parameter for the given field. The companion lets callers disambiguate "leave unchanged / apply DB default" (omit the parameter) from "explicitly set this column to NULL" (<Param>_Clear = 1). Required only for nullable columns whose database default is itself non-NULL — without the companion, a caller could not preserve a literal NULL because the dialect's IsNull wrap would always substitute the default.

      Routes the NULL-literal check through the dialect so future dialects can override what "this value is NULL" looks like in their generated SQL. Pure decision logic — no rendering.

      Parameters

      Returns boolean

    • PG-specific base-view regeneration with 42P16 recovery.

      Runs the provided CREATE OR REPLACE VIEW SQL through executeWithFallback on a dedicated connection: happy path issues the CREATE OR REPLACE directly, and only on SQLSTATE 42P16 does the capture/drop/recreate/restore dance fire inside a transaction that preserves every dependent view, function, grant, comment, and owner. See viewFallback.ts for the contract.

      willRegenerate is passed through so dependents CodeGen is about to rebuild in the same run are skipped at restore time (avoids restoring a stale captured definition against a newly-regenerated target).

      Parameters

      • entity: EntityInfo
      • viewSQL: string
      • OptionalwillRegenerate: Set<string>

      Returns Promise<void>

    • Resolves which of the parent's primary-key columns a cascading FK references, so the cascade's WHERE <fk> = @<param> binds to the matching spDelete parameter (one is declared per parent PK column). An FK always targets exactly one column:

      • Single-column parent key: the FK necessarily targets it — returned directly.
      • Composite parent key: the referenced column is fkField.RelatedEntityFieldName. When it names a parent PK column that column is returned; when it names nothing (metadata not yet synced) or a non-key unique column, null is returned because no spDelete parameter carries that value — the caller must skip the cascade rather than silently bind the FK to the wrong key column.

      Parameters

      Returns EntityFieldInfo | null

    • The generated-SQL note for organic keys this dialect will not index, or null when there are none.

      A normalizing organic key is the one case where the honest answer is "no index, and here is why". Saying it in the emitted SQL follows GenerateSoftPrimaryKeyIndex: the failure mode both methods exist to end is an index that is silently absent, and swapping one silence for another leaves the next person with the same puzzle. The fix is an expression index, which has to match the normalization expression exactly and is therefore a deliberate act, not something to guess at from metadata.

      Parameters

      Returns string | null

    • Cross-check every entity's declared fields against the columns its base view actually produces.

      A field the metadata promises but the view cannot emit is not a cosmetic inconsistency: the runtime selects fields by name, so the first read of that entity fails with column "X" does not exist. Grids surface that as an empty result rather than an error, so the entity simply appears to hold no data — a failure mode that can persist for months without anyone seeing a stack trace.

      This drift is created whenever a migration adds a column to a base TABLE and registers an EntityField for it without also rebuilding the base VIEW. CodeGen would normally repair that on its next run, but excludeSchemas skips SQL generation entirely for excluded schemas (permissions only) — and __mj is in that list by convention on essentially every install. So for the core schema there is no regeneration pass to lean on, and the drift is permanent.

      Deliberately NOT filtered by excludeSchemas: excluded schemas are precisely where nothing else is watching.

      Read-only — this reports, it does not repair.

      Parameters

      Returns Promise<FieldResolutionGap[]>

    • Cross-checks the entity-level AllowAPI/spGenerated/sp* configuration against the routines actually present in the database after CodeGen finishes. Returns one entry per missing routine.

      Why this exists: silent generation gaps (e.g. an entity dropped because an upstream batch errored, or stale entity-field metadata causing the PK check to fail) historically reported success at the pipeline level while leaving runtime CRUD broken. This validator turns that into a loud, actionable failure list before the install pipeline exits.

      What's expected per entity:

      • Skip virtual entities (no DB-backed routines).
      • For each of Create / Update / Delete:
        • Skip when the corresponding Allow{Type}API flag is false.
        • Look up the routine name via getCRUDRoutineName (which honors entity.spCreate/spUpdate/spDelete overrides; otherwise returns the dialect-generated default).
        • Report it as missing when not found in the DB catalog.

      Schema-level case sensitivity: SQL Server is case-insensitive, PostgreSQL is case-preserving. Both lookups normalize via lowercase to keep the validator dialect-agnostic.

      Default implementation works for both dialects via the getRoutineNamesBySchemaSQL helper. Providers may override to add platform-specific shortcuts (e.g. checking only sys.procedures on SQL Server) but the default is fine for all current dialects.

      Parameters

      Returns Promise<CRUDValidationMissing[]>

    • The columns on THIS entity that MJ itself joins or filters on, over and above its declared foreign keys, lower-cased for comparison.

      WHY FK METADATA ALONE IS NOT ENOUGH. RelatedEntityID is the only thing isIndexableForeignKey used to look at, so indexing and FK metadata were the same decision. On an imported schema they are not: the real joins are on external ids, the inferred FKs pointing at them get deleted as wrong (that is what the delete is for), and the entity is then left with a join MJ performs on every parent record and no index for it. Removing bad FK metadata and getting the indexes you need were mutually exclusive, and the more correct the metadata became the worse the query plans got.

      TWO SOURCES, both of which are MJ's own verified join declarations rather than inferences:

      • EntityRelationship.EntityKeyField on the relationships this entity parents. That column is exactly what BuildRelationshipViewParams puts on the left of the predicate for every related-records view, once per parent record. It is only ever set for a NON-PK join, so every value here is a column that no primary-key index covers. (One that a unique index already covers is filtered out in isUnindexedVerifiedJoinColumn.)

      • The match fields of this entity's ACTIVE, ExactMatch-normalized organic keys. An organic key is a human-verified join over columns that carry no constraint at all.

      ONLY ExactMatch, DELIBERATELY. Every other normalization strategy wraps the column in a function — LOWER(LTRIM(RTRIM(col))) for the default LowerCaseTrim — and a plain B-tree index on col cannot serve LOWER(TRIM(col)) = ? on either dialect. Emitting one would ship an index that is never used, costs a write on every insert, and reads in a catalog listing exactly like an index that works. See unindexableOrganicKeyDisclosure for what is said instead.

      Parameters

      Returns ReadonlySet<string>

    • Wraps an INSERT statement with a conditional existence check at the statement level. SQL Server: Adds IF NOT EXISTS (...) BEGIN prefix and END suffix. PostgreSQL: Adds ON CONFLICT DO NOTHING suffix (no prefix needed).

      Parameters

      • _conflictCheckSQL: string

        The SQL Server existence check query. Ignored on PostgreSQL.

      Returns { prefix: string; suffix: string }

      An object with prefix and suffix strings to wrap around the INSERT.