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
80 changes: 66 additions & 14 deletions .github/workflows/duckdb-extension-release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ on:
default: "v1.5.6"
type: string
create_github_release:
description: "Create or update a GitHub release with extension artifacts"
description: "Publish a new release; the matching extension version tag must already point to this commit"
required: false
default: false
type: boolean
Expand All @@ -27,15 +27,41 @@ permissions:

env:
GEN: ninja
CMAKE_BUILD_PARALLEL_LEVEL: 2
CARGO_BUILD_JOBS: 2
MACOSX_DEPLOYMENT_TARGET: "11.0"

jobs:
build:
name: Build source extension package
runs-on: ubuntu-latest
name: Build ${{ matrix.platform }} extension package
runs-on: ${{ matrix.runner }}
container: ${{ matrix.container }}
strategy:
fail-fast: false
matrix:
include:
- platform: linux_amd64
runner: ubuntu-24.04
container: quay.io/pypa/manylinux_2_28_x86_64:latest
- platform: linux_arm64
runner: ubuntu-24.04-arm
container: quay.io/pypa/manylinux_2_28_aarch64:latest
- platform: osx_amd64
runner: macos-15-intel
- platform: osx_arm64
runner: macos-15

steps:
- uses: actions/checkout@v4

- name: Install Linux build dependencies
if: runner.os == 'Linux'
run: dnf install -y cmake ninja-build gcc gcc-c++ git make

- name: Install macOS build dependencies
if: runner.os == 'macOS'
run: command -v ninja >/dev/null || brew install ninja

- name: Install Rust
uses: dtolnay/rust-toolchain@stable

Expand Down Expand Up @@ -72,11 +98,10 @@ jobs:
DUCKDB_VERSION: ${{ steps.duckdb.outputs.version }}
run: make deps DUCKDB_VERSION="$DUCKDB_VERSION"

- name: Install build dependencies
run: sudo apt-get update && sudo apt-get install -y ninja-build

- name: Build extension
working-directory: sidemantic-duckdb
env:
DUCKDB_PLATFORM: ${{ matrix.platform }}
run: make

- name: Run extension tests
Expand All @@ -87,16 +112,24 @@ jobs:
working-directory: sidemantic-duckdb
env:
DUCKDB_VERSION: ${{ steps.duckdb.outputs.version }}
PLATFORM: ${{ matrix.platform }}
run: |
mkdir -p dist
DUCKDB_VERSION="${DUCKDB_VERSION#v}"
cp build/release/extension/sidemantic/sidemantic.duckdb_extension "dist/sidemantic-duckdb-${DUCKDB_VERSION}-linux-amd64.duckdb_extension"
ASSET="sidemantic-duckdb-${DUCKDB_VERSION}-${PLATFORM}.duckdb_extension"
cp build/release/extension/sidemantic/sidemantic.duckdb_extension "dist/$ASSET"
cd dist
if command -v sha256sum >/dev/null; then
sha256sum "$ASSET" > "$ASSET.sha256"
else
shasum -a 256 "$ASSET" > "$ASSET.sha256"
fi

- name: Upload extension artifact
uses: actions/upload-artifact@v4
with:
name: sidemantic-duckdb-extension-linux-amd64
path: sidemantic-duckdb/dist/*.duckdb_extension
name: sidemantic-duckdb-extension-${{ matrix.platform }}
path: sidemantic-duckdb/dist/*
if-no-files-found: error

- name: Save completed build caches
Expand All @@ -119,6 +152,7 @@ jobs:
- name: Download extension artifact
uses: actions/download-artifact@v4
with:
pattern: sidemantic-duckdb-extension-*
path: dist
merge-multiple: true

Expand All @@ -135,15 +169,33 @@ jobs:
TAG="sidemantic-duckdb-v${VERSION}"
fi

if [[ ! "$TAG" =~ ^sidemantic-duckdb-v[0-9]+\.[0-9]+\.[0-9]+$ ]]; then
echo "Invalid extension release tag" >&2
exit 1
fi

echo "tag=$TAG" >> "$GITHUB_OUTPUT"

- name: Create or update GitHub release
- name: Verify and publish immutable artifacts
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
TAG: ${{ steps.release.outputs.tag }}
RELEASE_COMMIT: ${{ github.sha }}
run: |
if gh release view "$TAG" >/dev/null 2>&1; then
gh release upload "$TAG" dist/* --clobber
else
gh release create "$TAG" dist/* --title "$TAG" --generate-notes
set -euo pipefail
git fetch --no-tags origin "refs/tags/$TAG:refs/tags/$TAG"
if [ "$(git rev-parse "$TAG^{commit}")" != "$RELEASE_COMMIT" ]; then
echo "Release tag does not identify the built commit" >&2
exit 1
fi
cd dist
for PLATFORM in linux_amd64 linux_arm64 osx_amd64 osx_arm64; do
ASSET="sidemantic-duckdb-1.5.6-${PLATFORM}.duckdb_extension"
test -s "$ASSET"
sha256sum --check "$ASSET.sha256"
done
cat *.sha256 > SHA256SUMS
cp ../LICENSE .
# Creation fails if the tag already has a release; never overwrite published bytes.
gh release create "$TAG" *.duckdb_extension SHA256SUMS LICENSE \
--verify-tag --target "$RELEASE_COMMIT" --title "$TAG" --generate-notes
4 changes: 2 additions & 2 deletions sidemantic-duckdb/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -118,7 +118,7 @@ if(NOT CARGO_EXECUTABLE)
message(FATAL_ERROR "cargo is required to build the sidemantic DuckDB extension")
endif()

set(SIDEMANTIC_CARGO_BUILD_ARGS build --manifest-path "${SIDEMANTIC_RS_DIR}/Cargo.toml" --lib)
set(SIDEMANTIC_CARGO_BUILD_ARGS build --locked --manifest-path "${SIDEMANTIC_RS_DIR}/Cargo.toml" --lib)
if(SIDEMANTIC_CARGO_TARGET)
list(APPEND SIDEMANTIC_CARGO_BUILD_ARGS --target "${SIDEMANTIC_CARGO_TARGET}")
endif()
Expand All @@ -145,7 +145,7 @@ add_custom_target(sidemantic_rust_staticlib DEPENDS "${SIDEMANTIC_LIB}")
# Include Rust library headers
include_directories(${SIDEMANTIC_INCLUDE})

set(EXTENSION_SOURCES src/sidemantic_extension.cpp src/sidemantic_parser.cpp src/sidemantic_grammar.cpp src/sidemantic_catalog.cpp src/sidemantic_api.cpp)
set(EXTENSION_SOURCES src/sidemantic_extension.cpp src/sidemantic_parser.cpp src/sidemantic_grammar.cpp src/sidemantic_catalog.cpp src/sidemantic_api.cpp src/sidemantic_discovery.cpp)

build_static_extension(${TARGET_NAME} ${EXTENSION_SOURCES})
build_loadable_extension(${TARGET_NAME} " " ${EXTENSION_SOURCES})
Expand Down
25 changes: 21 additions & 4 deletions sidemantic-duckdb/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,12 @@ A DuckDB extension that adds a SQL-first semantic layer. Define metrics and dime

Current builds are loaded from a local build or GitHub release artifact:

The release workflow builds DuckDB 1.5.6 packages for Linux and macOS on AMD64 and
ARM64, with a `SHA256SUMS` manifest. Linux builds use a glibc 2.28 baseline. Choose
the matching version and platform, verify the checksum, and rename the downloaded
file to `sidemantic.duckdb_extension` before loading it. Published assets are
immutable; corrections receive a new release tag.

Start DuckDB with unsigned-extension loading enabled because these artifacts are not signed yet:

```bash
Expand Down Expand Up @@ -114,6 +120,18 @@ use `EXPLAIN SELECT` to validate a complete combination of metrics and dimension
`SHOW SEMANTIC METRICS`, and the corresponding forms for other kinds, are also
accepted.

Standard SQL clients can also discover loaded models as virtual views in the
current database's `semantic` schema through `information_schema.tables`,
`information_schema.columns`, and the corresponding `pg_catalog` relations.
Query them with ordinary SQL, for example
`select status, revenue from semantic.orders_model`. Dimension and metric column
types come from binding their compiled expressions against the current database.
If a source cannot bind, its fields remain visible with `NULL` data types and
`duckdb_views().is_bound = false`. An existing physical `semantic.orders_model`
table or view takes precedence. Discovery observes transactions and creates no
persistent schema or views; `SET schema = 'semantic'` requires an actual schema
created by the user.

Qualified definitions work independently of the connection's active model:

```sql
Expand Down Expand Up @@ -547,9 +565,8 @@ rows, including equal-valued metrics on distinct customers.
- DuckDB 1.5.6 uses the compatibility frontend. Native PEG support is tested against
the pinned 2.x commit, not an arbitrary future 2.x build. Rust still parses model
properties and compiles semantic queries.
- Distribution currently targets unsigned Linux amd64 release packages. Signed
community installation and a published multi-platform binary matrix are not
provided by this repository.
- The release workflow packages unsigned Linux and macOS builds on AMD64 and ARM64.
Signed community installation is not yet provided by this repository.

## Utility Functions

Expand Down Expand Up @@ -603,7 +620,7 @@ CI also builds `make deps DUCKDB_VERSION=v2.0-cyanoptera`, pinned in the Makefil
commit `80e17fc252edd6d9e9b090ae00a1100daef4876a`. Run its tests with
`SIDEMANTIC_NATIVE_PEG=1 make test`. Use separate build directories/checkouts when
switching DuckDB versions. CMake probes host APIs rather than assuming C++ ABI
compatibility across releases. Release packaging targets unsigned Linux amd64
compatibility across releases. Release packaging targets unsigned Linux and macOS
builds for 1.5.6; development-version CI does not publish stable artifacts.

## Architecture
Expand Down
4 changes: 4 additions & 0 deletions sidemantic-duckdb/src/include/sidemantic_catalog.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,10 @@ struct SidemanticCatalogSnapshot {
// sidecars are read only when no native snapshot exists and are never modified.
SidemanticCatalogSnapshot ReadSidemanticCatalog(ClientContext &context);

// Virtual semantic relations are exposed through the host's standard catalogs.
bool SidemanticPhysicalRelationExists(ClientContext &context, const string &model);
void RegisterSidemanticDiscovery(DatabaseInstance &db);

void RegisterSidemanticCatalogRead(ClientContext &context, StatementProperties &properties);
void RegisterSidemanticCatalogWrite(ClientContext &context, StatementProperties &properties);

Expand Down
Loading
Loading