From 83efc1aae5be6541a934168410d59e65116e9eb4 Mon Sep 17 00:00:00 2001 From: Cursor Agent Date: Mon, 29 Sep 2025 15:32:49 +0000 Subject: [PATCH 01/26] Checkpoint before follow-up message Co-authored-by: contact --- .cargo/config.toml | 3 +++ Cargo.toml | 1 + Dockerfile | 22 +++++++++++++++------- README.md | 4 ++-- configuration.md | 2 +- 5 files changed, 22 insertions(+), 10 deletions(-) diff --git a/.cargo/config.toml b/.cargo/config.toml index d22ed76a..fc3820e7 100644 --- a/.cargo/config.toml +++ b/.cargo/config.toml @@ -5,3 +5,6 @@ # set by the user, and this is a good thing. If the user already set some # LIBSQLITE3_FLAGS, he probably knows what he is doing. LIBSQLITE3_FLAGS = "-DSQLITE_ENABLE_MATH_FUNCTIONS" + +[build] +rustflags = ["-C", "link-arg=-Wl,-rpath=$ORIGIN/lib"] diff --git a/Cargo.toml b/Cargo.toml index c026173a..998e3fb8 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -49,6 +49,7 @@ sqlparser = { version = "0.59.0", default-features = false, features = [ "std", "visitor", ] } +odbc-sys = { version = "0.27", features = ["static"] } async-stream = "0.3" async-trait = "0.1.61" async-recursion = "1.0.0" diff --git a/Dockerfile b/Dockerfile index 8e930dec..57156b5e 100644 --- a/Dockerfile +++ b/Dockerfile @@ -7,21 +7,22 @@ RUN apt-get update && \ if [ "$TARGETARCH" = "$BUILDARCH" ]; then \ rustup target list --installed > TARGET && \ echo gcc > LINKER && \ - apt-get install -y gcc libgcc-s1 cmake unixodbc-dev libodbc2 libltdl7 && \ - LIBDIR="/lib/*"; \ - USRLIBDIR="/usr/lib/*"; \ + apt-get install -y gcc libgcc-s1 cmake unixodbc-dev libltdl-dev pkg-config && \ + LIBMULTIARCH=$(gcc -print-multiarch); \ + LIBDIR="/lib/$LIBMULTIARCH"; \ + USRLIBDIR="/usr/lib/$LIBMULTIARCH"; \ elif [ "$TARGETARCH" = "arm64" ]; then \ echo aarch64-unknown-linux-gnu > TARGET && \ echo aarch64-linux-gnu-gcc > LINKER && \ dpkg --add-architecture arm64 && apt-get update && \ - apt-get install -y gcc-aarch64-linux-gnu libgcc-s1-arm64-cross unixodbc-dev:arm64 libodbc2:arm64 libltdl7:arm64 && \ + apt-get install -y gcc-aarch64-linux-gnu libgcc-s1-arm64-cross unixodbc-dev:arm64 libltdl-dev:arm64 && \ LIBDIR="/lib/aarch64-linux-gnu"; \ USRLIBDIR="/usr/lib/aarch64-linux-gnu"; \ elif [ "$TARGETARCH" = "arm" ]; then \ echo armv7-unknown-linux-gnueabihf > TARGET && \ echo arm-linux-gnueabihf-gcc > LINKER && \ dpkg --add-architecture armhf && apt-get update && \ - apt-get install -y gcc-arm-linux-gnueabihf libgcc-s1-armhf-cross cmake libclang1 clang unixodbc-dev:armhf libodbc2:armhf libltdl7:armhf && \ + apt-get install -y gcc-arm-linux-gnueabihf libgcc-s1-armhf-cross cmake libclang1 clang unixodbc-dev:armhf libltdl-dev:armhf && \ cargo install --force --locked bindgen-cli && \ SYSROOT=$(arm-linux-gnueabihf-gcc -print-sysroot); \ echo "--sysroot=$SYSROOT -I$SYSROOT/usr/include -I$SYSROOT/usr/include/arm-linux-gnueabihf" > BINDGEN_EXTRA_CLANG_ARGS; \ @@ -31,13 +32,16 @@ RUN apt-get update && \ echo "Unsupported cross compilation target: $TARGETARCH"; \ exit 1; \ fi && \ - cp $LIBDIR/libgcc_s.so.1 $USRLIBDIR/libodbc.so.2 $USRLIBDIR/libltdl.so.7 /opt/sqlpage-libs/ && \ + echo $USRLIBDIR > ODBC_LIBDIR && \ + cp $LIBDIR/libgcc_s.so.1 /opt/sqlpage-libs/ && \ rustup target add $(cat TARGET) && \ cargo init . # Build dependencies (creates a layer that avoids recompiling dependencies on every build) COPY Cargo.toml Cargo.lock ./ RUN BINDGEN_EXTRA_CLANG_ARGS=$(cat BINDGEN_EXTRA_CLANG_ARGS || true) \ + RS_ODBC_LINK_SEARCH=$(cat ODBC_LIBDIR) \ + PKG_CONFIG_ALL_STATIC=1 \ cargo build \ --target $(cat TARGET) \ --config target.$(cat TARGET).linker='"'$(cat LINKER)'"' \ @@ -46,6 +50,8 @@ RUN BINDGEN_EXTRA_CLANG_ARGS=$(cat BINDGEN_EXTRA_CLANG_ARGS || true) \ # Build the project COPY . . RUN touch src/main.rs && \ + RS_ODBC_LINK_SEARCH=$(cat ODBC_LIBDIR) \ + PKG_CONFIG_ALL_STATIC=1 \ cargo build \ --target $(cat TARGET) \ --config target.$(cat TARGET).linker='"'$(cat LINKER)'"' \ @@ -62,7 +68,9 @@ ENV SQLPAGE_WEB_ROOT=/var/www ENV SQLPAGE_CONFIGURATION_DIRECTORY=/etc/sqlpage WORKDIR /var/www COPY --from=builder /usr/src/sqlpage/sqlpage.bin /usr/local/bin/sqlpage -COPY --from=builder /opt/sqlpage-libs/* /lib/ +# Provide runtime helper libs next to the binary for rpath=$ORIGIN/lib +RUN mkdir -p /usr/local/bin/lib +COPY --from=builder /opt/sqlpage-libs/* /usr/local/bin/lib/ USER sqlpage COPY --from=builder --chown=sqlpage:sqlpage /usr/src/sqlpage/sqlpage/sqlpage.db sqlpage/sqlpage.db EXPOSE 8080 diff --git a/README.md b/README.md index 69f365e0..a9e7235d 100644 --- a/README.md +++ b/README.md @@ -190,12 +190,12 @@ You can skip this section if you want to use one of the built-in database driver SQLPage supports ODBC connections to connect to databases that don't have native drivers, such as Oracle, Snowflake, BigQuery, IBM DB2, and many others. -ODBC support requires an ODBC driver manager and appropriate database drivers to be installed on your system. +On Linux, the SQLPage binary and Docker image now statically link against the `unixODBC` driver manager, so you generally do not need to install `unixodbc` on the host anymore. You still need to install the database-specific ODBC driver for the database you want to connect to. #### Install ODBC - On windows, it's installed by default. - - On linux: `sudo apt-get install -y unixodbc odbcinst unixodbc-common libodbcinst2` + - On linux, the driver manager is bundled with SQLPage; install only your database driver's package. - On mac: `brew install unixodbc` diff --git a/configuration.md b/configuration.md index 5be0f1ca..92589c42 100644 --- a/configuration.md +++ b/configuration.md @@ -105,7 +105,7 @@ DATABASE_URL="Driver={Oracle ODBC Driver};Server=localhost:1521/XE;UID=hr;PWD=pa DATABASE_URL="Driver={SnowflakeDSIIDriver};Server=account.snowflakecomputing.com;Database=mydb;UID=user;PWD=password" ``` -ODBC drivers must be installed and configured on your system. On Linux, you typically need `unixodbc` and the appropriate database-specific ODBC driver. +ODBC drivers must be installed and configured on your system. On Linux, the `unixODBC` driver manager is statically linked into the SQLPage binary, so you usually only need to install and configure the database-specific ODBC driver for your target database (for example Snowflake, Oracle, DuckDB...). If the `database_password` configuration parameter is set, it will override any password specified in the `database_url`. It does not need to be percent-encoded. From 9511964d92a445510e515c66e74407af9c62e43f Mon Sep 17 00:00:00 2001 From: Cursor Agent Date: Mon, 29 Sep 2025 15:34:56 +0000 Subject: [PATCH 02/26] Update rpath to include sqlpage directory for ODBC drivers Co-authored-by: contact --- .cargo/config.toml | 2 +- Dockerfile | 6 +++--- README.md | 2 +- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/.cargo/config.toml b/.cargo/config.toml index fc3820e7..9e2ddf61 100644 --- a/.cargo/config.toml +++ b/.cargo/config.toml @@ -7,4 +7,4 @@ LIBSQLITE3_FLAGS = "-DSQLITE_ENABLE_MATH_FUNCTIONS" [build] -rustflags = ["-C", "link-arg=-Wl,-rpath=$ORIGIN/lib"] +rustflags = ["-C", "link-arg=-Wl,-rpath=$ORIGIN/sqlpage:$ORIGIN/lib"] diff --git a/Dockerfile b/Dockerfile index 57156b5e..fe3dcf18 100644 --- a/Dockerfile +++ b/Dockerfile @@ -68,9 +68,9 @@ ENV SQLPAGE_WEB_ROOT=/var/www ENV SQLPAGE_CONFIGURATION_DIRECTORY=/etc/sqlpage WORKDIR /var/www COPY --from=builder /usr/src/sqlpage/sqlpage.bin /usr/local/bin/sqlpage -# Provide runtime helper libs next to the binary for rpath=$ORIGIN/lib -RUN mkdir -p /usr/local/bin/lib -COPY --from=builder /opt/sqlpage-libs/* /usr/local/bin/lib/ +# Provide runtime helper libs next to the binary for rpath=$ORIGIN/sqlpage +RUN mkdir -p /usr/local/bin/sqlpage +COPY --from=builder /opt/sqlpage-libs/* /usr/local/bin/sqlpage/ USER sqlpage COPY --from=builder --chown=sqlpage:sqlpage /usr/src/sqlpage/sqlpage/sqlpage.db sqlpage/sqlpage.db EXPOSE 8080 diff --git a/README.md b/README.md index a9e7235d..fdf2850b 100644 --- a/README.md +++ b/README.md @@ -190,7 +190,7 @@ You can skip this section if you want to use one of the built-in database driver SQLPage supports ODBC connections to connect to databases that don't have native drivers, such as Oracle, Snowflake, BigQuery, IBM DB2, and many others. -On Linux, the SQLPage binary and Docker image now statically link against the `unixODBC` driver manager, so you generally do not need to install `unixodbc` on the host anymore. You still need to install the database-specific ODBC driver for the database you want to connect to. +On Linux, the SQLPage binary and Docker image now statically link against the `unixODBC` driver manager, so you generally do not need to install `unixodbc` on the host anymore. You still need to install the database-specific ODBC driver for the database you want to connect to. SQLPage also searches for drivers in a `sqlpage/` directory placed next to the executable (rpath includes `$ORIGIN/sqlpage`). You can drop driver `.so` files there when packaging a self-contained application. #### Install ODBC From 35514347a6df1547dd3ce8e016b242173de4fbc7 Mon Sep 17 00:00:00 2001 From: Cursor Agent Date: Mon, 29 Sep 2025 17:25:37 +0000 Subject: [PATCH 03/26] Build: Add unixODBC build and cross-compilation support Co-authored-by: contact --- Dockerfile | 18 ++++++++++++------ 1 file changed, 12 insertions(+), 6 deletions(-) diff --git a/Dockerfile b/Dockerfile index fe3dcf18..28b3b42e 100644 --- a/Dockerfile +++ b/Dockerfile @@ -7,32 +7,40 @@ RUN apt-get update && \ if [ "$TARGETARCH" = "$BUILDARCH" ]; then \ rustup target list --installed > TARGET && \ echo gcc > LINKER && \ - apt-get install -y gcc libgcc-s1 cmake unixodbc-dev libltdl-dev pkg-config && \ + apt-get install -y gcc libgcc-s1 cmake make autoconf automake libtool pkg-config curl ca-certificates && \ LIBMULTIARCH=$(gcc -print-multiarch); \ LIBDIR="/lib/$LIBMULTIARCH"; \ USRLIBDIR="/usr/lib/$LIBMULTIARCH"; \ + HOST_TRIPLE=$(gcc -dumpmachine); \ elif [ "$TARGETARCH" = "arm64" ]; then \ echo aarch64-unknown-linux-gnu > TARGET && \ echo aarch64-linux-gnu-gcc > LINKER && \ dpkg --add-architecture arm64 && apt-get update && \ - apt-get install -y gcc-aarch64-linux-gnu libgcc-s1-arm64-cross unixodbc-dev:arm64 libltdl-dev:arm64 && \ + apt-get install -y gcc-aarch64-linux-gnu libgcc-s1-arm64-cross make autoconf automake libtool pkg-config curl ca-certificates && \ LIBDIR="/lib/aarch64-linux-gnu"; \ USRLIBDIR="/usr/lib/aarch64-linux-gnu"; \ + HOST_TRIPLE="aarch64-linux-gnu"; \ elif [ "$TARGETARCH" = "arm" ]; then \ echo armv7-unknown-linux-gnueabihf > TARGET && \ echo arm-linux-gnueabihf-gcc > LINKER && \ dpkg --add-architecture armhf && apt-get update && \ - apt-get install -y gcc-arm-linux-gnueabihf libgcc-s1-armhf-cross cmake libclang1 clang unixodbc-dev:armhf libltdl-dev:armhf && \ + apt-get install -y gcc-arm-linux-gnueabihf libgcc-s1-armhf-cross cmake libclang1 clang make autoconf automake libtool pkg-config curl ca-certificates && \ cargo install --force --locked bindgen-cli && \ SYSROOT=$(arm-linux-gnueabihf-gcc -print-sysroot); \ echo "--sysroot=$SYSROOT -I$SYSROOT/usr/include -I$SYSROOT/usr/include/arm-linux-gnueabihf" > BINDGEN_EXTRA_CLANG_ARGS; \ LIBDIR="/lib/arm-linux-gnueabihf"; \ USRLIBDIR="/usr/lib/arm-linux-gnueabihf"; \ + HOST_TRIPLE="arm-linux-gnueabihf"; \ else \ echo "Unsupported cross compilation target: $TARGETARCH"; \ exit 1; \ fi && \ - echo $USRLIBDIR > ODBC_LIBDIR && \ + ODBC_VERSION="2.3.12" && \ + curl -fsSL https://www.unixodbc.org/unixODBC-$ODBC_VERSION.tar.gz | tar -xz -C /tmp && \ + cd /tmp/unixODBC-$ODBC_VERSION && \ + CC=$(cat LINKER) ./configure --disable-shared --enable-static --host="$HOST_TRIPLE" --prefix=/opt/unixodbc && \ + make -j"$(nproc)" && make install && \ + echo /opt/unixodbc/lib > ODBC_LIBDIR && \ cp $LIBDIR/libgcc_s.so.1 /opt/sqlpage-libs/ && \ rustup target add $(cat TARGET) && \ cargo init . @@ -41,7 +49,6 @@ RUN apt-get update && \ COPY Cargo.toml Cargo.lock ./ RUN BINDGEN_EXTRA_CLANG_ARGS=$(cat BINDGEN_EXTRA_CLANG_ARGS || true) \ RS_ODBC_LINK_SEARCH=$(cat ODBC_LIBDIR) \ - PKG_CONFIG_ALL_STATIC=1 \ cargo build \ --target $(cat TARGET) \ --config target.$(cat TARGET).linker='"'$(cat LINKER)'"' \ @@ -51,7 +58,6 @@ RUN BINDGEN_EXTRA_CLANG_ARGS=$(cat BINDGEN_EXTRA_CLANG_ARGS || true) \ COPY . . RUN touch src/main.rs && \ RS_ODBC_LINK_SEARCH=$(cat ODBC_LIBDIR) \ - PKG_CONFIG_ALL_STATIC=1 \ cargo build \ --target $(cat TARGET) \ --config target.$(cat TARGET).linker='"'$(cat LINKER)'"' \ From 1b71bc42184f3fb02257a18a1d3f425bf3212ec6 Mon Sep 17 00:00:00 2001 From: Cursor Agent Date: Mon, 29 Sep 2025 17:28:07 +0000 Subject: [PATCH 04/26] Configure rpath for Linux targets and conditionalize odbc-sys Co-authored-by: contact --- .cargo/config.toml | 9 +++++++++ Cargo.toml | 8 +++++++- 2 files changed, 16 insertions(+), 1 deletion(-) diff --git a/.cargo/config.toml b/.cargo/config.toml index 9e2ddf61..b10e4507 100644 --- a/.cargo/config.toml +++ b/.cargo/config.toml @@ -7,4 +7,13 @@ LIBSQLITE3_FLAGS = "-DSQLITE_ENABLE_MATH_FUNCTIONS" [build] +rustflags = [] + +[target.x86_64-unknown-linux-gnu] +rustflags = ["-C", "link-arg=-Wl,-rpath=$ORIGIN/sqlpage:$ORIGIN/lib"] + +[target.aarch64-unknown-linux-gnu] +rustflags = ["-C", "link-arg=-Wl,-rpath=$ORIGIN/sqlpage:$ORIGIN/lib"] + +[target.armv7-unknown-linux-gnueabihf] rustflags = ["-C", "link-arg=-Wl,-rpath=$ORIGIN/sqlpage:$ORIGIN/lib"] diff --git a/Cargo.toml b/Cargo.toml index 998e3fb8..c9b5b572 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -49,7 +49,6 @@ sqlparser = { version = "0.59.0", default-features = false, features = [ "std", "visitor", ] } -odbc-sys = { version = "0.27", features = ["static"] } async-stream = "0.3" async-trait = "0.1.61" async-recursion = "1.0.0" @@ -79,3 +78,10 @@ rustls = "0.23" actix-rt = "2.8" libflate = "2" futures-util = "0.3.21" + +[target.'cfg(all(unix, not(target_os = "macos")))'.dependencies] +# Enable static linking of unixODBC only on Linux/Unix, not on Windows/macOS +odbc-sys = { version = "0.27", features = ["static"] } + +[target.'cfg(any(target_os = "windows", target_os = "macos"))'.dependencies] +odbc-sys = { version = "0.27" } From df242b4faec929112bbe6d5a27508585a98d54aa Mon Sep 17 00:00:00 2001 From: Cursor Agent Date: Thu, 2 Oct 2025 12:48:50 +0000 Subject: [PATCH 05/26] Refactor: Optimize dependencies and library paths in Dockerfile Co-authored-by: contact --- Dockerfile | 18 ++++++------------ 1 file changed, 6 insertions(+), 12 deletions(-) diff --git a/Dockerfile b/Dockerfile index 28b3b42e..5d83e202 100644 --- a/Dockerfile +++ b/Dockerfile @@ -7,7 +7,7 @@ RUN apt-get update && \ if [ "$TARGETARCH" = "$BUILDARCH" ]; then \ rustup target list --installed > TARGET && \ echo gcc > LINKER && \ - apt-get install -y gcc libgcc-s1 cmake make autoconf automake libtool pkg-config curl ca-certificates && \ + apt-get install -y gcc libgcc-s1 cmake unixodbc-dev libltdl-dev pkg-config && \ LIBMULTIARCH=$(gcc -print-multiarch); \ LIBDIR="/lib/$LIBMULTIARCH"; \ USRLIBDIR="/usr/lib/$LIBMULTIARCH"; \ @@ -16,7 +16,7 @@ RUN apt-get update && \ echo aarch64-unknown-linux-gnu > TARGET && \ echo aarch64-linux-gnu-gcc > LINKER && \ dpkg --add-architecture arm64 && apt-get update && \ - apt-get install -y gcc-aarch64-linux-gnu libgcc-s1-arm64-cross make autoconf automake libtool pkg-config curl ca-certificates && \ + apt-get install -y gcc-aarch64-linux-gnu libgcc-s1-arm64-cross unixodbc-dev:arm64 libltdl-dev:arm64 pkg-config && \ LIBDIR="/lib/aarch64-linux-gnu"; \ USRLIBDIR="/usr/lib/aarch64-linux-gnu"; \ HOST_TRIPLE="aarch64-linux-gnu"; \ @@ -24,7 +24,7 @@ RUN apt-get update && \ echo armv7-unknown-linux-gnueabihf > TARGET && \ echo arm-linux-gnueabihf-gcc > LINKER && \ dpkg --add-architecture armhf && apt-get update && \ - apt-get install -y gcc-arm-linux-gnueabihf libgcc-s1-armhf-cross cmake libclang1 clang make autoconf automake libtool pkg-config curl ca-certificates && \ + apt-get install -y gcc-arm-linux-gnueabihf libgcc-s1-armhf-cross cmake libclang1 clang unixodbc-dev:armhf libltdl-dev:armhf pkg-config && \ cargo install --force --locked bindgen-cli && \ SYSROOT=$(arm-linux-gnueabihf-gcc -print-sysroot); \ echo "--sysroot=$SYSROOT -I$SYSROOT/usr/include -I$SYSROOT/usr/include/arm-linux-gnueabihf" > BINDGEN_EXTRA_CLANG_ARGS; \ @@ -35,12 +35,7 @@ RUN apt-get update && \ echo "Unsupported cross compilation target: $TARGETARCH"; \ exit 1; \ fi && \ - ODBC_VERSION="2.3.12" && \ - curl -fsSL https://www.unixodbc.org/unixODBC-$ODBC_VERSION.tar.gz | tar -xz -C /tmp && \ - cd /tmp/unixODBC-$ODBC_VERSION && \ - CC=$(cat LINKER) ./configure --disable-shared --enable-static --host="$HOST_TRIPLE" --prefix=/opt/unixodbc && \ - make -j"$(nproc)" && make install && \ - echo /opt/unixodbc/lib > ODBC_LIBDIR && \ + echo $USRLIBDIR > ODBC_LIBDIR && \ cp $LIBDIR/libgcc_s.so.1 /opt/sqlpage-libs/ && \ rustup target add $(cat TARGET) && \ cargo init . @@ -74,9 +69,8 @@ ENV SQLPAGE_WEB_ROOT=/var/www ENV SQLPAGE_CONFIGURATION_DIRECTORY=/etc/sqlpage WORKDIR /var/www COPY --from=builder /usr/src/sqlpage/sqlpage.bin /usr/local/bin/sqlpage -# Provide runtime helper libs next to the binary for rpath=$ORIGIN/sqlpage -RUN mkdir -p /usr/local/bin/sqlpage -COPY --from=builder /opt/sqlpage-libs/* /usr/local/bin/sqlpage/ +# Provide runtime helper libs in system lib directory for the glibc busybox base +COPY --from=builder /opt/sqlpage-libs/* /lib/ USER sqlpage COPY --from=builder --chown=sqlpage:sqlpage /usr/src/sqlpage/sqlpage/sqlpage.db sqlpage/sqlpage.db EXPOSE 8080 From 186686b6a3ff9176cc71bb5097d9e57c3bf29ecf Mon Sep 17 00:00:00 2001 From: Cursor Agent Date: Thu, 2 Oct 2025 15:03:25 +0000 Subject: [PATCH 06/26] Add odbc feature and conditional odbc-sys dependency Co-authored-by: contact --- Cargo.toml | 18 ++++++++++++------ 1 file changed, 12 insertions(+), 6 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 7e6e5336..969217fc 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -17,6 +17,16 @@ lto = "fat" panic = "abort" codegen-units = 2 +[features] +# Build variants +# - default (odbc-dynamic): ODBC enabled, dynamically linked (works on Linux, macOS, Windows) +# - odbc-static: ODBC enabled, statically linked to the driver manager (Linux only) +# - disable ODBC entirely: use `--no-default-features` +default = ["odbc-dynamic"] +odbc = ["sqlx/odbc"] +odbc-dynamic = ["odbc"] +odbc-static = ["odbc", "odbc-sys/static"] + [dependencies] sqlx = { package = "sqlx-oldapi", git = "https://github.com/sqlpage/sqlx-oldapi", version = "0.6.49-beta.6", default-features = false, features = [ "any", @@ -26,7 +36,6 @@ sqlx = { package = "sqlx-oldapi", git = "https://github.com/sqlpage/sqlx-oldapi" "postgres", "mysql", "mssql", - "odbc", "chrono", "bigdecimal", "json", @@ -75,6 +84,7 @@ clap = { version = "4.5.17", features = ["derive"] } tokio-util = "0.7.12" openidconnect = { version = "4.0.0", default-features = false } encoding_rs = "0.8.35" +odbc-sys = { version = "0.27", optional = true } [build-dependencies] awc = { version = "3", features = ["rustls-0_23-webpki-roots"] } @@ -84,8 +94,4 @@ libflate = "2" futures-util = "0.3.21" [target.'cfg(all(unix, not(target_os = "macos")))'.dependencies] -# Enable static linking of unixODBC only on Linux/Unix, not on Windows/macOS -odbc-sys = { version = "0.27", features = ["static"] } - -[target.'cfg(any(target_os = "windows", target_os = "macos"))'.dependencies] -odbc-sys = { version = "0.27" } +odbc-sys = { version = "0.27", optional = true, features = [] } From 8866cf4759cbda18b71640df5ef69e6f40c7d91a Mon Sep 17 00:00:00 2001 From: Cursor Agent Date: Thu, 2 Oct 2025 16:17:48 +0000 Subject: [PATCH 07/26] feat: Add ODBC build modes and documentation Co-authored-by: contact --- CONTRIBUTING.md | 29 +++++++++++++++++++++++++++-- README.md | 2 +- src/webserver/database/connect.rs | 11 ++++++++--- 3 files changed, 36 insertions(+), 6 deletions(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 3735646f..d82f0f2b 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -37,6 +37,33 @@ cargo build --release The resulting executable will be in `target/release/sqlpage`. +### ODBC build modes + +SQLPage supports three additive build modes for ODBC. Choose the mode via Cargo features: + +- Dynamic ODBC (default): + - Works on Linux, macOS and Windows (Windows has ODBC built-in). + - Command: + ```bash + cargo build # or: cargo build --features odbc-dynamic + ``` +- Static ODBC (Linux only): + - Statically links the ODBC driver manager; simplifies distribution. + - Command: + ```bash + cargo build --features odbc-static + ``` +- No ODBC: + - Disables ODBC support entirely. + - Command: + ```bash + cargo build --no-default-features + ``` + +Notes: +- When cross-compiling in Docker, headers come from the base image (e.g. `unixodbc-dev`). +- Runtime rpath on Linux includes `$ORIGIN/sqlpage:$ORIGIN/lib` so you can colocate drivers next to the binary when needed. + ## Code Style and Linting ### Rust @@ -199,8 +226,6 @@ git checkout -b feature/your-feature-name - Run frontend linting with Biome - Test against multiple databases (PostgreSQL, MySQL, MSSQL) -5. Wait for review and address any feedback - ## Release Process Releases are automated when pushing tags that match the pattern `v*` (e.g., `v1.0.0`). The CI pipeline will: diff --git a/README.md b/README.md index fdf2850b..85e79475 100644 --- a/README.md +++ b/README.md @@ -190,7 +190,7 @@ You can skip this section if you want to use one of the built-in database driver SQLPage supports ODBC connections to connect to databases that don't have native drivers, such as Oracle, Snowflake, BigQuery, IBM DB2, and many others. -On Linux, the SQLPage binary and Docker image now statically link against the `unixODBC` driver manager, so you generally do not need to install `unixodbc` on the host anymore. You still need to install the database-specific ODBC driver for the database you want to connect to. SQLPage also searches for drivers in a `sqlpage/` directory placed next to the executable (rpath includes `$ORIGIN/sqlpage`). You can drop driver `.so` files there when packaging a self-contained application. +On Linux, SQLPage supports both dynamic and static ODBC linking. The Docker image uses the system `unixODBC` (dynamic). Release binaries can be built with static ODBC linking by enabling the `odbc-static` feature. You still need to install or provide the database-specific ODBC driver for the database you want to connect to. SQLPage also searches for drivers in a `sqlpage/` directory placed next to the executable (rpath includes `$ORIGIN/sqlpage`). #### Install ODBC diff --git a/src/webserver/database/connect.rs b/src/webserver/database/connect.rs index b67dd431..f3060077 100644 --- a/src/webserver/database/connect.rs +++ b/src/webserver/database/connect.rs @@ -10,11 +10,12 @@ use anyhow::Context; use futures_util::future::BoxFuture; use sqlx::{ any::{Any, AnyConnectOptions, AnyKind}, - odbc::OdbcConnectOptions, pool::PoolOptions, sqlite::{Function, SqliteConnectOptions, SqliteFunctionCtx}, ConnectOptions, Connection, Executor, }; +#[cfg(feature = "odbc")] +use sqlx::odbc::OdbcConnectOptions; impl Database { pub async fn init(config: &AppConfig) -> anyhow::Result { @@ -209,8 +210,11 @@ fn set_custom_connect_options(options: &mut AnyConnectOptions, config: &AppConfi if let Some(sqlite_options) = options.as_sqlite_mut() { set_custom_connect_options_sqlite(sqlite_options, config); } - if let Some(odbc_options) = options.as_odbc_mut() { - set_custom_connect_options_odbc(odbc_options, config); + #[cfg(feature = "odbc")] + { + if let Some(odbc_options) = options.as_odbc_mut() { + set_custom_connect_options_odbc(odbc_options, config); + } } } @@ -239,6 +243,7 @@ fn make_sqlite_fun(name: &str, f: fn(&str) -> String) -> Function { }) } +#[cfg(feature = "odbc")] fn set_custom_connect_options_odbc(odbc_options: &mut OdbcConnectOptions, config: &AppConfig) { // Allow fetching very large text fields when using ODBC by removing the max column size limit let batch_size = config.max_pending_rows.clamp(1, 1024); From 74fc9e6bb045f874af03ce8179993e889d8806ca Mon Sep 17 00:00:00 2001 From: Cursor Agent Date: Thu, 2 Oct 2025 19:37:02 +0000 Subject: [PATCH 08/26] Remove unused odbc-sys dependency Co-authored-by: contact --- Cargo.toml | 3 --- 1 file changed, 3 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 969217fc..ad7b85f3 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -92,6 +92,3 @@ rustls = "0.23" actix-rt = "2.8" libflate = "2" futures-util = "0.3.21" - -[target.'cfg(all(unix, not(target_os = "macos")))'.dependencies] -odbc-sys = { version = "0.27", optional = true, features = [] } From bbf0c8c69164ad1055f6bbc0d8e31958b339d4e7 Mon Sep 17 00:00:00 2001 From: lovasoa Date: Thu, 2 Oct 2025 22:39:33 +0200 Subject: [PATCH 09/26] shorter agents rules --- AGENTS.md | 31 ++++--------------------------- 1 file changed, 4 insertions(+), 27 deletions(-) diff --git a/AGENTS.md b/AGENTS.md index 1d9f5acd..e629980f 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -32,33 +32,10 @@ DATABASE_URL='mssql://root:Password123!@localhost/sqlpage' cargo test # all dbms - Components: defined in `./sqlpage/templates/*.handlebars` - Functions: `src/webserver/database/sqlpage_functions/functions.rs` registered with `make_function!`. - Components and functions are documented in [official website](./examples/official-site/sqlpage/migrations/); one migration per component and per function. -- ```sql - CREATE TABLE component( - name TEXT PRIMARY KEY, - description TEXT NOT NULL, - icon TEXT, -- icon name from tabler icon - introduced_in_version TEXT - ); - - CREATE TABLE parameter_type(name TEXT PRIMARY KEY); - INSERT INTO parameter_type(name) VALUES ('BOOLEAN'), ('COLOR'), ('HTML'), ('ICON'), ('INTEGER'), ('JSON'), ('REAL'), ('TEXT'), ('TIMESTAMP'), ('URL'); - - CREATE TABLE parameter( - top_level BOOLEAN DEFAULT FALSE, - name TEXT, - component TEXT REFERENCES component(name) ON DELETE CASCADE, - description TEXT, - description_md TEXT, - type TEXT REFERENCES parameter_type(name) ON DELETE CASCADE, - optional BOOLEAN DEFAULT FALSE, - ); - - CREATE TABLE example( - component TEXT REFERENCES component(name) ON DELETE CASCADE, - description TEXT, - properties JSON, - ); - ``` + - tables + - `component(name,description,icon,introduced_in_version)` -- icon name from tabler icon + - `parameter(top_level BOOLEAN, name, component REFERENCES component(name), description, description_md, type, optional BOOLEAN)` parameter types: BOOLEAN, COLOR, HTML, ICON, INTEGER, JSON, REAL, TEXT, TIMESTAMP, URL + - `example(component REFERENCES component(name), description, properties JSON)` - [Configuration](./configuration.md): see [AppConfig](./src/app_config.rs) - Routing: file-based in `src/webserver/routing.rs`; not found handled via `src/default_404.sql`. - Follow patterns from similar modules before introducing new abstractions. From 6790c50120c9a37a871a2098efd6c9cf82b4dff7 Mon Sep 17 00:00:00 2001 From: lovasoa Date: Thu, 2 Oct 2025 22:39:36 +0200 Subject: [PATCH 10/26] deps --- Cargo.lock | 1 + 1 file changed, 1 insertion(+) diff --git a/Cargo.lock b/Cargo.lock index a2b58f12..38e77c9f 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4298,6 +4298,7 @@ dependencies = [ "log", "markdown", "mime_guess", + "odbc-sys", "openidconnect", "password-hash", "percent-encoding", From a2ad7bbfabc7d9145549c482b842150678746191 Mon Sep 17 00:00:00 2001 From: Cursor Agent Date: Thu, 2 Oct 2025 20:54:05 +0000 Subject: [PATCH 11/26] Refactor: Move odbc import to top of file Co-authored-by: contact --- src/webserver/database/connect.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/webserver/database/connect.rs b/src/webserver/database/connect.rs index f3060077..5903b688 100644 --- a/src/webserver/database/connect.rs +++ b/src/webserver/database/connect.rs @@ -8,14 +8,14 @@ use crate::{ }; use anyhow::Context; use futures_util::future::BoxFuture; +#[cfg(feature = "odbc")] +use sqlx::odbc::OdbcConnectOptions; use sqlx::{ any::{Any, AnyConnectOptions, AnyKind}, pool::PoolOptions, sqlite::{Function, SqliteConnectOptions, SqliteFunctionCtx}, ConnectOptions, Connection, Executor, }; -#[cfg(feature = "odbc")] -use sqlx::odbc::OdbcConnectOptions; impl Database { pub async fn init(config: &AppConfig) -> anyhow::Result { From 515005cac7e50c52a86bb0ac7996ad7e4e6b3341 Mon Sep 17 00:00:00 2001 From: lovasoa Date: Fri, 3 Oct 2025 22:50:22 +0200 Subject: [PATCH 12/26] truly static iodbc link --- .cargo/config.toml | 6 +++--- .github/workflows/ci.yml | 4 ---- Cargo.lock | 6 ++++-- Cargo.toml | 6 ++++++ 4 files changed, 13 insertions(+), 9 deletions(-) diff --git a/.cargo/config.toml b/.cargo/config.toml index b10e4507..bf15dccf 100644 --- a/.cargo/config.toml +++ b/.cargo/config.toml @@ -10,10 +10,10 @@ LIBSQLITE3_FLAGS = "-DSQLITE_ENABLE_MATH_FUNCTIONS" rustflags = [] [target.x86_64-unknown-linux-gnu] -rustflags = ["-C", "link-arg=-Wl,-rpath=$ORIGIN/sqlpage:$ORIGIN/lib"] +rustflags = ["-C", "link-arg=-Wl,-rpath=$ORIGIN/sqlpage:$ORIGIN/sqlpage/drivers"] [target.aarch64-unknown-linux-gnu] -rustflags = ["-C", "link-arg=-Wl,-rpath=$ORIGIN/sqlpage:$ORIGIN/lib"] +rustflags = ["-C", "link-arg=-Wl,-rpath=$ORIGIN/sqlpage:$ORIGIN/sqlpage/drivers"] [target.armv7-unknown-linux-gnueabihf] -rustflags = ["-C", "link-arg=-Wl,-rpath=$ORIGIN/sqlpage:$ORIGIN/lib"] +rustflags = ["-C", "link-arg=-Wl,-rpath=$ORIGIN/sqlpage:$ORIGIN/sqlpage/drivers"] diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 0bf28e9a..0d7e474c 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -64,10 +64,6 @@ jobs: - uses: actions/checkout@v4 - name: Set up cargo cache uses: Swatinem/rust-cache@9d47c6ad4b02e050fd481d890b2ea34778fd09d6 - - name: Install ODBC dependencies - run: | - sudo apt-get update - sudo apt-get install -y unixodbc-dev freetds-dev - name: Setup ODBC for testing if: matrix.setup_odbc run: | diff --git a/Cargo.lock b/Cargo.lock index 38e77c9f..8083d8e8 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3123,8 +3123,10 @@ dependencies = [ [[package]] name = "odbc-sys" version = "0.27.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "acb069b57ebbad5234fb7197af7ee0c40daceb3946a86fa8d3f7a38393bf2770" +source = "git+https://github.com/sqlpage/odbc-sys?branch=main#0b5489e7b07d45e6b126f5a57ed6c301f022d2da" +dependencies = [ + "cc", +] [[package]] name = "oid-registry" diff --git a/Cargo.toml b/Cargo.toml index ad7b85f3..fffd8373 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -86,6 +86,12 @@ openidconnect = { version = "4.0.0", default-features = false } encoding_rs = "0.8.35" odbc-sys = { version = "0.27", optional = true } +[target.'cfg(any(target_os = "linux", target_os = "macos"))'.dependencies] +odbc-sys = { version = "0.27", optional = true, features = ["iodbc", "static"] } + +[patch.crates-io] +odbc-sys = { git = "https://github.com/sqlpage/odbc-sys", branch = "main" } + [build-dependencies] awc = { version = "3", features = ["rustls-0_23-webpki-roots"] } rustls = "0.23" From 25f8385067b60255f6b3771960c0b4e95d7509ba Mon Sep 17 00:00:00 2001 From: lovasoa Date: Fri, 3 Oct 2025 23:00:01 +0200 Subject: [PATCH 13/26] make odbc mandatory --- Cargo.lock | 40 +++++++++++++++---------------- Cargo.toml | 19 +++++---------- src/webserver/database/connect.rs | 10 +++----- 3 files changed, 28 insertions(+), 41 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 8083d8e8..dd43bdd7 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -365,9 +365,9 @@ dependencies = [ [[package]] name = "anstream" -version = "0.6.20" +version = "0.6.21" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3ae563653d1938f79b1ab1b5e668c87c76a9930414574a6583a7b7e11a8e6192" +checksum = "43d5b281e737544384e969a5ccad3f1cdd24b48086a0fc1b2a5262a26b8f4f4a" dependencies = [ "anstyle", "anstyle-parse", @@ -895,9 +895,9 @@ dependencies = [ [[package]] name = "cc" -version = "1.2.39" +version = "1.2.40" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e1354349954c6fc9cb0deab020f27f783cf0b604e8bb754dc4658ecf0d29c35f" +checksum = "e1d05d92f4b1fd76aad469d46cdd858ca761576082cd37df81416691e50199fb" dependencies = [ "find-msvc-tools", "jobserver", @@ -1739,9 +1739,9 @@ checksum = "28dea519a9695b9977216879a3ebfddf92f1c08c05d984f8996aecd6ecdc811d" [[package]] name = "find-msvc-tools" -version = "0.1.2" +version = "0.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1ced73b1dacfc750a6db6c0a0c3a3853c8b41997e2e2c563dc90804ae6867959" +checksum = "0399f9d26e5191ce32c498bebd31e7a3ceabc2745f0ac54af3f335126c3f24b3" [[package]] name = "flate2" @@ -3329,20 +3329,19 @@ checksum = "9b4f627cb1b25917193a259e49bdad08f671f8d9708acfd5fe0a8c1455d87220" [[package]] name = "pest" -version = "2.8.2" +version = "2.8.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "21e0a3a33733faeaf8651dfee72dd0f388f0c8e5ad496a3478fa5a922f49cfa8" +checksum = "989e7521a040efde50c3ab6bbadafbe15ab6dc042686926be59ac35d74607df4" dependencies = [ "memchr", - "thiserror 2.0.17", "ucd-trie", ] [[package]] name = "pest_derive" -version = "2.8.2" +version = "2.8.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bc58706f770acb1dbd0973e6530a3cff4746fb721207feb3a8a6064cd0b6c663" +checksum = "187da9a3030dbafabbbfb20cb323b976dc7b7ce91fcd84f2f74d6e31d378e2de" dependencies = [ "pest", "pest_generator", @@ -3350,9 +3349,9 @@ dependencies = [ [[package]] name = "pest_generator" -version = "2.8.2" +version = "2.8.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6d4f36811dfe07f7b8573462465d5cb8965fffc2e71ae377a33aecf14c2c9a2f" +checksum = "49b401d98f5757ebe97a26085998d6c0eecec4995cad6ab7fc30ffdf4b052843" dependencies = [ "pest", "pest_meta", @@ -3363,9 +3362,9 @@ dependencies = [ [[package]] name = "pest_meta" -version = "2.8.2" +version = "2.8.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "42919b05089acbd0a5dcd5405fb304d17d1053847b81163d09c4ad18ce8e8420" +checksum = "72f27a2cfee9f9039c4d86faa5af122a0ac3851441a34865b8a043b46be0065a" dependencies = [ "pest", "sha2", @@ -4131,9 +4130,9 @@ dependencies = [ [[package]] name = "serde_with" -version = "3.14.1" +version = "3.15.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c522100790450cf78eeac1507263d0a350d4d5b30df0c8e1fe051a10c22b376e" +checksum = "6093cd8c01b25262b84927e0f7151692158fab02d961e04c979d3903eba7ecc5" dependencies = [ "base64 0.22.1", "chrono", @@ -4142,8 +4141,7 @@ dependencies = [ "indexmap 2.11.4", "schemars 0.9.0", "schemars 1.0.4", - "serde", - "serde_derive", + "serde_core", "serde_json", "serde_with_macros", "time", @@ -4151,9 +4149,9 @@ dependencies = [ [[package]] name = "serde_with_macros" -version = "3.14.1" +version = "3.15.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "327ada00f7d64abaac1e55a6911e90cf665aa051b9a561c7006c157f4633135e" +checksum = "a7e6c180db0816026a61afa1cff5344fb7ebded7e4d3062772179f2501481c27" dependencies = [ "darling 0.21.3", "proc-macro2", diff --git a/Cargo.toml b/Cargo.toml index fffd8373..3f63683b 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -17,16 +17,6 @@ lto = "fat" panic = "abort" codegen-units = 2 -[features] -# Build variants -# - default (odbc-dynamic): ODBC enabled, dynamically linked (works on Linux, macOS, Windows) -# - odbc-static: ODBC enabled, statically linked to the driver manager (Linux only) -# - disable ODBC entirely: use `--no-default-features` -default = ["odbc-dynamic"] -odbc = ["sqlx/odbc"] -odbc-dynamic = ["odbc"] -odbc-static = ["odbc", "odbc-sys/static"] - [dependencies] sqlx = { package = "sqlx-oldapi", git = "https://github.com/sqlpage/sqlx-oldapi", version = "0.6.49-beta.6", default-features = false, features = [ "any", @@ -36,6 +26,7 @@ sqlx = { package = "sqlx-oldapi", git = "https://github.com/sqlpage/sqlx-oldapi" "postgres", "mysql", "mssql", + "odbc", "chrono", "bigdecimal", "json", @@ -84,13 +75,15 @@ clap = { version = "4.5.17", features = ["derive"] } tokio-util = "0.7.12" openidconnect = { version = "4.0.0", default-features = false } encoding_rs = "0.8.35" -odbc-sys = { version = "0.27", optional = true } [target.'cfg(any(target_os = "linux", target_os = "macos"))'.dependencies] -odbc-sys = { version = "0.27", optional = true, features = ["iodbc", "static"] } +odbc-sys = { version = "0.27", features = ["iodbc", "static"] } + +[target.'cfg(any(target_os = "windows"))'.dependencies] +odbc-sys = { version = "0.27", features = ["iodbc", "static"] } [patch.crates-io] -odbc-sys = { git = "https://github.com/sqlpage/odbc-sys", branch = "main" } +odbc-sys = { git = "https://github.com/sqlpage/odbc-sys", branch = "main" } # see https://github.com/pacman82/odbc-sys/pull/60 [build-dependencies] awc = { version = "3", features = ["rustls-0_23-webpki-roots"] } diff --git a/src/webserver/database/connect.rs b/src/webserver/database/connect.rs index 5903b688..09528b0c 100644 --- a/src/webserver/database/connect.rs +++ b/src/webserver/database/connect.rs @@ -8,7 +8,6 @@ use crate::{ }; use anyhow::Context; use futures_util::future::BoxFuture; -#[cfg(feature = "odbc")] use sqlx::odbc::OdbcConnectOptions; use sqlx::{ any::{Any, AnyConnectOptions, AnyKind}, @@ -210,11 +209,9 @@ fn set_custom_connect_options(options: &mut AnyConnectOptions, config: &AppConfi if let Some(sqlite_options) = options.as_sqlite_mut() { set_custom_connect_options_sqlite(sqlite_options, config); } - #[cfg(feature = "odbc")] - { - if let Some(odbc_options) = options.as_odbc_mut() { - set_custom_connect_options_odbc(odbc_options, config); - } + + if let Some(odbc_options) = options.as_odbc_mut() { + set_custom_connect_options_odbc(odbc_options, config); } } @@ -243,7 +240,6 @@ fn make_sqlite_fun(name: &str, f: fn(&str) -> String) -> Function { }) } -#[cfg(feature = "odbc")] fn set_custom_connect_options_odbc(odbc_options: &mut OdbcConnectOptions, config: &AppConfig) { // Allow fetching very large text fields when using ODBC by removing the max column size limit let batch_size = config.max_pending_rows.clamp(1, 1024); From b8d3f2812735ac81e0400d55b339df34f98756d0 Mon Sep 17 00:00:00 2001 From: lovasoa Date: Fri, 3 Oct 2025 23:07:12 +0200 Subject: [PATCH 14/26] windows build --- Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Cargo.toml b/Cargo.toml index 3f63683b..f4bba840 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -80,7 +80,7 @@ encoding_rs = "0.8.35" odbc-sys = { version = "0.27", features = ["iodbc", "static"] } [target.'cfg(any(target_os = "windows"))'.dependencies] -odbc-sys = { version = "0.27", features = ["iodbc", "static"] } +odbc-sys = { version = "0.27", features = [] } [patch.crates-io] odbc-sys = { git = "https://github.com/sqlpage/odbc-sys", branch = "main" } # see https://github.com/pacman82/odbc-sys/pull/60 From 79502ff4d76a62cecc74881a62129ef96d643428 Mon Sep 17 00:00:00 2001 From: lovasoa Date: Fri, 3 Oct 2025 23:11:14 +0200 Subject: [PATCH 15/26] iodbc -> unixodbc --- Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Cargo.toml b/Cargo.toml index f4bba840..4a862c3e 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -77,7 +77,7 @@ openidconnect = { version = "4.0.0", default-features = false } encoding_rs = "0.8.35" [target.'cfg(any(target_os = "linux", target_os = "macos"))'.dependencies] -odbc-sys = { version = "0.27", features = ["iodbc", "static"] } +odbc-sys = { version = "0.27", features = ["static"] } [target.'cfg(any(target_os = "windows"))'.dependencies] odbc-sys = { version = "0.27", features = [] } From 95315f68e13b1762a146984c1c76ec10567f41a6 Mon Sep 17 00:00:00 2001 From: lovasoa Date: Fri, 3 Oct 2025 23:52:23 +0200 Subject: [PATCH 16/26] Update ODBC driver configuration in CI and docker-compose for PostgreSQL --- .github/workflows/ci.yml | 8 +++----- docker-compose.yml | 1 + 2 files changed, 4 insertions(+), 5 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 0d7e474c..9e022c31 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -58,17 +58,15 @@ jobs: db_url: "mssql://root:Password123!@127.0.0.1/sqlpage" - database: odbc container: postgres - db_url: "Driver={PostgreSQL Unicode};Server=127.0.0.1;Port=5432;Database=sqlpage;UID=root;PWD=Password123!" + db_url: "Driver=/usr/lib64/psqlodbcw.so;Server=127.0.0.1;Port=5432;Database=sqlpage;UID=root;PWD=Password123!" setup_odbc: true steps: - uses: actions/checkout@v4 - name: Set up cargo cache uses: Swatinem/rust-cache@9d47c6ad4b02e050fd481d890b2ea34778fd09d6 - - name: Setup ODBC for testing + - name: Install PostgreSQL ODBC driver if: matrix.setup_odbc - run: | - # Install PostgreSQL ODBC driver (automatically registers the driver) - sudo apt-get install -y odbc-postgresql + run: sudo apt-get install -y odbc-postgresql - name: Start database container run: docker compose up --wait ${{ matrix.container }} - name: Show container logs diff --git a/docker-compose.yml b/docker-compose.yml index 0d92fb83..b67355d9 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -4,6 +4,7 @@ # DATABASE_URL='postgres://root:Password123!@localhost/sqlpage' # DATABASE_URL='mssql://root:Password123!@localhost/sqlpage' # DATABASE_URL='mysql://root:Password123!@localhost/sqlpage' +# DATABASE_URL='Driver={/usr/lib64/psqlodbcw.so};Server=127.0.0.1;Port=5432;Database=sqlpage;UID=root;PWD=Password123!' # Run for instance: # docker compose up postgres From 8b858015a88661f6a3a4ea4c777d6845a6a2f0c5 Mon Sep 17 00:00:00 2001 From: lovasoa Date: Sat, 4 Oct 2025 00:18:33 +0200 Subject: [PATCH 17/26] Update PostgreSQL ODBC driver configuration in CI workflow --- .github/workflows/ci.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 9e022c31..14d4da2a 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -58,7 +58,7 @@ jobs: db_url: "mssql://root:Password123!@127.0.0.1/sqlpage" - database: odbc container: postgres - db_url: "Driver=/usr/lib64/psqlodbcw.so;Server=127.0.0.1;Port=5432;Database=sqlpage;UID=root;PWD=Password123!" + db_url: "Driver=psqlodbcw.so;Server=127.0.0.1;Port=5432;Database=sqlpage;UID=root;PWD=Password123!" setup_odbc: true steps: - uses: actions/checkout@v4 From 3bb75133ea6d092c507a261ee7075cfc202f2ca7 Mon Sep 17 00:00:00 2001 From: lovasoa Date: Sat, 4 Oct 2025 00:25:26 +0200 Subject: [PATCH 18/26] update driver path --- .github/workflows/ci.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 14d4da2a..4cb7cf9d 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -58,7 +58,7 @@ jobs: db_url: "mssql://root:Password123!@127.0.0.1/sqlpage" - database: odbc container: postgres - db_url: "Driver=psqlodbcw.so;Server=127.0.0.1;Port=5432;Database=sqlpage;UID=root;PWD=Password123!" + db_url: "Driver=/usr/lib/x86_64-linux-gnu/odbc;Server=127.0.0.1;Port=5432;Database=sqlpage;UID=root;PWD=Password123!" setup_odbc: true steps: - uses: actions/checkout@v4 From 5fb17c65d0eefb509021cacb6ae7302dd78ad654 Mon Sep 17 00:00:00 2001 From: lovasoa Date: Sat, 4 Oct 2025 00:25:52 +0200 Subject: [PATCH 19/26] update driver path --- .github/workflows/ci.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 4cb7cf9d..9c53ec3c 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -58,7 +58,7 @@ jobs: db_url: "mssql://root:Password123!@127.0.0.1/sqlpage" - database: odbc container: postgres - db_url: "Driver=/usr/lib/x86_64-linux-gnu/odbc;Server=127.0.0.1;Port=5432;Database=sqlpage;UID=root;PWD=Password123!" + db_url: "Driver=/usr/lib/x86_64-linux-gnu/odbc/psqlodbcw.so;Server=127.0.0.1;Port=5432;Database=sqlpage;UID=root;PWD=Password123!" setup_odbc: true steps: - uses: actions/checkout@v4 From a7ff6d6c12efe5a3bddb4360365aede90150e0d6 Mon Sep 17 00:00:00 2001 From: lovasoa Date: Sat, 4 Oct 2025 20:23:42 +0200 Subject: [PATCH 20/26] Update ODBC dependency configuration and CI workflow to use all features --- .github/workflows/ci.yml | 6 +----- .github/workflows/release.yml | 6 +----- Cargo.toml | 9 +++++---- 3 files changed, 7 insertions(+), 14 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 9c53ec3c..e326dfa8 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -27,10 +27,6 @@ jobs: - uses: actions/checkout@v4 - run: npm ci - run: npm test - - name: Install ODBC dependencies - run: | - sudo apt-get update - sudo apt-get install -y unixodbc-dev freetds-dev - name: Set up cargo cache uses: Swatinem/rust-cache@9d47c6ad4b02e050fd481d890b2ea34778fd09d6 - run: cargo fmt --all -- --check @@ -74,7 +70,7 @@ jobs: run: docker compose logs ${{ matrix.container }} - name: Run tests against ${{ matrix.database }} timeout-minutes: 5 - run: cargo test + run: cargo test --all-features env: DATABASE_URL: ${{ matrix.db_url }} RUST_BACKTRACE: 1 diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 97dcf3ec..80420551 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -83,10 +83,6 @@ jobs: container: quay.io/pypa/manylinux_2_28_x86_64 steps: - uses: actions/checkout@v4 - - name: Install ODBC dependencies - run: | - yum update -y - yum install -y unixODBC-devel freetds-devel - name: Install Rust toolchain uses: dtolnay/rust-toolchain@stable with: @@ -94,7 +90,7 @@ jobs: - name: Set up cargo cache uses: Swatinem/rust-cache@9d47c6ad4b02e050fd481d890b2ea34778fd09d6 - name: Build - run: cargo build --profile superoptimized --locked --target x86_64-unknown-linux-gnu + run: cargo build --profile superoptimized --locked --target x86_64-unknown-linux-gnu --all-features - uses: actions/upload-artifact@v4 with: name: sqlpage ubuntu-latest diff --git a/Cargo.toml b/Cargo.toml index 4a862c3e..ab4c2931 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -75,12 +75,13 @@ clap = { version = "4.5.17", features = ["derive"] } tokio-util = "0.7.12" openidconnect = { version = "4.0.0", default-features = false } encoding_rs = "0.8.35" +odbc-sys = { version = "0.27", features = [], optional = false } -[target.'cfg(any(target_os = "linux", target_os = "macos"))'.dependencies] -odbc-sys = { version = "0.27", features = ["static"] } -[target.'cfg(any(target_os = "windows"))'.dependencies] -odbc-sys = { version = "0.27", features = [] } +[features] +default = [] +odbc-static = ["odbc-sys/static"] + [patch.crates-io] odbc-sys = { git = "https://github.com/sqlpage/odbc-sys", branch = "main" } # see https://github.com/pacman82/odbc-sys/pull/60 From 719937beb1bd17064ee518d7303547b1c7812937 Mon Sep 17 00:00:00 2001 From: lovasoa Date: Sat, 4 Oct 2025 20:34:07 +0200 Subject: [PATCH 21/26] remove rpath --- .cargo/config.toml | 9 --------- 1 file changed, 9 deletions(-) diff --git a/.cargo/config.toml b/.cargo/config.toml index bf15dccf..9247b719 100644 --- a/.cargo/config.toml +++ b/.cargo/config.toml @@ -8,12 +8,3 @@ LIBSQLITE3_FLAGS = "-DSQLITE_ENABLE_MATH_FUNCTIONS" [build] rustflags = [] - -[target.x86_64-unknown-linux-gnu] -rustflags = ["-C", "link-arg=-Wl,-rpath=$ORIGIN/sqlpage:$ORIGIN/sqlpage/drivers"] - -[target.aarch64-unknown-linux-gnu] -rustflags = ["-C", "link-arg=-Wl,-rpath=$ORIGIN/sqlpage:$ORIGIN/sqlpage/drivers"] - -[target.armv7-unknown-linux-gnueabihf] -rustflags = ["-C", "link-arg=-Wl,-rpath=$ORIGIN/sqlpage:$ORIGIN/sqlpage/drivers"] From c5c03346ff04120610320dc084c23a39b5f47768 Mon Sep 17 00:00:00 2001 From: lovasoa Date: Sat, 4 Oct 2025 20:34:38 +0200 Subject: [PATCH 22/26] contributing --- CONTRIBUTING.md | 43 ++++++++++++++++++------------------------- 1 file changed, 18 insertions(+), 25 deletions(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index d82f0f2b..e6ad47db 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -39,37 +39,22 @@ The resulting executable will be in `target/release/sqlpage`. ### ODBC build modes -SQLPage supports three additive build modes for ODBC. Choose the mode via Cargo features: - -- Dynamic ODBC (default): - - Works on Linux, macOS and Windows (Windows has ODBC built-in). - - Command: - ```bash - cargo build # or: cargo build --features odbc-dynamic - ``` -- Static ODBC (Linux only): - - Statically links the ODBC driver manager; simplifies distribution. - - Command: - ```bash - cargo build --features odbc-static - ``` -- No ODBC: - - Disables ODBC support entirely. - - Command: - ```bash - cargo build --no-default-features - ``` - -Notes: -- When cross-compiling in Docker, headers come from the base image (e.g. `unixodbc-dev`). -- Runtime rpath on Linux includes `$ORIGIN/sqlpage:$ORIGIN/lib` so you can colocate drivers next to the binary when needed. +SQLPage can either be built with an integrated odbc driver manager (static linking), +or depend on having one already installed on the system where it is running (dynamic linking). + +- Dynamic ODBC (default): `cargo build` +- Static ODBC (Linux and MacOS only): `cargo build --features odbc-static` + +Windows comes with ODBC pre-installed; SQLPage cannot statically link to the unixODBC driver manager on windows. ## Code Style and Linting ### Rust + - Use `cargo fmt` to format your Rust code - Run `cargo clippy` to catch common mistakes and improve code quality - All code must pass the following checks: + ```bash cargo fmt --all -- --check cargo clippy @@ -82,6 +67,7 @@ We use Biome for linting and formatting of the frontend code. ```bash npx @biomejs/biome check . ``` + This will check the entire codebase (html, css, js). ## Testing @@ -107,6 +93,7 @@ cargo test ``` ### End-to-End Tests + We use Playwright for end-to-end testing of dynamic frontend features. Tests are located in [`tests/end-to-end/`](./tests/end-to-end/). Key areas covered include: @@ -130,6 +117,7 @@ npm run test ## Documentation ### Component Documentation + When adding new components, comprehensive documentation is required. Example from a component documentation: ```sql @@ -137,7 +125,7 @@ INSERT INTO component(name, icon, description, introduced_in_version) VALUES ('component_name', 'icon_name', 'Description of the component', 'version'); -- Document all parameters -INSERT INTO parameter(component, name, description, type, top_level, optional) +INSERT INTO parameter(component, name, description, type, top_level, optional) VALUES ('component_name', 'param_name', 'param_description', 'TEXT|BOOLEAN|NUMBER|JSON|ICON|COLOR', false, true); -- Include usage examples @@ -154,6 +142,7 @@ If you are editing an existing component, edit the existing sql documentation fi If you are adding a new component, add a new sql file in the folder, and add the appropriate insert statements above. ### SQLPage Function Documentation + When adding new SQLPage functions, document them using a SQL migrations. Example structure: ```sql @@ -195,6 +184,7 @@ VALUES ( ``` Key elements to include in function documentation: + - Clear description of the function's purpose - Version number where the function was introduced - Appropriate icon @@ -206,11 +196,13 @@ Key elements to include in function documentation: ## Pull Request Process 1. Create a new branch for your feature/fix: + ```bash git checkout -b feature/your-feature-name ``` 2. Make your changes, ensuring: + - All tests pass - Code is properly formatted - New features are documented @@ -229,6 +221,7 @@ git checkout -b feature/your-feature-name ## Release Process Releases are automated when pushing tags that match the pattern `v*` (e.g., `v1.0.0`). The CI pipeline will: + - Build and test the code - Create Docker images for multiple architectures - Push images to Docker Hub From 6ceeb81ccba5d930c5aa0c9a77579b8b3ad528eb Mon Sep 17 00:00:00 2001 From: lovasoa Date: Sat, 4 Oct 2025 20:36:09 +0200 Subject: [PATCH 23/26] test with static odbc --- .github/workflows/ci.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index e326dfa8..87bae98b 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -31,7 +31,7 @@ jobs: uses: Swatinem/rust-cache@9d47c6ad4b02e050fd481d890b2ea34778fd09d6 - run: cargo fmt --all -- --check - run: cargo clippy --all-targets --all-features -- -D warnings - - run: cargo test + - run: cargo test --all-features - name: Upload Linux binary uses: actions/upload-artifact@v4 with: From 3cad946738339608e945ca538a653ac27a4838a7 Mon Sep 17 00:00:00 2001 From: lovasoa Date: Sat, 4 Oct 2025 21:11:16 +0200 Subject: [PATCH 24/26] documentation --- .github/workflows/release.yml | 6 ++++-- README.md | 8 ++++++-- 2 files changed, 10 insertions(+), 4 deletions(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 80420551..e67936fd 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -24,8 +24,10 @@ jobs: - os: windows-latest binary_extension: .exe target: x86_64-pc-windows-msvc + features: "" - os: macos-latest target: x86_64-apple-darwin + features: "odbc-static" steps: - uses: actions/checkout@v4 - name: Install Rust toolchain @@ -35,7 +37,7 @@ jobs: - name: Set up cargo cache uses: Swatinem/rust-cache@9d47c6ad4b02e050fd481d890b2ea34778fd09d6 - name: Build - run: cargo build --profile superoptimized --locked --target ${{ matrix.target }} + run: cargo build --profile superoptimized --locked --target ${{ matrix.target }} --features "${{ matrix.features }}" - name: Upload unsigned Windows artifact if: matrix.os == 'windows-latest' id: upload_unsigned @@ -90,7 +92,7 @@ jobs: - name: Set up cargo cache uses: Swatinem/rust-cache@9d47c6ad4b02e050fd481d890b2ea34778fd09d6 - name: Build - run: cargo build --profile superoptimized --locked --target x86_64-unknown-linux-gnu --all-features + run: cargo build --profile superoptimized --locked --target x86_64-unknown-linux-gnu --features "odbc-static" - uses: actions/upload-artifact@v4 with: name: sqlpage ubuntu-latest diff --git a/README.md b/README.md index 85e79475..f34df621 100644 --- a/README.md +++ b/README.md @@ -190,7 +190,9 @@ You can skip this section if you want to use one of the built-in database driver SQLPage supports ODBC connections to connect to databases that don't have native drivers, such as Oracle, Snowflake, BigQuery, IBM DB2, and many others. -On Linux, SQLPage supports both dynamic and static ODBC linking. The Docker image uses the system `unixODBC` (dynamic). Release binaries can be built with static ODBC linking by enabling the `odbc-static` feature. You still need to install or provide the database-specific ODBC driver for the database you want to connect to. SQLPage also searches for drivers in a `sqlpage/` directory placed next to the executable (rpath includes `$ORIGIN/sqlpage`). +On Linux, SQLPage supports both dynamic and static ODBC linking. The Docker image uses the system `unixODBC` (dynamic). +Linux and MacOS release binaries are built with a statically linked unixODBC. +You still need to install or provide the database-specific ODBC driver for the database you want to connect to. #### Install ODBC @@ -207,7 +209,9 @@ On Linux, SQLPage supports both dynamic and static ODBC linking. The Docker imag #### Connect to your database - - Find your [connection string](https://www.connectionstrings.com/). It will look like this: `Driver={SnowflakeDSIIDriver};Server=xyz.snowflakecomputing.com;Database=MY_DB;Schema=PUBLIC;UID=my_user;PWD=my_password` + - Find your [connection string](https://www.connectionstrings.com/). + - It will look like this: `Driver=/opt/snowflake_odbc/lib/libSnowflake.so;Server=xyz.snowflakecomputing.com;Database=MY_DB;Schema=PUBLIC;UID=my_user;PWD=my_password` + - It must reference the path to the database driver you installed earlier, plus any connection parameter required by the driver itself. Follow the instructions from the driver's own documentation. - Use it in the [DATABASE_URL configuration option](./configuration.md) From e186f13d0def93a4e52f0e6fd2536bae93e81f56 Mon Sep 17 00:00:00 2001 From: lovasoa Date: Sat, 4 Oct 2025 21:22:12 +0200 Subject: [PATCH 25/26] instructions --- README.md | 6 ------ examples/official-site/your-first-sql-website/tutorial.md | 1 + 2 files changed, 1 insertion(+), 6 deletions(-) diff --git a/README.md b/README.md index f34df621..c25c8867 100644 --- a/README.md +++ b/README.md @@ -194,12 +194,6 @@ On Linux, SQLPage supports both dynamic and static ODBC linking. The Docker imag Linux and MacOS release binaries are built with a statically linked unixODBC. You still need to install or provide the database-specific ODBC driver for the database you want to connect to. -#### Install ODBC - - - On windows, it's installed by default. - - On linux, the driver manager is bundled with SQLPage; install only your database driver's package. - - On mac: `brew install unixodbc` - #### Install your ODBC database driver - [DuckDB](https://duckdb.org/docs/stable/clients/odbc/overview.html) diff --git a/examples/official-site/your-first-sql-website/tutorial.md b/examples/official-site/your-first-sql-website/tutorial.md index 79819136..6faaaf1c 100644 --- a/examples/official-site/your-first-sql-website/tutorial.md +++ b/examples/official-site/your-first-sql-website/tutorial.md @@ -91,6 +91,7 @@ Later, when you want to deploy your website online, you can switch back to a per - a PostgreSQL-compatible server with `postgres://user:password@host/database` ([see options](https://docs.rs/sqlx-oldapi/latest/sqlx_oldapi/postgres/struct.PgConnectOptions.html)), - a MySQL-compatible server with `mysql://user:password@host/database` ([see options](https://docs.rs/sqlx-oldapi/latest/sqlx_oldapi/mysql/struct.MySqlConnectOptions.html)), - a Microsoft SQL Server with `mssql://user:password@host/database` ([see options](https://docs.rs/sqlx-oldapi/latest/sqlx_oldapi/mssql/struct.MssqlConnectOptions.html#method.from_str), [note about named instances](https://github.com/sqlpage/SQLPage/issues/92)), +- any ODBC-compatible database like DuckDB, ClickHouse, Databricks, Snowflake, BigQuery, Oracle, Db2, and many more. See [ODBC database connection instructions](https://github.com/sqlpage/SQLPage#odbc-setup). > If `user` or `password` **contains special characters**, you should [**percent-encode**](https://en.wikipedia.org/wiki/Percent-encoding) them. > From 2c7cb9072935d143cc4a24b41f4a9eda50a0db4f Mon Sep 17 00:00:00 2001 From: lovasoa Date: Sat, 4 Oct 2025 22:31:12 +0200 Subject: [PATCH 26/26] add compatibility section to homepage --- AGENTS.md | 5 +- examples/official-site/assets/db-bigquery.svg | 1 + .../official-site/assets/db-clickhouse.svg | 1 + .../official-site/assets/db-databricks.svg | 1 + examples/official-site/assets/db-db2.svg | 1 + examples/official-site/assets/db-duckdb.svg | 1 + examples/official-site/assets/db-mysql.svg | 1 + examples/official-site/assets/db-odbc.svg | 1 + examples/official-site/assets/db-oracle.svg | 1 + examples/official-site/assets/db-postgres.svg | 1 + .../official-site/assets/db-snowflake.svg | 1 + examples/official-site/assets/db-sqlite.svg | 1 + .../official-site/assets/db-sqlserver.svg | 1 + .../sqlpage/templates/shell-home.handlebars | 391 +++++++++++++++++- 14 files changed, 404 insertions(+), 4 deletions(-) create mode 100644 examples/official-site/assets/db-bigquery.svg create mode 100644 examples/official-site/assets/db-clickhouse.svg create mode 100644 examples/official-site/assets/db-databricks.svg create mode 100644 examples/official-site/assets/db-db2.svg create mode 100644 examples/official-site/assets/db-duckdb.svg create mode 100644 examples/official-site/assets/db-mysql.svg create mode 100644 examples/official-site/assets/db-odbc.svg create mode 100644 examples/official-site/assets/db-oracle.svg create mode 100644 examples/official-site/assets/db-postgres.svg create mode 100644 examples/official-site/assets/db-snowflake.svg create mode 100644 examples/official-site/assets/db-sqlite.svg create mode 100644 examples/official-site/assets/db-sqlserver.svg diff --git a/AGENTS.md b/AGENTS.md index e629980f..b7c66435 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -3,16 +3,17 @@ HTML streamed to client ## Validation +### When working on rust code Mandatory formatting (rust): `cargo fmt --all` - Mandatory linting: `cargo clippy --all-targets --all-features -- -D warnings` +### When working on css or js Frontend formatting: `npm run format` More about testing: see [github actions](./.github/workflows/ci.yml). Project structure: see [contribution guide](./CONTRIBUTING.md) -Don’t reformat unrelated files. Always run tests/lints/format before stopping when you changed code. +NEVER reformat/lint/touch files unrelated to your task. Always run tests/lints/format before stopping when you changed code. ### Testing diff --git a/examples/official-site/assets/db-bigquery.svg b/examples/official-site/assets/db-bigquery.svg new file mode 100644 index 00000000..21fbe250 --- /dev/null +++ b/examples/official-site/assets/db-bigquery.svg @@ -0,0 +1 @@ +Google BigQuery diff --git a/examples/official-site/assets/db-clickhouse.svg b/examples/official-site/assets/db-clickhouse.svg new file mode 100644 index 00000000..82d7d981 --- /dev/null +++ b/examples/official-site/assets/db-clickhouse.svg @@ -0,0 +1 @@ +ClickHouse diff --git a/examples/official-site/assets/db-databricks.svg b/examples/official-site/assets/db-databricks.svg new file mode 100644 index 00000000..129ea2ac --- /dev/null +++ b/examples/official-site/assets/db-databricks.svg @@ -0,0 +1 @@ +Databricks diff --git a/examples/official-site/assets/db-db2.svg b/examples/official-site/assets/db-db2.svg new file mode 100644 index 00000000..02811396 --- /dev/null +++ b/examples/official-site/assets/db-db2.svg @@ -0,0 +1 @@ +IBM \ No newline at end of file diff --git a/examples/official-site/assets/db-duckdb.svg b/examples/official-site/assets/db-duckdb.svg new file mode 100644 index 00000000..7e590871 --- /dev/null +++ b/examples/official-site/assets/db-duckdb.svg @@ -0,0 +1 @@ +DuckDB diff --git a/examples/official-site/assets/db-mysql.svg b/examples/official-site/assets/db-mysql.svg new file mode 100644 index 00000000..e1606ffd --- /dev/null +++ b/examples/official-site/assets/db-mysql.svg @@ -0,0 +1 @@ +MySQL diff --git a/examples/official-site/assets/db-odbc.svg b/examples/official-site/assets/db-odbc.svg new file mode 100644 index 00000000..b364b5a6 --- /dev/null +++ b/examples/official-site/assets/db-odbc.svg @@ -0,0 +1 @@ +ODBCODBC diff --git a/examples/official-site/assets/db-oracle.svg b/examples/official-site/assets/db-oracle.svg new file mode 100644 index 00000000..1e41072f --- /dev/null +++ b/examples/official-site/assets/db-oracle.svg @@ -0,0 +1 @@ +Oracle \ No newline at end of file diff --git a/examples/official-site/assets/db-postgres.svg b/examples/official-site/assets/db-postgres.svg new file mode 100644 index 00000000..d7ccd9e3 --- /dev/null +++ b/examples/official-site/assets/db-postgres.svg @@ -0,0 +1 @@ +PostgreSQL diff --git a/examples/official-site/assets/db-snowflake.svg b/examples/official-site/assets/db-snowflake.svg new file mode 100644 index 00000000..b62af544 --- /dev/null +++ b/examples/official-site/assets/db-snowflake.svg @@ -0,0 +1 @@ +Snowflake diff --git a/examples/official-site/assets/db-sqlite.svg b/examples/official-site/assets/db-sqlite.svg new file mode 100644 index 00000000..e6e77901 --- /dev/null +++ b/examples/official-site/assets/db-sqlite.svg @@ -0,0 +1 @@ +SQLite diff --git a/examples/official-site/assets/db-sqlserver.svg b/examples/official-site/assets/db-sqlserver.svg new file mode 100644 index 00000000..ecb4c222 --- /dev/null +++ b/examples/official-site/assets/db-sqlserver.svg @@ -0,0 +1 @@ +Microsoft SQL Server \ No newline at end of file diff --git a/examples/official-site/sqlpage/templates/shell-home.handlebars b/examples/official-site/sqlpage/templates/shell-home.handlebars index f5839f09..095c6b73 100644 --- a/examples/official-site/sqlpage/templates/shell-home.handlebars +++ b/examples/official-site/sqlpage/templates/shell-home.handlebars @@ -495,7 +495,7 @@ font-weight: 500; background: var(--gradient-primary); color: rgba(255, 255, 255, 0.95); - text-decoration: none; + text-decoration: none !important; border-radius: 12px; transition: all var(--transition-slow); border: 1px solid rgba(255, 255, 255, 0.1); @@ -803,6 +803,343 @@ } } + .compat-section { + position: relative; + background: radial-gradient(circle at 20% 20%, rgba(88, 125, 255, 0.18), transparent 50%), radial-gradient(circle at 80% 10%, rgba(255, 130, 255, 0.18), transparent 55%), linear-gradient(160deg, rgba(10, 19, 44, 0.95), rgba(7, 12, 26, 0.92)); + overflow: hidden; + isolation: isolate; + } + + .compat-section::before, + .compat-section::after { + content: ""; + position: absolute; + inset: 0; + pointer-events: none; + } + + .compat-section::before { + background-image: radial-gradient(2px 2px at 25px 35px, rgba(255, 255, 255, 0.6), transparent), radial-gradient(1px 1px at 120px 80px, rgba(255, 255, 255, 0.4), transparent), radial-gradient(1.5px 1.5px at 220px 140px, rgba(255, 255, 255, 0.55), transparent), radial-gradient(2px 2px at 320px 60px, rgba(255, 255, 255, 0.5), transparent), radial-gradient(1px 1px at 420px 180px, rgba(255, 255, 255, 0.4), transparent), radial-gradient(1.5px 1.5px at 520px 90px, rgba(255, 255, 255, 0.5), transparent); + background-size: 240px 220px; + opacity: 0.45; + animation: twinkle 14s linear infinite; + } + + .compat-section::after { + background: radial-gradient(450px 450px at 80% 110%, rgba(63, 104, 255, 0.35), transparent 70%); + } + + .compat-layout { + display: grid; + grid-template-columns: minmax(400px, 1fr) minmax(450px, 1.2fr); + gap: var(--gap-lg); + padding-top: calc(var(--container-padding) * 1.5); + padding-bottom: calc(var(--container-padding) * 1.5); + position: relative; + z-index: 1; + } + + .compat-intro { + display: flex; + flex-direction: column; + gap: 1.5rem; + } + + .compat-eyebrow { + font-size: 0.85rem; + letter-spacing: 0.3em; + text-transform: uppercase; + color: rgba(173, 188, 255, 0.75); + } + + .compat-intro h2 { + font-size: 3.2rem; + line-height: 1.1; + color: rgba(229, 237, 255, 0.94); + } + + .compat-intro p { + font-size: 1.15rem; + color: rgba(209, 219, 255, 0.78); + max-width: 90%; + } + + .compat-highlights { + display: flex; + flex-wrap: wrap; + gap: 0.75rem; + } + + .compat-pill { + padding: 0.45rem 0.9rem; + border-radius: 999px; + background: rgba(99, 118, 255, 0.12); + border: 1px solid rgba(143, 162, 255, 0.25); + color: rgba(207, 221, 255, 0.78); + font-size: 0.85rem; + backdrop-filter: blur(6px); + } + + .compat-showcase { + position: relative; + display: flex; + flex-direction: column; + align-items: center; + gap: 2rem; + width: 100%; + min-width: 0; + } + + .compat-orbit { + position: relative; + width: 320px; + height: 320px; + margin: 0 auto; + border-radius: 50%; + background: radial-gradient(circle, rgba(64, 84, 182, 0.35), rgba(19, 27, 51, 0.4)); + box-shadow: inset 0 0 40px rgba(127, 162, 255, 0.2), 0 20px 60px rgba(6, 12, 32, 0.7); + } + + .compat-orbit::before { + content: ""; + position: absolute; + inset: 14px; + border-radius: 50%; + border: 1px dashed rgba(162, 186, 255, 0.4); + animation: orbitSpin 26s linear infinite; + } + + .compat-orbit::after { + content: ""; + position: absolute; + inset: 44px; + border-radius: 50%; + border: 1px solid rgba(86, 112, 221, 0.2); + filter: blur(0.3px); + } + + .compat-core { + position: absolute; + width: 160px; + height: 160px; + border-radius: 50%; + background: radial-gradient(circle at 30% 30%, rgba(35, 46, 98, 0.95), rgba(20, 30, 70, 0.9)); + box-shadow: 0 0 40px rgba(150, 178, 255, 0.32); + display: flex; + align-items: center; + justify-content: center; + text-align: center; + padding: 1rem; + color: rgba(229, 236, 255, 0.95); + font-size: 0.9rem; + line-height: 1.3; + backdrop-filter: blur(6px); + margin: auto; + top: 50%; + left: 50%; + transform: translate(-50%, -50%); + } + + .compat-node { + position: absolute; + width: 84px; + height: 84px; + border-radius: 18px; + background: rgba(10, 17, 36, 0.72); + box-shadow: 0 12px 28px rgba(2, 6, 18, 0.6), inset 0 0 24px rgba(137, 157, 255, 0.24); + display: flex; + align-items: center; + justify-content: center; + backdrop-filter: blur(10px); + border: 1px solid rgba(143, 166, 255, 0.35); + outline: 1px solid rgba(12, 18, 38, 0.6); + outline-offset: -4px; + } + + .compat-node img { + width: 54px; + height: 54px; + } + + .compat-node:nth-child(2) { + top: 6%; + left: 50%; + transform: translate(-50%, -50%); + animation: floatNode 7s ease-in-out infinite; + } + + .compat-node:nth-child(3) { + top: 50%; + right: 2%; + transform: translate(50%, -50%); + animation: floatNode 9s ease-in-out infinite; + } + + .compat-node:nth-child(4) { + bottom: 4%; + left: 50%; + transform: translate(-50%, 50%); + animation: floatNode 8s ease-in-out infinite; + } + + .compat-node:nth-child(5) { + top: 50%; + left: 2%; + transform: translate(-50%, -50%); + animation: floatNode 10s ease-in-out infinite; + } + + .compat-card { + background: rgba(12, 20, 42, 0.68); + border-radius: 18px; + padding: 1.8rem; + border: 1px solid rgba(110, 138, 238, 0.32); + box-shadow: 0 18px 40px rgba(4, 8, 22, 0.6); + backdrop-filter: blur(12px); + display: grid; + gap: 1.2rem; + width: 100%; + max-width: 500px; + min-width: 380px; + } + + .compat-card h3 { + font-size: 1.35rem; + color: rgba(228, 236, 255, 0.92); + } + + .compat-card p { + font-size: 0.98rem; + color: rgba(204, 214, 255, 0.72); + line-height: 1.6; + } + + .compat-logos { + display: grid; + grid-template-columns: repeat(4, 1fr); + gap: 0.75rem; + } + + .compat-logos .db-logo { + display: flex; + align-items: center; + justify-content: center; + border-radius: 12px; + padding: 0.7rem; + background: rgba(19, 30, 58, 0.9); + border: 1px solid rgba(118, 140, 233, 0.25); + position: relative; + overflow: hidden; + } + + .compat-logos img { + width: 44px; + height: 44px; + } + + .compat-badges { + display: flex; + flex-wrap: wrap; + gap: 0.6rem 0.75rem; + } + + .compat-badge { + padding: 0.35rem 0.75rem; + border-radius: 999px; + background: rgba(57, 76, 162, 0.32); + color: rgba(225, 232, 255, 0.78); + font-size: 0.82rem; + } + + @keyframes twinkle { + 0% { + opacity: 0.4; + transform: scale(1) translate3d(0, 0, 0); + } + + 50% { + opacity: 0.75; + transform: scale(1.05) translate3d(-2%, -3%, 0); + } + + 100% { + opacity: 0.4; + transform: scale(1) translate3d(0, 0, 0); + } + } + + @keyframes orbitSpin { + 0% { + transform: rotate(0deg); + } + + 100% { + transform: rotate(360deg); + } + } + + @keyframes floatNode { + 0% { + transform: translate3d(0, 0, 0); + } + + 50% { + transform: translate3d(0, -10px, 0); + } + + 100% { + transform: translate3d(0, 0, 0); + } + } + + @media (max-width: 968px) { + .compat-layout { + grid-template-columns: 1fr; + text-align: center; + } + + .compat-intro p { + max-width: 100%; + } + + .compat-highlights { + justify-content: center; + } + + .compat-orbit { + width: 260px; + height: 260px; + } + + .compat-node { + width: 74px; + height: 74px; + } + + .compat-node img { + width: 46px; + height: 46px; + } + + .compat-logos { + grid-template-columns: repeat(3, 1fr); + } + } + + @media (max-width: 640px) { + .compat-intro h2 { + font-size: 2.6rem; + } + + .compat-logos { + grid-template-columns: repeat(2, 1fr); + } + + .compat-card { + padding: 1.4rem; + } + } + /* FOOTER STYLES */ footer { background: linear-gradient(135deg, @@ -1041,7 +1378,7 @@
-

More scalable than a spreadsheet

+

More scalable than a spreadsheet

SQL queries sort, filter, and aggregate millions of rows in milliseconds. No more slow spreadsheet formulas or memory limitations. Your app remains smooth and responsive even @@ -1074,6 +1411,56 @@

+
+
+
+
Database Compatibility
+

Works with your database

+

SQLPage connects to the database engine you already rely on today. + Keep your data in place and surface it through a + friendly interface that stays in sync. + If you don't have a DB yet, SQLPage comes with a built-in query engine. +

+
+
SQLite built-in
+
MySQL & MariaDB
+
PostgreSQL family
+
Microsoft SQL Server
+
ODBC bridge
+
+
+
+
+
+ Native connectors +
+
SQLite
+
MySQL
+
PostgreSQL
+
Microsoft SQL Server
+
+
+

Wherever your data lives

+

Through ODBC you can plug SQLPage into any warehouses and enterprise engines.

+
+ + + + + + + + +
+
+ No data copy + Streams query results +
+
+
+
+
+