diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 10a314c6..8cef3410 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -201,9 +201,15 @@ jobs: run: | base="${BASE_REF:+origin/$BASE_REF}" base="${base:-HEAD^}" + # The packages were renamed to what crates.io publishes them as, + # and a baseline from before that has no zudb to compare with. + if ! git show "$base:crates/zu/Cargo.toml" | grep -qx 'name = "zudb"'; then + echo "the baseline has no package named zudb, so there is nothing to compare against" + exit 0 + fi perl -0pi -e 's/ pub fn epoch\(&self\) -> u64 \{/ pub fn epoch_renamed(&self) -> u64 {/' crates/zu/src/session.rs set +e - cargo semver-checks check-release -p zu --baseline-rev "$base" --release-type patch + cargo semver-checks check-release -p zudb --baseline-rev "$base" --release-type patch rc=$? set -e git checkout -- crates/zu/src/session.rs @@ -214,14 +220,20 @@ jobs: run: | base="${BASE_REF:+origin/$BASE_REF}" base="${base:-HEAD^}" + # The packages were renamed to what crates.io publishes them as, + # and a baseline from before that has no zudb to compare with. + if ! git show "$base:crates/zu/Cargo.toml" | grep -qx 'name = "zudb"'; then + echo "the baseline has no package named zudb, so there is nothing to compare against" + exit 0 + fi was=$(git show "$base:Cargo.toml" | sed -n 's/^version = "\(.*\)"/\1/p' | head -1) now=$(sed -n 's/^version = "\(.*\)"/\1/p' Cargo.toml | head -1) if [ "$was" != "$now" ]; then echo "version moved from $was to $now, so a break here is one that was declared" - cargo semver-checks check-release -p zu -p zu-corpus --baseline-rev "$base" --release-type patch || true + cargo semver-checks check-release -p zudb -p zudb-corpus --baseline-rev "$base" --release-type patch || true exit 0 fi - cargo semver-checks check-release -p zu -p zu-corpus --baseline-rev "$base" --release-type patch + cargo semver-checks check-release -p zudb -p zudb-corpus --baseline-rev "$base" --release-type patch # The tool builds the baseline in a checkout of its own under # target, and takes it away again while it works. The cache action # walks target when the job ends, and a directory that went while @@ -232,6 +244,20 @@ jobs: if: always() run: rm -rf target/semver-checks + # Every crate the release uploads, packaged and built from its own + # tarball the way crates.io will build it. A path reaching outside a + # crate, a file the package leaves behind, or a dependency without a + # version all build fine in the workspace and fail only here, and + # finding that out on the day of a release is finding it out after + # the first crates are already up. + package: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v7 + - uses: ./.github/actions/rust + - uses: Swatinem/rust-cache@v2 + - run: cargo publish --workspace --locked --dry-run + deny: runs-on: ubuntu-latest steps: @@ -290,7 +316,7 @@ jobs: # the differential suite compares the two executors with the whole # engine on that side of the choice. - if: matrix.os == 'ubuntu-latest' - run: ZU_POINT_READS=1 cargo test -p zu --test differential + run: ZU_POINT_READS=1 cargo test -p zudb --test differential membudget: runs-on: ubuntu-latest @@ -298,7 +324,7 @@ jobs: - uses: actions/checkout@v7 - uses: ./.github/actions/rust - uses: Swatinem/rust-cache@v2 - - run: cargo build --release -p zu-cli + - run: cargo build --release -p zudb-cli # The G6 budget is 32 MiB total for an embedded reader. The build # and the write path run unconstrained; every read path then runs # under a 32 MiB address-space cap, so a decoder or open path that @@ -336,12 +362,12 @@ jobs: # this runs the roundtrip distribution sweep, which crosses every # encoder and decoder path at miri-sized inputs, rather than the # whole unit suite with its hundred-thousand-value columns. - - run: cargo +nightly miri test -p zu-encoding --test roundtrip + - run: cargo +nightly miri test -p zudb-encoding --test roundtrip # The vector layer carries the unsafe: arena allocation, the # MaybeUninit compare staging, and StrView. The lib tests plus one # differential compare run cross all of it at miri-sized inputs. - - run: cargo +nightly miri test -p zu-vector --lib - - run: cargo +nightly miri test -p zu-vector --test reference compare_i64_const + - run: cargo +nightly miri test -p zudb-vector --lib + - run: cargo +nightly miri test -p zudb-vector --test reference compare_i64_const check-asm: runs-on: ubuntu-latest @@ -351,7 +377,7 @@ jobs: - uses: Swatinem/rust-cache@v2 # The perf/11 tier 1 gate: every hot kernel that relies on # auto-vectorization must actually vectorize on this toolchain. - - run: cargo bench -p zu-vector --no-run + - run: cargo bench -p zudb-vector --no-run - run: bench/check_asm.sh cardinality: @@ -364,7 +390,7 @@ jobs: # the LDBC bench it needs no dataset and runs here. Holds the # q-error percentiles to bench/budgets.toml and fails on the # first ceiling the data walks through. - - run: ZU_GATE=1 cargo bench -p zu --bench cardinality + - run: ZU_GATE=1 cargo bench -p zudb --bench cardinality refusal: runs-on: ubuntu-latest @@ -377,7 +403,7 @@ jobs: # gate above, so it needs no dataset. The number it enforces is a # ratio between two paths timed in the same run on the same # machine, which is why it can gate on a shared runner at all. - - run: ZU_GATE=1 cargo bench -p zu --bench refuse + - run: ZU_GATE=1 cargo bench -p zudb --bench refuse tail: runs-on: ubuntu-latest @@ -391,7 +417,7 @@ jobs: # eight million edges each, in a few seconds. Like the two gates # above it enforces a ratio between numbers timed in one run on # one machine, so a shared runner cannot move it. - - run: ZU_GATE=1 cargo bench -p zu --bench tail + - run: ZU_GATE=1 cargo bench -p zudb --bench tail paths: runs-on: ubuntu-latest @@ -405,7 +431,7 @@ jobs: # them, so it builds in no time and still fails loudly if the # count goes back to being a walk. A ratio between two numbers # timed together, so a shared runner cannot move it. - - run: ZU_GATE=1 cargo bench -p zu --bench paths + - run: ZU_GATE=1 cargo bench -p zudb --bench paths fold: runs-on: ubuntu-latest @@ -420,7 +446,7 @@ jobs: # the list asks for a kilobyte a row, and neither number moves # with the machine or the load. The clock is a loose second gate, # since the walk in front of the fold is most of what it times. - - run: ZU_GATE=1 cargo bench -p zu --bench fold + - run: ZU_GATE=1 cargo bench -p zudb --bench fold relprops: runs-on: ubuntu-latest @@ -434,7 +460,7 @@ jobs: # second. Like the gates above it enforces a ratio between numbers # timed in one run on one machine, so a shared runner cannot move # it. - - run: ZU_GATE=1 cargo bench -p zu --bench relprops + - run: ZU_GATE=1 cargo bench -p zudb --bench relprops write: runs-on: ubuntu-latest @@ -454,7 +480,7 @@ jobs: # number is read against this host's own speed on a proxy loop, so # there is nothing to tell the bench about the box it is on. It # prints the calibration and the ceiling it enforced. - - run: ZU_GATE=1 cargo bench -p zu --bench write + - run: ZU_GATE=1 cargo bench -p zudb --bench write commit: runs-on: ubuntu-latest @@ -469,7 +495,7 @@ jobs: # catch is commits going back to a sync each, which reads as a # throughput that stops rising with the width and a latency that # starts rising with it. - - run: ZU_GATE=1 cargo bench -p zu --bench commit + - run: ZU_GATE=1 cargo bench -p zudb --bench commit connect: runs-on: ubuntu-latest @@ -483,7 +509,7 @@ jobs: # a ratio between two paths timed in one run on one machine, so a # shared runner cannot move it, and it is what stops the SDK from # growing a copy or a re-read on the hot path unnoticed. - - run: ZU_GATE=1 cargo bench -p zu --bench connect + - run: ZU_GATE=1 cargo bench -p zudb --bench connect append: runs-on: ubuntu-latest @@ -497,7 +523,7 @@ jobs: # run on one machine, and it is the appender's reason to exist # stated as a number, so a flush that stopped being a batch fails # here rather than being noticed by a user loading a million rows. - - run: ZU_GATE=1 cargo bench -p zu --bench append + - run: ZU_GATE=1 cargo bench -p zudb --bench append capi-result: runs-on: ubuntu-latest @@ -524,7 +550,7 @@ jobs: # process, so it is a ratio and a shared runner does not move it, # and it fails on exactly one defect: a copy coming back to the # path whose whole point is that there is not one. - - run: ZU_GATE=1 cargo bench -p zu-arrow --bench export + - run: ZU_GATE=1 cargo bench -p zudb-arrow --bench export cli: runs-on: ubuntu-latest @@ -538,7 +564,7 @@ jobs: # deliberately loose, since startup regresses in kind and not by # percent; the assertion that no help form costs twice the floor # runs on every machine and is the sensitive half. - - run: ZU_GATE=1 cargo bench -p zu-cli --bench cli + - run: ZU_GATE=1 cargo bench -p zudb-cli --bench cli corpus: runs-on: ubuntu-latest @@ -558,12 +584,12 @@ jobs: # because the cases are the contract and the engine catches up to # them. It is not allowed on a branch heading for a release, so # the gate lives here rather than in the runner's defaults. - - run: cargo run -p zu-cli --release -- corpus conformance/cases --strict + - run: cargo run -p zudb-cli --release -- corpus conformance/cases --strict # The reader is read by nine repositories on every CI run of each # of them, so its cost per case is asserted to stay linear as the # corpus grows. The bench fails rather than reporting when it # does not. - - run: cargo bench -p zu-corpus --bench corpus + - run: cargo bench -p zudb-corpus --bench corpus # The artifact eight other repositories consume, built here on # every pull request rather than for the first time on the day of # a release. `--check` writes nothing: what is being gated is that @@ -623,7 +649,7 @@ jobs: status=0 "$RUNNER_TEMP/runner" --dir "$RUNNER_TEMP/corpus" --strict \ conformance/cases/*.yaml > "$RUNNER_TEMP/c.txt" || status=$? - cargo run -q -p zu-cli --release -- corpus conformance/cases \ + cargo run -q -p zudb-cli --release -- corpus conformance/cases \ > "$RUNNER_TEMP/rust.txt" || true diff -u "$RUNNER_TEMP/rust.txt" "$RUNNER_TEMP/c.txt" cat "$RUNNER_TEMP/c.txt" @@ -633,7 +659,7 @@ jobs: # diff and not the status. "$RUNNER_TEMP/runner" --dir "$RUNNER_TEMP/corpus" \ conformance/c/wrong/wrong.yaml > "$RUNNER_TEMP/c-wrong.txt" || true - cargo run -q -p zu-cli --release -- corpus conformance/c/wrong \ + cargo run -q -p zudb-cli --release -- corpus conformance/c/wrong \ > "$RUNNER_TEMP/rust-wrong.txt" || true diff -u "$RUNNER_TEMP/rust-wrong.txt" "$RUNNER_TEMP/c-wrong.txt" exit $status @@ -647,7 +673,7 @@ jobs: - name: The C smoke test under the sanitizers run: | printf '1 2\n1 3\n2 3\n3 1\n' > "$RUNNER_TEMP/edges.txt" - cargo run -q -p zu-cli --release -- \ + cargo run -q -p zudb-cli --release -- \ copy "$RUNNER_TEMP/edges.txt" "$RUNNER_TEMP/smoke.zu1" cc -std=c99 -O1 -g -Wall -Wextra -Werror -pedantic \ -fsanitize=address,undefined -fno-sanitize-recover=all \ diff --git a/.github/workflows/conformance.yml b/.github/workflows/conformance.yml index 6ba1ecdd..c732675b 100644 --- a/.github/workflows/conformance.yml +++ b/.github/workflows/conformance.yml @@ -47,7 +47,7 @@ jobs: with: repository: tamnd/gql-compat path: gql-compat - - run: cargo build --release -p zu-cli + - run: cargo build --release -p zudb-cli - name: Build the harness run: go build -o "$RUNNER_TEMP/gql-compat" ./cmd/gql-compat working-directory: gql-compat @@ -90,7 +90,7 @@ jobs: with: repository: tamnd/gql-compat path: gql-compat - - run: cargo build --release -p zu-cli + - run: cargo build --release -p zudb-cli - name: Build the harness run: go build -o "$RUNNER_TEMP/gql-compat" ./cmd/gql-compat working-directory: gql-compat @@ -125,7 +125,7 @@ jobs: with: repository: tamnd/gql-compat path: gql-compat - - run: cargo build --release -p zu-cli + - run: cargo build --release -p zudb-cli - name: Build the harness run: go build -o "$RUNNER_TEMP/gql-compat" ./cmd/gql-compat working-directory: gql-compat diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 5403fafd..3ecbfcdc 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -5,8 +5,9 @@ name: release # it, and what is real here is deliberate. The build is the same matrix # every pull request runs, the assemble step gathers exactly the rows of # artifacts.toml, and the verify step reads the directory back against -# the same table. Every publish step is a no-op that says what it would -# do. +# the same table. Two of the publishes are real, crates.io and the +# GitHub release, and only on a tag. Every other publish step is a +# no-op that says what it would do. # # The ordering is the part worth having this early, because it is the # part that is expensive to discover late: crates.io lands before the @@ -37,12 +38,103 @@ concurrency: env: CARGO_TERM_COLOR: always +# Read only unless a job says otherwise. The one job that writes to the +# repository is the GitHub release, and the one that holds a credential +# is crates.io, which reads it from an environment rather than from the +# repository so no other job can. +permissions: + contents: read + jobs: + # A tag that does not pass this does not become a release. Everything + # here is cheaper to find out before the first upload than after it, + # because a version on crates.io cannot be taken back, only yanked. + verify: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v7 + - uses: ./.github/actions/rust + - name: The tag is the workspace version + if: github.event_name == 'push' + shell: bash + run: | + set -euo pipefail + tag="${{ github.ref_name }}" + have=$(cargo metadata --format-version 1 --no-deps | jq -r '.packages[] | select(.name == "zudb") | .version') + if [ "${tag#v}" != "$have" ]; then + echo "the tag $tag says ${tag#v} and Cargo.toml says $have" + exit 1 + fi + # The release notes are this section, so a tag without one is a + # release with nothing to say about itself. + - name: The changelog has the version + shell: bash + run: | + set -euo pipefail + tag="${{ inputs.version || github.ref_name }}" + if [ "${{ github.event_name }}" = push ] && ! grep -qx "## ${tag#v}" CHANGELOG.md; then + echo "CHANGELOG.md has no section for ${tag#v}" + exit 1 + fi + # Every crate packaged and built from its own tarball, the way + # crates.io will see it, before any of them is uploaded. The upload + # verifies each crate too, but only as it reaches it, and a crate + # that fails there leaves the ones before it published at a version + # the rest will never join. + - name: Every crate builds as published + run: cargo publish --workspace --locked --dry-run + # The seven tier 1 platforms, called rather than repeated, so a # release ships the artifacts CI has been building all along. libzu: + needs: verify uses: ./.github/workflows/libzu.yml + # Every crate without `publish = false`, in dependency order, which is + # zudb and the zudb-* crates under it and zudb-cli. They go first of + # the registries because everything else builds against them, and the + # GitHub release waits for them so that a release which exists is one + # `cargo add zudb` can get. + # + # The token is a secret of the crates-io environment, which only a + # `v*` tag can deploy to, so a branch, a pull request or a rehearsal + # never sees it. It is in the environment of one step and nothing + # prints it. No cache step: this job compiles each crate once, inside + # `cargo publish`, and never again. + # + # The first release is fourteen crates crates.io has never seen, at + # one every ten minutes after a burst of five, so the timeout is set + # for that. If it runs out nothing is lost: the script asks the index + # what is up and re-running the job carries on from there. + crates-io: + needs: verify + if: github.event_name == 'push' + runs-on: ubuntu-latest + timeout-minutes: 240 + environment: + name: crates-io + url: https://crates.io/crates/zudb + steps: + - uses: actions/checkout@v7 + - uses: ./.github/actions/rust + # Current tokens are `cio` and 32 characters. Anything else is a + # token revoked in 2020 or a value that picked up a quote or a + # newline on its way into the secret, and either is worth finding + # before the first upload. Only the verdict is printed. + - name: The token is shaped like a token + env: + CARGO_REGISTRY_TOKEN: ${{ secrets.CARGO_REGISTRY_TOKEN }} + shell: bash + run: | + if ! printf '%s' "${CARGO_REGISTRY_TOKEN:-}" | grep -Eq '^(cio)?[A-Za-z0-9]{32}$'; then + echo "CARGO_REGISTRY_TOKEN on the crates-io environment is missing or malformed" + exit 1 + fi + - name: Publish + env: + CARGO_REGISTRY_TOKEN: ${{ secrets.CARGO_REGISTRY_TOKEN }} + run: scripts/publish-crates.sh + assemble: needs: libzu runs-on: ubuntu-latest @@ -99,9 +191,19 @@ jobs: with: version: ${{ inputs.version || github.ref_name }} + # A rehearsal skips crates.io, and a job whose needs were skipped is + # skipped with them unless it says otherwise, so this says run when + # nothing failed. The steps that publish check for a tag themselves. publish: - needs: [assemble, conductor] + needs: [assemble, conductor, crates-io] + if: ${{ !failure() && !cancelled() }} runs-on: ubuntu-latest + permissions: + contents: write + # Provenance, so a downloaded archive can be checked against the + # workflow run and the commit that built it. + id-token: write + attestations: write steps: - uses: actions/checkout@v7 - uses: actions/download-artifact@v4 @@ -115,12 +217,38 @@ jobs: # will run, doing nothing. They are separate steps rather than one # loop because the run's own step list is then the ordering, which # is what a person reads when a release stops half way through. + - name: Attest the archives + if: github.event_name == 'push' + uses: actions/attest-build-provenance@v4 + with: + subject-path: dist/* + # The notes are the changelog section, written by hand, with + # GitHub's list of merged pull requests after it. Created once and + # uploaded over on a re-run, so running this job again ends where + # the first run would have. - name: GitHub release + if: github.event_name == 'push' + env: + GH_TOKEN: ${{ github.token }} + shell: bash run: | - echo "no-op: upload dist/ to the release for ${{ github.ref_name }}, signed and attested (dx/14 section 8, DX5)" + set -euo pipefail + tag="${{ github.ref_name }}" + awk -v want="## ${tag#v}" ' + $0 == want { inside = 1; next } + inside && /^## / { exit } + inside { print } + ' CHANGELOG.md > notes.md + test -s notes.md + if gh release view "$tag" >/dev/null 2>&1; then + gh release upload "$tag" dist/* --clobber + else + gh release create "$tag" dist/* --verify-tag --title "$tag" \ + --notes-file notes.md --generate-notes + fi - name: crates.io run: | - echo "no-op: publish zudb, zudb-async and zu-cli, first because everything else builds against them" + echo "done in the crates-io job before this one: zudb, the zudb-* crates under it, and zudb-cli" - name: PyPI run: | echo "no-op: publish the wheels zu-python built against these artifacts" @@ -142,4 +270,5 @@ jobs: - name: What this run did not do run: | echo "The dispatches to the eight repositories ran and did nothing, because none of them has a release workflow yet." + echo "What is real is crates.io and the GitHub release, and only on a tag." echo "Every publish above is idempotent when it is real, so a partial release is resumed and not restarted." diff --git a/CHANGELOG.md b/CHANGELOG.md new file mode 100644 index 00000000..671eb066 --- /dev/null +++ b/CHANGELOG.md @@ -0,0 +1,25 @@ +# Changelog + +Every release has a section here, and the section is the release notes: +the release workflow refuses a tag without one and copies it onto the +GitHub release. The crates are versioned together, so one number is the +version of all of them. + +## 0.0.1 + +The first release, and the first time zu is on crates.io. It is early: +the specification in `docs/` is complete and the engine behind it is +not, so this is for trying the API rather than for keeping data in. + +- `zudb` on crates.io is the embedded API, `cargo add zudb` and + `use zudb::Database`. The crate name `zu` is taken there, so every + published crate is `zudb` or `zudb-*`. The repository, the binary and + the file extension are still `zu`. +- `zudb-cli` installs the `zu` binary with `cargo install zudb-cli`. +- The engine crates under them are published as `zudb-common`, + `zudb-encoding`, `zudb-storage`, `zudb-vector`, `zudb-zu1`, + `zudb-sqlite`, `zudb-s3`, `zudb-query`, `zudb-exec`, `zudb-arrow`, + `zudb-json` and `zudb-corpus`. They are versioned with `zudb` and pinned + to it exactly. Depend on `zudb` rather than on them. +- `libzu`, the C library, is attached to the GitHub release for each + tier 1 platform, with a `SHA256SUMS` and build provenance. diff --git a/Cargo.toml b/Cargo.toml index 4e4d1858..0fe7c254 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -12,19 +12,19 @@ repository = "https://github.com/tamnd/zu" authors = ["Tam Nguyen "] [workspace.dependencies] -zu-common = { path = "crates/zu-common" } -zu-encoding = { path = "crates/zu-encoding" } -zu-json = { path = "crates/zu-json" } -zu-vector = { path = "crates/zu-vector" } -zu-storage = { path = "crates/zu-storage" } -zu-zu1 = { path = "crates/zu-zu1" } -zu-sqlite = { path = "crates/zu-sqlite" } -zu-s3 = { path = "crates/zu-s3" } -zu-query = { path = "crates/zu-query" } -zu-exec = { path = "crates/zu-exec" } -zu = { path = "crates/zu" } -zu-arrow = { path = "crates/zu-arrow" } -zu-corpus = { path = "crates/zu-corpus" } +zu-common = { package = "zudb-common", path = "crates/zu-common", version = "=0.0.1" } +zu-encoding = { package = "zudb-encoding", path = "crates/zu-encoding", version = "=0.0.1" } +zu-json = { package = "zudb-json", path = "crates/zu-json", version = "=0.0.1" } +zu-vector = { package = "zudb-vector", path = "crates/zu-vector", version = "=0.0.1" } +zu-storage = { package = "zudb-storage", path = "crates/zu-storage", version = "=0.0.1" } +zu-zu1 = { package = "zudb-zu1", path = "crates/zu-zu1", version = "=0.0.1" } +zu-sqlite = { package = "zudb-sqlite", path = "crates/zu-sqlite", version = "=0.0.1" } +zu-s3 = { package = "zudb-s3", path = "crates/zu-s3", version = "=0.0.1" } +zu-query = { package = "zudb-query", path = "crates/zu-query", version = "=0.0.1" } +zu-exec = { package = "zudb-exec", path = "crates/zu-exec", version = "=0.0.1" } +zu = { package = "zudb", path = "crates/zu", version = "=0.0.1" } +zu-arrow = { package = "zudb-arrow", path = "crates/zu-arrow", version = "=0.0.1" } +zu-corpus = { package = "zudb-corpus", path = "crates/zu-corpus", version = "=0.0.1" } thiserror = "2" crc32c = "0.6" diff --git a/Dockerfile b/Dockerfile index 8462dc68..cba2c936 100644 --- a/Dockerfile +++ b/Dockerfile @@ -35,7 +35,7 @@ COPY . . # usual dependency-caching dance needs a stub per crate and this # workspace has seventeen of them, so it buys a warm cache at the price # of a build that silently succeeds against stubs when a crate is added. -RUN cargo build --release --locked -p zu-cli +RUN cargo build --release --locked -p zudb-cli FROM alpine:${alpine} diff --git a/Makefile b/Makefile index d9951d15..2c686e6c 100644 --- a/Makefile +++ b/Makefile @@ -12,10 +12,10 @@ check-artifacts: cd crates/zu-common/artifacts && shasum -a 256 -c SHA256SUMS bench: - cargo bench -p zu-encoding --bench decode + cargo bench -p zudb-encoding --bench decode gate: - ZU_GATE=1 cargo bench -p zu-encoding --bench decode + ZU_GATE=1 cargo bench -p zudb-encoding --bench decode build: cargo build --workspace --all-features diff --git a/README.md b/README.md index e2fca690..d4539e17 100644 --- a/README.md +++ b/README.md @@ -99,11 +99,12 @@ irm https://raw.githubusercontent.com/tamnd/zu/main/install.ps1 | iex # brew install tamnd/tap/zu scoop install zu docker run --rm -v "$PWD:/data" ghcr.io/tamnd/zu stat graph.zu1 +cargo install zudb-cli # from source, anywhere Rust builds ``` Each of these lands the same thing: the release archive for your platform, unpacked as an install prefix, so `bin/zu` arrives with `include/zu.h`, both library forms, the pkg-config file and the CMake package config beside it. Every one of them fetches the release's `SHA256SUMS` first and refuses to unpack an archive that is not what it says it is. -There is no release yet, so none of these fetch anything today. They are here, tested and held to the platform table, because the install path is the first thing a user runs and the last thing anybody wants to be writing on release day. +The Homebrew tap, the Scoop bucket and the container image are not published yet, so those three fetch nothing today; the install scripts and `cargo install` work from the first release. They are here, tested and held to the platform table, because the install path is the first thing a user runs and the last thing anybody wants to be writing on release day. ## Building diff --git a/bench/check_asm.sh b/bench/check_asm.sh index b83e7c52..1b47d369 100755 --- a/bench/check_asm.sh +++ b/bench/check_asm.sh @@ -8,14 +8,14 @@ # drops a loop back to scalar shows up here, not three releases later # in a bench regression. # -# Usage: cargo bench -p zu-vector --no-run && bench/check_asm.sh +# Usage: cargo bench -p zudb-vector --no-run && bench/check_asm.sh set -eu cd "$(dirname "$0")/.." bin=$(ls -t target/release/deps/kernels-* 2>/dev/null | grep -v '\.d$' | head -1 || true) if [ -z "$bin" ]; then - echo "check_asm: no kernels bench binary; run cargo bench -p zu-vector --no-run first" >&2 + echo "check_asm: no kernels bench binary; run cargo bench -p zudb-vector --no-run first" >&2 exit 1 fi diff --git a/conformance.toml b/conformance.toml index 1d516a85..97a1440e 100644 --- a/conformance.toml +++ b/conformance.toml @@ -1,7 +1,7 @@ # What zu declares it can do, for the gql-compat harness. # # Generated by `zu conformance --declare`. Do not edit by hand: run -# `ZU_UPDATE_CONFORMANCE=1 cargo test -p zu-cli --test conformance_toml`. +# `ZU_UPDATE_CONFORMANCE=1 cargo test -p zudb-cli --test conformance_toml`. # # Every entry carries a reason, because a `false` with no reason is # indistinguishable from a `false` nobody thought about, and the second diff --git a/conformance/README.md b/conformance/README.md index a246322c..fa6c9bb1 100644 --- a/conformance/README.md +++ b/conformance/README.md @@ -9,7 +9,7 @@ The cases live in this repository because they are versioned with the engine and ## Running it ``` -cargo test -p zu-corpus # the reader, the runner, and the cases +cargo test -p zudb-corpus # the reader, the runner, and the cases zu corpus conformance/cases # the same cases through the shipped binary zu corpus conformance/cases --strict # and no case may be ahead of the engine ``` diff --git a/crates/xtask/src/main.rs b/crates/xtask/src/main.rs index c13438ae..11ebb405 100644 --- a/crates/xtask/src/main.rs +++ b/crates/xtask/src/main.rs @@ -121,7 +121,16 @@ cargo xtask grammar [--table PATH] [--root DIR] [--check] [--list] [--queries DI /// `pub use`. rustdoc documents one crate at a time, so each of these /// has to be generated as well or a third of the API is a name with /// nothing behind it. -const REEXPORTED: [&str; 4] = ["zu-common", "zu-storage", "zu-zu1", "zu-query"]; +/// +/// Each is the package cargo is asked for and the library rustdoc +/// writes. They differ because the packages are published as `zudb-*`, +/// `zu` being taken on crates.io, while the libraries kept their names. +const REEXPORTED: [(&str, &str); 4] = [ + ("zudb-common", "zu_common"), + ("zudb-storage", "zu_storage"), + ("zudb-zu1", "zu_zu1"), + ("zudb-query", "zu_query"), +]; fn main() -> ExitCode { let args: Vec = std::env::args().skip(1).collect(); @@ -181,8 +190,14 @@ fn model_command(args: &[String]) -> Result { } let mut docs = Vec::with_capacity(REEXPORTED.len() + 1); - for package in std::iter::once("zu").chain(REEXPORTED) { - docs.push(rustdoc::generate(package, &toolchain)?); + // The engine crate is `zudb` to cargo and to its users, and the + // model goes on calling it `zu`, so the bindings that read the model + // do not see every path in it move because of a registry name. + let mut engine = rustdoc::generate("zudb", "zudb", &toolchain)?; + engine.name = "zu".to_string(); + docs.push(engine); + for (package, lib) in REEXPORTED { + docs.push(rustdoc::generate(package, lib, &toolchain)?); } let model = model::build(&docs, "zu")?; let text = model.to_json().to_pretty(); diff --git a/crates/xtask/src/rustdoc.rs b/crates/xtask/src/rustdoc.rs index 288a9af1..6c801951 100644 --- a/crates/xtask/src/rustdoc.rs +++ b/crates/xtask/src/rustdoc.rs @@ -26,11 +26,14 @@ pub struct CrateDoc { /// Runs `cargo rustdoc` for one package and reads what it wrote. /// +/// `lib` is the library the package builds, which is what rustdoc +/// names its file after and need not be the package name. +/// /// `toolchain` is passed to cargo as `+name`. The caller supplies it /// rather than this function hard-coding `+nightly`, so CI can pin the /// exact nightly from the toolchain table and get the same bytes on /// every run, which is the whole point of committing the model. -pub fn generate(package: &str, toolchain: &str) -> Result { +pub fn generate(package: &str, lib: &str, toolchain: &str) -> Result { let target = target_dir()?; let mut cmd = Command::new("cargo"); cmd.arg(format!("+{toolchain}")) @@ -48,9 +51,8 @@ pub fn generate(package: &str, toolchain: &str) -> Result { String::from_utf8_lossy(&out.stderr) )); } - let name = package.replace('-', "_"); - let path = target.join("doc").join(format!("{name}.json")); - read(&path, &name) + let path = target.join("doc").join(format!("{lib}.json")); + read(&path, lib) } /// Reads one rustdoc JSON file that is already on disk. diff --git a/crates/zu-adbc/Cargo.toml b/crates/zu-adbc/Cargo.toml index 923c4a71..310e3ff6 100644 --- a/crates/zu-adbc/Cargo.toml +++ b/crates/zu-adbc/Cargo.toml @@ -19,7 +19,7 @@ name = "zu_adbc" crate-type = ["lib", "cdylib"] [dependencies] -zudb = { package = "zu", path = "../zu" } +zudb = { path = "../zu" } # No features: what ADBC wants back is a `RecordBatchReader`, and the # driver exporter is what turns one into a C stream. `zu-arrow`'s own # `ffi` would be a second way to do the same thing. diff --git a/crates/zu-arrow/Cargo.toml b/crates/zu-arrow/Cargo.toml index ad4bf828..f382a6d7 100644 --- a/crates/zu-arrow/Cargo.toml +++ b/crates/zu-arrow/Cargo.toml @@ -1,7 +1,6 @@ [package] -name = "zu-arrow" +name = "zudb-arrow" description = "A zu result as Arrow arrays, off the buffers the engine already filled" -publish = false version.workspace = true edition.workspace = true rust-version.workspace = true @@ -9,6 +8,11 @@ license.workspace = true repository.workspace = true authors.workspace = true +# Published as zudb-*, because zu is taken on crates.io, but the +# library keeps the name every crate in this workspace already uses. +[lib] +name = "zu_arrow" + [dependencies] zu-common.workspace = true zu-query.workspace = true diff --git a/crates/zu-arrow/benches/export.rs b/crates/zu-arrow/benches/export.rs index c4714d6f..3cd2ff21 100644 --- a/crates/zu-arrow/benches/export.rs +++ b/crates/zu-arrow/benches/export.rs @@ -16,7 +16,7 @@ //! line is untimed and is the other half of the point, since the copy is //! not only time but a second whole answer resident while both exist. //! -//! Run: ZU_GATE=1 cargo bench -p zu-arrow --bench export +//! Run: ZU_GATE=1 cargo bench -p zudb-arrow --bench export use std::time::Instant; diff --git a/crates/zu-capi/Cargo.toml b/crates/zu-capi/Cargo.toml index cc6b827d..edbd7a13 100644 --- a/crates/zu-capi/Cargo.toml +++ b/crates/zu-capi/Cargo.toml @@ -24,7 +24,7 @@ crate-type = ["lib", "cdylib", "staticlib"] [dependencies] zu-common.workspace = true -zudb = { package = "zu", path = "../zu" } +zudb = { path = "../zu" } # The Arrow export, which is a feature rather than a dependency because # it is the one part of this library that costs a megabyte to link. On # by default, since a shipped libzu is what every binding's Arrow path diff --git a/crates/zu-cli/Cargo.toml b/crates/zu-cli/Cargo.toml index 40aaa296..096d2c8a 100644 --- a/crates/zu-cli/Cargo.toml +++ b/crates/zu-cli/Cargo.toml @@ -1,13 +1,15 @@ [package] -name = "zu-cli" +name = "zudb-cli" description = "Command-line interface for zu" -publish = false version.workspace = true edition.workspace = true rust-version.workspace = true license.workspace = true repository.workspace = true authors.workspace = true +readme = "../../README.md" +keywords = ["database", "graph", "gql", "cli"] +categories = ["command-line-utilities", "database-implementations"] [[bin]] name = "zu" diff --git a/crates/zu-cli/benches/cli.rs b/crates/zu-cli/benches/cli.rs index e8175aa5..dba41b75 100644 --- a/crates/zu-cli/benches/cli.rs +++ b/crates/zu-cli/benches/cli.rs @@ -18,7 +18,7 @@ //! With ZU_GATE=1 the process exits nonzero when startup misses the //! ceiling in bench/budgets.toml. //! -//! Run: ZU_GATE=1 cargo bench -p zu-cli +//! Run: ZU_GATE=1 cargo bench -p zudb-cli use std::path::Path; use std::process::{Command, Stdio}; diff --git a/crates/zu-cli/benches/editor.rs b/crates/zu-cli/benches/editor.rs index 1a4197ec..0d9c4ba1 100644 --- a/crates/zu-cli/benches/editor.rs +++ b/crates/zu-cli/benches/editor.rs @@ -18,7 +18,7 @@ //! With ZU_GATE=1 the process exits nonzero when a keystroke misses the //! ceiling in bench/budgets.toml. //! -//! Run: ZU_GATE=1 cargo bench -p zu-cli --bench editor +//! Run: ZU_GATE=1 cargo bench -p zudb-cli --bench editor // The editor's source is compiled whole, tests and all, and only the // typing path is measured, so the parts a terminal loop would call are diff --git a/crates/zu-cli/src/conformance.rs b/crates/zu-cli/src/conformance.rs index ee95efbd..c624d245 100644 --- a/crates/zu-cli/src/conformance.rs +++ b/crates/zu-cli/src/conformance.rs @@ -508,7 +508,7 @@ pub(crate) fn render() -> String { "# What zu declares it can do, for the gql-compat harness.\n\ #\n\ # Generated by `zu conformance --declare`. Do not edit by hand: run\n\ - # `ZU_UPDATE_CONFORMANCE=1 cargo test -p zu-cli --test conformance_toml`.\n\ + # `ZU_UPDATE_CONFORMANCE=1 cargo test -p zudb-cli --test conformance_toml`.\n\ #\n\ # Every entry carries a reason, because a `false` with no reason is\n\ # indistinguishable from a `false` nobody thought about, and the second\n\ diff --git a/crates/zu-cli/src/impdef/generated.rs b/crates/zu-cli/src/impdef/generated.rs index 341463cd..825acc90 100644 --- a/crates/zu-cli/src/impdef/generated.rs +++ b/crates/zu-cli/src/impdef/generated.rs @@ -2,7 +2,7 @@ //! and `gql-implementation-dependent.xml`, the two ISO/IEC //! 39075:2024 artifacts that list what the standard leaves to the //! implementation. Do not edit by hand: run -//! `ZU_UPDATE_IMPDEF=1 cargo test -p zu-cli --test impdef_table`. +//! `ZU_UPDATE_IMPDEF=1 cargo test -p zudb-cli --test impdef_table`. //! //! Codes and descriptions are the standard's, verbatim, with runs //! of whitespace folded to one space so a description is one line. diff --git a/crates/zu-cli/src/statement.rs b/crates/zu-cli/src/statement.rs index 7019bd03..4445c637 100644 --- a/crates/zu-cli/src/statement.rs +++ b/crates/zu-cli/src/statement.rs @@ -276,7 +276,7 @@ pub fn render(t: &Tally) -> String { "The full report behind the tally is a megabyte of per-case timings, host readings and a wall clock, none of it the same twice, so it is not checked in anywhere. What is checked in is the tally this page is rendered from, and the four commands that regenerate the whole chain from an engine binary:\n\n", ); out.push_str("```\n"); - out.push_str("cargo build --release -p zu-cli\n"); + out.push_str("cargo build --release -p zudb-cli\n"); out.push_str( "gql-compat run -adapter zu -binary target/release/zu -fail-on none -out reports/zu\n", ); diff --git a/crates/zu-cli/src/statement/features.rs b/crates/zu-cli/src/statement/features.rs index 93a5d636..409b64ec 100644 --- a/crates/zu-cli/src/statement/features.rs +++ b/crates/zu-cli/src/statement/features.rs @@ -1,7 +1,7 @@ //! Generated from `crates/zu-common/artifacts/gql-features.xml`, the //! ISO/IEC 39075:2024 artifact that lists every optional language //! feature the standard defines. Do not edit by hand: run -//! `ZU_UPDATE_STATEMENT=1 cargo test -p zu-cli --test statement`. +//! `ZU_UPDATE_STATEMENT=1 cargo test -p zudb-cli --test statement`. //! //! Codes and descriptions are the standard's, verbatim, with runs of //! whitespace folded to one space so a description is one line. diff --git a/crates/zu-cli/tests/impdef_table.rs b/crates/zu-cli/tests/impdef_table.rs index 0b7b0210..225aeca0 100644 --- a/crates/zu-cli/tests/impdef_table.rs +++ b/crates/zu-cli/tests/impdef_table.rs @@ -94,7 +94,7 @@ fn render(rows: &[Row]) -> String { //! and `gql-implementation-dependent.xml`, the two ISO/IEC\n\ //! 39075:2024 artifacts that list what the standard leaves to the\n\ //! implementation. Do not edit by hand: run\n\ - //! `ZU_UPDATE_IMPDEF=1 cargo test -p zu-cli --test impdef_table`.\n\ + //! `ZU_UPDATE_IMPDEF=1 cargo test -p zudb-cli --test impdef_table`.\n\ //!\n\ //! Codes and descriptions are the standard's, verbatim, with runs\n\ //! of whitespace folded to one space so a description is one line.\n\ @@ -177,7 +177,7 @@ fn generated_table_matches_the_artifacts() { have.trim_end(), rendered.trim_end(), "src/impdef/generated.rs is stale; run \ - ZU_UPDATE_IMPDEF=1 cargo test -p zu-cli --test impdef_table" + ZU_UPDATE_IMPDEF=1 cargo test -p zudb-cli --test impdef_table" ); } diff --git a/crates/zu-cli/tests/snapshots.rs b/crates/zu-cli/tests/snapshots.rs index a972ccdc..b569ee21 100644 --- a/crates/zu-cli/tests/snapshots.rs +++ b/crates/zu-cli/tests/snapshots.rs @@ -13,7 +13,7 @@ //! the hundred lines here is a file format and a diff, and the thing //! this suite actually needs is the scrubbing below, which is what //! separates a snapshot from a clock. `ZU_UPDATE_SNAPSHOTS=1 cargo test -//! -p zu-cli --test snapshots` rewrites every file, and the diff on the +//! -p zudb-cli --test snapshots` rewrites every file, and the diff on the //! way into the commit is the review. //! //! Nothing here holds text an operating system wrote. A missing file @@ -543,7 +543,7 @@ fn snapshot(name: &str, actual: &str) { } panic!( "{} is not what the CLI prints. Read the difference, and if the new output is the \ - intended one, `ZU_UPDATE_SNAPSHOTS=1 cargo test -p zu-cli --test snapshots` writes \ + intended one, `ZU_UPDATE_SNAPSHOTS=1 cargo test -p zudb-cli --test snapshots` writes \ it.\n\n--- committed\n{committed}\n--- printed\n{actual}", path.display() ); diff --git a/crates/zu-cli/tests/statement.rs b/crates/zu-cli/tests/statement.rs index c5f3313f..b7d0ec7c 100644 --- a/crates/zu-cli/tests/statement.rs +++ b/crates/zu-cli/tests/statement.rs @@ -90,7 +90,7 @@ fn render(rows: &[Row]) -> String { "//! Generated from `crates/zu-common/artifacts/gql-features.xml`, the\n\ //! ISO/IEC 39075:2024 artifact that lists every optional language\n\ //! feature the standard defines. Do not edit by hand: run\n\ - //! `ZU_UPDATE_STATEMENT=1 cargo test -p zu-cli --test statement`.\n\ + //! `ZU_UPDATE_STATEMENT=1 cargo test -p zudb-cli --test statement`.\n\ //!\n\ //! Codes and descriptions are the standard's, verbatim, with runs of\n\ //! whitespace folded to one space so a description is one line.\n\ @@ -158,7 +158,7 @@ fn generated_table_matches_the_artifact() { have.trim_end(), rendered.trim_end(), "src/statement/features.rs is stale; run \ - ZU_UPDATE_STATEMENT=1 cargo test -p zu-cli --test statement" + ZU_UPDATE_STATEMENT=1 cargo test -p zudb-cli --test statement" ); } diff --git a/crates/zu-common/Cargo.toml b/crates/zu-common/Cargo.toml index 87f269ab..fa08c3e7 100644 --- a/crates/zu-common/Cargo.toml +++ b/crates/zu-common/Cargo.toml @@ -1,7 +1,6 @@ [package] -name = "zu-common" +name = "zudb-common" description = "Shared identifiers, errors, and constants for zu" -publish = false version.workspace = true edition.workspace = true rust-version.workspace = true @@ -9,6 +8,11 @@ license.workspace = true repository.workspace = true authors.workspace = true +# Published as zudb-*, because zu is taken on crates.io, but the +# library keeps the name every crate in this workspace already uses. +[lib] +name = "zu_common" + [dependencies] thiserror.workspace = true diff --git a/crates/zu-common/src/gqlstatus/generated.rs b/crates/zu-common/src/gqlstatus/generated.rs index aa92bd69..500e9147 100644 --- a/crates/zu-common/src/gqlstatus/generated.rs +++ b/crates/zu-common/src/gqlstatus/generated.rs @@ -1,6 +1,6 @@ //! Generated from `artifacts/gql-conditions.xml`, the ISO/IEC //! 39075:2024 conditions artifact. Do not edit by hand: run -//! `ZU_UPDATE_GQLSTATUS=1 cargo test -p zu-common --test gqlstatus_table`. +//! `ZU_UPDATE_GQLSTATUS=1 cargo test -p zudb-common --test gqlstatus_table`. //! //! Codes and natural-language names are the standard's, verbatim. diff --git a/crates/zu-common/src/keywords/generated.rs b/crates/zu-common/src/keywords/generated.rs index a49d8b30..67a762fb 100644 --- a/crates/zu-common/src/keywords/generated.rs +++ b/crates/zu-common/src/keywords/generated.rs @@ -1,6 +1,6 @@ //! Generated from `artifacts/gql-bnf.xml`, the ISO/IEC //! 39075:2024 grammar artifact. Do not edit by hand: run -//! `ZU_UPDATE_KEYWORDS=1 cargo test -p zu-common --test keyword_table`. +//! `ZU_UPDATE_KEYWORDS=1 cargo test -p zudb-common --test keyword_table`. //! //! Every list is sorted, so the lookups are a binary search. diff --git a/crates/zu-common/src/unicode/generated.rs b/crates/zu-common/src/unicode/generated.rs index 44647c5f..fc0a0812 100644 --- a/crates/zu-common/src/unicode/generated.rs +++ b/crates/zu-common/src/unicode/generated.rs @@ -1,7 +1,7 @@ //! Generated from `artifacts/UnicodeData.txt` and //! `artifacts/CompositionExclusions.txt`, the Unicode Character //! Database. Do not edit by hand: run -//! `ZU_UPDATE_UNICODE=1 cargo test -p zu-common --test unicode_tables`. +//! `ZU_UPDATE_UNICODE=1 cargo test -p zudb-common --test unicode_tables`. //! //! The decompositions are fully expanded and the composition //! pairs already have the exclusions taken out of them, so every diff --git a/crates/zu-common/tests/gqlstatus_table.rs b/crates/zu-common/tests/gqlstatus_table.rs index 50db0752..620d238d 100644 --- a/crates/zu-common/tests/gqlstatus_table.rs +++ b/crates/zu-common/tests/gqlstatus_table.rs @@ -169,7 +169,7 @@ fn render(rows: &[Row]) -> String { out.push_str( "//! Generated from `artifacts/gql-conditions.xml`, the ISO/IEC\n\ //! 39075:2024 conditions artifact. Do not edit by hand: run\n\ - //! `ZU_UPDATE_GQLSTATUS=1 cargo test -p zu-common --test gqlstatus_table`.\n\ + //! `ZU_UPDATE_GQLSTATUS=1 cargo test -p zudb-common --test gqlstatus_table`.\n\ //!\n\ //! Codes and natural-language names are the standard's, verbatim.\n\ \n\ diff --git a/crates/zu-common/tests/keyword_table.rs b/crates/zu-common/tests/keyword_table.rs index 368a7d0f..311e3b70 100644 --- a/crates/zu-common/tests/keyword_table.rs +++ b/crates/zu-common/tests/keyword_table.rs @@ -54,7 +54,7 @@ fn render(reserved: &[String], pre: &[String], non: &[String]) -> String { out.push_str( "//! Generated from `artifacts/gql-bnf.xml`, the ISO/IEC\n\ //! 39075:2024 grammar artifact. Do not edit by hand: run\n\ - //! `ZU_UPDATE_KEYWORDS=1 cargo test -p zu-common --test keyword_table`.\n\ + //! `ZU_UPDATE_KEYWORDS=1 cargo test -p zudb-common --test keyword_table`.\n\ //!\n\ //! Every list is sorted, so the lookups are a binary search.\n\ \n", diff --git a/crates/zu-common/tests/unicode_tables.rs b/crates/zu-common/tests/unicode_tables.rs index ebcf6d67..74ceb5c2 100644 --- a/crates/zu-common/tests/unicode_tables.rs +++ b/crates/zu-common/tests/unicode_tables.rs @@ -1,6 +1,6 @@ //! Regenerates `src/unicode/generated.rs` from the Unicode Character //! Database and fails on drift, the same way the GQLSTATUS table test -//! works. Run with `ZU_UPDATE_UNICODE=1 cargo test -p zu-common --test +//! works. Run with `ZU_UPDATE_UNICODE=1 cargo test -p zudb-common --test //! unicode_tables` after the artifacts change. //! //! The artifacts are checked in at `artifacts/UnicodeData.txt` and @@ -309,7 +309,7 @@ fn render(tables: &Tables) -> String { "//! Generated from `artifacts/UnicodeData.txt` and\n\ //! `artifacts/CompositionExclusions.txt`, the Unicode Character\n\ //! Database. Do not edit by hand: run\n\ - //! `ZU_UPDATE_UNICODE=1 cargo test -p zu-common --test unicode_tables`.\n\ + //! `ZU_UPDATE_UNICODE=1 cargo test -p zudb-common --test unicode_tables`.\n\ //!\n\ //! The decompositions are fully expanded and the composition\n\ //! pairs already have the exclusions taken out of them, so every\n\ @@ -515,7 +515,7 @@ fn generated_tables_match_the_artifacts() { assert_eq!( on_disk.replace("\r\n", "\n"), rendered, - "src/unicode/generated.rs is stale; run ZU_UPDATE_UNICODE=1 cargo test -p zu-common --test unicode_tables" + "src/unicode/generated.rs is stale; run ZU_UPDATE_UNICODE=1 cargo test -p zudb-common --test unicode_tables" ); } diff --git a/crates/zu-corpus/Cargo.toml b/crates/zu-corpus/Cargo.toml index 31c7c3b0..bb3fc94e 100644 --- a/crates/zu-corpus/Cargo.toml +++ b/crates/zu-corpus/Cargo.toml @@ -1,5 +1,5 @@ [package] -name = "zu-corpus" +name = "zudb-corpus" version.workspace = true edition.workspace = true rust-version.workspace = true @@ -8,6 +8,11 @@ repository.workspace = true authors.workspace = true description = "The cross-client conformance corpus and its Rust runner" +# Published as zudb-*, because zu is taken on crates.io, but the +# library keeps the name every crate in this workspace already uses. +[lib] +name = "zu_corpus" + [[bench]] name = "corpus" harness = false diff --git a/crates/zu-corpus/benches/corpus.rs b/crates/zu-corpus/benches/corpus.rs index 1da629c3..5f005b98 100644 --- a/crates/zu-corpus/benches/corpus.rs +++ b/crates/zu-corpus/benches/corpus.rs @@ -22,7 +22,7 @@ //! database-per-case rule is the only thing that would ever need //! revisiting, at around four seconds for a thousand cases. //! -//! Run: cargo bench -p zu-corpus --bench corpus +//! Run: cargo bench -p zudb-corpus --bench corpus use std::hint::black_box; use std::path::Path; diff --git a/crates/zu-encoding/Cargo.toml b/crates/zu-encoding/Cargo.toml index ecbcf087..a8987fd9 100644 --- a/crates/zu-encoding/Cargo.toml +++ b/crates/zu-encoding/Cargo.toml @@ -1,7 +1,6 @@ [package] -name = "zu-encoding" +name = "zudb-encoding" description = "Lightweight columnar encodings for zu (FastLanes, ALP, FSST, cascades)" -publish = false version.workspace = true edition.workspace = true rust-version.workspace = true @@ -9,6 +8,11 @@ license.workspace = true repository.workspace = true authors.workspace = true +# Published as zudb-*, because zu is taken on crates.io, but the +# library keeps the name every crate in this workspace already uses. +[lib] +name = "zu_encoding" + [dependencies] zu-common.workspace = true ruzstd.workspace = true diff --git a/crates/zu-encoding/benches/decode.rs b/crates/zu-encoding/benches/decode.rs index c32051c1..dea5d9d2 100644 --- a/crates/zu-encoding/benches/decode.rs +++ b/crates/zu-encoding/benches/decode.rs @@ -6,7 +6,7 @@ //! With ZU_GATE=1 the process exits nonzero if any encoding decodes below //! its floor in bench/budgets.toml, measured as decoded output bytes/s. //! -//! Run: ZU_GATE=1 ZU_DATA=~/data/zu cargo bench -p zu-encoding +//! Run: ZU_GATE=1 ZU_DATA=~/data/zu cargo bench -p zudb-encoding use std::time::Instant; diff --git a/crates/zu-encoding/benches/encode.rs b/crates/zu-encoding/benches/encode.rs index 51ca7487..90a372dd 100644 --- a/crates/zu-encoding/benches/encode.rs +++ b/crates/zu-encoding/benches/encode.rs @@ -17,7 +17,7 @@ //! With ZU_GATE=1 the process exits nonzero if any shape encodes below //! its floor in bench/budgets.toml. //! -//! Run: ZU_GATE=1 cargo bench -p zu-encoding --bench encode +//! Run: ZU_GATE=1 cargo bench -p zudb-encoding --bench encode use std::time::Instant; diff --git a/crates/zu-exec/Cargo.toml b/crates/zu-exec/Cargo.toml index 6e08de41..8ac5d870 100644 --- a/crates/zu-exec/Cargo.toml +++ b/crates/zu-exec/Cargo.toml @@ -1,7 +1,6 @@ [package] -name = "zu-exec" +name = "zudb-exec" description = "Push-based morsel-parallel pipeline executor for zu" -publish = false version.workspace = true edition.workspace = true rust-version.workspace = true @@ -9,6 +8,11 @@ license.workspace = true repository.workspace = true authors.workspace = true +# Published as zudb-*, because zu is taken on crates.io, but the +# library keeps the name every crate in this workspace already uses. +[lib] +name = "zu_exec" + [dependencies] zu-common.workspace = true zu-vector.workspace = true diff --git a/crates/zu-exec/benches/join.rs b/crates/zu-exec/benches/join.rs index 4f6d4e53..c9048c9e 100644 --- a/crates/zu-exec/benches/join.rs +++ b/crates/zu-exec/benches/join.rs @@ -27,7 +27,7 @@ //! exec_join_build_mrows_s floors the build, which every worker waits //! on before it probes anything. //! -//! Run: ZU_GATE=1 cargo bench -p zu-exec --bench join +//! Run: ZU_GATE=1 cargo bench -p zudb-exec --bench join use std::collections::{HashMap, HashSet}; use std::hint::black_box; diff --git a/crates/zu-exec/benches/sip.rs b/crates/zu-exec/benches/sip.rs index 4e25a2bd..effc6cd7 100644 --- a/crates/zu-exec/benches/sip.rs +++ b/crates/zu-exec/benches/sip.rs @@ -27,7 +27,7 @@ //! exec_sip_select_mrows_s floors the bloom select, the general case //! and the slower of the two filters. //! -//! Run: ZU_GATE=1 cargo bench -p zu-exec --bench sip +//! Run: ZU_GATE=1 cargo bench -p zudb-exec --bench sip use std::hint::black_box; use std::time::Instant; diff --git a/crates/zu-json/Cargo.toml b/crates/zu-json/Cargo.toml index 3742e64e..b78b483d 100644 --- a/crates/zu-json/Cargo.toml +++ b/crates/zu-json/Cargo.toml @@ -1,7 +1,6 @@ [package] -name = "zu-json" +name = "zudb-json" description = "A small JSON reader and writer, shared by the CLI and the codegen tools" -publish = false version.workspace = true edition.workspace = true rust-version.workspace = true @@ -9,6 +8,11 @@ license.workspace = true repository.workspace = true authors.workspace = true +# Published as zudb-*, because zu is taken on crates.io, but the +# library keeps the name every crate in this workspace already uses. +[lib] +name = "zu_json" + [[bench]] name = "json" harness = false diff --git a/crates/zu-json/benches/json.rs b/crates/zu-json/benches/json.rs index 88c9aa5f..8427fd8c 100644 --- a/crates/zu-json/benches/json.rs +++ b/crates/zu-json/benches/json.rs @@ -14,7 +14,7 @@ //! and not gated: it runs on a developer machine or in a codegen job, //! and putting a floor on a build tool buys a flaky check. //! -//! Run: ZU_GATE=1 cargo bench -p zu-json +//! Run: ZU_GATE=1 cargo bench -p zudb-json use std::hint::black_box; use std::time::Instant; diff --git a/crates/zu-query/Cargo.toml b/crates/zu-query/Cargo.toml index e540d958..c3b2edd3 100644 --- a/crates/zu-query/Cargo.toml +++ b/crates/zu-query/Cargo.toml @@ -1,7 +1,6 @@ [package] -name = "zu-query" +name = "zudb-query" description = "Parser, planner, and factorized vectorized executor for zu" -publish = false version.workspace = true edition.workspace = true rust-version.workspace = true @@ -9,6 +8,11 @@ license.workspace = true repository.workspace = true authors.workspace = true +# Published as zudb-*, because zu is taken on crates.io, but the +# library keeps the name every crate in this workspace already uses. +[lib] +name = "zu_query" + [dependencies] crossbeam-deque = "0.8.7" zu-common.workspace = true diff --git a/crates/zu-query/benches/columnar.rs b/crates/zu-query/benches/columnar.rs index 7f188b95..ee1ab58f 100644 --- a/crates/zu-query/benches/columnar.rs +++ b/crates/zu-query/benches/columnar.rs @@ -12,7 +12,7 @@ //! Informational, with no gate floor. The floor that matters is the one //! in the client, against DuckDB, and it is published with the release. //! -//! Run: cargo bench -p zu-query --bench columnar +//! Run: cargo bench -p zudb-query --bench columnar use std::hint::black_box; use std::time::Instant; diff --git a/crates/zu-query/benches/kernels.rs b/crates/zu-query/benches/kernels.rs index dfbb6f50..94c1af46 100644 --- a/crates/zu-query/benches/kernels.rs +++ b/crates/zu-query/benches/kernels.rs @@ -8,7 +8,7 @@ //! sides of the hybrid morsel switch at a spread of source counts. //! No gate floors yet: the numbers are informational. //! -//! Run: ZU_DATA=~/data/zu cargo bench -p zu-query +//! Run: ZU_DATA=~/data/zu cargo bench -p zudb-query use std::time::Instant; diff --git a/crates/zu-s3/Cargo.toml b/crates/zu-s3/Cargo.toml index 43d97e9c..c69b8cec 100644 --- a/crates/zu-s3/Cargo.toml +++ b/crates/zu-s3/Cargo.toml @@ -1,7 +1,6 @@ [package] -name = "zu-s3" +name = "zudb-s3" description = "Object-storage-native engine for zu with fixed-cost batching" -publish = false version.workspace = true edition.workspace = true rust-version.workspace = true @@ -9,6 +8,11 @@ license.workspace = true repository.workspace = true authors.workspace = true +# Published as zudb-*, because zu is taken on crates.io, but the +# library keeps the name every crate in this workspace already uses. +[lib] +name = "zu_s3" + [dependencies] zu-common.workspace = true zu-storage.workspace = true diff --git a/crates/zu-snippets/Cargo.toml b/crates/zu-snippets/Cargo.toml index c348e5df..e6f78e11 100644 --- a/crates/zu-snippets/Cargo.toml +++ b/crates/zu-snippets/Cargo.toml @@ -17,7 +17,7 @@ path = "src/lib.rs" # after `cargo add zudb` and therefore the name the printed snippet has # to use. A crate cannot depend on itself under another name, which is # the whole reason the snippets are a package of their own. -zudb = { package = "zu", path = "../zu" } +zudb = { path = "../zu" } [dev-dependencies] tempfile.workspace = true diff --git a/crates/zu-sqlite/Cargo.toml b/crates/zu-sqlite/Cargo.toml index 80acbee3..ed07d449 100644 --- a/crates/zu-sqlite/Cargo.toml +++ b/crates/zu-sqlite/Cargo.toml @@ -1,7 +1,6 @@ [package] -name = "zu-sqlite" +name = "zudb-sqlite" description = "SQLite-backed storage engine for zu" -publish = false version.workspace = true edition.workspace = true rust-version.workspace = true @@ -9,6 +8,11 @@ license.workspace = true repository.workspace = true authors.workspace = true +# Published as zudb-*, because zu is taken on crates.io, but the +# library keeps the name every crate in this workspace already uses. +[lib] +name = "zu_sqlite" + [dependencies] zu-common.workspace = true zu-storage.workspace = true diff --git a/crates/zu-storage/Cargo.toml b/crates/zu-storage/Cargo.toml index 3dc2993a..d94759de 100644 --- a/crates/zu-storage/Cargo.toml +++ b/crates/zu-storage/Cargo.toml @@ -1,7 +1,6 @@ [package] -name = "zu-storage" +name = "zudb-storage" description = "Storage engine trait and shared segment types for zu" -publish = false version.workspace = true edition.workspace = true rust-version.workspace = true @@ -9,6 +8,11 @@ license.workspace = true repository.workspace = true authors.workspace = true +# Published as zudb-*, because zu is taken on crates.io, but the +# library keeps the name every crate in this workspace already uses. +[lib] +name = "zu_storage" + [dependencies] zu-common.workspace = true zu-encoding.workspace = true diff --git a/crates/zu-vector/Cargo.toml b/crates/zu-vector/Cargo.toml index 26dcbc06..7f4efc95 100644 --- a/crates/zu-vector/Cargo.toml +++ b/crates/zu-vector/Cargo.toml @@ -1,7 +1,6 @@ [package] -name = "zu-vector" +name = "zudb-vector" description = "Typed columnar vectors, selection, and expression kernels for zu" -publish = false version.workspace = true edition.workspace = true rust-version.workspace = true @@ -9,6 +8,11 @@ license.workspace = true repository.workspace = true authors.workspace = true +# Published as zudb-*, because zu is taken on crates.io, but the +# library keeps the name every crate in this workspace already uses. +[lib] +name = "zu_vector" + [dependencies] zu-common.workspace = true diff --git a/crates/zu-vector/benches/kernels.rs b/crates/zu-vector/benches/kernels.rs index 1f63a3ec..92f4bcab 100644 --- a/crates/zu-vector/benches/kernels.rs +++ b/crates/zu-vector/benches/kernels.rs @@ -7,7 +7,7 @@ //! the slowest gate machine; the spec targets are printed next to each //! measurement for the roofline picture. //! -//! Run: ZU_GATE=1 cargo bench -p zu-vector +//! Run: ZU_GATE=1 cargo bench -p zudb-vector use std::hint::black_box; use std::sync::Arc; diff --git a/crates/zu-zu1/Cargo.toml b/crates/zu-zu1/Cargo.toml index b40b64d3..b062321e 100644 --- a/crates/zu-zu1/Cargo.toml +++ b/crates/zu-zu1/Cargo.toml @@ -1,7 +1,6 @@ [package] -name = "zu-zu1" +name = "zudb-zu1" description = "Native single-file columnar storage engine for zu" -publish = false version.workspace = true edition.workspace = true rust-version.workspace = true @@ -9,6 +8,11 @@ license.workspace = true repository.workspace = true authors.workspace = true +# Published as zudb-*, because zu is taken on crates.io, but the +# library keeps the name every crate in this workspace already uses. +[lib] +name = "zu_zu1" + [dependencies] zu-common.workspace = true zu-encoding.workspace = true diff --git a/crates/zu-zu1/benches/blob.rs b/crates/zu-zu1/benches/blob.rs index ee43f8f3..f3b5bbb9 100644 --- a/crates/zu-zu1/benches/blob.rs +++ b/crates/zu-zu1/benches/blob.rs @@ -10,7 +10,7 @@ //! against the source rows. With ZU_GATE=1 the process exits nonzero //! when a floor or ceiling in bench/budgets.toml is missed. //! -//! Run: ZU_GATE=1 ZU_DATA=~/data/zu cargo bench -p zu-zu1 --bench blob +//! Run: ZU_GATE=1 ZU_DATA=~/data/zu cargo bench -p zudb-zu1 --bench blob use std::io::BufRead; use std::time::Instant; diff --git a/crates/zu-zu1/benches/ingest.rs b/crates/zu-zu1/benches/ingest.rs index 3a713db8..d40a8fa0 100644 --- a/crates/zu-zu1/benches/ingest.rs +++ b/crates/zu-zu1/benches/ingest.rs @@ -10,7 +10,7 @@ //! B6 scale check: the same COPY path over the 117 M edge com-Orkut //! graph, since B6 is defined at 100 M edges and LiveJournal is 69 M. //! -//! Run: ZU_GATE=1 ZU_DATA=~/data/zu ZU_B6=1 cargo bench -p zu-zu1 +//! Run: ZU_GATE=1 ZU_DATA=~/data/zu ZU_B6=1 cargo bench -p zudb-zu1 use std::time::Instant; diff --git a/crates/zu-zu1/benches/open.rs b/crates/zu-zu1/benches/open.rs index 70241290..ef6f4574 100644 --- a/crates/zu-zu1/benches/open.rs +++ b/crates/zu-zu1/benches/open.rs @@ -17,7 +17,7 @@ //! overrides the target for local smoke runs; the gate only applies at //! the full 10 GB on real data. //! -//! Run: ZU_GATE=1 ZU_DATA=~/data/zu ZU_B7=1 cargo bench -p zu-zu1 --bench open +//! Run: ZU_GATE=1 ZU_DATA=~/data/zu ZU_B7=1 cargo bench -p zudb-zu1 --bench open use std::hint::black_box; use std::time::Instant; diff --git a/crates/zu-zu1/benches/read.rs b/crates/zu-zu1/benches/read.rs index 74641b1b..209847df 100644 --- a/crates/zu-zu1/benches/read.rs +++ b/crates/zu-zu1/benches/read.rs @@ -16,7 +16,7 @@ //! chunks. gather reads random row batches through the props gather, //! one decode per touched chunk. //! -//! Run: ZU_GATE=1 cargo bench -p zu-zu1 --bench read +//! Run: ZU_GATE=1 cargo bench -p zudb-zu1 --bench read use std::time::Instant; diff --git a/crates/zu-zu1/src/ingest.rs b/crates/zu-zu1/src/ingest.rs index 402a084c..53c28243 100644 --- a/crates/zu-zu1/src/ingest.rs +++ b/crates/zu-zu1/src/ingest.rs @@ -947,7 +947,7 @@ mod tests { } /// Not a gate, a manual probe for the T4 ingest target. Run with - /// `cargo test -q -p zu-zu1 --release ingest_throughput -- --ignored --nocapture`. + /// `cargo test -q -p zudb-zu1 --release ingest_throughput -- --ignored --nocapture`. #[test] #[ignore = "manual throughput probe"] fn ingest_throughput_probe() { diff --git a/crates/zu-zu1/tests/loom.rs b/crates/zu-zu1/tests/loom.rs index 64211b86..532ff141 100644 --- a/crates/zu-zu1/tests/loom.rs +++ b/crates/zu-zu1/tests/loom.rs @@ -3,7 +3,7 @@ //! can reorder, exhausting every interleaving of the writer publishing //! commits, readers pinning snapshots, and the checkpoint computing //! its fold horizon. Run with -//! `RUSTFLAGS="--cfg loom" cargo test -q -p zu-zu1 --test loom --release`. +//! `RUSTFLAGS="--cfg loom" cargo test -q -p zudb-zu1 --test loom --release`. #![cfg(loom)] use loom::sync::Arc; diff --git a/crates/zu/Cargo.toml b/crates/zu/Cargo.toml index ea0ee38a..646efffb 100644 --- a/crates/zu/Cargo.toml +++ b/crates/zu/Cargo.toml @@ -1,13 +1,15 @@ [package] -name = "zu" +name = "zudb" description = "Embedded property-graph database: columnar, factorized, three storage engines" -publish = false version.workspace = true edition.workspace = true rust-version.workspace = true license.workspace = true repository.workspace = true authors.workspace = true +readme = "../../README.md" +keywords = ["database", "graph", "embedded", "gql", "columnar"] +categories = ["database-implementations"] [dependencies] zu-common.workspace = true diff --git a/crates/zu/benches/alloc.rs b/crates/zu/benches/alloc.rs index d755188c..c9fa3800 100644 --- a/crates/zu/benches/alloc.rs +++ b/crates/zu/benches/alloc.rs @@ -29,15 +29,15 @@ //! The counter is off except during the counted runs, so nothing here //! is measuring the warmup or the graph build. //! -//! Run: ZU_GATE=1 cargo bench -p zu --bench alloc +//! Run: ZU_GATE=1 cargo bench -p zudb --bench alloc use std::alloc::{GlobalAlloc, Layout, System}; use std::sync::atomic::{AtomicBool, AtomicU64, Ordering}; -use zu::query::Value; -use zu::session::Session; -use zu::zu1::file::Zu1File; -use zu::zu1::graph::bulk_load_as; +use zudb::query::Value; +use zudb::session::Session; +use zudb::zu1::file::Zu1File; +use zudb::zu1::graph::bulk_load_as; fn budget(key: &str) -> Option { let path = concat!(env!("CARGO_MANIFEST_DIR"), "/../../bench/budgets.toml"); @@ -148,7 +148,7 @@ fn build(path: &std::path::Path) -> Vec { degree } -fn count_of(r: &zu::query::QueryResult) -> i64 { +fn count_of(r: &zudb::query::QueryResult) -> i64 { match r.rows.first().and_then(|row| row.first()) { Some(Value::Int(n)) => *n, other => panic!("expected one count, got {other:?}"), diff --git a/crates/zu/benches/append.rs b/crates/zu/benches/append.rs index e603b170..744bcc43 100644 --- a/crates/zu/benches/append.rs +++ b/crates/zu/benches/append.rs @@ -14,15 +14,15 @@ //! back through a query and compares them against what went in, so a //! run that got faster by writing less fails instead of scoring. //! -//! Run: ZU_GATE=1 cargo bench -p zu --bench append +//! Run: ZU_GATE=1 cargo bench -p zudb --bench append use std::time::Instant; -use zu::query::Value; -use zu::zu1::file::Zu1File; -use zu::zu1::graph::bulk_load_as; -use zu::zu1::props::{PropValues, store_props}; -use zu::{Config, Database}; +use zudb::query::Value; +use zudb::zu1::file::Zu1File; +use zudb::zu1::graph::bulk_load_as; +use zudb::zu1::props::{PropValues, store_props}; +use zudb::{Config, Database}; fn budget(key: &str) -> Option { let path = concat!(env!("CARGO_MANIFEST_DIR"), "/../../bench/budgets.toml"); @@ -68,7 +68,7 @@ fn build(path: &std::path::Path) { .expect("props"); } -fn people(conn: &mut zu::Connection) -> i64 { +fn people(conn: &mut zudb::Connection) -> i64 { let r = conn .query("MATCH (p:person) RETURN count(p) AS n") .expect("count"); diff --git a/crates/zu/benches/batch.rs b/crates/zu/benches/batch.rs index 9f490ce0..0145909a 100644 --- a/crates/zu/benches/batch.rs +++ b/crates/zu/benches/batch.rs @@ -40,14 +40,14 @@ //! exec_batch_mkeys_s_core floors the batch read in millions of keys a //! second. //! -//! Run: ZU_GATE=1 cargo bench -p zu --bench batch +//! Run: ZU_GATE=1 cargo bench -p zudb --bench batch use std::time::Instant; -use zu::query::{self, Value}; -use zu::zu1::file::Zu1File; -use zu::zu1::graph::bulk_load_as; -use zu::zu1::props::{PropValues, store_props}; +use zudb::query::{self, Value}; +use zudb::zu1::file::Zu1File; +use zudb::zu1::graph::bulk_load_as; +use zudb::zu1::props::{PropValues, store_props}; fn budget(key: &str) -> Option { let path = concat!(env!("CARGO_MANIFEST_DIR"), "/../../bench/budgets.toml"); diff --git a/crates/zu/benches/branch.rs b/crates/zu/benches/branch.rs index 894d1bcf..3846f723 100644 --- a/crates/zu/benches/branch.rs +++ b/crates/zu/benches/branch.rs @@ -48,14 +48,14 @@ //! end is read by nobody and turns into a weight. That is the one that //! notices a fallback, since the walk it skips is most of the work. //! -//! Run: ZU_GATE=1 cargo bench -p zu --bench branch +//! Run: ZU_GATE=1 cargo bench -p zudb --bench branch use std::time::Instant; -use zu::query::{self, Value}; -use zu::zu1::file::Zu1File; -use zu::zu1::graph::bulk_load_as; -use zu::zu1::props::{PropValues, store_props}; +use zudb::query::{self, Value}; +use zudb::zu1::file::Zu1File; +use zudb::zu1::graph::bulk_load_as; +use zudb::zu1::props::{PropValues, store_props}; fn budget(key: &str) -> Option { let path = concat!(env!("CARGO_MANIFEST_DIR"), "/../../bench/budgets.toml"); diff --git a/crates/zu/benches/call.rs b/crates/zu/benches/call.rs index 45402cb9..0ebae693 100644 --- a/crates/zu/benches/call.rs +++ b/crates/zu/benches/call.rs @@ -37,14 +37,14 @@ //! exec_call_mrows_s_core floors the count case in millions of yielded //! rows a second, end to end. //! -//! Run: ZU_GATE=1 cargo bench -p zu --bench call +//! Run: ZU_GATE=1 cargo bench -p zudb --bench call use std::time::Instant; -use zu::query::{self, Value}; -use zu::zu1::algo; -use zu::zu1::file::Zu1File; -use zu::zu1::graph::{GraphReader, bulk_load_as}; +use zudb::query::{self, Value}; +use zudb::zu1::algo; +use zudb::zu1::file::Zu1File; +use zudb::zu1::graph::{GraphReader, bulk_load_as}; fn budget(key: &str) -> Option { let path = concat!(env!("CARGO_MANIFEST_DIR"), "/../../bench/budgets.toml"); diff --git a/crates/zu/benches/cardinality.rs b/crates/zu/benches/cardinality.rs index ec554bd9..2eb2aee9 100644 --- a/crates/zu/benches/cardinality.rs +++ b/crates/zu/benches/cardinality.rs @@ -22,14 +22,14 @@ //! card_gen_qerror_p90 and card_gen_qerror_max are ceilings on the //! pooled q-errors across all three shapes. //! -//! Run: ZU_GATE=1 cargo bench -p zu --bench cardinality +//! Run: ZU_GATE=1 cargo bench -p zudb --bench cardinality use std::time::Instant; -use zu::query::{self, Value}; -use zu::zu1::file::Zu1File; -use zu::zu1::graph::bulk_load_as; -use zu::zu1::props::{PropValues, store_props}; +use zudb::query::{self, Value}; +use zudb::zu1::file::Zu1File; +use zudb::zu1::graph::bulk_load_as; +use zudb::zu1::props::{PropValues, store_props}; fn budget(key: &str) -> Option { let path = concat!(env!("CARGO_MANIFEST_DIR"), "/../../bench/budgets.toml"); @@ -119,7 +119,7 @@ fn build(path: &std::path::Path, edges: &[(u32, u32)]) { ], ) .expect("props"); - zu::zu1::colors::analyze(&mut db).expect("analyze"); + zudb::zu1::colors::analyze(&mut db).expect("analyze"); } /// One entry of the corpus: a name for the printout, the query text, diff --git a/crates/zu/benches/columns.rs b/crates/zu/benches/columns.rs index e04b53b7..c942e014 100644 --- a/crates/zu/benches/columns.rs +++ b/crates/zu/benches/columns.rs @@ -22,16 +22,16 @@ //! Everything runs at one worker, so the rate is per core and the //! fleet's core counts stay out of the number. //! -//! Run: cargo bench -p zu --bench columns +//! Run: cargo bench -p zudb --bench columns use std::hint::black_box; use std::time::Instant; -use zu::query::column::ColumnData; -use zu::query::{self, QueryResult, Value}; -use zu::zu1::file::Zu1File; -use zu::zu1::graph::bulk_load_as; -use zu::zu1::props::{PropValues, store_props}; +use zudb::query::column::ColumnData; +use zudb::query::{self, QueryResult, Value}; +use zudb::zu1::file::Zu1File; +use zudb::zu1::graph::bulk_load_as; +use zudb::zu1::props::{PropValues, store_props}; const NODES: u64 = 1_000_000; const RUNS: usize = 5; diff --git a/crates/zu/benches/commit.rs b/crates/zu/benches/commit.rs index 608c7e4d..f5ebb1bd 100644 --- a/crates/zu/benches/commit.rs +++ b/crates/zu/benches/commit.rs @@ -89,17 +89,17 @@ //! needs more than a couple of hundred of them before which //! percentile it lands in stops being luck. //! -//! Run: ZU_GATE=1 cargo bench -p zu --bench commit +//! Run: ZU_GATE=1 cargo bench -p zudb --bench commit use std::path::Path; use std::time::Instant; -use zu::query::Value; -use zu::zu1::file::Zu1File; -use zu::zu1::graph::bulk_load_as; -use zu::zu1::props::{PropValues, store_props}; -use zu::zu1::wal::commit_counters; -use zu::{Config, Database}; +use zudb::query::Value; +use zudb::zu1::file::Zu1File; +use zudb::zu1::graph::bulk_load_as; +use zudb::zu1::props::{PropValues, store_props}; +use zudb::zu1::wal::commit_counters; +use zudb::{Config, Database}; /// The table the writers write into. Small, because what is measured is /// the commit and not the table: an `INSERT` adds a row past the end of diff --git a/crates/zu/benches/compare.rs b/crates/zu/benches/compare.rs index 2e7cef32..cf252864 100644 --- a/crates/zu/benches/compare.rs +++ b/crates/zu/benches/compare.rs @@ -39,14 +39,14 @@ //! exec_compare_mrows_s_core floors the float bound, the shape the //! rewrite is for. //! -//! Run: ZU_GATE=1 cargo bench -p zu --bench compare +//! Run: ZU_GATE=1 cargo bench -p zudb --bench compare use std::time::Instant; -use zu::query::{self, Value}; -use zu::zu1::file::Zu1File; -use zu::zu1::graph::bulk_load_as; -use zu::zu1::props::{PropValues, store_props}; +use zudb::query::{self, Value}; +use zudb::zu1::file::Zu1File; +use zudb::zu1::graph::bulk_load_as; +use zudb::zu1::props::{PropValues, store_props}; fn budget(key: &str) -> Option { let path = concat!(env!("CARGO_MANIFEST_DIR"), "/../../bench/budgets.toml"); @@ -97,7 +97,7 @@ fn build(path: &std::path::Path) { } /// The one row and the count in it. -fn count(r: &zu::query::QueryResult) -> i64 { +fn count(r: &zudb::query::QueryResult) -> i64 { assert_eq!(r.rows.len(), 1, "a counting query returns one row"); match r.rows[0][0] { Value::Int(n) => n, diff --git a/crates/zu/benches/connect.rs b/crates/zu/benches/connect.rs index e5863253..9d48ecf4 100644 --- a/crates/zu/benches/connect.rs +++ b/crates/zu/benches/connect.rs @@ -21,15 +21,15 @@ //! of the warm point read through a connection to the same read through //! a session, and it is the number that says this API is free. //! -//! Run: ZU_GATE=1 cargo bench -p zu --bench connect +//! Run: ZU_GATE=1 cargo bench -p zudb --bench connect use std::time::Instant; -use zu::query::Value; -use zu::session::Session; -use zu::zu1::file::Zu1File; -use zu::zu1::graph::bulk_load_as; -use zu::{Config, Database}; +use zudb::query::Value; +use zudb::session::Session; +use zudb::zu1::file::Zu1File; +use zudb::zu1::graph::bulk_load_as; +use zudb::{Config, Database}; fn budget(key: &str) -> Option { let path = concat!(env!("CARGO_MANIFEST_DIR"), "/../../bench/budgets.toml"); @@ -76,7 +76,7 @@ fn build(path: &std::path::Path) -> Vec { degree } -fn count_of(r: &zu::query::QueryResult) -> i64 { +fn count_of(r: &zudb::query::QueryResult) -> i64 { match r.rows.first().and_then(|row| row.first()) { Some(Value::Int(n)) => *n, other => panic!("expected one count, got {other:?}"), diff --git a/crates/zu/benches/convert.rs b/crates/zu/benches/convert.rs index 3dba3114..eff48c76 100644 --- a/crates/zu/benches/convert.rs +++ b/crates/zu/benches/convert.rs @@ -24,13 +24,13 @@ //! wrote, so a conversion that got faster by writing less fails here //! instead of scoring. //! -//! Run: ZU_GATE=1 cargo bench -p zu --bench convert +//! Run: ZU_GATE=1 cargo bench -p zudb --bench convert use std::time::Instant; -use zu::query::Value; -use zu::zu1::file::Zu1File; use zu_sqlite::{ColumnType, SqliteStore, Value as SqlValue}; +use zudb::query::Value; +use zudb::zu1::file::Zu1File; fn budget(key: &str) -> Option { let path = concat!(env!("CARGO_MANIFEST_DIR"), "/../../bench/budgets.toml"); @@ -84,7 +84,7 @@ fn stage(path: &std::path::Path) { fn check(path: &std::path::Path) { let mut db = Zu1File::open(path).expect("open converted"); let count = |db: &mut Zu1File, source: &str| -> i64 { - let r = zu::query::run(source, db, &[]).expect(source); + let r = zudb::query::run(source, db, &[]).expect(source); match r.rows.first().and_then(|row| row.first()) { Some(Value::Int(n)) => *n, other => panic!("{source}: expected a count, got {other:?}"), @@ -105,7 +105,7 @@ fn check(path: &std::path::Path) { ); // A property read of the last row, which is the row a conversion // that stopped early would be missing. - let r = zu::query::run( + let r = zudb::query::run( "MATCH (p:person) WHERE p.id = 999999 RETURN p.name AS name", &mut db, &[], @@ -132,7 +132,7 @@ fn main() { let out = dir.path().join("converted.zu1"); let t = Instant::now(); - zu::convert::sqlite_to_zu1(&staging, &out).expect("convert"); + zudb::convert::sqlite_to_zu1(&staging, &out).expect("convert"); let secs = t.elapsed().as_secs_f64(); check(&out); let nodes_s = NODES as f64 / secs; diff --git a/crates/zu/benches/density.rs b/crates/zu/benches/density.rs index 3fcd67a3..20d198e0 100644 --- a/crates/zu/benches/density.rs +++ b/crates/zu/benches/density.rs @@ -27,11 +27,11 @@ //! rather than gated, because a ceiling on a ratio fails when the good //! side of it improves. //! -//! Run: ZU_GATE=1 cargo bench -p zu --bench density +//! Run: ZU_GATE=1 cargo bench -p zudb --bench density -use zu::query::Value; -use zu::zu1::file::Zu1File; use zu_sqlite::{ColumnType, SqliteStore, Value as SqlValue}; +use zudb::query::Value; +use zudb::zu1::file::Zu1File; fn budget(key: &str) -> Option { let path = concat!(env!("CARGO_MANIFEST_DIR"), "/../../bench/budgets.toml"); @@ -148,7 +148,7 @@ fn build(dir: &std::path::Path, shape: Shape) -> (std::path::PathBuf, u64) { sq.checkpoint().expect("checkpoint"); let out = dir.join(format!("{name}.zu1")); - zu::convert::sqlite_to_zu1(&staging, &out).expect("convert"); + zudb::convert::sqlite_to_zu1(&staging, &out).expect("convert"); if shape == Shape::Closed { // The closed side is closed: the file gets a graph type naming // the element types its tables hold, which is the object the @@ -156,7 +156,7 @@ fn build(dir: &std::path::Path, shape: Shape) -> (std::path::PathBuf, u64) { // is inside the closed figure, since a type nobody paid for is // not a comparison. let mut db = Zu1File::open(&out).expect("open closed"); - zu::query::run("CREATE GRAPH TYPE social LIKE home", &mut db, &[]).expect("graph type"); + zudb::query::run("CREATE GRAPH TYPE social LIKE home", &mut db, &[]).expect("graph type"); } let bytes = std::fs::metadata(&out).expect("metadata").len(); (out, bytes) @@ -169,7 +169,7 @@ fn build(dir: &std::path::Path, shape: Shape) -> (std::path::PathBuf, u64) { fn check(path: &std::path::Path, shape: Shape) { let mut db = Zu1File::open(path).expect("open"); let one = |db: &mut Zu1File, source: &str| -> Value { - let r = zu::query::run(source, db, &[]).unwrap_or_else(|e| panic!("{source}: {e}")); + let r = zudb::query::run(source, db, &[]).unwrap_or_else(|e| panic!("{source}: {e}")); r.rows .first() .and_then(|row| row.first()) diff --git a/crates/zu/benches/dynamic.rs b/crates/zu/benches/dynamic.rs index f38ce4f2..87ca5c87 100644 --- a/crates/zu/benches/dynamic.rs +++ b/crates/zu/benches/dynamic.rs @@ -44,15 +44,15 @@ //! the row engine would be a promise about a path this milestone is //! explicitly not making fast. //! -//! Run: cargo bench -p zu --bench dynamic +//! Run: cargo bench -p zudb --bench dynamic use std::time::Instant; -use zu::query::{self, Value}; -use zu::session::Session; -use zu::zu1::file::Zu1File; -use zu::zu1::graph::bulk_load_as; -use zu::zu1::props::{PropValues, store_props}; +use zudb::query::{self, Value}; +use zudb::session::Session; +use zudb::zu1::file::Zu1File; +use zudb::zu1::graph::bulk_load_as; +use zudb::zu1::props::{PropValues, store_props}; const NODES: u64 = 2_000_000; @@ -69,7 +69,7 @@ fn build(path: &std::path::Path) { store_props(&mut db, "person", &[("age", PropValues::Int(&age))]).expect("props"); } -fn count(r: &zu::query::QueryResult) -> i64 { +fn count(r: &zudb::query::QueryResult) -> i64 { assert_eq!(r.rows.len(), 1, "a counting query returns one row"); match r.rows[0][0] { Value::Int(n) => n, diff --git a/crates/zu/benches/exists.rs b/crates/zu/benches/exists.rs index 3eb7c99a..52f13b75 100644 --- a/crates/zu/benches/exists.rs +++ b/crates/zu/benches/exists.rs @@ -55,14 +55,14 @@ //! exec_exists_mrows_s_core floors the counted semi in millions of //! outer rows a second. //! -//! Run: ZU_GATE=1 cargo bench -p zu --bench exists +//! Run: ZU_GATE=1 cargo bench -p zudb --bench exists use std::time::Instant; -use zu::query::{self, Value}; -use zu::zu1::file::Zu1File; -use zu::zu1::graph::bulk_load_as; -use zu::zu1::props::{PropValues, store_props}; +use zudb::query::{self, Value}; +use zudb::zu1::file::Zu1File; +use zudb::zu1::graph::bulk_load_as; +use zudb::zu1::props::{PropValues, store_props}; fn budget(key: &str) -> Option { let path = concat!(env!("CARGO_MANIFEST_DIR"), "/../../bench/budgets.toml"); diff --git a/crates/zu/benches/factor.rs b/crates/zu/benches/factor.rs index 2978a8a3..adc65e99 100644 --- a/crates/zu/benches/factor.rs +++ b/crates/zu/benches/factor.rs @@ -31,14 +31,14 @@ //! exec_factor_mrows_s_core floors the counted key in millions of hop //! rows a second, the rows the walk would have produced. //! -//! Run: ZU_GATE=1 cargo bench -p zu --bench factor +//! Run: ZU_GATE=1 cargo bench -p zudb --bench factor use std::time::Instant; -use zu::query::{self, Value}; -use zu::zu1::file::Zu1File; -use zu::zu1::graph::bulk_load_as; -use zu::zu1::props::{PropValues, store_props}; +use zudb::query::{self, Value}; +use zudb::zu1::file::Zu1File; +use zudb::zu1::graph::bulk_load_as; +use zudb::zu1::props::{PropValues, store_props}; fn budget(key: &str) -> Option { let path = concat!(env!("CARGO_MANIFEST_DIR"), "/../../bench/budgets.toml"); diff --git a/crates/zu/benches/fold.rs b/crates/zu/benches/fold.rs index 2076fe78..4177a559 100644 --- a/crates/zu/benches/fold.rs +++ b/crates/zu/benches/fold.rs @@ -33,16 +33,16 @@ //! and seven tenths. fold_over_list_x is set to catch that and not to //! catch a tenth. //! -//! Run: ZU_GATE=1 cargo bench -p zu --bench fold +//! Run: ZU_GATE=1 cargo bench -p zudb --bench fold use std::alloc::{GlobalAlloc, Layout, System}; use std::sync::atomic::{AtomicBool, AtomicU64, Ordering}; use std::time::Instant; -use zu::query::Value; -use zu::session::Session; -use zu::zu1::file::Zu1File; -use zu::zu1::graph::bulk_load_as; +use zudb::query::Value; +use zudb::session::Session; +use zudb::zu1::file::Zu1File; +use zudb::zu1::graph::bulk_load_as; fn budget(key: &str) -> Option { let path = concat!(env!("CARGO_MANIFEST_DIR"), "/../../bench/budgets.toml"); diff --git a/crates/zu/benches/frames.rs b/crates/zu/benches/frames.rs index b655903e..6e5d4645 100644 --- a/crates/zu/benches/frames.rs +++ b/crates/zu/benches/frames.rs @@ -13,7 +13,7 @@ //! and the two eight-byte lanes are read where they lie, so a scan of a //! frame should run at memory speed rather than at decode speed. //! -//! Run: cargo bench -p zu --bench frames +//! Run: cargo bench -p zudb --bench frames use std::any::Any; use std::hint::black_box; @@ -21,10 +21,10 @@ use std::ptr::NonNull; use std::sync::Arc; use std::time::Instant; -use zu::query::Value; -use zu::zu1::file::Zu1File; -use zu::zu1::graph::bulk_load_as; -use zu::{Column, Database, FloatBits, Frame, IntBits, Layout, LogicalType}; +use zudb::query::Value; +use zudb::zu1::file::Zu1File; +use zudb::zu1::graph::bulk_load_as; +use zudb::{Column, Database, FloatBits, Frame, IntBits, Layout, LogicalType}; const ROWS: usize = 10_000_000; diff --git a/crates/zu/benches/graphalytics.rs b/crates/zu/benches/graphalytics.rs index 0560abf7..82254944 100644 --- a/crates/zu/benches/graphalytics.rs +++ b/crates/zu/benches/graphalytics.rs @@ -32,17 +32,17 @@ //! Get the data: curl -sO https://datasets.ldbcouncil.org/graphalytics/kgs.tar.zst //! && tar --zstd -xf kgs.tar.zst under ZU_DATA. //! -//! Run: ZU_GATE=1 ZU_DATA=~/data/zu cargo bench -p zu --bench graphalytics +//! Run: ZU_GATE=1 ZU_DATA=~/data/zu cargo bench -p zudb --bench graphalytics use std::collections::HashMap; use std::time::Instant; -use zu::zu1::algo; -use zu::zu1::file::Zu1File; -use zu::zu1::graph::{ +use zu_query::exec::Value; +use zudb::zu1::algo; +use zudb::zu1::file::Zu1File; +use zudb::zu1::graph::{ GraphReader, bulk_load_keyed, densify_keyed, read_key_edge_list, read_key_list, }; -use zu_query::exec::Value; /// The marker Graphalytics reference files use for an unreachable /// vertex in BFS output. @@ -214,7 +214,7 @@ fn main() { println!("kgs louvain: {count} communities in {louvain_s:.3} s, deterministic across two runs"); let t = Instant::now(); - let r = zu::query::run( + let r = zudb::query::run( "CALL wcc('e') YIELD node, component RETURN count(DISTINCT component) AS c", &mut db, &[], diff --git a/crates/zu/benches/groupby.rs b/crates/zu/benches/groupby.rs index 5eaa89f0..c47ba2cf 100644 --- a/crates/zu/benches/groupby.rs +++ b/crates/zu/benches/groupby.rs @@ -21,14 +21,14 @@ //! and the final sort of the groups together, because that is what a //! user waits for. //! -//! Run: ZU_GATE=1 cargo bench -p zu --bench groupby +//! Run: ZU_GATE=1 cargo bench -p zudb --bench groupby use std::time::Instant; -use zu::query::{self, Value}; -use zu::zu1::file::Zu1File; -use zu::zu1::graph::bulk_load_as; -use zu::zu1::props::{PropValues, store_props}; +use zudb::query::{self, Value}; +use zudb::zu1::file::Zu1File; +use zudb::zu1::graph::bulk_load_as; +use zudb::zu1::props::{PropValues, store_props}; fn budget(key: &str) -> Option { let path = concat!(env!("CARGO_MANIFEST_DIR"), "/../../bench/budgets.toml"); @@ -78,7 +78,7 @@ fn build(path: &std::path::Path) { /// Rows returned and the total of the count column, which must be every /// scanned row whatever the key was. -fn shape(r: &zu::query::QueryResult) -> (usize, i64) { +fn shape(r: &zudb::query::QueryResult) -> (usize, i64) { let total = r .rows .iter() diff --git a/crates/zu/benches/join.rs b/crates/zu/benches/join.rs index 75c805ea..fc6dd6f8 100644 --- a/crates/zu/benches/join.rs +++ b/crates/zu/benches/join.rs @@ -108,14 +108,14 @@ //! that goes into the walk, and that one sits above one on every host, //! because a row it rejects is a row nothing builds. //! -//! Run: ZU_GATE=1 cargo bench -p zu --bench join +//! Run: ZU_GATE=1 cargo bench -p zudb --bench join use std::time::Instant; -use zu::query::{self, Value}; -use zu::zu1::file::Zu1File; -use zu::zu1::graph::bulk_load_as; -use zu::zu1::props::{PropValues, store_props}; +use zudb::query::{self, Value}; +use zudb::zu1::file::Zu1File; +use zudb::zu1::graph::bulk_load_as; +use zudb::zu1::props::{PropValues, store_props}; fn budget(key: &str) -> Option { let path = concat!(env!("CARGO_MANIFEST_DIR"), "/../../bench/budgets.toml"); diff --git a/crates/zu/benches/ldbc.rs b/crates/zu/benches/ldbc.rs index 8b65b6ab..43624173 100644 --- a/crates/zu/benches/ldbc.rs +++ b/crates/zu/benches/ldbc.rs @@ -23,7 +23,7 @@ //! undirected, the shape that keeps the binary probe and so runs the //! semijoin folded into the expand, p50 in ms. IS is the IS1-shaped //! profile read by original -//! id, all eight properties through zu::query::run, gated at the T2 1 +//! id, all eight properties through zudb::query::run, gated at the T2 1 //! ms warm p50. //! IC is an IC-shaped 2-hop friends-of-friends read with DISTINCT, //! ORDER BY, and LIMIT, p50 in ms. Distinct two-hop is the same @@ -43,17 +43,17 @@ //! nonzero when a ceiling in bench/budgets.toml is missed, and missing //! data fails the gate instead of skipping it. //! -//! Run: ZU_GATE=1 ZU_DATA=~/data/zu cargo bench -p zu --bench ldbc +//! Run: ZU_GATE=1 ZU_DATA=~/data/zu cargo bench -p zudb --bench ldbc use std::collections::HashMap; use std::time::Instant; -use zu::zu1::file::Zu1File; -use zu::zu1::graph::{ +use zu_query::exec::Value; +use zudb::zu1::file::Zu1File; +use zudb::zu1::graph::{ Direction, GraphReader, bulk_load_keyed, densify_keyed, read_key_edge_list, read_key_list, }; -use zu::zu1::props::{PropValues, store_props}; -use zu_query::exec::Value; +use zudb::zu1::props::{PropValues, store_props}; /// The tail of `struct rusage` this bench does not read, sized so the /// kernel writes inside the allocation rather than past it. @@ -268,7 +268,7 @@ fn load(data: &str, path: &std::path::Path) -> (Vec<(u32, u32)>, Vec, Profi ) .expect("store props"); let analyze_started = Instant::now(); - zu::zu1::colors::analyze(&mut db).expect("analyze"); + zudb::zu1::colors::analyze(&mut db).expect("analyze"); println!( "sf1: {} persons, {} knows edges, 9 props columns, parse {:.2}s, load {:.2}s, analyze {:.2}s", by_row.len(), @@ -439,12 +439,12 @@ fn run_two_hop(path: &std::path::Path, edges: &[(u32, u32)], node_count: u64) -> let source = Q_TWO_HOP; let runs = 50usize; for _ in 0..5 { - zu::query::run(source, &mut db, &[]).expect("warmup run"); + zudb::query::run(source, &mut db, &[]).expect("warmup run"); } let mut lat = Vec::with_capacity(runs); for _ in 0..runs { let t = Instant::now(); - let r = zu::query::run(source, &mut db, &[]).expect("two-hop count"); + let r = zudb::query::run(source, &mut db, &[]).expect("two-hop count"); lat.push(t.elapsed()); assert_eq!( r.rows, @@ -485,12 +485,12 @@ fn run_triangle_count(path: &std::path::Path, edges: &[(u32, u32)], node_count: let source = Q_TRIANGLE; let runs = 15usize; for _ in 0..3 { - zu::query::run(source, &mut db, &[]).expect("warmup run"); + zudb::query::run(source, &mut db, &[]).expect("warmup run"); } let mut lat = Vec::with_capacity(runs); for _ in 0..runs { let t = Instant::now(); - let r = zu::query::run(source, &mut db, &[]).expect("triangle count"); + let r = zudb::query::run(source, &mut db, &[]).expect("triangle count"); lat.push(t.elapsed()); assert_eq!( r.rows, @@ -513,12 +513,12 @@ fn run_triangle_count(path: &std::path::Path, edges: &[(u32, u32)], node_count: // the process-global variable is safe. unsafe { std::env::set_var("ZU_WCOJ", "0") }; for _ in 0..3 { - zu::query::run(source, &mut db, &[]).expect("binary warmup run"); + zudb::query::run(source, &mut db, &[]).expect("binary warmup run"); } let mut blat = Vec::with_capacity(runs); for _ in 0..runs { let t = Instant::now(); - let r = zu::query::run(source, &mut db, &[]).expect("binary triangle count"); + let r = zudb::query::run(source, &mut db, &[]).expect("binary triangle count"); blat.push(t.elapsed()); assert_eq!( r.rows, @@ -574,12 +574,12 @@ fn run_ordered_triangle( let source = Q_ORDERED; let runs = 15usize; for _ in 0..3 { - zu::query::run(source, &mut db, &[]).expect("warmup run"); + zudb::query::run(source, &mut db, &[]).expect("warmup run"); } let mut lat = Vec::with_capacity(runs); for _ in 0..runs { let t = Instant::now(); - let r = zu::query::run(source, &mut db, &[]).expect("ordered triangle"); + let r = zudb::query::run(source, &mut db, &[]).expect("ordered triangle"); lat.push(t.elapsed()); assert_eq!( r.rows, @@ -633,12 +633,12 @@ fn run_undirected_close( let source = Q_CLOSE; let runs = 15usize; for _ in 0..3 { - zu::query::run(source, &mut db, &[]).expect("warmup run"); + zudb::query::run(source, &mut db, &[]).expect("warmup run"); } let mut lat = Vec::with_capacity(runs); for _ in 0..runs { let t = Instant::now(); - let r = zu::query::run(source, &mut db, &[]).expect("undirected close"); + let r = zudb::query::run(source, &mut db, &[]).expect("undirected close"); lat.push(t.elapsed()); assert_eq!( r.rows, @@ -657,7 +657,7 @@ fn run_undirected_close( } /// IS: the IS1-shaped profile read, all eight person properties by -/// original id through zu::query::run, parse to result. Every measured +/// original id through zudb::query::run, parse to result. Every measured /// run is asserted field by field against the raw props file, so the /// number cannot come from a reader that returns the wrong row or a /// column stored out of order. @@ -669,7 +669,7 @@ fn run_is_reads(path: &std::path::Path, by_row: &[u64], profiles: &ProfileRows) for _ in 0..200 { let row = (xorshift(&mut rng) % n) as usize; let id = Value::Int(by_row[row] as i64); - zu::query::run(source, &mut db, &[("id", id)]).expect("warmup profile read"); + zudb::query::run(source, &mut db, &[("id", id)]).expect("warmup profile read"); } let runs = 2_000usize; let mut lat = Vec::with_capacity(runs); @@ -677,7 +677,7 @@ fn run_is_reads(path: &std::path::Path, by_row: &[u64], profiles: &ProfileRows) let row = (xorshift(&mut rng) % n) as usize; let id = Value::Int(by_row[row] as i64); let t = Instant::now(); - let r = zu::query::run(source, &mut db, &[("id", id)]).expect("profile read"); + let r = zudb::query::run(source, &mut db, &[("id", id)]).expect("profile read"); lat.push(t.elapsed()); let p = &profiles[row]; let want = vec![ @@ -738,7 +738,7 @@ fn run_distinct_two_hop( for _ in 0..50 { let seed = seeds[(xorshift(&mut rng) as usize) % seeds.len()]; let id = Value::Int(by_row[seed] as i64); - zu::query::run(source, &mut db, &[("id", id)]).expect("warmup distinct two-hop"); + zudb::query::run(source, &mut db, &[("id", id)]).expect("warmup distinct two-hop"); } let runs = 500usize; let mut lat = Vec::with_capacity(runs); @@ -746,7 +746,7 @@ fn run_distinct_two_hop( let seed = seeds[(xorshift(&mut rng) as usize) % seeds.len()]; let id = Value::Int(by_row[seed] as i64); let t = Instant::now(); - let r = zu::query::run(source, &mut db, &[("id", id)]).expect("distinct two-hop"); + let r = zudb::query::run(source, &mut db, &[("id", id)]).expect("distinct two-hop"); lat.push(t.elapsed()); assert_eq!( r.rows, @@ -805,7 +805,7 @@ fn run_ic_friends_of_friends( for _ in 0..50 { let seed = seeds[(xorshift(&mut rng) as usize) % seeds.len()]; let id = Value::Int(by_row[seed] as i64); - zu::query::run(source, &mut db, &[("id", id)]).expect("warmup fof read"); + zudb::query::run(source, &mut db, &[("id", id)]).expect("warmup fof read"); } let runs = 500usize; let mut lat = Vec::with_capacity(runs); @@ -813,7 +813,7 @@ fn run_ic_friends_of_friends( let seed = seeds[(xorshift(&mut rng) as usize) % seeds.len()]; let id = Value::Int(by_row[seed] as i64); let t = Instant::now(); - let r = zu::query::run(source, &mut db, &[("id", id)]).expect("fof read"); + let r = zudb::query::run(source, &mut db, &[("id", id)]).expect("fof read"); lat.push(t.elapsed()); assert_eq!( r.rows, @@ -936,7 +936,7 @@ fn run_cardinality( let mut violations = 0usize; for (name, source, params) in &corpus { let borrowed: Vec<(&str, Value)> = params.iter().map(|(k, v)| (*k, v.clone())).collect(); - let profile = zu::query::profile(source, &mut db, &borrowed).expect("profile"); + let profile = zudb::query::profile(source, &mut db, &borrowed).expect("profile"); let mut worst: Option<(f64, String, f64, u64)> = None; for stage in &profile.stages { for op in &stage.ops { @@ -987,7 +987,7 @@ fn run_table_functions( by_row: &[u64], node_count: u64, ) -> (f64, f64, f64, f64) { - use zu::zu1::algo; + use zudb::zu1::algo; let n = node_count as usize; let mut db = Zu1File::open(path).expect("open"); let mut reader = GraphReader::load_table(&mut db, "knows").expect("reader"); @@ -1112,7 +1112,7 @@ fn run_table_functions( println!("sf1 louvain: {count} communities in {louvain_s:.3} s, deterministic across two runs"); let t = Instant::now(); - let r = zu::query::run( + let r = zudb::query::run( "CALL pagerank('knows') YIELD node, rank RETURN count(node) AS n, sum(rank) AS total", &mut db, &[], @@ -1162,12 +1162,12 @@ fn attribute(path: &std::path::Path, label: &str, source: &str, seed: Option { let seeded = match seed { Some(id) => format!(", one run seeded with person {id}"), @@ -1249,7 +1249,7 @@ fn main() { "sf1 memory: {:.1} MiB memory_limit, {:.1} MiB resident after the load, \ {:.1} MiB highest between phases, {:.1} MiB peak \ (the peak includes the load and the crosscheck references)", - mib(zu::zu1::file::DEFAULT_MEMORY_LIMIT as u64), + mib(zudb::zu1::file::DEFAULT_MEMORY_LIMIT as u64), mib(resting), mib(between), mib(peak) diff --git a/crates/zu/benches/optional.rs b/crates/zu/benches/optional.rs index bbde5c61..b7630a14 100644 --- a/crates/zu/benches/optional.rs +++ b/crates/zu/benches/optional.rs @@ -36,14 +36,14 @@ //! exec_optional_mrows_s_core floors the counted bracket in millions of //! outer rows a second. //! -//! Run: ZU_GATE=1 cargo bench -p zu --bench optional +//! Run: ZU_GATE=1 cargo bench -p zudb --bench optional use std::time::Instant; -use zu::query::{self, Value}; -use zu::zu1::file::Zu1File; -use zu::zu1::graph::bulk_load_as; -use zu::zu1::props::{PropValues, store_props}; +use zudb::query::{self, Value}; +use zudb::zu1::file::Zu1File; +use zudb::zu1::graph::bulk_load_as; +use zudb::zu1::props::{PropValues, store_props}; fn budget(key: &str) -> Option { let path = concat!(env!("CARGO_MANIFEST_DIR"), "/../../bench/budgets.toml"); diff --git a/crates/zu/benches/paths.rs b/crates/zu/benches/paths.rs index 25917553..13b3a650 100644 --- a/crates/zu/benches/paths.rs +++ b/crates/zu/benches/paths.rs @@ -26,14 +26,14 @@ //! Both are crosschecked against the closed form, so a run that got //! fast by answering the wrong number fails instead of scoring. //! -//! Run: ZU_GATE=1 cargo bench -p zu --bench paths +//! Run: ZU_GATE=1 cargo bench -p zudb --bench paths use std::time::Instant; -use zu::query::Value; -use zu::session::Session; -use zu::zu1::file::Zu1File; -use zu::zu1::graph::bulk_load_as; +use zudb::query::Value; +use zudb::session::Session; +use zudb::zu1::file::Zu1File; +use zudb::zu1::graph::bulk_load_as; fn budget(key: &str) -> Option { let path = concat!(env!("CARGO_MANIFEST_DIR"), "/../../bench/budgets.toml"); diff --git a/crates/zu/benches/project.rs b/crates/zu/benches/project.rs index a4c53527..2eaf61f9 100644 --- a/crates/zu/benches/project.rs +++ b/crates/zu/benches/project.rs @@ -26,14 +26,14 @@ //! exec_project_mrows_s_core floors the summed expression, the shape //! with no row build in the way of the arithmetic. //! -//! Run: ZU_GATE=1 cargo bench -p zu --bench project +//! Run: ZU_GATE=1 cargo bench -p zudb --bench project use std::time::Instant; -use zu::query::{self, Value}; -use zu::zu1::file::Zu1File; -use zu::zu1::graph::bulk_load_as; -use zu::zu1::props::{PropValues, store_props}; +use zudb::query::{self, Value}; +use zudb::zu1::file::Zu1File; +use zudb::zu1::graph::bulk_load_as; +use zudb::zu1::props::{PropValues, store_props}; fn budget(key: &str) -> Option { let path = concat!(env!("CARGO_MANIFEST_DIR"), "/../../bench/budgets.toml"); @@ -86,7 +86,7 @@ fn value_of(i: u64) -> i64 { /// Rows returned and the total of the last column, which is the only /// crosscheck all three shapes share. -fn shape(r: &zu::query::QueryResult) -> (usize, i64) { +fn shape(r: &zudb::query::QueryResult) -> (usize, i64) { let last = r.columns.len() - 1; let total = r .rows diff --git a/crates/zu/benches/refuse.rs b/crates/zu/benches/refuse.rs index c2d06ac5..689fb65b 100644 --- a/crates/zu/benches/refuse.rs +++ b/crates/zu/benches/refuse.rs @@ -42,16 +42,16 @@ //! close to the machine it was written for and is reported and not held //! anywhere else. See [`REFERENCE_ANSWER_US`]. //! -//! Run: ZU_GATE=1 cargo bench -p zu --bench refuse +//! Run: ZU_GATE=1 cargo bench -p zudb --bench refuse use std::time::Instant; -use zu::gqlstatus::codes; -use zu::query::Value; -use zu::session::Session; -use zu::zu1::file::Zu1File; -use zu::zu1::graph::bulk_load_as; -use zu::{GqlStatus, ZuError}; +use zudb::gqlstatus::codes; +use zudb::query::Value; +use zudb::session::Session; +use zudb::zu1::file::Zu1File; +use zudb::zu1::graph::bulk_load_as; +use zudb::{GqlStatus, ZuError}; fn budget(key: &str) -> Option { let path = concat!(env!("CARGO_MANIFEST_DIR"), "/../../bench/budgets.toml"); @@ -139,7 +139,7 @@ fn build(path: &std::path::Path) -> Vec { degree } -fn count_of(r: &zu::query::QueryResult) -> i64 { +fn count_of(r: &zudb::query::QueryResult) -> i64 { match r.rows.first().and_then(|row| row.first()) { Some(Value::Int(n)) => *n, other => panic!("expected one count, got {other:?}"), diff --git a/crates/zu/benches/relprops.rs b/crates/zu/benches/relprops.rs index 61e30b0a..5527df48 100644 --- a/crates/zu/benches/relprops.rs +++ b/crates/zu/benches/relprops.rs @@ -19,14 +19,14 @@ //! column off the node the edge lands on, which is the same walk and //! the same number of column reads with the ordinal lookup taken out. //! -//! Run: ZU_GATE=1 cargo bench -p zu --bench relprops +//! Run: ZU_GATE=1 cargo bench -p zudb --bench relprops use std::time::Instant; -use zu::query::{self, Value}; -use zu::zu1::file::Zu1File; -use zu::zu1::graph::bulk_load_keyed; -use zu::zu1::props::{PropValues, store_props, store_rel_props}; +use zudb::query::{self, Value}; +use zudb::zu1::file::Zu1File; +use zudb::zu1::graph::bulk_load_keyed; +use zudb::zu1::props::{PropValues, store_props, store_rel_props}; const NODES: u32 = 200_000; const DEGREE: u32 = 10; diff --git a/crates/zu/benches/rows.rs b/crates/zu/benches/rows.rs index 20043e62..91e5fe4b 100644 --- a/crates/zu/benches/rows.rs +++ b/crates/zu/benches/rows.rs @@ -24,14 +24,14 @@ //! measures about five times the read by index, which is the number //! that tells a caller to hoist `column_index` out of its loop. //! -//! Run: ZU_GATE=1 cargo bench -p zu --bench rows +//! Run: ZU_GATE=1 cargo bench -p zudb --bench rows use std::time::Instant; -use zu::query::{QueryResult, Value}; -use zu::zu1::file::Zu1File; -use zu::zu1::graph::bulk_load_as; -use zu::{Config, Database, params}; +use zudb::query::{QueryResult, Value}; +use zudb::zu1::file::Zu1File; +use zudb::zu1::graph::bulk_load_as; +use zudb::{Config, Database, params}; fn budget(key: &str) -> Option { let path = concat!(env!("CARGO_MANIFEST_DIR"), "/../../bench/budgets.toml"); diff --git a/crates/zu/benches/scale.rs b/crates/zu/benches/scale.rs index 59c208cf..be42c07c 100644 --- a/crates/zu/benches/scale.rs +++ b/crates/zu/benches/scale.rs @@ -8,7 +8,7 @@ //! keeps single-group scans sequential because forking snapshots //! costs more than the scan, so the parallel path needs a table that //! actually earns it. Two queries run at 1, 2, 4, and 8 threads -//! through the public zu::query::run path, a scan-filter-count and an +//! through the public zudb::query::run path, a scan-filter-count and an //! expand-filter-count whose per-row gathers give workers real work //! beyond memory bandwidth. The intermediate counts separate executor //! scaling from the host ceiling: a machine with four fast cores and @@ -21,14 +21,14 @@ //! one. The gate only arms on hosts with at least 8 cores; server1 //! has 4 and prints information numbers. //! -//! Run: ZU_GATE=1 cargo bench -p zu --bench scale +//! Run: ZU_GATE=1 cargo bench -p zudb --bench scale use std::time::Instant; -use zu::query::{self, Value}; -use zu::zu1::file::Zu1File; -use zu::zu1::graph::bulk_load_as; -use zu::zu1::props::{PropValues, store_props}; +use zudb::query::{self, Value}; +use zudb::zu1::file::Zu1File; +use zudb::zu1::graph::bulk_load_as; +use zudb::zu1::props::{PropValues, store_props}; fn budget(key: &str) -> Option { let path = concat!(env!("CARGO_MANIFEST_DIR"), "/../../bench/budgets.toml"); @@ -78,7 +78,7 @@ fn build(path: &std::path::Path) -> Vec<(u32, u32)> { edges } -fn count_of(r: &zu::query::QueryResult) -> i64 { +fn count_of(r: &zudb::query::QueryResult) -> i64 { match r.rows.first().and_then(|row| row.first()) { Some(Value::Int(n)) => *n, other => panic!("expected one count, got {other:?}"), diff --git a/crates/zu/benches/session.rs b/crates/zu/benches/session.rs index 2c265485..b9ddacdb 100644 --- a/crates/zu/benches/session.rs +++ b/crates/zu/benches/session.rs @@ -27,14 +27,14 @@ //! in fifty, and that is exactly the regression these gates exist to //! catch. //! -//! Run: ZU_GATE=1 cargo bench -p zu --bench session +//! Run: ZU_GATE=1 cargo bench -p zudb --bench session use std::time::Instant; -use zu::query::Value; -use zu::session::Session; -use zu::zu1::file::Zu1File; -use zu::zu1::graph::bulk_load_as; +use zudb::query::Value; +use zudb::session::Session; +use zudb::zu1::file::Zu1File; +use zudb::zu1::graph::bulk_load_as; fn budget(key: &str) -> Option { let path = concat!(env!("CARGO_MANIFEST_DIR"), "/../../bench/budgets.toml"); @@ -89,7 +89,7 @@ fn build(path: &std::path::Path) -> Vec { degree } -fn count_of(r: &zu::query::QueryResult) -> i64 { +fn count_of(r: &zudb::query::QueryResult) -> i64 { match r.rows.first().and_then(|row| row.first()) { Some(Value::Int(n)) => *n, other => panic!("expected one count, got {other:?}"), @@ -186,7 +186,7 @@ fn run_one_shot_point(path: &std::path::Path, degree: &[i64]) -> (f64, f64) { let src = (xorshift(&mut rng) % u64::from(NODES)) as i64; let start = Instant::now(); let mut db = Zu1File::open(path).expect("open"); - let r = zu::query::run(POINT_Q, &mut db, &[("src", Value::Int(src))]).expect("one shot"); + let r = zudb::query::run(POINT_Q, &mut db, &[("src", Value::Int(src))]).expect("one shot"); lat.push(start.elapsed().as_nanos() as u64); assert_eq!(count_of(&r), degree[src as usize], "src {src}"); } diff --git a/crates/zu/benches/stream.rs b/crates/zu/benches/stream.rs index 5489e3c8..cf1a34e2 100644 --- a/crates/zu/benches/stream.rs +++ b/crates/zu/benches/stream.rs @@ -17,15 +17,15 @@ //! the whole thing, which is the case that turns a full scan into a //! bounded one and should measure near zero. //! -//! Run: ZU_GATE=1 cargo bench -p zu --bench stream +//! Run: ZU_GATE=1 cargo bench -p zudb --bench stream use std::alloc::{GlobalAlloc, Layout, System}; use std::sync::atomic::{AtomicBool, AtomicIsize, Ordering}; use std::time::Instant; -use zu::zu1::file::Zu1File; -use zu::zu1::graph::bulk_load_as; -use zu::{Config, Connection, Database, Flow}; +use zudb::zu1::file::Zu1File; +use zudb::zu1::graph::bulk_load_as; +use zudb::{Config, Connection, Database, Flow}; /// Live bytes above the last reset, and the highest that ever got. /// diff --git a/crates/zu/benches/tail.rs b/crates/zu/benches/tail.rs index a8ad754a..ba9d04b1 100644 --- a/crates/zu/benches/tail.rs +++ b/crates/zu/benches/tail.rs @@ -36,15 +36,15 @@ //! tail_p99_p50_uniform_x and tail_p99_p50_power_x are the ceilings, on //! the friend list shape on each graph. //! -//! Run: ZU_GATE=1 cargo bench -p zu --bench tail +//! Run: ZU_GATE=1 cargo bench -p zudb --bench tail use std::time::Instant; -use zu::query::Value; -use zu::session::Session; -use zu::zu1::file::Zu1File; -use zu::zu1::graph::bulk_load_as; -use zu::zu1::props::{PropValues, store_props}; +use zudb::query::Value; +use zudb::session::Session; +use zudb::zu1::file::Zu1File; +use zudb::zu1::graph::bulk_load_as; +use zudb::zu1::props::{PropValues, store_props}; fn budget(key: &str) -> Option { let path = concat!(env!("CARGO_MANIFEST_DIR"), "/../../bench/budgets.toml"); @@ -169,7 +169,7 @@ fn stream( source: &str, seeds: &[i64], want: impl Fn(i64) -> u64, - rows_of: impl Fn(&zu::query::QueryResult) -> u64, + rows_of: impl Fn(&zudb::query::QueryResult) -> u64, ) -> Tail { // A warm pass over the same seeds: the plan compiles once, the // catalog and the readers land, and what is timed below is the @@ -200,14 +200,14 @@ fn stream( } } -fn count_rows(r: &zu::query::QueryResult) -> u64 { +fn count_rows(r: &zudb::query::QueryResult) -> u64 { match r.rows.first().map(|row| &row[0]) { Some(&Value::Int(n)) => n as u64, other => panic!("expected a count, got {other:?}"), } } -fn returned_rows(r: &zu::query::QueryResult) -> u64 { +fn returned_rows(r: &zudb::query::QueryResult) -> u64 { r.rows.len() as u64 } diff --git a/crates/zu/benches/temporal.rs b/crates/zu/benches/temporal.rs index 66bbc529..3d80745f 100644 --- a/crates/zu/benches/temporal.rs +++ b/crates/zu/benches/temporal.rs @@ -34,15 +34,15 @@ //! //! exec_temporal_mrows_s_core floors the date bound. //! -//! Run: ZU_GATE=1 cargo bench -p zu --bench temporal +//! Run: ZU_GATE=1 cargo bench -p zudb --bench temporal use std::time::Instant; -use zu::query::{self, Value}; -use zu::zu1::file::Zu1File; -use zu::zu1::graph::bulk_load_as; -use zu::zu1::props::{PropValues, store_props}; use zu_common::DurationKind; +use zudb::query::{self, Value}; +use zudb::zu1::file::Zu1File; +use zudb::zu1::graph::bulk_load_as; +use zudb::zu1::props::{PropValues, store_props}; fn budget(key: &str) -> Option { let path = concat!(env!("CARGO_MANIFEST_DIR"), "/../../bench/budgets.toml"); @@ -99,7 +99,7 @@ fn build(path: &std::path::Path) { } /// The one row and the count in it. -fn count(r: &zu::query::QueryResult) -> i64 { +fn count(r: &zudb::query::QueryResult) -> i64 { assert_eq!(r.rows.len(), 1, "a counting query returns one row"); match r.rows[0][0] { Value::Int(n) => n, @@ -110,7 +110,7 @@ fn count(r: &zu::query::QueryResult) -> i64 { /// The rows a grouping answered, summed, which is the row count when /// every row falls in a group and is what says the grouping saw them /// all. -fn grouped(r: &zu::query::QueryResult) -> i64 { +fn grouped(r: &zudb::query::QueryResult) -> i64 { r.rows .iter() .map(|row| match row[1] { @@ -122,7 +122,7 @@ fn grouped(r: &zu::query::QueryResult) -> i64 { /// Median ms of `source`, with the answer checked on every run. fn measure(db: &mut Zu1File, source: &str, want: i64, group: bool, runs: usize) -> f64 { - let read = |r: &zu::query::QueryResult| match group { + let read = |r: &zudb::query::QueryResult| match group { true => grouped(r), false => count(r), }; diff --git a/crates/zu/benches/topn.rs b/crates/zu/benches/topn.rs index 07cd89c1..1fff14ce 100644 --- a/crates/zu/benches/topn.rs +++ b/crates/zu/benches/topn.rs @@ -31,14 +31,14 @@ //! exec_sort_wide_mrows_s is a floor on the rate the wide fan orders //! at, the fan's rows over the time the ORDER BY adds to the query. //! -//! Run: ZU_GATE=1 cargo bench -p zu --bench topn +//! Run: ZU_GATE=1 cargo bench -p zudb --bench topn use std::time::Instant; -use zu::query::{self, Value}; -use zu::zu1::file::Zu1File; -use zu::zu1::graph::bulk_load_as; -use zu::zu1::props::{PropValues, store_props}; +use zudb::query::{self, Value}; +use zudb::zu1::file::Zu1File; +use zudb::zu1::graph::bulk_load_as; +use zudb::zu1::props::{PropValues, store_props}; fn budget(key: &str) -> Option { let path = concat!(env!("CARGO_MANIFEST_DIR"), "/../../bench/budgets.toml"); diff --git a/crates/zu/benches/write.rs b/crates/zu/benches/write.rs index e55f9b1c..90c14e38 100644 --- a/crates/zu/benches/write.rs +++ b/crates/zu/benches/write.rs @@ -84,18 +84,18 @@ //! counted and read back after the loop, so a write path that got //! faster by writing less fails instead of scoring. //! -//! Run: ZU_GATE=1 cargo bench -p zu --bench write +//! Run: ZU_GATE=1 cargo bench -p zudb --bench write use std::path::Path; use std::time::Instant; -use zu::query::Value; -use zu::session::Session; -use zu::zu1::file::Zu1File; -use zu::zu1::graph::bulk_load_as; -use zu::zu1::props::{PropValues, store_labels, store_props, store_rel_props}; -use zu::zu1::txn::Cell; -use zu::{Config, Database}; +use zudb::query::Value; +use zudb::session::Session; +use zudb::zu1::file::Zu1File; +use zudb::zu1::graph::bulk_load_as; +use zudb::zu1::props::{PropValues, store_labels, store_props, store_rel_props}; +use zudb::zu1::txn::Cell; +use zudb::{Config, Database}; /// The small table, where the fold is cheap enough that the statement /// itself is most of the number. @@ -152,7 +152,7 @@ const PASSES: u64 = 3; const MB: f64 = 1024.0 * 1024.0; /// The store's block, which is the granularity everything the fold /// takes and gives back is counted in. -const BLOCK: u32 = zu::zu1::BLOCK_SIZE; +const BLOCK: u32 = zudb::zu1::BLOCK_SIZE; /// How many rows the store [`calibrate`] reads holds. /// @@ -243,7 +243,7 @@ fn calibrate() -> f64 { CALIBRATION_ROWS, &ring(CALIBRATION_ROWS), ); - let read = |conn: &mut zu::Connection, age: u64| { + let read = |conn: &mut zudb::Connection, age: u64| { one( conn, &format!("MATCH (p:person) WHERE p.age = {age} RETURN count(p) AS n"), @@ -691,7 +691,7 @@ fn seed(db: &mut Zu1File, rows: u64, edges: &[(u32, u32)]) { .expect("props"); } -fn one(conn: &mut zu::Connection, text: &str) -> i64 { +fn one(conn: &mut zudb::Connection, text: &str) -> i64 { let r = conn.query(text).expect("query"); match r.rows.first().and_then(|row| row.first()) { Some(Value::Int(n)) => *n, @@ -1388,7 +1388,7 @@ fn run_detach(dir: &Path, rows: u64) -> Cost { /// The statement both sustained runs make. It writes one cell over a /// row the store already holds and touches nothing else, so what the /// run costs above the cell is the housekeeping. -fn set(conn: &mut zu::Connection, age: u64) { +fn set(conn: &mut zudb::Connection, age: u64) { conn.query(&format!( "MATCH (p:person) WHERE p.age = {age} SET p.age = {age}" )) @@ -1447,7 +1447,7 @@ fn fold_every(rows: u64) -> u64 { /// /// Asking costs the writer lock and gives it straight back, so it is /// something to do between windows rather than inside one. -fn folds_so_far(conn: &mut zu::Connection) -> u64 { +fn folds_so_far(conn: &mut zudb::Connection) -> u64 { conn.session_mut().fold_count().expect("fold count") } @@ -1855,7 +1855,7 @@ fn main() { // Half a block of headroom on top, for the fold that crosses the // threshold: the check fires at the commit after, so the last fold // is over the line by whatever it took. - let slack = zu::write::checkpoint_slack_bytes(sustained.opened) + BLOCK as u64 / 2; + let slack = zudb::write::checkpoint_slack_bytes(sustained.opened) + BLOCK as u64 / 2; let allowed = sustained.opened + slack; println!( "sustained_window_slack: {:.1} MB grown against the {:.1} MB the checkpoint rule \ @@ -1867,7 +1867,7 @@ fn main() { // measured window, and the one that is gated. The ramp folds too, // so the file is already carrying churn when the window opens and // a bound on the window alone would not see it. - let run_slack = zu::write::checkpoint_slack_bytes(sustained.loaded); + let run_slack = zudb::write::checkpoint_slack_bytes(sustained.loaded); let slack_x = (sustained.peak - sustained.loaded) as f64 / run_slack as f64; println!( "sustained_slack_x: {slack_x:.2}x the checkpoint slack, {:.1} MB loaded to {:.1} MB at \ diff --git a/crates/zu/examples/point_write.rs b/crates/zu/examples/point_write.rs index 164be745..addcf35e 100644 --- a/crates/zu/examples/point_write.rs +++ b/crates/zu/examples/point_write.rs @@ -15,10 +15,10 @@ use std::path::Path; -use zu::zu1::file::Zu1File; -use zu::zu1::graph::bulk_load_as; -use zu::zu1::props::{PropValues, store_props}; -use zu::{Config, Database}; +use zudb::zu1::file::Zu1File; +use zudb::zu1::graph::bulk_load_as; +use zudb::zu1::props::{PropValues, store_props}; +use zudb::{Config, Database}; const ROWS: u64 = 100_000; diff --git a/crates/zu/src/append.rs b/crates/zu/src/append.rs index 2152cb33..278dfee6 100644 --- a/crates/zu/src/append.rs +++ b/crates/zu/src/append.rs @@ -12,7 +12,7 @@ //! carries ten rows or ten million. //! //! ```no_run -//! use zu::Database; +//! use zudb::Database; //! //! let db = Database::open("social.zu1")?; //! let mut conn = db.connect()?; @@ -20,7 +20,7 @@ //! app.append_row((1i64, "ada"))?; //! app.append_row((2i64, "grace"))?; //! app.close()?; -//! # Ok::<(), zu::ZuError>(()) +//! # Ok::<(), zudb::ZuError>(()) //! ``` //! //! A row is every column of the table, in the order the table declares diff --git a/crates/zu/src/db.rs b/crates/zu/src/db.rs index f6a590e2..93347300 100644 --- a/crates/zu/src/db.rs +++ b/crates/zu/src/db.rs @@ -12,12 +12,12 @@ //! have it first. //! //! ```no_run -//! use zu::Database; +//! use zudb::Database; //! //! let db = Database::open("social.zu1")?; //! let mut conn = db.connect()?; //! let rows = conn.query("MATCH (p:Person) RETURN p.name")?; -//! # Ok::<(), zu::ZuError>(()) +//! # Ok::<(), zudb::ZuError>(()) //! ``` //! //! [`Database::open`] takes a path and nothing else, because a @@ -373,7 +373,7 @@ impl Connection { /// anything past it copies what it wants out. /// /// ```no_run - /// use zu::{Database, Flow}; + /// use zudb::{Database, Flow}; /// /// let db = Database::open("social.zu1")?; /// let mut conn = db.connect()?; @@ -384,7 +384,7 @@ impl Connection { /// } /// Ok(Flow::More) /// })?; - /// # Ok::<(), zu::ZuError>(()) + /// # Ok::<(), zudb::ZuError>(()) /// ``` pub fn query_stream( &mut self, @@ -463,7 +463,7 @@ impl Connection { /// two ways rather than two renderings that can drift. /// /// ```no_run - /// use zu::Database; + /// use zudb::Database; /// /// let db = Database::open("social.zu1")?; /// let mut conn = db.connect()?; @@ -471,7 +471,7 @@ impl Connection { /// let root = plan.root.as_ref().expect("a statement with operators"); /// assert_eq!(root.op, "Project"); /// assert_eq!(plan.columns, ["id"]); - /// # Ok::<(), zu::ZuError>(()) + /// # Ok::<(), zudb::ZuError>(()) /// ``` pub fn explain_plan(&mut self, source: &str) -> Result { let out = self.session.explain_plan(source); @@ -587,7 +587,7 @@ impl Connection { /// a trap. /// /// ```no_run - /// use zu::Database; + /// use zudb::Database; /// /// let db = Database::memory()?; /// let mut conn = db.connect()?; diff --git a/crates/zu/tests/arithmetic.rs b/crates/zu/tests/arithmetic.rs index 66d05df0..0b19993b 100644 --- a/crates/zu/tests/arithmetic.rs +++ b/crates/zu/tests/arithmetic.rs @@ -8,15 +8,15 @@ //! on the same rows, since a query cannot tell which engine answered //! it and an answer that depended on that would be no answer at all. -use zu::Database; -use zu::zu1::file::Zu1File; -use zu::zu1::graph::bulk_load_as; +use zudb::Database; +use zudb::zu1::file::Zu1File; +use zudb::zu1::graph::bulk_load_as; const NODES: u32 = 6; struct Fixture { _dir: tempfile::TempDir, - conn: zu::Connection, + conn: zudb::Connection, } impl Fixture { diff --git a/crates/zu/tests/bindings.rs b/crates/zu/tests/bindings.rs index 1abd4f71..c94d7dec 100644 --- a/crates/zu/tests/bindings.rs +++ b/crates/zu/tests/bindings.rs @@ -12,10 +12,10 @@ //! the only way to be sure the filling happened, in the right order //! and before the first row, is to run a statement and read the rows. -use zu::query::Value; -use zu::session::Session; -use zu::zu1::file::Zu1File; -use zu::zu1::graph::bulk_load_as; +use zudb::query::Value; +use zudb::session::Session; +use zudb::zu1::file::Zu1File; +use zudb::zu1::graph::bulk_load_as; /// Four people with a name and an age, three edges, which is the /// fixture the conformance suite uses so that a number here can be @@ -26,12 +26,12 @@ fn opened(name: &str) -> (tempfile::TempDir, Session) { let mut db = Zu1File::create(&path).expect("create"); bulk_load_as(&mut db, "person", "knows", 4, &[(0, 1), (1, 2), (3, 3)]).expect("load"); let names: Vec<&[u8]> = vec![b"ann", b"bo", b"cy", b"di"]; - zu::zu1::props::store_props( + zudb::zu1::props::store_props( &mut db, "person", &[ - ("name", zu::zu1::props::PropValues::Str(&names)), - ("age", zu::zu1::props::PropValues::Int(&[30, 40, 50, 40])), + ("name", zudb::zu1::props::PropValues::Str(&names)), + ("age", zudb::zu1::props::PropValues::Int(&[30, 40, 50, 40])), ], ) .expect("props"); diff --git a/crates/zu/tests/boolean_test.rs b/crates/zu/tests/boolean_test.rs index 05e48964..3cec7e38 100644 --- a/crates/zu/tests/boolean_test.rs +++ b/crates/zu/tests/boolean_test.rs @@ -15,9 +15,9 @@ //! at once. That looks wrong until you read it as what it says, which //! is that a value nobody knows is not known to be either one. -use zu::query::{Value, run}; -use zu::{Database, Engine, Options}; use zu_zu1::file::Zu1File; +use zudb::query::{Value, run}; +use zudb::{Database, Engine, Options}; fn db(dir: &std::path::Path) -> Zu1File { Zu1File::create(&dir.join("boolean_test.zu1")).unwrap() diff --git a/crates/zu/tests/bounded_sort.rs b/crates/zu/tests/bounded_sort.rs index 91099648..5092d1e0 100644 --- a/crates/zu/tests/bounded_sort.rs +++ b/crates/zu/tests/bounded_sort.rs @@ -17,11 +17,11 @@ //! the general path still answers, since a null orders outside the //! direction and the fast path declines it. -use zu::query::run; -use zu::zu1::file::Zu1File; -use zu::zu1::graph::bulk_load_as; -use zu::zu1::props::{PropInput, PropValues, store_props_nullable}; use zu_query::exec::Value; +use zudb::query::run; +use zudb::zu1::file::Zu1File; +use zudb::zu1::graph::bulk_load_as; +use zudb::zu1::props::{PropInput, PropValues, store_props_nullable}; const NODES: u64 = 5_000; diff --git a/crates/zu/tests/case.rs b/crates/zu/tests/case.rs index 90475cfb..5efaae9d 100644 --- a/crates/zu/tests/case.rs +++ b/crates/zu/tests/case.rs @@ -9,7 +9,7 @@ //! are the abbreviations ISO writes for two shapes that come up often //! enough to have a spelling of their own. -use zu::Database; +use zudb::Database; /// Four people of three ages, and a pet for two of them, which is where /// the nulls come from: an OPTIONAL MATCH that found nothing leaves the diff --git a/crates/zu/tests/cast.rs b/crates/zu/tests/cast.rs index 5018ee18..cf0c6cde 100644 --- a/crates/zu/tests/cast.rs +++ b/crates/zu/tests/cast.rs @@ -7,11 +7,11 @@ //! a wrong one, and that the two conditions the corpus asks for come //! back with their gqlstatus codes attached. -use zu::query::{Value, run, run_with}; -use zu::{Engine, Options}; use zu_common::Decimal; use zu_zu1::file::Zu1File; use zu_zu1::graph::bulk_load_as; +use zudb::query::{Value, run, run_with}; +use zudb::{Engine, Options}; /// The smallest graph a query can run against: the executor needs a /// catalog, and no case here reads a property from it. @@ -40,7 +40,7 @@ fn status(db: &mut Zu1File, source: &str) -> String { /// rather than a variable in the environment: the environment belongs /// to the process and this binary runs its tests in parallel, so /// setting it here set it for whichever test was between plans (#513). -fn on_rows(db: &mut Zu1File, source: &str) -> zu::query::QueryResult { +fn on_rows(db: &mut Zu1File, source: &str) -> zudb::query::QueryResult { let options = Options { engine: Engine::Rows, ..Options::default() diff --git a/crates/zu/tests/catalog_statements.rs b/crates/zu/tests/catalog_statements.rs index 5bd8ab72..5db2a056 100644 --- a/crates/zu/tests/catalog_statements.rs +++ b/crates/zu/tests/catalog_statements.rs @@ -21,9 +21,9 @@ //! argument is worth nothing if an `ALTER` quietly appears one release //! later, so a list of the things zu is not is pinned at the bottom. -use zu::query::run; use zu_zu1::file::Zu1File; use zu_zu1::graph::bulk_load_as; +use zudb::query::run; fn graph(dir: &std::path::Path) -> Zu1File { let mut zu = Zu1File::create(&dir.join("catalog.zu1")).unwrap(); diff --git a/crates/zu/tests/chains.rs b/crates/zu/tests/chains.rs index 512f10b8..1facdebe 100644 --- a/crates/zu/tests/chains.rs +++ b/crates/zu/tests/chains.rs @@ -6,9 +6,9 @@ //! it answers: three statements over a real store, each one reading the //! result the one before it returned, and nothing else of it. -use zu::Database; -use zu::zu1::file::Zu1File; -use zu::zu1::graph::bulk_load_as; +use zudb::Database; +use zudb::zu1::file::Zu1File; +use zudb::zu1::graph::bulk_load_as; const NODES: u32 = 200; @@ -131,7 +131,7 @@ fn a_chain_plans_as_one_pipeline() { let dir = tempfile::tempdir().expect("tempdir"); let path = dir.path().join("fuse.zu1"); seeded(&path); - let mut session = zu::session::Session::open(&path).expect("open"); + let mut session = zudb::session::Session::open(&path).expect("open"); let chained = session .explain( "MATCH (p:person) WHERE p.id < 3 RETURN p AS p \ diff --git a/crates/zu/tests/columns.rs b/crates/zu/tests/columns.rs index 93626008..5a693646 100644 --- a/crates/zu/tests/columns.rs +++ b/crates/zu/tests/columns.rs @@ -10,12 +10,12 @@ use std::path::{Path, PathBuf}; -use zu::dataset::{NodeFile, RelFile, load_dataset}; -use zu::query::column::{ColumnData, ColumnType}; -use zu::query::{QueryResult, run, run_with}; -use zu::{Engine, Options}; use zu_query::exec::Value; use zu_zu1::file::Zu1File; +use zudb::dataset::{NodeFile, RelFile, load_dataset}; +use zudb::query::column::{ColumnData, ColumnType}; +use zudb::query::{QueryResult, run, run_with}; +use zudb::{Engine, Options}; /// Three accounts with a name and a balance, and three people two of /// whom own one, so the third owns nothing and an OPTIONAL MATCH over diff --git a/crates/zu/tests/comparison.rs b/crates/zu/tests/comparison.rs index dff98fb4..8d90002f 100644 --- a/crates/zu/tests/comparison.rs +++ b/crates/zu/tests/comparison.rs @@ -8,8 +8,8 @@ //! order `ORDER BY x` produces are read from the same table, and a //! query can rely on the two agreeing. -use zu::query::{Value, run}; use zu_zu1::file::Zu1File; +use zudb::query::{Value, run}; fn db(dir: &std::path::Path) -> Zu1File { Zu1File::create(&dir.join("comparison.zu1")).unwrap() diff --git a/crates/zu/tests/composites.rs b/crates/zu/tests/composites.rs index 9a98b477..4c81562e 100644 --- a/crates/zu/tests/composites.rs +++ b/crates/zu/tests/composites.rs @@ -8,9 +8,9 @@ //! spellings of the set operators and for the one conjunction that is //! not a set operator at all. -use zu::Database; -use zu::zu1::file::Zu1File; -use zu::zu1::graph::bulk_load_as; +use zudb::Database; +use zudb::zu1::file::Zu1File; +use zudb::zu1::graph::bulk_load_as; const NODES: u32 = 20; @@ -28,7 +28,7 @@ fn seeded(path: &std::path::Path) { struct Fixture { _dir: tempfile::TempDir, - conn: zu::Connection, + conn: zudb::Connection, } impl Fixture { diff --git a/crates/zu/tests/convert.rs b/crates/zu/tests/convert.rs index cab06902..0e02685a 100644 --- a/crates/zu/tests/convert.rs +++ b/crates/zu/tests/convert.rs @@ -4,9 +4,6 @@ //! and adjacency list survives, and a query answers identically on //! the original and the twice-converted store. -use zu::convert::{sqlite_to_zu1, zu1_to_sqlite}; -use zu::query::run as run_zu1; -use zu::sqlite::run as run_sqlite; use zu_query::exec::Value as QValue; use zu_sqlite::{ColumnType, SqliteStore, Value as SqlValue}; use zu_storage::Direction; @@ -14,6 +11,9 @@ use zu_zu1::catalog::Catalog; use zu_zu1::file::Zu1File; use zu_zu1::graph::{Direction as Zu1Direction, GraphReader, bulk_load_as}; use zu_zu1::props::{PropValues, PropsReader, load_props, store_props}; +use zudb::convert::{sqlite_to_zu1, zu1_to_sqlite}; +use zudb::query::run as run_zu1; +use zudb::sqlite::run as run_sqlite; const NAMES: [&str; 6] = ["ada", "bob", "cat", "dan", "eve", "fay"]; const AGES: [u64; 6] = [20, 30, 30, 40, 50, 25]; @@ -484,7 +484,7 @@ fn float_and_byte_columns_survive_both_hops() { .rows .iter() .map(|r| match r[0] { - zu::query::Value::Float(f) => f, + zudb::query::Value::Float(f) => f, ref other => panic!("expected a float, got {other:?}"), }) .collect(); @@ -549,7 +549,7 @@ fn a_boolean_column_survives_the_sqlite_hop_on_its_declaration() { .rows .iter() .map(|r| match r[0] { - zu::query::Value::Bool(v) => v, + zudb::query::Value::Bool(v) => v, ref other => panic!("expected a boolean, got {other:?}"), }) .collect(); @@ -648,7 +648,7 @@ fn a_list_column_survives_both_hops_with_its_element_type() { .rows .iter() .map(|r| match r[0] { - zu::query::Value::Int(v) => v, + zudb::query::Value::Int(v) => v, ref other => panic!("expected a count, got {other:?}"), }) .collect(); @@ -661,9 +661,9 @@ fn a_list_column_survives_both_hops_with_its_element_type() { .unwrap(); assert_eq!( got.rows[0][0], - zu::query::Value::List(vec![ - zu::query::Value::Str("say \"hi\"".into()), - zu::query::Value::Str("back\\slash".into()), + zudb::query::Value::List(vec![ + zudb::query::Value::Str("say \"hi\"".into()), + zudb::query::Value::Str("back\\slash".into()), ]) ); @@ -912,14 +912,14 @@ fn a_duplicated_edge_carries_both_values_across_the_hop() { drop(sq); sqlite_to_zu1(&a, &b).unwrap(); - let mut db = zu::zu1::file::Zu1File::open(&b).unwrap(); - let mut graph = zu::zu1::graph::GraphReader::load_table(&mut db, "knows").unwrap(); + let mut db = zudb::zu1::file::Zu1File::open(&b).unwrap(); + let mut graph = zudb::zu1::graph::GraphReader::load_table(&mut db, "knows").unwrap(); let (nbrs, base) = graph.out_neighbors_from(&mut db, 0).unwrap(); assert_eq!(nbrs, [1, 1], "both edges are stored"); let base = base as usize; let root = graph.directory().props; let mut props = - zu::zu1::props::PropsReader::new(zu::zu1::props::load_props_at(&mut db, root).unwrap()); + zudb::zu1::props::PropsReader::new(zudb::zu1::props::load_props_at(&mut db, root).unwrap()); let col = props.col("weight").unwrap(); let mut values = Vec::new(); props.read_int_column(&mut db, col, &mut values).unwrap(); diff --git a/crates/zu/tests/cycles.rs b/crates/zu/tests/cycles.rs index 928ad787..9c0f0994 100644 --- a/crates/zu/tests/cycles.rs +++ b/crates/zu/tests/cycles.rs @@ -13,9 +13,9 @@ //! which is what a query joining a cycle to further patterns compiles //! to, and about the two of them each reading their own answer. -use zu::query::{Value, run}; use zu_zu1::file::Zu1File; use zu_zu1::graph::{bulk_load_as, bulk_load_keyed}; +use zudb::query::{Value, run}; /// The count one query answers. fn count(db: &mut Zu1File, source: &str) -> i64 { diff --git a/crates/zu/tests/dataset.rs b/crates/zu/tests/dataset.rs index 2346e668..808e83ba 100644 --- a/crates/zu/tests/dataset.rs +++ b/crates/zu/tests/dataset.rs @@ -8,10 +8,10 @@ use std::path::{Path, PathBuf}; -use zu::dataset::{NodeFile, RelFile, load_dataset}; -use zu::query::run; use zu_query::exec::Value; use zu_zu1::file::Zu1File; +use zudb::dataset::{NodeFile, RelFile, load_dataset}; +use zudb::query::run; /// Accounts 10, 11 and 12; people 100 and 101. Neither range starts at /// zero and neither is a row number, so a load that answers with the @@ -259,7 +259,7 @@ fn a_key_the_table_already_holds_is_refused_rather_than_written() { let (nodes, rels) = write(dir.path()); let path = dir.path().join("keys.zu1"); load_dataset(&nodes, &rels, &path).expect("load"); - let mut session = zu::session::Session::open(&path).expect("open"); + let mut session = zudb::session::Session::open(&path).expect("open"); let err = session .run("INSERT (:Account {id: 11, name: 'again', balance: 0})", &[]) .expect_err("11 is an account the file already holds"); @@ -290,7 +290,7 @@ fn a_key_the_table_already_holds_is_refused_rather_than_written() { ); drop(db); // And a fold has nothing to choke on, which is the whole point. - let mut session = zu::session::Session::open(&path).expect("reopen"); + let mut session = zudb::session::Session::open(&path).expect("reopen"); session .run("INSERT (:Account {id: 21, name: 'next', balance: 0})", &[]) .expect("21 is free"); diff --git a/crates/zu/tests/element_predicates.rs b/crates/zu/tests/element_predicates.rs index 5e995e02..598c4587 100644 --- a/crates/zu/tests/element_predicates.rs +++ b/crates/zu/tests/element_predicates.rs @@ -7,7 +7,7 @@ //! non local predicate: the node just reached can be compared with the //! node the walk came from. -use zu::Database; +use zudb::Database; /// A chain of four steps carrying the numbers that let a predicate /// compare one node with another: diff --git a/crates/zu/tests/errors.rs b/crates/zu/tests/errors.rs index a1dc6b2b..20dc623e 100644 --- a/crates/zu/tests/errors.rs +++ b/crates/zu/tests/errors.rs @@ -7,10 +7,10 @@ //! where the condition is written up, and whether running the same //! statement again could get anywhere. -use zu::gqlstatus::Severity; -use zu::zu1::file::Zu1File; -use zu::zu1::graph::bulk_load_as; -use zu::{Database, ZuError}; +use zudb::gqlstatus::Severity; +use zudb::zu1::file::Zu1File; +use zudb::zu1::graph::bulk_load_as; +use zudb::{Database, ZuError}; const NODES: u32 = 8; diff --git a/crates/zu/tests/exists.rs b/crates/zu/tests/exists.rs index 6a33f641..2c5d6ed2 100644 --- a/crates/zu/tests/exists.rs +++ b/crates/zu/tests/exists.rs @@ -9,9 +9,9 @@ //! whether it answered a row, so what it returns is never read and the //! run stops at the first row it makes. -use zu::Database; -use zu::zu1::file::Zu1File; -use zu::zu1::graph::bulk_load_as; +use zudb::Database; +use zudb::zu1::file::Zu1File; +use zudb::zu1::graph::bulk_load_as; const NODES: u32 = 5; @@ -24,7 +24,7 @@ fn seeded(path: &std::path::Path) { struct Fixture { _dir: tempfile::TempDir, - conn: zu::Connection, + conn: zudb::Connection, } impl Fixture { diff --git a/crates/zu/tests/filter_let.rs b/crates/zu/tests/filter_let.rs index ec936412..18df0dcb 100644 --- a/crates/zu/tests/filter_let.rs +++ b/crates/zu/tests/filter_let.rs @@ -7,9 +7,9 @@ //! taking any away, and that the two compose with the statement //! chaining the milestone before them built. -use zu::Database; -use zu::zu1::file::Zu1File; -use zu::zu1::graph::bulk_load_as; +use zudb::Database; +use zudb::zu1::file::Zu1File; +use zudb::zu1::graph::bulk_load_as; const NODES: u32 = 20; @@ -24,7 +24,7 @@ fn seeded(path: &std::path::Path) { struct Fixture { _dir: tempfile::TempDir, - conn: zu::Connection, + conn: zudb::Connection, } impl Fixture { diff --git a/crates/zu/tests/finish.rs b/crates/zu/tests/finish.rs index eb48d3d2..29cdb5b1 100644 --- a/crates/zu/tests/finish.rs +++ b/crates/zu/tests/finish.rs @@ -9,15 +9,15 @@ //! that it answers no columns and no rows, and that the words which //! read a result are refused behind it. -use zu::Database; -use zu::zu1::file::Zu1File; -use zu::zu1::graph::bulk_load_as; +use zudb::Database; +use zudb::zu1::file::Zu1File; +use zudb::zu1::graph::bulk_load_as; const NODES: u32 = 4; struct Fixture { _dir: tempfile::TempDir, - conn: zu::Connection, + conn: zudb::Connection, } impl Fixture { @@ -135,7 +135,7 @@ fn a_write_in_front_of_it_wrote() { fn it_reports_the_omitted_result() { let mut fx = Fixture::open("finish-status.zu1"); let finished = fx.conn.query("MATCH (p:person) FINISH").expect("query"); - assert_eq!(finished.status(), zu::gqlstatus::codes::C00001); + assert_eq!(finished.status(), zudb::gqlstatus::codes::C00001); let empty = fx .conn .query("MATCH (p:person) FILTER p.id > 1000 RETURN p.id AS id") diff --git a/crates/zu/tests/for_statement.rs b/crates/zu/tests/for_statement.rs index f641d826..de013d98 100644 --- a/crates/zu/tests/for_statement.rs +++ b/crates/zu/tests/for_statement.rs @@ -6,9 +6,9 @@ //! rows it makes, the counter it may number them with, and what //! happens when it stands under a match and runs once per row. -use zu::Database; -use zu::zu1::file::Zu1File; -use zu::zu1::graph::bulk_load_as; +use zudb::Database; +use zudb::zu1::file::Zu1File; +use zudb::zu1::graph::bulk_load_as; const NODES: u32 = 6; @@ -21,7 +21,7 @@ fn seeded(path: &std::path::Path) { struct Fixture { _dir: tempfile::TempDir, - conn: zu::Connection, + conn: zudb::Connection, } impl Fixture { diff --git a/crates/zu/tests/frames.rs b/crates/zu/tests/frames.rs index f320371d..5bb0482d 100644 --- a/crates/zu/tests/frames.rs +++ b/crates/zu/tests/frames.rs @@ -15,10 +15,10 @@ use std::any::Any; use std::ptr::NonNull; use std::sync::Arc; -use zu::query::Value; -use zu::zu1::file::Zu1File; -use zu::zu1::graph::bulk_load_as; -use zu::{Column, Database, FloatBits, Frame, IntBits, Layout, LogicalType}; +use zudb::query::Value; +use zudb::zu1::file::Zu1File; +use zudb::zu1::graph::bulk_load_as; +use zudb::{Column, Database, FloatBits, Frame, IntBits, Layout, LogicalType}; /// The arrays a test registers, kept alive exactly as a caller's would /// be: the frame holds this and the columns point into it. @@ -131,7 +131,7 @@ fn seeded(path: &std::path::Path) { bulk_load_as(&mut db, "person", "knows", 8, &edges).expect("load"); } -fn open(dir: &std::path::Path) -> zu::Connection { +fn open(dir: &std::path::Path) -> zudb::Connection { let path = dir.join("frames.zu1"); seeded(&path); Database::open(&path) @@ -307,6 +307,6 @@ fn a_frame_id_and_a_catalog_id_share_one_space() { // widens its id, this is what says the frame side has to widen too. assert_eq!( zu_query::frame::TOP_TABLE_ID, - zu::zu1::catalog::MAX_TABLE_ID + zudb::zu1::catalog::MAX_TABLE_ID ); } diff --git a/crates/zu/tests/functions.rs b/crates/zu/tests/functions.rs index f3d0cba9..16e99235 100644 --- a/crates/zu/tests/functions.rs +++ b/crates/zu/tests/functions.rs @@ -9,10 +9,10 @@ //! answered while the statement is bound rather than once per row it //! would have reached. -use zu::Database; -use zu::query::Value; use zu_common::temporal::NANOS_PER_DAY; use zu_common::{DurationKind, Temporal}; +use zudb::Database; +use zudb::query::Value; fn opened(dir: &std::path::Path) -> Database { let db = Database::create(dir.join("functions.zu1")).expect("create"); @@ -114,7 +114,7 @@ fn the_numeric_library_answers_over_a_column() { let rows = conn .query(source) .unwrap_or_else(|e| panic!("{source}: {e}")); - let read = |rows: &zu::query::QueryResult, name: &str| -> Vec { + let read = |rows: &zudb::query::QueryResult, name: &str| -> Vec { rows.iter() .map(|row| row.get_by_name::(name).expect(name)) .collect() @@ -626,9 +626,9 @@ fn the_set_functions_answer_over_a_graph() { // through the merge and not out of one accumulator. const NODES: u32 = 50_000; { - let mut file = zu::zu1::file::Zu1File::create(&path).expect("create"); + let mut file = zudb::zu1::file::Zu1File::create(&path).expect("create"); let edges: Vec<(u32, u32)> = (0..NODES).map(|i| (i, (i + 1) % NODES)).collect(); - zu::zu1::graph::bulk_load_as(&mut file, "person", "knows", NODES.into(), &edges) + zudb::zu1::graph::bulk_load_as(&mut file, "person", "knows", NODES.into(), &edges) .expect("load"); } let db = Database::open(&path).expect("open"); @@ -643,7 +643,7 @@ fn the_set_functions_answer_over_a_graph() { let population = (squares / f64::from(NODES)).sqrt(); let sample = (squares / f64::from(NODES - 1)).sqrt(); - let float = |conn: &mut zu::Connection, source: &str| -> f64 { + let float = |conn: &mut zudb::Connection, source: &str| -> f64 { let rows = conn .query(source) .unwrap_or_else(|e| panic!("{source}: {e}")); @@ -723,9 +723,9 @@ fn the_percentiles_answer_over_a_graph() { let path = dir.path().join("percentiles.zu1"); const NODES: u32 = 100_000; { - let mut file = zu::zu1::file::Zu1File::create(&path).expect("create"); + let mut file = zudb::zu1::file::Zu1File::create(&path).expect("create"); let edges: Vec<(u32, u32)> = (0..NODES).map(|i| (i, (i + 1) % NODES)).collect(); - zu::zu1::graph::bulk_load_as(&mut file, "person", "knows", NODES.into(), &edges) + zudb::zu1::graph::bulk_load_as(&mut file, "person", "knows", NODES.into(), &edges) .expect("load"); } let db = Database::open(&path).expect("open"); @@ -758,7 +758,7 @@ fn the_percentiles_answer_over_a_graph() { let rows = conn .query_with( "MATCH (p:person) WHERE p.id < 4 RETURN percentile_cont(p.id, $p) AS v", - &[("p", zu::query::Value::Float(0.5))], + &[("p", zudb::query::Value::Float(0.5))], ) .expect("query"); let rows: Vec<_> = rows.iter().collect(); @@ -804,7 +804,7 @@ fn the_percentiles_answer_over_a_graph() { let path = dir.path().join("weighted.zu1"); const REACH: u32 = 300; { - let mut file = zu::zu1::file::Zu1File::create(&path).expect("create"); + let mut file = zudb::zu1::file::Zu1File::create(&path).expect("create"); let mut edges: Vec<(u32, u32)> = Vec::new(); for i in 0..REACH { edges.push((i, (i + 1) % REACH)); @@ -816,7 +816,7 @@ fn the_percentiles_answer_over_a_graph() { } } edges.sort_unstable(); - zu::zu1::graph::bulk_load_as(&mut file, "person", "knows", REACH.into(), &edges) + zudb::zu1::graph::bulk_load_as(&mut file, "person", "knows", REACH.into(), &edges) .expect("load"); } let db = Database::open(&path).expect("open"); @@ -866,9 +866,9 @@ fn the_cardinality_of_a_walk_is_what_it_holds() { let path = dir.path().join("cardinality.zu1"); const NODES: u32 = 20_000; { - let mut file = zu::zu1::file::Zu1File::create(&path).expect("create"); + let mut file = zudb::zu1::file::Zu1File::create(&path).expect("create"); let edges: Vec<(u32, u32)> = (0..NODES).map(|i| (i, (i + 1) % NODES)).collect(); - zu::zu1::graph::bulk_load_as(&mut file, "person", "knows", NODES.into(), &edges) + zudb::zu1::graph::bulk_load_as(&mut file, "person", "knows", NODES.into(), &edges) .expect("load"); } let db = Database::open(&path).expect("open"); diff --git a/crates/zu/tests/graph_type.rs b/crates/zu/tests/graph_type.rs index 23f815f8..35ca64a5 100644 --- a/crates/zu/tests/graph_type.rs +++ b/crates/zu/tests/graph_type.rs @@ -11,9 +11,9 @@ //! frontier itself, which is S2's work, and it is pinned below so that //! a type crossing it is a change somebody is measuring. -use zu::query::run; use zu_zu1::file::Zu1File; use zu_zu1::graph::bulk_load_as; +use zudb::query::run; fn graph(dir: &std::path::Path) -> Zu1File { let mut zu = Zu1File::create(&dir.join("graph_type.zu1")).unwrap(); diff --git a/crates/zu/tests/grouping.rs b/crates/zu/tests/grouping.rs index a70c0dc5..f6ed132b 100644 --- a/crates/zu/tests/grouping.rs +++ b/crates/zu/tests/grouping.rs @@ -7,9 +7,9 @@ //! when the two agree, and that a projection the grouping cannot //! explain is refused rather than answered with a row per input. -use zu::Database; -use zu::zu1::file::Zu1File; -use zu::zu1::graph::bulk_load_as; +use zudb::Database; +use zudb::zu1::file::Zu1File; +use zudb::zu1::graph::bulk_load_as; const NODES: u32 = 9; @@ -24,7 +24,7 @@ fn seeded(path: &std::path::Path) { struct Fixture { _dir: tempfile::TempDir, - conn: zu::Connection, + conn: zudb::Connection, } impl Fixture { diff --git a/crates/zu/tests/groups.rs b/crates/zu/tests/groups.rs index ce7c5748..a31c3f06 100644 --- a/crates/zu/tests/groups.rs +++ b/crates/zu/tests/groups.rs @@ -10,9 +10,9 @@ //! one answers the list of the properties, and an aggregate around one //! folds that row's group rather than the rows. -use zu::query::{Value, run}; use zu_zu1::file::Zu1File; use zu_zu1::graph::bulk_load_as; +use zudb::query::{Value, run}; /// A chain of five, so a stretch repeated twice has somewhere to go and /// a stretch repeated three times still has: diff --git a/crates/zu/tests/interrupt.rs b/crates/zu/tests/interrupt.rs index 2f0d7de1..e91bff21 100644 --- a/crates/zu/tests/interrupt.rs +++ b/crates/zu/tests/interrupt.rs @@ -10,9 +10,9 @@ //! the same handle and it is what a shell paints while a person waits. use std::sync::atomic::{AtomicBool, Ordering}; -use zu::{Database, ZuError}; use zu_zu1::file::Zu1File; use zu_zu1::graph::bulk_load_as; +use zudb::{Database, ZuError}; /// A graph big enough that the join below cannot finish while the /// watcher thread is waking up, and small enough to load in a moment. diff --git a/crates/zu/tests/kernels.rs b/crates/zu/tests/kernels.rs index a6f486cb..b67cdbca 100644 --- a/crates/zu/tests/kernels.rs +++ b/crates/zu/tests/kernels.rs @@ -20,12 +20,12 @@ //! honest: the line says which functions are kernels and this file //! says which ones are not and why. -use zu::query::Value; -use zu::session::Session; -use zu::zu1::file::Zu1File; -use zu::zu1::graph::bulk_load_as; -use zu::zu1::props::{PropValues, store_props}; -use zu::{Engine, Options}; +use zudb::query::Value; +use zudb::session::Session; +use zudb::zu1::file::Zu1File; +use zudb::zu1::graph::bulk_load_as; +use zudb::zu1::props::{PropValues, store_props}; +use zudb::{Engine, Options}; /// Four people in a ring of `knows`, each with a whole number, a /// number with a fraction and a name. Every height is above nought and diff --git a/crates/zu/tests/lists.rs b/crates/zu/tests/lists.rs index cedea6d8..bb9c4159 100644 --- a/crates/zu/tests/lists.rs +++ b/crates/zu/tests/lists.rs @@ -7,11 +7,11 @@ //! here, end to end through the parser rather than against the lattice //! directly, because the spellings are half of GV50. -use zu::query::{Value, run}; use zu_common::{IntBits, LogicalType}; use zu_zu1::file::Zu1File; use zu_zu1::graph::bulk_load_as; use zu_zu1::props::{ListElement, PropValues, store_props}; +use zudb::query::{Value, run}; fn graph(dir: &std::path::Path) -> Zu1File { let mut zu = Zu1File::create(&dir.join("lists.zu1")).unwrap(); diff --git a/crates/zu/tests/literals.rs b/crates/zu/tests/literals.rs index 16fbe901..c093f3e9 100644 --- a/crates/zu/tests/literals.rs +++ b/crates/zu/tests/literals.rs @@ -7,9 +7,9 @@ //! matters is that the engine reads the words rather than stopping at //! them. -use zu::query::{Value, run}; use zu_common::{DurationKind, Temporal}; use zu_zu1::file::Zu1File; +use zudb::query::{Value, run}; fn db(dir: &std::path::Path) -> Zu1File { Zu1File::create(&dir.join("literals.zu1")).unwrap() diff --git a/crates/zu/tests/match_block.rs b/crates/zu/tests/match_block.rs index 358831fb..355c1c2c 100644 --- a/crates/zu/tests/match_block.rs +++ b/crates/zu/tests/match_block.rs @@ -8,9 +8,9 @@ //! those is where the interesting rule is, since a block that finds //! nothing nulls every name it writes rather than dropping the row. -use zu::Database; -use zu::zu1::file::Zu1File; -use zu::zu1::graph::bulk_load_as; +use zudb::Database; +use zudb::zu1::file::Zu1File; +use zudb::zu1::graph::bulk_load_as; const NODES: u32 = 5; @@ -25,7 +25,7 @@ fn seeded(path: &std::path::Path) { struct Fixture { _dir: tempfile::TempDir, - conn: zu::Connection, + conn: zudb::Connection, } impl Fixture { diff --git a/crates/zu/tests/match_modes.rs b/crates/zu/tests/match_modes.rs index 7c40f7c2..be9d40b2 100644 --- a/crates/zu/tests/match_modes.rs +++ b/crates/zu/tests/match_modes.rs @@ -9,9 +9,9 @@ //! edge paired with itself. REPEATABLE ELEMENTS lifts that and lets a //! pattern take whatever fits. -use zu::Database; -use zu::zu1::file::Zu1File; -use zu::zu1::graph::bulk_load_as; +use zudb::Database; +use zudb::zu1::file::Zu1File; +use zudb::zu1::graph::bulk_load_as; const NODES: u32 = 3; @@ -32,7 +32,7 @@ fn seeded(path: &std::path::Path) { struct Fixture { _dir: tempfile::TempDir, - conn: zu::Connection, + conn: zudb::Connection, } impl Fixture { diff --git a/crates/zu/tests/merge.rs b/crates/zu/tests/merge.rs index f69f2a9c..b33f4a53 100644 --- a/crates/zu/tests/merge.rs +++ b/crates/zu/tests/merge.rs @@ -12,17 +12,17 @@ //! writes it once, and that the patterns a merge cannot mean are turned //! away rather than half run. -use zu::Database; -use zu::query::Value; -use zu::zu1::file::Zu1File; -use zu::zu1::graph::bulk_load_as; -use zu::zu1::props::{PropValues, store_props}; +use zudb::Database; +use zudb::query::Value; +use zudb::zu1::file::Zu1File; +use zudb::zu1::graph::bulk_load_as; +use zudb::zu1::props::{PropValues, store_props}; const NODES: u64 = 4; struct Fixture { _dir: tempfile::TempDir, - conn: zu::Connection, + conn: zudb::Connection, } impl Fixture { diff --git a/crates/zu/tests/misuse.rs b/crates/zu/tests/misuse.rs index 1f07fe2f..d5808a31 100644 --- a/crates/zu/tests/misuse.rs +++ b/crates/zu/tests/misuse.rs @@ -29,8 +29,8 @@ use std::io::ErrorKind; use std::path::Path; -use zu::query::Value; -use zu::{Config, Database, ZuError, params}; +use zudb::query::Value; +use zudb::{Config, Database, ZuError, params}; /// The statement every case is followed by, on the database it just /// failed against. @@ -65,7 +65,7 @@ struct Misuse { what: &'static str, /// Runs it against a database that is already there. Returning /// `Ok` fails the test: every program in the table is wrong. - run: fn(&Database) -> zu::Result<()>, + run: fn(&Database) -> zudb::Result<()>, /// Every phrase the message has to carry. These are the engine's /// own words, not the operating system's, so they are the same on /// every platform; the three failures that are the operating diff --git a/crates/zu/tests/numeric.rs b/crates/zu/tests/numeric.rs index 15f1061b..d3a83222 100644 --- a/crates/zu/tests/numeric.rs +++ b/crates/zu/tests/numeric.rs @@ -12,9 +12,9 @@ //! the two runs below are two connections and neither can be disturbed //! by a test running beside it (#513). -use zu::zu1::file::Zu1File; -use zu::zu1::graph::bulk_load_as; -use zu::{Database, Engine, Options}; +use zudb::zu1::file::Zu1File; +use zudb::zu1::graph::bulk_load_as; +use zudb::{Database, Engine, Options}; const NODES: u32 = 6; diff --git a/crates/zu/tests/ordering.rs b/crates/zu/tests/ordering.rs index 4cf4ed29..c8580e62 100644 --- a/crates/zu/tests/ordering.rs +++ b/crates/zu/tests/ordering.rs @@ -9,10 +9,10 @@ //! the pipeline sort's own null handling is covered where it lives, in //! the sink's tests. -use zu::convert::sqlite_to_zu1; -use zu::query::run; use zu_sqlite::{ColumnType, SqliteStore, Value as SqlValue}; use zu_zu1::file::Zu1File; +use zudb::convert::sqlite_to_zu1; +use zudb::query::run; /// Five people, three of whom have an age. The two without are the /// rows every case here is about, and the names are distinct so a tie diff --git a/crates/zu/tests/paging.rs b/crates/zu/tests/paging.rs index 63039811..ca49d6f9 100644 --- a/crates/zu/tests/paging.rs +++ b/crates/zu/tests/paging.rs @@ -6,9 +6,9 @@ //! ends is the window it looks like, and that a page of an ordered //! result is a page rather than an arbitrary handful of rows. -use zu::Database; -use zu::zu1::file::Zu1File; -use zu::zu1::graph::bulk_load_as; +use zudb::Database; +use zudb::zu1::file::Zu1File; +use zudb::zu1::graph::bulk_load_as; const NODES: u32 = 8; @@ -21,7 +21,7 @@ fn seeded(path: &std::path::Path) { struct Fixture { _dir: tempfile::TempDir, - conn: zu::Connection, + conn: zudb::Connection, } impl Fixture { diff --git a/crates/zu/tests/path_modes.rs b/crates/zu/tests/path_modes.rs index 7266574e..ba37c78c 100644 --- a/crates/zu/tests/path_modes.rs +++ b/crates/zu/tests/path_modes.rs @@ -9,9 +9,9 @@ //! rather than refusing the statement, which is what zu did until this //! file existed. -use zu::Database; -use zu::zu1::file::Zu1File; -use zu::zu1::graph::bulk_load_as; +use zudb::Database; +use zudb::zu1::file::Zu1File; +use zudb::zu1::graph::bulk_load_as; const NODES: u32 = 5; @@ -36,7 +36,7 @@ fn seeded(path: &std::path::Path) { struct Fixture { _dir: tempfile::TempDir, - conn: zu::Connection, + conn: zudb::Connection, } impl Fixture { @@ -136,7 +136,7 @@ fn the_plan_names_the_mode() { let dir = tempfile::tempdir().expect("tempdir"); let path = dir.path().join("simple-plan.zu1"); seeded(&path); - let mut session = zu::session::Session::open(&path).expect("open"); + let mut session = zudb::session::Session::open(&path).expect("open"); let plan = session .explain("MATCH SIMPLE (a:person {id: 0})-[:knows*1..3]->(b) RETURN b.id AS id") .expect("a plan"); diff --git a/crates/zu/tests/path_predicates.rs b/crates/zu/tests/path_predicates.rs index 81a3592e..7856260c 100644 --- a/crates/zu/tests/path_predicates.rs +++ b/crates/zu/tests/path_predicates.rs @@ -13,11 +13,11 @@ //! between accounts, each stamped with a time, and a question that only //! wants the transfers inside a window. -use zu::convert::sqlite_to_zu1; -use zu::query::run; use zu_query::exec::Value; use zu_sqlite::{ColumnType, SqliteStore, Value as SqlValue}; use zu_zu1::file::Zu1File; +use zudb::convert::sqlite_to_zu1; +use zudb::query::run; /// Four accounts and the transfers between them, each with a time. /// @@ -174,12 +174,12 @@ fn the_plan_text_shows_the_gate_on_the_expansion() { let source = "MATCH (a:account)-[t:transfer*1..3 WHERE t.ts >= 5]->(b:account) \ RETURN b.id AS id"; let catalog = zu_zu1::catalog::Catalog::load(&mut db).expect("catalog"); - let logical = zu::query::explain(source, &catalog).expect("explain"); + let logical = zudb::query::explain(source, &catalog).expect("explain"); assert!( logical.contains("[t:transfer*1..3 WHERE t.ts >= 5]"), "the gate belongs inside the brackets: {logical}" ); - let physical = zu::query::explain_analyze(source, &mut db, &[]).expect("explain analyze"); + let physical = zudb::query::explain_analyze(source, &mut db, &[]).expect("explain analyze"); assert!( physical.contains("VarExpand") && physical.contains("where t.ts >= 5"), "the gate belongs on the expansion: {physical}" diff --git a/crates/zu/tests/path_selectors.rs b/crates/zu/tests/path_selectors.rs index 75f6a972..caf2e5ce 100644 --- a/crates/zu/tests/path_selectors.rs +++ b/crates/zu/tests/path_selectors.rs @@ -7,9 +7,9 @@ //! two paths of one of those lengths. A selector read as another one //! would answer a number these tests do not expect. -use zu::Database; -use zu::zu1::file::Zu1File; -use zu::zu1::graph::bulk_load_as; +use zudb::Database; +use zudb::zu1::file::Zu1File; +use zudb::zu1::graph::bulk_load_as; const NODES: u32 = 7; @@ -38,7 +38,7 @@ fn seeded(path: &std::path::Path) { struct Fixture { _dir: tempfile::TempDir, - conn: zu::Connection, + conn: zudb::Connection, } impl Fixture { @@ -207,7 +207,7 @@ fn the_plan_names_the_selector() { let dir = tempfile::tempdir().expect("tempdir"); let path = dir.path().join("selector-plan.zu1"); seeded(&path); - let mut session = zu::session::Session::open(&path).expect("open"); + let mut session = zudb::session::Session::open(&path).expect("open"); for (selector, want) in [ ("ANY", "any 1"), ("ANY 3", "any 3"), diff --git a/crates/zu/tests/paths.rs b/crates/zu/tests/paths.rs index 8fa7c7e1..f6d4375d 100644 --- a/crates/zu/tests/paths.rs +++ b/crates/zu/tests/paths.rs @@ -8,9 +8,9 @@ //! measure one, and 22G0Z refuse a sequence that is shaped like one and //! describes a walk nobody can take. -use zu::query::{Value, run}; use zu_zu1::file::Zu1File; use zu_zu1::graph::bulk_load_as; +use zudb::query::{Value, run}; /// A chain, 0 to 1 to 2 to 3, so a path has hops to have and the two /// ends of one are different nodes. diff --git a/crates/zu/tests/plan_reuse.rs b/crates/zu/tests/plan_reuse.rs index b872abe2..2d25d81a 100644 --- a/crates/zu/tests/plan_reuse.rs +++ b/crates/zu/tests/plan_reuse.rs @@ -14,10 +14,10 @@ //! what holds it. The oracle is a connection that has not seen the //! text before, which compiles it from nothing. -use zu::query::Value; -use zu::zu1::file::Zu1File; -use zu::zu1::graph::bulk_load_as; -use zu::{Connection, Database}; +use zudb::query::Value; +use zudb::zu1::file::Zu1File; +use zudb::zu1::graph::bulk_load_as; +use zudb::{Connection, Database}; const NODES: u32 = 200; diff --git a/crates/zu/tests/plans.rs b/crates/zu/tests/plans.rs index 0e921eb4..8b06b830 100644 --- a/crates/zu/tests/plans.rs +++ b/crates/zu/tests/plans.rs @@ -7,10 +7,10 @@ //! renderings are those structures printed rather than a second //! description of them. -use zu::query::Value; -use zu::zu1::file::Zu1File; -use zu::zu1::graph::bulk_load_as; -use zu::{Config, Database}; +use zudb::query::Value; +use zudb::zu1::file::Zu1File; +use zudb::zu1::graph::bulk_load_as; +use zudb::{Config, Database}; const NODES: u32 = 200; diff --git a/crates/zu/tests/predicates.rs b/crates/zu/tests/predicates.rs index 4fefd234..96d9d22a 100644 --- a/crates/zu/tests/predicates.rs +++ b/crates/zu/tests/predicates.rs @@ -11,10 +11,10 @@ //! its ends, a node carries its table, and which properties a table has //! is a question about the table. -use zu::Database; -use zu::query::{Value, run}; use zu_zu1::file::Zu1File; use zu_zu1::graph::{bulk_load_as, bulk_load_undirected_as}; +use zudb::Database; +use zudb::query::{Value, run}; /// One directed edge, the graph where the direction predicate has /// something to say yes about. diff --git a/crates/zu/tests/procedures.rs b/crates/zu/tests/procedures.rs index 25b09092..da7e71fa 100644 --- a/crates/zu/tests/procedures.rs +++ b/crates/zu/tests/procedures.rs @@ -10,10 +10,10 @@ //! while a binding table is read when the call runs and may be //! anything a query answered. -use zu::query::Value; -use zu::session::Session; -use zu::zu1::file::Zu1File; -use zu::zu1::graph::bulk_load_as; +use zudb::query::Value; +use zudb::session::Session; +use zudb::zu1::file::Zu1File; +use zudb::zu1::graph::bulk_load_as; const NODES: u32 = 5; @@ -30,10 +30,10 @@ fn opened(name: &str) -> (tempfile::TempDir, Session) { // A property column, because an INSERT adds a row to the columns // the table has and a table with none has nowhere to put one. let ids: Vec = (0..NODES.into()).collect(); - zu::zu1::props::store_props( + zudb::zu1::props::store_props( &mut db, "person", - &[("id", zu::zu1::props::PropValues::Int(&ids))], + &[("id", zudb::zu1::props::PropValues::Int(&ids))], ) .expect("props"); drop(db); diff --git a/crates/zu/tests/quantifiers.rs b/crates/zu/tests/quantifiers.rs index 7de20edb..1f49a8be 100644 --- a/crates/zu/tests/quantifiers.rs +++ b/crates/zu/tests/quantifiers.rs @@ -7,9 +7,9 @@ //! step. It matters because the standard's own examples and the //! conformance corpus are written this way. -use zu::Database; -use zu::zu1::file::Zu1File; -use zu::zu1::graph::bulk_load_as; +use zudb::Database; +use zudb::zu1::file::Zu1File; +use zudb::zu1::graph::bulk_load_as; const NODES: u32 = 5; @@ -25,7 +25,7 @@ fn seeded(path: &std::path::Path) { struct Fixture { _dir: tempfile::TempDir, - conn: zu::Connection, + conn: zudb::Connection, } impl Fixture { diff --git a/crates/zu/tests/reachability.rs b/crates/zu/tests/reachability.rs index a57c1b73..47867e7d 100644 --- a/crates/zu/tests/reachability.rs +++ b/crates/zu/tests/reachability.rs @@ -11,9 +11,9 @@ //! person id, and it is the difference between a walk over the //! reachable set and a walk over every path through it. -use zu::query::{Value, explain_analyze, run}; use zu_zu1::file::Zu1File; use zu_zu1::graph::bulk_load_as; +use zudb::query::{Value, explain_analyze, run}; const NODES: u32 = 24; diff --git a/crates/zu/tests/records.rs b/crates/zu/tests/records.rs index cfff0615..3a91202a 100644 --- a/crates/zu/tests/records.rs +++ b/crates/zu/tests/records.rs @@ -10,9 +10,9 @@ //! reasonable engine makes quietly: answering false where the standard //! says raise, and answering about the wrong field. -use zu::query::{Value, run}; use zu_zu1::file::Zu1File; use zu_zu1::graph::bulk_load_as; +use zudb::query::{Value, run}; fn graph(dir: &std::path::Path) -> Zu1File { let mut zu = Zu1File::create(&dir.join("records.zu1")).unwrap(); diff --git a/crates/zu/tests/references.rs b/crates/zu/tests/references.rs index 1e0dd8e0..415cbe45 100644 --- a/crates/zu/tests/references.rs +++ b/crates/zu/tests/references.rs @@ -16,11 +16,11 @@ //! binding table is written as the query whose rows it holds. What is //! checked of those is that they answer what the parameter answers. -use zu::query::Value; -use zu::session::Session; -use zu::zu1::file::Zu1File; -use zu::zu1::graph::bulk_load_as; use zu_query::refs::{BindingTable, GraphHandle}; +use zudb::query::Value; +use zudb::session::Session; +use zudb::zu1::file::Zu1File; +use zudb::zu1::graph::bulk_load_as; /// Two people with a name, one edge, so that a row can hold an /// element and a later statement has a column to write into. @@ -30,10 +30,10 @@ fn opened(name: &str) -> (tempfile::TempDir, Session) { let mut db = Zu1File::create(&path).expect("create"); bulk_load_as(&mut db, "person", "knows", 2, &[(0, 1)]).expect("load"); let names: Vec<&[u8]> = vec![b"ada", b"kay"]; - zu::zu1::props::store_props( + zudb::zu1::props::store_props( &mut db, "person", - &[("name", zu::zu1::props::PropValues::Str(&names))], + &[("name", zudb::zu1::props::PropValues::Str(&names))], ) .expect("props"); drop(db); @@ -148,7 +148,7 @@ fn two_tables_over_the_same_rows_are_two_references() { assert_eq!(first, first.clone()); } -fn session_run(session: &mut Session, source: &str) -> zu::query::QueryResult { +fn session_run(session: &mut Session, source: &str) -> zudb::query::QueryResult { session.run(source, &[]).expect("rows") } diff --git a/crates/zu/tests/refusal_shape.rs b/crates/zu/tests/refusal_shape.rs index 4644d57a..8ad76288 100644 --- a/crates/zu/tests/refusal_shape.rs +++ b/crates/zu/tests/refusal_shape.rs @@ -23,15 +23,15 @@ //! ast, which is a change to a shared type and its own piece of work. //! The count at the bottom is what makes that debt a number. //! -//! `ZU_UPDATE_SNAPSHOTS=1 cargo test --release -p zu --test refusal_shape` +//! `ZU_UPDATE_SNAPSHOTS=1 cargo test --release -p zudb --test refusal_shape` //! rewrites the file, and the diff on the way into the commit is the //! review. use std::path::PathBuf; -use zu::query::run; use zu_zu1::file::Zu1File; use zu_zu1::graph::bulk_load_as; +use zudb::query::run; /// A file this test wrote itself, so every refusal below is about the /// statement rather than about the file. @@ -173,7 +173,7 @@ fn snapshot(name: &str, actual: &str) { } panic!( "{} is not what a refused declaration says. Read the difference, and if the new \ - wording is the intended one, `ZU_UPDATE_SNAPSHOTS=1 cargo test --release -p zu \ + wording is the intended one, `ZU_UPDATE_SNAPSHOTS=1 cargo test --release -p zudb \ --test refusal_shape` writes it.\n\n--- committed\n{committed}\n--- printed\n{actual}", path.display() ); diff --git a/crates/zu/tests/refusals.rs b/crates/zu/tests/refusals.rs index 3efbae94..ab1dcc0b 100644 --- a/crates/zu/tests/refusals.rs +++ b/crates/zu/tests/refusals.rs @@ -18,9 +18,9 @@ //! is a lie, and the same line is right in one caller and wrong in the //! other. -use zu::query::run; use zu_zu1::file::Zu1File; use zu_zu1::graph::bulk_load_as; +use zudb::query::run; /// A file this test wrote itself, so nothing in it is damaged and any /// sentence about damage is about something else. diff --git a/crates/zu/tests/rows.rs b/crates/zu/tests/rows.rs index 898287b7..3264f1af 100644 --- a/crates/zu/tests/rows.rs +++ b/crates/zu/tests/rows.rs @@ -8,10 +8,10 @@ //! copied out of it, and a parameter written with `params!` binds //! without the caller ever spelling a `Value`. -use zu::query::Value; -use zu::zu1::file::Zu1File; -use zu::zu1::graph::bulk_load_as; -use zu::{Database, params}; +use zudb::query::Value; +use zudb::zu1::file::Zu1File; +use zudb::zu1::graph::bulk_load_as; +use zudb::{Database, params}; const NODES: u32 = 200; @@ -103,7 +103,7 @@ fn asking_a_column_for_the_wrong_type_names_the_column_and_the_two_types() { .expect("query"); let row = rows.row(0).expect("a row"); let err = row.get_at::<&str>(0).expect_err("an int is not a string"); - assert_eq!(err.gqlstatus(), Some(zu::gqlstatus::codes::C22G03)); + assert_eq!(err.gqlstatus(), Some(zudb::gqlstatus::codes::C22G03)); let message = err.to_string(); assert!(message.contains("column 'id'"), "{message}"); assert!(message.contains("STRING"), "{message}"); diff --git a/crates/zu/tests/select.rs b/crates/zu/tests/select.rs index 8834bc1c..9aa18efc 100644 --- a/crates/zu/tests/select.rs +++ b/crates/zu/tests/select.rs @@ -17,8 +17,8 @@ //! than in front of the statement, and the having clause, which the //! other form has no word for at all. -use zu::Database; -use zu::query::Value; +use zudb::Database; +use zudb::query::Value; /// Five people in three cities, and one company, which is the second /// label a graph match list needs to have two matches to list. diff --git a/crates/zu/tests/sessions.rs b/crates/zu/tests/sessions.rs index b24ccee2..8f754b6e 100644 --- a/crates/zu/tests/sessions.rs +++ b/crates/zu/tests/sessions.rs @@ -11,12 +11,12 @@ //! a statement that answers no rows: the only way to see that it did //! anything is to run a second statement and read what it says. -use zu::query::Value; -use zu::session::{Session, Stale}; -use zu::zu1::file::Zu1File; -use zu::zu1::graph::bulk_load_as; use zu_common::Temporal; use zu_common::gqlstatus::codes; +use zudb::query::Value; +use zudb::session::{Session, Stale}; +use zudb::zu1::file::Zu1File; +use zudb::zu1::graph::bulk_load_as; /// The same four people the binding variable tests use, so a number /// here can be checked against a number there. @@ -26,12 +26,12 @@ fn opened(name: &str) -> (tempfile::TempDir, Session) { let mut db = Zu1File::create(&path).expect("create"); bulk_load_as(&mut db, "person", "knows", 4, &[(0, 1), (1, 2), (3, 3)]).expect("load"); let names: Vec<&[u8]> = vec![b"ann", b"bo", b"cy", b"di"]; - zu::zu1::props::store_props( + zudb::zu1::props::store_props( &mut db, "person", &[ - ("name", zu::zu1::props::PropValues::Str(&names)), - ("age", zu::zu1::props::PropValues::Int(&[30, 40, 50, 40])), + ("name", zudb::zu1::props::PropValues::Str(&names)), + ("age", zudb::zu1::props::PropValues::Int(&[30, 40, 50, 40])), ], ) .expect("props"); diff --git a/crates/zu/tests/simplified.rs b/crates/zu/tests/simplified.rs index 1f7bb937..5757a6df 100644 --- a/crates/zu/tests/simplified.rs +++ b/crates/zu/tests/simplified.rs @@ -18,9 +18,9 @@ //! as the pattern written as many ways as it has terms, which is what //! the bar between two whole path patterns is read as. -use zu::Database; -use zu::zu1::file::Zu1File; -use zu::zu1::graph::bulk_load_as; +use zudb::Database; +use zudb::zu1::file::Zu1File; +use zudb::zu1::graph::bulk_load_as; const NODES: u32 = 5; @@ -44,7 +44,7 @@ fn seeded_with_a_second_type(path: &std::path::Path) { struct Fixture { _dir: tempfile::TempDir, - conn: zu::Connection, + conn: zudb::Connection, } impl Fixture { diff --git a/crates/zu/tests/status_value.rs b/crates/zu/tests/status_value.rs index 7eabff92..1c99192f 100644 --- a/crates/zu/tests/status_value.rs +++ b/crates/zu/tests/status_value.rs @@ -7,10 +7,10 @@ //! the five characters, the words for them, and the diagnostic records //! under that. -use zu::Database; -use zu::query::Value; -use zu::zu1::file::Zu1File; -use zu::zu1::graph::bulk_load_as; +use zudb::Database; +use zudb::query::Value; +use zudb::zu1::file::Zu1File; +use zudb::zu1::graph::bulk_load_as; const NODES: u32 = 4; @@ -44,7 +44,7 @@ fn text(value: &Value, name: &str) -> String { } } -fn status(conn: &mut zu::Connection) -> Value { +fn status(conn: &mut zudb::Connection) -> Value { let result = conn .query("RETURN current_status() AS s") .expect("the status of the statement before this one"); diff --git a/crates/zu/tests/streaming.rs b/crates/zu/tests/streaming.rs index 635eb586..2f4a2058 100644 --- a/crates/zu/tests/streaming.rs +++ b/crates/zu/tests/streaming.rs @@ -8,10 +8,10 @@ //! for, that a caller can stop early and that the statements which //! cannot stream still arrive through the same loop. -use zu::query::Value; -use zu::zu1::file::Zu1File; -use zu::zu1::graph::bulk_load_as; -use zu::{Database, Engine, Flow, Options}; +use zudb::query::Value; +use zudb::zu1::file::Zu1File; +use zudb::zu1::graph::bulk_load_as; +use zudb::{Database, Engine, Flow, Options}; const NODES: u32 = 500; @@ -49,7 +49,7 @@ fn opened_with(name: &str, nodes: u32) -> (tempfile::TempDir, Database) { /// parallel, so setting it here was setting it for whichever test was /// between plans (#513). The interrupt handle survives the swap, which /// is what lets the stop test hold one across both engines. -fn pin(conn: &mut zu::Connection, engine: Engine) { +fn pin(conn: &mut zudb::Connection, engine: Engine) { let options = Options { engine, ..conn.session_mut().options().clone() @@ -57,7 +57,7 @@ fn pin(conn: &mut zu::Connection, engine: Engine) { conn.session_mut().set_options(options); } -fn buffered(conn: &mut zu::Connection, source: &str) -> Vec { +fn buffered(conn: &mut zudb::Connection, source: &str) -> Vec { conn.query(source) .expect("query") .iter() @@ -221,7 +221,7 @@ fn the_two_executors_stream_the_same_rows_in_the_same_order() { "MATCH (p:person) RETURN p.id AS id SKIP 33 LIMIT 111", "MATCH (p:person)-[:knows]->(f) RETURN f.id AS id LIMIT 250", ]; - let read = |conn: &mut zu::Connection, source: &str| { + let read = |conn: &mut zudb::Connection, source: &str| { let mut got = Vec::new(); let out = conn .query_stream_batched(source, &[], 48, |batch| { @@ -260,7 +260,7 @@ fn a_statement_interrupted_partway_through_a_stream_says_so() { let mut conn = db.connect().expect("connect"); let interrupt = conn.session_mut().interrupt(); - let stopping = |conn: &mut zu::Connection| { + let stopping = |conn: &mut zudb::Connection| { let mut rows = 0u64; let out = conn.query_stream_batched("MATCH (p:person) RETURN p.id AS id", &[], 16, |batch| { @@ -278,7 +278,10 @@ fn a_statement_interrupted_partway_through_a_stream_says_so() { pin(&mut conn, engine); let (rows, out) = stopping(&mut conn); let err = out.expect_err("the statement was interrupted"); - assert!(matches!(err, zu::ZuError::Interrupted), "{engine:?}: {err}"); + assert!( + matches!(err, zudb::ZuError::Interrupted), + "{engine:?}: {err}" + ); assert!( rows < u64::from(MANY), "{engine:?}: {rows} rows is the whole scan" @@ -319,7 +322,7 @@ fn a_streamed_statement_takes_parameters_and_a_failing_sink_fails_the_call() { // own condition back rather than one the engine invented for it. let err = conn .query_stream("MATCH (p:person) RETURN p.id AS id", &[], |_| { - Err(zu::ZuError::InvalidArgument( + Err(zudb::ZuError::InvalidArgument( "the writer is full".to_string(), )) }) diff --git a/crates/zu/tests/strings.rs b/crates/zu/tests/strings.rs index cac43003..ba6f2e56 100644 --- a/crates/zu/tests/strings.rs +++ b/crates/zu/tests/strings.rs @@ -7,8 +7,8 @@ //! company on anything that is not ASCII, and a number written where a //! string belongs is refused rather than measured by its spelling. -use zu::Database; -use zu::query::Value; +use zudb::Database; +use zudb::query::Value; /// Three names with spaces around one of them, and one word that is not /// ASCII, which is the only thing that tells the two lengths apart. diff --git a/crates/zu/tests/subpaths.rs b/crates/zu/tests/subpaths.rs index 45988307..bdf36cd2 100644 --- a/crates/zu/tests/subpaths.rs +++ b/crates/zu/tests/subpaths.rs @@ -7,9 +7,9 @@ //! variable bound outside the brackets, which is what makes it a non //! local predicate. -use zu::query::{Value, run}; use zu_zu1::file::Zu1File; use zu_zu1::graph::bulk_load_as; +use zudb::query::{Value, run}; /// A chain with a way back from the middle: /// diff --git a/crates/zu/tests/tck.rs b/crates/zu/tests/tck.rs index c90f73e5..ea4af6a0 100644 --- a/crates/zu/tests/tck.rs +++ b/crates/zu/tests/tck.rs @@ -13,13 +13,13 @@ //! ages 20, 30, 30, 40, 50, 25, and knows edges (0,1) (0,2) (1,3) //! (2,3) (2,5) (3,4) (4,0). -use zu::query::run as run_zu1; -use zu::sqlite::run as run_sqlite; use zu_query::exec::Value; use zu_sqlite::{ColumnType, SqliteStore, Value as SqlValue}; use zu_zu1::file::Zu1File; use zu_zu1::graph::bulk_load_as; use zu_zu1::props::{PropValues, store_props}; +use zudb::query::run as run_zu1; +use zudb::sqlite::run as run_sqlite; const NAMES: [&str; 6] = ["ada", "bob", "cat", "dan", "eve", "fay"]; const AGES: [u64; 6] = [20, 30, 30, 40, 50, 25]; diff --git a/crates/zu/tests/temporal.rs b/crates/zu/tests/temporal.rs index 295a2690..8763bbc6 100644 --- a/crates/zu/tests/temporal.rs +++ b/crates/zu/tests/temporal.rs @@ -6,13 +6,13 @@ //! whole way: written as a literal, stored in a lane, read back by the //! executor, and compared against the literal it was written from. -use zu::convert::sqlite_to_zu1; -use zu::query::{Value, run}; use zu_common::{DurationKind, Temporal}; use zu_sqlite::{ColumnType, SqliteStore, Value as SqlValue}; use zu_zu1::file::Zu1File; use zu_zu1::graph::bulk_load_as; use zu_zu1::props::{PropValues, store_props}; +use zudb::convert::sqlite_to_zu1; +use zudb::query::{Value, run}; fn graph(dir: &std::path::Path) -> Zu1File { let mut zu = Zu1File::create(&dir.join("temporal.zu1")).unwrap(); diff --git a/crates/zu/tests/typed.rs b/crates/zu/tests/typed.rs index 353740be..5c52e6aa 100644 --- a/crates/zu/tests/typed.rs +++ b/crates/zu/tests/typed.rs @@ -6,10 +6,10 @@ //! the types with structure in them parse where a name would do, and //! that a value type predicate is a boolean in a real row. -use zu::query::{Value, run, run_with}; -use zu::{Engine, Options}; use zu_zu1::file::Zu1File; use zu_zu1::graph::bulk_load_as; +use zudb::query::{Value, run, run_with}; +use zudb::{Engine, Options}; fn graph(dir: &std::path::Path) -> Zu1File { let mut zu = Zu1File::create(&dir.join("typed.zu1")).unwrap(); @@ -39,7 +39,7 @@ fn no(db: &mut Zu1File, predicate: &str) { /// rather than a variable in the environment: the environment belongs /// to the process and this binary runs its tests in parallel, so /// setting it here set it for whichever test was between plans (#513). -fn on_rows(db: &mut Zu1File, source: &str) -> zu::query::QueryResult { +fn on_rows(db: &mut Zu1File, source: &str) -> zudb::query::QueryResult { let options = Options { engine: Engine::Rows, ..Options::default() diff --git a/crates/zu/tests/undirected.rs b/crates/zu/tests/undirected.rs index 8b67f3d0..c6883e43 100644 --- a/crates/zu/tests/undirected.rs +++ b/crates/zu/tests/undirected.rs @@ -8,9 +8,9 @@ //! pattern half: `~[]~` walks it from either end, the arrows refuse it, //! and the two mixed spellings take it either way round. -use zu::query::{Value, run}; use zu_zu1::file::Zu1File; use zu_zu1::graph::{bulk_load_as, bulk_load_undirected_as}; +use zudb::query::{Value, run}; /// One undirected edge between two peers, which is the smallest graph /// where the way round matters. diff --git a/crates/zu/tests/use_graph.rs b/crates/zu/tests/use_graph.rs index 5b8e7fae..853c8632 100644 --- a/crates/zu/tests/use_graph.rs +++ b/crates/zu/tests/use_graph.rs @@ -20,11 +20,11 @@ //! for one statement, so naming two is refused rather than read as the //! graph changing partway through. -use zu::query::Value; -use zu::session::Session; -use zu::zu1::file::Zu1File; -use zu::zu1::graph::bulk_load_as; use zu_query::refs::GraphHandle; +use zudb::query::Value; +use zudb::session::Session; +use zudb::zu1::file::Zu1File; +use zudb::zu1::graph::bulk_load_as; const NODES: u32 = 5; @@ -41,10 +41,10 @@ fn opened(name: &str) -> (tempfile::TempDir, Session) { // columns the table has and a table with none has nowhere to put // one. let ids: Vec = (0..NODES.into()).collect(); - zu::zu1::props::store_props( + zudb::zu1::props::store_props( &mut db, "person", - &[("id", zu::zu1::props::PropValues::Int(&ids))], + &[("id", zudb::zu1::props::PropValues::Int(&ids))], ) .expect("props"); drop(db); diff --git a/crates/zu/tests/value_query.rs b/crates/zu/tests/value_query.rs index 1e10fa92..b86c8f58 100644 --- a/crates/zu/tests/value_query.rs +++ b/crates/zu/tests/value_query.rs @@ -13,10 +13,10 @@ //! with a warning saying so: the rows are right either way, and what //! is wrong with the statement is what it costs. -use zu::Database; -use zu::zu1::file::Zu1File; -use zu::zu1::graph::bulk_load_as; use zu_query::exec::Value; +use zudb::Database; +use zudb::zu1::file::Zu1File; +use zudb::zu1::graph::bulk_load_as; const NODES: u32 = 5; @@ -30,7 +30,7 @@ fn seeded(path: &std::path::Path) { struct Fixture { _dir: tempfile::TempDir, - conn: zu::Connection, + conn: zudb::Connection, } impl Fixture { @@ -59,11 +59,11 @@ impl Fixture { /// The same fixture opened as a session, which is the way in that /// hands back the warnings a statement raised beside its rows. -fn opened(name: &str) -> (tempfile::TempDir, zu::session::Session) { +fn opened(name: &str) -> (tempfile::TempDir, zudb::session::Session) { let dir = tempfile::tempdir().expect("tempdir"); let path = dir.path().join(name); seeded(&path); - let session = zu::session::Session::open(&path).expect("open"); + let session = zudb::session::Session::open(&path).expect("open"); (dir, session) } diff --git a/crates/zu/tests/visibility.rs b/crates/zu/tests/visibility.rs index 570b5993..922b46e0 100644 --- a/crates/zu/tests/visibility.rs +++ b/crates/zu/tests/visibility.rs @@ -6,8 +6,8 @@ use std::path::Path; use std::sync::Arc; use std::sync::atomic::{AtomicUsize, Ordering}; -use zu::dataset::{NodeFile, RelFile, load_dataset}; -use zu::session::Session; +use zudb::dataset::{NodeFile, RelFile, load_dataset}; +use zudb::session::Session; /// Accounts 10, 11 and 12, keyed, with two transfers between them. fn fixture(dir: &Path) -> std::path::PathBuf { diff --git a/crates/zu/tests/yield_clause.rs b/crates/zu/tests/yield_clause.rs index 4262d3df..35b70f22 100644 --- a/crates/zu/tests/yield_clause.rs +++ b/crates/zu/tests/yield_clause.rs @@ -14,9 +14,9 @@ //! statement in front returned rather than the variables a match wrote, //! which is the same clause reading the same scope. -use zu::Database; -use zu::zu1::file::Zu1File; -use zu::zu1::graph::bulk_load_as; +use zudb::Database; +use zudb::zu1::file::Zu1File; +use zudb::zu1::graph::bulk_load_as; const NODES: u32 = 5; @@ -33,7 +33,7 @@ fn seeded(path: &std::path::Path) { struct Fixture { _dir: tempfile::TempDir, - conn: zu::Connection, + conn: zudb::Connection, } impl Fixture { diff --git a/crates/zu/tests/zuql_parity.rs b/crates/zu/tests/zuql_parity.rs index 37d05afc..0cfcf8fd 100644 --- a/crates/zu/tests/zuql_parity.rs +++ b/crates/zu/tests/zuql_parity.rs @@ -7,13 +7,13 @@ //! the dense row contract, node offsets from zero, so ids compare //! verbatim with no translation. -use zu::query::run as run_zu1; -use zu::sqlite::run as run_sqlite; use zu_query::exec::Value; use zu_sqlite::{ColumnType, SqliteStore, Value as SqlValue}; use zu_zu1::file::Zu1File; use zu_zu1::graph::bulk_load_as; use zu_zu1::props::{PropValues, store_props}; +use zudb::query::run as run_zu1; +use zudb::sqlite::run as run_sqlite; /// splitmix64: deterministic, seedable, dependency-free. struct Rng(u64); diff --git a/docs/11-benchmarks-and-targets.md b/docs/11-benchmarks-and-targets.md index 4234d49f..49148dd3 100644 --- a/docs/11-benchmarks-and-targets.md +++ b/docs/11-benchmarks-and-targets.md @@ -18,7 +18,7 @@ Discipline rule (SoK arXiv:2404.00766): publish only reproducible, spec-complian | B10 | s3 monthly bill, T9 scenario replay | < $40 ±10% | simulated from request log | | B11 | cardinality q-error, generated graphs | p50 ≤ 2, p90 ≤ 10, p99 ≤ 100 | runs in CI, any bound violation fails | -B11 is `cargo bench -p zu --bench cardinality`. It builds its own uniform, hub, and funnel graphs rather than reading a dataset, which is why it runs in CI where the LDBC gate cannot. The same q-errors over LDBC SF1 come out of the `ldbc` bench on the gate machines. A bound violation, meaning an operator that produced more rows than the optimizer's pessimistic ceiling allowed, fails the run gated or not: the join order DP is built on those ceilings holding. +B11 is `cargo bench -p zudb --bench cardinality`. It builds its own uniform, hub, and funnel graphs rather than reading a dataset, which is why it runs in CI where the LDBC gate cannot. The same q-errors over LDBC SF1 come out of the `ldbc` bench on the gate machines. A bound violation, meaning an operator that produced more rows than the optimizer's pessimistic ceiling allowed, fails the run gated or not: the join order DP is built on those ceilings holding. ## 2. Macro benchmarks diff --git a/docs/13-bench-machines.md b/docs/13-bench-machines.md index 775a78ac..5df488f0 100644 --- a/docs/13-bench-machines.md +++ b/docs/13-bench-machines.md @@ -32,7 +32,7 @@ scripts/bench-remote.sh gamingpc # gate a ref on a host, prints GB/s and pass/fa make gate # same thing locally ``` -The gate is `ZU_GATE=1 cargo bench -p zu-encoding --bench decode`: it measures decoded bytes per second per encoding against the floors in `bench/budgets.toml` and exits nonzero below floor. Every new bench added in later milestones follows the same pattern: a harness-free bench binary, a floor in `budgets.toml`, real input from `~/data/zu`. +The gate is `ZU_GATE=1 cargo bench -p zudb-encoding --bench decode`: it measures decoded bytes per second per encoding against the floors in `bench/budgets.toml` and exits nonzero below floor. Every new bench added in later milestones follows the same pattern: a harness-free bench binary, a floor in `budgets.toml`, real input from `~/data/zu`. When an SF1 phase misses its ceiling the ldbc bench reruns that phase's query under `zu::query::profile` and prints the per operator profile under the GATE FAIL line, so the failure names an operator instead of just a phase. The seeded phases pick one seed for the rerun and print which, and a phase whose work is not a query says so rather than printing nothing. diff --git a/docs/api/model.json b/docs/api/model.json index 5ab636af..572ee7b9 100644 --- a/docs/api/model.json +++ b/docs/api/model.json @@ -8,7 +8,7 @@ "id": "zu::Appender", "kind": "struct", "name": "Appender", - "source": "zu::append::Appender", + "source": "zudb::append::Appender", "doc": "A bulk load in progress: rows buffered in memory, written to the\ndatabase when you flush.\n\nTake one with [`crate::Connection::appender`], append rows to it,\nand call [`Appender::close`]. Dropping it without closing flushes\ntoo, because a loader that forgot is better served by its data\narriving than by it vanishing, but a drop has nowhere to put an\nerror and so cannot tell you whether the flush worked. That is the\none footgun dx/04 §6 accepts, and `#[must_use]` is what points a\ncaller at the version that reports. The drop does not panic on a\nbuffer it is handed, in debug builds either: a loader that hits an\nerror between rows and returns is exactly the case where an\nappender is dropped with rows in it, and turning that into an abort\non the way out of an unwind would be the worse failure.\n\nTwo things a table can be that an appender will not append to, both\nrefused when the appender opens rather than at the flush that would\nhave failed. A column that holds a null has no way in this API to\nsay which rows the appended values are for, so the write statements\nof G3 are what answers it. A table whose row domain a rel table's\nkey index is built over cannot grow without that index being\nreallocated, so the fold refuses it, and this refuses it earlier,\nbefore a load is buffered against it." }, { @@ -170,7 +170,7 @@ "id": "zu::Config", "kind": "struct", "name": "Config", - "source": "zu::db::Config", + "source": "zudb::db::Config", "doc": "How a database is opened and what its statements are allowed to do.\n\nEvery field has a default that works, so a caller sets the one thing\nit cares about and nothing else. The builder takes `self` and gives\nit back, so the whole configuration is one expression and a\nhalf-built `Config` is never a thing anybody holds." }, { @@ -217,7 +217,7 @@ "id": "zu::Connection", "kind": "struct", "name": "Connection", - "source": "zu::db::Connection", + "source": "zudb::db::Connection", "doc": "One connection: statements run on it, in order, one at a time.\n\nIt is `Send` and not `Sync`, and every method takes `&mut self`, so\nthe compiler is what stops two threads from using one connection.\nThat is the same rule the C ABI states and has to check at runtime\n(`dx/02` §5), enforced here at no cost.\n\nA connection reads the database as of the statement it is running.\nEvery connection to one file shares the write side, and a statement\npicks up what that side has published before it compiles anything,\nso a commit on another connection is visible to the next statement\nhere without reconnecting. Nothing moves under a statement that has\nstarted: what it took at the top is what it reads to the end, which\nis the snapshot isolation of docs/08 §1.\n\nWrites queue. One connection at a time holds the write side of a\nfile, for the length of a write statement or of an explicit\ntransaction, and the rest wait in the order they asked." }, { @@ -242,7 +242,7 @@ "name": "duplicate", "of": "zu::Connection", "signature": "fn duplicate(&self) -> Result", - "doc": "Another connection to the same database, made from this one\nrather than from the path.\n\nThis is how a pool is written. [`Database::connect`] opens the\nfile and looks up the write side under its path; this forks a\ndescriptor off the side this connection already holds, so it\ncosts a schema load and no lookup, and it works on a database\nin memory, which has no path to look up.\n\nThe two are connections in every sense, not two names for one:\neach has its own plan cache, its own readers, its own interrupt\nand its own transaction. What they share is the write side, so\nthey queue behind each other to write and each sees what the\nother has committed, exactly as two connections from the same\n[`Database`] do. The switches and the read-only setting are\ncarried across, because a pool that handed out connections\nconfigured differently from the one it was seeded with would be\na trap.\n\n```no_run\nuse zu::Database;\n\nlet db = Database::memory()?;\nlet mut conn = db.connect()?;\nconn.query(\"INSERT (p:person {id: 1, name: 'ada'})\")?;\nlet mut other = conn.duplicate()?;\nlet rows = other.query(\"MATCH (p:person) RETURN p.name AS name\")?;\nassert_eq!(rows.rows.len(), 1);\n# Ok::<(), zu_common::ZuError>(())\n```" + "doc": "Another connection to the same database, made from this one\nrather than from the path.\n\nThis is how a pool is written. [`Database::connect`] opens the\nfile and looks up the write side under its path; this forks a\ndescriptor off the side this connection already holds, so it\ncosts a schema load and no lookup, and it works on a database\nin memory, which has no path to look up.\n\nThe two are connections in every sense, not two names for one:\neach has its own plan cache, its own readers, its own interrupt\nand its own transaction. What they share is the write side, so\nthey queue behind each other to write and each sees what the\nother has committed, exactly as two connections from the same\n[`Database`] do. The switches and the read-only setting are\ncarried across, because a pool that handed out connections\nconfigured differently from the one it was seeded with would be\na trap.\n\n```no_run\nuse zudb::Database;\n\nlet db = Database::memory()?;\nlet mut conn = db.connect()?;\nconn.query(\"INSERT (p:person {id: 1, name: 'ada'})\")?;\nlet mut other = conn.duplicate()?;\nlet rows = other.query(\"MATCH (p:person) RETURN p.name AS name\")?;\nassert_eq!(rows.rows.len(), 1);\n# Ok::<(), zu_common::ZuError>(())\n```" }, { "id": "zu::Connection::execute", @@ -274,7 +274,7 @@ "name": "explain_plan", "of": "zu::Connection", "signature": "fn explain_plan(&mut self, source: &str) -> Result", - "doc": "The same plan as operators rather than as text: the tree, the\ncolumns the statement answers with, the parameters it wants, and\nthe notes compiling it raised.\n\nA caller that reads a plan is asking a question about it, and\nevery one of those questions is easier to ask of a tree than of\na listing: whether the scan reaches an index, how deep the\nexpands go, which tables are touched. [`QueryPlan::render`] is\nwhat [`Self::explain`] returns, so the two are one thing printed\ntwo ways rather than two renderings that can drift.\n\n```no_run\nuse zu::Database;\n\nlet db = Database::open(\"social.zu1\")?;\nlet mut conn = db.connect()?;\nlet plan = conn.explain_plan(\"MATCH (p:person) RETURN p.id AS id\")?;\nlet root = plan.root.as_ref().expect(\"a statement with operators\");\nassert_eq!(root.op, \"Project\");\nassert_eq!(plan.columns, [\"id\"]);\n# Ok::<(), zu::ZuError>(())\n```" + "doc": "The same plan as operators rather than as text: the tree, the\ncolumns the statement answers with, the parameters it wants, and\nthe notes compiling it raised.\n\nA caller that reads a plan is asking a question about it, and\nevery one of those questions is easier to ask of a tree than of\na listing: whether the scan reaches an index, how deep the\nexpands go, which tables are touched. [`QueryPlan::render`] is\nwhat [`Self::explain`] returns, so the two are one thing printed\ntwo ways rather than two renderings that can drift.\n\n```no_run\nuse zudb::Database;\n\nlet db = Database::open(\"social.zu1\")?;\nlet mut conn = db.connect()?;\nlet plan = conn.explain_plan(\"MATCH (p:person) RETURN p.id AS id\")?;\nlet root = plan.root.as_ref().expect(\"a statement with operators\");\nassert_eq!(root.op, \"Project\");\nassert_eq!(plan.columns, [\"id\"]);\n# Ok::<(), zudb::ZuError>(())\n```" }, { "id": "zu::Connection::interrupt", @@ -322,7 +322,7 @@ "name": "query_stream", "of": "zu::Connection", "signature": "fn query_stream(&mut self, source: &str, params: &[(&str, Value)], sink: impl FnMut) -> Result", - "doc": "Runs one statement and hands its rows to `sink` in batches as\nthey are made, instead of returning them all.\n\nThis is the shape for a result that is too big to want in\nmemory, and for a caller that will not read all of it: the sink\nanswers [`Flow::Stop`] and the scan under it stops at the next\nboundary, the same boundary an interrupt is answered at. A batch\nborrows the rows for the length of the call, so a caller keeping\nanything past it copies what it wants out.\n\n```no_run\nuse zu::{Database, Flow};\n\nlet db = Database::open(\"social.zu1\")?;\nlet mut conn = db.connect()?;\nlet mut total = 0i64;\nconn.query_stream(\"MATCH (p:person) RETURN p.id AS id\", &[], |batch| {\n for row in batch.iter() {\n total += row.get_at::(0)?;\n }\n Ok(Flow::More)\n})?;\n# Ok::<(), zu::ZuError>(())\n```" + "doc": "Runs one statement and hands its rows to `sink` in batches as\nthey are made, instead of returning them all.\n\nThis is the shape for a result that is too big to want in\nmemory, and for a caller that will not read all of it: the sink\nanswers [`Flow::Stop`] and the scan under it stops at the next\nboundary, the same boundary an interrupt is answered at. A batch\nborrows the rows for the length of the call, so a caller keeping\nanything past it copies what it wants out.\n\n```no_run\nuse zudb::{Database, Flow};\n\nlet db = Database::open(\"social.zu1\")?;\nlet mut conn = db.connect()?;\nlet mut total = 0i64;\nconn.query_stream(\"MATCH (p:person) RETURN p.id AS id\", &[], |batch| {\n for row in batch.iter() {\n total += row.get_at::(0)?;\n }\n Ok(Flow::More)\n})?;\n# Ok::<(), zudb::ZuError>(())\n```" }, { "id": "zu::Connection::query_stream_batched", @@ -384,7 +384,7 @@ "id": "zu::Database", "kind": "struct", "name": "Database", - "source": "zu::db::Database", + "source": "zudb::db::Database", "doc": "An open database: a path, a configuration, and the fact that both\nhave been checked against a real file.\n\nIt is `Send + Sync` and holds no file descriptor and no cache, which\nis what makes it shareable without a lock. The state that cannot be\nshared, the seek position of a handle and the plans compiled against\na catalog, lives on the connections instead, one set per connection,\nwhich is the same division the C ABI and every binding above it use." }, { @@ -664,7 +664,7 @@ "id": "zu::Field", "kind": "enum", "name": "Field", - "source": "zu::append::Field", + "source": "zudb::append::Field", "doc": "One value on its way into a column.\n\nThis is deliberately not [`crate::query::Value`]. A value coming out\nof a query owns its string, because it outlives the row it was read\nfrom; a value going into a column borrows one, because the caller\nalready has it and the appender is about to copy it into a buffer\neither way. On a string column that is the difference between one\ncopy per row and two.\n\nThere is no null. A column that holds a null cannot be appended to\nat all, for the reason [`Appender`] gives, so a null here could only\never be refused, and refusing it in the type is better than\nrefusing it at flush." }, { @@ -2576,7 +2576,7 @@ "id": "zu::append", "kind": "module", "name": "append", - "doc": "The bulk-load appender of dx/04 §6.\n\nA row at a time through a write statement is the wrong shape for\nloading data: every row would be parsed, bound, planned, and\ncommitted, and the commit is the expensive part, so a million rows\nwould be a million commits and the load would be dominated by\ndurability work nobody asked for. The appender is the other shape.\nRows go into per-column buffers in memory, and a flush turns the\nwhole buffer into sealed segments in the data file with one\n`IngestRef` frame in the log, which is the WAL bypass docs/08 §6\ndescribes: the log stays a handful of bytes whether the flush\ncarries ten rows or ten million.\n\n```no_run\nuse zu::Database;\n\nlet db = Database::open(\"social.zu1\")?;\nlet mut conn = db.connect()?;\nlet mut app = conn.appender(\"person\")?;\napp.append_row((1i64, \"ada\"))?;\napp.append_row((2i64, \"grace\"))?;\napp.close()?;\n# Ok::<(), zu::ZuError>(())\n```\n\nA row is every column of the table, in the order the table declares\nthem, and a column is a position rather than a name: naming the\ncolumns per row would cost a lookup per value on the one path where\nper-value cost is the whole story, and a loader knows its own\ncolumn order.\n\nA flush is one commit. When it returns the rows are durable and\nevery later query sees them, and before it returns nothing sees\nanything, because a flush ends by folding the sealed segments into\nthe base the query path reads. That fold costs time proportional to\nthe table rather than to the batch, which is the whole reason the\nappender buffers: flush once per load, not once per row." + "doc": "The bulk-load appender of dx/04 §6.\n\nA row at a time through a write statement is the wrong shape for\nloading data: every row would be parsed, bound, planned, and\ncommitted, and the commit is the expensive part, so a million rows\nwould be a million commits and the load would be dominated by\ndurability work nobody asked for. The appender is the other shape.\nRows go into per-column buffers in memory, and a flush turns the\nwhole buffer into sealed segments in the data file with one\n`IngestRef` frame in the log, which is the WAL bypass docs/08 §6\ndescribes: the log stays a handful of bytes whether the flush\ncarries ten rows or ten million.\n\n```no_run\nuse zudb::Database;\n\nlet db = Database::open(\"social.zu1\")?;\nlet mut conn = db.connect()?;\nlet mut app = conn.appender(\"person\")?;\napp.append_row((1i64, \"ada\"))?;\napp.append_row((2i64, \"grace\"))?;\napp.close()?;\n# Ok::<(), zudb::ZuError>(())\n```\n\nA row is every column of the table, in the order the table declares\nthem, and a column is a position rather than a name: naming the\ncolumns per row would cost a lookup per value on the one path where\nper-value cost is the whole story, and a loader knows its own\ncolumn order.\n\nA flush is one commit. When it returns the rows are durable and\nevery later query sees them, and before it returns nothing sees\nanything, because a flush ends by folding the sealed segments into\nthe base the query path reads. That fold costs time proportional to\nthe table rather than to the batch, which is the whole reason the\nappender buffers: flush once per load, not once per row." }, { "id": "zu::append::AppendRow", @@ -2912,7 +2912,7 @@ "id": "zu::db", "kind": "module", "name": "db", - "doc": "The public Rust API: a [`Database`] you open once and share, and a\n[`Connection`] you take one of per thread.\n\n[`crate::session::Session`] is the engine's own entry point and it\nis one object doing both jobs, which works exactly as long as a\nprocess wants one. The moment two threads want to read the same\ngraph, or a pool wants to hand a connection out and take it back,\nthe two jobs come apart: the database is the thing that is shared\nand immutable, and the connection is the thing that is cheap,\nserial, and owned by whoever is using it. Every binding on the C ABI\ninherits this split (`dx/02` §3), so it is the Rust API that has to\nhave it first.\n\n```no_run\nuse zu::Database;\n\nlet db = Database::open(\"social.zu1\")?;\nlet mut conn = db.connect()?;\nlet rows = conn.query(\"MATCH (p:Person) RETURN p.name\")?;\n# Ok::<(), zu::ZuError>(())\n```\n\n[`Database::open`] takes a path and nothing else, because a\nconfiguration argument every caller has to write is a tax on every\ncaller to serve the few who set anything; [`Database::open_with`] is\nfor those few. Opening reads 12 KiB and pages the rest lazily, so\nconnecting is an open and a catalog load rather than a copy of the\nfile, and a connection is genuinely cheap to take." + "doc": "The public Rust API: a [`Database`] you open once and share, and a\n[`Connection`] you take one of per thread.\n\n[`crate::session::Session`] is the engine's own entry point and it\nis one object doing both jobs, which works exactly as long as a\nprocess wants one. The moment two threads want to read the same\ngraph, or a pool wants to hand a connection out and take it back,\nthe two jobs come apart: the database is the thing that is shared\nand immutable, and the connection is the thing that is cheap,\nserial, and owned by whoever is using it. Every binding on the C ABI\ninherits this split (`dx/02` §3), so it is the Rust API that has to\nhave it first.\n\n```no_run\nuse zudb::Database;\n\nlet db = Database::open(\"social.zu1\")?;\nlet mut conn = db.connect()?;\nlet rows = conn.query(\"MATCH (p:Person) RETURN p.name\")?;\n# Ok::<(), zudb::ZuError>(())\n```\n\n[`Database::open`] takes a path and nothing else, because a\nconfiguration argument every caller has to write is a tax on every\ncaller to serve the few who set anything; [`Database::open_with`] is\nfor those few. Opening reads 12 KiB and pages the rest lazily, so\nconnecting is an open and a catalog load rather than a copy of the\nfile, and a connection is genuinely cheap to take." }, { "id": "zu::db::Config", @@ -2988,7 +2988,7 @@ "name": "duplicate", "of": "zu::db::Connection", "signature": "fn duplicate(&self) -> Result", - "doc": "Another connection to the same database, made from this one\nrather than from the path.\n\nThis is how a pool is written. [`Database::connect`] opens the\nfile and looks up the write side under its path; this forks a\ndescriptor off the side this connection already holds, so it\ncosts a schema load and no lookup, and it works on a database\nin memory, which has no path to look up.\n\nThe two are connections in every sense, not two names for one:\neach has its own plan cache, its own readers, its own interrupt\nand its own transaction. What they share is the write side, so\nthey queue behind each other to write and each sees what the\nother has committed, exactly as two connections from the same\n[`Database`] do. The switches and the read-only setting are\ncarried across, because a pool that handed out connections\nconfigured differently from the one it was seeded with would be\na trap.\n\n```no_run\nuse zu::Database;\n\nlet db = Database::memory()?;\nlet mut conn = db.connect()?;\nconn.query(\"INSERT (p:person {id: 1, name: 'ada'})\")?;\nlet mut other = conn.duplicate()?;\nlet rows = other.query(\"MATCH (p:person) RETURN p.name AS name\")?;\nassert_eq!(rows.rows.len(), 1);\n# Ok::<(), zu_common::ZuError>(())\n```" + "doc": "Another connection to the same database, made from this one\nrather than from the path.\n\nThis is how a pool is written. [`Database::connect`] opens the\nfile and looks up the write side under its path; this forks a\ndescriptor off the side this connection already holds, so it\ncosts a schema load and no lookup, and it works on a database\nin memory, which has no path to look up.\n\nThe two are connections in every sense, not two names for one:\neach has its own plan cache, its own readers, its own interrupt\nand its own transaction. What they share is the write side, so\nthey queue behind each other to write and each sees what the\nother has committed, exactly as two connections from the same\n[`Database`] do. The switches and the read-only setting are\ncarried across, because a pool that handed out connections\nconfigured differently from the one it was seeded with would be\na trap.\n\n```no_run\nuse zudb::Database;\n\nlet db = Database::memory()?;\nlet mut conn = db.connect()?;\nconn.query(\"INSERT (p:person {id: 1, name: 'ada'})\")?;\nlet mut other = conn.duplicate()?;\nlet rows = other.query(\"MATCH (p:person) RETURN p.name AS name\")?;\nassert_eq!(rows.rows.len(), 1);\n# Ok::<(), zu_common::ZuError>(())\n```" }, { "id": "zu::db::Connection::execute", @@ -3020,7 +3020,7 @@ "name": "explain_plan", "of": "zu::db::Connection", "signature": "fn explain_plan(&mut self, source: &str) -> Result", - "doc": "The same plan as operators rather than as text: the tree, the\ncolumns the statement answers with, the parameters it wants, and\nthe notes compiling it raised.\n\nA caller that reads a plan is asking a question about it, and\nevery one of those questions is easier to ask of a tree than of\na listing: whether the scan reaches an index, how deep the\nexpands go, which tables are touched. [`QueryPlan::render`] is\nwhat [`Self::explain`] returns, so the two are one thing printed\ntwo ways rather than two renderings that can drift.\n\n```no_run\nuse zu::Database;\n\nlet db = Database::open(\"social.zu1\")?;\nlet mut conn = db.connect()?;\nlet plan = conn.explain_plan(\"MATCH (p:person) RETURN p.id AS id\")?;\nlet root = plan.root.as_ref().expect(\"a statement with operators\");\nassert_eq!(root.op, \"Project\");\nassert_eq!(plan.columns, [\"id\"]);\n# Ok::<(), zu::ZuError>(())\n```" + "doc": "The same plan as operators rather than as text: the tree, the\ncolumns the statement answers with, the parameters it wants, and\nthe notes compiling it raised.\n\nA caller that reads a plan is asking a question about it, and\nevery one of those questions is easier to ask of a tree than of\na listing: whether the scan reaches an index, how deep the\nexpands go, which tables are touched. [`QueryPlan::render`] is\nwhat [`Self::explain`] returns, so the two are one thing printed\ntwo ways rather than two renderings that can drift.\n\n```no_run\nuse zudb::Database;\n\nlet db = Database::open(\"social.zu1\")?;\nlet mut conn = db.connect()?;\nlet plan = conn.explain_plan(\"MATCH (p:person) RETURN p.id AS id\")?;\nlet root = plan.root.as_ref().expect(\"a statement with operators\");\nassert_eq!(root.op, \"Project\");\nassert_eq!(plan.columns, [\"id\"]);\n# Ok::<(), zudb::ZuError>(())\n```" }, { "id": "zu::db::Connection::interrupt", @@ -3068,7 +3068,7 @@ "name": "query_stream", "of": "zu::db::Connection", "signature": "fn query_stream(&mut self, source: &str, params: &[(&str, Value)], sink: impl FnMut) -> Result", - "doc": "Runs one statement and hands its rows to `sink` in batches as\nthey are made, instead of returning them all.\n\nThis is the shape for a result that is too big to want in\nmemory, and for a caller that will not read all of it: the sink\nanswers [`Flow::Stop`] and the scan under it stops at the next\nboundary, the same boundary an interrupt is answered at. A batch\nborrows the rows for the length of the call, so a caller keeping\nanything past it copies what it wants out.\n\n```no_run\nuse zu::{Database, Flow};\n\nlet db = Database::open(\"social.zu1\")?;\nlet mut conn = db.connect()?;\nlet mut total = 0i64;\nconn.query_stream(\"MATCH (p:person) RETURN p.id AS id\", &[], |batch| {\n for row in batch.iter() {\n total += row.get_at::(0)?;\n }\n Ok(Flow::More)\n})?;\n# Ok::<(), zu::ZuError>(())\n```" + "doc": "Runs one statement and hands its rows to `sink` in batches as\nthey are made, instead of returning them all.\n\nThis is the shape for a result that is too big to want in\nmemory, and for a caller that will not read all of it: the sink\nanswers [`Flow::Stop`] and the scan under it stops at the next\nboundary, the same boundary an interrupt is answered at. A batch\nborrows the rows for the length of the call, so a caller keeping\nanything past it copies what it wants out.\n\n```no_run\nuse zudb::{Database, Flow};\n\nlet db = Database::open(\"social.zu1\")?;\nlet mut conn = db.connect()?;\nlet mut total = 0i64;\nconn.query_stream(\"MATCH (p:person) RETURN p.id AS id\", &[], |batch| {\n for row in batch.iter() {\n total += row.get_at::(0)?;\n }\n Ok(Flow::More)\n})?;\n# Ok::<(), zudb::ZuError>(())\n```" }, { "id": "zu::db::Connection::query_stream_batched", @@ -10691,6 +10691,13 @@ "signature": "fn settable(&self, col: usize, row: u64) -> bool", "doc": "Whether [`Self::set`] would take a write of `col` on row `row`,\nwhich a writer asks before it takes the commit that would make\none. It has to be asked rather than tried, because a commit that\ncannot be patched has to fold whole and nothing of it may reach\nthe patch first." }, + { + "id": "zu::zu1::props::fixed_octets", + "kind": "function", + "name": "fixed_octets", + "signature": "fn fixed_octets(ty: &zu_common::LogicalType) -> Option", + "doc": "The octet count every row of a column of this type holds, for a\ndeclaration that fixes one. `None` is a column whose rows may differ\nin length.\n\nA character bound is not one of these. `STRING(5,5)` is five\ncharacters and a character is one to four octets, so the width it\nfixes is a width in a unit the storage does not count in. A bound on\ncharacters is a check; a bound on octets is a layout.\n\nTwo of these are numbers rather than byte strings. The lane is sixty\nfour bits, so a hundred and twenty eight bit integer and a decimal\nwhose unscaled units want more than a word both go on the blob side\nof the store as sixteen little endian bytes, which is where a\n`BINARY(16)` already goes and by the same layout: one width, no\noffsets. What tells the three apart afterwards is the column's own\ntype, the way it tells a byte string from a character string.\n\nThe decimal is the one whose answer here depends on an argument\nrather than on the type, and that is the declared and encoding split\nat its plainest: `DECIMAL(9,2)` and `DECIMAL(29,2)` are one declared\ntype family stored two ways, and the reader is told which by the\nprecision it was declared with." + }, { "id": "zu::zu1::props::list_elements", "kind": "function", diff --git a/docs/clients/duckdb.md b/docs/clients/duckdb.md index 4b992309..95f29512 100644 --- a/docs/clients/duckdb.md +++ b/docs/clients/duckdb.md @@ -67,7 +67,7 @@ That part is fixed. `zu::query::column` does the transpose once, in the engine, | execute and Arrow table | 148 ms | 73 ms | | execute and pandas | 148 ms | 79 ms | -The statement itself is 45 ms of each of those, so the export went from 103 ms to 26 ms. Of the 26 that are left, 22 are the transpose, which `cargo bench -p zu-query --bench columnar` times on its own. The Arrow half is about four milliseconds and there is not much left in it. +The statement itself is 45 ms of each of those, so the export went from 103 ms to 26 ms. Of the 26 that are left, 22 are the transpose, which `cargo bench -p zudb-query --bench columnar` times on its own. The Arrow half is about four milliseconds and there is not much left in it. So the remaining gap was no longer a client problem. It was the sink, and it was one number: 22 ms to read a result whose rows the executor built out of vectors it then threw away. Section 4 is where that number went. `record_batches` is fixed alongside it, having been worse rather than better than its name promised: it built every batch into a `Vec` before handing back a reader, and a batch is a slice of a finished column now. @@ -87,7 +87,7 @@ That sentence was the work this page asked for, and it is done. `crates/zu-exec/ The types are known before the first row, which is what makes it simpler than the walk it replaces rather than harder. A projected item is a stored column with a declared type, a node, a row id or a constant, so there is no inference pass and no column that changes its mind halfway. One consequence is worth writing down: a result with no rows now reports the types the plan declared where the walk reported a column of nulls, which is what DuckDB does and is more use to a client building a schema. -`cargo bench -p zu --bench columns` measures it end to end on the same million rows of `INT64`, `DOUBLE` and a short `VARCHAR`, at one worker, with `ZU_SINK=rows` pinning the row build so the two sinks are timed on the same engine on the same machine in one sitting: +`cargo bench -p zudb --bench columns` measures it end to end on the same million rows of `INT64`, `DOUBLE` and a short `VARCHAR`, at one worker, with `ZU_SINK=rows` pinning the row build so the two sinks are timed on the same engine on the same machine in one sitting: | path | rows, as was | columns | |---|---|---| diff --git a/docs/gql-conformance-statement.md b/docs/gql-conformance-statement.md index b1caa167..73eeb65f 100644 --- a/docs/gql-conformance-statement.md +++ b/docs/gql-conformance-statement.md @@ -392,7 +392,7 @@ It is also a claim about one build on one machine on one day. Both are printed a The full report behind the tally is a megabyte of per-case timings, host readings and a wall clock, none of it the same twice, so it is not checked in anywhere. What is checked in is the tally this page is rendered from, and the four commands that regenerate the whole chain from an engine binary: ``` -cargo build --release -p zu-cli +cargo build --release -p zudb-cli gql-compat run -adapter zu -binary target/release/zu -fail-on none -out reports/zu zu conformance --tally reports/zu/zu.json > docs/conformance/zu.json zu conformance --statement docs/conformance/zu.json > docs/gql-conformance-statement.md diff --git a/docs/releasing.md b/docs/releasing.md new file mode 100644 index 00000000..8c051fef --- /dev/null +++ b/docs/releasing.md @@ -0,0 +1,60 @@ +# Releasing + +A release is a `v*` tag on `main`. The tag runs `.github/workflows/release.yml`, +and nothing a person does by hand after pushing it is part of the release. + +## Cutting one + +1. Move `version` in `[workspace.package]` and every `version = "=x.y.z"` + pin in `[workspace.dependencies]` together. The pins are exact, so a + pin left behind is a build that fails to resolve rather than a crate + published against the previous release. +2. `cargo check --workspace` to move `Cargo.lock`, which is committed: + the upload runs with `--locked`. +3. Add a `## x.y.z` section to `CHANGELOG.md`. It is the release notes, + and the workflow refuses a tag without one. +4. Merge that, then tag the merge commit and push the tag: + + git tag -a vX.Y.Z -m vX.Y.Z && git push origin vX.Y.Z + +## What the tag does + +- `verify` checks the tag against the workspace version and the + changelog, then runs `cargo publish --workspace --locked --dry-run`, + which builds every crate from its own tarball. Nothing is uploaded + until this passes. +- `crates-io` runs `scripts/publish-crates.sh`. It publishes every crate + without `publish = false` in dependency order, skips what the index + already has, and waits out the rate limits. A crate crates.io has never + seen goes up at one every ten minutes after a burst of five, so a + release that adds crates is slow once. A version of a crate it has seen + goes up at one a minute after a burst of thirty, and a crate can take + twenty versions a day. +- `publish` creates the GitHub release with the changelog section as its + notes and attaches the libzu archives with build provenance. It waits + for crates.io, so a release that exists is one `cargo add zudb` can get. + +A rehearsal (`workflow_dispatch`) runs everything but the two uploads. + +## When it stops halfway + +Re-run the failed jobs. The publish script reads the index on every +attempt and carries on from the first crate that is not up, so a re-run +is how a partial release is finished. If crates.io's daily version limit +is the reason it stopped, the job says so: wait for a slot to age out +before re-running. + +## The token + +`CARGO_REGISTRY_TOKEN` is a secret of the `crates-io` environment and not +of the repository. Only a `v*` tag can deploy to that environment, so +a branch, a pull request and a rehearsal never see it, and it sits in the +environment of one step. Use a crates.io token scoped to +`publish-new` and `publish-update` on `zudb` and `zudb-*`, with an expiry. +To replace it: + + gh secret set CARGO_REGISTRY_TOKEN --env crates-io -R tamnd/zu < token-file + +Once every crate exists on crates.io, trusted publishing (GitHub OIDC) +can replace the token for versions of them. A crate that is new to the +registry still needs a token for its first upload. diff --git a/fuzz/Cargo.toml b/fuzz/Cargo.toml index 7d5599e8..ce60e62a 100644 --- a/fuzz/Cargo.toml +++ b/fuzz/Cargo.toml @@ -10,10 +10,10 @@ cargo-fuzz = true [dependencies] libfuzzer-sys = "0.4" tempfile = "3" -zu-encoding = { path = "../crates/zu-encoding", features = ["zstd"] } -zu-query = { path = "../crates/zu-query" } -zu-vector = { path = "../crates/zu-vector" } -zu-zu1 = { path = "../crates/zu-zu1" } +zu-encoding = { package = "zudb-encoding", path = "../crates/zu-encoding", features = ["zstd"] } +zu-query = { package = "zudb-query", path = "../crates/zu-query" } +zu-vector = { package = "zudb-vector", path = "../crates/zu-vector" } +zu-zu1 = { package = "zudb-zu1", path = "../crates/zu-zu1" } [[bin]] name = "decode_for_bitpack" diff --git a/scripts/bench-remote.sh b/scripts/bench-remote.sh index 7a4bbdbe..0831b533 100755 --- a/scripts/bench-remote.sh +++ b/scripts/bench-remote.sh @@ -26,13 +26,13 @@ git fetch -q origin git reset -q --hard "$REF" . \$HOME/.cargo/env 2>/dev/null || true echo "host: \$(hostname), \$(nproc) cores, \$(rustc --version | cut -d' ' -f1-2)" -ZU_GATE=1 ZU_DATA=\$HOME/data/zu cargo bench -q -p zu-encoding --features zstd --bench decode 2>/dev/null -ZU_GATE=1 ZU_DATA=\$HOME/data/zu ZU_B6=$B6 cargo bench -q -p zu-zu1 --bench ingest 2>/dev/null -ZU_GATE=1 ZU_DATA=\$HOME/data/zu cargo bench -q -p zu-zu1 --bench blob 2>/dev/null -ZU_GATE=1 ZU_DATA=\$HOME/data/zu ZU_B7=$B7 cargo bench -q -p zu-zu1 --bench open 2>/dev/null -ZU_GATE=1 ZU_DATA=\$HOME/data/zu cargo bench -q -p zu --bench ldbc 2>/dev/null -ZU_GATE=1 cargo bench -q -p zu --bench groupby 2>/dev/null -ZU_GATE=1 cargo bench -q -p zu --bench topn 2>/dev/null +ZU_GATE=1 ZU_DATA=\$HOME/data/zu cargo bench -q -p zudb-encoding --features zstd --bench decode 2>/dev/null +ZU_GATE=1 ZU_DATA=\$HOME/data/zu ZU_B6=$B6 cargo bench -q -p zudb-zu1 --bench ingest 2>/dev/null +ZU_GATE=1 ZU_DATA=\$HOME/data/zu cargo bench -q -p zudb-zu1 --bench blob 2>/dev/null +ZU_GATE=1 ZU_DATA=\$HOME/data/zu ZU_B7=$B7 cargo bench -q -p zudb-zu1 --bench open 2>/dev/null +ZU_GATE=1 ZU_DATA=\$HOME/data/zu cargo bench -q -p zudb --bench ldbc 2>/dev/null +ZU_GATE=1 cargo bench -q -p zudb --bench groupby 2>/dev/null +ZU_GATE=1 cargo bench -q -p zudb --bench topn 2>/dev/null EOF ) diff --git a/scripts/crate-check.sh b/scripts/crate-check.sh index e561969d..bc26158b 100755 --- a/scripts/crate-check.sh +++ b/scripts/crate-check.sh @@ -28,9 +28,8 @@ # outside the workspace. # # The dependency is written in the git form rather than as a version, -# because zu is publish = false and there is nothing on crates.io yet. -# The day there is, this is the line that changes and the rest of the -# program stays as it is. +# because what is being checked is a revision, and a revision is on +# crates.io only once it has been released. set -eu @@ -68,7 +67,7 @@ version = "0.0.0" edition = "2024" [dependencies] -zudb = { package = "zu", git = "$repository", rev = "$revision" } +zudb = { git = "$repository", rev = "$revision" } EOF cd "$crate" diff --git a/scripts/libzu-build.sh b/scripts/libzu-build.sh index bf19205f..586cf4db 100755 --- a/scripts/libzu-build.sh +++ b/scripts/libzu-build.sh @@ -27,7 +27,7 @@ stage="dist/libzu-$target" # the CLI's feature and not the library's: the C ABI loads columns a # caller already has in memory, so an Arrow reader behind it would be # weight every embedder pays and nobody calls. -cargo build --release --target "$target" -p zu-capi -p zu-cli --features zu-cli/arrow +cargo build --release --target "$target" -p zu-capi -p zudb-cli --features zudb-cli/arrow # The dx/14 section 4 ceilings, from the same table as the matrix. Size # is a real adoption factor for serverless and mobile targets and it diff --git a/scripts/publish-crates.sh b/scripts/publish-crates.sh new file mode 100755 index 00000000..44011faa --- /dev/null +++ b/scripts/publish-crates.sh @@ -0,0 +1,144 @@ +#!/usr/bin/env bash +# Publishes the workspace to crates.io, and finishes what an earlier run +# started when it is run again. +# +# CARGO_REGISTRY_TOKEN=... scripts/publish-crates.sh +# +# `cargo publish --workspace` does the work: it skips every crate marked +# `publish = false`, puts the rest in dependency order and waits for the +# index between them. It does not cope with being rate limited or with +# being run a second time. A 429 halfway through stops it, and a second +# call then stops at the first crate that is already up. So this asks +# the index what is there, excludes it, and waits when the registry says +# to wait. tamnd/rudb learned all of this on its own releases, and this +# is its script with zu's names in it. +# +# crates.io has three limits, and only two of them can be waited out +# inside one job: +# +# a crate it has never seen a burst of 5, then one every ten minutes +# a new version of a crate a burst of 30, then one a minute +# a new version of a crate twenty in any twenty four hours +# +# The first release here is fourteen crates the registry has never seen, +# so it is about an hour and a half of mostly waiting, once. Every +# release after it takes a couple of minutes. The third limit is waited +# out in hours, so when it is the one in play this stops and says so, +# and a re-run once a slot frees finishes the release. +# +# Running it when everything is already up is a no-op that exits zero, +# which is what makes re-running the release job the way to recover from +# a partial upload. Nothing it prints contains the token. + +set -euo pipefail + +if [ -z "${CARGO_REGISTRY_TOKEN:-}" ]; then + echo "CARGO_REGISTRY_TOKEN is empty, so nothing can be published" >&2 + echo "it is a secret of the crates-io environment on tamnd/zu" >&2 + exit 1 +fi + +# The new crate limit plus slack, so a clock that disagrees with the +# registry by a few seconds does not cost a whole extra round. +new_pause=610 +# The same for the one a minute limit on a crate that already exists. +existing_pause=70 + +metadata=$(cargo metadata --format-version 1 --no-deps) +version=$(echo "$metadata" | jq -r '.packages[] | select(.name == "zudb") | .version') +# `publish = false` comes out of cargo metadata as an empty list. +crates=$(echo "$metadata" | jq -r '.packages[] | select(.publish != []) | .name' | sort) +total=$(echo "$crates" | wc -w | tr -d ' ') +echo "publishing $total crates at $version:" $crates + +# Where a crate lives in the sparse index, which is by the length of its +# name and is the rule every registry client implements. +index_path() { + local name=$1 + case ${#name} in + 1) echo "1/$name" ;; + 2) echo "2/$name" ;; + 3) echo "3/${name:0:1}/$name" ;; + *) echo "${name:0:2}/${name:2:2}/$name" ;; + esac +} + +# The index rather than the API, because the index is what cargo reads +# and a crate never published is a 404 there rather than an empty +# answer. That 404 is what decides which rate limit applies. +index_entry() { + curl --silent --fail "https://index.crates.io/$(index_path "$1")" 2>/dev/null || true +} + +log=$(mktemp) +trap 'rm -f "$log"' EXIT + +# One attempt per crate still to do, plus a few. An attempt that uploads +# nothing is the only kind worth budgeting for, and no shape of rate +# limit makes two of those in a row. +attempts=$((total + 5)) +remaining=$total + +for attempt in $(seq 1 "$attempts"); do + exclude=() + up=0 + brand_new=0 + for crate in $crates; do + entry=$(index_entry "$crate") + if echo "$entry" | grep -q "\"vers\":\"$version\""; then + exclude+=(--exclude "$crate") + up=$((up + 1)) + elif [ -z "$entry" ]; then + brand_new=$((brand_new + 1)) + fi + done + + if [ "$up" -eq "$total" ]; then + echo "all $total crates are on crates.io at $version" + exit 0 + fi + + remaining=$((total - up)) + if [ "$brand_new" -gt 0 ]; then + pause=$new_pause + echo "attempt $attempt: $up of $total up, $remaining to go, $brand_new never published before" + else + pause=$existing_pause + echo "attempt $attempt: $up of $total up, $remaining to go, all of them existing crates" + fi + + # `${x[@]+"${x[@]}"}` because bash 3.2, which is the bash on a Mac, + # calls an empty array unbound under `set -u`, and a Mac is where + # this gets run by hand when the runner is not cooperating. + if cargo publish --workspace --locked ${exclude[@]+"${exclude[@]}"} 2>&1 | tee "$log"; then + echo "published $remaining crates at $version" + exit 0 + fi + + # Something else published a crate while this ran, which is the + # normal case when the workflow and a hand run race. The index is + # read again at the top of the loop and that read decides. + if grep -q "already exists on crates.io index" "$log"; then + echo "a crate went up from somewhere else while this ran, reading the index again" + sleep 10 + continue + fi + + if grep -q "too many versions of this crate in the last 24 hours" "$log"; then + echo "crates.io allows twenty versions of a crate a day and this one is at it" >&2 + echo "$up of $total are up at $version, and a re-run carries on from there" >&2 + echo "a slot frees when the oldest upload of the day ages out" >&2 + exit 1 + fi + + if ! grep -q "429 Too Many Requests" "$log"; then + echo "the publish failed for a reason that waiting will not fix" >&2 + exit 1 + fi + echo "crates.io asked for a slower pace, waiting ${pause}s and carrying on" + sleep "$pause" +done + +echo "gave up after $attempts attempts with $((total - remaining)) of $total up at $version" >&2 +echo "nothing is lost: re-run the job and it carries on from here" >&2 +exit 1