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
7 changes: 7 additions & 0 deletions Directory.Build.props
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,13 @@
<HighEntropyVA>true</HighEntropyVA>
<RootDir>$(MSBuildThisFileDirectory)</RootDir>

<!-- STS2's non-production SQLite E2E subject is compiled with a different
dependency graph. Keep its outputs isolated so solution builds cannot
overwrite (or be overwritten by) the production ServiceLayer output. -->
<BaseOutputPath Condition="'$(Sts2IsolatedSqliteBuild)' == 'true'">bin\sts2-sqlite\</BaseOutputPath>
<BaseIntermediateOutputPath Condition="'$(Sts2IsolatedSqliteBuild)' == 'true'">obj\sts2-sqlite\</BaseIntermediateOutputPath>
<DefaultItemExcludes Condition="'$(Sts2IsolatedSqliteBuild)' == 'true'">$(DefaultItemExcludes);bin\**;obj\**</DefaultItemExcludes>

<SqlToolsServiceDotNetVersion Condition="'$(SqlToolsServiceDotNetVersion)' == ''">net10.0</SqlToolsServiceDotNetVersion>
<!-- SqlCore is limited by the Sql Fabric workload version -->
<SqlCoreDotNetVersion Condition="'$(SqlCoreDotNetVersion)' == ''">net8.0</SqlCoreDotNetVersion>
Expand Down
4 changes: 4 additions & 0 deletions docs/sts2/CLIENT.md
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,10 @@ table and `docs/sts2/TRACE-SCHEMA.md` for the envelope/trace format.
This is a copy-pasteable reference; it is documented rather than run in CI (no Node
toolchain is assumed in this repo). The wire shapes match `docs/sts2/CONTRACT.md`.

The sample uses the non-production SQLite test driver. Production/default publishes
contain only the SQL Client driver; build locally with
`-p:IncludeSts2SqliteDriver=true` to make the `sqlite` driver available.

```typescript
import { spawn } from "node:child_process";
import {
Expand Down
6 changes: 3 additions & 3 deletions docs/sts2/COMPONENTS.md
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
<!-- GENERATED by Microsoft.SqlTools.Sts2.Testing.GeneratedDocs; do not edit by hand. Regenerate: ./scripts/update-sts2-docs.ps1 -->
# STS2 Components

Roles come from each project's COMPONENT.md; references are read from the csproj files on disk. The allowed matrix is SPEC §4; DependencyMatrixTests enforces it (I11).
Roles come from each project's COMPONENT.md; references are read from the csproj files on disk. Conditional references include their MSBuild condition. The allowed matrix is SPEC §4; DependencyMatrixTests enforces it (I11).

## Microsoft.SqlTools.Sts2.Abstractions

Expand All @@ -14,7 +14,7 @@ Driver port (IDbDriver/IDbSession), clock and id abstractions.

Composition root invoked by legacy Program.cs; owns --enable-sts2 / STS_ENABLE_STS2 activation and process wiring.

- Project references: Microsoft.SqlTools.Sts2.Contracts, Microsoft.SqlTools.Sts2.Drivers.SqlClient, Microsoft.SqlTools.Sts2.Drivers.Sqlite, Microsoft.SqlTools.Sts2.Hosting, Microsoft.SqlTools.Sts2.Multiplexer, Microsoft.SqlTools.Sts2.Runtime
- Project references: Microsoft.SqlTools.Sts2.Contracts, Microsoft.SqlTools.Sts2.Drivers.SqlClient, Microsoft.SqlTools.Sts2.Drivers.Sqlite (condition: `'$(IncludeSts2SqliteDriver)' == 'true'`), Microsoft.SqlTools.Sts2.Hosting, Microsoft.SqlTools.Sts2.Multiplexer, Microsoft.SqlTools.Sts2.Runtime
- Package references: none (BCL only)

## Microsoft.SqlTools.Sts2.Contracts
Expand All @@ -40,7 +40,7 @@ Production driver adapter over Microsoft.Data.SqlClient.

## Microsoft.SqlTools.Sts2.Drivers.Sqlite

Portable real-I/O driver adapter over Microsoft.Data.Sqlite.
Non-production real-I/O test driver adapter over Microsoft.Data.Sqlite.

- Project references: Microsoft.SqlTools.Sts2.Abstractions, Microsoft.SqlTools.Sts2.Contracts
- Package references: Microsoft.Data.Sqlite, SQLitePCLRaw.lib.e_sqlite3
Expand Down
4 changes: 3 additions & 1 deletion src/sts2/Microsoft.SqlTools.Sts2.Bootstrap/COMPONENT.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,10 @@

**Role:** Composition root invoked by legacy Program.cs; owns --enable-sts2 / STS_ENABLE_STS2 activation and process wiring.

**Allowed dependencies:** Hosting, Runtime, Multiplexer, Drivers.SqlClient, Drivers.Sqlite, Contracts
**Allowed dependencies:** Hosting, Runtime, Multiplexer, Drivers.SqlClient, Contracts; Drivers.Sqlite only when `IncludeSts2SqliteDriver=true`

**Forbidden:** legacy namespaces

See docs/sts2/SPEC.md SS4 for the authoritative dependency matrix.

The default and production publish graph excludes Drivers.Sqlite. Local and E2E builds may opt in with `-p:IncludeSts2SqliteDriver=true`.
Original file line number Diff line number Diff line change
@@ -1,13 +1,18 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<Description>STS2 composition root called by legacy Program.cs. Owns activation flags and process-level wiring.</Description>
<!-- SQLite is a non-production test driver. It must not enter the default
ServiceLayer publish graph; local/test builds opt in explicitly. -->
<IncludeSts2SqliteDriver Condition="'$(IncludeSts2SqliteDriver)' == ''">false</IncludeSts2SqliteDriver>
<DefineConstants Condition="'$(IncludeSts2SqliteDriver)' == 'true'">$(DefineConstants);STS2_INCLUDE_SQLITE_DRIVER</DefineConstants>
</PropertyGroup>
<ItemGroup>
<ProjectReference Include="..\Microsoft.SqlTools.Sts2.Hosting\Microsoft.SqlTools.Sts2.Hosting.csproj" />
<ProjectReference Include="..\Microsoft.SqlTools.Sts2.Runtime\Microsoft.SqlTools.Sts2.Runtime.csproj" />
<ProjectReference Include="..\Microsoft.SqlTools.Sts2.Multiplexer\Microsoft.SqlTools.Sts2.Multiplexer.csproj" />
<ProjectReference Include="..\Microsoft.SqlTools.Sts2.Drivers.SqlClient\Microsoft.SqlTools.Sts2.Drivers.SqlClient.csproj" />
<ProjectReference Include="..\Microsoft.SqlTools.Sts2.Drivers.Sqlite\Microsoft.SqlTools.Sts2.Drivers.Sqlite.csproj" />
<ProjectReference Include="..\Microsoft.SqlTools.Sts2.Drivers.Sqlite\Microsoft.SqlTools.Sts2.Drivers.Sqlite.csproj"
Condition="'$(IncludeSts2SqliteDriver)' == 'true'" />
<ProjectReference Include="..\Microsoft.SqlTools.Sts2.Contracts\Microsoft.SqlTools.Sts2.Contracts.csproj" />
</ItemGroup>
<ItemGroup>
Expand Down
22 changes: 17 additions & 5 deletions src/sts2/Microsoft.SqlTools.Sts2.Bootstrap/Sts2Bootstrap.cs
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,9 @@
using System.Text;
using System.Threading.Tasks;
using Microsoft.SqlTools.Sts2.Abstractions;
#if STS2_INCLUDE_SQLITE_DRIVER
using Microsoft.SqlTools.Sts2.Drivers.Sqlite;
#endif
using Microsoft.SqlTools.Sts2.Hosting;
using Microsoft.SqlTools.Sts2.Multiplexer;

Expand Down Expand Up @@ -118,11 +120,7 @@ public static Sts2BootstrapHandle TryStart(string[] args, string? logFilePath)
// never conflate this run with an earlier one in the shared log directory.
JournalDirectory = Path.Combine(logDirectory, "sts2", runId),
ServiceVersion = typeof(Sts2Bootstrap).Assembly.GetName().Version?.ToString() ?? "0.0.0.0",
Drivers = new Dictionary<string, IDbDriver>
{
["sqlclient"] = new Drivers.SqlClient.SqlClientDriver(), // production (M5)
["sqlite"] = new SqliteDriver(), // portable (M4)
},
Drivers = CreateDrivers(),
CommandLine = SanitizeCommandLine(args),
},
multiplexer.Sts2OutputWriter,
Expand All @@ -138,6 +136,20 @@ public static Sts2BootstrapHandle TryStart(string[] args, string? logFilePath)
return new Sts2BootstrapHandle(multiplexer.LegacyInput, multiplexer.LegacyOutput, multiplexer, session, diagnosticsLog);
}

private static Dictionary<string, IDbDriver> CreateDrivers()
{
var drivers = new Dictionary<string, IDbDriver>
{
["sqlclient"] = new Drivers.SqlClient.SqlClientDriver(), // production (M5)
};
#if STS2_INCLUDE_SQLITE_DRIVER
// Non-production real-I/O test driver. The conditional project reference
// keeps Microsoft.Data.Sqlite and every native SQLite RID out of release builds.
drivers["sqlite"] = new SqliteDriver();
#endif
return drivers;
}

/// <summary>
/// Records the command line for the journal manifest by SHAPE, not by blocklist
/// (R033): flag NAMES are kept verbatim (they aren't secret and are useful forensics),
Expand Down
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
# Microsoft.SqlTools.Sts2.Drivers.Sqlite

**Role:** Portable real-I/O driver adapter over Microsoft.Data.Sqlite.
**Role:** Non-production real-I/O test driver adapter over Microsoft.Data.Sqlite.

**Allowed dependencies:** Abstractions, Contracts, Microsoft.Data.Sqlite, SQLitePCLRaw.lib.e_sqlite3

**Forbidden:** Core, Runtime, Hosting, legacy namespaces

The enforced dependency matrix is in [DependencyMatrixTests](../../../test/sts2/Microsoft.SqlTools.Sts2.UnitTests/Architecture/DependencyMatrixTests.cs) (I11); see the generated [component graph](../../../docs/sts2/COMPONENTS.md) for the references on disk.

This project is tested directly. It enters the ServiceLayer graph only when a local or test build sets `IncludeSts2SqliteDriver=true`.
Original file line number Diff line number Diff line change
@@ -1,14 +1,14 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<Description>STS2 portable real-I/O driver adapter over Microsoft.Data.Sqlite. Quick-CI honesty check for the port.</Description>
<Description>STS2 non-production real-I/O test driver over Microsoft.Data.Sqlite. Quick-CI honesty check for the port.</Description>
</PropertyGroup>
<ItemGroup>
<ProjectReference Include="..\Microsoft.SqlTools.Sts2.Abstractions\Microsoft.SqlTools.Sts2.Abstractions.csproj" />
<ProjectReference Include="..\Microsoft.SqlTools.Sts2.Contracts\Microsoft.SqlTools.Sts2.Contracts.csproj" />
<PackageReference Include="Microsoft.Data.Sqlite" />
<!-- Force the patched SQLitePCLRaw native lib over the vulnerable 2.1.11 that
Microsoft.Data.Sqlite pins transitively (CVE-2025-6965). This override propagates to
every consumer (Bootstrap -> legacy ServiceLayer -> tests). Version in Packages.props. -->
every opted-in consumer and the direct test references. Version in Packages.props. -->
<PackageReference Include="SQLitePCLRaw.lib.e_sqlite3" />
</ItemGroup>
</Project>
11 changes: 9 additions & 2 deletions src/sts2/Microsoft.SqlTools.Sts2.Testing/GeneratedDocs.cs
Original file line number Diff line number Diff line change
Expand Up @@ -230,7 +230,7 @@ public static string Components(string sts2SourceDir)
{
var sb = new StringBuilder(Header);
sb.Append("# STS2 Components\n\n");
sb.Append("Roles come from each project's COMPONENT.md; references are read from the csproj files on disk. The allowed matrix is SPEC §4; DependencyMatrixTests enforces it (I11).\n\n");
sb.Append("Roles come from each project's COMPONENT.md; references are read from the csproj files on disk. Conditional references include their MSBuild condition. The allowed matrix is SPEC §4; DependencyMatrixTests enforces it (I11).\n\n");

foreach (string projectDir in Directory.EnumerateDirectories(sts2SourceDir).Order(StringComparer.Ordinal))
{
Expand All @@ -252,7 +252,14 @@ public static string Components(string sts2SourceDir)

XDocument csproj = XDocument.Load(csprojPath);
string[] projectRefs = csproj.Descendants("ProjectReference")
.Select(r => Path.GetFileNameWithoutExtension(r.Attribute("Include")!.Value.Replace('\\', '/')))
.Select(r =>
{
string name = Path.GetFileNameWithoutExtension(r.Attribute("Include")!.Value.Replace('\\', '/'));
string? condition = r.Attribute("Condition")?.Value;
return string.IsNullOrWhiteSpace(condition)
? name
: $"{name} (condition: `{condition}`)";
})
.Order(StringComparer.Ordinal).ToArray();
string[] packageRefs = csproj.Descendants("PackageReference")
.Select(r => (r.Attribute("Include") ?? r.Attribute("Update"))!.Value)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,10 +10,31 @@
<PackageReference Include="xunit" />
<PackageReference Include="xunit.runner.visualstudio" />
</ItemGroup>
<PropertyGroup>
<!-- The SQLite-specific test needs a second subject with an opt-in dependency
graph. NuGet restore does not propagate per-ProjectReference properties, so
build that subject explicitly into isolated bin/obj directories. -->
<Sts2E2EServiceProject>$(MSBuildThisFileDirectory)..\..\..\src\Microsoft.SqlTools.ServiceLayer\Microsoft.SqlTools.ServiceLayer.csproj</Sts2E2EServiceProject>
<Sts2E2EServiceProperties>Configuration=$(Configuration);IncludeSts2SqliteDriver=true;Sts2IsolatedSqliteBuild=true</Sts2E2EServiceProperties>
</PropertyGroup>
<ItemGroup>
<!-- The spawned executable is the subject of these tests. Force the exact current
configuration/TFM to build without taking a compile-time assembly dependency. -->
<ProjectReference Include="..\..\..\src\Microsoft.SqlTools.ServiceLayer\Microsoft.SqlTools.ServiceLayer.csproj"
<!-- All other tests spawn the default production graph. Build the subject without
taking a compile-time assembly dependency. -->
<ProjectReference Include="$(Sts2E2EServiceProject)"
ReferenceOutputAssembly="false" />
</ItemGroup>
<Target Name="BuildSts2E2EService"
AfterTargets="Build"
Condition="'$(DesignTimeBuild)' != 'true'">
<MSBuild Projects="$(Sts2E2EServiceProject)"
Targets="Restore"
Properties="$(Sts2E2EServiceProperties)"
RemoveProperties="TargetFramework"
BuildInParallel="false" />
<MSBuild Projects="$(Sts2E2EServiceProject)"
Targets="Build"
Properties="$(Sts2E2EServiceProperties)"
RemoveProperties="TargetFramework"
BuildInParallel="false" />
</Target>
</Project>
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ private ServiceProcessClient(Process process)
/// <summary>Queue of notifications received from the service, by method name.</summary>
public ConcurrentQueue<(string Method, JsonElement Params)> Notifications { get; } = new();

public static string LocateServiceDll()
public static string LocateServiceDll(bool includeSqlite = false)
{
string? dir = AppContext.BaseDirectory;
while (dir != null && !File.Exists(Path.Combine(dir, "sqltoolsservice.sln")))
Expand All @@ -60,11 +60,17 @@ public static string LocateServiceDll()
DirectoryInfo testOutputDirectory = new(AppContext.BaseDirectory);
string currentTfm = testOutputDirectory.Name;
string currentConfiguration = testOutputDirectory.Parent?.Name ?? "Debug";
string candidate = Path.Combine(
string serviceOutputRoot = Path.Combine(
dir,
"src",
"Microsoft.SqlTools.ServiceLayer",
"bin",
"bin");
if (includeSqlite)
{
serviceOutputRoot = Path.Combine(serviceOutputRoot, "sts2-sqlite");
}
string candidate = Path.Combine(
serviceOutputRoot,
currentConfiguration,
currentTfm,
"MicrosoftSqlToolsServiceLayer.dll");
Expand All @@ -77,10 +83,10 @@ public static string LocateServiceDll()
". The E2E project reference must build ServiceLayer for the current configuration and TFM.");
}

public static ServiceProcessClient Start(bool enableSts2, string logDirectory)
public static ServiceProcessClient Start(bool enableSts2, string logDirectory, bool includeSqlite = false)
{
Directory.CreateDirectory(logDirectory);
string args = "\"" + LocateServiceDll() + "\" --log-file \"" + Path.Combine(logDirectory, "sqltools.log") + "\"";
string args = "\"" + LocateServiceDll(includeSqlite) + "\" --log-file \"" + Path.Combine(logDirectory, "sqltools.log") + "\"";
if (enableSts2)
{
args += " --enable-sts2";
Expand Down
12 changes: 11 additions & 1 deletion test/sts2/Microsoft.SqlTools.Sts2.E2ETests/StdioE2ETests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,13 @@ public void SpawnedSubjectMatchesCurrentTestBuild()
Assert.True(File.Exists(serviceDll));
}

[Fact]
public void DefaultSubjectExcludesSqliteAssets()
{
string serviceDirectory = Path.GetDirectoryName(ServiceProcessClient.LocateServiceDll())!;
Assert.Empty(Directory.EnumerateFiles(serviceDirectory, "*sqlite*", SearchOption.AllDirectories));
Comment thread
kburtram marked this conversation as resolved.
}

[Fact]
public async Task DisabledMode_V1VersionWorks_AndNoSts2ArtifactsAreCreated()
{
Expand Down Expand Up @@ -119,7 +126,10 @@ public async Task EnabledMode_InitializeWorksAndJournalIsWritten()
[Fact]
public async Task EnabledMode_SqliteQueryExercisesPagedLifecycleOverRealStdio()
{
await using var client = ServiceProcessClient.Start(enableSts2: true, logDirectory: logDirectory);
await using var client = ServiceProcessClient.Start(
enableSts2: true,
logDirectory: logDirectory,
includeSqlite: true);
await client.RequestAsync("v2/initialize", new { clientName = "e2e" }, TestTimeout);

JsonElement open = await client.RequestAsync("v2/connection.open",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -97,6 +97,31 @@ public void AllMatrixProjectsExistOnDisk()
}
}

[Fact]
public void SqliteIsOptInAtProductCompositionRoot()
{
string bootstrapProjectPath = Path.Combine(
RepoRoot.Sts2SourceDir,
"Microsoft.SqlTools.Sts2.Bootstrap",
"Microsoft.SqlTools.Sts2.Bootstrap.csproj");
XDocument bootstrapProject = XDocument.Load(bootstrapProjectPath);

XElement includeProperty = Assert.Single(
bootstrapProject.Descendants("IncludeSts2SqliteDriver"));
Assert.Equal("false", includeProperty.Value);
Assert.Contains("IncludeSts2SqliteDriver", includeProperty.Attribute("Condition")?.Value);

XElement sqliteReference = Assert.Single(
bootstrapProject.Descendants("ProjectReference").Where(reference =>
string.Equals(
Path.GetFileNameWithoutExtension(reference.Attribute("Include")!.Value.Replace('\\', '/')),
"Microsoft.SqlTools.Sts2.Drivers.Sqlite",
StringComparison.OrdinalIgnoreCase)));
string? condition = sqliteReference.Attribute("Condition")?.Value;
Assert.Contains("IncludeSts2SqliteDriver", condition);
Assert.Contains("true", condition);
Comment thread
kburtram marked this conversation as resolved.
}

[Fact]
public void Sts2SourcesNeverUseLegacyServiceLayerNamespaces()
{
Expand Down