Protected_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.
Returns the batch separator for the database platform. SQL Server: 'GO' PostgreSQL: '' (empty string, uses semicolons)
The SQL dialect instance for this provider.
Whether this platform needs explicit view refresh after schema changes.
SQL Server: true (uses sp_refreshview)
PostgreSQL: false (views resolve at query time)
Whether this platform needs a post-sync fix for virtual field nullability.
SQL Server: false
PostgreSQL: true (PG view columns always report attnotnull=false)
The database platform key (e.g., 'sqlserver', 'postgresql').
Returns the native timestamp-with-timezone type name for this platform.
SQL Server: DATETIMEOFFSET
PostgreSQL: TIMESTAMPTZ
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]
OptionaldefaultExpression: stringGenerates SQL to add a default constraint/value to a column.
SQL Server: ALTER TABLE ... ADD CONSTRAINT DF_name DEFAULT expr FOR [col]
PostgreSQL: ALTER TABLE ... ALTER COLUMN "col" SET DEFAULT expr
Generates ALTER TABLE ... ALTER COLUMN to change type and nullability.
SQL Server: ALTER TABLE ... ALTER COLUMN col TYPE NULL|NOT NULL
PostgreSQL: ALTER TABLE ... ALTER COLUMN "col" TYPE type, ALTER COLUMN "col" SET|DROP NOT NULL
ProtectedbuildBuilds 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.
The entity field being passed
The SQL expression for the value (e.g. @prefixed_var)
Whether this field is being explicitly set to NULL
Builds the four SQL fragments needed to work with an entity's primary key columns in cursor-based cascade operations:
varDeclarations: @{prefix}{PK} TYPE variable declarations for DECLARE.selectFields: Bracketed column names for the cursor SELECT.fetchInto: @{prefix}{PK} variable references for FETCH INTO.routineParams: Named parameter assignments (@PK = @{prefix}{PK}) for EXEC.Supports composite primary keys by iterating all PK fields. The optional prefix
defaults to 'Related' to avoid variable name collisions in nested cursor blocks.
Optionalprefix: stringBuilds a SQL Server EXEC statement for invoking a stored procedure. When paramNames
are provided, generates named parameter syntax (@ParamName=value); otherwise uses
positional parameter values. Returns just EXEC [schema].[routine] if no params.
OptionalparamNames: string[]Optional_discardResult: booleanWhether this dialect can handle a base view that LEFT-JOINs itself to read
a virtual computed column (e.g. vwRecordChanges joining to itself for the
RestoredFromID virtual NameField lookup).
Default: false. No shipped provider currently supports this pattern:
CREATE OR REPLACE VIEW resolves view names against catalog
state at parse time, so a self-reference fails with 42P01 undefined_table. 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.DROP VIEW then CREATE VIEW,
and SQL Server resolves view-body references at parse/bind time — there
is no deferred name resolution for view bodies the way there is for
stored procedures. After the DROP, the post-DROP self-reference fails
with error 208 "Invalid object name".With the default of false, sql_codegen.ts skips the self-virtual-
NameField join entirely for self-FK + virtual-NameField cases. The trade-
off: the corresponding virtual lookup column (e.g. RestoredFrom on
vwRecordChanges, or Parent on a vwTags-style view if the Name Field
were computed) is not emitted on the base view. Matches the baseline-
shipped view shapes.
Subclasses can override to return true if a future dialect (or a
provider that switches to a different emit pattern, e.g. stub-then-alter)
can support the self-reference. The fix for the underlying conflation
between SQL Server computed columns and view-only columns under
IsVirtual = 1 would let the join target the base table instead,
removing the need for this capability flag entirely.
ProtectedcolumnThe column portion of an automatic FK index name, in this dialect's spelling.
Compares two data type names, accounting for platform-specific aliases.
For example, PostgreSQL reports timestamp with time zone in information_schema
but DDL uses timestamptz.
The type name as reported by the database catalog.
The expected type name (from DDL or configuration).
True if the types are equivalent.
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.
The SELECT query to check for existence. Identifiers must be pre-quoted.
The INSERT statement to execute if the check returns no rows. Identifiers must be pre-quoted.
Generates SQL to dynamically find and drop the default constraint on a column.
Unlike PostgreSQL's simple ALTER COLUMN DROP DEFAULT, SQL Server requires looking
up the constraint name from sys.default_constraints joined through sys.tables,
sys.schemas, and sys.columns, then executing a dynamic ALTER TABLE DROP CONSTRAINT
via EXEC(). The generated SQL is safe to run even if no default constraint exists
(guarded by IF @constraintName IS NOT NULL).
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.
OptionalexecuteOptional — dialect-specific phased execution of a single entity's full CodeGen SQL package (view, CRUD functions, permissions) for the main per-entity run path.
Default path concatenates all the SQL and hands it to the shell executor, which runs it as a single multi-statement query. When any statement fails, pg's simple-query protocol aborts the rest of the batch — so a view that fails 42P16 silently blocks the CREATE FUNCTIONs that follow for the same entity.
Providers that implement this method run the pieces in separate phases so a failure in phase 1 prevents phase 2 from producing functions that reference a missing or stale view. PG's implementation additionally routes the view phase through the 42P16 capture/restore fallback.
Phasing contract:
Phase 0 = TVF DDL (tvfSQL) — root-ID functions for recursive FKs that
the base view references. Must run before phase 1 or PG
rejects the view with function does not exist.
Phase 1 = view DDL (viewSQL) — may invoke provider-specific recovery.
Phase 2 = CRUD function DDL — ONLY runs if phase 1 succeeded.
Phase 3 = view permissions (viewPermSQL) — runs only if phase 2 succeeded.
The success/phase pair in the result identifies exactly where things
fell over so the caller doesn't have to bisect.
Root-ID TVF DDL emitted ahead of the view. Empty when the entity has no recursive ParentID FKs.
OptionalwillRegenerate?: Set<string>Executes a SQL file against SQL Server using the sqlcmd command-line utility. Reads
connection details (server, port, instance, user, password, database) from the shared
sqlConfig object. On Windows, converts file paths containing spaces to 8.3 short
format to avoid quoting issues with sqlcmd. Spawns the process with QUOTED_IDENTIFIER
enabled (-I) and severity threshold 17 (-V 17). Optionally adds -C for
trustServerCertificate. Returns true on successful execution, false on failure.
ProtectedforeignComposes 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.
ProtectedformatRenders one complete composite index statement for the dialect — quoting, column order, and the "create only if absent" idempotency form. The index NAME arrives pre-composed and pre-truncated; implementations must use it verbatim.
Formats a raw default value string for embedding in generated SQL. Recognizes common
SQL Server functions (NEWID(), NEWSEQUENTIALID(), GETDATE(), GETUTCDATE(),
SYSDATETIME(), SYSDATETIMEOFFSET(), CURRENT_TIMESTAMP, USER_NAME(),
SUSER_NAME(), SYSTEM_USER) and strips wrapping parentheses from them. For literal
values, strips surrounding single quotes and re-applies them only if needsQuotes
is true. Returns 'NULL' for empty or whitespace-only inputs.
ProtectedformatRenders one complete index statement for the dialect — quoting, and the
"create only if absent" idempotency form (SQL Server wraps a sys.indexes check
around a bare CREATE INDEX; PostgreSQL uses CREATE INDEX IF NOT EXISTS).
The index NAME is supplied pre-composed and pre-truncated; implementations must use it verbatim. The indexed COLUMN, by contrast, is referenced from the field's real column name — which is not necessarily the token used to build the name.
ProtectedformatRender hook: returns the default value for a field formatted for
embedding in a generated INSERT statement. The base implementation
delegates to formatDefaultValue (which handles SQL functions, quoted
literals, etc.). Dialects with type-strict semantics may need to
massage the value further — for example, PostgreSQL maps SQL Server
BIT literal defaults (0/1) to PG BOOLEAN literals
(FALSE/TRUE) so that COALESCE(boolean_param, 0) doesn't fail
with a type-mismatch error.
Generates a comment header for the combined all-entities SQL file.
Generates a SQL Server inline TVF that returns all ancestors of an arbitrary node walking upward.
The function is named fn{BaseTable}{FieldName}_GetAncestors.
Generates a SQL Server CREATE VIEW statement for an entity's base view. The view
selects all columns from the base table plus any related, parent, and root ID fields
supplied by the orchestrator context. For entities with soft-delete enabled, appends
a WHERE ... IS NULL clause filtering out soft-deleted rows. Includes the conditional
DROP guard and a descriptive comment header.
Generates the spCreate stored procedure for a SQL Server entity. Handles three
primary key strategies:
SCOPE_IDENTITY() to retrieve the generated key.NEWSEQUENTIALID()): Uses an OUTPUT
clause into a table variable with a two-branch IF @PK IS NOT NULL pattern so
callers can optionally supply their own GUID or let the database default apply.ISNULL(@PK, NEWID()).The procedure always returns the newly created record via the entity's base view. Includes GRANT EXECUTE permissions for authorized roles.
Generates the spDelete stored procedure for a SQL Server entity. Supports both
hard delete (DELETE FROM) and soft delete (UPDATE ... SET __mj_DeletedAt = GETUTCDATE()
with a guard against re-deleting already soft-deleted rows). Prepends any cascade
delete/update SQL for related entities. Uses @@ROWCOUNT to determine success:
returns the PK values on success or NULL PK values if no row was affected.
Generates the parameter list string for a CRUD routine with tolerant SP signatures (Pillar 1 of the cross-app migration architecture).
Structural logic — what's required, what's optional, when a _Clear
companion appears — lives here in the base class and is shared across
dialects. The dialect-specific syntax (parameter prefix, default
keyword, type rendering) is delegated to:
Dialect.ParameterRef(name) — @Name vs. p_nameDialect.ParameterDefault(value) — = NULL vs. DEFAULT NULLDialect.NullLiteral — NULL literalrenderParameterType(ef) — type formatting (T-SQL native vs. PG-mapped)Subclasses can override the hooks above without re-implementing the full method. Subclasses can also override the method itself if their dialect imposes additional constraints not modelable through hooks (e.g. PostgreSQL's "all params after the first DEFAULT must also have DEFAULTs" rule, which the PostgreSQL provider handles via override).
Generates GRANT EXECUTE SQL for a CRUD stored procedure, granting permission only
to roles whose EntityPermission record allows the specified CRUD operation type.
Produces a single comma-separated GRANT EXECUTE ON ... TO [role1], [role2] statement,
preceded by the managed-scope reconciliation REVOKEs (see generateManagedPermissionRevokes).
Generates the spUpdate stored procedure for a SQL Server entity. Performs a standard
UPDATE ... SET ... WHERE PK = @PK and uses @@ROWCOUNT to detect whether the row
was found: if no rows were updated (e.g., stale PK or concurrent delete), returns an
empty result set with the base view's column structure (SELECT TOP 0 ... WHERE 1=0);
otherwise returns the updated record from the base view. Also generates the
__mj_UpdatedAt timestamp trigger if the entity has that column.
Generates a SQL Server inline TVF that returns all descendant records of an arbitrary root node
with an optional maximum relative depth parameter.
The function is named fn{BaseTable}{FieldName}_GetDescendants.
Generates SQL Server-style conditional DROP guards using the IF OBJECT_ID(...) IS NOT NULL
pattern. Maps each object type to its SQL Server type code: 'V' for views, 'P' for
procedures, 'IF' for inline table-valued functions, and 'TR' for triggers.
Each guard is terminated with a GO batch separator.
Generates CREATE INDEX statements for all foreign key columns on an entity. Returns an array of individual index DDL strings.
This is a template method: every dialect-INDEPENDENT decision (which fields qualify as indexable foreign keys, and how the index name is composed and truncated) lives here so it cannot drift between providers. Dialects supply only the genuinely different pieces via formatIndexStatement, tableToken, columnToken, indexPrefix, and maxIdentifierLength.
Previously this was abstract, so each provider reimplemented the whole thing and
they drifted in a way that was not dialect-specific — SQL Server omitted the
primary-key/virtual-field exclusions that PostgreSQL had. See
isIndexableForeignKey.
Generates SQL Server full-text search infrastructure for an entity, conditionally producing up to three components based on entity metadata flags:
FullTextCatalogGenerated): CREATE FULLTEXT CATALOG if
one doesn't already exist, defaulting to MJ_FullTextCatalog.FullTextIndexGenerated): Drops any existing FT index on the
table, then creates a new one covering the specified search fields with English language.FullTextSearchFunctionGenerated): An inline table-valued
function that wraps CONTAINS() to return matching primary key values.The generated SQL and the resolved function name for permission grants.
Generates GRANT EXECUTE permission for a full-text search function.
Generates a SQL Server OUTER APPLY clause to join the hierarchy metadata inline TVF
into the entity's base view.
Generates the SELECT expressions for hierarchy fields in a base view.
Projects: Root
Generates a SQL Server inline table-valued function (ITVF) that calculates hierarchy metadata
(RootID, Depth, Path, IsLeaf, ChildCount) for a record in a table with a self-referencing foreign key.
The function is named fn{BaseTable}{FieldName}_GetHierarchyMeta.
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.
Schema of the view whose existence gates innerSQL
View whose existence gates innerSQL
Statements to run when the view exists. Must be a complete statement batch.
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:
CurrentTimestampUTC() (created/updated) or NullLiteral
(deleted-at).CASE that detects
the empty-GUID sentinel and falls back to the database default;
otherwise wraps with IsNull.IsNull(@Param, default)._Clear
companion CASE so callers can distinguish "leave default"
from "explicitly NULL."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).SQL that rebinds a layered entity's application-owned outer view after the
inner generated view has been rewritten. SQL Server: empty here — the
outer is sp_refreshview'd via generateViewRefreshSQL. PostgreSQL:
a call to __mj.spRebindLayeredOuterView that restars g.* from
pg_get_viewdef so newly added inner columns appear on the wrapper.
Default is empty: platforms that do not need a distinct rebind step leave it alone. Must be a complete statement. The caller guards it on the outer view existing (bootstrap pass).
SQL Server materialized-table DDL. The create is conditional (only if the table
is absent) so a migration-provided materialized_<Name> table with bespoke indexing
is detected and reused rather than clobbered (plan §12). Emits the single-column
surrogate PRIMARY KEY, which is itself the required unique index.
Returns a single GO-free batch: it is executed directly via LogSQLAndExecute
(ds.query, which doesn't split on GO), and the caller passes
includeBatchSeparator: true so the migration file still gets a GO after it.
SQL Server wrapper-view DDL — the stable read contract over the materialized table.
Uses CREATE OR ALTER VIEW (SQL Server 2016 SP1+) so the same statement both creates
the view and atomically repoints it at a freshly-built table during refresh (plan §11.2).
Returns a single GO-free batch (executed via ds.query); the caller adds the file
batch separator. CREATE OR ALTER VIEW is valid as the sole statement in its batch.
Generates a SQL Server OUTER APPLY clause to join the root ID inline table-valued
function into the entity's base view. The function is invoked with the record's primary
key and the self-referencing FK value as arguments.
Generates the SELECT expression for a root ID field in a base view.
SQL Server: rootFn.RootID AS [FieldRoot...]
PostgreSQL: root_fn.root_id AS "FieldRoot..."
Generates a SQL Server inline table-valued function (ITVF) that calculates the top-level root
record ID for a self-referencing foreign key hierarchy. The function is named
fn{BaseTable}{FieldName}_GetRootID and returns a single-column result (RootID),
designed to be consumed via OUTER APPLY in the entity's base view.
Generates the cascade delete/update SQL for a single related entity. Called by the orchestrator for each FK relationship when CascadeDeletes is true.
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.
Generates a comment header for a generated SQL file.
Currently unused in production — timestamp columns are added individually via ensureSpecialDateFieldExistsAndHasCorrectDefaultValue() → addColumnSQL(). This method exists to satisfy the abstract contract. Uses the same defensive multi-step pattern as addColumnSQL() to remain safe if ever called on tables with existing rows on SQL Azure.
Generates a SQL Server AFTER UPDATE trigger that automatically sets the
__mj_UpdatedAt column to GETUTCDATE() on every update. The trigger joins
the base table to the INSERTED pseudo-table on all primary key columns to
target only the affected rows. Returns an empty string if the entity does not
have an __mj_UpdatedAt field.
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.
Generates SQL to refresh/recompile a view.
SQL Server: EXEC sp_refreshview 'schema.viewName';
PostgreSQL: returns empty string (no-op).
Generates a simple test query to validate a view is functional.
SQL Server: SELECT TOP 1 * FROM [schema].[viewName]
PostgreSQL: SELECT * FROM "schema"."viewName" LIMIT 1
Produces the canonical database function name for the ancestors traversal helper function.
Returns an additional WHERE clause fragment for the check-constraints query.
SQL Server: WHERE SchemaName NOT IN (...) when excludeSchemas is provided.
PostgreSQL: empty string (the PG view already handles schema filtering).
Returns a SQL query that checks whether a column participates in a composite (multi-column)
unique constraint. Queries sys.indexes, sys.index_columns, and sys.columns to find
non-primary-key unique indexes that include the specified column AND have more than one
column. Returns rows if the column is part of such a constraint; the orchestrator checks
recordset.length > 0.
Returns the name of the CRUD routine for an entity.
SQL Server: spCreateEntityName
PostgreSQL: fn_create_entity_name
Produces the canonical database function name for the descendants traversal helper function.
Returns an additional WHERE clause fragment for the missing-base-tables query.
SQL Server: WHERE VirtualEntity=0
PostgreSQL: empty string (PG query doesn't need this filter).
Generates SQL to fix virtual field nullability after metadata sync. PostgreSQL: Updates AllowsNull for virtual fields based on the FK column's nullability. SQL Server: returns empty string (no fix needed).
The MJ core schema name.
Returns a SQL query string to check if a foreign key index already exists.
Used by the orchestrator to conditionally create FK indexes.
SQL Server: queries sys.indexes with OBJECT_ID
PostgreSQL: queries pg_indexes
The result set should return rows if the index exists (length > 0 means exists).
Produces the canonical database function name for the hierarchy metadata helper function.
SQL Server KEYED surrogate key type: the combined-key SHA2_256 hash the refresh writes is a
fixed 64-char lowercase-hex string (CONVERT(varchar(64), HASHBYTES('SHA2_256', …), 2)), so
the minted entity's PK column is typed to MATCH the post-refresh physical column exactly —
avoiding the int-vs-hash divergence between CodeGen metadata and the rebuilt table.
SQL Server synthetic surrogate key: an auto-incrementing IDENTITY column.
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.
Generates the full SQL query to retrieve entity fields that exist in the database schema but are not yet registered in MJ metadata. The query is assembled from three parts:
vwForeignKeys, vwTablePrimaryKeys, and
vwTableUniqueKeys into temp tables so SQL Server can build real statistics instead
of expanding nested view-on-view joins with bad cardinality estimates.MaxSequences CTE to calculate proper field ordering and
NumberedRows CTE to deduplicate results, joining against the temp tables for FK,
PK, and unique key detection.OptionalentityIDs: string[]OptionalexcludeSchemas: string[]Returns a SQL query that retrieves the primary key index name for a table by joining
sys.indexes, sys.objects, and sys.key_constraints and filtering on constraint
type 'PK'. The result column is named IndexName as expected by the orchestrator.
Produces the canonical database function name for the root ID helper function.
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.
Returns an array of system schema names that should be excluded from
metadata synchronization. Empty array if the platform has no system
schemas that need excluding.
SQL Server: [] (no system schemas need excluding)
PostgreSQL: ['information_schema', 'pg_catalog', 'pg_toast', ...]
ProtectedgetDialect SQL returning every view column in the given schemas as
{ schema_name, view_name, column_name }.
information_schema.columns restricted to views is standard in both dialects,
so the base implementation serves both; override only if a dialect needs a
catalog-specific path.
Returns a SQL query that retrieves column metadata for a view by joining sys.columns,
sys.types, sys.views, and sys.schemas. Returns columns named FieldName, Type,
Length, Precision, Scale, and AllowsNull, ordered by column_id to preserve
the original column definition order.
Returns a SQL query string to retrieve the current view definition from the database.
SQL Server: SELECT OBJECT_DEFINITION(OBJECT_ID('[schema].[viewName]')) AS ViewDefinition
PostgreSQL: SELECT pg_get_viewdef('"schema"."viewName"'::regclass, true) AS "ViewDefinition"
The result set must include a column named ViewDefinition.
Returns SQL to check if a view exists.
The query uses @ViewName and @SchemaName as named parameters.
Returns 1 row if the view exists.
ProtectedindexPrefix for automatic FK index names. Dialects override to match their casing convention.
ProtectedisDecides 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.
ProtectedisSQL Server refuses NVARCHAR(MAX) (and the other LOB types) as an index KEY column
outright, regardless of the data it holds. Length === -1 is how MJ spells MAX.
Note the trap this closes: a source schema that declares a key column with no explicit
length maps to NVARCHAR(MAX), and the index then vanishes without complaint. The base
class turns that into a comment in the generated file naming the offending columns,
rather than an empty one.
ProtectedisTolerant-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.
ProtectedmaxMaximum identifier length for this dialect; FK index names are truncated to it.
ProtectedneedsReturns 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.
Parses a raw SQL Server column default value from the system catalog into a clean form.
SQL Server wraps defaults in parentheses (e.g., (getdate()), ((1)), (N'foo')),
so this method applies three successive stripping passes:
N'...' Unicode string prefix.Returns null if the input is null or undefined.
Quotes mixed-case identifiers in a raw SQL string for execution. SQL Server: returns the SQL unchanged (case-insensitive identifiers). PostgreSQL: double-quotes PascalCase identifiers to preserve case.
OptionalregenerateOptional — dialect-specific fast path for regenerating a single entity's base view with recovery logic.
When provided, the orchestration layer (sql.ts regenerateFailedBaseViews)
will route regeneration through this method instead of the generic
write-temp-file-and-shell-out path. Implementations can add capture/
recovery behavior around the CREATE OR REPLACE VIEW — e.g. PG's 42P16
capture-and-restore fallback that preserves dependent views, functions,
grants, comments, and ownership across the unavoidable DROP CASCADE.
The entity whose base view is being regenerated.
The full output of generateBaseView for this entity.
OptionalwillRegenerate: Set<string>Optional set of "schema.viewName" strings for
views the caller will regenerate later in the same
run — implementations may skip restoring those
dependents since CodeGen will recreate them.
ProtectedrenderRender hook: returns the SQL type token that should appear in a CRUD
parameter declaration for the given field. SQL Server emits the
entity-field's SQLFullType directly (T-SQL native); PostgreSQL maps
it through its type mapper. Override per dialect if your generated
SP signatures need a transformed type.
ProtectedresolveResolves 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:
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.SQL Server implementation of CodeGenDatabaseProvider.SetupDataSource.
Opens (or reuses) the module-cached mssql pool via MSSQLConnection(),
wires up the SQL Server metadata provider, builds the dialect-aware
connection, and resolves the audit user via UserCache.Refresh() —
the canonical pattern that has lived in runCodeGen.setupSQLServerDataSource()
since the multi-provider work started. It now sits behind the
CodeGenDatabaseProvider factory so adding a third platform doesn't
mean touching the orchestrator.
ProtectedshouldDelegates to EntityFieldInfo.IsSPParameter — the single source
of truth for whether a field appears as a parameter in spCreate /
spUpdate. Runtime data providers consume the same predicate so the
SP signature emitted here and the EXEC argument list built at save
time always agree.
ProtectedsoftThe entity's soft-PK fields in ordinal order, or an empty array if its PK is not soft.
Requires EVERY primary-key field to be soft. A mixed key would mean the table has a real constraint on part of its key, which is not a shape the integration schema builder produces, and guessing at the right index for it is worse than leaving it alone.
ProtectedsoftComposes the soft-PK index name in this dialect's spelling, truncated to its limit.
ProtectedsoftPrefix for the automatic soft-PK index name. Dialects override to match their casing.
ProtectedtableGenerates conditional CREATE INDEX statements for all foreign key columns on an entity.
Each index uses the naming convention IDX_AUTO_MJ_FKEY_{Table}_{Column}, truncated
to 128 characters (SQL Server's identifier length limit). Wraps each statement in an
IF NOT EXISTS check against sys.indexes to avoid duplicate index creation.
ProtectedunresolvedSQL comment emitted (and warning logged) when a cascade cannot be generated because the FK on a composite-key parent does not resolve to one of the parent's key columns.
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.
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:
Allow{Type}API flag is false.getCRUDRoutineName (which honors
entity.spCreate/spUpdate/spDelete overrides; otherwise returns
the dialect-generated default).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.
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).
The SQL Server existence check query. Ignored on PostgreSQL.
An object with prefix and suffix strings to wrap around the INSERT.
SQL Server implementation of the CodeGen database provider. Generates SQL Server-native DDL for views, stored procedures, triggers, indexes, full-text search, permissions, and other database objects.
Registered with
MJGlobal.ClassFactoryagainst the canonical'sqlserver'platform key —SQLCodeGenBaseresolves this provider viaClassFactory.CreateInstance(CodeGenDatabaseProvider, configInfo.dbPlatform). Downstream packages can subclass and re-register with higher priority to override codegen behavior — same extension hook every other MJ class uses.