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
15 changes: 15 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -103,6 +103,21 @@ jobs:
make _test_dotnet DOTNET_TEST_PROJECTS="DataProvider/Nimblesite.DataProvider.Tests
DataProvider/Nimblesite.DataProvider.Example.Tests"

# Implements [MIG-AOT-CI]: the release ships DataProviderMigrate as a Native AOT
# binary, so every PR proves that publish and the native smoke tests still pass.
- name: Install Linux AOT toolchain
run: sudo apt-get update && sudo apt-get install -y clang zlib1g-dev

- name: Native AOT publish + smoke migrate (DataProviderMigrate)
run: make aot RID=linux-x64

- name: Native AOT smoke tests (DataProviderMigrate)
env:
DATAPROVIDERMIGRATE_AOT_BIN: ${{ github.workspace }}/Migration/DataProviderMigrate/bin/Release/net9.0/linux-x64/publish/DataProviderMigrate
run: >-
dotnet test Migration/Nimblesite.DataProvider.Migration.Tests
--filter "FullyQualifiedName~NativeAotMigrateSmokeTests"

- name: Upload test results
uses: actions/upload-artifact@v4
if: always()
Expand Down
17 changes: 6 additions & 11 deletions DataProvider/Nimblesite.DataProvider.SQLite/SqliteCodeGenerator.cs
Original file line number Diff line number Diff line change
Expand Up @@ -16,11 +16,6 @@ namespace Nimblesite.DataProvider.SQLite;
[Generator]
public sealed class SqliteCodeGenerator : IIncrementalGenerator
{
private static readonly JsonSerializerOptions JsonOptions = new()
{
PropertyNameCaseInsensitive = true,
};

/// <summary>
/// Generates C# source for a SQL file using real database metadata.
/// Delegates to <see cref="SqlAntlrCodeGenerator.GenerateCodeWithMetadata"/>
Expand Down Expand Up @@ -260,9 +255,9 @@ ImmutableArray<AdditionalText> GroupingFiles
{
try
{
config = JsonSerializer.Deserialize<SourceGeneratorDataProviderConfiguration>(
configContent!,
JsonOptions
config = JsonSerializer.Deserialize(
configContent,
SqliteGeneratorJsonContext.Default.SourceGeneratorDataProviderConfiguration
);
}
catch (JsonException ex)
Expand Down Expand Up @@ -495,9 +490,9 @@ as Result<IReadOnlyList<DatabaseColumn>, SqlError>.Error<
{
try
{
groupingConfig = JsonSerializer.Deserialize<GroupingConfig>(
groupingText!,
JsonOptions
groupingConfig = JsonSerializer.Deserialize(
groupingText,
SqliteGeneratorJsonContext.Default.GroupingConfig
);
}
catch (JsonException ex)
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
using System.Text.Json.Serialization;

namespace Nimblesite.DataProvider.SQLite;

/// <summary>
/// Source-generated JSON metadata for the SQLite generator's configuration files.
/// Implements [MIG-AOT-JSON]: the Native AOT migrate binary compiles this assembly
/// for its SQLite parser, so its JSON reads must be reflection-free.
/// </summary>
[JsonSourceGenerationOptions(PropertyNameCaseInsensitive = true)]
[JsonSerializable(typeof(SourceGeneratorDataProviderConfiguration))]
[JsonSerializable(typeof(GroupingConfig))]
internal sealed partial class SqliteGeneratorJsonContext : JsonSerializerContext { }
2 changes: 1 addition & 1 deletion Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -304,7 +304,7 @@ _aot_dotnet:
ifeq ($(OS),Windows_NT)
AOT_SMOKE_DB := $(TEMP)\dataprovider_aot_smoke.db
else
AOT_SMOKE_DB := $(shell printf '%s' "$${TMPDIR:-/tmp}")dataprovider_aot_smoke.db
AOT_SMOKE_DB := $(patsubst %/,%,$(shell printf '%s' "$${TMPDIR:-/tmp}"))/dataprovider_aot_smoke.db
endif

# --- RUST (LQL LSP) ---
Expand Down
6 changes: 6 additions & 0 deletions Migration/DataProviderMigrate/DataProviderMigrate.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,12 @@
<ProjectReference Include="..\Nimblesite.DataProvider.Migration.Core\Nimblesite.DataProvider.Migration.Core.csproj" />
<ProjectReference Include="..\Nimblesite.DataProvider.Migration.SQLite\Nimblesite.DataProvider.Migration.SQLite.csproj" />
<ProjectReference Include="..\Nimblesite.DataProvider.Migration.Postgres\Nimblesite.DataProvider.Migration.Postgres.csproj" />
</ItemGroup>

<!-- Implements [MIG-AOT-SQLSERVER]. Microsoft.Data.SqlClient is not trim or Native
AOT compatible, so only the managed `dotnet tool` build carries the SQL Server
provider; the native binary reports it as unavailable. -->
<ItemGroup Condition="'$(PublishAot)' != 'true'">
<ProjectReference Include="..\Nimblesite.DataProvider.Migration.SqlServer\Nimblesite.DataProvider.Migration.SqlServer.csproj" />
</ItemGroup>
</Project>
61 changes: 61 additions & 0 deletions Migration/DataProviderMigrate/Program.SqlServer.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
#if !AOT
using Microsoft.Data.SqlClient;
using Nimblesite.DataProvider.Migration.Core;
using Nimblesite.DataProvider.Migration.SqlServer;
#endif

namespace DataProviderMigrate;

// Implements [MIG-AOT-SQLSERVER]. Microsoft.Data.SqlClient is not trim or Native AOT
// compatible, so the native binary leaves out the SQL Server provider. SQL Server
// migrations remain available in the managed `dotnet tool` and the NuGet packages.
public static partial class Program
{
#if AOT
private static int ShowSqlServerUnavailable()
{
Console.WriteLine(
"Error: the 'sqlserver' provider is not available in the native (AOT) build.\n"
+ "Microsoft.Data.SqlClient does not support Native AOT.\n"
+ "Use the managed dotnet tool 'DataProviderMigrate migrate --provider sqlserver ...' instead."
);
return 1;
}
#else
// Implements [MIG-SQLSERVER].
private static int MigrateSqlServerDatabase(
SchemaDefinition schema,
string connectionString,
bool allowDestructive,
MigratePhase phase
)
{
try
{
using var connection = new SqlConnection(connectionString);
connection.Open();
Console.WriteLine("Connected to SQL Server database");

return ApplyDiff(
schema,
allowDestructive,
phase,
() => SqlServerSchemaInspector.Inspect(connection),
ops =>
MigrationRunner.Apply(
connection,
ops,
SqlServerDdlGenerator.Generate,
new MigrationOptions { AllowDestructive = allowDestructive }
),
(_, desired) => SqlServerSchemaNormalizer.Normalize(desired)
);
}
catch (Exception ex)
{
Console.WriteLine($"Error: SQL Server connection/migration failed: {ex}");
return 1;
}
}
#endif
}
42 changes: 4 additions & 38 deletions Migration/DataProviderMigrate/Program.cs
Original file line number Diff line number Diff line change
@@ -1,10 +1,8 @@
using System.Collections.Immutable;
using Microsoft.Data.SqlClient;
using Microsoft.Data.Sqlite;
using Nimblesite.DataProvider.Migration.Core;
using Nimblesite.DataProvider.Migration.Postgres;
using Nimblesite.DataProvider.Migration.SQLite;
using Nimblesite.DataProvider.Migration.SqlServer;
using Npgsql;
using SchemaIntegrityResultError = Outcome.Result<
System.Collections.Immutable.ImmutableArray<string>,
Expand Down Expand Up @@ -142,12 +140,16 @@ private static int ExecuteMigration(MigrateParseResult.Success args)
args.AllowDestructive,
args.Phase
),
#if AOT
"sqlserver" or "mssql" => ShowSqlServerUnavailable(),
#else
"sqlserver" or "mssql" => MigrateSqlServerDatabase(
schema,
args.OutputPath,
args.AllowDestructive,
args.Phase
),
#endif
_ => ShowProviderError(args.Provider),
};
}
Expand Down Expand Up @@ -246,42 +248,6 @@ MigratePhase phase
}
}

// Implements [MIG-SQLSERVER].
private static int MigrateSqlServerDatabase(
SchemaDefinition schema,
string connectionString,
bool allowDestructive,
MigratePhase phase
)
{
try
{
using var connection = new SqlConnection(connectionString);
connection.Open();
Console.WriteLine("Connected to SQL Server database");

return ApplyDiff(
schema,
allowDestructive,
phase,
() => SqlServerSchemaInspector.Inspect(connection),
ops =>
MigrationRunner.Apply(
connection,
ops,
SqlServerDdlGenerator.Generate,
new MigrationOptions { AllowDestructive = allowDestructive }
),
(_, desired) => SqlServerSchemaNormalizer.Normalize(desired)
);
}
catch (Exception ex)
{
Console.WriteLine($"Error: SQL Server connection/migration failed: {ex}");
return 1;
}
}

/// <summary>
/// Inspect → diff → filter → apply pipeline shared between SQLite and
/// Postgres. <paramref name="phase"/> filters operations so callers can
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,26 @@ public void NativeBinary_ExportCommand_ReportsUnsupported()
Assert.Contains("not available in the native", result.Output, StringComparison.Ordinal);
}

[SkippableFact]
public void NativeBinary_SqlServerProvider_ReportsUnsupported()
{
var bin = RequireNativeBinary();
var (schemaPath, _) = WriteFixtures();

// Implements [MIG-AOT-SQLSERVER]: the SQL Server provider is compiled out of the native build.
var result = RunRaw(
bin,
["migrate", "--schema", schemaPath, "--output", "Server=x", "--provider", "sqlserver"]
);

Assert.True(result.ExitCode != 0, userMessage: result.Output);
Assert.Contains(
"'sqlserver' provider is not available in the native",
result.Output,
StringComparison.Ordinal
);
}

private static string RequireNativeBinary()
{
var bin = NativeBinaryPath();
Expand Down
11 changes: 11 additions & 0 deletions docs/plans/dataprovider-migrate-aot.md
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,17 @@ binary (cannot load arbitrary external managed DLLs). Decision required:
- Option B: drop `export` from the CLI entirely.
Default to **A** unless the user says otherwise.

### [MIG-AOT-SQLSERVER] Microsoft.Data.SqlClient is not AOT compatible
`Microsoft.Data.SqlClient` (6.0.1 and 7.1.1) produces `IL2104`/`IL3053` trim and AOT
errors, and pulls in `System.Data` XML serialization and `ConfigurationManager`.
- **DONE.** As with `[MIG-AOT-EXPORT]`, the native binary compiles out the SQL Server
provider: `DataProviderMigrate.csproj` references `Migration.SqlServer` only when
`PublishAot` is not set, and `--provider sqlserver` reports that the provider is
unavailable in the native build. SQL Server migrations remain in the managed
`dotnet tool` and the NuGet packages. `NativeAotMigrateSmokeTests` asserts the message.
- `[MIG-AOT-CI]`: `ci.yml` runs `make aot` and the native smoke tests on every PR, so
an AOT regression fails the PR instead of the release.

### [MIG-AOT-NPGSQL] Npgsql trim/AOT cleanliness
Npgsql 9 is largely AOT-friendly but may emit trim warnings for type mapping.
Capture and resolve any `IL2xxx`/`IL3xxx` from Npgsql; add a trim feature switch or
Expand Down
Loading