Port status
From the project's own docs/ROADMAP.md ↗ and README. The roadmap's Current status block is pinned to the commit it was written at, and new work lands after it — so this page quotes it, then adds what has landed since.
Newer than the roadmap block
Verified in the source, not taken on trust from commit titles:
| Landed after the roadmap refresh | What it means |
|---|---|
Predicates: BETWEEN, IN (list and constant set), LIKE, ILIKE, REGEXP, NOT | Ported as the reference's own predicate types — BetweenPredicate, ConditionIn / ConditionInList / ConditionInConstantSet, CompareLike, ConditionNot — not a shortcut in the parser, and none of them refuses. |
CASE, both forms | SimpleCase and SearchedCase with OperationN. |
Aggregates: JSON_ARRAYAGG, JSON_OBJECTAGG, HISTOGRAM, MODE | JSON aggregates build straight into bytes; HISTOGRAM/MODE on a value-to-count map. |
| Window frame units, bound types, exclusions | The frame machinery is ported. Window functions (OVER/FILTER) still are not. |
UPDATE SET list | Built from the reference's update-action array. |
| Built-in functions | A function dispatcher plus the built-ins: ABS, MOD, FLOOR/CEIL/ROUND/TRUNC, the trigonometry, LN/EXP/SQRT/LOG/POWER, the whole bit family (BITAND…ROTATERIGHT), EXTRACT, DATE_TRUNC, DATEADD/DATEDIFF, FORMATDATETIME/PARSEDATETIME, DAYNAME/MONTHNAME, the string family (UPPER…SUBSTRING, TRIM, LPAD/RPAD, REPLACE, REGEXP_*, SOUNDEX/DIFFERENCE), XML*, JSON_OBJECT/JSON_ARRAY, array functions, COMPRESS/EXPAND, CONCAT, COALESCE, NULLIF, RANDOM. |
AS in a table alias | Parsed and pinned against the reference's answers. The implicit alias (FROM T e) worked before; note that bare column aliases still refuse. |
CAST | Cast in expressions and the data type reader are ported, including the format template the reference reads after the type — CAST(x AS DATE FORMAT 'yyyy-MM-dd'). |
CAST | Cast in expressions and the data type reader are ported, including the format template the reference reads after the type — CAST(x AS DATE FORMAT 'yyyy-MM-dd'). |
| Statements commit at the reference's points | The reference's stop and commitIfNonTransactional are ported, so a single INSERT/UPDATE/DELETE is durable on its own and DDL commits its transaction. |
CREATE TABLE … PRIMARY KEY | Creates, names and drops the index the way H2 does — the PRIMARY_KEY_ prefix, and the same name back when the table is recreated, as on the JVM. |
| Constraints are schema objects | Named and table-level PRIMARY KEYs own their index and the 23505 duplicate-key prefix; ALTER TABLE adds and drops them and frees their names. |
CREATE INDEX / DROP INDEX | Ported over the full MVSecondaryIndex and its row factories. |
| LIKE / REGEXP semantics | Semantics suites pass; the default LIKE escape and IN parameter binding were fixed. |
| Allocation work | Pooled page scratches, one Bits.Reverse for the MVStore byte helpers, the pattern Regex cached once, digit appends without an intermediate string, numeric conversion delegates hoisted to statics. |
| Differential harness | Oracle snippets compile once into a per-machine cache and run from it, so the JVM side costs nothing per repeat. |
Phases
SQL that executes
| Area | Works |
|---|---|
| Queries | Flat SELECT, UNION/UNION ALL, DISTINCT, GROUP BY/HAVING, ORDER BY, LIMIT/OFFSET/FETCH, DUAL, CAST, literals |
| Predicates | BETWEEN, IN, LIKE, ILIKE, REGEXP, NOT, CASE (simple and searched) — as the reference's own predicate types |
| Parameters | ? placeholders, and @name rewritten |
| Aggregates | COUNT/COUNT(*), SUM, MIN, MAX, AVG, STDDEV*, VAR*, ANY/EVERY, MEDIAN, ANY_VALUE, HISTOGRAM, MODE, PERCENTILE_CONT/DISC, LISTAGG (+ GROUP_CONCAT/STRING_AGG), JSON_ARRAYAGG/JSON_OBJECTAGG, REGR_* (count, avgx, avgy, sxx, syy, slope, intercept, r2, sxy), COVAR_*, CORR, BIT_*_AGG (+ BIT_AND/BIT_OR), ARRAY_AGG |
| DML | INSERT, UPDATE, DELETE (text with ? parameters) |
| DDL | CREATE TABLE (columns + inline PK), DROP TABLE, CREATE INDEX, DROP INDEX, ALTER TABLE (adding and dropping PK constraints) — ALTER TABLE is partial |
SQL conformance suite
The repo carries tests/H2SharpSql.Specs — 947 tests, one per documented SQL construct, each anchored to a line number in the frozen snapshot the repo keeps in docs/. It is deliberately not part of H2Sharp.sln: while the port is young it shows hundreds of failures, and the daily ritual dotnet test H2Sharp.sln should not depend on it.
| Area | Passed | Total | Note |
|---|---|---|---|
SystemTables/ | 17 | 37 | was 0 — the catalogue is ported now |
Commands/ | 17 | 254 | was 11 |
Grammar/ | 61 | 263 | was 49 |
Datatypes/ | 38 | 66 | was 37 |
| Total | 251 | 947 | the first run, on 2026-10-19, was 102 |
The biggest gaps by failure count were: EXTRACT in the parser (66), FILTER/OVER (35), INFORMATION_SCHEMA as a table (25), CREATE DOMAIN (15), ALTER TABLE beyond ADD/DROP CONSTRAINT (12), YEAR (12), derived tables (12), CREATE ROLE (11), HOUR (10), table constraints other than PRIMARY KEY (9), CREATE SEQUENCE (8).
The catalogue is ported
INFORMATION_SCHEMA came across with the reference's own column lists, read verbatim from its getColumns(), with an index of its own whose cursor computes the rows at read time — so the view is always current. The schema is created with the database, as in the reference.
| Filled | Empty for now |
|---|---|
CATALOG_NAME, SCHEMATA, TABLES, VIEWS, COLUMNS, TABLE_CONSTRAINTS, KEY_COLUMN_USAGE, CONSTRAINT_COLUMN_USAGE, INDEXES, INDEX_COLUMNS, COLLATIONS (only OFF — no ICU in the port), SETTINGS (only the version) | USERS, ROLES, RIGHTS, TABLE_PRIVILEGES, COLUMN_PRIVILEGES, PARAMETERS, ROUTINES, CONSTANTS, DOMAINS, TRIGGERS, SYNONYMS, SEQUENCES, SESSIONS, LOCKS and others — the views are there with the reference's columns, and fill as their objects arrive |
The parser learned a qualified name in FROM (INFORMATION_SCHEMA.TABLES, an unknown schema giving 90079 as in the reference) and SYSTEM_RANGE(min, max[, step]) with its GENERATE_SERIES alias. A selection list may now be a comparison — SELECT COUNT(*) > 0 FROM … reads.
Verified against the reference: InformationSchemaOracleTests.TheCatalogueAnswersTheSameAsTheReference asks twelve questions (schemata, all tables, all columns — every one of the 33 views and all their columns — constraints, key usage, constraint usage, indexes, index columns, views, collations, SYSTEM_RANGE both ways) and compares every row and every cell.
The providers
Two provider projects run alongside the engine, each in its own repository, each with its own history: dimzon/h2sharp-efcore — a fork of EFCore.PG with PostgreSQL's surface replaced by H2's, keeping EF Core's own specification suite as the measure; and dimzon/h2sharp-linq2db — written from scratch against linq2db's public API, not a fork.
| Run | Passed | Failed | Total |
|---|---|---|---|
Category!=BlockedByPort — the provider is correct as far as the port allows | 1034 | 0 | 1034 |
Category=BlockedByPort — the port defects the provider has signed up for | 19 | 185 | 206 |
| Total | 1053 | 185 | 1240 |
Both runs are needed, and the split is the point: a test failing because a function was not ported stays in the suite, tagged BlockedByPort, so the port's remaining work is visible rather than quietly removed. The provider is checked against the reference engine on the JVM, not against the port — a test against the port would only prove the port agrees with itself.
The engine gaps it is waiting on are numbered in its docs/DIVERGENCES.md: quoted column references, AS aliases (now landed), GENERATED … AS IDENTITY, FOREIGN KEY in CREATE TABLE, INTERVAL types, DB_CLOSE_DELAY, the INFORMATION_SCHEMA catalogue, and CREATE SEQUENCE.
The linq2db provider
Written from scratch against linq2db's public API and the engine's public API — not a fork of another provider. It generates H2's SQL: always-quoted identifiers, H2's own types, LIMIT/OFFSET, || for concatenation, named @name parameters, and a table alias without the AS — the form the engine reads. Query, insert, filter with a parameter and order work against the engine; the schema provider reads the catalogue the way H2 2.5 keeps it.
Its docs/DIVERGENCES.md lists the port defects it waits on, and every one of them is the engine's work, not a limit of H2: the catalogue's name differs from the reference's, DB_CLOSE_DELAY and file databases, the engine's catalogue not being reachable from the ADO layer (H2Connection.Session is internal), JOIN, AS before a table alias, identity DDL, CREATE SEQUENCE, and IN with a subquery.
Not yet — as port defects, not as limits of H2
The project's own wording: a feature missing from the port is unfinished work, not a property of the database. JOIN, subqueries, window functions, MERGE, INFORMATION_SCHEMA, CREATE SEQUENCE, identity DDL and AS before a table alias all exist in H2 2.5 and will be in the port — the orientation is the frozen documentation and the reference jar, not the port's current state. Each gap below is a tracked defect with an owner, and the tests that fail on it stay in the suite, tagged BlockedByPort.
JOIN, subqueries, window functions (OVER/FILTER),MERGE,MERGE USING,EXPLAIN— the window frame machinery is ported, window functions are not- The
SAVEPOINTstatement — row operations use an internal savepoint, which is not the SQL command - The reference's statement scope, savepoint wrapping and rollback of a failed attempt — the comments in
Command.cssay so outright;ExecuteQueryand batch execution still refuse - Quoted column references (
SELECT "e"."Id") — the query layer's resolver is not ported - File databases and async
OpenAsync— in-memory only today - Constraints and rights
- Table constraints other than
PRIMARY KEY;CREATE SEQUENCE,CREATE DOMAIN,CREATE ROLE INFORMATION_SCHEMAas a queryable table — the catalogue is ported now (17 of 37 system-table tests pass); the rest need DDL the port does not have yet
Tests
| Project | Count |
|---|---|
H2Sharp.Tests | 1115 |
H2Sharp.Data.Tests | 343, plus 19 red — the same AS-alias, CTE, JOIN and MERGE pins |
H2Sharp.MvStore.Tests | 86 |
H2Sharp.Engine.Tests | 4 of 9 — red by design (the reference TestIndex port; the rest of the engine suite is not ported) |