Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
32 commits
Select commit Hold shift + click to select a range
2816280
Add runner filtering, lifecycle stages and transaction modes
jogibear9988 Sep 22, 2026
55b5811
Add guarded SQL preview, native locks, CLI and DI integration
jogibear9988 Sep 22, 2026
9730079
Register packaged CLI database factories before connecting
jogibear9988 Sep 22, 2026
fedf79f
Harden tooling error boundaries, preview lifecycle and logging
jogibear9988 Sep 22, 2026
90d81ca
Preserve migration failures when releasing a deployment lock fails
jogibear9988 Sep 22, 2026
3747c8d
Make CLI rollback reject upward migration targets under the lock
jogibear9988 Sep 22, 2026
bde0a05
Replace stale matrix artifacts when failed CI jobs are rerun
jogibear9988 Sep 22, 2026
b4a6edd
Exercise concurrent runners and stale history under native deployment…
jogibear9988 Sep 22, 2026
5203ae2
Await both concurrent runner tasks before test database cleanup
jogibear9988 Sep 22, 2026
9804fef
Preserve runner review fixes while aligning the PR stack with master
jogibear9988 Sep 22, 2026
8cde651
Document upgrade source capabilities and refresh framework comparison
jogibear9988 Sep 22, 2026
aec29ab
Refresh framework comparison against the reviewed upgrade source
jogibear9988 Sep 22, 2026
508218a
Document script semantics, ownership cleanup and the complete issue i…
jogibear9988 Sep 22, 2026
593ac15
Refresh comparison and ownership guidance against the integrated stack
jogibear9988 Sep 22, 2026
5beaf05
Record individual baseline evidence for the historical issue audit
jogibear9988 Sep 22, 2026
b254927
Document PostgreSQL metadata scope and verified concurrent-runner cov…
jogibear9988 Sep 22, 2026
8ee8d39
Record successful CI for the final integrated source revision
jogibear9988 Sep 22, 2026
d9998be
Introduce v13 named table constraints and structured metadata
jogibear9988 Sep 22, 2026
dba4db4
Avoid reserved Oracle bind names in structured constraint metadata
jogibear9988 Sep 22, 2026
3962b37
Preserve named SQLite primary keys through table reconstruction
jogibear9988 Sep 22, 2026
811a852
Replace column constraint flags with explicit v13 schema definitions
jogibear9988 Sep 22, 2026
ebdbb42
Fix v13 provider catalog regressions and share SQLite table rendering
jogibear9988 Sep 22, 2026
529e35e
Add explicit SQL defaults and semantic column collations for both aut…
jogibear9988 Sep 22, 2026
2504c15
Render SQL Server collations as validated T-SQL tokens
jogibear9988 Sep 22, 2026
662ee0b
Preserve raw default expressions in SQL Server, Oracle and PostgreSQL…
jogibear9988 Sep 22, 2026
e606a39
Handle expression boundaries and adjacent comments in SQLite schema i…
jogibear9988 Sep 22, 2026
11d6083
Preserve constraint details and reject ignored Oracle index options
jogibear9988 Sep 22, 2026
ac36162
Qualify additional FluentMigrator engines against real CI infrastructure
jogibear9988 Sep 22, 2026
c42b0d1
Use the supported Linux shared-memory segment limit for HANA qualific…
jogibear9988 Sep 22, 2026
0b854f6
Add the SAP HANA provider after actual-engine CI qualification
jogibear9988 Sep 22, 2026
fbe9c58
Fix HANA factory activation and use engine-supported column defaults
jogibear9988 Sep 22, 2026
eabec55
Retain the original CI connection string for HANA factory integration…
jogibear9988 Sep 22, 2026
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
4 changes: 4 additions & 0 deletions .github/qualification/Hana/Hana.csproj
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup><OutputType>Exe</OutputType><TargetFramework>net9.0</TargetFramework><ImplicitUsings>enable</ImplicitUsings></PropertyGroup>
<ItemGroup><PackageReference Include="Sap.Data.Hana.Net.v8.0" Version="2.30.27" /></ItemGroup>
</Project>
39 changes: 39 additions & 0 deletions .github/qualification/Hana/Program.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
using Sap.Data.Hana;

using var connection = new HanaConnection(Environment.GetEnvironmentVariable("MIGRATOR_HANA")
?? throw new InvalidOperationException("MIGRATOR_HANA must identify the disposable CI database."));
connection.Open();
void Execute(string sql)
{
using var command = connection.CreateCommand();
command.CommandText = sql;
command.ExecuteNonQuery();
}
object Scalar(string sql)
{
using var command = connection.CreateCommand();
command.CommandText = sql;
return command.ExecuteScalar();
}
Execute("CREATE SCHEMA MIGRATOR_QUALIFICATION");
try
{
Execute("CREATE ROW TABLE MIGRATOR_QUALIFICATION.ITEMS (ID INTEGER GENERATED BY DEFAULT AS IDENTITY, LABEL NVARCHAR(40) DEFAULT 'initial', CONSTRAINT PK_ITEMS PRIMARY KEY (ID), CONSTRAINT UQ_LABEL UNIQUE (LABEL))");
Execute("INSERT INTO MIGRATOR_QUALIFICATION.ITEMS (LABEL) VALUES ('kept')");
if (Convert.ToInt32(Scalar("SELECT COUNT(*) FROM MIGRATOR_QUALIFICATION.ITEMS")) != 1)
throw new Exception("Inserted row missing.");
using (var transaction = connection.BeginTransaction())
{
using var command = connection.CreateCommand();
command.Transaction = transaction;
command.CommandText = "INSERT INTO MIGRATOR_QUALIFICATION.ITEMS (LABEL) VALUES ('rolled back')";
command.ExecuteNonQuery();
transaction.Rollback();
}
if (Convert.ToInt32(Scalar("SELECT COUNT(*) FROM MIGRATOR_QUALIFICATION.ITEMS")) != 1)
throw new Exception("Rollback did not preserve the original row count.");
if (Convert.ToInt32(Scalar("SELECT COUNT(*) FROM SYS.TABLE_COLUMNS WHERE SCHEMA_NAME='MIGRATOR_QUALIFICATION' AND TABLE_NAME='ITEMS'")) != 2)
throw new Exception("Column catalog could not be read.");
Console.WriteLine("HANA qualification passed: native .NET connection, DDL, identity, constraints, data, rollback and catalog access.");
}
finally { Execute("DROP SCHEMA MIGRATOR_QUALIFICATION CASCADE"); }
1 change: 1 addition & 0 deletions .github/scripts/start-database.sh
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ pull() {
}
case "$database" in
Unit|SQLite) exit 0 ;;
Hana) bash .github/scripts/start-hana.sh; exit 0 ;;
MySQL)
docker run -d --name migrator-db -p 3306:3306 -e MYSQL_ROOT_PASSWORD=rootpass -e MYSQL_DATABASE=testdb -e MYSQL_USER=testuser -e MYSQL_PASSWORD=testpass mysql:8.0.44
ready() { docker exec migrator-db mysql -uroot -prootpass -e 'SELECT 1' >/dev/null 2>&1; }
Expand Down
28 changes: 28 additions & 0 deletions .github/scripts/start-hana.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
#!/usr/bin/env bash
set -euo pipefail
sudo sysctl -w fs.file-max=20000000 fs.aio-max-nr=262144 vm.memory_failure_early_kill=1 vm.max_map_count=135217728
mkdir -p "$RUNNER_TEMP/hana"
printf '{"master_password":"MgT9ci7Q4xZ2"}' > "$RUNNER_TEMP/hana/password.json"
sudo chown -R 12000:79 "$RUNNER_TEMP/hana"
sudo chmod 600 "$RUNNER_TEMP/hana/password.json"
docker pull saplabs/hanaexpress:2.00.088.00.20251110.1
docker run -d --name migrator-db --hostname hxe -p 39041:39041 \
--ulimit nofile=1048576:1048576 \
--sysctl kernel.shmmax=1073741824 --sysctl kernel.shmmni=32768 --sysctl kernel.shmall=8388608 \
--sysctl net.ipv4.ip_local_port_range="40000 60999" \
-v "$RUNNER_TEMP/hana:/hana/mounts" \
saplabs/hanaexpress:2.00.088.00.20251110.1 \
--passwords-url file:///hana/mounts/password.json --agree-to-sap-license
for attempt in $(seq 1 180); do
if docker exec migrator-db /usr/sap/HXE/HDB90/exe/hdbsql -i 90 -d HXE -u SYSTEM -p MgT9ci7Q4xZ2 'SELECT 1 FROM DUMMY' >/dev/null 2>&1; then
echo "MIGRATOR_HANA=Server=localhost:39041;UserID=SYSTEM;Password=MgT9ci7Q4xZ2" >> "$GITHUB_ENV"
exit 0
fi
if [ "$(docker inspect -f '{{.State.Running}}' migrator-db)" != true ]; then
docker logs migrator-db
exit 1
fi
sleep 5
done
docker logs migrator-db
exit 1
6 changes: 3 additions & 3 deletions .github/scripts/test.ps1
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
param(
[ValidateSet('Unit','SQLite','SQLServer','PostgreSQL','Oracle','MySQL','MariaDB','Firebird','Db2','Informix','Sybase')]
[ValidateSet('Unit','SQLite','SQLServer','PostgreSQL','Oracle','MySQL','MariaDB','Firebird','Db2','Informix','Sybase','Hana')]
[string]$Database = 'Unit'
)
$ErrorActionPreference = 'Stop'
$databases = @('SQLite','SQLServer','PostgreSQL','Oracle','MySQL','MariaDB','Firebird','Db2','Informix','Sybase')
$databases = @('SQLite','SQLServer','PostgreSQL','Oracle','MySQL','MariaDB','Firebird','Db2','Informix','Sybase','Hana')
$filter = if ($Database -eq 'Unit') { ($databases | ForEach-Object { "TestCategory!=$_" }) -join '&' } else { "TestCategory=$Database" }
$xmlDirectory = Join-Path (Get-Location) "TestResults/$Database"
dotnet test Migrator.slnx --no-build --filter $filter --logger "trx;LogFileName=$Database.trx" --results-directory TestResults -- NUnit.NumberOfTestWorkers=0 "NUnit.TestOutputXml=$xmlDirectory"
Expand All @@ -15,6 +15,6 @@ if ([int]$counters.failed -gt 0) { throw "Failures in $Database results" }
$skipped = @($results.TestRun.Results.UnitTestResult | Where-Object outcome -eq NotExecuted)
Write-Host "$Database : $($counters.passed) passed, $($skipped.Count) skipped"
foreach ($test in $skipped) { Write-Host "Skipped: $($test.testName) $($test.Output.ErrorInfo.Message)" }
if ($Database -in @('MySQL','MariaDB','Firebird','Db2','Informix','Sybase') -and $skipped.Count -gt 0) {
if ($Database -in @('MySQL','MariaDB','Firebird','Db2','Informix','Sybase','Hana') -and $skipped.Count -gt 0) {
throw "New database suites must not skip tests."
}
2 changes: 1 addition & 1 deletion .github/scripts/verify-test-coverage.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
import xml.etree.ElementTree as ET

expected = {"Unit", "SQLite", "SQLServer", "PostgreSQL", "Oracle", "MySQL",
"MariaDB", "Firebird", "Db2", "Informix", "Sybase"}
"MariaDB", "Firebird", "Db2", "Informix", "Sybase", "Hana"}
seen = {}
executed = 0
counts = set()
Expand Down
3 changes: 2 additions & 1 deletion .github/workflows/dotnetpull.yml
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ jobs:
strategy:
fail-fast: false
matrix:
database: [Unit, SQLite, SQLServer, PostgreSQL, Oracle, MySQL, MariaDB, Firebird, Db2, Informix, Sybase]
database: [Unit, SQLite, SQLServer, PostgreSQL, Oracle, MySQL, MariaDB, Firebird, Db2, Informix, Sybase, Hana]
steps:
- uses: actions/checkout@v4
- uses: actions/setup-dotnet@v4
Expand Down Expand Up @@ -62,6 +62,7 @@ jobs:
if: always()
with:
name: test-results-${{ matrix.database }}
overwrite: true
path: TestResults/
if-no-files-found: error
- name: Remove test container
Expand Down
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -17,3 +17,4 @@ packages/

**/appsettings.Development.json
TestResults/
/artifacts/
11 changes: 11 additions & 0 deletions Migrator.slnx
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,9 @@
<Folder Name="/Core/">
<Project Path="src/Migrator/DotNetProjects.Migrator.csproj" />
</Folder>
<Folder Name="/examples/">
<Project Path="examples/FluentQuickStart/FluentQuickStart.csproj" />
</Folder>
<Folder Name="/Extras/">
<File Path="README.md" />
</Folder>
Expand All @@ -13,6 +16,14 @@
<Folder Name="/Solution Items/">
<File Path=".editorconfig" />
</Folder>
<Folder Name="/src/" />
<Folder Name="/src/Migrator.Extensions.DependencyInjection/">
<Project Path="src/Migrator.Extensions.DependencyInjection/DotNetProjects.Migrator.Extensions.DependencyInjection.csproj" />
</Folder>
<Folder Name="/src/Migrator.Tool/">
<Project Path="src/Migrator.Tool/DotNetProjects.Migrator.Tool.csproj" />
</Folder>
<Folder Name="/src/Migrator/" />
<Folder Name="/Tests/">
<Project Path="src/Migrator.Tests/Migrator.Tests.csproj" />
</Folder>
Expand Down
18 changes: 16 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ DotNetProjects.Migrator is a fork of [Migrator.NET](https://github.com/migratord
- **Bring your database driver.** The library does not directly reference database-driver packages; supply an ADO.NET connection or configure the driver factory.
- **SQLite schema handling.** This fork includes schema inspection and table-recreation logic for operations SQLite cannot perform directly.

Migrator is a library you embed in a migration host. It does not provide EF-style model-difference scaffolding, a packaged command-line runner, or built-in migration-content checksum validation.
The source upgrade adds a structured fluent API, runner filtering/lifecycle options, SQL-preview subset, native locking, a CLI project and optional Microsoft DI/logging integration. These changes are under review and **are not a released NuGet feature claim**. See the [runner and fluent guide](docs/runner-guide.md) and [detailed framework comparison](docs/migration-framework-comparison.md). EF-style model scaffolding and migration-content checksums remain outside the implementation.

## Installation and requirements

Expand Down Expand Up @@ -81,7 +81,7 @@ public class CreateUsers : Migration
public override void Up()
{
Database.AddTable("Users",
new Column("Id", DbType.Int32, ColumnProperty.NotNull),
new Column("Id", DbType.Int32) { IsNullable = false },
new Column("Name", DbType.String, 255));
Database.AddPrimaryKey("PK_Users", "Users", "Id");
}
Expand Down Expand Up @@ -187,6 +187,16 @@ Important details:

See [ProviderFactory](src/Migrator/ProviderFactory.cs), [MigrationLoader](src/Migrator/MigrationLoader.cs) and [history implementation](src/Migrator/Providers/TransformationProvider.cs).

## Fluent API and deployment tooling

Run the [compiled fluent example](examples/FluentQuickStart/Program.cs):

```sh
dotnet run --project examples/FluentQuickStart
```

The example creates a complete table definition, previews it without changing history, runs a whole-session migration, then verifies automatic reversal. The [runner guide](docs/runner-guide.md) covers CLI commands, tags/profiles, maintenance, transactions, optional DI/logging, locks and preview limitations. Build the source packages locally to try the new tooling; no NuGet publication accompanies these PRs.

## Schema and data operations

Inside a migration, `Database` implements [`ITransformationProvider`](src/Migrator/Framework/ITransformationProvider.cs). It includes:
Expand Down Expand Up @@ -305,3 +315,7 @@ This project continues the original [Migrator.NET](https://github.com/migratordo
## License

The package declares **Mozilla Public License 1.1 (MPL-1.1)** in its [project metadata](src/Migrator/DotNetProjects.Migrator.csproj). See the [license text](https://www.mozilla.org/en-US/MPL/1.1/) and source-file notices.

### Version 13 source changes

The unreleased v13 stack separates columns from named table constraints and removes the old column flags and duplicate fluent builder. See the [12.1-to-13 migration guide](docs/migration-guide-12.1-to-13.md) before recompiling migrations. These source features are not claims about the published 12.1 NuGet package.
69 changes: 69 additions & 0 deletions docs/additional-database-qualification.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
# Additional database qualification

Reviewed 22 September 2026 against FluentMigrator's current
[runner projects](https://github.com/fluentmigrator/fluentmigrator/tree/main/src)
and [provider configuration](https://fluentmigrator.github.io/intro/configuration.html).
This is an implementation gate, not a claim of released provider support.

| Additional engine | Real-engine GitHub Actions route | Current disposition |
| --- | --- | --- |
| SAP HANA | Official HANA Express Linux container and SAP's .NET driver, disposable schema | Infrastructure passed; provider and mandatory matrix tests added, pending their actual-engine results |
| Amazon Redshift | AWS test warehouse/serverless endpoint with CI credentials, network access and resource cleanup | No configured test infrastructure; defer provider |
| Snowflake | Snowflake test account, warehouse, credentials and disposable database/schema | No configured test infrastructure; defer provider |
| Db2 for IBM i | IBM i endpoint on Power infrastructure and compatible .NET/ODBC driver | No configured test infrastructure; defer provider |

The existing Db2 job runs Db2 LUW, not Db2 for IBM i. PostgreSQL compatibility does
not prove Redshift behavior. Snowpark's local test framework does not test Snowflake
DDL and catalog behavior through the production .NET driver.

Older provider lists also mention SQL Server Compact and SAP SQL Anywhere.
FluentMigrator's current FAQ marks these as dropped; they are not current additions
to pursue. Different SQL Server/PostgreSQL dialect versions and Oracle drivers are
not additional database engines.

## HANA admission criteria

The HANA matrix startup pins HANA Express 2.00.088.00.20251110.1 and
Sap.Data.Hana.Net.v8.0 2.30.27. It uses a disposable public CI credential, bounded
startup, native .NET connection, identity/constraint DDL, persisted data, rollback
and catalog checks. Any startup or behavioral failure fails the job. Logs and
runner resource evidence are retained; cleanup runs independently of test success.
The prerequisite probe passed in [run 35766200488](https://github.com/dotnetprojects/Migrator.NET/actions/runs/35766200488). The standalone workflow is replaced by the mandatory Hana job in the complete database matrix; the probe project remains reproducible evidence.

Provider admission now requires the new matrix coverage for
imperative/fluent schema creation, constraint metadata, data, migration history,
restart/rollback, preview parity and explicit unsupported operations. Do not mark
a provider supported on the basis of SQL string tests or a skipped secret-gated job.

## Primary sources

- [SAP's official HANA Express image and installation requirements](https://hub.docker.com/r/saplabs/hanaexpress)
- [SAP Docker installation guide](https://developers.sap.com/tutorials/hxe-ua-install-using-docker)
- [Redshift Serverless setup](https://docs.aws.amazon.com/redshift/latest/gsg/)
- [Snowflake local testing framework scope](https://docs.snowflake.com/en/developer-guide/snowpark/python/testing-locally)
- [Db2 for IBM i platform](https://www.ibm.com/support/pages/db2-ibm-i)
- [FluentMigrator's current provider FAQ](https://fluentmigrator.github.io/intro/faq.html)

## HANA provider scope

The source provider is selected by `ProviderTypes.Hana` and accepts a caller-owned
`HanaConnection` or the SAP factory. The core remains free of SAP driver references.
The tests use Sap.Data.Hana.Net.v8.0 2.30.27 and HANA Express 2.00.088.00.20251110.1.

Supported operations include row/column table creation, explicit keys/checks/FKs,
column add/change/remove/rename, table rename/remove, ordinary and unique indexes,
parameterized data operations, schema/constraint/index metadata, versioned history,
and structured create-table/add-column preview. Names are case-preserving and accept
unquoted table or schema.table input; embedded identifier dots require explicit SQL.

Whole-session transactions and native locking are not advertised. The provider
retains HANA's default DDL autocommit behavior; data rollback does not prove DDL rollback.
Use explicit SQL for tenant administration, computed columns, specialized indexes,
collation configuration, and provider-specific data types without a mapped CLR type.
The provider rejects unsupported included/filtered/clustered indexes and semantic
collation requests instead of ignoring them.

[HANA constraints](https://help.sap.com/docs/SAP_HANA_PLATFORM/4fe29514fd584807ac9f2a04f6754767/209f7cf5751910149d9ce6b033d8ddce.html),
[referential constraints](https://help.sap.com/docs/SAP_HANA_PLATFORM/4fe29514fd584807ac9f2a04f6754767/20ccc0a175191014901b88e6bc175c44.html),
and [DDL autocommit](https://help.sap.com/docs/SAP_HANA_PLATFORM/4fe29514fd584807ac9f2a04f6754767/d538d11053bd4f3f847ec5ce817a3d4c.html)
are documented by SAP.
2 changes: 1 addition & 1 deletion docs/fluent-operation-coverage.json
Original file line number Diff line number Diff line change
Expand Up @@ -69,5 +69,5 @@
"MigrationApplied": "Explicit Context history/transaction APIs; not schema expressions",
"MigrationUnApplied": "Explicit Context history/transaction APIs; not schema expressions",
"IsMigrationApplied": "Explicit Context history/transaction APIs; not schema expressions",
"ExecuteSchemaBuilder": "Execute.WithProvider(p => p.ExecuteSchemaBuilder(legacyBuilder))"
"GetTableConstraints": "Schema.Table(table).ConstraintDefinitions()"
}
Loading
Loading