Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
34 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
e5f6b70
Document v13 APIs and qualify source examples and package identity
jogibear9988 Sep 22, 2026
253b032
Document HANA qualification, refresh v13 evidence and remove obsolete…
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
52 changes: 46 additions & 6 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 All @@ -58,12 +58,14 @@ Building the `.slnx` solution requires an SDK that understands that format, such

## Quick start

This example targets **unreleased v13 source**. Clone/check out the upgrade branch before running these commands from the repository root. For published 12.1, follow its version-specific API; see the [migration guide](docs/migration-guide-12.1-to-13.md).

### 1. Create a migration host

```sh
dotnet new console -n MigrationDemo -f net9.0
cd MigrationDemo
dotnet add package DotNetProjects.Migrator
dotnet add reference ../src/Migrator/DotNetProjects.Migrator.csproj
dotnet add package Microsoft.Data.Sqlite --version 9.0.7
```

Expand All @@ -81,9 +83,9 @@ public class CreateUsers : Migration
public override void Up()
{
Database.AddTable("Users",
new Column("Id", DbType.Int32, ColumnProperty.NotNull),
new Column("Name", DbType.String, 255));
Database.AddPrimaryKey("PK_Users", "Users", "Id");
new Column("Id", DbType.Int32) { IsNullable = false },
new Column("Name", DbType.String, 255),
new PrimaryKeyConstraint("PK_Users", "Id"));
}

public override void Down()
Expand Down Expand Up @@ -187,6 +189,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 All @@ -212,7 +224,7 @@ public override void Down()
}
```

Provider implementations determine which operations are available and how they map to SQL. Use `Database.ExecuteNonQuery(...)` for custom SQL and keep dialect-specific statements explicit. The source also includes a [schema builder API](src/Migrator/Framework/SchemaBuilder/SchemaBuilder.cs).
Provider implementations determine which operations are available and how they map to SQL. Use `Database.ExecuteNonQuery(...)` for custom SQL and keep dialect-specific statements explicit. The source also includes the [MigrationBuilder fluent API](src/Migrator/Framework/Fluent/MigrationBuilder.cs).

## Database providers

Expand All @@ -230,6 +242,7 @@ The [provider factory](src/Migrator/ProviderFactory.cs) contains these database
| IBM Informix | `IBM_Informix` |
| Firebird | `Firebird` |
| Ingres | `Ingres` |
| SAP HANA (v13 source) | `Hana` |
| Sybase | `Sybase` |

This is an inventory of dialects present in source, **not a guarantee that every server version, driver or operation is supported**. Some entries are legacy variants. Verify the combination you deploy against the [provider implementations](src/Migrator/Providers/Impl) and [provider tests](src/Migrator.Tests/Providers).
Expand Down Expand Up @@ -305,3 +318,30 @@ 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.


### SQL expressions and collations in v13

```csharp
new Column("Id", DbType.String, 27) { DefaultValue = RawSql.Insert("ksuid_new()") };
builder.Create.Table("Events").WithColumn("Id").AsString(27)
.WithDefaultValue(RawSql.Insert("ksuid_new()"));

new Column("Name", DbType.String, 100) { Collation = Collation.CaseInsensitive };
builder.Create.Table("Names").WithColumn("Name").AsString(100)
.WithCollation(Collation.CaseInsensitive);
```

SQL expressions are trusted migration code and must exist on the target database.
Ordinary string defaults remain quoted literals. Semantic collations have explicit
provider limits; SQLite's `AsciiIgnoreCase` never substitutes for Unicode folding.
Use `Collation.Named("provider_name")` for a specific language or installed collation.
See the [mapping and migration guide](docs/migration-guide-12.1-to-13.md#explicit-sql-defaults-and-semantic-collations).

Additional engines are admitted only with passing real-database CI.
[SAP HANA provider scope, CI evidence and deferred engine requirements](docs/additional-database-qualification.md)
cover the current FluentMigrator gaps without claiming untested support.
6 changes: 3 additions & 3 deletions docs/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ Open http://localhost:8766. Content and navigation work without JavaScript. Copy

## Publish on GitHub Pages

1. In the repository's **Settings → Pages → Build and deployment**, set **Source** to **GitHub Actions**.
1. In the repository's **Settings → Pages → Build and deployment**, set **Source** to **GitHub Actions**.
2. Merge the site and `.github/workflows/pages.yml` into `master`.
3. The workflow publishes only `docs/`. After enabling Pages, you can also run **Deploy homepage to GitHub Pages** manually on `master`.

Expand All @@ -24,6 +24,6 @@ All site assets use relative URLs, so the repository subpath works without a cus

## Keep the comparison accurate

The comparison distinguishes source capabilities from guarantees about released packages or database compatibility. Update the review date and source links together when reviewing it. Avoid equating transaction rollback with reversing completed migrations, treating a provider enum as a support guarantee, or treating scoped history as a migration discovery filter.
The comparison distinguishes source capabilities from guarantees about released packages or database compatibility. Update the review date and source links together when reviewing it. Avoid equating transaction rollback with reversing completed migrations, treating a provider enum as a support guarantee, or assuming scopes isolate physical tables. The v13 runner filters explicitly scoped migrations and lets unscoped migrations inherit its effective scope.

The quick start targets the current source's .NET 9 API. Check the selected NuGet release's target frameworks. The SQLite driver version matches the repository test dependency. Validate authoring and runner snippets together when changing them.
The quick start targets the unreleased v13 source's .NET 9 API and references the source project. Check the selected NuGet release's target frameworks. The SQLite driver version matches the repository test dependency. Validate authoring and runner snippets together when changing them.
Loading
Loading