Skip to content

Commit 84751a6

Browse files
committed
Merge fix/bundled-duckdb-json (41547ab): compile DuckDB's json extension into bundled builds
2 parents 2ce85b3 + 41547ab commit 84751a6

6 files changed

Lines changed: 114 additions & 27 deletions

File tree

‎.github/workflows/build-distributions.yml‎

Lines changed: 29 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,11 @@ on:
3434
required: false
3535
type: number
3636
default: 0
37+
platforms:
38+
description: "Comma-separated wheel platforms to build, such as linux-x86_64; empty builds all"
39+
required: false
40+
type: string
41+
default: ""
3742
outputs:
3843
sha:
3944
description: "Commit the distributions were built from"
@@ -49,6 +54,7 @@ jobs:
4954
outputs:
5055
sha: ${{ steps.source.outputs.sha }}
5156
tree: ${{ steps.source.outputs.tree }}
57+
platforms: ${{ steps.platforms.outputs.platforms }}
5258
steps:
5359
- uses: actions/checkout@v4
5460
with:
@@ -61,6 +67,28 @@ jobs:
6167
echo "sha=${sha}" >> "${GITHUB_OUTPUT}"
6268
echo "tree=${tree}" >> "${GITHUB_OUTPUT}"
6369
echo "Distributions for commit ${sha} with tree ${tree}" >> "${GITHUB_STEP_SUMMARY}"
70+
# The wheel platform matrix. DuckDB's C++ needs aligned deallocation, available from macOS 10.13.
71+
- name: Choose the wheel platforms
72+
id: platforms
73+
env:
74+
ONLY: ${{ inputs.platforms }}
75+
run: |
76+
all='[
77+
{"name": "linux-x86_64", "runner": "ubuntu-24.04", "target": "x86_64"},
78+
{"name": "linux-aarch64", "runner": "ubuntu-24.04-arm", "target": "aarch64"},
79+
{"name": "macos-x86_64", "runner": "macos-15-intel", "target": "x86_64", "macos-target": "10.15"},
80+
{"name": "macos-arm64", "runner": "macos-15", "target": "aarch64", "macos-target": "11.0"},
81+
{"name": "windows-x86_64", "runner": "windows-2025", "target": "x86_64"}
82+
]'
83+
chosen="$(jq -c --arg only "${ONLY}" \
84+
'if $only == "" then . else [.[] | select(.name as $name | $only | split(",") | index($name))] end' \
85+
<<< "${all}")"
86+
if [[ "${chosen}" == "[]" ]]; then
87+
echo "No wheel platform is named ${ONLY}" >&2
88+
exit 1
89+
fi
90+
echo "platforms=${chosen}" >> "${GITHUB_OUTPUT}"
91+
echo "Wheel platforms: $(jq -r 'map(.name) | join(", ")' <<< "${chosen}")" >> "${GITHUB_STEP_SUMMARY}"
6492
- name: Download the prebuilt manifest
6593
if: inputs.prebuilt-run-id != ''
6694
uses: actions/download-artifact@v4
@@ -92,25 +120,7 @@ jobs:
92120
strategy:
93121
fail-fast: false
94122
matrix:
95-
platform:
96-
- name: linux-x86_64
97-
runner: ubuntu-24.04
98-
target: x86_64
99-
- name: linux-aarch64
100-
runner: ubuntu-24.04-arm
101-
target: aarch64
102-
# DuckDB's C++ needs aligned deallocation, available from macOS 10.13.
103-
- name: macos-x86_64
104-
runner: macos-15-intel
105-
target: x86_64
106-
macos-target: "10.15"
107-
- name: macos-arm64
108-
runner: macos-15
109-
target: aarch64
110-
macos-target: "11.0"
111-
- name: windows-x86_64
112-
runner: windows-2025
113-
target: x86_64
123+
platform: ${{ fromJSON(needs.source.outputs.platforms) }}
114124
steps:
115125
- uses: actions/checkout@v4
116126
if: inputs.prebuilt-run-id == ''

‎.github/workflows/distributions.yml‎

Lines changed: 26 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,12 @@
11
name: Distributions
22

3-
# Builds and smoke tests every wheel and sdist when a pull request changes what goes into them.
3+
# Builds and smoke tests every wheel and sdist when a pull request changes what goes into them. On
4+
# every push to main it builds and smoke tests the linux-x86_64 wheels only, and runs the command
5+
# line's convert tests with DuckDB compiled in, so a distribution-only failure shows at the landing
6+
# rather than at the next release. Neither blocks a landing.
47
on:
8+
push:
9+
branches: [main]
510
pull_request:
611
paths:
712
- "python/**"
@@ -25,3 +30,23 @@ jobs:
2530
distributions:
2631
name: Distributions
2732
uses: ./.github/workflows/build-distributions.yml
33+
with:
34+
platforms: ${{ github.event_name == 'push' && 'linux-x86_64' || '' }}
35+
sdist: ${{ github.event_name != 'push' }}
36+
37+
# The command line built with `bundled` alone, as the sqlquack wheel compiles DuckDB in: no system
38+
# DuckDB and none of the `extensions` feature's extra extensions. It runs only the zone-free convert
39+
# test until the bundled build compiles in ICU; then it runs the full convert test.
40+
bundled-convert:
41+
name: Convert with DuckDB compiled in
42+
runs-on: ubuntu-24.04
43+
timeout-minutes: 90
44+
steps:
45+
- uses: actions/checkout@v4
46+
- name: Install the pinned Rust toolchain
47+
run: rustup toolchain install
48+
- uses: Swatinem/rust-cache@v2
49+
- name: Run the convert tests
50+
run: >-
51+
cargo test --locked -p sqlquack --features bundled --test cli
52+
given_zone_free_snowflake_sql_when_converting_then_the_checking_engine_binds_it

‎Makefile‎

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@ PYTHON_TYPED := scripts tests/unit/scripts python/src $(PYTHON_API_TESTS)
3030
PYTHON_TESTS := tests/unit/scripts $(PYTHON_API_TESTS)
3131
endif
3232

33-
.PHONY: distribution-notices format rust-lint python-check fensu syntax-coverage depth-probe depth-probe-debug gate-build polyglot-guard engine-free dialect-builds generate-cases generated-cases generate-syntax-cases syntax-cases dialect-cases corpus-rerecord generate-referee-cases referee-cases referee-pending translation-files translation-results help pending check-syntax record-syntax check-convert record-convert sql-sizes sql-size-baseline sql-benchmark check-ci check-merge test test-rust test-python python-module python-api python-wheel verify-recorded evidence-clone evidence-refresh evidence-pin evidence-pin-check clutter-guard polyglot-duckshop census-duckshop fuzz-smoke gate gate-parallel census duckshop-compiled duckshop-measure
33+
.PHONY: distribution-notices format rust-lint python-check fensu syntax-coverage depth-probe depth-probe-debug gate-build polyglot-guard engine-free bundled-json dialect-builds generate-cases generated-cases generate-syntax-cases syntax-cases dialect-cases corpus-rerecord generate-referee-cases referee-cases referee-pending translation-files translation-results help pending check-syntax record-syntax check-convert record-convert sql-sizes sql-size-baseline sql-benchmark check-ci check-merge test test-rust test-python python-module python-api python-wheel verify-recorded evidence-clone evidence-refresh evidence-pin evidence-pin-check clutter-guard polyglot-duckshop census-duckshop fuzz-smoke gate gate-parallel census duckshop-compiled duckshop-measure
3434

3535
format:
3636
cargo fmt --all
@@ -116,6 +116,14 @@ engine-free:
116116
echo "sqlquack-convert reaches DuckDB's engine" >&2; exit 1; \
117117
fi
118118

119+
# Fails when DuckDB compiled into a distribution lacks its json extension, which the checking
120+
# engine's helper macros need: the sqlquack wheel and the sqlquack-cli wheel both bundle DuckDB.
121+
bundled-json:
122+
@features="$$(cargo tree --locked -p sqlquack-blocks --features bundled -e features -i duckdb --prefix none)" || exit 1; \
123+
grep -q '^duckdb feature "json"' <<<"$$features" || { \
124+
echo "sqlquack-blocks' bundled feature compiles DuckDB without its json extension" >&2; exit 1; \
125+
}
126+
119127
# The crates whose features include dialects, as consumers build them one dialect at a time.
120128
DIALECT_CRATES ?= sqlquack-semantics sqlquack-parse sqlquack-fingerprint sqlquack-format sqlquack-lint \
121129
sqlquack-convert sqlquack-diagnostics
@@ -199,7 +207,7 @@ referee-pending:
199207
uv run python -m scripts.write_referee_pending $(REFEREE_ARGS)
200208

201209
# The static gate, the first step of `gate`, and the Python API's tests.
202-
check-ci: rust-lint python-check fensu syntax-coverage depth-probe evidence-pin-check clutter-guard polyglot-guard engine-free dialect-builds generated-cases syntax-cases dialect-cases referee-cases translation-files python-api distribution-notices ## The static gate CI runs.
210+
check-ci: rust-lint python-check fensu syntax-coverage depth-probe evidence-pin-check clutter-guard polyglot-guard engine-free bundled-json dialect-builds generated-cases syntax-cases dialect-cases referee-cases translation-files python-api distribution-notices ## The static gate CI runs.
203211

204212
# Each distribution ships the licence and third-party notices; its copies must match the root files.
205213
distribution-notices:

‎crates/sqlquack-blocks/Cargo.toml‎

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -15,11 +15,13 @@ rust-version.workspace = true
1515
[features]
1616
default = []
1717
engine = ["dep:duckdb"]
18-
# Compiles DuckDB into the binary, for distributions that must not need a system DuckDB.
19-
bundled = ["engine", "duckdb/bundled"]
20-
# DuckDB's parquet and json extensions compiled in, so `sqlquack run` reads Parquet fixtures and
21-
# JSON columns without downloading an extension.
22-
extensions = ["bundled", "duckdb/parquet", "duckdb/json"]
18+
# Compiles DuckDB into the binary, for distributions that must not need a system DuckDB. Its json
19+
# extension comes with it: the checking engine's helper macros call json functions, so without it
20+
# every conversion fails its check.
21+
bundled = ["engine", "duckdb/bundled", "duckdb/json"]
22+
# DuckDB's parquet extension compiled in as well, so `sqlquack run` reads Parquet fixtures without
23+
# downloading an extension.
24+
extensions = ["bundled", "duckdb/parquet"]
2325

2426
[dependencies]
2527
duckdb = { workspace = true, optional = true }

‎crates/sqlquack/tests/cli.rs‎

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,8 @@
22
mod helpers;
33
#[path = "cli/test_builtin_policy.rs"]
44
mod test_builtin_policy;
5+
#[path = "cli/test_bundled.rs"]
6+
mod test_bundled;
57
#[path = "cli/test_check.rs"]
68
mod test_check;
79
#[path = "cli/test_compare.rs"]
Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
use crate::helpers::{convert_outcome, run_convert};
2+
use crate::test_types::ConvertTestCase;
3+
4+
// The distributions workflow also runs this test with DuckDB compiled in (`--features bundled`), as
5+
// the wheels build it. It holds only zone-free statements: the compiled-in DuckDB lacks ICU until
6+
// the bundled build gains it, and then that job also runs the full convert test in test_convert.rs.
7+
#[test]
8+
fn given_zone_free_snowflake_sql_when_converting_then_the_checking_engine_binds_it() {
9+
let test_cases = [
10+
ConvertTestCase {
11+
description: "a constant converts once the checking engine binds it",
12+
sql: "SELECT 1 AS total",
13+
flags: &[],
14+
mention: "sqlquack convert: done, converted under utc-v1",
15+
expected_outcome: convert_outcome(0, true),
16+
},
17+
ConvertTestCase {
18+
description: "a VARIANT path converts with DuckDB's json functions",
19+
sql: "SELECT PARSE_JSON('{\"total\": 3}'):total AS total",
20+
flags: &[],
21+
mention: "sqlquack convert: done, converted under utc-v1",
22+
expected_outcome: convert_outcome(0, true),
23+
},
24+
ConvertTestCase {
25+
description: "a statement refused for its own reason still names it",
26+
sql: "WITH totals AS (SELECT 1 AS \"orderTotal\") SELECT ordertotal FROM totals",
27+
flags: &[],
28+
mention: " --> <stdin>:1:",
29+
expected_outcome: convert_outcome(1, false),
30+
},
31+
];
32+
for test_case in test_cases {
33+
assert_eq!(
34+
run_convert(test_case.sql, test_case.flags, test_case.mention),
35+
test_case.expected_outcome,
36+
"{}",
37+
test_case.description
38+
);
39+
}
40+
}

0 commit comments

Comments
 (0)