diff --git a/.github/workflows/rust.yml b/.github/workflows/rust.yml index 278ef45..da511e6 100644 --- a/.github/workflows/rust.yml +++ b/.github/workflows/rust.yml @@ -11,7 +11,7 @@ on: - master env: - toolchain: nightly-2022-08-22 + toolchain: stable GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} jobs: diff --git a/docs/case-studies/issue-23/README.md b/docs/case-studies/issue-23/README.md new file mode 100644 index 0000000..00fa5d5 --- /dev/null +++ b/docs/case-studies/issue-23/README.md @@ -0,0 +1,252 @@ +# Case Study: Upgrade to Latest `doublets-rs` and Switch to Stable Rust (Issue #23) + +## Summary + +The `Comparisons.PostgreSQLVSDoublets` benchmark project depended on an old, +pre-release, **git-pinned** build of [`doublets-rs`](https://github.com/linksplatform/doublets-rs) +(`0.1.0-pre+beta.15`) and required the **nightly** Rust toolchain +(`nightly-2022-08-22`). The nightly pin was needed because that version of +`doublets` relied on unstable language features (`allocator_api`, +`generic_associated_types`, and the `Try` trait for `?` on a custom control-flow +type). + +Meanwhile, upstream `doublets-rs` released **v0.3.0** (2026-04-18), which is +published on crates.io and — most importantly — **migrated from nightly to +stable Rust (1.85+)**. This issue tracks adopting that release and removing the +nightly requirement from this repository. + +- **Issue**: https://github.com/linksplatform/Comparisons.PostgreSQLVSDoublets/issues/23 +- **Pull Request**: https://github.com/linksplatform/Comparisons.PostgreSQLVSDoublets/pull/24 +- **Upstream library**: https://github.com/linksplatform/doublets-rs +- **Labels**: `documentation`, `enhancement`, `dependencies` + +## Requirements (extracted from the issue) + +The issue body contains both explicit migration requirements and process +requirements. Each is listed below and tracked to its resolution. + +| # | Requirement | Source (issue text) | Status | +|---|-------------|---------------------|--------| +| R1 | Use the **latest version of `doublets-rs`** | "Make sure to use latest version of doublets-rs" (title) + link to repo | ✅ `doublets = "0.3.0"` (crates.io, latest release) | +| R2 | Switch the project to **stable Rust** | "make sure to switch to stable rust" (title) | ✅ `rust-toolchain.toml` + CI use `stable`; no `#![feature(...)]` remains | +| R3 | Collect data about the issue into `./docs/case-studies/issue-{id}` | "compile that data to `./docs/case-studies/issue-{id}` folder" | ✅ This `docs/case-studies/issue-23/` folder | +| R4 | Deep case-study analysis, **including online research** | "do deep case study analysis (also make sure to search online for additional facts and data)" | ✅ This document (upstream release notes, crates.io, tags) | +| R5 | List of **each and all requirements** from the issue | "list of each and all requirements from the issue" | ✅ This table | +| R6 | Propose **possible solutions and solution plans** for each requirement | "propose possible solutions and solution plans for each requirement" | ✅ [Proposed Solutions](#proposed-solutions) | +| R7 | Check **known existing components/libraries** that solve a similar problem | "we should also check known existing components/libraries…" | ✅ [Existing Components & Libraries](#existing-components--libraries-surveyed) | +| R8 | Execute everything in the **single existing PR #24** | "Please plan and execute everything in this single pull request" | ✅ All commits land on branch `issue-23-dff35da4182d` (PR #24) | + +## Background & Online Research + +Data gathered from the upstream repository, crates.io, and the locally vendored +crate sources (`~/.cargo/registry`). + +### `doublets-rs` release history + +| Version | Date | Toolchain | Notes | +|---------|------|-----------|-------| +| `0.1.0-pre+beta.15` | (pre-release) | nightly | The version this repo was pinned to (git dependency, no tag) | +| `v0.2.0` | 2026-04-14 | nightly-2022-08-22 | First tagged/published release; still nightly | +| `v0.3.0` | 2026-04-18 | **stable (1.85+)** | Latest release; the target of this issue | + +Source: `gh release list --repo linksplatform/doublets-rs` and +`cargo search doublets` (shows `doublets = "0.3.0"` as the newest). + +### Key facts from the `doublets` v0.3.0 release notes + +The v0.3.0 changelog lists several **BREAKING** changes directly relevant here: + +> - **BREAKING**: Migrated from nightly Rust to stable Rust (1.85+) +> - **BREAKING**: `Fuse` no longer implements `FnMut`; use `.call()` method instead +> - **BREAKING**: Removed `Handler` trait; handlers now use `FnMut(Link, Link) -> Flow` directly +> - Migrated `platform-mem` dependency from git submodule to crates.io v0.3.0 +> - Migrated `platform-trees` dependency from git submodule to crates.io v0.3.3 + +The crate manifest confirms the toolchain target: + +```toml +# doublets 0.3.0 Cargo.toml +edition = "2021" +rust-version = "1.85" +license = "Unlicense" +repository = "https://github.com/linksplatform/doublets-rs" +``` + +### Transitive dependency versions pulled by `doublets 0.3.0` + +| Crate | Version | Role | +|-------|---------|------| +| `platform-data` | 2.0.0 | `Flow`, `LinkType`/`LinkReference`, `LinksConstants`, handlers | +| `platform-mem` | 0.3.0 | Memory backends (`Global`, `FileMapped`) | +| `platform-num` | 0.8.0 | `LinkReference` numeric trait | +| `allocator-api2` | 0.4.0 | Stable shim for the nightly `allocator_api` | + +The critical insight: the nightly `allocator_api` is now provided on **stable** +through the `allocator-api2` crate, which is why `doublets 0.3.0` no longer needs +nightly for custom allocators. + +## Root Cause Analysis (why nightly was required before) + +The previous dependency (`0.1.0-pre+beta.15`) forced nightly for three reasons: + +### 1. `allocator_api` (custom allocator generics) +The old in-memory backend was spelled `Alloc`, where +`std::alloc::Global` and the `Allocator` trait were nightly-only. This is the +single biggest reason the repo pinned `nightly-2022-08-22`. + +### 2. `generic_associated_types` (GATs) +The `Benched` trait uses an associated type with a lifetime parameter +(`type Builder<'params>;`). GATs were unstable in 2022 but **stabilized in Rust +1.65**, so no source change is required for this on a modern stable compiler. + +### 3. `Try` for `?` on `Flow` +The old `doublets::data::Flow` implemented the unstable `Try` trait so callback +code could write `handler(link)?` to short-circuit iteration. On stable, `Flow` +no longer implements `Try`, so `?` cannot be applied to it. + +## Proposed Solutions + +For each requirement, the solution actually adopted is marked **[chosen]**. + +### R1 — Use the latest `doublets-rs` + +- **Option A [chosen]: Depend on the published crate `doublets = "0.3.0"`.** + Reproducible, versioned, no submodules, resolved through crates.io. Matches the + upstream recommendation and the `dependencies` label intent. +- Option B: Keep a `git` dependency pinned to tag `v0.3.0`. Rejected — a + published crate is strictly better for reproducibility and tooling (Dependabot, + `cargo audit`). +- Option C: Track `git` `HEAD`. Rejected — non-reproducible. + +### R2 — Switch to stable Rust + +- **Option A [chosen]: Set the toolchain to `stable` and migrate all + nightly-only API.** See the migration map below. +- Option B: Stay on nightly but only bump the dependency. Rejected — directly + contradicts the issue title. + +The migration map applied to this repository: + +| Old (nightly, beta.15) | New (stable, 0.3.0) | Files | +|------------------------|---------------------|-------| +| `#![feature(allocator_api)]`, `#![feature(generic_associated_types)]` | *(removed)* | `rust/src/lib.rs`, `rust/benches/bench.rs` | +| `Alloc` | `doublets::mem::Global`, built with `Global::new()` | `doublets_benched.rs`, all `benches/.../doublets/*.rs` | +| `doublets::data::LinkType` | `doublets::data::LinkReference` | `client.rs`, `transaction.rs`, `*_benched.rs`, `lib.rs` | +| `T::ZERO` | `T::from_byte(0)` | `client.rs`, `transaction.rs` | +| `value.as_i64()` | `as_i64(value)` helper wrapping `TryInto` | `client.rs`, `transaction.rs`, `lib.rs` | +| `handler(link)?` (relied on `Try for Flow`) | `if handler(link).is_break() { return Flow::Break; }` | `client.rs`, `transaction.rs` | +| `doublets::parts::LinkPart` | `doublets::unit::LinkPart` | `benches/.../doublets/*.rs` | +| `channel = "nightly-2022-08-22"` | `channel = "stable"` | `rust-toolchain.toml` | +| CI `env.toolchain: nightly-2022-08-22` | `toolchain: stable` | `.github/workflows/rust.yml` | + +Notably, `doublets`'s own `Links`/`Doublets` traits keep their +`Link`-based read/write handler signatures, so the PostgreSQL backend logic in +`client.rs`/`transaction.rs` only needed **mechanical symbol renames**, not a +structural rewrite. (The new `Links` does add a `Send + Sync` bound, which the +`Exclusive` wrapper already satisfies — `Send` is auto-derived and `Sync` is +manually `unsafe impl`'d.) + +### R3 / R4 / R5 — Documentation, research, requirement list + +- **[chosen]** Author this `README.md` in `docs/case-studies/issue-23/`, + mirroring the structure of the existing `docs/case-studies/issue-15/` study, + with an explicit requirements table (R5), online-research section (R4), and the + data compiled into the folder (R3). + +### R6 — Solution plans per requirement + +- **[chosen]** This "Proposed Solutions" section enumerates options and the + chosen approach per requirement. + +### R7 — Existing components/libraries + +See the dedicated survey below. + +### R8 — Single PR + +- **[chosen]** All work is committed to branch `issue-23-dff35da4182d` and + surfaced through the pre-existing PR #24; no new PR is opened. + +## Existing Components & Libraries Surveyed + +| Component | What it solves | Decision | +|-----------|----------------|----------| +| [`doublets` 0.3.0](https://crates.io/crates/doublets) | The links data structure under benchmark; now stable | **Adopted** (R1/R2) | +| [`allocator-api2`](https://crates.io/crates/allocator-api2) | Stable polyfill for nightly `allocator_api`; lets `doublets::mem::Global` work without nightly | Used transitively via `doublets`; no direct dependency needed | +| `platform-mem` 0.3.0 | Provides `Global` and `FileMapped` memory backends | Used via `doublets::mem` re-exports | +| `platform-num` 0.8.0 | Provides the `LinkReference` numeric trait (replacing `LinkType`) | Used via `doublets::data` re-export | +| `std::ops::ControlFlow` | Standard-library control-flow enum analogous to `Flow` | Not required — `Flow` exposes `is_break()`/`is_continue()`/`into_control_flow()`, sufficient for the migration | +| [`postgres`](https://crates.io/crates/postgres) 0.19 | Synchronous PostgreSQL client (the comparison baseline) | Unchanged | +| [`criterion`](https://crates.io/crates/criterion) 0.4 | Benchmark harness | Unchanged | + +## Evidence + +### 1. Dependency is the latest published release +``` +$ cargo search doublets +doublets = "0.3.0" # Doublets (links) data structure implementation. + +$ grep -A2 'name = "doublets"' rust/Cargo.lock +name = "doublets" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +``` + +### 2. Toolchain is stable, no nightly features remain +``` +$ cat rust-toolchain.toml +[toolchain] +channel = "stable" + +$ grep -rn '#!\[feature' rust/ # (no output — all feature gates removed) +``` + +### 3. Builds on stable +The exact command CI runs succeeds on stable (`rustc 1.95.0`): +``` +$ cargo build --release --all-features --manifest-path rust/Cargo.toml + Finished `release` profile [optimized] target(s) + +$ cargo bench --no-run --all-features + Finished `bench` profile [optimized] target(s) + Executable benches/bench.rs (target/release/deps/bench-...) +``` + +> Note: the benchmarks connect to a live PostgreSQL instance only at **runtime** +> (`localhost:5432`), which is provided by the CI `postgres` service. Locally, +> compilation (`cargo build` / `cargo bench --no-run`) is the verifiable step. + +## Verification Steps + +1. `rustup show` → active toolchain resolves to `stable` (via `rust-toolchain.toml`). +2. `cargo build --release --all-features --manifest-path rust/Cargo.toml` succeeds. +3. `cargo bench --no-run --all-features` compiles all benchmark binaries. +4. `grep -rn '#!\[feature' rust/` returns nothing (no nightly gates). +5. `grep 'doublets' rust/Cargo.toml` shows `doublets = "0.3.0"`. +6. CI (`.github/workflows/rust.yml`) runs the build and `cargo bench` against the + PostgreSQL service on the `stable` toolchain. + +## Lessons Learned + +1. **Pin to published, tagged releases, not git `HEAD`.** The original git + dependency on a pre-release (`0.1.0-pre+beta.15`) coupled this repo to an + unstable toolchain and made upgrades implicit and fragile. +2. **Nightly features migrate to stable over time.** GATs (1.65) and the + `allocator_api` (via `allocator-api2`) are no longer reasons to stay on + nightly; periodically re-checking lets downstreams drop the nightly pin. +3. **Track upstream release notes for BREAKING markers.** The `doublets` v0.3.0 + changelog precisely predicted the symbol renames (`LinkType` → `LinkReference`, + `Handler` removal) needed downstream. +4. **Prefer mechanical, reviewable migrations.** Because the trait handler + signatures were preserved upstream, the PostgreSQL backend changed only in + naming — keeping the diff small and auditable. + +## Related Resources + +- Upstream library: https://github.com/linksplatform/doublets-rs +- `doublets` on crates.io: https://crates.io/crates/doublets/0.3.0 +- `doublets` v0.3.0 release notes: https://github.com/linksplatform/doublets-rs/releases/tag/v0.3.0 +- `allocator-api2` (stable allocator shim): https://crates.io/crates/allocator-api2 +- Issue: https://github.com/linksplatform/Comparisons.PostgreSQLVSDoublets/issues/23 +- Pull Request: https://github.com/linksplatform/Comparisons.PostgreSQLVSDoublets/pull/24 +- Sibling case study (format reference): `docs/case-studies/issue-15/` diff --git a/rust-toolchain.toml b/rust-toolchain.toml index d7c5705..292fe49 100644 --- a/rust-toolchain.toml +++ b/rust-toolchain.toml @@ -1,2 +1,2 @@ [toolchain] -channel = "nightly-2022-08-22" +channel = "stable" diff --git a/rust/Cargo.lock b/rust/Cargo.lock index 16b3616..75e70b3 100644 --- a/rust/Cargo.lock +++ b/rust/Cargo.lock @@ -1,6 +1,6 @@ # This file is automatically @generated by Cargo. # It is not intended for manual editing. -version = 3 +version = 4 [[package]] name = "addr2line" @@ -26,6 +26,12 @@ dependencies = [ "memchr", ] +[[package]] +name = "allocator-api2" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c880a97d28a3681c0267bd29cff89621202715b065127cd445fa0f0fe0aa2880" + [[package]] name = "anes" version = "0.1.6" @@ -40,7 +46,7 @@ checksum = "a507401cad91ec6a857ed5513a2073c82a9b9048762b885bb98655b306964681" dependencies = [ "proc-macro2", "quote", - "syn 2.0.58", + "syn", ] [[package]] @@ -278,17 +284,6 @@ dependencies = [ "typenum", ] -[[package]] -name = "delegate" -version = "0.7.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d70a2d4995466955a415223acf3c9c934b9ff2339631cdf4ffc893da4bacd717" -dependencies = [ - "proc-macro2", - "quote", - "syn 1.0.109", -] - [[package]] name = "digest" version = "0.10.7" @@ -302,14 +297,15 @@ dependencies = [ [[package]] name = "doublets" -version = "0.1.0-pre+beta.15" -source = "git+https://github.com/linksplatform/doublets-rs.git#5522d91c536654934b7181829f6efb570fb8bb44" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5d5acc4914b466aabfcf42c60b4adbb6e3fc38fb241cd30f50eab36fcc7e9872" dependencies = [ - "bumpalo", "cfg-if", "leak_slice", "platform-data", "platform-mem", + "platform-num", "platform-trees", "tap", "thiserror", @@ -349,12 +345,6 @@ version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8fcfdc7a0362c9f4444381a9e697c79d435fe65b52a37466fc2c1184cee9edc6" -[[package]] -name = "funty" -version = "2.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e6d5a32815ae3f33302d95fdcb2ce17862f8c65363dcfd29360480ba1001fc9c" - [[package]] name = "futures-channel" version = "0.3.30" @@ -379,7 +369,7 @@ checksum = "87750cf4b7a4c0625b1529e4c543c2182106e4dedc60a2a6455e00d212c489ac" dependencies = [ "proc-macro2", "quote", - "syn 2.0.58", + "syn", ] [[package]] @@ -577,9 +567,9 @@ checksum = "6c8640c5d730cb13ebd907d8d04b52f55ac9a2eec55b440c8892f40d56c76c1d" [[package]] name = "memmap2" -version = "0.5.10" +version = "0.9.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "83faa42c0a078c393f6b29d5db232d8be22776a891f8f56e5284faee4a20b327" +checksum = "714098028fe011992e1c3962653c96b2d578c4b4bce9036e15ff220319b1e0e3" dependencies = [ "libc", ] @@ -606,9 +596,9 @@ dependencies = [ [[package]] name = "num-traits" -version = "0.2.18" +version = "0.2.19" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "da0df0e5185db44f69b44f26786fe401b6c293d1907744beaa7fa62b2e5a517a" +checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841" dependencies = [ "autocfg", ] @@ -711,33 +701,43 @@ checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" [[package]] name = "platform-data" -version = "0.1.0-beta.3" -source = "git+https://github.com/linksplatform/doublets-rs.git#5522d91c536654934b7181829f6efb570fb8bb44" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6782bc71345465116de96d250a36dcf49336010a2320d958d12a5d4390186c90" dependencies = [ "beef", - "funty", + "platform-num", "thiserror", ] [[package]] name = "platform-mem" -version = "0.1.0-pre+beta.2" -source = "git+https://github.com/linksplatform/doublets-rs.git#5522d91c536654934b7181829f6efb570fb8bb44" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f27cff7c92440ac926c8c91ea3151db6e52a262f602d0c157f254e422fc15b12" dependencies = [ - "delegate", + "allocator-api2", "memmap2", - "tap", "tempfile", "thiserror", ] +[[package]] +name = "platform-num" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2c4ca8e18138b1c90ad802aff931f946a0e6bd760c35af30f1ff2489489ab54a" +dependencies = [ + "num-traits", +] + [[package]] name = "platform-trees" -version = "0.1.0-beta.1" -source = "git+https://github.com/linksplatform/doublets-rs.git#5522d91c536654934b7181829f6efb570fb8bb44" +version = "0.3.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "40e25a531617fa762c8505826c930f6c1cfcc226f63dea09882b56ae0b8ed078" dependencies = [ - "funty", - "platform-data", + "platform-num", ] [[package]] @@ -819,9 +819,9 @@ checksum = "5b40af805b3121feab8a3c29f04d8ad262fa8e0561883e7653e024ae4479e6de" [[package]] name = "proc-macro2" -version = "1.0.79" +version = "1.0.106" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e835ff2298f5721608eb1a980ecaee1aef2c132bf95ecc026a11b7bf3c01c02e" +checksum = "8fd00f0bb2e90d81d1044c2b32617f68fcb9fa3bb7640c23e9c748e53fb30934" dependencies = [ "unicode-ident", ] @@ -980,7 +980,7 @@ checksum = "7eb0b34b42edc17f6b7cac84a52a1c5f0e1bb2227e997ca9011ea3dd34e8610b" dependencies = [ "proc-macro2", "quote", - "syn 2.0.58", + "syn", ] [[package]] @@ -1064,20 +1064,9 @@ checksum = "81cdd64d312baedb58e21336b31bc043b77e01cc99033ce76ef539f78e965ebc" [[package]] name = "syn" -version = "1.0.109" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237" -dependencies = [ - "proc-macro2", - "quote", - "unicode-ident", -] - -[[package]] -name = "syn" -version = "2.0.58" +version = "2.0.117" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "44cfb93f38070beee36b3fef7d4f5a16f27751d94b187b666a5cc5e9b0d30687" +checksum = "e665b8803e7b1d2a727f4023456bbbbe74da67099c585258af0ad9c5013b9b99" dependencies = [ "proc-macro2", "quote", @@ -1110,22 +1099,22 @@ checksum = "23d434d3f8967a09480fb04132ebe0a3e088c173e6d0ee7897abbdf4eab0f8b9" [[package]] name = "thiserror" -version = "1.0.58" +version = "2.0.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "03468839009160513471e86a034bb2c5c0e4baae3b43f79ffc55c4a5427b3297" +checksum = "4288b5bcbc7920c07a1149a35cf9590a2aa808e0bc1eafaade0b80947865fbc4" dependencies = [ "thiserror-impl", ] [[package]] name = "thiserror-impl" -version = "1.0.58" +version = "2.0.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c61f3ba182994efc43764a46c018c347bc492c79f024e705f46567b418f6d4f7" +checksum = "ebc4ee7f67670e9b64d05fa4253e753e016c6c95ff35b89b7941d6b856dec1d5" dependencies = [ "proc-macro2", "quote", - "syn 2.0.58", + "syn", ] [[package]] @@ -1180,7 +1169,7 @@ checksum = "5b8a1e28f2deaa14e508979454cb3a223b10b938b45af148bc0986de36f1923b" dependencies = [ "proc-macro2", "quote", - "syn 2.0.58", + "syn", ] [[package]] @@ -1318,7 +1307,7 @@ dependencies = [ "once_cell", "proc-macro2", "quote", - "syn 2.0.58", + "syn", "wasm-bindgen-shared", ] @@ -1340,7 +1329,7 @@ checksum = "e94f17b526d0a461a191c78ea52bbce64071ed5c04c9ffe424dcb38f74171bb7" dependencies = [ "proc-macro2", "quote", - "syn 2.0.58", + "syn", "wasm-bindgen-backend", "wasm-bindgen-shared", ] diff --git a/rust/Cargo.toml b/rust/Cargo.toml index 29f5d6a..1f3fb7f 100644 --- a/rust/Cargo.toml +++ b/rust/Cargo.toml @@ -10,7 +10,7 @@ harness = false [dependencies] postgres = { version = "0.19.5" } tokio = { version = "1.29.1", features = ["full"] } -doublets = { git = "https://github.com/linksplatform/doublets-rs.git" } +doublets = "0.3.0" [dev-dependencies] criterion = "=0.4.0" \ No newline at end of file diff --git a/rust/benches/bench.rs b/rust/benches/bench.rs index 9240b6a..29e6f96 100644 --- a/rust/benches/bench.rs +++ b/rust/benches/bench.rs @@ -1,5 +1,3 @@ -#![feature(allocator_api)] - use { benchmarks::{ doublets_create_links, doublets_delete_links, doublets_each_all, doublets_each_concrete, diff --git a/rust/benches/benchmarks/doublets/create.rs b/rust/benches/benchmarks/doublets/create.rs index 49296d9..c3535b3 100644 --- a/rust/benches/benchmarks/doublets/create.rs +++ b/rust/benches/benchmarks/doublets/create.rs @@ -10,17 +10,14 @@ //! - Updating source and target indexes //! - Time complexity: O(log n) for index updates -use std::{ - alloc::Global, - time::{Duration, Instant}, -}; +use std::time::{Duration, Instant}; use criterion::{measurement::WallTime, BenchmarkGroup, Criterion}; use doublets::{ - mem::{Alloc, FileMapped}, - parts::LinkPart, + mem::{FileMapped, Global}, split::{self, DataPart, IndexPart}, - unit, Doublets, + unit::{self, LinkPart}, + Doublets, }; use linkspsql::{bench, benchmark_links, Benched, Fork}; @@ -50,7 +47,7 @@ pub fn create_links(c: &mut Criterion) { bench( &mut group, "Doublets_United_Volatile", - unit::Store::, Global>>::setup(()).unwrap() + unit::Store::>>::setup(()).unwrap() ) } tri! { @@ -64,7 +61,7 @@ pub fn create_links(c: &mut Criterion) { bench( &mut group, "Doublets_Split_Volatile", - split::Store::, _>, Alloc, _>>::setup(()).unwrap() + split::Store::>, Global>>::setup(()).unwrap() ) } tri! { diff --git a/rust/benches/benchmarks/doublets/delete.rs b/rust/benches/benchmarks/doublets/delete.rs index 8ae270d..3d38fea 100644 --- a/rust/benches/benchmarks/doublets/delete.rs +++ b/rust/benches/benchmarks/doublets/delete.rs @@ -9,17 +9,14 @@ //! - Marking the slot as free for reuse //! - Time complexity: O(log n) for index updates -use std::{ - alloc::Global, - time::{Duration, Instant}, -}; +use std::time::{Duration, Instant}; use criterion::{measurement::WallTime, BenchmarkGroup, Criterion}; use doublets::{ - mem::{Alloc, FileMapped}, - parts::LinkPart, + mem::{FileMapped, Global}, split::{self, DataPart, IndexPart}, - unit, Doublets, + unit::{self, LinkPart}, + Doublets, }; use linkspsql::{background_links, bench, benchmark_links, Benched, Fork}; @@ -53,7 +50,7 @@ pub fn delete_links(c: &mut Criterion) { bench( &mut group, "Doublets_United_Volatile", - unit::Store::, Global>>::setup(()).unwrap() + unit::Store::>>::setup(()).unwrap() ) } tri! { @@ -67,7 +64,7 @@ pub fn delete_links(c: &mut Criterion) { bench( &mut group, "Doublets_Split_Volatile", - split::Store::, _>, Alloc, _>>::setup(()).unwrap() + split::Store::>, Global>>::setup(()).unwrap() ) } tri! { diff --git a/rust/benches/benchmarks/doublets/each/all.rs b/rust/benches/benchmarks/doublets/each/all.rs index 1dbc9e9..dce095d 100644 --- a/rust/benches/benchmarks/doublets/each/all.rs +++ b/rust/benches/benchmarks/doublets/each/all.rs @@ -8,18 +8,15 @@ //! - Sequential iteration through the internal array //! - Time complexity: O(n) where n is the number of links -use std::{ - alloc::Global, - time::{Duration, Instant}, -}; +use std::time::{Duration, Instant}; use criterion::{measurement::WallTime, BenchmarkGroup, Criterion}; use doublets::{ data::Flow, - mem::{Alloc, FileMapped}, - parts::LinkPart, + mem::{FileMapped, Global}, split::{self, DataPart, IndexPart}, - unit, Doublets, + unit::{self, LinkPart}, + Doublets, }; use linkspsql::{bench, Benched, Fork}; @@ -47,7 +44,7 @@ pub fn each_all(c: &mut Criterion) { bench( &mut group, "Doublets_United_Volatile", - unit::Store::, Global>>::setup(()).unwrap() + unit::Store::>>::setup(()).unwrap() ) } tri! { @@ -61,7 +58,7 @@ pub fn each_all(c: &mut Criterion) { bench( &mut group, "Doublets_Split_Volatile", - split::Store::, _>, Alloc, _>>::setup(()).unwrap() + split::Store::>, Global>>::setup(()).unwrap() ) } tri! { diff --git a/rust/benches/benchmarks/doublets/each/concrete.rs b/rust/benches/benchmarks/doublets/each/concrete.rs index 42aa6a1..ea4be54 100644 --- a/rust/benches/benchmarks/doublets/each/concrete.rs +++ b/rust/benches/benchmarks/doublets/each/concrete.rs @@ -8,18 +8,15 @@ //! - Index tree lookup followed by filter //! - Time complexity: O(log n) for index lookup -use std::{ - alloc::Global, - time::{Duration, Instant}, -}; +use std::time::{Duration, Instant}; use criterion::{measurement::WallTime, BenchmarkGroup, Criterion}; use doublets::{ data::{Flow, LinksConstants}, - mem::{Alloc, FileMapped}, - parts::LinkPart, + mem::{FileMapped, Global}, split::{self, DataPart, IndexPart}, - unit, Doublets, + unit::{self, LinkPart}, + Doublets, }; use linkspsql::{background_links, bench, Benched, Fork}; @@ -51,7 +48,7 @@ pub fn each_concrete(c: &mut Criterion) { bench( &mut group, "Doublets_United_Volatile", - unit::Store::, Global>>::setup(()).unwrap() + unit::Store::>>::setup(()).unwrap() ) } tri! { @@ -65,7 +62,7 @@ pub fn each_concrete(c: &mut Criterion) { bench( &mut group, "Doublets_Split_Volatile", - split::Store::, _>, Alloc, _>>::setup(()).unwrap() + split::Store::>, Global>>::setup(()).unwrap() ) } tri! { diff --git a/rust/benches/benchmarks/doublets/each/identity.rs b/rust/benches/benchmarks/doublets/each/identity.rs index a68eee7..f7a732e 100644 --- a/rust/benches/benchmarks/doublets/each/identity.rs +++ b/rust/benches/benchmarks/doublets/each/identity.rs @@ -8,18 +8,15 @@ //! - Direct array access: `links[id]` //! - Time complexity: O(1) -use std::{ - alloc::Global, - time::{Duration, Instant}, -}; +use std::time::{Duration, Instant}; use criterion::{measurement::WallTime, BenchmarkGroup, Criterion}; use doublets::{ data::{Flow, LinksConstants}, - mem::{Alloc, FileMapped}, - parts::LinkPart, + mem::{FileMapped, Global}, split::{self, DataPart, IndexPart}, - unit, Doublets, + unit::{self, LinkPart}, + Doublets, }; use linkspsql::{background_links, bench, Benched, Fork}; @@ -51,7 +48,7 @@ pub fn each_identity(c: &mut Criterion) { bench( &mut group, "Doublets_United_Volatile", - unit::Store::, Global>>::setup(()).unwrap() + unit::Store::>>::setup(()).unwrap() ) } tri! { @@ -65,7 +62,7 @@ pub fn each_identity(c: &mut Criterion) { bench( &mut group, "Doublets_Split_Volatile", - split::Store::, _>, Alloc, _>>::setup(()).unwrap() + split::Store::>, Global>>::setup(()).unwrap() ) } tri! { diff --git a/rust/benches/benchmarks/doublets/each/incoming.rs b/rust/benches/benchmarks/doublets/each/incoming.rs index 81ade62..2d8ce36 100644 --- a/rust/benches/benchmarks/doublets/each/incoming.rs +++ b/rust/benches/benchmarks/doublets/each/incoming.rs @@ -8,18 +8,15 @@ //! - Target index tree traversal //! - Time complexity: O(log n + k) where k is the number of matching links -use std::{ - alloc::Global, - time::{Duration, Instant}, -}; +use std::time::{Duration, Instant}; use criterion::{measurement::WallTime, BenchmarkGroup, Criterion}; use doublets::{ data::{Flow, LinksConstants}, - mem::{Alloc, FileMapped}, - parts::LinkPart, + mem::{FileMapped, Global}, split::{self, DataPart, IndexPart}, - unit, Doublets, + unit::{self, LinkPart}, + Doublets, }; use linkspsql::{background_links, bench, Benched, Fork}; @@ -51,7 +48,7 @@ pub fn each_incoming(c: &mut Criterion) { bench( &mut group, "Doublets_United_Volatile", - unit::Store::, Global>>::setup(()).unwrap() + unit::Store::>>::setup(()).unwrap() ) } tri! { @@ -65,7 +62,7 @@ pub fn each_incoming(c: &mut Criterion) { bench( &mut group, "Doublets_Split_Volatile", - split::Store::, _>, Alloc, _>>::setup(()).unwrap() + split::Store::>, Global>>::setup(()).unwrap() ) } tri! { diff --git a/rust/benches/benchmarks/doublets/each/outgoing.rs b/rust/benches/benchmarks/doublets/each/outgoing.rs index 93cbc9b..7e3e235 100644 --- a/rust/benches/benchmarks/doublets/each/outgoing.rs +++ b/rust/benches/benchmarks/doublets/each/outgoing.rs @@ -8,18 +8,15 @@ //! - Source index tree traversal //! - Time complexity: O(log n + k) where k is the number of matching links -use std::{ - alloc::Global, - time::{Duration, Instant}, -}; +use std::time::{Duration, Instant}; use criterion::{measurement::WallTime, BenchmarkGroup, Criterion}; use doublets::{ data::{Flow, LinksConstants}, - mem::{Alloc, FileMapped}, - parts::LinkPart, + mem::{FileMapped, Global}, split::{self, DataPart, IndexPart}, - unit, Doublets, + unit::{self, LinkPart}, + Doublets, }; use linkspsql::{background_links, bench, Benched, Fork}; @@ -51,7 +48,7 @@ pub fn each_outgoing(c: &mut Criterion) { bench( &mut group, "Doublets_United_Volatile", - unit::Store::, Global>>::setup(()).unwrap() + unit::Store::>>::setup(()).unwrap() ) } tri! { @@ -65,7 +62,7 @@ pub fn each_outgoing(c: &mut Criterion) { bench( &mut group, "Doublets_Split_Volatile", - split::Store::, _>, Alloc, _>>::setup(()).unwrap() + split::Store::>, Global>>::setup(()).unwrap() ) } tri! { diff --git a/rust/benches/benchmarks/doublets/update.rs b/rust/benches/benchmarks/doublets/update.rs index 36b5b5f..deaa137 100644 --- a/rust/benches/benchmarks/doublets/update.rs +++ b/rust/benches/benchmarks/doublets/update.rs @@ -9,14 +9,11 @@ //! - Re-indexing if source or target changed //! - Time complexity: O(log n) for index updates -use std::{ - alloc::Global, - time::{Duration, Instant}, -}; +use std::time::{Duration, Instant}; use criterion::{measurement::WallTime, BenchmarkGroup, Criterion}; use doublets::{ - mem::{Alloc, FileMapped}, + mem::{FileMapped, Global}, split::{self, DataPart, IndexPart}, unit::{self, LinkPart}, Doublets, @@ -51,7 +48,7 @@ pub fn update_links(c: &mut Criterion) { bench( &mut group, "Doublets_United_Volatile", - unit::Store::, Global>>::setup(()).unwrap() + unit::Store::>>::setup(()).unwrap() ) } tri! { @@ -65,7 +62,7 @@ pub fn update_links(c: &mut Criterion) { bench( &mut group, "Doublets_Split_Volatile", - split::Store::, _>, Alloc, _>>::setup(()).unwrap() + split::Store::>, Global>>::setup(()).unwrap() ) } tri! { diff --git a/rust/src/benched/doublets_benched.rs b/rust/src/benched/doublets_benched.rs index 63245d3..5e586a8 100644 --- a/rust/src/benched/doublets_benched.rs +++ b/rust/src/benched/doublets_benched.rs @@ -8,9 +8,9 @@ //! | Backend | Description | //! |----------------------------------|--------------------------------| //! | `unit::Store>` | File-mapped unit storage | -//! | `unit::Store>` | In-memory unit storage | +//! | `unit::Store>` | In-memory unit storage | //! | `split::Store>` | File-mapped split storage | -//! | `split::Store>` | In-memory split storage | +//! | `split::Store>` | In-memory split storage | //! //! ## Cleanup Strategy //! @@ -19,18 +19,17 @@ use crate::{map_file, Result}; use doublets::{ - data::LinkType, - mem::{Alloc, FileMapped}, + data::LinkReference, + mem::{FileMapped, Global}, split::{self, DataPart, IndexPart}, unit::{self, LinkPart}, Doublets, }; -use std::alloc::Global; use super::Benched; /// Benched implementation for file-mapped unit storage. -impl Benched for unit::Store>> { +impl Benched for unit::Store>> { type Builder<'a> = &'a str; fn setup(builder: Self::Builder<'_>) -> Result { @@ -43,11 +42,11 @@ impl Benched for unit::Store>> { } /// Benched implementation for in-memory unit storage. -impl Benched for unit::Store, Global>> { +impl Benched for unit::Store>> { type Builder<'a> = (); fn setup(_: Self::Builder<'_>) -> Result { - Self::new(Alloc::new(Global)).map_err(Into::into) + Self::new(Global::new()).map_err(Into::into) } unsafe fn unfork(&mut self) { @@ -56,7 +55,7 @@ impl Benched for unit::Store, Global>> { } /// Benched implementation for file-mapped split storage. -impl Benched for split::Store>, FileMapped>> { +impl Benched for split::Store>, FileMapped>> { type Builder<'a> = (&'a str, &'a str); fn setup((data, index): Self::Builder<'_>) -> Result { @@ -69,13 +68,13 @@ impl Benched for split::Store>, FileMappe } /// Benched implementation for in-memory split storage. -impl Benched - for split::Store, Global>, Alloc, Global>> +impl Benched + for split::Store>, Global>> { type Builder<'a> = (); fn setup(_: Self::Builder<'_>) -> Result { - Self::new(Alloc::new(Global), Alloc::new(Global)).map_err(Into::into) + Self::new(Global::new(), Global::new()).map_err(Into::into) } unsafe fn unfork(&mut self) { diff --git a/rust/src/benched/mod.rs b/rust/src/benched/mod.rs index 02db6e7..9d66abc 100644 --- a/rust/src/benched/mod.rs +++ b/rust/src/benched/mod.rs @@ -32,7 +32,7 @@ pub trait Benched: Sized { /// Create a fork for a single benchmark iteration. /// /// This allows each iteration to run in isolation without affecting others. - fn fork(&mut self) -> Fork { + fn fork(&mut self) -> Fork<'_, Self> { Fork(self) } diff --git a/rust/src/benched/psql_benched.rs b/rust/src/benched/psql_benched.rs index 098e962..cabbf1c 100644 --- a/rust/src/benched/psql_benched.rs +++ b/rust/src/benched/psql_benched.rs @@ -28,12 +28,12 @@ //! ``` use crate::{Client, Exclusive, Fork, Result, Sql, Transaction}; -use doublets::data::LinkType; +use doublets::data::LinkReference; use super::Benched; /// Benched implementation for PostgreSQL client (non-transactional). -impl Benched for Exclusive> { +impl Benched for Exclusive> { type Builder<'a> = (); fn setup(_: Self::Builder<'_>) -> Result { @@ -41,7 +41,7 @@ impl Benched for Exclusive> { unsafe { Ok(Exclusive::new(crate::connect()?)) } } - fn fork(&mut self) -> Fork { + fn fork(&mut self) -> Fork<'_, Self> { let _ = self.create_table(); Fork(self) } @@ -52,7 +52,7 @@ impl Benched for Exclusive> { } /// Benched implementation for PostgreSQL transaction. -impl<'a, T: LinkType> Benched for Exclusive> { +impl<'a, T: LinkReference> Benched for Exclusive> { type Builder<'b> = &'a mut Client; fn setup(builder: Self::Builder<'_>) -> Result { @@ -62,7 +62,7 @@ impl<'a, T: LinkType> Benched for Exclusive> { unsafe { Ok(Exclusive::new(transaction)) } } - fn fork(&mut self) -> Fork { + fn fork(&mut self) -> Fork<'_, Self> { let _ = self.create_table(); Fork(self) } diff --git a/rust/src/client.rs b/rust/src/client.rs index e2c960d..c29827e 100644 --- a/rust/src/client.rs +++ b/rust/src/client.rs @@ -1,17 +1,17 @@ use { - crate::{transaction::Transaction, Exclusive, Result, Sql}, + crate::{as_i64, transaction::Transaction, Exclusive, Result, Sql}, doublets::{ - data::{Error, Flow, LinkType, LinksConstants, ReadHandler, WriteHandler}, + data::{Error, Flow, LinkReference, LinksConstants, ReadHandler, WriteHandler}, Doublets, Link, Links, }, }; -pub struct Client { +pub struct Client { client: postgres::Client, constants: LinksConstants, } -impl Client { +impl Client { pub fn new(mut client: postgres::Client) -> Result { client.query( "CREATE TABLE IF NOT EXISTS Links (id bigint GENERATED ALWAYS AS IDENTITY PRIMARY KEY, from_id bigint, to_id bigint);", @@ -27,7 +27,7 @@ impl Client { } } -impl Sql for Client { +impl Sql for Client { fn create_table(&mut self) -> Result<()> { self.client.query( "CREATE TABLE IF NOT EXISTS Links (id bigint GENERATED ALWAYS AS IDENTITY PRIMARY KEY, from_id bigint, to_id bigint);", @@ -45,7 +45,7 @@ impl Sql for Client { } } -impl<'c, T: LinkType> Links for Exclusive> { +impl<'c, T: LinkReference> Links for Exclusive> { fn constants(&self) -> &LinksConstants { &self.constants } @@ -63,7 +63,7 @@ impl<'c, T: LinkType> Links for Exclusive> { let result = self .get() .client - .query("SELECT COUNT(*) FROM Links WHERE id = $1;", &[&query[0].as_i64()]) + .query("SELECT COUNT(*) FROM Links WHERE id = $1;", &[&as_i64(query[0])]) .unwrap(); result[0].get::<_, i64>(0).try_into().unwrap() } @@ -92,7 +92,7 @@ impl<'c, T: LinkType> Links for Exclusive> { .unwrap(); Ok(handler( Link::nothing(), - Link::new(result[0].get::<_, i64>(0).try_into().unwrap(), T::ZERO, T::ZERO), + Link::new(result[0].get::<_, i64>(0).try_into().unwrap(), T::from_byte(0), T::from_byte(0)), )) } @@ -101,11 +101,13 @@ impl<'c, T: LinkType> Links for Exclusive> { if query.is_empty() { let result = self.get().client.query("SELECT * FROM Links;", &[]).unwrap(); for row in result { - handler(Link::new( + if handler(Link::new( row.get::<_, i64>(0).try_into().unwrap(), row.get::<_, i64>(1).try_into().unwrap(), row.get::<_, i64>(2).try_into().unwrap(), - ))? + )).is_break() { + return Flow::Break; + } } Flow::Continue } else if query.len() == 1 { @@ -115,14 +117,16 @@ impl<'c, T: LinkType> Links for Exclusive> { let result = self .get() .client - .query("SELECT * FROM Links WHERE id = %1;", &[&query[0].as_i64()]) + .query("SELECT * FROM Links WHERE id = %1;", &[&as_i64(query[0])]) .unwrap(); for row in result { - handler(Link::new( + if handler(Link::new( row.get::<_, i64>(0).try_into().unwrap(), row.get::<_, i64>(1).try_into().unwrap(), row.get::<_, i64>(2).try_into().unwrap(), - ))? + )).is_break() { + return Flow::Break; + } } Flow::Continue } @@ -142,11 +146,13 @@ impl<'c, T: LinkType> Links for Exclusive> { let statement = &format!("SELECT * FROM Links WHERE {id}{source}{target}"); let result = self.get().client.query(statement, &[]).unwrap(); for row in result { - handler(Link::new( + if handler(Link::new( row.get::<_, i64>(0).try_into().unwrap(), row.get::<_, i64>(1).try_into().unwrap(), row.get::<_, i64>(2).try_into().unwrap(), - ))? + )).is_break() { + return Flow::Break; + } } Flow::Continue } else { @@ -164,14 +170,14 @@ impl<'c, T: LinkType> Links for Exclusive> { let source = change[1]; let target = change[2]; let old_links = - self.client.query("SELECT * FROM Links WHERE id = $1;", &[&id.as_i64()]).unwrap(); + self.client.query("SELECT * FROM Links WHERE id = $1;", &[&as_i64(id)]).unwrap(); let (old_source, old_target) = ( old_links[0].get::<_, i64>(1).try_into().unwrap(), old_links[0].get::<_, i64>(2).try_into().unwrap(), ); let _ = self.client.query( "UPDATE Links SET from_id = $1, to_id = $2 WHERE id = $3;", - &[&source.as_i64(), &target.as_i64(), &id.as_i64()], + &[&as_i64(source), &as_i64(target), &as_i64(id)], ); Ok(handler(Link::new(id, old_source, old_target), Link::new(id, source, target))) } @@ -180,7 +186,7 @@ impl<'c, T: LinkType> Links for Exclusive> { let id = query[0]; let result = self .client - .query("DELETE FROM Links WHERE id = $1 RETURNING from_id, to_id", &[&id.as_i64()]) + .query("DELETE FROM Links WHERE id = $1 RETURNING from_id, to_id", &[&as_i64(id)]) .unwrap(); let row = if result.is_empty() { return Err(Error::::NotExists(id)); @@ -198,10 +204,10 @@ impl<'c, T: LinkType> Links for Exclusive> { } } -impl<'c, T: LinkType> Doublets for Exclusive> { +impl<'c, T: LinkReference> Doublets for Exclusive> { fn get_link(&self, index: T) -> Option> { let result = - self.get().client.query("SELECT * FROM Links WHERE id = $1", &[&index.as_i64()]); + self.get().client.query("SELECT * FROM Links WHERE id = $1", &[&as_i64(index)]); match result { Ok(rows) => { let row = &rows[0]; diff --git a/rust/src/lib.rs b/rust/src/lib.rs index 498a527..c65dd42 100644 --- a/rust/src/lib.rs +++ b/rust/src/lib.rs @@ -1,5 +1,3 @@ -#![feature(allocator_api, generic_associated_types)] - #[macro_export] macro_rules! bench { {|$fork:ident| as $B:ident { $($body:tt)* }} => { @@ -35,7 +33,7 @@ pub use { }; use { - doublets::{data::LinkType, mem::FileMapped}, + doublets::{data::LinkReference, mem::FileMapped}, postgres::NoTls, std::{env, error, fs::File, io, result}, }; @@ -69,10 +67,19 @@ pub fn benchmark_links() -> usize { } const PARAMS: &str = "user=postgres dbname=postgres password=postgres host=localhost port=5432"; -pub fn connect() -> Result> { +pub fn connect() -> Result> { Client::new(postgres::Client::connect(PARAMS, NoTls)?).map_err(Into::into) } +/// Converts a link value into the `i64` used by PostgreSQL `bigint` columns. +/// +/// The previous `doublets` API exposed a `LinkType::as_i64` helper. The current +/// `LinkReference` trait instead provides the standard `TryInto` conversion, +/// which this function wraps for convenience. +pub fn as_i64(value: T) -> i64 { + value.try_into().expect("link value does not fit into i64") +} + pub fn map_file(filename: &str) -> io::Result> { let file = File::options().create(true).write(true).read(true).open(filename)?; FileMapped::new(file) diff --git a/rust/src/transaction.rs b/rust/src/transaction.rs index a75272d..e2e1764 100644 --- a/rust/src/transaction.rs +++ b/rust/src/transaction.rs @@ -1,17 +1,17 @@ use { - crate::{Exclusive, Result, Sql}, + crate::{as_i64, Exclusive, Result, Sql}, doublets::{ - data::{Flow, LinkType, LinksConstants, ReadHandler, WriteHandler}, + data::{Flow, LinkReference, LinksConstants, ReadHandler, WriteHandler}, Doublets, Error, Link, Links, }, }; -pub struct Transaction<'a, T: LinkType> { +pub struct Transaction<'a, T: LinkReference> { transaction: postgres::Transaction<'a>, constants: LinksConstants, } -impl<'a, T: LinkType> Transaction<'a, T> { +impl<'a, T: LinkReference> Transaction<'a, T> { pub fn new(transaction: postgres::Transaction<'a>) -> Self { Self { transaction, constants: LinksConstants::::new() } } @@ -23,7 +23,7 @@ impl<'a, T: LinkType> Transaction<'a, T> { } } -impl<'a, T: LinkType> Sql for Transaction<'a, T> { +impl<'a, T: LinkReference> Sql for Transaction<'a, T> { fn create_table(&mut self) -> Result<()> { self.transaction.query( "CREATE TABLE IF NOT EXISTS Links (id bigint GENERATED ALWAYS AS IDENTITY PRIMARY KEY, from_id bigint, to_id bigint);", @@ -42,7 +42,7 @@ impl<'a, T: LinkType> Sql for Transaction<'a, T> { } } -impl Links for Exclusive> { +impl Links for Exclusive> { fn constants(&self) -> &LinksConstants { &self.constants } @@ -60,7 +60,7 @@ impl Links for Exclusive> { let result = self .get() .transaction - .query("SELECT COUNT(*) FROM Links WHERE id = $1;", &[&query[0].as_i64()]) + .query("SELECT COUNT(*) FROM Links WHERE id = $1;", &[&as_i64(query[0])]) .unwrap(); result[0].get::<_, i64>(0).try_into().unwrap() } @@ -92,7 +92,7 @@ impl Links for Exclusive> { .unwrap(); Ok(handler( Link::nothing(), - Link::new(result[0].get::<_, i64>(0).try_into().unwrap(), T::ZERO, T::ZERO), + Link::new(result[0].get::<_, i64>(0).try_into().unwrap(), T::from_byte(0), T::from_byte(0)), )) } @@ -101,11 +101,13 @@ impl Links for Exclusive> { if query.is_empty() { let result = self.get().transaction.query("SELECT * FROM Links;", &[]).unwrap(); for row in result { - handler(Link::new( + if handler(Link::new( row.get::<_, i64>(0).try_into().unwrap(), row.get::<_, i64>(1).try_into().unwrap(), row.get::<_, i64>(2).try_into().unwrap(), - ))? + )).is_break() { + return Flow::Break; + } } Flow::Continue } else if query.len() == 1 { @@ -115,14 +117,16 @@ impl Links for Exclusive> { let result = self .get() .transaction - .query("SELECT * FROM Links WHERE id = $1;", &[&query[0].as_i64()]) + .query("SELECT * FROM Links WHERE id = $1;", &[&as_i64(query[0])]) .unwrap(); for row in result { - handler(Link::new( + if handler(Link::new( row.get::<_, i64>(0).try_into().unwrap(), row.get::<_, i64>(1).try_into().unwrap(), row.get::<_, i64>(2).try_into().unwrap(), - ))? + )).is_break() { + return Flow::Break; + } } Flow::Continue } @@ -142,11 +146,13 @@ impl Links for Exclusive> { let statement = &format!("SELECT * FROM Links WHERE {id}{source}{target}"); let result = self.get().transaction.query(statement, &[]).unwrap(); for row in result { - handler(Link::new( + if handler(Link::new( row.get::<_, i64>(0).try_into().unwrap(), row.get::<_, i64>(1).try_into().unwrap(), row.get::<_, i64>(2).try_into().unwrap(), - ))? + )).is_break() { + return Flow::Break; + } } Flow::Continue } else { @@ -164,7 +170,7 @@ impl Links for Exclusive> { let source = change[1]; let target = change[2]; let old_links = - self.transaction.query("SELECT * FROM Links WHERE id = $1;", &[&id.as_i64()]).unwrap(); + self.transaction.query("SELECT * FROM Links WHERE id = $1;", &[&as_i64(id)]).unwrap(); let (old_source, old_target) = ( old_links[0].get::<_, i64>(1).try_into().unwrap(), old_links[0].get::<_, i64>(2).try_into().unwrap(), @@ -172,7 +178,7 @@ impl Links for Exclusive> { self.transaction .query( "UPDATE Links SET from_id = $1, to_id = $2 WHERE id = $3;", - &[&source.as_i64(), &target.as_i64(), &id.as_i64()], + &[&as_i64(source), &as_i64(target), &as_i64(id)], ) .unwrap(); Ok(handler(Link::new(id, old_source, old_target), Link::new(id, source, target))) @@ -182,7 +188,7 @@ impl Links for Exclusive> { let id = query[0]; let result = self .transaction - .query("DELETE FROM Links WHERE id = $1 RETURNING from_id, to_id", &[&id.as_i64()]) + .query("DELETE FROM Links WHERE id = $1 RETURNING from_id, to_id", &[&as_i64(id)]) .unwrap(); let row = if result.is_empty() { return Err(Error::::NotExists(id)); @@ -200,10 +206,10 @@ impl Links for Exclusive> { } } -impl Doublets for Exclusive> { +impl Doublets for Exclusive> { fn get_link(&self, index: T) -> Option> { let result = - self.get().transaction.query("SELECT * FROM Links WHERE id = $1", &[&index.as_i64()]); + self.get().transaction.query("SELECT * FROM Links WHERE id = $1", &[&as_i64(index)]); match result { Ok(rows) => { let ref row = rows[0];