diff --git a/.cargo/config.toml b/.cargo/config.toml index b809288aef..a9ed99c0fe 100644 --- a/.cargo/config.toml +++ b/.cargo/config.toml @@ -23,3 +23,11 @@ linker = "rust-lld" # Large Rust projects in debug mode overflow the 1 MiB stack when DB query # paths pull in deep serde/driver call stacks. This embeds the size in the PE. rustflags = ["-C", "link-arg=/STACK:8388608"] + +# The legacy Windows 7 / Server 2012 R2 target keeps the same 8 MiB main-thread +# stack. CI builds it with an explicit RUSTFLAGS env (static CRT), which +# replaces this table, so the same link arg is repeated in those workflows and +# audited by .github/scripts/assert-win7-pe-compat.ps1. Without it the legacy PE +# keeps the 1 MiB default and the app aborts with 0xC00000FD ("DBX 已停止工作"). +[target.x86_64-win7-windows-msvc] +rustflags = ["-C", "link-arg=/STACK:8388608"] diff --git a/.github/scripts/assert-win7-pe-compat.ps1 b/.github/scripts/assert-win7-pe-compat.ps1 index 84438f9f1d..e715878c74 100644 --- a/.github/scripts/assert-win7-pe-compat.ps1 +++ b/.github/scripts/assert-win7-pe-compat.ps1 @@ -69,3 +69,38 @@ if ($violations.Count -gt 0) { } Write-Host "Windows 7 PE import audit passed: $BinaryPath" + +# Main-thread stack reserve audit. The app needs the 8 MiB stack that +# .cargo/config.toml applies to the standard Windows target; the legacy target is +# built with an explicit env RUSTFLAGS (static CRT), which replaces the config +# rustflags, so the workflow repeats `-C link-arg=/STACK:8388608`. Without it the +# PE keeps the 1 MiB linker default and deep connect/serde call stacks abort with +# 0xC00000FD ("DBX 已停止工作"), which users reported on Windows 7 / Server 2012 R2. +$minimumStackReserve = 8 * 1024 * 1024 +$image = [System.IO.File]::ReadAllBytes($BinaryPath) +if ($image.Length -lt 0x100) { + throw "Windows 7 PE audit target is truncated: $BinaryPath" +} +$peOffset = [System.BitConverter]::ToInt32($image, 0x3C) +if ($peOffset -le 0 -or $peOffset + 24 -gt $image.Length) { + throw "Invalid PE header offset in ${BinaryPath}" +} +$peSignature = [System.BitConverter]::ToUInt32($image, $peOffset) +if ($peSignature -ne 0x00004550) { + throw "Invalid PE signature in ${BinaryPath}" +} +$optionalHeaderOffset = $peOffset + 24 +$optionalHeaderMagic = [System.BitConverter]::ToUInt16($image, $optionalHeaderOffset) +$stackReserveOffset = $optionalHeaderOffset + 72 +$stackReserve = if ($optionalHeaderMagic -eq 0x20B) { + [System.BitConverter]::ToUInt64($image, $stackReserveOffset) +} elseif ($optionalHeaderMagic -eq 0x10B) { + [System.BitConverter]::ToUInt32($image, $stackReserveOffset) +} else { + throw ("Unsupported PE optional header magic 0x{0:X4} in {1}" -f $optionalHeaderMagic, $BinaryPath) +} +if ($stackReserve -lt $minimumStackReserve) { + throw ("Windows 7 main-thread stack reserve is {0} bytes, below the {1}-byte minimum. Add -C link-arg=/STACK:8388608 to the build RUSTFLAGS. Binary: {2}" -f $stackReserve, $minimumStackReserve, $BinaryPath) +} + +Write-Host "Windows 7 PE stack reserve audit passed: $stackReserve bytes ($BinaryPath)" diff --git a/.github/scripts/ci-workflow.test.mjs b/.github/scripts/ci-workflow.test.mjs index 6f3662c440..d9005ddf18 100644 --- a/.github/scripts/ci-workflow.test.mjs +++ b/.github/scripts/ci-workflow.test.mjs @@ -496,3 +496,26 @@ test("path filtering and the planner use the exact test merge first parent", () } assert.doesNotMatch(changes, /steps\.filter\.outputs\.windows_win7_bundle|github\.event\.pull_request\.base\.sha|WIN7_CURRENT/); }); + +test("legacy Windows builds keep the 8 MiB main-thread stack", () => { + const release = readFileSync(new URL("../workflows/release.yml", import.meta.url), "utf8").replaceAll("\r\n", "\n"); + const sqlServerUserTest = readFileSync(new URL("../workflows/sqlserver2000-windows-user-test.yml", import.meta.url), "utf8").replaceAll("\r\n", "\n"); + const stackFlag = /RUSTFLAGS:.*link-arg=\/STACK:8388608/; + // An env RUSTFLAGS replaces .cargo/config.toml target rustflags, so every job + // that links a Windows PE has to repeat the /STACK link argument; otherwise + // the 1 MiB linker default returns and deep DB call stacks abort with + // 0xC00000FD ("DBX 已停止工作"). + for (const name of ["windows-standard-check", "windows-win7-bundle", "duckdb-windows-driver"]) { + assert.match(job(name), stackFlag, `${name} must keep the 8 MiB main-thread stack in RUSTFLAGS`); + } + for (const name of ["build-windows-7-offline", "build-windows-7-static-browser"]) { + assert.match(job(name, release), stackFlag, `release job ${name} must keep the 8 MiB main-thread stack in RUSTFLAGS`); + } + assert.match(job("build-windows-7-x64", sqlServerUserTest), stackFlag, "the SQL Server 2000 Win7 test build must keep the 8 MiB main-thread stack"); +}); + +test("the Win7 PE audit fails when the stack reserve regresses", () => { + const audit = readFileSync(new URL("./assert-win7-pe-compat.ps1", import.meta.url), "utf8"); + assert.ok(audit.includes("minimumStackReserve")); + assert.ok(audit.includes("link-arg=/STACK:8388608")); +}); diff --git a/.github/workflows/agents-release.yml b/.github/workflows/agents-release.yml index ace4e518f4..1208ee507b 100644 --- a/.github/workflows/agents-release.yml +++ b/.github/workflows/agents-release.yml @@ -963,7 +963,9 @@ jobs: shell: bash run: | if [ "${{ runner.os }}" = "Windows" ]; then - export RUSTFLAGS="${RUSTFLAGS:+$RUSTFLAGS }-C target-feature=+crt-static" + # An env RUSTFLAGS replaces .cargo/config.toml target rustflags; keep + # the 8 MiB main-thread stack of the DBX app for the Win7 driver too. + export RUSTFLAGS="${RUSTFLAGS:+$RUSTFLAGS }-C target-feature=+crt-static -C link-arg=/STACK:8388608" fi if [ -n "${{ matrix.manylinux_image }}" ]; then docker run --rm \ diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index e27689ba74..dfb7c25c32 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -313,7 +313,11 @@ jobs: timeout-minutes: 45 env: CARGO_INCREMENTAL: "0" - RUSTFLAGS: -C debuginfo=line-tables-only -C target-feature=+crt-static + # An env RUSTFLAGS replaces .cargo/config.toml target rustflags, so the + # 8 MiB main-thread stack must be repeated here: without it the Windows PE + # keeps the 1 MiB linker default and deep connect/serde call stacks abort + # with 0xC00000FD ("DBX 已停止工作"). Guarded by assert-win7-pe-compat.ps1. + RUSTFLAGS: -C debuginfo=line-tables-only -C target-feature=+crt-static -C link-arg=/STACK:8388608 RUSTC_WRAPPER: sccache # Prefer S3 when available; GitHub's 10 GB cache quota evicts the shared # Linux target caches when thousands of GHA sccache entries pile up. @@ -388,7 +392,11 @@ jobs: CARGO_INCREMENTAL: "0" CARGO_PROFILE_RELEASE_LTO: "off" CARGO_PROFILE_RELEASE_CODEGEN_UNITS: "8" - RUSTFLAGS: -C debuginfo=line-tables-only -C target-feature=+crt-static + # An env RUSTFLAGS replaces .cargo/config.toml target rustflags, so the + # 8 MiB main-thread stack must be repeated here: without it the Windows PE + # keeps the 1 MiB linker default and deep connect/serde call stacks abort + # with 0xC00000FD ("DBX 已停止工作"). Guarded by assert-win7-pe-compat.ps1. + RUSTFLAGS: -C debuginfo=line-tables-only -C target-feature=+crt-static -C link-arg=/STACK:8388608 RUSTC_WRAPPER: sccache # Prefer S3 when available; GitHub's 10 GB cache quota evicts the shared # Linux target caches when thousands of GHA sccache entries pile up. @@ -830,7 +838,11 @@ jobs: env: CARGO_INCREMENTAL: "0" CARGO_TARGET_DIR: ${{ github.workspace }}/target/duckdb-driver - RUSTFLAGS: -C debuginfo=line-tables-only -C target-feature=+crt-static + # An env RUSTFLAGS replaces .cargo/config.toml target rustflags, so the + # 8 MiB main-thread stack must be repeated here: without it the Windows PE + # keeps the 1 MiB linker default and deep connect/serde call stacks abort + # with 0xC00000FD ("DBX 已停止工作"). Guarded by assert-win7-pe-compat.ps1. + RUSTFLAGS: -C debuginfo=line-tables-only -C target-feature=+crt-static -C link-arg=/STACK:8388608 RUSTC_WRAPPER: sccache # Prefer S3 when available; GitHub's 10 GB cache quota evicts the shared # Linux target caches when thousands of GHA sccache entries pile up. diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 779dba8571..090d086031 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -579,7 +579,11 @@ jobs: # The vendored loader is a tracked Cargo input, so Rust compile caching is safe. env: CARGO_INCREMENTAL: "0" - RUSTFLAGS: -C debuginfo=line-tables-only -C target-feature=+crt-static + # An env RUSTFLAGS replaces .cargo/config.toml target rustflags, so the + # 8 MiB main-thread stack must be repeated here: without it the legacy PE + # keeps the 1 MiB linker default and deep connect/serde call stacks abort + # with 0xC00000FD ("DBX 已停止工作"). Guarded by assert-win7-pe-compat.ps1. + RUSTFLAGS: -C debuginfo=line-tables-only -C target-feature=+crt-static -C link-arg=/STACK:8388608 RUSTC_WRAPPER: sccache SCCACHE_GHA_ENABLED: ${{ secrets.SCCACHE_S3_BUCKET == '' && 'true' || 'false' }} SCCACHE_GHA_VERSION: win7-webview2-1.0.902.49-v1 @@ -747,7 +751,11 @@ jobs: arch: x86_64 env: CARGO_INCREMENTAL: "0" - RUSTFLAGS: -C debuginfo=line-tables-only -C target-feature=+crt-static + # An env RUSTFLAGS replaces .cargo/config.toml target rustflags, so the + # 8 MiB main-thread stack must be repeated here: without it the legacy PE + # keeps the 1 MiB linker default and deep connect/serde call stacks abort + # with 0xC00000FD ("DBX 已停止工作"). Guarded by assert-win7-pe-compat.ps1. + RUSTFLAGS: -C debuginfo=line-tables-only -C target-feature=+crt-static -C link-arg=/STACK:8388608 steps: - uses: actions/checkout@v7 diff --git a/.github/workflows/sqlserver2000-windows-user-test.yml b/.github/workflows/sqlserver2000-windows-user-test.yml index 081ceb51a5..f3359f4a7c 100644 --- a/.github/workflows/sqlserver2000-windows-user-test.yml +++ b/.github/workflows/sqlserver2000-windows-user-test.yml @@ -12,7 +12,9 @@ jobs: timeout-minutes: 120 env: CARGO_INCREMENTAL: "0" - RUSTFLAGS: -C debuginfo=line-tables-only -C target-feature=+crt-static + # Keep the Win7/Server 2012 R2 main-thread stack at 8 MiB: an env + # RUSTFLAGS replaces the .cargo/config.toml target rustflags. + RUSTFLAGS: -C debuginfo=line-tables-only -C target-feature=+crt-static -C link-arg=/STACK:8388608 steps: - uses: actions/checkout@v7 diff --git a/crates/dbx-sql-schema/src/schema_diff.rs b/crates/dbx-sql-schema/src/schema_diff.rs index a981dd60d6..804f24e6fd 100644 --- a/crates/dbx-sql-schema/src/schema_diff.rs +++ b/crates/dbx-sql-schema/src/schema_diff.rs @@ -3860,7 +3860,7 @@ fn normalized_foreign_key_action(action: Option<&str>) -> Option { /// - "NO ACTION: ... For InnoDB, this is equivalent to RESTRICT". /// - "Specifying RESTRICT (or NO ACTION) is the same as omitting the ON DELETE or ON UPDATE clause." /// - "For an ON DELETE or ON UPDATE that is not specified, the default action is always NO ACTION." -/// MariaDB KB "Foreign Keys" likewise documents `NO ACTION` as a "Synonym for RESTRICT". +/// MariaDB KB "Foreign Keys" likewise documents `NO ACTION` as a "Synonym for RESTRICT". /// /// Only the comparison is normalized; generated DDL keeps the source's original action text. /// Known caveat: on MySQL NDB Cluster `NO ACTION` is a deferred check, but schema metadata does