/DB

Generator diagnostics

Every build-time diagnostic the source generator can raise, with its SCGDB id, what triggers it, and how to suppress or escalate it from .editorconfig.

updated 2 Sept 20267 min readv0.3.8View as Markdown

Overview

Beyond emitting code, the source generator inspects your tables and .sql procedure files at build time and reports problems as standard Roslyn diagnostics. Each one has a stable SCGDB### id, shows up in the IDE error list and in dotnet build output, and points at the offending property, attribute, or .sql file.

Because they are ordinary compiler diagnostics, you control their severity from .editorconfig exactly like any analyzer rule. See Configuring severity. All diagnostics share the Socigy.DB category.


Table & column definitions

These fire on the [Table] class or property they describe, so the squiggle lands directly on your C# source.

Id Severity Triggered when
SCGDB001 Error [AutoIncrement] is applied to a property whose type is not short, int, or long.
SCGDB002 Error [Encrypted] is combined with [ValueConvertor], [JsonColumn], or [RawJsonColumn] on the same property. See Encrypted columns.
SCGDB016 Warning A [Table] class declares no [PrimaryKey] column. Generated Update() / Delete() need a primary key to target rows.
SCGDB017 Warning A [Table] class has no mapped columns.
SCGDB018 Error A [Column("")] is given an empty or whitespace name.
SCGDB023 Error [Encrypted] is applied to a [PrimaryKey] or [AutoIncrement] column. Encrypted values are stored as non-deterministic bytea and cannot serve as a key.
SCGDB024 Error Two properties of the same [Table] map to one column name after snake_case normalization.
SCGDB025 Error A [Table] / [TableType] type is generic or nested. The generator would emit an uncompilable partial declaration.
SCGDB026 Error An [Index] names a property the table does not have, in its column list or in Include / DescendingColumns / NullsFirstColumns / NullsLastColumns. The index would be generated over a column that does not exist and the migration would fail to apply. Using nameof gets the same check from the compiler; this covers string literals.

Call sites

These inspect where you call the generated API rather than how you declare a table, so they point at the invocation in your own code. They run as a Roslyn analyzer rather than from the generator, which is what lets them bind the generated methods and read the row type, the InsertFields value, and the keep list from real symbols instead of matching on how the call happens to be written.

Id Severity Triggered when
SCGDB027 Info An insert passes InsertFields.ServerDefaults while the row type has [Default] columns the call does not name in keep. Those columns are omitted from the INSERT, so the database default is written instead of whatever the row holds.

SCGDB027 — ServerDefaults omits a `[Default]` column

InsertFields.ServerDefaults omits every [Default] column, whether or not the row carries a value for it. That is deliberate and documented, but it is decided by an attribute in a different file from the call site: adding [Default] to an existing column silently changes every ServerDefaults insert that does not name it, with no compile error, no runtime error and no log line.

[Table("outbox")]
public partial class OutboxMessage
{
    [PrimaryKey] public Guid Id { get; set; }
    [Default(DbDefaults.Time.Now)] public DateTime OccurredAt { get; set; }   // added later
    public string Payload { get; set; } = "";
}

// Written before OccurredAt gained its [Default]. Still compiles, still runs — but OccurredAt
// now falls to the server default and the value assigned above never reaches the database.
await db.OutboxMessages.InsertMultipleAsync(messages, InsertFields.ServerDefaults);

Two ways to resolve it, both reported in the message:

// Name the column, and supply its value yourself.
await db.OutboxMessages.InsertMultipleAsync(messages, [nameof(OutboxMessage.OccurredAt)]);

// Or omit it only where the row actually left it unset.
await db.OutboxMessages.InsertMultipleAsync(messages, InsertFields.ServerDefaultsWhenUnset);
NOTE
SCGDB027 defaults to Info, so it appears as an editor suggestion and stays out of build output. A codebase can legitimately have hundreds of ServerDefaults call sites, and a warning on each would be noise rather than signal. Promote it when you want the full list — typically once you have found one column that silently took its default and want to know where else it can happen:

It covers every way the library offers to insert — the generated context's table set (db.Rows.InsertAsync(row, …)), the static methods on the row class, BulkCopy, and the fluent row.Insert().ExcludeAutoFields() builder — and it reads the InsertFields value through a constant or a shared field, not just when the enum member is written inline:

public const InsertFields ServerFilled = InsertFields.ServerDefaults;   // still recognised
await db.Outbox.InsertAsync(row, ServerFilled);

It stays quiet where it cannot be certain. A keep list whose contents are not visible at compile time — one built at runtime, or passed as a variable — suppresses the diagnostic for that call rather than risking a false report about a column you already handle.


SQL procedure files

These fire while processing procedure-mapping .sql files and attach to the .sql file itself.

Id Severity Triggered when
SCGDB003 Warning A .sql file contains no {{Type}} / {{Type.Property}} placeholder. Suppress per-file with -- @ignore warning (see Suppressing the placeholder warning).
SCGDB004 Error A placeholder references a type that does not exist in the compilation.
SCGDB005 Error A {{Type.Property}} placeholder references a property that does not exist on the named type.
SCGDB006 Error A placeholder is malformed; it must be {{TypeName}} (table) or {{TypeName.PropertyName}} (column).
SCGDB007 Error A placeholder references a type that is not a [Table] / [FlagTable].
SCGDB008 Error A placeholder's simple type name is ambiguous; use a fully-qualified name.
SCGDB009 Warning A -- @param is declared but never referenced in the SQL body.
SCGDB010 Warning The SQL body references @name but no matching -- @param declaration exists.
SCGDB011 Warning The -- @returns type cannot be resolved in the compilation.
SCGDB012 Warning A -- @param line is malformed; expected -- @param name: CSharpType.
SCGDB013 Warning A .sql file is registered as <AdditionalFiles> but lives outside Socigy/Procedures/, so it was ignored.
SCGDB014 Warning A .sql file has an empty body after its header and produced no method.
SCGDB015 Error Two procedure files resolve to the same method name in the same namespace group. Only the first is emitted.
NOTE
SCGDB009 and SCGDB010 analyse raw SQL text, so unusual constructs (PostgreSQL @@ operators, casts inside string literals) can occasionally produce a false positive. They are warnings by design. Silence an individual rule with .editorconfig if it does not fit your SQL style.

Suppressing diagnostics

Per-file: `-- @ignore warning`

The missing-placeholder warning (SCGDB003) is silenced for a single .sql file by adding the directive to its header:

-- @ignore warning: optional free-form reason

See Procedure mapping → Suppressing the placeholder warning.

Inline: `#pragma` and `[SuppressMessage]`

Diagnostics reported on a .cs location (the table, column, and call-site rules) can be suppressed inline like any analyzer warning:

#pragma warning disable SCGDB016
[Table("audit_log")] // intentionally key-less, append-only table
public partial class AuditLog { /* ... */ }
#pragma warning restore SCGDB016

Configuring severity

Set any diagnostic's severity from .editorconfig. The section glob must match the file the diagnostic points at (.cs for table, column, and call-site rules, .sql for procedure rules), or use a global section:

# Promote the missing-primary-key warning to an error
[*.cs]
dotnet_diagnostic.SCGDB016.severity = error

# Make a missing schema placeholder a hard build error
[*.sql]
dotnet_diagnostic.SCGDB003.severity = error

# Silence "declared but unused parameter" everywhere
[*.sql]
dotnet_diagnostic.SCGDB009.severity = none

Valid severities are error, warning, suggestion, silent, none, and default.

NOTE
.editorconfig severity and the -- @ignore warning directive are independent and complementary. Use the directive to silence one file; use .editorconfig to change a rule across the whole project.