Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion Directory.Build.props
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
<?xml version="1.0" encoding="utf-8"?>
<Project>
<PropertyGroup>
<Version>9.0.0-alpha.3</Version>
<Version>9.0.0-alpha.4</Version>
<LangVersion>13.0</LangVersion>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
Expand Down
9 changes: 9 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,15 @@ Read also more in [Introducing Weasel for Database Development](https://jeremydm

You can acccess the docs [here](https://weasel.jasperfx.net).

## Branch Strategy

| Branch | Version line | Purpose |
| --- | --- | --- |
| **`master`** | **9.0** (Critter Stack 2026) | Active development. All new work targets this branch. |
| `8.0` | 8.x | Maintenance only — critical fixes for the 8.x line. |

Weasel 9.0 is part of the [Critter Stack 2026](https://github.com/JasperFx/jasperfx/issues/217) release wave, shipping in lockstep with [JasperFx 2.0](https://github.com/JasperFx/jasperfx), [JasperFx.Events 2.0](https://github.com/JasperFx/jasperfx), [Marten 9.0](https://github.com/JasperFx/marten), and [Polecat 4.0](https://github.com/JasperFx/polecat). See the [9.0 master plan](https://github.com/JasperFx/weasel/issues/263) and the [migration guide](https://weasel.jasperfx.net/migration-guide) for upgrade details.

## Support Plans

<div align="center">
Expand Down
3 changes: 2 additions & 1 deletion docs/.vitepress/config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,8 @@ export default withMermaid(
{ text: 'Schema Migrations', link: '/core/schema-migrations' },
{ text: 'Command Builders & Batching', link: '/core/command-builders' },
{ text: 'Extension Methods', link: '/core/extension-methods' },
{ text: 'Multi-Tenancy', link: '/core/multi-tenancy' }
{ text: 'Multi-Tenancy', link: '/core/multi-tenancy' },
{ text: 'Provider Trait Matrix', link: '/core/provider-trait-matrix' }
]
},
{
Expand Down
139 changes: 139 additions & 0 deletions docs/core/provider-trait-matrix.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,139 @@
# Provider Trait Matrix

Weasel supports five database providers — PostgreSQL, SQL Server, Oracle,
MySQL, and SQLite — each with subtly different feature sets. The audit at
[#270](https://github.com/JasperFx/weasel/issues/270) documented this matrix
explicitly so users can write provider-agnostic schema code with confidence
about what works everywhere versus what's provider-specific.

## At a glance

| Feature | PostgreSQL | SQL Server | Oracle | MySQL | SQLite |
| --- | --- | --- | --- | --- | --- |
| Default schema | `public` | `dbo` | `WEASEL` | `public` | `main` |
| Identifier quoting | `"name"` | `[name]` | `"NAME"` | `` `name` `` | `"name"` |
| Identifier case | Sensitive | Folded | Folded (upper) | Folded (lower) | Folded |
| Max identifier length | 63 | 128 | 128 | 64 | (unlimited) |
| Auto-increment SQL | `SERIAL` | `IDENTITY(1,1)` | `GENERATED…AS IDENTITY` | `AUTO_INCREMENT` | `AUTOINCREMENT` |
| `ALTER TABLE ADD COLUMN` | ✓ | ✓ | ✓ | ✓ | ✓ |
| `ALTER TABLE DROP COLUMN` | ✓ | ✓ | ✓ | ✓ | ✓ (3.35+) |
| `ALTER TABLE ALTER COLUMN` | ✓ | ✓ | ✓ | ✓ | ✗ (recreate) |
| `ALTER TABLE ADD CONSTRAINT FK` | ✓ | ✓ | ✓ | ✓ | ✗ (inline only) |
| `ALTER TABLE DROP CONSTRAINT FK` | ✓ | ✓ | ✓ | ✓ | ✗ (recreate) |
| Cascading actions: `RESTRICT` | ✓ | ✗ (→ NoAction) | ✗ (→ NoAction) | ✓ | ✓ |
| Native sequences | ✓ | ✓ | ✓ | Table-emulated | Table-emulated |
| Views | ✓ + materialized | ✓ | ✓ | ✓ | ✓ (read-only) |
| Functions / Stored Procedures | ✓ (PL/pgSQL) | ✓ (T-SQL) | ✓ (PL/SQL) | (limited) | Connection-scoped |
| Partitioning | ✓ (hash/range/list) | ✓ (range) | ✓ | ✓ | ✗ |
| JSON storage | `jsonb` native | `nvarchar(max)` | `CLOB` | `JSON` | `TEXT` (JSON1) |
| Array columns | ✓ | ✗ | ✗ | ✗ | ✗ (except `byte[]`) |
| Statement terminator | `;` | `;` | `/` (PL/SQL blocks) | `;` | `;` |

## Identity / auto-increment

In 9.0 every provider exposes a canonical `.AutoIncrement()` fluent method on
`Table.ColumnExpression`. Previously each provider used a different spelling;
the old names are kept as `[Obsolete]` aliases for one major-version cycle.

| Provider | Canonical 9.0 | Pre-9.0 alias (`[Obsolete]`) | SQL Emitted |
| --- | --- | --- | --- |
| PostgreSQL | `AutoIncrement()` | `Serial()` | `SERIAL` |
| PostgreSQL | `BigAutoIncrement()` | `BigSerial()` | `BIGSERIAL` |
| PostgreSQL | `SmallAutoIncrement()` | `SmallSerial()` | `SMALLSERIAL` |
| SQL Server | `AutoIncrement()` | `AutoNumber()` | `IDENTITY(1,1)` |
| Oracle | `AutoIncrement()` | `AutoNumber()` | `GENERATED BY DEFAULT AS IDENTITY` |
| MySQL | `AutoIncrement()` | `AutoNumber()` | `AUTO_INCREMENT` |
| SQLite | `AutoIncrement()` | (no change) | `AUTOINCREMENT` |

```csharp
// Same code compiles against any of the five providers
table.AddColumn<int>("id").AsPrimaryKey().AutoIncrement();
```

## Identifier comparison

`Table.ColumnFor` / `HasColumn` / `IndexFor` / `HasIndex` use a per-provider
`NameComparison`:

| Provider | `NameComparison` | Rationale |
| --- | --- | --- |
| PostgreSQL | `Ordinal` | PG is case-sensitive when quoted (the default for Weasel-generated DDL) |
| SQL Server | `OrdinalIgnoreCase` | SS identifier comparison is case-insensitive by default |
| Oracle | `OrdinalIgnoreCase` | Oracle folds unquoted identifiers to upper case |
| MySQL | `OrdinalIgnoreCase` | MySQL identifier comparison is case-insensitive on most platforms |
| SQLite | `OrdinalIgnoreCase` | SQLite identifiers are case-folded |

`RemoveColumn` is always case-insensitive on every provider — every concrete
provider used `EqualsIgnoreCase` historically.

## Primary-key name defaults

When you don't supply a `PrimaryKeyName`, each provider generates one with
its own convention:

| Provider | Default `PrimaryKeyName` |
| --- | --- |
| PostgreSQL | `pkey_{table}_{cols}` |
| SQL Server | `pkey_{table}_{cols}` |
| Oracle | `pk_{table}_{cols}` |
| MySQL | `pk_{table}_{cols}` |
| SQLite | `pk_{table}` |

## Schema-object base types

Most schema objects share a base class in `Weasel.Core`:

| Object | Core base | Notes |
| --- | --- | --- |
| Tables | `TableBase<TColumn, TIndex, TForeignKey>` | New in 9.0 (#270 step 9) |
| Foreign keys | `ForeignKeyBase` | Owns `Parse`, `LinkColumns`, `Equals`, `ReadReferentialActions` |
| Sequences | `SequenceBase` | PG / SS / Oracle native, MySQL / SQLite table-emulated |
| Functions | `FunctionBase` | PG + SS only; Oracle uses procedures; SQLite functions are connection-scoped |
| Views | `ViewBase` | All providers; SQLite is read-only |
| Indexes | `INamed` only | Provider-specific (PG is ~4× richer than the others) |
| Table columns | `ITableColumn` only | Provider-specific declaration formatting |

## DDL syntax strategy

The shared CREATE / DROP algorithm is gradually migrating to consume a
[`IDdlSyntaxStrategy`](../../../src/Weasel.Core/IDdlSyntaxStrategy.cs) object
per provider (#270 step 8). Currently routed through it:

- `WriteDropTable` (PG appends `CASCADE`, SQLite doesn't)
- `WriteCreateTableHeader` (with / without `IF NOT EXISTS`)

The remainder of `WriteCreateStatement` is still per-provider while the
strategy shape settles. Plan is to migrate column / PK / FK emission as a
follow-up.

## Constructor pattern

All five `Table` classes share the same constructor signatures:

```csharp
new Table(DbObjectName identifier); // identifier wrapped in provider-specific subclass internally
new Table(string tableName); // parsed via provider's DbObjectName.Parse
```

The PostgreSQL and SQLite implementations wrap the supplied `DbObjectName` in
`PostgresqlObjectName` / `SqliteObjectName` respectively; SS / Oracle / MySQL
pass through the `DbObjectName` directly. Either way, the public surface is
uniform — polymorphic schema-building code doesn't need to know which provider
it's targeting until DDL is emitted.

## When you do need provider-specific code

Provider-specific extensions stay on the concrete `Table` / `ColumnExpression`:

- **PostgreSQL**: `MaterializedView`, `Partitioning` (hash / range / list),
`IgnorePartitionsInMigration`, `FullTextIndex`, `UpsertFunction`,
`NpgsqlRange<T>` columns
- **SQL Server**: `SqlServerPartitioning` (range), `PartitionByRange`
- **Oracle**: `PartitionStrategy`, `PartitionExpressions`
- **MySQL**: `Engine`, `Charset`, `Collation`, `PartitionCount`,
`AddFulltextIndex`
- **SQLite**: `WithoutRowId`, `StrictTypes`, `GeneratedAs`, JSON expression
indexes via `ForJsonPath`

Polymorphic code uses the shared `ITable` / `TableBase` surface; code that
needs provider extras casts to the concrete `Table` type.
2 changes: 1 addition & 1 deletion src/DocSamples/MySqlSamples.cs
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ public void mysql_define_table()
#region sample_mysql_define_table
var table = new Table("users");

table.AddColumn<int>("id").AsPrimaryKey().AutoNumber();
table.AddColumn<int>("id").AsPrimaryKey().AutoIncrement();
table.AddColumn<string>("name").NotNull();
table.AddColumn<string>("email").NotNull().AddIndex(idx => idx.IsUnique = true);
table.AddColumn<DateTime>("created_at");
Expand Down
4 changes: 2 additions & 2 deletions src/DocSamples/SqlServerSamples.cs
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@ public void ss_define_table()
#region sample_ss_define_table
var table = new Table("dbo.users");

table.AddColumn<int>("id").AsPrimaryKey().AutoNumber();
table.AddColumn<int>("id").AsPrimaryKey().AutoIncrement();
table.AddColumn<string>("name").NotNull();
table.AddColumn<string>("email").NotNull().AddIndex(idx => idx.IsUnique = true);
table.AddColumn<DateTime>("created_at").DefaultValueByExpression("GETUTCDATE()");
Expand All @@ -64,7 +64,7 @@ public void ss_foreign_keys()
{
#region sample_ss_foreign_keys
var orders = new Table("dbo.orders");
orders.AddColumn<int>("id").AsPrimaryKey().AutoNumber();
orders.AddColumn<int>("id").AsPrimaryKey().AutoIncrement();
orders.AddColumn<int>("user_id").NotNull()
.ForeignKeyTo("dbo.users", "id", onDelete: Weasel.SqlServer.CascadeAction.Cascade);
orders.AddColumn<decimal>("total").NotNull();
Expand Down
3 changes: 2 additions & 1 deletion src/Weasel.Core/CommandBuilderBase.cs
Original file line number Diff line number Diff line change
Expand Up @@ -281,7 +281,8 @@
/// weasel#266: reflects on <c>parameters.GetType().GetProperties()</c>.
/// The static type is <see cref="object"/>, which can't carry a
/// <see cref="DynamicallyAccessedMembersAttribute"/> annotation at
/// the parameter site, so this method propagates
/// the parameter site (IL2098 — DAM is only valid on parameters of type
/// <see cref="Type"/> or <see cref="string"/>), so this method propagates
/// <see cref="RequiresUnreferencedCodeAttribute"/> to AOT-publishing
/// consumers. Callers that need AOT compatibility should pass an
/// <see cref="IDictionary{TKey,TValue}"/> instead (the dictionary
Expand Down Expand Up @@ -530,7 +531,7 @@
/// <param name="ct"></param>
/// <typeparam name="T"></typeparam>
/// <returns></returns>
public static async Task<IReadOnlyList<T>> FetchListAsync<T, TCommand>(

Check warning on line 534 in src/Weasel.Core/CommandBuilderBase.cs

View workflow job for this annotation

GitHub Actions / SQLite ubuntu-latest net10.0

Type parameter 'TCommand' has no matching typeparam tag in the XML comment on 'CommandBuilderExtensions.FetchListAsync<T, TCommand>(DbConnection, ICommandBuilder<TCommand>, Func<DbDataReader, CancellationToken, Task<T>>, DbTransaction?, CancellationToken)' (but other type parameters do)

Check warning on line 534 in src/Weasel.Core/CommandBuilderBase.cs

View workflow job for this annotation

GitHub Actions / SQLite ubuntu-latest net9.0

Type parameter 'TCommand' has no matching typeparam tag in the XML comment on 'CommandBuilderExtensions.FetchListAsync<T, TCommand>(DbConnection, ICommandBuilder<TCommand>, Func<DbDataReader, CancellationToken, Task<T>>, DbTransaction?, CancellationToken)' (but other type parameters do)

Check warning on line 534 in src/Weasel.Core/CommandBuilderBase.cs

View workflow job for this annotation

GitHub Actions / MSSQL mcr.microsoft.com/mssql/server:2019-latest net8.0

Type parameter 'TCommand' has no matching typeparam tag in the XML comment on 'CommandBuilderExtensions.FetchListAsync<T, TCommand>(DbConnection, ICommandBuilder<TCommand>, Func<DbDataReader, CancellationToken, Task<T>>, DbTransaction?, CancellationToken)' (but other type parameters do)

Check warning on line 534 in src/Weasel.Core/CommandBuilderBase.cs

View workflow job for this annotation

GitHub Actions / MSSQL mcr.microsoft.com/mssql/server:2022-latest net8.0

Type parameter 'TCommand' has no matching typeparam tag in the XML comment on 'CommandBuilderExtensions.FetchListAsync<T, TCommand>(DbConnection, ICommandBuilder<TCommand>, Func<DbDataReader, CancellationToken, Task<T>>, DbTransaction?, CancellationToken)' (but other type parameters do)

Check warning on line 534 in src/Weasel.Core/CommandBuilderBase.cs

View workflow job for this annotation

GitHub Actions / SQLite ubuntu-latest net8.0

Type parameter 'TCommand' has no matching typeparam tag in the XML comment on 'CommandBuilderExtensions.FetchListAsync<T, TCommand>(DbConnection, ICommandBuilder<TCommand>, Func<DbDataReader, CancellationToken, Task<T>>, DbTransaction?, CancellationToken)' (but other type parameters do)

Check warning on line 534 in src/Weasel.Core/CommandBuilderBase.cs

View workflow job for this annotation

GitHub Actions / Postgres postgres:15.3-alpine net8.0 Case Sensitive false

Type parameter 'TCommand' has no matching typeparam tag in the XML comment on 'CommandBuilderExtensions.FetchListAsync<T, TCommand>(DbConnection, ICommandBuilder<TCommand>, Func<DbDataReader, CancellationToken, Task<T>>, DbTransaction?, CancellationToken)' (but other type parameters do)

Check warning on line 534 in src/Weasel.Core/CommandBuilderBase.cs

View workflow job for this annotation

GitHub Actions / MySql mysql:8.0 net9.0

Type parameter 'TCommand' has no matching typeparam tag in the XML comment on 'CommandBuilderExtensions.FetchListAsync<T, TCommand>(DbConnection, ICommandBuilder<TCommand>, Func<DbDataReader, CancellationToken, Task<T>>, DbTransaction?, CancellationToken)' (but other type parameters do)

Check warning on line 534 in src/Weasel.Core/CommandBuilderBase.cs

View workflow job for this annotation

GitHub Actions / MSSQL mcr.microsoft.com/mssql/server:2019-latest net9.0

Type parameter 'TCommand' has no matching typeparam tag in the XML comment on 'CommandBuilderExtensions.FetchListAsync<T, TCommand>(DbConnection, ICommandBuilder<TCommand>, Func<DbDataReader, CancellationToken, Task<T>>, DbTransaction?, CancellationToken)' (but other type parameters do)

Check warning on line 534 in src/Weasel.Core/CommandBuilderBase.cs

View workflow job for this annotation

GitHub Actions / Postgres postgres:15.3-alpine net8.0 Case Sensitive true

Type parameter 'TCommand' has no matching typeparam tag in the XML comment on 'CommandBuilderExtensions.FetchListAsync<T, TCommand>(DbConnection, ICommandBuilder<TCommand>, Func<DbDataReader, CancellationToken, Task<T>>, DbTransaction?, CancellationToken)' (but other type parameters do)

Check warning on line 534 in src/Weasel.Core/CommandBuilderBase.cs

View workflow job for this annotation

GitHub Actions / MySql mysql:8.0 net10.0

Type parameter 'TCommand' has no matching typeparam tag in the XML comment on 'CommandBuilderExtensions.FetchListAsync<T, TCommand>(DbConnection, ICommandBuilder<TCommand>, Func<DbDataReader, CancellationToken, Task<T>>, DbTransaction?, CancellationToken)' (but other type parameters do)

Check warning on line 534 in src/Weasel.Core/CommandBuilderBase.cs

View workflow job for this annotation

GitHub Actions / MSSQL mcr.microsoft.com/mssql/server:2019-latest net10.0

Type parameter 'TCommand' has no matching typeparam tag in the XML comment on 'CommandBuilderExtensions.FetchListAsync<T, TCommand>(DbConnection, ICommandBuilder<TCommand>, Func<DbDataReader, CancellationToken, Task<T>>, DbTransaction?, CancellationToken)' (but other type parameters do)

Check warning on line 534 in src/Weasel.Core/CommandBuilderBase.cs

View workflow job for this annotation

GitHub Actions / MSSQL mcr.microsoft.com/mssql/server:2022-latest net9.0

Type parameter 'TCommand' has no matching typeparam tag in the XML comment on 'CommandBuilderExtensions.FetchListAsync<T, TCommand>(DbConnection, ICommandBuilder<TCommand>, Func<DbDataReader, CancellationToken, Task<T>>, DbTransaction?, CancellationToken)' (but other type parameters do)

Check warning on line 534 in src/Weasel.Core/CommandBuilderBase.cs

View workflow job for this annotation

GitHub Actions / Postgres postgres:15.3-alpine net9.0 Case Sensitive false

Type parameter 'TCommand' has no matching typeparam tag in the XML comment on 'CommandBuilderExtensions.FetchListAsync<T, TCommand>(DbConnection, ICommandBuilder<TCommand>, Func<DbDataReader, CancellationToken, Task<T>>, DbTransaction?, CancellationToken)' (but other type parameters do)

Check warning on line 534 in src/Weasel.Core/CommandBuilderBase.cs

View workflow job for this annotation

GitHub Actions / Postgres postgres:15.3-alpine net10.0 Case Sensitive false

Type parameter 'TCommand' has no matching typeparam tag in the XML comment on 'CommandBuilderExtensions.FetchListAsync<T, TCommand>(DbConnection, ICommandBuilder<TCommand>, Func<DbDataReader, CancellationToken, Task<T>>, DbTransaction?, CancellationToken)' (but other type parameters do)

Check warning on line 534 in src/Weasel.Core/CommandBuilderBase.cs

View workflow job for this annotation

GitHub Actions / Postgres postgres:15.3-alpine net9.0 Case Sensitive true

Type parameter 'TCommand' has no matching typeparam tag in the XML comment on 'CommandBuilderExtensions.FetchListAsync<T, TCommand>(DbConnection, ICommandBuilder<TCommand>, Func<DbDataReader, CancellationToken, Task<T>>, DbTransaction?, CancellationToken)' (but other type parameters do)

Check warning on line 534 in src/Weasel.Core/CommandBuilderBase.cs

View workflow job for this annotation

GitHub Actions / Postgres postgres:15.3-alpine net10.0 Case Sensitive true

Type parameter 'TCommand' has no matching typeparam tag in the XML comment on 'CommandBuilderExtensions.FetchListAsync<T, TCommand>(DbConnection, ICommandBuilder<TCommand>, Func<DbDataReader, CancellationToken, Task<T>>, DbTransaction?, CancellationToken)' (but other type parameters do)

Check warning on line 534 in src/Weasel.Core/CommandBuilderBase.cs

View workflow job for this annotation

GitHub Actions / Oracle net8.0

Type parameter 'TCommand' has no matching typeparam tag in the XML comment on 'CommandBuilderExtensions.FetchListAsync<T, TCommand>(DbConnection, ICommandBuilder<TCommand>, Func<DbDataReader, CancellationToken, Task<T>>, DbTransaction?, CancellationToken)' (but other type parameters do)

Check warning on line 534 in src/Weasel.Core/CommandBuilderBase.cs

View workflow job for this annotation

GitHub Actions / Oracle net9.0

Type parameter 'TCommand' has no matching typeparam tag in the XML comment on 'CommandBuilderExtensions.FetchListAsync<T, TCommand>(DbConnection, ICommandBuilder<TCommand>, Func<DbDataReader, CancellationToken, Task<T>>, DbTransaction?, CancellationToken)' (but other type parameters do)

Check warning on line 534 in src/Weasel.Core/CommandBuilderBase.cs

View workflow job for this annotation

GitHub Actions / Oracle net10.0

Type parameter 'TCommand' has no matching typeparam tag in the XML comment on 'CommandBuilderExtensions.FetchListAsync<T, TCommand>(DbConnection, ICommandBuilder<TCommand>, Func<DbDataReader, CancellationToken, Task<T>>, DbTransaction?, CancellationToken)' (but other type parameters do)

Check warning on line 534 in src/Weasel.Core/CommandBuilderBase.cs

View workflow job for this annotation

GitHub Actions / MySql mysql:8.0 net8.0

Type parameter 'TCommand' has no matching typeparam tag in the XML comment on 'CommandBuilderExtensions.FetchListAsync<T, TCommand>(DbConnection, ICommandBuilder<TCommand>, Func<DbDataReader, CancellationToken, Task<T>>, DbTransaction?, CancellationToken)' (but other type parameters do)
DbConnection connection,
ICommandBuilder<TCommand> commandBuilder,
Func<DbDataReader, CancellationToken, Task<T>> transform,
Expand Down
2 changes: 1 addition & 1 deletion src/Weasel.Core/CommandLine/AssertCommand.cs
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ public class AssertCommand: JasperFxAsyncCommand<WeaselInput>
// an ExceptionFormatter path that's RequiresDynamicCode. db-assert is
// a dev-time CLI tool (not a production hot path), so suppressing the
// warning here is the right call rather than propagating it via
// [RequiresDynamicCode] — propagation would trigger IL3051 because the
// [RequiresDynamicCode] — propagation triggers IL3051 because the
// JasperFx base method JasperFxAsyncCommand<T>.Execute(T) doesn't
// carry the attribute, and adding it there would ripple to every
// JasperFx command across the Critter Stack. Suppression keeps the
Expand Down
48 changes: 48 additions & 0 deletions src/Weasel.Core/DatabaseProvider.cs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
using System.Data.Common;
using ImTools;
using JasperFx.Core;
using JasperFx.Core.Reflection;
using Weasel.Core.Names;

namespace Weasel.Core;
Expand Down Expand Up @@ -170,6 +171,53 @@ public void RegisterMapping(Type type, string databaseType, TParameterType? para
ParameterTypeMemo.Swap(d => d.AddOrUpdate(type, parameterType));
}

/// <summary>
/// Shared memo lookup for the database-type string of a CLR type, with the
/// standard nullable-promote fallback (if <c>T?</c> is asked but <c>T</c> is
/// in the memo, copy the entry to <c>T?</c> and return it). Returns null
/// when neither the type nor its inner-nullable is mapped — subclasses then
/// apply provider-specific fallbacks (PG queries Npgsql's plugin type map,
/// SS/Oracle/MySQL throw, SQLite returns null to signal "TEXT/JSON").
/// </summary>
protected string? ResolveDatabaseTypeFromMemo(Type type)
{
if (DatabaseTypeMemo.Value.TryFind(type, out var value))
{
return value;
}

if (type.IsNullable() && DatabaseTypeMemo.Value.TryFind(type.GetInnerTypeFromNullable(), out var inner))
{
DatabaseTypeMemo.Swap(d => d.AddOrUpdate(type, inner));
return inner;
}

return null;
}

/// <summary>
/// Shared memo lookup for the provider parameter-type enum of a CLR type,
/// mirror of <see cref="ResolveDatabaseTypeFromMemo" />. Subclasses apply
/// their own fallback when this returns null (PG queries Npgsql plugin;
/// SS returns <c>Variant</c>; Oracle returns <c>Varchar2</c>; MySQL returns
/// <c>VarChar</c>; SQLite returns <c>Text</c>).
/// </summary>
protected TParameterType? ResolveParameterTypeFromMemo(Type type)
{
if (ParameterTypeMemo.Value.TryFind(type, out var value))
{
return value;
}

if (type.IsNullable() && ParameterTypeMemo.Value.TryFind(type.GetInnerTypeFromNullable(), out var inner))
{
ParameterTypeMemo.Swap(d => d.AddOrUpdate(type, inner));
return inner;
}

return null;
}

protected abstract Type[] determineClrTypesForParameterType(TParameterType dbType);

public TParameter AddParameter(TCommand command, object? value, TParameterType? dbType = null)
Expand Down
Loading
Loading