MariaDB provider
Use
nextorm.mariadbfor MariaDB 10.4+; it reuses the MySQL rendering and adds theINTERSECT ALL/EXCEPT ALLset-operation variants.
Prerequisites: Provider overview · MySQL
Overview
MariaDbDataContext (src/nextorm.mariadb/MariaDbDataContext.cs) derives from MySqlDataContext, so it uses
the same MySqlConnector driver and the same connection and parameter handling. It returns
Instance from its Dialect property.
MariaDbDialect (src/nextorm.mariadb/MariaDbDialect.cs) derives from MySqlDialect and changes the
capability set: INTERSECT ALL and EXCEPT ALL are supported by MariaDB 10.4 and later, so
SupportsIntersectExceptAll is true; MariaDB 10.3+ renders the window percentiles
percentile_cont/percentile_disc (... within group (order by ...) over (...)), so
SupportsPercentileWindow is true; the same
analytic shape backs SqlFunctions.MySql.median(value).Over(...) (rendered
percentile_cont(0.5) within group (order by value) over (...), one row per input row, partition-only
window), while the generic SqlFunctions.MySql.percentile has no MariaDB spelling and throws
NotSupportedException; and UPDATE ... RETURNING is enabled only when an explicit
server version 13.0+ is configured (SupportsUpdateReturning). It also keeps the arbitrary-value
aggregate any_agg gated off, because MariaDB has no ANY_VALUE in 10.4–12.x (the SQL-2023 T626
feature is still pending, targeted for 13.2). Everything else (parameters, quoting, concat,
coalesce, paging, aggregate names, the if(...) spelling of the portable iif) is inherited unchanged.
Registering the provider
Two overloads are available on DataContextBuilder
(src/nextorm.mariadb/DI/MariaDbDataContextOptionsBuilderExtensions.cs):
using NextORM.Core;
using NextORM.MariaDb;
var builder = new DataContextBuilder()
.UseMariaDb("Server=localhost;Port=3306;Database=app;User ID=app;Password=secret");
using var ctx = builder.CreateDataContext(); // IDataContext
You can also construct the context directly:
using NextORM.Core;
using NextORM.MariaDb;
using IDataContext ctx = new MariaDbDataContext(
"Server=localhost;Database=app;User ID=app;Password=secret", new DataContextBuilder());
Server version
The dialect can be pinned to the server version, which gates UPDATE ... RETURNING and the bulk
Returning* terminals. Configuration is explicit — nextorm never probes the live server. Pass the
version with the context constructor's Version parameter. Unlike PostgreSQL, an unset version does
not enable the feature: without an explicit 13.0+ the Returning() terminal of an UPDATE and a bulk
Returning* call reject with NotSupportedException. Single-row insert and delete RETURNING are
unchanged (MariaDB rejects them as before), and the ANY_VALUE gate (any_agg, targeted for 13.2) is also
unchanged. Bulk Returning* is supported on 13.0+ and refused below; the live certification runs on
mariadb:11.4, so it covers the refusal arm.
// Explicit 13.0+: `UPDATE ... RETURNING` is enabled.
using var ctx = new MariaDbDataContext(
"Server=localhost;Database=app;...", new DataContextBuilder(), new Version(13, 0));
The same explicit version also gates the native functions MariaDB added after 11.4. Each is emitted only at or
above its own minimum; an unversioned context (the two-argument constructor) refuses it with
NotSupportedException at translation/preparation rather than emitting SQL the server rejects:
| Member | Native form | Minimum |
|---|---|---|
SqlFunctions.MySql.months_between(a, b) |
months_between(...) |
12.2.1 |
SqlFunctions.MySql.to_date(value, format) |
to_date(...) |
12.3 |
SqlFunctions.MySql.to_number(value, format) |
to_number(...) |
12.2 family, enforced 12.2.1 |
SqlFunctions.Sql.gen_random_uuid() |
uuid_v4() |
11.7 |
SqlFunctions.Sql.uuidv7() |
uuid_v7() |
11.7 |
These are function minima, not a raised provider floor: the package still supports MariaDB 10.4+ and
add_months/to_char/the rest of the catalogue keep their own availability. The MariaDB-11.4 baseline names
(add_months, to_char, nvl, the JSON family, sequences) are not version-gated.
Unlike PostgreSQL, MariaDB imposes no one-version-per-concrete-context-type guard: each context carries its own dialect instance and capability predicate, so the same concrete context type may be built with different versions. A strongly typed subclass is still a convenient way to pin one version:
public sealed class MariaDb130DataContext : MariaDbDataContext
{
public MariaDb130DataContext(string connectionString, DataContextBuilder builder)
: base(connectionString, builder, new Version(13, 0)) { }
}
Set operations
ctx.From<ISimpleEntity>().Select(x => x.Id).IntersectAll(ctx.From<ISimpleEntity>().Select(x => x.Id));
ctx.From<ISimpleEntity>().Select(x => x.Id).ExceptAll(ctx.From<ISimpleEntity>().Select(x => x.Id));
select id
from simple_entity
intersect all
select id
from simple_entity
Bulk insert
MariaDB shares the MySQL implementation, so the native path is MySqlBulkCopy (a LOAD DATA LOCAL INFILE
load) and it needs AllowLoadLocalInfile=true on the connection string plus the server variable
@@GLOBAL.local_infile=1, read with a read-only preflight. nextorm never enables either one; without them
Auto falls back only when the portable path honours the whole request and Native refuses by name.
- Identity and duplicates follow MySQL:
KeepIdentitywrites the explicit identity column under a scopedNO_AUTO_VALUE_ON_ZEROguard, andIgnoreDuplicatesisConflictOption.Ignoreon the native load orINSERT IGNOREon the portable statement. CheckConstraints=falseis honoured through scoped session controls, snapshotted and restored on the same session even when the load fails;TableLockandFireTriggersstay refused.KeepNulls=falseneeds a default-equivalent shape.Returning*on the bulk path is version-gated. MariaDB honours it at an explicitly configured 13.0+ and refuses it below (unset or older) with a namedNotSupportedException; single-row insert and deleteRETURNINGstay refused at every version, so read keys withCreateInsertBuilderthere. The live certification runs onmariadb:11.4, so it exercises the refusal arm; the 13.0+ arm is certified offline for the renderedINSERT ... RETURNINGSQL only.DatabaseNamerendersdb.table;ServerNameis refused (no rerouting).Timeoutis one whole-invocation budget; an expired budget is aTimeoutExceptionand cancellation wins.
See Bulk insert.
Provider differences
MariaDB differs from MySQL in the set-operation capability, the window percentiles, the version-gated UPDATE ... RETURNING and the arbitrary-value aggregate:
| Aspect | MariaDB |
|---|---|
| Parameter placeholder | @name |
| Concat | concat(a, b) |
| Coalesce | coalesce |
| Identifier quoting | backticks (as `t1`) |
| Derived table alias | required |
*ALL |
supported (MariaDB 10.4+) |
UPDATE ... RETURNING |
enabled only with an explicit server version 13.0+ (SupportsUpdateReturning); otherwise NotSupportedException |
| Server version | configured explicitly via the context Version |
| Text JSON | inherited from MySQL (JSON_EXTRACT/JSON_SET) |
| Session/info functions | inherited from MySQL (current_user(), session_user(), schema(), database(), version()) |
| Arbitrary-value aggregate | not supported (no ANY_VALUE in 10.4–12.x; pending MDEV-10426, targeted for 13.2) |
| Conditional function | inherited from MySQL (iif(cond, a, b) → if(cond, a, b)) |
| Window percentiles | percentile_cont/percentile_disc as ... within group (order by x) over (...) (MariaDB 10.3+); analytic: one row per input row |
| Median (analytic) | SqlFunctions.MySql.median(v).Over(...) → percentile_cont(0.5) within group (order by v) over (...): one row per input row; partition-only OVER (window ORDER BY/frame refused) |
| Generic percentile | not supported: SqlFunctions.MySql.percentile(fraction, v) throws NotSupportedException("The percentile function is not supported by MariaDB.") |
| Percentile fraction | double in [0, 1] (not a percentage), validated before rendering; an out-of-range or non-finite value throws ArgumentOutOfRangeException |
| Native functions | the SqlFunctions.MySql surface inherited from MySQL minus uuid_to_bin/bin_to_uuid, plus the MariaDB-only names (extended regexp, nvl/nvl2, add_months/months_between, to_char/to_date/to_number, kdf, xxh3/xxh32, json_detailed/json_compact, sequence access). months_between/to_date/to_number (12.2.1 / 12.3 / 12.2.1) and xxh3/xxh32 (13.1) are version-gated: an unversioned context, or a server below the minimum, refuses them with NotSupportedException before any SQL is emitted |
LOB streaming (ToStream/ToTextReader) |
NotSupportedException |
See also
- Provider overview
- MySQL and MariaDB-specific SQL
- MySQL
- ClickHouse
- In-memory
- Limitations and out-of-scope features
Source: src/nextorm.mariadb/MariaDbDialect.cs, src/nextorm.mariadb/MariaDbDataContext.cs,
src/nextorm.mariadb/DI/MariaDbDataContextOptionsBuilderExtensions.cs,
tests/nextorm.mariadb.tests/Dialect/MariaDbDialectTests.cs, tests/nextorm.mariadb.tests/SqlGeneration/SqlGenerationTests.cs.