# 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.

## 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](#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](/database/0.3.8/defining-models/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]`](/database/0.3.8/defining-models/indexes) 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`](/database/0.3.8/querying/writing/insert#letting-the-server-fill-columns) 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.

```csharp
[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:

```csharp
// 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:
>
> ```ini
> [*.cs]
> dotnet_diagnostic.SCGDB027.severity = warning
> ```

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:

```csharp
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](/database/0.3.8/advanced/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](/database/0.3.8/advanced/procedure-mapping#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:

```sql
-- @ignore warning: optional free-form reason
```

See [Procedure mapping → Suppressing the placeholder warning](/database/0.3.8/advanced/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:

```csharp
#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:

```ini
# 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.
