diff --git a/Cargo.lock b/Cargo.lock
index 7382d160..759df72a 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -1089,7 +1089,7 @@ dependencies = [
[[package]]
name = "kite_sql"
-version = "0.4.0"
+version = "0.4.1"
dependencies = [
"bumpalo",
"chrono",
diff --git a/Cargo.toml b/Cargo.toml
index dc7d94e6..61fb17c9 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -2,7 +2,7 @@
[package]
name = "kite_sql"
-version = "0.4.0"
+version = "0.4.1"
edition = "2021"
build = "build.rs"
authors = ["Kould ", "Xwg "]
diff --git a/README.md b/README.md
index 845033d3..12f731dc 100755
--- a/README.md
+++ b/README.md
@@ -29,31 +29,16 @@
## Introduction
-**KiteSQL** is a lightweight embedded relational database for Rust, inspired by **MyRocks** and **SQLite** and fully written in Rust. It is designed to work not only as a SQL engine, but also as a Rust-native data API that can be embedded directly into applications without relying on external services or heavyweight infrastructure.
+**KiteSQL** is a lightweight embedded relational database written in Rust, inspired by **MyRocks** and **SQLite**. It runs inside your application with no external service: execute SQL directly, or use typed ORM models, migrations, and builder-style queries.
-KiteSQL supports direct SQL execution, typed ORM models, schema migration, and builder-style queries, so you can combine relational power with an API surface that feels natural in Rust. On native targets, KiteSQL ships with both RocksDB-backed and LMDB-backed persistent storage builders, plus an in-memory builder for tests and temporary workloads.
+- Most of the SQL 2016 syntax
+- All metadata and data stored in KV storage (RocksDB, LMDB, or in-memory)
+- Typed ORM with schema migration (`orm` feature), see [`src/orm/README.md`](src/orm/README.md)
+- WebAssembly and Python bindings
-## Key Features
-- A lightweight embedded SQL database fully rewritten in Rust
-- A Rust-native relational API alongside direct SQL execution
-- Typed ORM models with migrations and a lightweight typed query/mutation builder
-- Higher write speed with an application-friendly embedding model
-- All metadata and actual data in KV storage, with no intermediate stateful service layer
-- Extensible storage integration for customized workloads
-- Supports most of the SQL 2016 syntax
-- Ships a WebAssembly build for JavaScript runtimes
+👉 [More features](docs/features.md)
-#### 👉[check more](docs/features.md)
-
-## ORM
-KiteSQL includes a built-in ORM behind the `orm` feature flag. With `#[derive(Model)]`, you can define typed models and get tuple mapping, schema creation, migration support, projections, set queries, and builder-style query/mutation workflows.
-
-### Schema Migration
-Model changes are part of the normal workflow. KiteSQL ORM can help evolve tables for common schema updates, including adding, dropping, renaming, and changing columns, so many migrations can stay close to the Rust model definition instead of being managed as hand-written SQL.
-
-For the full ORM guide, see [`src/orm/README.md`](src/orm/README.md).
-
-## Examples
+## Example
```rust
use kite_sql::db::DataBaseBuilder;
@@ -63,13 +48,10 @@ use kite_sql::Model;
#[derive(Default, Debug, PartialEq, Model)]
#[model(table = "users")]
-#[model(index(name = "users_name_age_idx", columns = "name, age"))]
struct User {
#[model(primary_key)]
id: i32,
- #[model(unique, varchar = 128)]
- email: String,
- #[model(rename = "user_name", varchar = 64)]
+ #[model(varchar = 64)]
name: String,
#[model(default = "18", index)]
age: Option,
@@ -77,142 +59,88 @@ struct User {
fn main() -> Result<(), DatabaseError> {
let mut database = DataBaseBuilder::path("./data").build_rocksdb()?;
- // Or: let database = DataBaseBuilder::path("./data").build_lmdb()?;
-
database.migrate::()?;
database.insert_many([
- User {
- id: 1,
- email: "alice@example.com".to_string(),
- name: "Alice".to_string(),
- age: Some(18),
- },
- User {
- id: 2,
- email: "bob@example.com".to_string(),
- name: "Bob".to_string(),
- age: Some(24),
- },
+ User { id: 1, name: "Alice".to_string(), age: Some(18) },
+ User { id: 2, name: "Bob".to_string(), age: Some(24) },
])?;
- database
- .bind(|ctx| {
- ctx.mutate::()?
- .filter(|e| e.column(User::id())?.eq(1))?
- .update(|u| u.set_value(User::age(), Some(19)))
- })?
- .done()?;
-
- database
- .bind(|ctx| {
- ctx.mutate::()?
- .filter(|e| e.column(User::id())?.eq(2))?
- .delete()
- })?
- .done()?;
-
let users = database
.bind(|ctx| {
ctx.from::()?
.filter(|e| e.column(User::age())?.gte(18))?
.project_scalars((User::id(), User::name()))?
.order_by(User::name())?
- .limit(10)?
.finish()
})?
.project_tuple::<(i32, String)>();
-
for user in users {
println!("{:?}", user?);
}
- // For ad-hoc or more SQL-shaped workloads, `run(...)` is still available.
-
+ // Plain SQL works too.
+ database.run("select count(*) from users")?.done()?;
Ok(())
}
```
+More: [hello_world](examples/hello_world.rs), [transaction](examples/transaction.rs).
+
## Storage Backends
-- `build_rocksdb()` opens a persistent RocksDB-backed database.
-- `build_lmdb()` opens a persistent LMDB-backed database.
-- `build_in_memory()` opens an in-memory database for tests, examples, and temporary workloads.
-- `build_optimistic()` is available on native targets when you specifically want optimistic transactions on top of RocksDB.
-- `Database::checkpoint(path)` creates a local consistent snapshot when the selected storage backend supports it.
-- Transaction isolation is documented in [`docs/transaction-isolation.md`](docs/transaction-isolation.md).
-- Cargo features:
- - `rocksdb` is enabled by default
- - `parser` is enabled by default and provides the SQL parser frontend
- - `spill` optionally enables spill-backed external sorting and aggregation
- - `spill` and `wasm` are mutually exclusive
- - `lmdb` is optional
- - `unsafe_txdb_checkpoint` enables experimental checkpoint support for RocksDB `TransactionDB`
- - `cargo check --no-default-features --features lmdb` builds an LMDB-only native configuration
-
-On native targets, `LMDB` shines when reads dominate, while `RocksDB` is usually the stronger choice when writes do.
-Checkpoint support and feature-gating details are documented in [docs/features.md](docs/features.md).
-
-## Shell
-- Run `cargo run --bin kitesql-shell` to open the local interactive shell.
-- Use `cargo run --bin kitesql-shell -- --path ./tmp/kitesql-shell-data` to point to a custom RocksDB directory.
-- Use `cargo run --bin kitesql-shell -- -e "select current_timestamp"` for a quick one-shot check.
-- In interactive mode, end SQL statements with `;`; an empty line also executes the buffered statement.
-- Supported metacommands include `.help`, `.quit`, `.tables`, `.views`, and `.schema `.
-
-👉**more examples**
-- [hello_world](examples/hello_world.rs)
-- [transaction](examples/transaction.rs)
-
-
-## WebAssembly
-- Build: `wasm-pack build --release --target nodejs` (outputs to `./pkg`; use `--target web` or `--target bundler` for browser/bundler setups).
-- Usage:
+| Builder | Storage |
+| --- | --- |
+| `build_rocksdb()` | RocksDB (default feature `rocksdb`), stronger for write-heavy workloads |
+| `build_lmdb()` | LMDB (feature `lmdb`), stronger for read-heavy workloads |
+| `build_in_memory()` | In-memory, for tests and temporary data |
+| `build_optimistic()` | RocksDB with optimistic transactions |
+
+Feature flags, checkpoints, and transaction isolation: [docs/features.md](docs/features.md), [docs/transaction-isolation.md](docs/transaction-isolation.md).
+
+## Shell, WebAssembly, Python
+
+### Shell
+```bash
+cargo run --bin kitesql-shell
+cargo run --bin kitesql-shell -- -e "select 1"
+```
+Type `.help` for metacommands.
+
+### WebAssembly
+```bash
+wasm-pack build --release --target nodejs
+```
```js
import { WasmDatabase } from "./pkg/kite_sql.js";
const db = new WasmDatabase();
await db.ddl("create table demo(id int primary key, v int)");
-await db.execute("insert into demo values (1, 2), (2, 4)");
-const rows = db.run("select * from demo").rows();
-console.log(rows.map((r) => r.values.map((v) => v.Int32 ?? v)));
+console.log(db.run("select * from demo").rows());
```
-- In Node.js, provide a small `localStorage` shim if you enable statistics-related features (see `examples/wasm_index_usage.test.mjs`).
-## Python (PyO3)
-- Enable bindings with Cargo feature `python`.
-- Constructor is explicit: `Database(path, backend="rocksdb")`; use `backend="lmdb"` to open LMDB. In-memory usage is `Database.in_memory()`.
-- Minimal usage:
+### Python
+Requires the `python` feature.
```python
import kite_sql
-db = kite_sql.Database.in_memory()
+db = kite_sql.Database.in_memory() # or Database(path, backend="lmdb")
db.execute("create table demo(id int primary key, v int)")
-db.execute("insert into demo values (1, 2), (2, 4)")
-for row in db.run("select * from demo"):
- print(row["values"])
+print(list(db.run("select * from demo")))
```
## TPC-C
-Run `make tpcc` (or `cargo run -p tpcc --release`) to execute the benchmark against the default KiteSQL storage. Use `--backend rocksdb` or `--backend lmdb` to compare the two persistent backends directly.
-Run `make tpcc-dual` to mirror every TPCC statement to an in-memory SQLite database alongside KiteSQL and assert the two engines return identical results; this target runs for 60 seconds (`--measure-time 60`). Use `cargo run -p tpcc --release -- --backend dual --measure-time ` for a custom duration.
+`make tpcc` runs the benchmark (`--backend rocksdb|lmdb`); `make tpcc-dual` cross-checks every statement against SQLite.
-- i9-13900HX
-- 32.0 GB
-- KIOXIA-EXCERIA PLUS G3 SSD
-- Tips: TPC-C currently only supports single thread
+720-second single-thread run on an i9-13900HX (32 GB, KIOXIA EXCERIA PLUS G3), every backend pinned to the same P-core. Latencies are p90 in µs, including commit.
-Recent stable-run 720-second local comparison on the machine above:
-
-| Backend | TpmC | New-Order p90 (µs) | Payment p90 (µs) | Order-Status p90 (µs) | Delivery p90 (µs) | Stock-Level p90 (µs) |
+| Backend | TpmC | New-Order | Payment | Order-Status | Delivery | Stock-Level |
| --- | ---: | ---: | ---: | ---: | ---: | ---: |
-| KiteSQL LMDB | 134909 | 360 | 94 | 147 | 503 | 466 |
-| KiteSQL RocksDB | 43866 | 521 | 289 | 269 | 11711 | 902 |
-| SQLite balanced | 49200 | 304 | 74 | 53 | 375 | 518 |
-| SQLite practical | 43385 | 368 | 74 | 44 | 482 | 386 |
-
-These rows are from the local run on `2026-09-27`; latencies are in microseconds and include transaction commit.
+| KiteSQL LMDB | 159616 | 315 | 81 | 44 | 435 | 332 |
+| KiteSQL RocksDB | 48035 | 459 | 258 | 200 | 11055 | 704 |
+| SQLite balanced | 67102 | 283 | 67 | 48 | 338 | 473 |
+| SQLite practical | 67153 | 336 | 66 | 41 | 427 | 339 |
-#### 👉[check more](tpcc/README.md)
+👉 [Details and how to reproduce](tpcc/README.md)
## Roadmap
- Get [SQL 2016](https://github.com/KipData/KiteSQL/issues/130) mostly supported
diff --git a/scripts/run_tpcc_stable.py b/scripts/run_tpcc_stable.py
index 09cf2f0b..d5b24431 100755
--- a/scripts/run_tpcc_stable.py
+++ b/scripts/run_tpcc_stable.py
@@ -64,13 +64,27 @@ def parse_args() -> argparse.Namespace:
parser.add_argument("--measure-time", type=int, default=None)
parser.add_argument("--tpcc-max-retry", type=int, default=5)
parser.add_argument("--duplicate-retry", type=int, default=1)
- parser.add_argument("--cool-temp-c", type=float, default=65.0)
+ parser.add_argument("--cool-temp-c", type=float, default=70.0)
parser.add_argument("--idle-cpu-percent", type=float, default=20.0)
parser.add_argument("--min-cooldown-sec", type=int, default=300)
parser.add_argument("--stable-samples", type=int, default=3)
parser.add_argument("--sample-interval-sec", type=float, default=10.0)
parser.add_argument("--max-wait-sec", type=int, default=7200)
parser.add_argument("--skip-health-check", action="store_true")
+ parser.add_argument(
+ "--cpu",
+ type=int,
+ help="Pin TPCC to this CPU. Defaults to the core with the highest max frequency.",
+ )
+ parser.add_argument(
+ "--variants",
+ help="Comma-separated subset of variants to run (default: all).",
+ )
+ parser.add_argument(
+ "--no-cpu-pin",
+ action="store_true",
+ help="Let the scheduler place TPCC on any CPU.",
+ )
parser.add_argument(
"--build",
action="store_true",
@@ -79,6 +93,56 @@ def parse_args() -> argparse.Namespace:
return parser.parse_args()
+def parse_cpu_list(text: str) -> list[int]:
+ cpus: list[int] = []
+ for part in text.strip().split(","):
+ if not part:
+ continue
+ if "-" in part:
+ start, end = part.split("-", 1)
+ cpus.extend(range(int(start), int(end) + 1))
+ else:
+ cpus.append(int(part))
+ return cpus
+
+
+def read_cpu_list(path: str) -> list[int]:
+ try:
+ return parse_cpu_list(Path(path).read_text(encoding="utf-8"))
+ except (OSError, ValueError):
+ return []
+
+
+def cpu_max_freq(cpu: int) -> int:
+ try:
+ return int(
+ Path(f"/sys/devices/system/cpu/cpu{cpu}/cpufreq/cpuinfo_max_freq").read_text(
+ encoding="utf-8"
+ )
+ )
+ except (OSError, ValueError):
+ return 0
+
+
+def default_pinned_cpu() -> int | None:
+ cpus = read_cpu_list("/sys/devices/system/cpu/online")
+ if not cpus:
+ return None
+ cpu0_siblings = set(
+ read_cpu_list("/sys/devices/system/cpu/cpu0/topology/thread_siblings_list")
+ ) or {0}
+ candidates = [cpu for cpu in cpus if cpu not in cpu0_siblings] or cpus
+ return max(candidates, key=lambda cpu: (cpu_max_freq(cpu), -cpu))
+
+
+def resolve_pinned_cpu(args: argparse.Namespace) -> int | None:
+ if args.no_cpu_pin:
+ return None
+ if args.cpu is not None:
+ return args.cpu
+ return default_pinned_cpu()
+
+
def read_proc_stat() -> tuple[int, int] | None:
try:
line = Path("/proc/stat").read_text(encoding="utf-8").splitlines()[0]
@@ -371,8 +435,20 @@ def run_variant(
append_line(log_file, f"$ {quote_cmd(cmd)}")
append_line(log_file)
+ if args.pinned_cpu is not None:
+ append_line(log_file, f"[runner] pinned to cpu {args.pinned_cpu}")
+ pinned_cpu = args.pinned_cpu
+ preexec = (
+ (lambda: os.sched_setaffinity(0, {pinned_cpu})) if pinned_cpu is not None else None
+ )
with log_file.open("a", encoding="utf-8", errors="replace") as file:
- proc = subprocess.run(cmd, cwd=ROOT_DIR, stdout=file, stderr=subprocess.STDOUT)
+ proc = subprocess.run(
+ cmd,
+ cwd=ROOT_DIR,
+ stdout=file,
+ stderr=subprocess.STDOUT,
+ preexec_fn=preexec,
+ )
if proc.returncode == 0:
break
@@ -433,6 +509,11 @@ def write_summary_header(args: argparse.Namespace, summary_file: Path) -> None:
f"- Duplicate-key retries per variant: {args.duplicate_retry}",
f"- Measure time: {measure}",
f"- Binary: `{args.binary}`",
+ (
+ f"- CPU pin: cpu {args.pinned_cpu}"
+ if args.pinned_cpu is not None
+ else "- CPU pin: none (scheduler placement)"
+ ),
(
"- Machine gates: "
f"temp<={args.cool_temp_c:.1f}C, "
@@ -455,6 +536,7 @@ def main() -> int:
args = parse_args()
args.binary = args.binary.resolve()
args.tmp_dir = args.tmp_dir.resolve()
+ args.pinned_cpu = resolve_pinned_cpu(args)
result_dir = (
args.result_dir.resolve()
if args.result_dir is not None
@@ -475,11 +557,20 @@ def main() -> int:
)
return 1
+ variants = VARIANTS
+ if args.variants:
+ names = {name.strip() for name in args.variants.split(",") if name.strip()}
+ unknown = sorted(names - {variant.name for variant in VARIANTS})
+ if unknown:
+ print(f"unknown variants: {', '.join(unknown)}", file=sys.stderr)
+ return 2
+ variants = [variant for variant in VARIANTS if variant.name in names]
+
write_summary_header(args, summary_file)
previous_finished_at: float | None = None
try:
- for variant in VARIANTS:
+ for variant in variants:
previous_finished_at = run_variant(
args, variant, log_dir, summary_file, previous_finished_at
)
diff --git a/src/binder/parser.rs b/src/binder/parser.rs
index b60da60a..610c33ee 100644
--- a/src/binder/parser.rs
+++ b/src/binder/parser.rs
@@ -226,7 +226,7 @@ impl Database {
let (plan, arena) = self
.state
.build_plan(&[], tx, |binder, arena| binder.bind(&statement, arena))?;
- self.state.execute(tx, plan, arena)
+ self.state.execute(tx, plan.into(), arena)
})() {
Ok(result) => result,
Err(err) => {
diff --git a/src/db.rs b/src/db.rs
index ff00d06e..24c10588 100644
--- a/src/db.rs
+++ b/src/db.rs
@@ -42,7 +42,7 @@ use crate::optimizer::rule::normalization::NormalizationRuleImpl;
#[cfg(feature = "orm")]
use crate::orm::FromQueryRow;
use crate::planner::operator::Operator;
-use crate::planner::{LogicalPlan, MetaArena, PlanArena, TableArenaCell};
+use crate::planner::{LogicalPlan, MetaArena, PlanArena, PlanInput, PlanKeeper, TableArenaCell};
#[cfg(all(not(target_arch = "wasm32"), feature = "lmdb"))]
use crate::storage::lmdb::{LmdbConfig, LmdbStorage};
use crate::storage::memory::MemoryStorage;
@@ -81,12 +81,12 @@ pub(crate) trait BindSource<'a> {
type Storage: Storage;
- fn execute(self, build: F) -> Result
+ fn execute>, A: MetaArena + 'a, F>(
+ self,
+ build: F,
+ ) -> Result
where
- F: FnOnce(
- &'a State,
- &Self::Transaction,
- ) -> Result<(LogicalPlan, A), DatabaseError>;
+ F: FnOnce(&'a State, &Self::Transaction) -> Result<(P, A), DatabaseError>;
#[cfg(feature = "orm")]
fn explain(self, params: A, build: F) -> Result
@@ -431,6 +431,27 @@ fn optimizer_pipeline() -> HepOptimizerPipeline {
.build()
}
+fn statement_stamp(
+ transaction: &mut T,
+ plan: &LogicalPlan,
+) -> Result {
+ let scans_and_writes = match &plan.operator {
+ Operator::Insert(_) => !matches!(plan.childrens.only().operator, Operator::Values(_)),
+ Operator::Update(_)
+ | Operator::AddColumn(_)
+ | Operator::ChangeColumn(_)
+ | Operator::DropColumn(_)
+ | Operator::CreateIndex(_)
+ | Operator::Analyze(_) => true,
+ _ => false,
+ };
+ if scans_and_writes {
+ transaction.next_statement_stamp()
+ } else {
+ Ok(0)
+ }
+}
+
pub(crate) struct State {
scala_functions: ScalaFunctions,
table_functions: TableFunctions,
@@ -551,6 +572,7 @@ impl State {
op.histogram_buckets = self.histogram_buckets;
}
}
+ plan.populate_output_schema_recursive(&mut arena);
Ok((plan, arena))
}
@@ -558,14 +580,17 @@ impl State {
pub(crate) fn execute<'a, 'txn, A: MetaArena + 'a>(
&'a self,
transaction: &'a mut S::TransactionType<'txn>,
- mut plan: LogicalPlan,
+ plan: PlanInput<'a>,
mut plan_arena: A,
) -> Result<(Schema, A, Executor<'a, S::TransactionType<'txn>>), DatabaseError>
where
S: 'txn,
{
- let schema = plan.take_schema(&mut plan_arena);
+ let keeper = PlanKeeper::new(plan);
+ let plan = keeper.plan();
+ let schema = plan.read_schema().clone();
let mut arena = ExecArena::new();
+ arena.set_statement_stamp(statement_stamp(transaction, plan)?);
let read_context = ExecutionContext::new(
&self.table_cache,
&self.view_cache,
@@ -574,7 +599,7 @@ impl State {
&self.table_functions,
);
let root = build_write(&mut arena, &mut plan_arena, plan, read_context, transaction);
- let executor = Executor::new(arena, root);
+ let executor = Executor::new(arena, root, keeper);
Ok((schema, plan_arena, executor))
}
@@ -636,8 +661,12 @@ impl State {
}
}
- let schema = plan.take_schema(&mut plan_arena);
+ plan.populate_output_schema_recursive(&mut plan_arena);
+ let keeper = PlanKeeper::new(PlanInput::Owned(plan));
+ let plan = keeper.plan();
+ let schema = plan.read_schema().clone();
let mut arena = ExecArena::new();
+ arena.set_statement_stamp(statement_stamp(transaction, plan)?);
let cache = ExecutionContext::new(
table_cache,
view_cache,
@@ -646,7 +675,7 @@ impl State {
table_functions,
);
let root = build_write(&mut arena, &mut plan_arena, plan, cache, transaction);
- let executor = Executor::new(arena, root);
+ let executor = Executor::new(arena, root, keeper);
Ok((schema, plan_arena, executor))
}
@@ -871,9 +900,12 @@ impl<'a, S: Storage> BindSource<'a> for &'a Database {
type Storage = S;
- fn execute(self, build: F) -> Result
+ fn execute>, A: MetaArena + 'a, F>(
+ self,
+ build: F,
+ ) -> Result
where
- F: FnOnce(&'a State, &Self::Transaction) -> Result<(LogicalPlan, A), DatabaseError>,
+ F: FnOnce(&'a State, &Self::Transaction) -> Result<(P, A), DatabaseError>,
{
let transaction = Box::into_raw(Box::new(
self.storage
@@ -883,7 +915,7 @@ impl<'a, S: Storage> BindSource<'a> for &'a Database {
let transaction = unsafe { &mut *transaction };
transaction.begin_statement_scope()?;
let (plan, arena) = build(&self.state, transaction)?;
- self.state.execute(transaction, plan, arena)
+ self.state.execute(transaction, plan.into(), arena)
})();
let (schema, arena, executor) = match result {
Ok(result) => result,
@@ -1089,16 +1121,19 @@ impl<'a, 'txn, S: Storage> BindSource<'a> for &'a mut DBTransaction<'txn, S> {
type Storage = S;
- fn execute(self, build: F) -> Result
+ fn execute>, A: MetaArena + 'a, F>(
+ self,
+ build: F,
+ ) -> Result
where
- F: FnOnce(&'a State, &Self::Transaction) -> Result<(LogicalPlan, A), DatabaseError>,
+ F: FnOnce(&'a State, &Self::Transaction) -> Result<(P, A), DatabaseError>,
{
self.inner.begin_statement_scope()?;
let (plan, arena) = build(self.state, &self.inner)?;
let transaction = std::ptr::from_mut(&mut self.inner);
let (schema, arena, executor) =
self.state
- .execute(unsafe { &mut *transaction }, plan, arena)?;
+ .execute(unsafe { &mut *transaction }, plan.into(), arena)?;
Ok(TransactionIter::new(
schema,
Box::new(arena) as Box,
@@ -1774,7 +1809,7 @@ pub(crate) mod test {
let plan = row.values[0].utf8().unwrap();
assert_eq!(
plan,
- "Projection [t1.a, t1.b, t1.a, t1.b, 9] [Project => (Sort Option: Follow)] LeftOuter Join Where (t1.a > 0) [NestLoopJoin => (Sort Option: None)] Projection [t1.a, t1.b] [Project => (Sort Option: Follow)] Filter (t1.b > 0), Is Having: false [Filter => (Sort Option: Follow)] TableScan t1 -> [t1.a, t1.b] [SeqScan => (Sort Option: None)] Projection [t1.a, t1.b] [Project => (Sort Option: Follow)] TableScan t1 -> [t1.a, t1.b] [IndexScan By pk_index => (1, +inf) => (Sort Option: OrderBy: (t1.a Asc Nulls Last) ignore_prefix_len: 0)]"
+ "Projection [t1.a, t1.b, t1.a, t1.b, 9] [Project => (Sort Option: Follow)] LeftOuter Join Where (t1.a > 0) [NestLoopJoin => (Sort Option: None)] Projection [t1.a, t1.b] [Project => (Sort Option: Follow)] Filter (t1.b > 0), Is Having: false [Filter => (Sort Option: Follow)] TableScan t1 -> [t1.a, t1.b] [SeqScan => (Sort Option: None)] Projection [t1.a, t1.b] [Project => (Sort Option: Follow)] TableScan t1 -> [t1.a, t1.b] [IndexScan By pk_index => ($3, +inf) => (Sort Option: OrderBy: (t1.a Asc Nulls Last) ignore_prefix_len: 0)]"
);
}
diff --git a/src/db/prepared.rs b/src/db/prepared.rs
index b217c580..4a4d0981 100644
--- a/src/db/prepared.rs
+++ b/src/db/prepared.rs
@@ -1,100 +1,11 @@
use super::*;
-use crate::expression::range_detacher::{IndexRangeColumn, RangeDetacher};
-use crate::planner::operator::table_scan::TableScanOperator;
-use crate::planner::operator::visitor_mut::OperatorVisitorMut;
+#[cfg(test)]
use crate::planner::operator::{PhysicalOption, PlanImpl, SortOption};
-use crate::planner::{ExprRef, MetaArena, ParamArena};
+use crate::planner::{ExprRef, ParamArena, PlanInput};
+#[cfg(test)]
use crate::types::index::IndexLookup;
use crate::types::LogicalType;
-struct ParameterBinder<'a> {
- params: &'a [(usize, DataValue)],
-}
-
-impl ParameterBinder<'_> {
- fn bind_index_infos(
- &self,
- index_infos: &mut [crate::types::index::IndexInfo],
- ) -> Result<(), DatabaseError> {
- for info in index_infos {
- if let Some(lookup) = &mut info.lookup {
- lookup.bind_parameters(self.params)?;
- }
- }
- Ok(())
- }
-}
-
-impl<'plan> OperatorVisitorMut<'plan> for ParameterBinder<'_> {
- fn visit_table_scan(
- &mut self,
- TableScanOperator { index_infos, .. }: &'plan mut TableScanOperator,
- ) -> Result<(), DatabaseError> {
- self.bind_index_infos(index_infos)
- }
-
- fn visit_physical_option(
- &mut self,
- physical_option: &'plan mut PhysicalOption,
- ) -> Result<(), DatabaseError> {
- if let PlanImpl::IndexScan(info) = &mut physical_option.plan {
- self.bind_index_infos(std::slice::from_mut(info))?;
- }
- Ok(())
- }
-}
-
-/// Extend the selected static index range using its bound residual predicate.
-struct SpecializeIndexRange<'a, A: MetaArena + ?Sized> {
- arena: &'a mut A,
-}
-
-impl<'plan, A: MetaArena + ?Sized> OperatorVisitorMut<'plan> for SpecializeIndexRange<'_, A> {
- fn visit_operator(
- &mut self,
- operator: &'plan mut Operator,
- physical_option: Option<&'plan mut PhysicalOption>,
- ) -> Result<(), DatabaseError> {
- let Operator::TableScan(scan) = operator else {
- return Ok(());
- };
- let Some(option) = physical_option else {
- return Ok(());
- };
- let PlanImpl::IndexScan(index) = &mut option.plan else {
- return Ok(());
- };
- let (Some(params_predicate), Some(IndexLookup::Static(original))) =
- (index.residual_predicate, &index.lookup)
- else {
- return Ok(());
- };
- let SortOption::OrderBy {
- ignore_prefix_len, ..
- } = &index.sort_option
- else {
- return Ok(());
- };
- let Some(range) = RangeDetacher::::specialize_range(
- index.meta,
- original,
- params_predicate,
- *ignore_prefix_len,
- self.arena,
- )?
- else {
- return Ok(());
- };
- index.lookup = Some(IndexLookup::Static(range));
- for candidate in &mut scan.index_infos {
- if candidate.meta == index.meta {
- candidate.lookup = index.lookup.clone();
- }
- }
- Ok(())
- }
-}
-
/// A bound and optimized reusable plan.
#[derive(Clone)]
pub struct PreparedPlan<'db> {
@@ -107,12 +18,8 @@ impl<'db> PreparedPlan<'db> {
pub(crate) fn bind_parameters(
&self,
params: &[(usize, DataValue)],
- ) -> Result<(LogicalPlan, ParamArena<'_>), DatabaseError> {
- let mut arena = ParamArena::new(&self.arena, &self.parameter_expressions, params)?;
- let mut plan = self.plan.clone();
- ParameterBinder { params }.visit_plan(&mut plan)?;
- SpecializeIndexRange { arena: &mut arena }.visit_plan(&mut plan)?;
- Ok((plan, arena))
+ ) -> Result, DatabaseError> {
+ ParamArena::new(&self.arena, &self.parameter_expressions, params)
}
}
@@ -128,8 +35,10 @@ impl Database {
"plan belongs to another database".into(),
));
}
- let (plan, arena) = prepared.bind_parameters(params.as_ref())?;
- BindSource::execute(self, |_, _| Ok((plan, arena)))
+ let arena = prepared.bind_parameters(params.as_ref())?;
+ BindSource::execute(self, |_, _| {
+ Ok((PlanInput::Borrowed(&prepared.plan), arena))
+ })
}
/// Prepare SQL with explicit positional parameter types.
@@ -160,8 +69,10 @@ impl<'db, S: Storage> DBTransaction<'db, S> {
"plan belongs to another database".into(),
));
}
- let (plan, arena) = prepared.bind_parameters(params.as_ref())?;
- BindSource::execute(self, |_, _| Ok((plan, arena)))
+ let arena = prepared.bind_parameters(params.as_ref())?;
+ BindSource::execute(self, |_, _| {
+ Ok((PlanInput::Borrowed(&prepared.plan), arena))
+ })
}
#[cfg(feature = "parser")]
@@ -211,8 +122,7 @@ impl State {
&mut PlanArena<'a>,
) -> Result,
{
- let (mut plan, mut arena) = self.build_plan(params, transaction, build)?;
- plan.output_schema(&mut arena);
+ let (plan, mut arena) = self.build_plan(params, transaction, build)?;
let parameter_expressions = arena.parameter_expressions();
Ok(PreparedPlan {
plan,
@@ -387,7 +297,7 @@ mod tests {
having: false,
is_optimized: true,
};
- let mut plan = LogicalPlan::new(
+ let plan = LogicalPlan::new(
Operator::Filter(filter.clone()),
Childrens::Only(Box::new(scan)),
);
@@ -395,20 +305,50 @@ mod tests {
if should_change {
expected_index.lookup = Some(IndexLookup::Static(expected.clone()));
}
- SpecializeIndexRange { arena: &mut arena }.visit_plan(&mut plan)?;
- assert_eq!(plan.operator, Operator::Filter(filter));
- let child = plan.childrens.pop_only();
- let option = child.physical_option.unwrap();
- assert_eq!(
- option.plan,
- PlanImpl::IndexScan(Box::new(expected_index.clone()))
- );
- assert_eq!(option.sort_option(), &sort);
- let Operator::TableScan(scan) = child.operator else {
+ let Childrens::Only(scan) = plan.childrens.as_ref() else {
panic!("expected scan")
};
- assert_eq!(scan.index_infos[0], expected_index);
- assert_eq!(scan.index_infos[1], other);
+ let Operator::TableScan(scan_op) = &scan.operator else {
+ panic!("expected table scan")
+ };
+ let Some(PhysicalOption {
+ plan: PlanImpl::IndexScan(info),
+ ..
+ }) = &scan.physical_option
+ else {
+ panic!("expected index scan")
+ };
+ if !matches!(info.lookup, Some(IndexLookup::Static(_))) {
+ continue;
+ }
+ let mut execution_arena = crate::execution::ExecArena::<
+ ::TransactionType<'_>,
+ >::new();
+ let executor = crate::execution::dql::index_scan::IndexScan::new(
+ scan_op,
+ info,
+ info.lookup.as_ref().expect("lookup"),
+ );
+ let ranges = executor.ranges(&mut execution_arena, &mut arena)?;
+ let expected_range = match &expected_index.lookup {
+ Some(IndexLookup::Static(range)) => range,
+ _ => unreachable!(),
+ };
+ if let IndexLookup::Static(original) = index.lookup.as_ref().unwrap() {
+ let want = if should_change {
+ expected_range
+ } else {
+ original
+ };
+ let mut ranges = ranges;
+ assert_eq!(ranges.next(), Some(want));
+ }
+ assert_eq!(
+ scan.physical_option.as_ref().unwrap().plan,
+ PlanImpl::IndexScan(Box::new(index.clone()))
+ );
+ assert_eq!(scan.physical_option.as_ref().unwrap().sort_option(), &sort);
+ assert_eq!(plan.operator, Operator::Filter(filter));
assert_eq!(
arena.expression(boundary),
&ScalarExpression::Binary {
@@ -588,13 +528,39 @@ mod tests {
(3, LogicalType::Integer),
],
)?;
- let (bound, _) = plan.bind_parameters(&[
+ let mut bound_arena = plan.bind_parameters(&[
(1, DataValue::Int32(2)),
(2, DataValue::Int32(5)),
(3, DataValue::Int32(22)),
])?;
+ fn find_scan(plan: &LogicalPlan) -> Option<&LogicalPlan> {
+ matches!(plan.operator, Operator::TableScan(_))
+ .then_some(plan)
+ .or_else(|| plan.childrens.iter().find_map(find_scan))
+ }
+ let scan = find_scan(&plan.plan).expect("table scan");
+ let Operator::TableScan(scan_op) = &scan.operator else {
+ unreachable!()
+ };
+ let Some(PhysicalOption {
+ plan: PlanImpl::IndexScan(info),
+ ..
+ }) = &scan.physical_option
+ else {
+ panic!("expected index scan")
+ };
+ let mut execution_arena = crate::execution::ExecArena::<
+ ::TransactionType<'_>,
+ >::new();
+ let ranges = crate::execution::dql::index_scan::IndexScan::new(
+ scan_op,
+ info,
+ info.lookup.as_ref().expect("lookup"),
+ )
+ .ranges(&mut execution_arena, &mut bound_arena)?;
+ let mut ranges = ranges;
assert_eq!(
- index_range(&bound),
+ ranges.next(),
Some(&Range::Scope {
min: Bound::Included(DataValue::Tuple(vec![
DataValue::Int32(2),
diff --git a/src/execution/ddl/add_column.rs b/src/execution/ddl/add_column.rs
index 758025ee..3417db45 100644
--- a/src/execution/ddl/add_column.rs
+++ b/src/execution/ddl/add_column.rs
@@ -22,50 +22,38 @@ use crate::planner::operator::alter_table::add_column::AddColumnOperator;
use crate::planner::MetaArena;
use crate::storage::Transaction;
use crate::types::index::{Index, IndexType};
-use crate::types::tuple_builder::TupleBuilder;
use crate::types::value::DataValue;
-pub struct AddColumn {
- op: Option,
+pub struct AddColumn<'a> {
+ op: &'a AddColumnOperator,
}
-impl From for AddColumn {
- fn from(op: AddColumnOperator) -> Self {
- Self { op: Some(op) }
- }
-}
-
-impl<'a, T: Transaction + 'a> WriteExecutor<'a, T> for AddColumn {
- type Input = Self;
+impl<'a, T: Transaction + 'a> WriteExecutor<'a, T> for AddColumn<'a> {
+ type Input = &'a AddColumnOperator;
fn into_executor(
- input: Self::Input,
+ op: Self::Input,
arena: &mut ExecArena<'a, T>,
_plan_arena: &mut (dyn MetaArena + 'a),
_: ExecutionContext<'_>,
_: &T,
) -> ExecId {
- let executor = input;
- arena.push(ExecNode::AddColumn(executor))
+ arena.push(ExecNode::AddColumn(AddColumn { op }))
}
}
-impl<'a, T: Transaction + 'a> ExecutorNode<'a, T> for AddColumn {
+impl<'a, T: Transaction + 'a> ExecutorNode<'a, T> for AddColumn<'a> {
fn next_tuple(
&mut self,
arena: &mut ExecArena<'a, T>,
plan_arena: &mut (dyn MetaArena + 'a),
) -> Result<(), DatabaseError> {
let table_cache = arena.table_cache();
- let Some(AddColumnOperator {
+ let AddColumnOperator {
table_name,
column,
if_not_exists,
- }) = self.op.take()
- else {
- arena.finish();
- return Ok(());
- };
+ } = self.op;
let (old_schema, pk_ty, column_exists) = {
let table_catalog = arena
@@ -79,9 +67,8 @@ impl<'a, T: Transaction + 'a> ExecutorNode<'a, T> for AddColumn {
)
};
if column_exists {
- if if_not_exists {
- arena.produce_tuple(TupleBuilder::build_result("1".to_string()));
- arena.resume();
+ if *if_not_exists {
+ arena.finish();
return Ok(());
}
return Err(DatabaseError::DuplicateColumn(column.name().to_string()));
@@ -94,9 +81,9 @@ impl<'a, T: Transaction + 'a> ExecutorNode<'a, T> for AddColumn {
let (table, col_id) = transaction.add_column(
table_codec,
plan_arena,
- &table_name,
- &column,
- if_not_exists,
+ table_name,
+ column,
+ *if_not_exists,
)?;
let unique_meta = if column.desc().is_unique() {
table
@@ -116,7 +103,7 @@ impl<'a, T: Transaction + 'a> ExecutorNode<'a, T> for AddColumn {
rewrite_table_in_batches(
transaction,
table_codec,
- &table_name,
+ table_name,
&pk_ty,
old_schema.len(),
|| {
@@ -149,14 +136,13 @@ impl<'a, T: Transaction + 'a> ExecutorNode<'a, T> for AddColumn {
std::slice::from_ref(value),
IndexType::Unique,
);
- transaction.add_index(table_codec, &table_name, index, tuple_id)?;
+ transaction.add_index(table_codec, table_name, index, tuple_id)?;
}
Ok(())
},
)?;
- arena.produce_tuple(TupleBuilder::build_result("1".to_string()));
- arena.resume();
+ arena.finish();
Ok(())
}
}
diff --git a/src/execution/ddl/change_column.rs b/src/execution/ddl/change_column.rs
index 350024f7..8bca2787 100644
--- a/src/execution/ddl/change_column.rs
+++ b/src/execution/ddl/change_column.rs
@@ -21,52 +21,40 @@ use crate::iter_ext::Itertools;
use crate::planner::operator::alter_table::change_column::{ChangeColumnOperator, NotNullChange};
use crate::planner::MetaArena;
use crate::storage::Transaction;
-use crate::types::tuple_builder::TupleBuilder;
-pub struct ChangeColumn {
- op: Option,
+pub struct ChangeColumn<'a> {
+ op: &'a ChangeColumnOperator,
}
-impl From for ChangeColumn {
- fn from(op: ChangeColumnOperator) -> Self {
- Self { op: Some(op) }
- }
-}
-
-impl<'a, T: Transaction + 'a> WriteExecutor<'a, T> for ChangeColumn {
- type Input = Self;
+impl<'a, T: Transaction + 'a> WriteExecutor<'a, T> for ChangeColumn<'a> {
+ type Input = &'a ChangeColumnOperator;
fn into_executor(
- input: Self::Input,
+ op: Self::Input,
arena: &mut ExecArena<'a, T>,
_plan_arena: &mut (dyn MetaArena + 'a),
_: ExecutionContext<'_>,
_: &T,
) -> ExecId {
- let executor = input;
- arena.push(ExecNode::ChangeColumn(executor))
+ arena.push(ExecNode::ChangeColumn(ChangeColumn { op }))
}
}
-impl<'a, T: Transaction + 'a> ExecutorNode<'a, T> for ChangeColumn {
+impl<'a, T: Transaction + 'a> ExecutorNode<'a, T> for ChangeColumn<'a> {
fn next_tuple(
&mut self,
arena: &mut ExecArena<'a, T>,
plan_arena: &mut (dyn MetaArena + 'a),
) -> Result<(), DatabaseError> {
let table_cache = arena.table_cache();
- let Some(ChangeColumnOperator {
+ let ChangeColumnOperator {
table_name,
old_column_name,
new_column_name,
data_type,
default_change,
not_null_change,
- }) = self.op.take()
- else {
- arena.finish();
- return Ok(());
- };
+ } = self.op;
let (old_schema, pk_ty, column_index, old_column_type, old_column_id, affected_index_name) = {
let table_catalog = arena
@@ -98,7 +86,7 @@ impl<'a, T: Transaction + 'a> ExecutorNode<'a, T> for ChangeColumn {
affected_index_name,
)
};
- let needs_data_rewrite = old_column_type != data_type;
+ let needs_data_rewrite = &old_column_type != data_type;
let needs_not_null_validation = matches!(not_null_change, NotNullChange::Set);
if needs_data_rewrite {
@@ -121,7 +109,7 @@ impl<'a, T: Transaction + 'a> ExecutorNode<'a, T> for ChangeColumn {
rewrite_table_in_batches(
transaction,
table_codec,
- &table_name,
+ table_name,
&pk_ty,
old_schema.len(),
|| {
@@ -156,7 +144,7 @@ impl<'a, T: Transaction + 'a> ExecutorNode<'a, T> for ChangeColumn {
visit_table_in_batches(
transaction,
table_codec,
- &table_name,
+ table_name,
&pk_ty,
old_schema.len(),
|| {
@@ -178,19 +166,18 @@ impl<'a, T: Transaction + 'a> ExecutorNode<'a, T> for ChangeColumn {
let table = transaction.change_column(
table_codec,
plan_arena,
- &table_name,
- &old_column_name,
- &new_column_name,
- &data_type,
- &default_change,
- ¬_null_change,
+ table_name,
+ old_column_name,
+ new_column_name,
+ data_type,
+ default_change,
+ not_null_change,
)?;
DDLApply::upsert_table(table, true)
};
arena.push_ddl_apply(apply);
- arena.produce_tuple(TupleBuilder::build_result(format!("{table_name}")));
- arena.resume();
+ arena.finish();
Ok(())
}
}
diff --git a/src/execution/ddl/create_index.rs b/src/execution/ddl/create_index.rs
index 9ad43081..1c2ed39a 100644
--- a/src/execution/ddl/create_index.rs
+++ b/src/execution/ddl/create_index.rs
@@ -24,73 +24,53 @@ use crate::planner::MetaArena;
use crate::storage::Transaction;
use crate::types::index::Index;
use crate::types::tuple::Schema;
-use crate::types::tuple_builder::TupleBuilder;
use crate::types::ColumnId;
-pub struct CreateIndex {
- op: Option,
+pub struct CreateIndex<'a> {
+ op: &'a CreateIndexOperator,
input_schema: Schema,
- input_plan: LogicalPlan,
input: ExecId,
}
-impl From<(CreateIndexOperator, LogicalPlan)> for CreateIndex {
- fn from((op, input): (CreateIndexOperator, LogicalPlan)) -> Self {
- Self {
- op: Some(op),
- input_schema: Default::default(),
- input_plan: input,
- input: 0,
- }
- }
-}
-
-impl<'a, T: Transaction + 'a> WriteExecutor<'a, T> for CreateIndex {
- type Input = Self;
+impl<'a, T: Transaction + 'a> WriteExecutor<'a, T> for CreateIndex<'a> {
+ type Input = (&'a CreateIndexOperator, &'a LogicalPlan);
fn into_executor(
- input: Self::Input,
+ (op, input_plan): Self::Input,
arena: &mut ExecArena<'a, T>,
plan_arena: &mut (dyn MetaArena + 'a),
cache: ExecutionContext<'_>,
transaction: &T,
) -> ExecId {
- let mut executor = input;
- executor.input_schema = executor.input_plan.take_schema(plan_arena);
- executor.input = build_read(
- arena,
- plan_arena,
- executor.input_plan.take(),
- cache,
- transaction,
- );
- arena.push(ExecNode::CreateIndex(executor))
+ let input_schema = input_plan.read_schema().clone();
+ let input = build_read(arena, plan_arena, input_plan, cache, transaction);
+ arena.push(ExecNode::CreateIndex(CreateIndex {
+ op,
+ input_schema,
+ input,
+ }))
}
}
-impl<'a, T: Transaction + 'a> ExecutorNode<'a, T> for CreateIndex {
+impl<'a, T: Transaction + 'a> ExecutorNode<'a, T> for CreateIndex<'a> {
fn next_tuple(
&mut self,
arena: &mut ExecArena<'a, T>,
plan_arena: &mut (dyn MetaArena + 'a),
) -> Result<(), DatabaseError> {
- let Some(CreateIndexOperator {
+ let CreateIndexOperator {
table_name,
index_name,
columns,
if_not_exists,
ty,
- }) = self.op.take()
- else {
- arena.finish();
- return Ok(());
- };
+ } = self.op;
- if if_not_exists
- && arena.table_cache().get(&table_name).is_some_and(|table| {
+ if *if_not_exists
+ && arena.table_cache().get(table_name).is_some_and(|table| {
table
.indexes()
- .any(|index| plan_arena.index(*index).name == index_name)
+ .any(|index| plan_arena.index(*index).name == *index_name)
})
{
arena.finish();
@@ -98,7 +78,8 @@ impl<'a, T: Transaction + 'a> ExecutorNode<'a, T> for CreateIndex {
}
let (column_ids, column_exprs): (Vec, Vec) = columns
- .into_iter()
+ .iter()
+ .copied()
.filter_map(|column| {
plan_arena.column(column).id().and_then(|id| {
self.input_schema
@@ -113,10 +94,10 @@ impl<'a, T: Transaction + 'a> ExecutorNode<'a, T> for CreateIndex {
let (table, index_id) = transaction.add_index_meta(
table_codec,
plan_arena,
- &table_name,
- index_name,
+ table_name,
+ index_name.clone(),
column_ids,
- ty,
+ *ty,
)?;
arena.push_ddl_apply(DDLApply::upsert_table(table, false));
Ok(index_id)
@@ -124,7 +105,7 @@ impl<'a, T: Transaction + 'a> ExecutorNode<'a, T> for CreateIndex {
let index_id = match index_id_result {
Ok(index_id) => index_id,
Err(DatabaseError::DuplicateIndex(index_name)) => {
- if if_not_exists {
+ if *if_not_exists {
arena.finish();
return Ok(());
} else {
@@ -142,13 +123,12 @@ impl<'a, T: Transaction + 'a> ExecutorNode<'a, T> for CreateIndex {
{
let mut state = arena.local_state(plan_arena);
let (values, transaction, table_codec) = state.index_values_transaction_codec_mut();
- let index = Index::new(index_id, values, ty);
+ let index = Index::new(index_id, values, *ty);
transaction.add_index(table_codec, table_name.as_ref(), index, &tuple_pk)?;
}
}
- arena.produce_tuple(TupleBuilder::build_result("1".to_string()));
- arena.resume();
+ arena.finish();
Ok(())
}
}
diff --git a/src/execution/ddl/create_table.rs b/src/execution/ddl/create_table.rs
index b9cab332..7633693c 100644
--- a/src/execution/ddl/create_table.rs
+++ b/src/execution/ddl/create_table.rs
@@ -19,63 +19,50 @@ use crate::execution::{
use crate::planner::operator::create_table::CreateTableOperator;
use crate::planner::MetaArena;
use crate::storage::Transaction;
-use crate::types::tuple_builder::TupleBuilder;
-pub struct CreateTable {
- op: Option,
+pub struct CreateTable<'a> {
+ op: &'a CreateTableOperator,
}
-impl From for CreateTable {
- fn from(op: CreateTableOperator) -> Self {
- CreateTable { op: Some(op) }
- }
-}
-
-impl<'a, T: Transaction + 'a> WriteExecutor<'a, T> for CreateTable {
- type Input = Self;
+impl<'a, T: Transaction + 'a> WriteExecutor<'a, T> for CreateTable<'a> {
+ type Input = &'a CreateTableOperator;
fn into_executor(
- input: Self::Input,
+ op: Self::Input,
arena: &mut ExecArena<'a, T>,
_plan_arena: &mut (dyn MetaArena + 'a),
_: ExecutionContext<'_>,
_: &T,
) -> ExecId {
- let executor = input;
- arena.push(ExecNode::CreateTable(executor))
+ arena.push(ExecNode::CreateTable(CreateTable { op }))
}
}
-impl<'a, T: Transaction + 'a> ExecutorNode<'a, T> for CreateTable {
+impl<'a, T: Transaction + 'a> ExecutorNode<'a, T> for CreateTable<'a> {
fn next_tuple(
&mut self,
arena: &mut ExecArena<'a, T>,
plan_arena: &mut (dyn MetaArena + 'a),
) -> Result<(), DatabaseError> {
- let Some(CreateTableOperator {
+ let CreateTableOperator {
table_name,
columns,
if_not_exists,
- }) = self.op.take()
- else {
- arena.finish();
- return Ok(());
- };
+ } = self.op;
let (transaction, table_codec) = arena.transaction_codec_mut();
let table = transaction.create_table(
table_codec,
plan_arena,
table_name.clone(),
- columns,
- if_not_exists,
+ columns.clone(),
+ *if_not_exists,
)?;
if let Some(table) = table {
arena.push_ddl_apply(DDLApply::upsert_table(table, false));
}
- arena.produce_tuple(TupleBuilder::build_result(format!("{table_name}")));
- arena.resume();
+ arena.finish();
Ok(())
}
}
diff --git a/src/execution/ddl/create_view.rs b/src/execution/ddl/create_view.rs
index 22c55f1f..8933d153 100644
--- a/src/execution/ddl/create_view.rs
+++ b/src/execution/ddl/create_view.rs
@@ -19,50 +19,37 @@ use crate::execution::{
use crate::planner::operator::create_view::CreateViewOperator;
use crate::planner::MetaArena;
use crate::storage::Transaction;
-use crate::types::tuple_builder::TupleBuilder;
-pub struct CreateView {
- op: Option,
+pub struct CreateView<'a> {
+ op: &'a CreateViewOperator,
}
-impl From for CreateView {
- fn from(op: CreateViewOperator) -> Self {
- CreateView { op: Some(op) }
- }
-}
-
-impl<'a, T: Transaction + 'a> WriteExecutor<'a, T> for CreateView {
- type Input = Self;
+impl<'a, T: Transaction + 'a> WriteExecutor<'a, T> for CreateView<'a> {
+ type Input = &'a CreateViewOperator;
fn into_executor(
- input: Self::Input,
+ op: Self::Input,
arena: &mut ExecArena<'a, T>,
_plan_arena: &mut (dyn MetaArena + 'a),
_: ExecutionContext<'_>,
_: &T,
) -> ExecId {
- let executor = input;
- arena.push(ExecNode::CreateView(executor))
+ arena.push(ExecNode::CreateView(CreateView { op }))
}
}
-impl<'a, T: Transaction + 'a> ExecutorNode<'a, T> for CreateView {
+impl<'a, T: Transaction + 'a> ExecutorNode<'a, T> for CreateView<'a> {
fn next_tuple(
&mut self,
arena: &mut ExecArena<'a, T>,
plan_arena: &mut (dyn MetaArena + 'a),
) -> Result<(), DatabaseError> {
- let Some(CreateViewOperator { view, or_replace }) = self.op.take() else {
- arena.finish();
- return Ok(());
- };
- let view_name = view.name.to_string();
+ let CreateViewOperator { view, or_replace } = self.op;
let (transaction, table_codec) = arena.transaction_codec_mut();
- let view = transaction.create_view(table_codec, plan_arena, view, or_replace)?;
+ let view = transaction.create_view(table_codec, plan_arena, view.clone(), *or_replace)?;
arena.push_ddl_apply(DDLApply::upsert_view(view));
- arena.produce_tuple(TupleBuilder::build_result(view_name));
- arena.resume();
+ arena.finish();
Ok(())
}
}
diff --git a/src/execution/ddl/drop_column.rs b/src/execution/ddl/drop_column.rs
index e2b47cb7..31a981d3 100644
--- a/src/execution/ddl/drop_column.rs
+++ b/src/execution/ddl/drop_column.rs
@@ -21,49 +21,37 @@ use crate::iter_ext::Itertools;
use crate::planner::operator::alter_table::drop_column::DropColumnOperator;
use crate::planner::MetaArena;
use crate::storage::Transaction;
-use crate::types::tuple_builder::TupleBuilder;
-pub struct DropColumn {
- op: Option,
+pub struct DropColumn<'a> {
+ op: &'a DropColumnOperator,
}
-impl From for DropColumn {
- fn from(op: DropColumnOperator) -> Self {
- Self { op: Some(op) }
- }
-}
-
-impl<'a, T: Transaction + 'a> WriteExecutor<'a, T> for DropColumn {
- type Input = Self;
+impl<'a, T: Transaction + 'a> WriteExecutor<'a, T> for DropColumn<'a> {
+ type Input = &'a DropColumnOperator;
fn into_executor(
- input: Self::Input,
+ op: Self::Input,
arena: &mut ExecArena<'a, T>,
_plan_arena: &mut (dyn MetaArena + 'a),
_: ExecutionContext<'_>,
_: &T,
) -> ExecId {
- let executor = input;
- arena.push(ExecNode::DropColumn(executor))
+ arena.push(ExecNode::DropColumn(DropColumn { op }))
}
}
-impl<'a, T: Transaction + 'a> ExecutorNode<'a, T> for DropColumn {
+impl<'a, T: Transaction + 'a> ExecutorNode<'a, T> for DropColumn<'a> {
fn next_tuple(
&mut self,
arena: &mut ExecArena<'a, T>,
plan_arena: &mut (dyn MetaArena + 'a),
) -> Result<(), DatabaseError> {
let table_cache = arena.table_cache();
- let Some(DropColumnOperator {
+ let DropColumnOperator {
table_name,
column_name,
if_exists,
- }) = self.op.take()
- else {
- arena.finish();
- return Ok(());
- };
+ } = self.op;
let (old_schema, pk_ty, column_info) = {
let table_catalog = arena
@@ -96,7 +84,7 @@ impl<'a, T: Transaction + 'a> ExecutorNode<'a, T> for DropColumn {
rewrite_table_in_batches(
transaction,
table_codec,
- &table_name,
+ table_name,
&pk_ty,
old_schema.len(),
|| {
@@ -121,12 +109,11 @@ impl<'a, T: Transaction + 'a> ExecutorNode<'a, T> for DropColumn {
{
let (transaction, table_codec) = arena.transaction_codec_mut();
let table =
- transaction.drop_column(table_codec, plan_arena, &table_name, &column_name)?;
+ transaction.drop_column(table_codec, plan_arena, table_name, column_name)?;
arena.push_ddl_apply(DDLApply::upsert_table(table, true));
}
- arena.produce_tuple(TupleBuilder::build_result("1".to_string()));
- arena.resume();
+ arena.finish();
Ok(())
} else if !if_exists {
Err(DatabaseError::column_not_found(column_name))
diff --git a/src/execution/ddl/drop_index.rs b/src/execution/ddl/drop_index.rs
index 8f34aab5..683fb42e 100644
--- a/src/execution/ddl/drop_index.rs
+++ b/src/execution/ddl/drop_index.rs
@@ -19,48 +19,36 @@ use crate::execution::{
use crate::planner::operator::drop_index::DropIndexOperator;
use crate::planner::MetaArena;
use crate::storage::Transaction;
-use crate::types::tuple_builder::TupleBuilder;
-pub struct DropIndex {
- op: Option,
+pub struct DropIndex<'a> {
+ op: &'a DropIndexOperator,
}
-impl From for DropIndex {
- fn from(op: DropIndexOperator) -> Self {
- Self { op: Some(op) }
- }
-}
-
-impl<'a, T: Transaction + 'a> WriteExecutor<'a, T> for DropIndex {
- type Input = Self;
+impl<'a, T: Transaction + 'a> WriteExecutor<'a, T> for DropIndex<'a> {
+ type Input = &'a DropIndexOperator;
fn into_executor(
- input: Self::Input,
+ op: Self::Input,
arena: &mut ExecArena<'a, T>,
_plan_arena: &mut (dyn MetaArena + 'a),
_: ExecutionContext<'_>,
_: &T,
) -> ExecId {
- let executor = input;
- arena.push(ExecNode::DropIndex(executor))
+ arena.push(ExecNode::DropIndex(DropIndex { op }))
}
}
-impl<'a, T: Transaction + 'a> ExecutorNode<'a, T> for DropIndex {
+impl<'a, T: Transaction + 'a> ExecutorNode<'a, T> for DropIndex<'a> {
fn next_tuple(
&mut self,
arena: &mut ExecArena<'a, T>,
plan_arena: &mut (dyn MetaArena + 'a),
) -> Result<(), DatabaseError> {
- let Some(DropIndexOperator {
+ let DropIndexOperator {
table_name,
index_name,
if_exists,
- }) = self.op.take()
- else {
- arena.finish();
- return Ok(());
- };
+ } = self.op;
let dropped = {
let (transaction, table_codec) = arena.transaction_codec_mut();
@@ -68,8 +56,8 @@ impl<'a, T: Transaction + 'a> ExecutorNode<'a, T> for DropIndex {
table_codec,
plan_arena,
table_name.clone(),
- &index_name,
- if_exists,
+ index_name,
+ *if_exists,
)?
};
if let Some((table, index_id)) = dropped {
@@ -80,8 +68,7 @@ impl<'a, T: Transaction + 'a> ExecutorNode<'a, T> for DropIndex {
});
}
- arena.produce_tuple(TupleBuilder::build_result(index_name.to_string()));
- arena.resume();
+ arena.finish();
Ok(())
}
}
diff --git a/src/execution/ddl/drop_table.rs b/src/execution/ddl/drop_table.rs
index b3470f54..9467418b 100644
--- a/src/execution/ddl/drop_table.rs
+++ b/src/execution/ddl/drop_table.rs
@@ -19,57 +19,44 @@ use crate::execution::{
use crate::planner::operator::drop_table::DropTableOperator;
use crate::planner::MetaArena;
use crate::storage::Transaction;
-use crate::types::tuple_builder::TupleBuilder;
-pub struct DropTable {
- op: Option,
+pub struct DropTable<'a> {
+ op: &'a DropTableOperator,
}
-impl From for DropTable {
- fn from(op: DropTableOperator) -> Self {
- DropTable { op: Some(op) }
- }
-}
-
-impl<'a, T: Transaction + 'a> WriteExecutor<'a, T> for DropTable {
- type Input = Self;
+impl<'a, T: Transaction + 'a> WriteExecutor<'a, T> for DropTable<'a> {
+ type Input = &'a DropTableOperator;
fn into_executor(
- input: Self::Input,
+ op: Self::Input,
arena: &mut ExecArena<'a, T>,
_plan_arena: &mut (dyn MetaArena + 'a),
_: ExecutionContext<'_>,
_: &T,
) -> ExecId {
- let executor = input;
- arena.push(ExecNode::DropTable(executor))
+ arena.push(ExecNode::DropTable(DropTable { op }))
}
}
-impl<'a, T: Transaction + 'a> ExecutorNode<'a, T> for DropTable {
+impl<'a, T: Transaction + 'a> ExecutorNode<'a, T> for DropTable<'a> {
fn next_tuple(
&mut self,
arena: &mut ExecArena<'a, T>,
plan_arena: &mut (dyn MetaArena + 'a),
) -> Result<(), DatabaseError> {
- let Some(DropTableOperator {
+ let DropTableOperator {
table_name,
if_exists,
- }) = self.op.take()
- else {
- arena.finish();
- return Ok(());
- };
+ } = self.op;
let (transaction, table_codec) = arena.transaction_codec_mut();
- if transaction.drop_table(table_codec, plan_arena, table_name.clone(), if_exists)? {
+ if transaction.drop_table(table_codec, plan_arena, table_name.clone(), *if_exists)? {
arena.push_ddl_apply(DDLApply::DropTable {
name: table_name.clone(),
});
}
- arena.produce_tuple(TupleBuilder::build_result(format!("{table_name}")));
- arena.resume();
+ arena.finish();
Ok(())
}
}
diff --git a/src/execution/ddl/drop_view.rs b/src/execution/ddl/drop_view.rs
index 891d31d8..30785ad6 100644
--- a/src/execution/ddl/drop_view.rs
+++ b/src/execution/ddl/drop_view.rs
@@ -19,57 +19,44 @@ use crate::execution::{
use crate::planner::operator::drop_view::DropViewOperator;
use crate::planner::MetaArena;
use crate::storage::Transaction;
-use crate::types::tuple_builder::TupleBuilder;
-pub struct DropView {
- op: Option,
+pub struct DropView<'a> {
+ op: &'a DropViewOperator,
}
-impl From for DropView {
- fn from(op: DropViewOperator) -> Self {
- DropView { op: Some(op) }
- }
-}
-
-impl<'a, T: Transaction + 'a> WriteExecutor<'a, T> for DropView {
- type Input = Self;
+impl<'a, T: Transaction + 'a> WriteExecutor<'a, T> for DropView<'a> {
+ type Input = &'a DropViewOperator;
fn into_executor(
- input: Self::Input,
+ op: Self::Input,
arena: &mut ExecArena<'a, T>,
_plan_arena: &mut (dyn MetaArena + 'a),
_: ExecutionContext<'_>,
_: &T,
) -> ExecId {
- let executor = input;
- arena.push(ExecNode::DropView(executor))
+ arena.push(ExecNode::DropView(DropView { op }))
}
}
-impl<'a, T: Transaction + 'a> ExecutorNode<'a, T> for DropView {
+impl<'a, T: Transaction + 'a> ExecutorNode<'a, T> for DropView<'a> {
fn next_tuple(
&mut self,
arena: &mut ExecArena<'a, T>,
_: &mut (dyn MetaArena + 'a),
) -> Result<(), DatabaseError> {
- let Some(DropViewOperator {
+ let DropViewOperator {
view_name,
if_exists,
- }) = self.op.take()
- else {
- arena.finish();
- return Ok(());
- };
+ } = self.op;
let (transaction, table_codec) = arena.transaction_codec_mut();
- if transaction.drop_view(table_codec, view_name.clone(), if_exists)? {
+ if transaction.drop_view(table_codec, view_name.clone(), *if_exists)? {
arena.push_ddl_apply(DDLApply::DropView {
name: view_name.clone(),
});
}
- arena.produce_tuple(TupleBuilder::build_result(format!("{view_name}")));
- arena.resume();
+ arena.finish();
Ok(())
}
}
diff --git a/src/execution/ddl/truncate.rs b/src/execution/ddl/truncate.rs
index 072b0b2e..32a5dc4c 100644
--- a/src/execution/ddl/truncate.rs
+++ b/src/execution/ddl/truncate.rs
@@ -19,49 +19,37 @@ use crate::execution::{
use crate::planner::operator::truncate::TruncateOperator;
use crate::planner::MetaArena;
use crate::storage::Transaction;
-use crate::types::tuple_builder::TupleBuilder;
-pub struct Truncate {
- op: Option,
+pub struct Truncate<'a> {
+ op: &'a TruncateOperator,
}
-impl From for Truncate {
- fn from(op: TruncateOperator) -> Self {
- Truncate { op: Some(op) }
- }
-}
-
-impl<'a, T: Transaction + 'a> WriteExecutor<'a, T> for Truncate {
- type Input = Self;
+impl<'a, T: Transaction + 'a> WriteExecutor<'a, T> for Truncate<'a> {
+ type Input = &'a TruncateOperator;
fn into_executor(
- input: Self::Input,
+ op: Self::Input,
arena: &mut ExecArena<'a, T>,
_plan_arena: &mut (dyn MetaArena + 'a),
_: ExecutionContext<'_>,
_: &T,
) -> ExecId {
- let executor = input;
- arena.push(ExecNode::Truncate(executor))
+ arena.push(ExecNode::Truncate(Truncate { op }))
}
}
-impl<'a, T: Transaction + 'a> ExecutorNode<'a, T> for Truncate {
+impl<'a, T: Transaction + 'a> ExecutorNode<'a, T> for Truncate<'a> {
fn next_tuple(
&mut self,
arena: &mut ExecArena<'a, T>,
plan_arena: &mut (dyn MetaArena + 'a),
) -> Result<(), DatabaseError> {
- let Some(TruncateOperator { table_name }) = self.op.take() else {
- arena.finish();
- return Ok(());
- };
+ let TruncateOperator { table_name } = self.op;
let mut state = arena.local_state(plan_arena);
let (transaction, table_codec) = state.transaction_codec_mut();
- transaction.drop_data(table_codec, &table_name)?;
+ transaction.drop_data(table_codec, table_name)?;
- arena.produce_tuple(TupleBuilder::build_result(format!("{table_name}")));
- arena.resume();
+ arena.finish();
Ok(())
}
}
diff --git a/src/execution/dml/analyze.rs b/src/execution/dml/analyze.rs
index 116f355f..f53fb017 100644
--- a/src/execution/dml/analyze.rs
+++ b/src/execution/dml/analyze.rs
@@ -33,57 +33,45 @@ use std::fmt::{self, Formatter};
const DEFAULT_NUM_OF_BUCKETS: usize = 100;
-pub struct Analyze {
- table_name: TableName,
- input_plan: LogicalPlan,
+pub struct Analyze<'a> {
+ table_name: &'a TableName,
input: Option,
histogram_buckets: Option,
}
-impl From<(AnalyzeOperator, LogicalPlan)> for Analyze {
- fn from(
+impl<'a, T: Transaction + 'a> WriteExecutor<'a, T> for Analyze<'a> {
+ type Input = (&'a AnalyzeOperator, &'a LogicalPlan);
+
+ fn into_executor(
(
AnalyzeOperator {
table_name,
- index_metas,
histogram_buckets,
+ ..
},
- input,
- ): (AnalyzeOperator, LogicalPlan),
- ) -> Self {
- let _ = index_metas;
- Analyze {
- table_name,
- input_plan: input,
- input: None,
- histogram_buckets,
- }
- }
-}
-
-impl<'a, T: Transaction + 'a> WriteExecutor<'a, T> for Analyze {
- type Input = Self;
-
- fn into_executor(
- input: Self::Input,
+ input_plan,
+ ): Self::Input,
arena: &mut ExecArena<'a, T>,
plan_arena: &mut (dyn MetaArena + 'a),
cache: ExecutionContext<'_>,
transaction: &T,
) -> ExecId {
- let mut executor = input;
- executor.input = Some(build_read(
+ let input = Some(build_read(
arena,
plan_arena,
- executor.input_plan.take(),
+ input_plan,
cache,
transaction,
));
- arena.push(ExecNode::Analyze(executor))
+ arena.push(ExecNode::Analyze(Analyze {
+ table_name,
+ input,
+ histogram_buckets: *histogram_buckets,
+ }))
}
}
-impl<'a, T: Transaction + 'a> ExecutorNode<'a, T> for Analyze {
+impl<'a, T: Transaction + 'a> ExecutorNode<'a, T> for Analyze<'a> {
fn next_tuple(
&mut self,
arena: &mut ExecArena<'a, T>,
@@ -131,7 +119,7 @@ impl<'a, T: Transaction + 'a> ExecutorNode<'a, T> for Analyze {
let mut state = arena.local_state(plan_arena);
let (transaction, table_codec, ddl_apply) = state.write_transaction_codec_ddl_apply_mut();
let values = Self::persist_statistics_meta(
- &self.table_name,
+ self.table_name,
builders,
ddl_apply,
transaction,
@@ -151,7 +139,7 @@ struct State {
histogram_buckets: Option,
}
-impl Analyze {
+impl<'a> Analyze<'a> {
fn persist_statistics_meta(
table_name: &TableName,
builders: Vec,
diff --git a/src/execution/dml/copy_from_file.rs b/src/execution/dml/copy_from_file.rs
index 72684157..50f88dc6 100644
--- a/src/execution/dml/copy_from_file.rs
+++ b/src/execution/dml/copy_from_file.rs
@@ -25,41 +25,31 @@ use crate::types::tuple_builder::TupleBuilder;
use std::fs::File;
use std::io::BufReader;
-pub struct CopyFromFile {
- op: Option,
+pub struct CopyFromFile<'a> {
+ op: &'a CopyFromFileOperator,
}
-impl From for CopyFromFile {
- fn from(op: CopyFromFileOperator) -> Self {
- CopyFromFile { op: Some(op) }
- }
-}
-
-impl<'a, T: Transaction + 'a> WriteExecutor<'a, T> for CopyFromFile {
- type Input = Self;
+impl<'a, T: Transaction + 'a> WriteExecutor<'a, T> for CopyFromFile<'a> {
+ type Input = &'a CopyFromFileOperator;
fn into_executor(
- input: Self::Input,
+ op: Self::Input,
arena: &mut ExecArena<'a, T>,
_plan_arena: &mut (dyn MetaArena + 'a),
_: ExecutionContext<'_>,
_: &T,
) -> ExecId {
- let executor = input;
- arena.push(ExecNode::CopyFromFile(executor))
+ arena.push(ExecNode::CopyFromFile(CopyFromFile { op }))
}
}
-impl<'a, T: Transaction + 'a> ExecutorNode<'a, T> for CopyFromFile {
+impl<'a, T: Transaction + 'a> ExecutorNode<'a, T> for CopyFromFile<'a> {
fn next_tuple(
&mut self,
arena: &mut ExecArena<'a, T>,
plan_arena: &mut (dyn MetaArena + 'a),
) -> Result<(), DatabaseError> {
- let Some(op) = self.op.take() else {
- arena.finish();
- return Ok(());
- };
+ let op = self.op;
let column_types = op
.schema_ref
.iter()
@@ -76,25 +66,24 @@ impl<'a, T: Transaction + 'a> ExecutorNode<'a, T> for CopyFromFile {
.ok_or(DatabaseError::TableNotFound)?;
let table_name = table.name().to_string();
- let file = File::open(op.source.path)?;
+ let file = File::open(&op.source.path)?;
let mut buf_reader = BufReader::new(file);
- let mut reader = match op.source.format {
+ let mut reader = match &op.source.format {
FileFormat::Csv {
delimiter,
quote,
escape,
header,
} => csv::ReaderBuilder::new()
- .delimiter(delimiter as u8)
- .quote(quote as u8)
+ .delimiter(*delimiter as u8)
+ .quote(*quote as u8)
.escape(escape.map(|c| c as u8))
- .has_headers(header)
+ .has_headers(*header)
.from_reader(&mut buf_reader),
};
let column_count = op.schema_ref.len();
let tuple_builder = TupleBuilder::new(column_types, Some(table.primary_key_indices()));
- let mut size = 0_usize;
for record in reader.records() {
let record = record?;
@@ -109,11 +98,9 @@ impl<'a, T: Transaction + 'a> ExecutorNode<'a, T> for CopyFromFile {
let mut state = arena.local_state(plan_arena);
let (transaction, table_codec) = state.transaction_codec_mut();
transaction.append_tuple(table_codec, &table_name, &chunk, &serializers, false)?;
- size += 1;
}
- arena.produce_tuple(TupleBuilder::build_result(size.to_string()));
- arena.resume();
+ arena.finish();
Ok(())
}
}
@@ -126,7 +113,6 @@ mod tests {
use crate::db::{CatalogKind, DataBaseBuilder};
use crate::errors::DatabaseError;
use crate::storage::Storage;
- use crate::types::tuple::TupleLike;
use crate::types::CharLengthUnits;
use crate::types::LogicalType;
use std::io::Write;
@@ -184,8 +170,8 @@ mod tests {
};
let transaction = db.storage.transaction()?;
- let mut executor = crate::execution::execute_mut(
- CopyFromFile::from(op),
+ let mut executor = crate::execution::execute_input_mut::<_, CopyFromFile>(
+ &op,
crate::execution::test_utils::empty_context(
db.state.table_cache(),
db.state.view_cache(),
@@ -195,10 +181,16 @@ mod tests {
&transaction,
);
- let result = executor
- .next_tuple()?
- .expect("copy from file should yield once");
- assert_eq!(result.value_at(0).to_string(), "2");
+ assert!(executor.next_tuple()?.is_none());
+ drop(executor);
+ transaction.commit()?;
+
+ let mut iter = db.run("select count(*) from test_copy")?;
+ let count = iter
+ .next_tuple(|_, tuple| tuple.values[0].to_string())?
+ .expect("count row");
+ assert_eq!(count, "2");
+ iter.done()?;
Ok(())
}
diff --git a/src/execution/dml/copy_to_file.rs b/src/execution/dml/copy_to_file.rs
index d9402c7b..c4511c42 100644
--- a/src/execution/dml/copy_to_file.rs
+++ b/src/execution/dml/copy_to_file.rs
@@ -24,65 +24,55 @@ use crate::planner::MetaArena;
use crate::storage::Transaction;
use crate::types::tuple_builder::TupleBuilder;
-pub struct CopyToFile {
- op: CopyToFileOperator,
- input_plan: LogicalPlan,
+pub struct CopyToFile<'a> {
+ op: &'a CopyToFileOperator,
column_names: Vec,
input: Option,
}
-impl From<(CopyToFileOperator, LogicalPlan)> for CopyToFile {
- fn from((op, input): (CopyToFileOperator, LogicalPlan)) -> Self {
- CopyToFile {
- op,
- input_plan: input,
- column_names: Default::default(),
- input: None,
- }
- }
-}
-
-impl<'a, T: Transaction + 'a> ReadExecutor<'a, T> for CopyToFile {
- type Input = Self;
+impl<'a, T: Transaction + 'a> ReadExecutor<'a, T> for CopyToFile<'a> {
+ type Input = (&'a CopyToFileOperator, &'a LogicalPlan);
fn into_executor(
- input: Self::Input,
+ (op, input_plan): Self::Input,
arena: &mut ExecArena<'a, T>,
plan_arena: &mut (dyn MetaArena + 'a),
cache: ExecutionContext<'_>,
transaction: &T,
) -> ExecId {
- let mut executor = input;
- executor.column_names = executor
- .input_plan
- .take_schema(plan_arena)
- .into_iter()
- .map(|column| plan_arena.column(column).name().to_string())
+ let column_names = input_plan
+ .read_schema()
+ .iter()
+ .map(|column| plan_arena.column(*column).name().to_string())
.collect_vec();
- executor.input = Some(build_read(
+ let input = Some(build_read(
arena,
plan_arena,
- executor.input_plan.take(),
+ input_plan,
cache,
transaction,
));
- arena.push(ExecNode::CopyToFile(executor))
+ arena.push(ExecNode::CopyToFile(CopyToFile {
+ op,
+ column_names,
+ input,
+ }))
}
}
-impl CopyToFile {
+impl CopyToFile<'_> {
fn create_writer(&self) -> Result, DatabaseError> {
- let mut writer = match self.op.target.format {
+ let mut writer = match &self.op.target.format {
FileFormat::Csv {
delimiter,
quote,
header,
..
} => csv::WriterBuilder::new()
- .delimiter(delimiter as u8)
- .quote(quote as u8)
- .has_headers(header)
- .from_path(self.op.target.path.clone())?,
+ .delimiter(*delimiter as u8)
+ .quote(*quote as u8)
+ .has_headers(*header)
+ .from_path(&self.op.target.path)?,
};
if let FileFormat::Csv { header: true, .. } = self.op.target.format {
@@ -93,7 +83,7 @@ impl CopyToFile {
}
}
-impl<'a, T: Transaction + 'a> ExecutorNode<'a, T> for CopyToFile {
+impl<'a, T: Transaction + 'a> ExecutorNode<'a, T> for CopyToFile<'a> {
fn next_tuple(
&mut self,
arena: &mut ExecArena<'a, T>,
@@ -170,19 +160,11 @@ mod tests {
.table(db.state.table_cache(), "t1".to_string().into())?
.unwrap();
- let executor = CopyToFile {
- op: op.clone(),
- input_plan: TableScanOperator::build(
- "t1".to_string().into(),
- table,
- true,
- &mut plan_arena,
- )?,
- column_names: Default::default(),
- input: None,
- };
- let mut executor = crate::execution::execute(
- executor,
+ let mut input_plan =
+ TableScanOperator::build("t1".to_string().into(), table, true, &mut plan_arena)?;
+ input_plan.populate_output_schema_recursive(&mut plan_arena);
+ let mut executor = crate::execution::execute_input::<_, CopyToFile>(
+ (&op, &input_plan),
crate::execution::test_utils::empty_context(
db.state.table_cache(),
db.state.view_cache(),
diff --git a/src/execution/dml/delete.rs b/src/execution/dml/delete.rs
index e16680a6..3f25092b 100644
--- a/src/execution/dml/delete.rs
+++ b/src/execution/dml/delete.rs
@@ -24,45 +24,33 @@ use crate::storage::Transaction;
use crate::types::index::Index;
use crate::types::tuple_builder::TupleBuilder;
-pub struct Delete {
- table_name: TableName,
- input_plan: LogicalPlan,
+pub struct Delete<'a> {
+ table_name: &'a TableName,
input: Option,
}
-impl From<(DeleteOperator, LogicalPlan)> for Delete {
- fn from((DeleteOperator { table_name, .. }, input): (DeleteOperator, LogicalPlan)) -> Self {
- Delete {
- table_name,
- input_plan: input,
- input: None,
- }
- }
-}
-
-impl<'a, T: Transaction + 'a> WriteExecutor<'a, T> for Delete {
- type Input = Self;
+impl<'a, T: Transaction + 'a> WriteExecutor<'a, T> for Delete<'a> {
+ type Input = (&'a DeleteOperator, &'a LogicalPlan);
fn into_executor(
- input: Self::Input,
+ (DeleteOperator { table_name, .. }, input_plan): Self::Input,
arena: &mut ExecArena<'a, T>,
plan_arena: &mut (dyn MetaArena + 'a),
cache: ExecutionContext<'_>,
transaction: &T,
) -> ExecId {
- let mut executor = input;
- executor.input = Some(build_read(
+ let input = Some(build_read(
arena,
plan_arena,
- executor.input_plan.take(),
+ input_plan,
cache,
transaction,
));
- arena.push(ExecNode::Delete(executor))
+ arena.push(ExecNode::Delete(Delete { table_name, input }))
}
}
-impl<'a, T: Transaction + 'a> ExecutorNode<'a, T> for Delete {
+impl<'a, T: Transaction + 'a> ExecutorNode<'a, T> for Delete<'a> {
fn next_tuple(
&mut self,
arena: &mut ExecArena<'a, T>,
@@ -105,12 +93,12 @@ impl<'a, T: Transaction + 'a> ExecutorNode<'a, T> for Delete {
let mut state = arena.local_state(plan_arena);
let (values, transaction, table_codec) = state.index_values_transaction_codec_mut();
let index = Index::new(*index_id, values, *index_ty);
- transaction.del_index(table_codec, &self.table_name, &index, &tuple_id)?;
+ transaction.del_index(table_codec, self.table_name, &index, &tuple_id)?;
}
let mut state = arena.local_state(plan_arena);
let (transaction, table_codec) = state.transaction_codec_mut();
- transaction.remove_tuple(table_codec, &self.table_name, &tuple_id)?;
+ transaction.remove_tuple(table_codec, self.table_name, &tuple_id)?;
deleted_count += 1;
}
diff --git a/src/execution/dml/insert.rs b/src/execution/dml/insert.rs
index 6b781d3a..9a40b8cb 100644
--- a/src/execution/dml/insert.rs
+++ b/src/execution/dml/insert.rs
@@ -29,57 +29,46 @@ use crate::types::value::DataValue;
use crate::types::ColumnId;
use std::collections::HashMap;
-pub struct Insert {
- table_name: TableName,
+pub struct Insert<'a> {
+ table_name: &'a TableName,
input_schema: Schema,
- input_plan: LogicalPlan,
input: Option,
is_overwrite: bool,
is_mapping_by_name: bool,
}
-impl From<(InsertOperator, LogicalPlan)> for Insert {
- fn from(
+impl<'a, T: Transaction + 'a> WriteExecutor<'a, T> for Insert<'a> {
+ type Input = (&'a InsertOperator, &'a LogicalPlan);
+
+ fn into_executor(
(
InsertOperator {
table_name,
is_overwrite,
is_mapping_by_name,
},
- input,
- ): (InsertOperator, LogicalPlan),
- ) -> Self {
- Insert {
- table_name,
- input_schema: Default::default(),
- input_plan: input,
- input: None,
- is_overwrite,
- is_mapping_by_name,
- }
- }
-}
-
-impl<'a, T: Transaction + 'a> WriteExecutor<'a, T> for Insert {
- type Input = Self;
-
- fn into_executor(
- input: Self::Input,
+ input_plan,
+ ): Self::Input,
arena: &mut ExecArena<'a, T>,
plan_arena: &mut (dyn MetaArena + 'a),
cache: ExecutionContext<'_>,
transaction: &T,
) -> ExecId {
- let mut executor = input;
- executor.input_schema = executor.input_plan.take_schema(plan_arena);
- executor.input = Some(build_read(
+ let input_schema = input_plan.read_schema().clone();
+ let input = Some(build_read(
arena,
plan_arena,
- executor.input_plan.take(),
+ input_plan,
cache,
transaction,
));
- arena.push(ExecNode::Insert(executor))
+ arena.push(ExecNode::Insert(Insert {
+ table_name,
+ input_schema,
+ input,
+ is_overwrite: *is_overwrite,
+ is_mapping_by_name: *is_mapping_by_name,
+ }))
}
}
@@ -89,11 +78,11 @@ enum MappingKey<'a> {
Id(Option),
}
-impl Insert {
- fn column_key<'a>(
- column: &'a crate::catalog::ColumnCatalog,
+impl Insert<'_> {
+ fn column_key<'c>(
+ column: &'c crate::catalog::ColumnCatalog,
is_mapping_by_name: bool,
- ) -> MappingKey<'a> {
+ ) -> MappingKey<'c> {
if is_mapping_by_name {
MappingKey::Name(column.name())
} else {
@@ -102,7 +91,7 @@ impl Insert {
}
}
-impl<'a, T: Transaction + 'a> ExecutorNode<'a, T> for Insert {
+impl<'a, T: Transaction + 'a> ExecutorNode<'a, T> for Insert<'a> {
fn next_tuple(
&mut self,
arena: &mut ExecArena<'a, T>,
@@ -173,13 +162,13 @@ impl<'a, T: Transaction + 'a> ExecutorNode<'a, T> for Insert {
let (values, transaction, table_codec) =
state.index_values_transaction_codec_mut();
let index = Index::new(index_meta.id, values, index_meta.ty);
- transaction.add_index(table_codec, &self.table_name, index, tuple_id)?;
+ transaction.add_index(table_codec, self.table_name, index, tuple_id)?;
}
let mut state = arena.local_state(plan_arena);
let (transaction, table_codec) = state.transaction_codec_mut();
transaction.append_tuple(
table_codec,
- &self.table_name,
+ self.table_name,
&tuple,
&serializers,
self.is_overwrite,
diff --git a/src/execution/dml/update.rs b/src/execution/dml/update.rs
index af30cb12..b993ebd1 100644
--- a/src/execution/dml/update.rs
+++ b/src/execution/dml/update.rs
@@ -19,8 +19,9 @@ use crate::execution::{
};
use crate::iter_ext::Itertools;
use crate::planner::operator::update::UpdateOperator;
+use crate::planner::ExprRef;
+use crate::planner::LogicalPlan;
use crate::planner::MetaArena;
-use crate::planner::{ExprRef, LogicalPlan};
use crate::storage::Transaction;
use crate::types::index::{Index, IndexMeta, IndexType};
use crate::types::tuple::{Schema, Tuple};
@@ -28,58 +29,47 @@ use crate::types::tuple_builder::TupleBuilder;
use crate::types::ColumnId;
use std::collections::{HashMap, HashSet};
-pub struct Update {
- table_name: TableName,
- value_exprs: Vec<(ColumnRef, ExprRef)>,
+pub struct Update<'a> {
+ table_name: &'a TableName,
+ value_exprs: &'a [(ColumnRef, ExprRef)],
input_schema: Schema,
- input_plan: LogicalPlan,
input: Option,
}
-impl From<(UpdateOperator, LogicalPlan)> for Update {
- fn from(
+impl<'a, T: Transaction + 'a> WriteExecutor<'a, T> for Update<'a> {
+ type Input = (&'a UpdateOperator, &'a LogicalPlan);
+
+ fn into_executor(
(
UpdateOperator {
table_name,
value_exprs,
},
- input,
- ): (UpdateOperator, LogicalPlan),
- ) -> Self {
- Update {
- table_name,
- value_exprs,
- input_schema: Default::default(),
- input_plan: input,
- input: None,
- }
- }
-}
-
-impl<'a, T: Transaction + 'a> WriteExecutor<'a, T> for Update {
- type Input = Self;
-
- fn into_executor(
- input: Self::Input,
+ input_plan,
+ ): Self::Input,
arena: &mut ExecArena<'a, T>,
plan_arena: &mut (dyn MetaArena + 'a),
cache: ExecutionContext<'_>,
transaction: &T,
) -> ExecId {
- let mut executor = input;
- executor.input_schema = executor.input_plan.take_schema(plan_arena);
- executor.input = Some(build_read(
+ let input_schema = input_plan.read_schema().clone();
+ let input = Some(build_read(
arena,
plan_arena,
- executor.input_plan.take(),
+ input_plan,
cache,
transaction,
));
- arena.push(ExecNode::Update(executor))
+ arena.push(ExecNode::Update(Update {
+ table_name,
+ value_exprs,
+ input_schema,
+ input,
+ }))
}
}
-impl Update {
+impl Update<'_> {
fn index_needs_update(
index_meta: &IndexMeta,
updated_column_ids: &HashSet,
@@ -97,7 +87,7 @@ impl Update {
}
}
-impl<'a, T: Transaction + 'a> ExecutorNode<'a, T> for Update {
+impl<'a, T: Transaction + 'a> ExecutorNode<'a, T> for Update<'a> {
fn next_tuple(
&mut self,
arena: &mut ExecArena<'a, T>,
@@ -110,7 +100,7 @@ impl<'a, T: Transaction + 'a> ExecutorNode<'a, T> for Update {
let mut exprs_map = HashMap::with_capacity(self.value_exprs.len());
let mut updated_column_ids = HashSet::with_capacity(self.value_exprs.len());
- for (column, expr) in self.value_exprs.drain(..) {
+ for &(column, expr) in self.value_exprs {
let column = plan_arena.column(column);
let column_id = column
.id()
@@ -186,7 +176,7 @@ impl<'a, T: Transaction + 'a> ExecutorNode<'a, T> for Update {
if primary_key_changed {
let mut state = arena.local_state(plan_arena);
let (transaction, table_codec) = state.transaction_codec_mut();
- transaction.remove_tuple(table_codec, &self.table_name, &old_pk)?;
+ transaction.remove_tuple(table_codec, self.table_name, &old_pk)?;
is_overwrite = false;
}
@@ -205,20 +195,23 @@ impl<'a, T: Transaction + 'a> ExecutorNode<'a, T> for Update {
let old_index = Index::new(index_id, &old_value, index_ty);
let new_index = Index::new(index_id, values, index_ty);
- transaction.del_index(table_codec, &self.table_name, &old_index, &old_pk)?;
- transaction.add_index(table_codec, &self.table_name, new_index, &new_pk)?;
+ transaction.del_index(table_codec, self.table_name, &old_index, &old_pk)?;
+ transaction.add_index(table_codec, self.table_name, new_index, &new_pk)?;
}
tuple.pk = Some(new_pk);
let mut state = arena.local_state(plan_arena);
let (transaction, table_codec) = state.transaction_codec_mut();
- transaction.append_tuple(
- table_codec,
- &self.table_name,
- &tuple,
- &serializers,
- is_overwrite,
- )?;
+ let stamp = if is_overwrite { 0 } else { table_codec.stamp() };
+ table_codec.with_stamp(stamp, |table_codec| {
+ transaction.append_tuple(
+ table_codec,
+ self.table_name,
+ &tuple,
+ &serializers,
+ is_overwrite,
+ )
+ })?;
updated_count += 1;
}
diff --git a/src/execution/dql/aggregate/hash_agg.rs b/src/execution/dql/aggregate/hash_agg.rs
index cb66f900..7e338d41 100644
--- a/src/execution/dql/aggregate/hash_agg.rs
+++ b/src/execution/dql/aggregate/hash_agg.rs
@@ -20,8 +20,9 @@ use crate::execution::{
build_read, ExecArena, ExecId, ExecNode, ExecutionContext, ExecutorNode, ReadExecutor,
};
use crate::planner::operator::aggregate::AggregateOperator;
+use crate::planner::ExprRef;
+use crate::planner::LogicalPlan;
use crate::planner::MetaArena;
-use crate::planner::{ExprRef, LogicalPlan};
use crate::storage::Transaction;
use crate::types::value::DataValue;
use std::collections::hash_map::IntoIter as HashMapIntoIter;
@@ -29,15 +30,15 @@ use std::collections::HashMap;
type HashAggOutput = HashMapIntoIter, Vec>>;
-pub struct HashAggExecutor {
- agg_calls: Vec,
- groupby_exprs: Vec,
+pub struct HashAggExecutor<'a> {
+ agg_calls: &'a [ExprRef],
+ groupby_exprs: &'a [ExprRef],
input: ExecId,
output: Option,
}
-impl<'a, T: Transaction + 'a> ReadExecutor<'a, T> for HashAggExecutor {
- type Input = (AggregateOperator, LogicalPlan);
+impl<'a, T: Transaction + 'a> ReadExecutor<'a, T> for HashAggExecutor<'a> {
+ type Input = (&'a AggregateOperator, &'a LogicalPlan);
fn into_executor(
(
@@ -63,7 +64,7 @@ impl<'a, T: Transaction + 'a> ReadExecutor<'a, T> for HashAggExecutor {
}
}
-impl<'a, T: Transaction + 'a> ExecutorNode<'a, T> for HashAggExecutor {
+impl<'a, T: Transaction + 'a> ExecutorNode<'a, T> for HashAggExecutor<'a> {
fn next_tuple(
&mut self,
arena: &mut ExecArena<'a, T>,
@@ -77,7 +78,7 @@ impl<'a, T: Transaction + 'a> ExecutorNode<'a, T> for HashAggExecutor {
while arena.next_tuple(self.input, plan_arena)? {
let tuple = arena.result_tuple();
group_keys.clear();
- for expr in &self.groupby_exprs {
+ for expr in self.groupby_exprs {
group_keys.push(
plan_arena
.expression(*expr)
@@ -87,10 +88,10 @@ impl<'a, T: Transaction + 'a> ExecutorNode<'a, T> for HashAggExecutor {
}
if let Some(accs) = group_hash_accs.get_mut(group_keys.as_slice()) {
- update_accumulators(accs, &self.agg_calls, tuple, plan_arena)?;
+ update_accumulators(accs, self.agg_calls, tuple, plan_arena)?;
} else {
- let mut accs = create_accumulators(&self.agg_calls, plan_arena)?;
- update_accumulators(&mut accs, &self.agg_calls, tuple, plan_arena)?;
+ let mut accs = create_accumulators(self.agg_calls, plan_arena)?;
+ update_accumulators(&mut accs, self.agg_calls, tuple, plan_arena)?;
group_hash_accs.insert(group_keys.clone(), accs);
}
}
@@ -211,11 +212,11 @@ mod test {
.instantiate(plan)
.find_best(None, &mut plan_arena)?;
- let Operator::Aggregate(op) = plan.operator else {
+ let Operator::Aggregate(op) = &plan.operator else {
unreachable!()
};
let tuples = try_collect(execute_input::<_, HashAggExecutor>(
- (op, plan.childrens.pop_only()),
+ (op, plan.childrens.iter().next().unwrap()),
crate::execution::empty_context(&table_cache, &view_cache, &meta_cache),
plan_arena,
&transaction,
diff --git a/src/execution/dql/aggregate/simple_agg.rs b/src/execution/dql/aggregate/simple_agg.rs
index e07be3c8..7d0e889b 100644
--- a/src/execution/dql/aggregate/simple_agg.rs
+++ b/src/execution/dql/aggregate/simple_agg.rs
@@ -19,17 +19,18 @@ use crate::execution::{
};
use crate::expression::ScalarExpression;
use crate::planner::operator::aggregate::AggregateOperator;
+use crate::planner::ExprRef;
+use crate::planner::LogicalPlan;
use crate::planner::MetaArena;
-use crate::planner::{ExprRef, LogicalPlan};
use crate::storage::Transaction;
-pub struct SimpleAggExecutor {
- agg_calls: Vec,
+pub struct SimpleAggExecutor<'a> {
+ agg_calls: &'a [ExprRef],
input: ExecId,
returned: bool,
}
-impl<'a, T: Transaction + 'a> ReadExecutor<'a, T> for SimpleAggExecutor {
- type Input = (AggregateOperator, LogicalPlan);
+impl<'a, T: Transaction + 'a> ReadExecutor<'a, T> for SimpleAggExecutor<'a> {
+ type Input = (&'a AggregateOperator, &'a LogicalPlan);
fn into_executor(
(AggregateOperator { agg_calls, .. }, input): Self::Input,
@@ -47,7 +48,7 @@ impl<'a, T: Transaction + 'a> ReadExecutor<'a, T> for SimpleAggExecutor {
}
}
-impl<'a, T: Transaction + 'a> ExecutorNode<'a, T> for SimpleAggExecutor {
+impl<'a, T: Transaction + 'a> ExecutorNode<'a, T> for SimpleAggExecutor<'a> {
fn next_tuple(
&mut self,
arena: &mut ExecArena<'a, T>,
@@ -58,7 +59,7 @@ impl<'a, T: Transaction + 'a> ExecutorNode<'a, T> for SimpleAggExecutor {
return Ok(());
}
- let mut accs = create_accumulators(&self.agg_calls, plan_arena)?;
+ let mut accs = create_accumulators(self.agg_calls, plan_arena)?;
while arena.next_tuple(self.input, plan_arena)? {
let tuple = arena.result_tuple();
diff --git a/src/execution/dql/aggregate/stream_agg.rs b/src/execution/dql/aggregate/stream_agg.rs
index 2b8ebb7f..2f238578 100644
--- a/src/execution/dql/aggregate/stream_agg.rs
+++ b/src/execution/dql/aggregate/stream_agg.rs
@@ -20,23 +20,24 @@ use crate::execution::{
build_read, ExecArena, ExecId, ExecNode, ExecutionContext, ExecutorNode, ReadExecutor,
};
use crate::planner::operator::aggregate::AggregateOperator;
+use crate::planner::ExprRef;
+use crate::planner::LogicalPlan;
use crate::planner::MetaArena;
-use crate::planner::{ExprRef, LogicalPlan};
use crate::storage::Transaction;
use crate::types::value::DataValue;
use std::mem;
// The optimizer selects this executor only when equal group keys are contiguous in the input.
-pub struct StreamAggExecutor {
- agg_calls: Vec,
- groupby_exprs: Vec,
+pub struct StreamAggExecutor<'a> {
+ agg_calls: &'a [ExprRef],
+ groupby_exprs: &'a [ExprRef],
group_keys: Option>,
accs: Vec>,
input: ExecId,
}
-impl<'a, T: Transaction + 'a> ReadExecutor<'a, T> for StreamAggExecutor {
- type Input = (AggregateOperator, LogicalPlan);
+impl<'a, T: Transaction + 'a> ReadExecutor<'a, T> for StreamAggExecutor<'a> {
+ type Input = (&'a AggregateOperator, &'a LogicalPlan);
fn into_executor(
(
@@ -63,7 +64,7 @@ impl<'a, T: Transaction + 'a> ReadExecutor<'a, T> for StreamAggExecutor {
}
}
-impl<'a, T: Transaction + 'a> ExecutorNode<'a, T> for StreamAggExecutor {
+impl<'a, T: Transaction + 'a> ExecutorNode<'a, T> for StreamAggExecutor<'a> {
fn next_tuple(
&mut self,
arena: &mut ExecArena<'a, T>,
@@ -86,7 +87,7 @@ impl<'a, T: Transaction + 'a> ExecutorNode<'a, T> for StreamAggExecutor {
let tuple = arena.result_tuple();
let mut group_keys = Vec::with_capacity(self.groupby_exprs.len());
- for expr in &self.groupby_exprs {
+ for expr in self.groupby_exprs {
group_keys.push(
plan_arena
.expression(*expr)
@@ -97,16 +98,16 @@ impl<'a, T: Transaction + 'a> ExecutorNode<'a, T> for StreamAggExecutor {
match &mut self.group_keys {
None => {
- self.accs = create_accumulators(&self.agg_calls, plan_arena)?;
- update_accumulators(&mut self.accs, &self.agg_calls, tuple, plan_arena)?;
+ self.accs = create_accumulators(self.agg_calls, plan_arena)?;
+ update_accumulators(&mut self.accs, self.agg_calls, tuple, plan_arena)?;
self.group_keys = Some(group_keys);
}
Some(current_keys) if current_keys == &group_keys => {
- update_accumulators(&mut self.accs, &self.agg_calls, tuple, plan_arena)?;
+ update_accumulators(&mut self.accs, self.agg_calls, tuple, plan_arena)?;
}
Some(current_keys) => {
- let mut next_accs = create_accumulators(&self.agg_calls, plan_arena)?;
- update_accumulators(&mut next_accs, &self.agg_calls, tuple, plan_arena)?;
+ let mut next_accs = create_accumulators(self.agg_calls, plan_arena)?;
+ update_accumulators(&mut next_accs, self.agg_calls, tuple, plan_arena)?;
mem::swap(current_keys, &mut group_keys);
let current_accs = mem::replace(&mut self.accs, next_accs);
write_aggregate_output(arena.result_tuple_mut(), current_accs, group_keys)?;
@@ -187,7 +188,7 @@ mod tests {
let transaction = storage.transaction()?;
let rows = try_collect(execute_input::<_, StreamAggExecutor>(
- (operator, input),
+ (&operator, &input),
empty_context(&table_cache, &view_cache, &meta_cache),
plan_arena,
&transaction,
@@ -231,7 +232,7 @@ mod tests {
let transaction = storage.transaction()?;
let rows = try_collect(execute_input::<_, StreamAggExecutor>(
- (operator, input),
+ (&operator, &input),
empty_context(&table_cache, &view_cache, &meta_cache),
plan_arena,
&transaction,
diff --git a/src/execution/dql/aggregate/stream_distinct.rs b/src/execution/dql/aggregate/stream_distinct.rs
index 0152418e..87aab16f 100644
--- a/src/execution/dql/aggregate/stream_distinct.rs
+++ b/src/execution/dql/aggregate/stream_distinct.rs
@@ -17,19 +17,20 @@ use crate::execution::{
build_read, ExecArena, ExecId, ExecNode, ExecutionContext, ExecutorNode, ReadExecutor,
};
use crate::planner::operator::aggregate::AggregateOperator;
+use crate::planner::ExprRef;
+use crate::planner::LogicalPlan;
use crate::planner::MetaArena;
-use crate::planner::{ExprRef, LogicalPlan};
use crate::storage::Transaction;
use crate::types::tuple::Tuple;
-pub struct StreamDistinctExecutor {
- groupby_exprs: Vec,
+pub struct StreamDistinctExecutor<'a> {
+ groupby_exprs: &'a [ExprRef],
input: ExecId,
last_keys: Option,
}
-impl<'a, T: Transaction + 'a> ReadExecutor<'a, T> for StreamDistinctExecutor {
- type Input = (AggregateOperator, LogicalPlan);
+impl<'a, T: Transaction + 'a> ReadExecutor<'a, T> for StreamDistinctExecutor<'a> {
+ type Input = (&'a AggregateOperator, &'a LogicalPlan);
fn into_executor(
(op, input): Self::Input,
@@ -40,14 +41,14 @@ impl<'a, T: Transaction + 'a> ReadExecutor<'a, T> for StreamDistinctExecutor {
) -> ExecId {
let input = build_read(arena, plan_arena, input, cache, transaction);
arena.push(ExecNode::StreamDistinct(StreamDistinctExecutor {
- groupby_exprs: op.groupby_exprs,
+ groupby_exprs: &op.groupby_exprs,
input,
last_keys: None,
}))
}
}
-impl<'a, T: Transaction + 'a> ExecutorNode<'a, T> for StreamDistinctExecutor {
+impl<'a, T: Transaction + 'a> ExecutorNode<'a, T> for StreamDistinctExecutor<'a> {
fn next_tuple(
&mut self,
arena: &mut ExecArena<'a, T>,
@@ -63,7 +64,7 @@ impl<'a, T: Transaction + 'a> ExecutorNode<'a, T> for StreamDistinctExecutor {
}
return Ok(());
}
- arena.rewrite(&self.groupby_exprs, plan_arena, None)?;
+ arena.rewrite(self.groupby_exprs, plan_arena, None)?;
if let Some(last_keys) = &mut self.last_keys {
if last_keys.values == arena.result_tuple().values {
@@ -168,14 +169,14 @@ mod tests {
};
let plan = LogicalPlan::new(Operator::Aggregate(agg), Childrens::Only(Box::new(input)));
let plan = optimize_exprs(plan, &mut plan_arena)?;
- let Operator::Aggregate(agg) = plan.operator else {
+ let Operator::Aggregate(agg) = &plan.operator else {
unreachable!()
};
let (table_cache, view_cache, meta_cache, _temp_dir, storage) = build_test_storage()?;
let transaction = storage.transaction()?;
let tuples = try_collect(execute_input::<_, StreamDistinctExecutor>(
- (agg, plan.childrens.pop_only()),
+ (agg, plan.childrens.iter().next().unwrap()),
crate::execution::empty_context(&table_cache, &view_cache, &meta_cache),
plan_arena,
&transaction,
@@ -226,14 +227,14 @@ mod tests {
};
let plan = LogicalPlan::new(Operator::Aggregate(agg), Childrens::Only(Box::new(input)));
let plan = optimize_exprs(plan, &mut plan_arena)?;
- let Operator::Aggregate(agg) = plan.operator else {
+ let Operator::Aggregate(agg) = &plan.operator else {
unreachable!()
};
let (table_cache, view_cache, meta_cache, _temp_dir, storage) = build_test_storage()?;
let transaction = storage.transaction()?;
let tuples = try_collect(execute_input::<_, StreamDistinctExecutor>(
- (agg, plan.childrens.pop_only()),
+ (agg, plan.childrens.iter().next().unwrap()),
crate::execution::empty_context(&table_cache, &view_cache, &meta_cache),
plan_arena,
&transaction,
diff --git a/src/execution/dql/describe.rs b/src/execution/dql/describe.rs
index 4aff2d41..23128b44 100644
--- a/src/execution/dql/describe.rs
+++ b/src/execution/dql/describe.rs
@@ -46,10 +46,10 @@ pub struct Describe {
cursor: usize,
}
-impl From for Describe {
- fn from(op: DescribeOperator) -> Self {
+impl From<&DescribeOperator> for Describe {
+ fn from(op: &DescribeOperator) -> Self {
Describe {
- table_name: op.table_name,
+ table_name: op.table_name.clone(),
columns: None,
cursor: 0,
}
@@ -57,7 +57,7 @@ impl From for Describe {
}
impl<'a, T: Transaction + 'a> ReadExecutor<'a, T> for Describe {
- type Input = Self;
+ type Input = &'a DescribeOperator;
fn into_executor(
input: Self::Input,
@@ -66,7 +66,7 @@ impl<'a, T: Transaction + 'a> ReadExecutor<'a, T> for Describe {
_: ExecutionContext<'_>,
_: &T,
) -> ExecId {
- let executor = input;
+ let executor = Describe::from(input);
arena.push(ExecNode::Describe(executor))
}
}
diff --git a/src/execution/dql/dummy.rs b/src/execution/dql/dummy.rs
index da3e2c73..ae0860ca 100644
--- a/src/execution/dql/dummy.rs
+++ b/src/execution/dql/dummy.rs
@@ -31,16 +31,16 @@ impl Default for Dummy {
}
impl<'a, T: Transaction + 'a> ReadExecutor<'a, T> for Dummy {
- type Input = Self;
+ type Input = ();
fn into_executor(
- input: Self::Input,
+ _: Self::Input,
arena: &mut ExecArena<'a, T>,
_plan_arena: &mut (dyn MetaArena + 'a),
_: ExecutionContext<'_>,
_: &T,
) -> ExecId {
- let executor = input;
+ let executor = Dummy::default();
arena.push(ExecNode::Dummy(executor))
}
}
diff --git a/src/execution/dql/explain.rs b/src/execution/dql/explain.rs
index a97bb8e5..05b113eb 100644
--- a/src/execution/dql/explain.rs
+++ b/src/execution/dql/explain.rs
@@ -20,22 +20,13 @@ use crate::storage::Transaction;
use crate::types::value::{DataValue, Utf8Type};
use crate::types::CharLengthUnits;
-pub struct Explain {
- plan: LogicalPlan,
+pub struct Explain<'a> {
+ plan: &'a LogicalPlan,
emitted: bool,
}
-impl From for Explain {
- fn from(plan: LogicalPlan) -> Self {
- Explain {
- plan,
- emitted: false,
- }
- }
-}
-
-impl<'a, T: Transaction + 'a> ReadExecutor<'a, T> for Explain {
- type Input = Self;
+impl<'a, T: Transaction + 'a> ReadExecutor<'a, T> for Explain<'a> {
+ type Input = &'a LogicalPlan;
fn into_executor(
input: Self::Input,
@@ -44,12 +35,14 @@ impl<'a, T: Transaction + 'a> ReadExecutor<'a, T> for Explain {
_: ExecutionContext<'_>,
_: &T,
) -> ExecId {
- let executor = input;
- arena.push(ExecNode::Explain(executor))
+ arena.push(ExecNode::Explain(Explain {
+ plan: input,
+ emitted: false,
+ }))
}
}
-impl<'a, T: Transaction + 'a> ExecutorNode<'a, T> for Explain {
+impl<'a, T: Transaction + 'a> ExecutorNode<'a, T> for Explain<'a> {
fn next_tuple(
&mut self,
arena: &mut ExecArena<'a, T>,
diff --git a/src/execution/dql/external_sort.rs b/src/execution/dql/external_sort.rs
index 2e7fe6df..2a913426 100644
--- a/src/execution/dql/external_sort.rs
+++ b/src/execution/dql/external_sort.rs
@@ -43,14 +43,14 @@ impl Run {
}
}
-pub struct ExternalSort {
+pub struct ExternalSort<'a> {
rows: Option>,
- sort_fields: Vec,
+ sort_fields: &'a [SortField],
input: ExecId,
}
-impl<'a, T: Transaction + 'a> ReadExecutor<'a, T> for ExternalSort {
- type Input = (SortOperator, LogicalPlan);
+impl<'a, T: Transaction + 'a> ReadExecutor<'a, T> for ExternalSort<'a> {
+ type Input = (&'a SortOperator, &'a LogicalPlan);
fn into_executor(
(SortOperator { sort_fields }, input): Self::Input,
@@ -68,7 +68,7 @@ impl<'a, T: Transaction + 'a> ReadExecutor<'a, T> for ExternalSort {
}
}
-impl<'a, T: Transaction + 'a> ExecutorNode<'a, T> for ExternalSort {
+impl<'a, T: Transaction + 'a> ExecutorNode<'a, T> for ExternalSort<'a> {
fn next_tuple(
&mut self,
arena: &mut ExecArena<'a, T>,
@@ -89,7 +89,7 @@ impl<'a, T: Transaction + 'a> ExecutorNode<'a, T> for ExternalSort {
// cached sort values, which are evaluated once here and spilled through every merge
// pass. By default R <= 1,024 and B is about 1 MiB; one oversized row makes this a
// soft bound.
- let sort_fields = &self.sort_fields;
+ let sort_fields = self.sort_fields;
let mut rows = SpillVec::new().on_flush(move |rows| sort_segment(sort_fields, rows));
let mut runs = Vec::new();
while arena.next_tuple(self.input, plan_arena)? {
@@ -98,7 +98,7 @@ impl<'a, T: Transaction + 'a> ExecutorNode<'a, T> for ExternalSort {
runs.push(Run::new(segment, 1));
}
}
- self.rows = Some(finish_sort(rows, runs, &self.sort_fields, MERGE_FAN_IN)?);
+ self.rows = Some(finish_sort(rows, runs, self.sort_fields, MERGE_FAN_IN)?);
}
}
}
diff --git a/src/execution/dql/filter.rs b/src/execution/dql/filter.rs
index d1cd3c15..3d6c0e51 100644
--- a/src/execution/dql/filter.rs
+++ b/src/execution/dql/filter.rs
@@ -17,8 +17,9 @@ use crate::execution::{
build_read, ExecArena, ExecId, ExecNode, ExecutionContext, ExecutorNode, ReadExecutor,
};
use crate::planner::operator::filter::FilterOperator;
+use crate::planner::ExprRef;
+use crate::planner::LogicalPlan;
use crate::planner::MetaArena;
-use crate::planner::{ExprRef, LogicalPlan};
use crate::storage::Transaction;
pub struct Filter {
predicate: ExprRef,
@@ -26,7 +27,7 @@ pub struct Filter {
}
impl<'a, T: Transaction + 'a> ReadExecutor<'a, T> for Filter {
- type Input = (FilterOperator, LogicalPlan);
+ type Input = (&'a FilterOperator, &'a LogicalPlan);
fn into_executor(
(FilterOperator { predicate, .. }, input): Self::Input,
@@ -36,7 +37,10 @@ impl<'a, T: Transaction + 'a> ReadExecutor<'a, T> for Filter {
transaction: &T,
) -> ExecId {
let input = build_read(arena, plan_arena, input, cache, transaction);
- arena.push(ExecNode::Filter(Filter { predicate, input }))
+ arena.push(ExecNode::Filter(Filter {
+ predicate: *predicate,
+ input,
+ }))
}
}
diff --git a/src/execution/dql/function_scan.rs b/src/execution/dql/function_scan.rs
index eb54d99c..e09455c3 100644
--- a/src/execution/dql/function_scan.rs
+++ b/src/execution/dql/function_scan.rs
@@ -20,22 +20,22 @@ use crate::planner::MetaArena;
use crate::storage::Transaction;
use crate::types::tuple::Tuple;
-pub struct FunctionScan {
- table_function: TableFunction,
+pub struct FunctionScan<'a> {
+ table_function: &'a TableFunction,
iter: Option>>>,
}
-impl From for FunctionScan {
- fn from(op: FunctionScanOperator) -> Self {
+impl<'a> From<&'a FunctionScanOperator> for FunctionScan<'a> {
+ fn from(op: &'a FunctionScanOperator) -> Self {
FunctionScan {
- table_function: op.table_function,
+ table_function: &op.table_function,
iter: None,
}
}
}
-impl<'a, T: Transaction + 'a> ReadExecutor<'a, T> for FunctionScan {
- type Input = Self;
+impl<'a, T: Transaction + 'a> ReadExecutor<'a, T> for FunctionScan<'a> {
+ type Input = &'a FunctionScanOperator;
fn into_executor(
input: Self::Input,
@@ -44,19 +44,19 @@ impl<'a, T: Transaction + 'a> ReadExecutor<'a, T> for FunctionScan {
_: ExecutionContext<'_>,
_: &T,
) -> ExecId {
- let executor = input;
+ let executor = FunctionScan::from(input);
arena.push(ExecNode::FunctionScan(executor))
}
}
-impl<'a, T: Transaction + 'a> ExecutorNode<'a, T> for FunctionScan {
+impl<'a, T: Transaction + 'a> ExecutorNode<'a, T> for FunctionScan<'a> {
fn next_tuple(
&mut self,
arena: &mut ExecArena<'a, T>,
plan_arena: &mut (dyn MetaArena + 'a),
) -> Result<(), DatabaseError> {
if self.iter.is_none() {
- let TableFunction { args, catalog } = &self.table_function;
+ let TableFunction { args, catalog } = self.table_function;
self.iter = Some(catalog.inner.eval(args, plan_arena)?);
}
diff --git a/src/execution/dql/index_scan.rs b/src/execution/dql/index_scan.rs
index 488b6ed5..fcd38bd6 100644
--- a/src/execution/dql/index_scan.rs
+++ b/src/execution/dql/index_scan.rs
@@ -14,63 +14,87 @@
use crate::errors::DatabaseError;
use crate::execution::{ExecArena, ExecId, ExecNode, ExecutionContext, ExecutorNode, ReadExecutor};
-use crate::expression::range_detacher::Range;
+use crate::expression::range_detacher::{IndexRangeColumn, Range, RangeDetacher};
use crate::planner::operator::table_scan::TableScanOperator;
+use crate::planner::operator::SortOption;
use crate::planner::MetaArena;
use crate::storage::{IndexIter, IndexRanges, Iter, Transaction};
-use crate::types::index::{IndexLookup, IndexMetaRef, RuntimeIndexProbe};
-use crate::types::serialize::TupleValueSerializableImpl;
+use crate::types::index::{IndexInfo, IndexLookup, RuntimeIndexProbe};
+use std::borrow::Cow;
pub(crate) struct IndexScan<'a, T: Transaction + 'a> {
- op: Option,
- index_by: IndexMetaRef,
- lookup: Option,
- covered_deserializers: Option>,
- cover_mapping: Option>,
+ op: &'a TableScanOperator,
+ info: &'a IndexInfo,
+ lookup: &'a IndexLookup,
iter: Option>,
}
-impl<'a, T: Transaction + 'a>
- From<(
- TableScanOperator,
- IndexMetaRef,
- IndexLookup,
- Option>,
- Option>,
- )> for IndexScan<'a, T>
-{
- fn from(
- (op, index_by, lookup, covered_deserializers, cover_mapping): (
- TableScanOperator,
- IndexMetaRef,
- IndexLookup,
- Option>,
- Option>,
- ),
+impl<'a, T: Transaction + 'a> IndexScan<'a, T> {
+ pub(crate) fn new(
+ op: &'a TableScanOperator,
+ info: &'a IndexInfo,
+ lookup: &'a IndexLookup,
) -> Self {
- IndexScan {
- op: Some(op),
- index_by,
- lookup: Some(lookup),
- covered_deserializers,
- cover_mapping,
+ Self {
+ op,
+ info,
+ lookup,
iter: None,
}
}
+
+ pub(crate) fn ranges(
+ &self,
+ arena: &mut ExecArena<'a, T>,
+ plan_arena: &mut (dyn MetaArena + 'a),
+ ) -> Result, DatabaseError> {
+ let info = self.info;
+ let mut range = match self.lookup {
+ IndexLookup::Static(range) => Cow::Borrowed(range),
+ IndexLookup::Probe => Cow::Owned(match arena.pop_runtime_probe() {
+ RuntimeIndexProbe::Eq(value) => Range::Eq(value),
+ RuntimeIndexProbe::Scope { min, max } => Range::Scope { min, max },
+ }),
+ };
+
+ if plan_arena.has_bound_params() && range.has_parameter() {
+ let plan_arena = &*plan_arena;
+ range
+ .to_mut()
+ .bind_parameters(&|id| plan_arena.bound_param(id))?;
+ }
+ if let (
+ Some(predicate),
+ SortOption::OrderBy {
+ ignore_prefix_len, ..
+ },
+ ) = (info.residual_predicate, &info.sort_option)
+ {
+ if let Some(specialized) = RangeDetacher::::specialize_range(
+ info.meta,
+ &range,
+ predicate,
+ *ignore_prefix_len,
+ plan_arena,
+ )? {
+ range = Cow::Owned(specialized);
+ }
+ }
+ Ok(IndexRanges::from(range).reversed(info.is_reverse()))
+ }
}
impl<'a, T: Transaction + 'a> ReadExecutor<'a, T> for IndexScan<'a, T> {
- type Input = Self;
+ type Input = (&'a TableScanOperator, &'a IndexInfo, &'a IndexLookup);
fn into_executor(
- input: Self::Input,
+ (op, info, lookup): Self::Input,
arena: &mut ExecArena<'a, T>,
_plan_arena: &mut (dyn MetaArena + 'a),
_: ExecutionContext<'_>,
_: &T,
) -> ExecId {
- let executor = input;
- arena.push(ExecNode::IndexScan(executor))
+ arena.push(ExecNode::IndexScan(IndexScan::new(op, info, lookup)))
}
}
@@ -80,44 +104,35 @@ impl<'a, T: Transaction + 'a> ExecutorNode<'a, T> for IndexScan<'a, T> {
arena: &mut ExecArena<'a, T>,
plan_arena: &mut (dyn MetaArena + 'a),
) -> Result<(), DatabaseError> {
- if self.iter.is_none() {
- let Some(TableScanOperator {
- table_name,
- columns,
- limit,
- with_pk,
- ..
- }) = self.op.take()
- else {
- arena.finish();
- return Ok(());
- };
- let ranges = Self::ranges_from_lookup(
- self.lookup.take().expect("index scan lookup initialized"),
- arena,
- );
- let state = arena.local_state(plan_arena);
- self.iter = Some(state.transaction().read_by_index(
- state.context.table_cache,
- state.plan_arena,
- table_name,
- limit,
- columns,
- self.index_by,
- ranges,
- with_pk,
- self.covered_deserializers.take(),
- self.cover_mapping.take(),
- )?);
- }
+ let iter = match &mut self.iter {
+ Some(iter) => iter,
+ None => {
+ let ranges = self.ranges(arena, plan_arena)?;
+ let TableScanOperator {
+ table_name,
+ columns,
+ limit,
+ with_pk,
+ ..
+ } = self.op;
+ let state = arena.local_state(plan_arena);
+ self.iter.insert(state.transaction().read_by_index(
+ state.context.table_cache,
+ state.plan_arena,
+ table_name.clone(),
+ *limit,
+ columns,
+ self.info.meta,
+ ranges,
+ *with_pk,
+ self.info.covered_deserializers.as_deref(),
+ self.info.cover_mapping.as_deref(),
+ )?)
+ }
+ };
let state = arena.local_state(plan_arena);
- if self
- .iter
- .as_mut()
- .expect("index scan iterator initialized")
- .next_tuple_into(state.table_codec, &mut state.result.tuple)?
- {
+ if iter.next_tuple_into(state.table_codec, &mut state.result.tuple)? {
arena.resume();
} else {
arena.finish();
@@ -125,16 +140,3 @@ impl<'a, T: Transaction + 'a> ExecutorNode<'a, T> for IndexScan<'a, T> {
Ok(())
}
}
-
-impl<'a, T: Transaction + 'a> IndexScan<'a, T> {
- fn ranges_from_lookup(lookup: IndexLookup, arena: &mut ExecArena<'a, T>) -> IndexRanges {
- match lookup {
- IndexLookup::Static(Range::SortedRanges(ranges)) => ranges.into(),
- IndexLookup::Static(range) => range.into(),
- IndexLookup::Probe => match arena.pop_runtime_probe() {
- RuntimeIndexProbe::Eq(value) => Range::Eq(value).into(),
- RuntimeIndexProbe::Scope { min, max } => Range::Scope { min, max }.into(),
- },
- }
- }
-}
diff --git a/src/execution/dql/join/hash_join.rs b/src/execution/dql/join/hash_join.rs
index d44d1d37..222ffac8 100644
--- a/src/execution/dql/join/hash_join.rs
+++ b/src/execution/dql/join/hash_join.rs
@@ -26,8 +26,9 @@ use crate::execution::{
build_read, ExecArena, ExecId, ExecNode, ExecutionContext, ExecutorNode, ReadExecutor,
};
use crate::planner::operator::join::{JoinCondition, JoinOperator, JoinType};
+use crate::planner::ExprRef;
+use crate::planner::LogicalPlan;
use crate::planner::MetaArena;
-use crate::planner::{ExprRef, LogicalPlan};
use crate::storage::Transaction;
use crate::types::tuple::Tuple;
use crate::types::value::DataValue;
@@ -43,8 +44,6 @@ pub struct HashJoin {
filter: Option,
left_schema_len: usize,
right_schema_len: usize,
- left_input_plan: LogicalPlan,
- right_input_plan: LogicalPlan,
left_input: ExecId,
right_input: ExecId,
bump: Box,
@@ -66,18 +65,19 @@ enum HashJoinState {
End,
}
-impl From<(JoinOperator, LogicalPlan, LogicalPlan)> for HashJoin {
- fn from(
- (JoinOperator { on, join_type, .. }, left_input, right_input): (
- JoinOperator,
- LogicalPlan,
- LogicalPlan,
- ),
+impl HashJoin {
+ fn new(
+ JoinOperator { on, join_type, .. }: &JoinOperator,
+ left_schema_len: usize,
+ right_schema_len: usize,
+ left_input: ExecId,
+ right_input: ExecId,
) -> Self {
let ((on_left_keys, on_right_keys), filter_expr) = match on {
- JoinCondition::On { on, filter } => (on.into_iter().unzip(), filter),
+ JoinCondition::On { on, filter } => (on.iter().copied().unzip(), *filter),
JoinCondition::None => ((vec![], vec![]), None),
};
+ let join_type = *join_type;
let init_error = if join_type == JoinType::Cross {
Some(DatabaseError::UnsupportedStmt(
@@ -97,12 +97,10 @@ impl From<(JoinOperator, LogicalPlan, LogicalPlan)> for HashJoin {
on_left_keys,
on_right_keys,
filter: filter_expr,
- left_schema_len: 0,
- right_schema_len: 0,
- left_input_plan: left_input,
- right_input_plan: right_input,
- left_input: 0,
- right_input: 0,
+ left_schema_len,
+ right_schema_len,
+ left_input,
+ right_input,
bump: Box::::default(),
init_error,
}
@@ -230,33 +228,25 @@ pub(crate) struct BuildState {
}
impl<'a, T: Transaction + 'a> ReadExecutor<'a, T> for HashJoin {
- type Input = Self;
+ type Input = (&'a JoinOperator, &'a LogicalPlan, &'a LogicalPlan);
fn into_executor(
- input: Self::Input,
+ (op, left_plan, right_plan): Self::Input,
arena: &mut ExecArena<'a, T>,
plan_arena: &mut (dyn MetaArena + 'a),
cache: ExecutionContext<'_>,
transaction: &T,
) -> ExecId {
- let mut executor = input;
- let left_schema_len = executor.left_input_plan.output_schema(plan_arena).len();
- let right_schema_len = executor.right_input_plan.output_schema(plan_arena).len();
- executor.left_schema_len = left_schema_len;
- executor.right_schema_len = right_schema_len;
- executor.left_input = build_read(
- arena,
- plan_arena,
- executor.left_input_plan.take(),
- cache,
- transaction,
- );
- executor.right_input = build_read(
- arena,
- plan_arena,
- executor.right_input_plan.take(),
- cache,
- transaction,
+ let left_schema_len = left_plan.read_schema().len();
+ let right_schema_len = right_plan.read_schema().len();
+ let left_input = build_read(arena, plan_arena, left_plan, cache, transaction);
+ let right_input = build_read(arena, plan_arena, right_plan, cache, transaction);
+ let executor = HashJoin::new(
+ op,
+ left_schema_len,
+ right_schema_len,
+ left_input,
+ right_input,
);
arena.push(ExecNode::HashJoin(executor))
}
@@ -524,9 +514,11 @@ mod test {
let Operator::Join(op) = plan.operator else {
unreachable!()
};
- let (left, right) = plan.childrens.pop_twins();
- let executor = crate::execution::execute(
- HashJoin::from((op, left, right)),
+ let (mut left, mut right) = plan.childrens.pop_twins();
+ left.populate_output_schema_recursive(&mut plan_arena);
+ right.populate_output_schema_recursive(&mut plan_arena);
+ let executor = crate::execution::execute_input::<_, HashJoin>(
+ (&op, &left, &right),
crate::execution::empty_context(&table_cache, &view_cache, &meta_cache),
plan_arena,
&transaction,
@@ -582,11 +574,12 @@ mod test {
let Operator::Join(op) = plan.operator else {
unreachable!()
};
- let (left, right) = plan.childrens.pop_twins();
+ let (mut left, mut right) = plan.childrens.pop_twins();
+ left.populate_output_schema_recursive(&mut plan_arena);
+ right.populate_output_schema_recursive(&mut plan_arena);
{
- let executor = HashJoin::from((op.clone(), left.clone(), right.clone()));
- let tuples = try_collect(crate::execution::execute(
- executor,
+ let tuples = try_collect(crate::execution::execute_input::<_, HashJoin>(
+ (&op, &left, &right),
crate::execution::empty_context(&table_cache, &view_cache, &meta_cache),
plan_arena,
&transaction,
@@ -646,9 +639,11 @@ mod test {
let Operator::Join(op) = plan.operator else {
unreachable!()
};
- let (left, right) = plan.childrens.pop_twins();
- let executor = crate::execution::execute(
- HashJoin::from((op, left, right)),
+ let (mut left, mut right) = plan.childrens.pop_twins();
+ left.populate_output_schema_recursive(&mut plan_arena);
+ right.populate_output_schema_recursive(&mut plan_arena);
+ let executor = crate::execution::execute_input::<_, HashJoin>(
+ (&op, &left, &right),
crate::execution::empty_context(&table_cache, &view_cache, &meta_cache),
plan_arena,
&transaction,
@@ -753,9 +748,11 @@ mod test {
let Operator::Join(op) = plan.operator else {
unreachable!()
};
- let (left, right) = plan.childrens.pop_twins();
- let executor = crate::execution::execute(
- HashJoin::from((op, left, right)),
+ let (mut left, mut right) = plan.childrens.pop_twins();
+ left.populate_output_schema_recursive(&mut plan_arena);
+ right.populate_output_schema_recursive(&mut plan_arena);
+ let executor = crate::execution::execute_input::<_, HashJoin>(
+ (&op, &left, &right),
crate::execution::empty_context(&table_cache, &view_cache, &meta_cache),
plan_arena,
&transaction,
@@ -806,9 +803,11 @@ mod test {
let Operator::Join(op) = plan.operator else {
unreachable!()
};
- let (left, right) = plan.childrens.pop_twins();
- let executor = crate::execution::execute(
- HashJoin::from((op, left, right)),
+ let (mut left, mut right) = plan.childrens.pop_twins();
+ left.populate_output_schema_recursive(&mut plan_arena);
+ right.populate_output_schema_recursive(&mut plan_arena);
+ let executor = crate::execution::execute_input::<_, HashJoin>(
+ (&op, &left, &right),
crate::execution::empty_context(&table_cache, &view_cache, &meta_cache),
plan_arena,
&transaction,
diff --git a/src/execution/dql/join/nested_loop_join.rs b/src/execution/dql/join/nested_loop_join.rs
index a0aa111a..5c9385a2 100644
--- a/src/execution/dql/join/nested_loop_join.rs
+++ b/src/execution/dql/join/nested_loop_join.rs
@@ -15,6 +15,7 @@
//! Defines the nested loop join executor, it supports [`JoinType::Inner`], [`JoinType::LeftOuter`],
//! [`JoinType::RightOuter`], [`JoinType::Cross`], [`JoinType::Full`].
+use crate::planner::LogicalPlan;
use crate::planner::MetaArena;
use crate::errors::DatabaseError;
@@ -24,7 +25,7 @@ use crate::execution::{
};
use crate::iter_ext::Itertools;
use crate::planner::operator::join::{JoinCondition, JoinOperator, JoinType};
-use crate::planner::{ExprRef, LogicalPlan};
+use crate::planner::ExprRef;
use crate::storage::Transaction;
use crate::types::tuple::{SplitTupleRef, Tuple};
use crate::types::value::DataValue;
@@ -74,9 +75,8 @@ impl EqualCondition {
/// | Right/RightSemi/RightAnti/Full | left | right |
/// |--------------------------------|----------------|----------------|
/// | Full | left | right |
-pub struct NestedLoopJoin {
- left_input_plan: LogicalPlan,
- right_input_plan: LogicalPlan,
+pub struct NestedLoopJoin<'a> {
+ right_input_plan: &'a LogicalPlan,
ty: JoinType,
filter: Option,
eq_cond: EqualCondition,
@@ -109,81 +109,52 @@ struct ActiveLeftState {
first_matches: Vec,
}
-impl From<(JoinOperator, LogicalPlan, LogicalPlan)> for NestedLoopJoin {
- fn from(
- (JoinOperator { on, join_type, .. }, left_input, right_input): (
- JoinOperator,
- LogicalPlan,
- LogicalPlan,
- ),
- ) -> Self {
+impl<'a, T: Transaction + 'a> ReadExecutor<'a, T> for NestedLoopJoin<'a> {
+ type Input = (&'a JoinOperator, &'a LogicalPlan, &'a LogicalPlan);
+
+ fn into_executor(
+ (JoinOperator { on, join_type, .. }, left_plan, right_plan): Self::Input,
+ arena: &mut ExecArena<'a, T>,
+ plan_arena: &mut (dyn MetaArena + 'a),
+ cache: ExecutionContext<'_>,
+ transaction: &T,
+ ) -> ExecId {
let ((mut on_left_keys, mut on_right_keys), filter) = match on {
- JoinCondition::On { on, filter } => (on.into_iter().unzip(), filter),
+ JoinCondition::On { on, filter } => (on.iter().copied().unzip(), *filter),
JoinCondition::None => ((vec![], vec![]), None),
};
+ let join_type = *join_type;
- let (mut left_input, mut right_input) = (left_input, right_input);
+ let (mut left_plan, mut right_plan) = (left_plan, right_plan);
if matches!(join_type, JoinType::RightOuter) {
- std::mem::swap(&mut left_input, &mut right_input);
+ std::mem::swap(&mut left_plan, &mut right_plan);
std::mem::swap(&mut on_left_keys, &mut on_right_keys);
}
let eq_cond = EqualCondition {
on_left_keys,
on_right_keys,
- left_len: 0,
- right_len: 0,
+ left_len: left_plan.read_schema().len(),
+ right_len: right_plan.read_schema().len(),
};
- NestedLoopJoin {
- left_input_plan: left_input,
- right_input_plan: right_input,
+ let left_input = build_read(arena, plan_arena, left_plan, cache, transaction);
+ let right_pos = arena.nodes.position();
+ build_read(arena, plan_arena, right_plan, cache, transaction);
+ arena.push(ExecNode::NestedLoopJoin(NestedLoopJoin {
+ right_input_plan: right_plan,
ty: join_type,
filter,
eq_cond,
- left_input: 0,
- right_pos: 0,
+ left_input,
+ right_pos,
state: NestedLoopJoinState::PullLeft { right_bitmap: None },
- }
+ }))
}
}
-impl<'a, T: Transaction + 'a> ReadExecutor<'a, T> for NestedLoopJoin {
- type Input = Self;
-
- fn into_executor(
- input: Self::Input,
- arena: &mut ExecArena<'a, T>,
- plan_arena: &mut (dyn MetaArena + 'a),
- cache: ExecutionContext<'_>,
- transaction: &T,
- ) -> ExecId {
- let mut executor = input;
- let left_len = executor.left_input_plan.output_schema(plan_arena).len();
- let right_len = executor.right_input_plan.output_schema(plan_arena).len();
- executor.eq_cond.left_len = left_len;
- executor.eq_cond.right_len = right_len;
- executor.left_input = build_read(
- arena,
- plan_arena,
- executor.left_input_plan.take(),
- cache,
- transaction,
- );
- executor.right_pos = arena.nodes.position();
- build_read(
- arena,
- plan_arena,
- executor.right_input_plan.clone(),
- cache,
- transaction,
- );
- arena.push(ExecNode::NestedLoopJoin(executor))
- }
-}
-
-impl<'a, T: Transaction + 'a> ExecutorNode<'a, T> for NestedLoopJoin {
+impl<'a, T: Transaction + 'a> ExecutorNode<'a, T> for NestedLoopJoin<'a> {
fn next_tuple(
&mut self,
arena: &mut ExecArena<'a, T>,
@@ -396,8 +367,8 @@ impl<'a, T: Transaction + 'a> ExecutorNode<'a, T> for NestedLoopJoin {
}
}
-impl NestedLoopJoin {
- fn build_right_input<'a, T: Transaction + 'a>(
+impl<'a> NestedLoopJoin<'a> {
+ fn build_right_input(
&mut self,
arena: &mut ExecArena<'a, T>,
plan_arena: &mut (dyn MetaArena + 'a),
@@ -406,13 +377,7 @@ impl NestedLoopJoin {
let transaction = arena.transaction();
// The same right-hand plan rebuilds the same slots, including nested joins.
arena.nodes.seek(self.right_pos);
- build_read(
- arena,
- plan_arena,
- self.right_input_plan.clone(),
- cache,
- transaction,
- )
+ build_read(arena, plan_arena, self.right_input_plan, cache, transaction)
}
/// Emit a tuple according to the join type.
@@ -475,7 +440,7 @@ mod test {
use crate::planner::operator::values::ValuesOperator;
use crate::planner::operator::Operator;
use crate::planner::test::PlanArenaTestExt;
- use crate::planner::Childrens;
+ use crate::planner::{Childrens, LogicalPlan};
use crate::storage::rocksdb::RocksStorage;
use crate::storage::Storage;
use crate::types::evaluator::binary_create;
@@ -673,11 +638,12 @@ mod test {
},
)
};
- let plan = cross(left.clone(), cross(left, right));
+ let mut plan = cross(left.clone(), cross(left, right));
+ plan.populate_output_schema_recursive(&mut plan_arena);
let context = crate::execution::empty_context(&table_cache, &view_cache, &meta_cache);
let mut arena = ExecArena::new();
arena.init_context(context, &transaction);
- let root = build_read(&mut arena, &mut plan_arena, plan, context, &transaction);
+ let root = build_read(&mut arena, &mut plan_arena, &plan, context, &transaction);
let count = arena.nodes.items.len();
let address = arena.nodes.items.as_ptr();
assert_eq!(count, 5);
@@ -723,9 +689,11 @@ mod test {
let Operator::Join(op) = plan.operator else {
unreachable!()
};
- let (left, right) = plan.childrens.pop_twins();
- let executor = crate::execution::execute(
- NestedLoopJoin::from((op, left, right)),
+ let (mut left, mut right) = plan.childrens.pop_twins();
+ left.populate_output_schema_recursive(&mut plan_arena);
+ right.populate_output_schema_recursive(&mut plan_arena);
+ let executor = crate::execution::execute_input::<_, NestedLoopJoin>(
+ (&op, &left, &right),
crate::execution::empty_context(&table_cache, &view_cache, &meta_cache),
plan_arena,
&transaction,
@@ -776,9 +744,11 @@ mod test {
let Operator::Join(op) = plan.operator else {
unreachable!()
};
- let (left, right) = plan.childrens.pop_twins();
- let executor = crate::execution::execute(
- NestedLoopJoin::from((op, left, right)),
+ let (mut left, mut right) = plan.childrens.pop_twins();
+ left.populate_output_schema_recursive(&mut plan_arena);
+ right.populate_output_schema_recursive(&mut plan_arena);
+ let executor = crate::execution::execute_input::<_, NestedLoopJoin>(
+ (&op, &left, &right),
crate::execution::empty_context(&table_cache, &view_cache, &meta_cache),
plan_arena,
&transaction,
@@ -858,9 +828,11 @@ mod test {
let Operator::Join(op) = plan.operator else {
unreachable!()
};
- let (left, right) = plan.childrens.pop_twins();
- let executor = crate::execution::execute(
- NestedLoopJoin::from((op, left, right)),
+ let (mut left, mut right) = plan.childrens.pop_twins();
+ left.populate_output_schema_recursive(&mut plan_arena);
+ right.populate_output_schema_recursive(&mut plan_arena);
+ let executor = crate::execution::execute_input::<_, NestedLoopJoin>(
+ (&op, &left, &right),
crate::execution::empty_context(&table_cache, &view_cache, &meta_cache),
plan_arena,
&transaction,
@@ -911,9 +883,11 @@ mod test {
let Operator::Join(op) = plan.operator else {
unreachable!()
};
- let (left, right) = plan.childrens.pop_twins();
- let executor = crate::execution::execute(
- NestedLoopJoin::from((op, left, right)),
+ let (mut left, mut right) = plan.childrens.pop_twins();
+ left.populate_output_schema_recursive(&mut plan_arena);
+ right.populate_output_schema_recursive(&mut plan_arena);
+ let executor = crate::execution::execute_input::<_, NestedLoopJoin>(
+ (&op, &left, &right),
crate::execution::empty_context(&table_cache, &view_cache, &meta_cache),
plan_arena,
&transaction,
@@ -979,9 +953,11 @@ mod test {
let Operator::Join(op) = plan.operator else {
unreachable!()
};
- let (left, right) = plan.childrens.pop_twins();
- let executor = crate::execution::execute(
- NestedLoopJoin::from((op, left, right)),
+ let (mut left, mut right) = plan.childrens.pop_twins();
+ left.populate_output_schema_recursive(&mut plan_arena);
+ right.populate_output_schema_recursive(&mut plan_arena);
+ let executor = crate::execution::execute_input::<_, NestedLoopJoin>(
+ (&op, &left, &right),
crate::execution::empty_context(&table_cache, &view_cache, &meta_cache),
plan_arena,
&transaction,
@@ -1022,9 +998,11 @@ mod test {
let Operator::Join(op) = plan.operator else {
unreachable!()
};
- let (left, right) = plan.childrens.pop_twins();
- let executor = crate::execution::execute(
- NestedLoopJoin::from((op, left, right)),
+ let (mut left, mut right) = plan.childrens.pop_twins();
+ left.populate_output_schema_recursive(&mut plan_arena);
+ right.populate_output_schema_recursive(&mut plan_arena);
+ let executor = crate::execution::execute_input::<_, NestedLoopJoin>(
+ (&op, &left, &right),
crate::execution::empty_context(&table_cache, &view_cache, &meta_cache),
plan_arena,
&transaction,
@@ -1099,9 +1077,11 @@ mod test {
let Operator::Join(op) = plan.operator else {
unreachable!()
};
- let (left, right) = plan.childrens.pop_twins();
- let executor = crate::execution::execute(
- NestedLoopJoin::from((op, left, right)),
+ let (mut left, mut right) = plan.childrens.pop_twins();
+ left.populate_output_schema_recursive(&mut plan_arena);
+ right.populate_output_schema_recursive(&mut plan_arena);
+ let executor = crate::execution::execute_input::<_, NestedLoopJoin>(
+ (&op, &left, &right),
crate::execution::empty_context(&table_cache, &view_cache, &meta_cache),
plan_arena,
&transaction,
@@ -1256,9 +1236,11 @@ mod test {
let Operator::Join(op) = plan.operator else {
unreachable!()
};
- let (left, right) = plan.childrens.pop_twins();
- let executor = crate::execution::execute(
- NestedLoopJoin::from((op, left, right)),
+ let (mut left, mut right) = plan.childrens.pop_twins();
+ left.populate_output_schema_recursive(&mut plan_arena);
+ right.populate_output_schema_recursive(&mut plan_arena);
+ let executor = crate::execution::execute_input::<_, NestedLoopJoin>(
+ (&op, &left, &right),
crate::execution::empty_context(&table_cache, &view_cache, &meta_cache),
plan_arena,
&transaction,
diff --git a/src/execution/dql/limit.rs b/src/execution/dql/limit.rs
index 5ab6a1b8..9c9f6874 100644
--- a/src/execution/dql/limit.rs
+++ b/src/execution/dql/limit.rs
@@ -29,7 +29,7 @@ pub struct Limit {
}
impl<'a, T: Transaction + 'a> ReadExecutor<'a, T> for Limit {
- type Input = (LimitOperator, LogicalPlan);
+ type Input = (&'a LimitOperator, &'a LogicalPlan);
fn into_executor(
(LimitOperator { offset, limit }, input): Self::Input,
@@ -40,8 +40,8 @@ impl<'a, T: Transaction + 'a> ReadExecutor<'a, T> for Limit {
) -> ExecId {
let input = build_read(arena, plan_arena, input, cache, transaction);
arena.push(ExecNode::Limit(Limit {
- offset,
- limit,
+ offset: *offset,
+ limit: *limit,
input,
skipped: 0,
emitted: 0,
diff --git a/src/execution/dql/mark_apply.rs b/src/execution/dql/mark_apply.rs
index 3670c9a5..6f11b25a 100644
--- a/src/execution/dql/mark_apply.rs
+++ b/src/execution/dql/mark_apply.rs
@@ -32,17 +32,17 @@ enum QuantifiedPredicateOutcome {
Skip,
}
-pub struct MarkApply {
- op: MarkApplyOperator,
- right_input_plan: LogicalPlan,
+pub struct MarkApply<'a> {
+ op: &'a MarkApplyOperator,
+ right_input_plan: &'a LogicalPlan,
left_input: ExecId,
right_pos: ExecId,
// Retain a streaming inner input across next_tuple calls, not its result rows.
join_input: Option<(ExecId, Tuple)>,
}
-impl<'a, T: Transaction + 'a> ReadExecutor<'a, T> for MarkApply {
- type Input = (MarkApplyOperator, LogicalPlan, LogicalPlan);
+impl<'a, T: Transaction + 'a> ReadExecutor<'a, T> for MarkApply<'a> {
+ type Input = (&'a MarkApplyOperator, &'a LogicalPlan, &'a LogicalPlan);
fn into_executor(
(op, left_input, right_input): Self::Input,
@@ -53,7 +53,7 @@ impl<'a, T: Transaction + 'a> ReadExecutor<'a, T> for MarkApply {
) -> ExecId {
let left_input = build_read(arena, plan_arena, left_input, cache, transaction);
let right_pos = arena.nodes.position();
- build_read(arena, plan_arena, right_input.clone(), cache, transaction);
+ build_read(arena, plan_arena, right_input, cache, transaction);
arena.push(ExecNode::MarkApply(Self {
op,
right_input_plan: right_input,
@@ -64,7 +64,7 @@ impl<'a, T: Transaction + 'a> ReadExecutor<'a, T> for MarkApply {
}
}
-impl<'a, T: Transaction + 'a> ExecutorNode<'a, T> for MarkApply {
+impl<'a, T: Transaction + 'a> ExecutorNode<'a, T> for MarkApply<'a> {
fn next_tuple(
&mut self,
arena: &mut ExecArena<'a, T>,
@@ -87,8 +87,8 @@ impl<'a, T: Transaction + 'a> ExecutorNode<'a, T> for MarkApply {
}
}
-impl MarkApply {
- fn next_join_tuple<'a, T: Transaction + 'a>(
+impl<'a> MarkApply<'a> {
+ fn next_join_tuple(
&mut self,
arena: &mut ExecArena<'a, T>,
plan_arena: &mut (dyn MetaArena + 'a),
@@ -137,7 +137,7 @@ impl MarkApply {
}
}
- fn build_right_input<'a, T: Transaction + 'a>(
+ fn build_right_input(
&self,
arena: &mut ExecArena<'a, T>,
plan_arena: &mut (dyn MetaArena + 'a),
@@ -150,7 +150,7 @@ impl MarkApply {
build_read(
arena,
plan_arena,
- self.right_input_plan.clone(),
+ self.right_input_plan,
arena.context(),
arena.transaction(),
)
@@ -172,7 +172,7 @@ impl MarkApply {
.transpose()
}
- fn mark_value<'a, T: Transaction + 'a>(
+ fn mark_value(
&self,
arena: &mut ExecArena<'a, T>,
plan_arena: &mut (dyn MetaArena + 'a),
@@ -253,7 +253,7 @@ impl MarkApply {
}
}
- fn scan_quantified_right_input<'a, T: Transaction + 'a>(
+ fn scan_quantified_right_input(
&self,
arena: &mut ExecArena<'a, T>,
plan_arena: &mut (dyn MetaArena + 'a),
@@ -319,7 +319,7 @@ impl MarkApply {
right_tuple: &Tuple,
plan_arena: &(dyn MetaArena + '_),
) -> Result {
- match self.eval_predicates(left_tuple, &right_tuple, plan_arena)? {
+ match self.eval_predicates(left_tuple, right_tuple, plan_arena)? {
Some(DataValue::Boolean(true)) => Ok(QuantifiedPredicateOutcome::True),
Some(DataValue::Boolean(false)) => Ok(QuantifiedPredicateOutcome::False),
Some(DataValue::Null) => Ok(QuantifiedPredicateOutcome::Null),
@@ -510,7 +510,7 @@ mod tests {
let mut arena = ExecArena::new();
arena.init_context(cache, &transaction);
let root = >::into_executor(
- (op, left, right),
+ (&op, &left, &right),
&mut arena,
&mut plan_arena,
cache,
@@ -560,7 +560,7 @@ mod tests {
let (table_cache, view_cache, meta_cache, _temp_dir, storage) = build_test_storage()?;
let transaction = storage.transaction()?;
let mut executor = execute_input::<_, MarkApply>(
- (op, left, right),
+ (&op, &left, &right),
crate::execution::empty_context(&table_cache, &view_cache, &meta_cache),
plan_arena,
&transaction,
@@ -591,6 +591,8 @@ mod tests {
let right_column = right.output_schema(&mut plan_arena)[0];
let predicate = build_equality_predicate(&mut plan_arena, left_column, 0, right_column, 1)?;
+ let op =
+ MarkApplyOperator::new_exists(build_marker_column(&mut plan_arena), vec![predicate]);
let (table_cache, view_cache, meta_cache, _temp_dir, storage) = build_test_storage()?;
let transaction = storage.transaction()?;
@@ -598,14 +600,7 @@ mod tests {
let mut arena = ExecArena::new();
arena.init_context(context, &transaction);
let root = >::into_executor(
- (
- MarkApplyOperator::new_exists(
- build_marker_column(&mut plan_arena),
- vec![predicate],
- ),
- left,
- right,
- ),
+ (&op, &left, &right),
&mut arena,
&mut plan_arena,
context,
@@ -656,18 +651,13 @@ mod tests {
let right_column = right.output_schema(&mut plan_arena)[0];
let predicate = build_equality_predicate(&mut plan_arena, left_column, 0, right_column, 1)?;
+ let op =
+ MarkApplyOperator::new_exists(build_marker_column(&mut plan_arena), vec![predicate]);
let (table_cache, view_cache, meta_cache, _temp_dir, storage) = build_test_storage()?;
let transaction = storage.transaction()?;
let tuples = try_collect(execute_input::<_, MarkApply>(
- (
- MarkApplyOperator::new_exists(
- build_marker_column(&mut plan_arena),
- vec![predicate],
- ),
- left,
- right,
- ),
+ (&op, &left, &right),
crate::execution::empty_context(&table_cache, &view_cache, &meta_cache),
plan_arena,
&transaction,
@@ -741,8 +731,8 @@ mod tests {
);
let exec: MarkApply = MarkApply {
- op,
- right_input_plan: right,
+ op: &op,
+ right_input_plan: &right,
left_input: 0,
right_pos: arena.nodes.position(),
join_input: None,
@@ -794,8 +784,8 @@ mod tests {
);
let exec: MarkApply = MarkApply {
- op,
- right_input_plan: right,
+ op: &op,
+ right_input_plan: &right,
left_input: 0,
right_pos: arena.nodes.position(),
join_input: None,
@@ -847,8 +837,8 @@ mod tests {
);
let exec: MarkApply = MarkApply {
- op,
- right_input_plan: right,
+ op: &op,
+ right_input_plan: &right,
left_input: 0,
right_pos: arena.nodes.position(),
join_input: None,
@@ -893,9 +883,9 @@ mod tests {
let transaction = storage.transaction()?;
let tuples = try_collect(execute_input::<_, MarkApply>(
(
- MarkApplyOperator::new_in(build_marker_column(&mut plan_arena), vec![predicate]),
- left,
- right,
+ &MarkApplyOperator::new_in(build_marker_column(&mut plan_arena), vec![predicate]),
+ &left,
+ &right,
),
crate::execution::empty_context(&table_cache, &view_cache, &meta_cache),
plan_arena,
@@ -941,9 +931,9 @@ mod tests {
let transaction = storage.transaction()?;
let tuples = try_collect(execute_input::<_, MarkApply>(
(
- MarkApplyOperator::new_in(build_marker_column(&mut plan_arena), vec![predicate]),
- left,
- right,
+ &MarkApplyOperator::new_in(build_marker_column(&mut plan_arena), vec![predicate]),
+ &left,
+ &right,
),
crate::execution::empty_context(&table_cache, &view_cache, &meta_cache),
plan_arena,
@@ -1009,12 +999,12 @@ mod tests {
let transaction = storage.transaction()?;
let tuples = try_collect(execute_input::<_, MarkApply>(
(
- MarkApplyOperator::new_in(
+ &MarkApplyOperator::new_in(
build_marker_column(&mut plan_arena),
vec![probe_predicate, correlated_predicate],
),
- left,
- right,
+ &left,
+ &right,
),
crate::execution::empty_context(&table_cache, &view_cache, &meta_cache),
plan_arena,
diff --git a/src/execution/dql/projection.rs b/src/execution/dql/projection.rs
index 7817dd88..8a98d768 100644
--- a/src/execution/dql/projection.rs
+++ b/src/execution/dql/projection.rs
@@ -17,17 +17,18 @@ use crate::execution::{
build_read, ExecArena, ExecId, ExecNode, ExecutionContext, ExecutorNode, ReadExecutor,
};
use crate::planner::operator::project::ProjectOperator;
+use crate::planner::ExprRef;
+use crate::planner::LogicalPlan;
use crate::planner::MetaArena;
-use crate::planner::{ExprRef, LogicalPlan};
use crate::storage::Transaction;
-pub struct Projection {
- exprs: Vec,
+pub struct Projection<'a> {
+ exprs: &'a [ExprRef],
input: ExecId,
}
-impl<'a, T: Transaction + 'a> ReadExecutor<'a, T> for Projection {
- type Input = (ProjectOperator, LogicalPlan);
+impl<'a, T: Transaction + 'a> ReadExecutor<'a, T> for Projection<'a> {
+ type Input = (&'a ProjectOperator, &'a LogicalPlan);
fn into_executor(
(ProjectOperator { exprs }, input): Self::Input,
@@ -41,7 +42,7 @@ impl<'a, T: Transaction + 'a> ReadExecutor<'a, T> for Projection {
}
}
-impl<'a, T: Transaction + 'a> ExecutorNode<'a, T> for Projection {
+impl<'a, T: Transaction + 'a> ExecutorNode<'a, T> for Projection<'a> {
fn next_tuple(
&mut self,
arena: &mut ExecArena<'a, T>,
@@ -52,7 +53,7 @@ impl<'a, T: Transaction + 'a> ExecutorNode<'a, T> for Projection {
return Ok(());
}
- arena.rewrite(&self.exprs, plan_arena, None)?;
+ arena.rewrite(self.exprs, plan_arena, None)?;
arena.resume();
Ok(())
}
diff --git a/src/execution/dql/recursive_cte.rs b/src/execution/dql/recursive_cte.rs
index 519075dd..a59441f1 100644
--- a/src/execution/dql/recursive_cte.rs
+++ b/src/execution/dql/recursive_cte.rs
@@ -175,7 +175,7 @@ enum RecursivePhase {
}
pub struct RecursiveCte<'a, T: Transaction + 'a> {
- recursive_plan: LogicalPlan,
+ recursive_plan: &'a LogicalPlan,
anchor_input: ExecId,
recursive_arena: ExecArena<'a, T>,
recursive_root: ExecId,
@@ -187,7 +187,7 @@ pub struct RecursiveCte<'a, T: Transaction + 'a> {
impl<'a, T: Transaction + 'a> RecursiveCte<'a, T> {
fn new(
anchor_input: ExecId,
- recursive_plan: LogicalPlan,
+ recursive_plan: &'a LogicalPlan,
recursive_arena: ExecArena<'a, T>,
) -> Self {
Self {
@@ -216,7 +216,7 @@ impl<'a, T: Transaction + 'a> RecursiveCte<'a, T> {
self.recursive_root = build_read(
&mut self.recursive_arena,
plan_arena,
- self.recursive_plan.clone(),
+ self.recursive_plan,
cache,
transaction,
);
@@ -225,7 +225,7 @@ impl<'a, T: Transaction + 'a> RecursiveCte<'a, T> {
}
impl<'a, T: Transaction + 'a> ReadExecutor<'a, T> for RecursiveCte<'a, T> {
- type Input = (LogicalPlan, LogicalPlan);
+ type Input = (&'a LogicalPlan, &'a LogicalPlan);
fn into_executor(
(anchor_plan, recursive_plan): Self::Input,
@@ -292,7 +292,7 @@ pub struct RecursiveScan {
}
impl<'a, T: Transaction + 'a> ReadExecutor<'a, T> for RecursiveScan {
- type Input = RecursiveScanOperator;
+ type Input = &'a RecursiveScanOperator;
fn into_executor(
_input: Self::Input,
@@ -333,6 +333,7 @@ mod tests {
use crate::planner::operator::Operator;
use crate::planner::test::PlanArenaTestExt;
use crate::planner::Childrens;
+ use crate::planner::LogicalPlan;
use crate::storage::rocksdb::RocksStorage;
use crate::storage::{StatisticsMetaCache, Storage, TableCache, ViewCache};
use crate::types::evaluator::binary_create;
@@ -458,7 +459,7 @@ mod tests {
let view_cache = ViewCache::default();
let meta_cache = StatisticsMetaCache::default();
let tuples = try_collect(execute_input::<_, RecursiveCte<'_, _>>(
- (anchor, recursive),
+ (&anchor, &recursive),
empty_context(&table_cache, &view_cache, &meta_cache),
plan_arena,
&transaction,
diff --git a/src/execution/dql/scalar_apply.rs b/src/execution/dql/scalar_apply.rs
index 7589fe6a..f0c74c2e 100644
--- a/src/execution/dql/scalar_apply.rs
+++ b/src/execution/dql/scalar_apply.rs
@@ -29,7 +29,7 @@ pub struct ScalarApply {
}
impl<'a, T: Transaction + 'a> ReadExecutor<'a, T> for ScalarApply {
- type Input = (ScalarApplyOperator, LogicalPlan, LogicalPlan);
+ type Input = (&'a ScalarApplyOperator, &'a LogicalPlan, &'a LogicalPlan);
fn into_executor(
(_, left_input, right_input): Self::Input,
@@ -54,12 +54,8 @@ impl<'a, T: Transaction + 'a> ExecutorNode<'a, T> for ScalarApply {
arena: &mut ExecArena<'a, T>,
plan_arena: &mut (dyn MetaArena + 'a),
) -> Result<(), DatabaseError> {
- Self::load_right_once(&mut self.cached_right, self.right_input, arena, plan_arena)?;
-
- let right_tuple = self
- .cached_right
- .as_ref()
- .expect("scalar apply right tuple initialized");
+ let right_tuple =
+ Self::load_right_once(&mut self.cached_right, self.right_input, arena, plan_arena)?;
if !arena.next_tuple(self.left_input, plan_arena)? {
arena.finish();
return Ok(());
@@ -74,22 +70,23 @@ impl<'a, T: Transaction + 'a> ExecutorNode<'a, T> for ScalarApply {
}
impl ScalarApply {
- fn load_right_once<'a, T: Transaction + 'a>(
- cached_right: &mut Option,
+ fn load_right_once<'a, 'c, T: Transaction + 'a>(
+ cached_right: &'c mut Option,
right_input: ExecId,
arena: &mut ExecArena<'a, T>,
plan_arena: &mut (dyn MetaArena + 'a),
- ) -> Result<(), DatabaseError> {
- if cached_right.is_none() {
- if !arena.next_tuple(right_input, plan_arena)? {
- return Err(DatabaseError::InvalidValue(
- "scalar apply right input returned no rows".to_string(),
- ));
+ ) -> Result<&'c Tuple, DatabaseError> {
+ match cached_right {
+ Some(tuple) => Ok(tuple),
+ None => {
+ if !arena.next_tuple(right_input, plan_arena)? {
+ return Err(DatabaseError::InvalidValue(
+ "scalar apply right input returned no rows".to_string(),
+ ));
+ }
+ Ok(cached_right.insert(arena.materialize_tuple()))
}
- *cached_right = Some(arena.materialize_tuple());
}
-
- Ok(())
}
}
@@ -151,7 +148,7 @@ mod tests {
fn scalar_apply_repeats_scalar_result_for_each_left_row() -> Result<(), DatabaseError> {
let table_arena = crate::planner::TableArenaCell::default();
let mut plan_arena = crate::planner::PlanArena::new(&table_arena);
- let left = build_values(
+ let mut left = build_values(
&mut plan_arena,
"left_c1",
vec![
@@ -159,7 +156,7 @@ mod tests {
vec![crate::types::value::DataValue::Int32(2)],
],
);
- let right = ScalarSubqueryOperator::build(build_values(
+ let mut right = ScalarSubqueryOperator::build(build_values(
&mut plan_arena,
"right_c1",
vec![vec![crate::types::value::DataValue::Int32(7)]],
@@ -167,8 +164,10 @@ mod tests {
let (table_cache, view_cache, meta_cache, _temp_dir, storage) = build_test_storage()?;
let transaction = storage.transaction()?;
+ left.populate_output_schema_recursive(&mut plan_arena);
+ right.populate_output_schema_recursive(&mut plan_arena);
let tuples = try_collect(execute_input::<_, ScalarApply>(
- (ScalarApplyOperator, left, right),
+ (&ScalarApplyOperator, &left, &right),
crate::execution::empty_context(&table_cache, &view_cache, &meta_cache),
plan_arena,
&transaction,
@@ -195,12 +194,12 @@ mod tests {
fn scalar_apply_repeats_null_scalar_result_for_each_left_row() -> Result<(), DatabaseError> {
let table_arena = crate::planner::TableArenaCell::default();
let mut plan_arena = crate::planner::PlanArena::new(&table_arena);
- let left = build_values(
+ let mut left = build_values(
&mut plan_arena,
"left_c1",
vec![vec![DataValue::Int32(1)], vec![DataValue::Int32(2)]],
);
- let right = ScalarSubqueryOperator::build(build_values(
+ let mut right = ScalarSubqueryOperator::build(build_values(
&mut plan_arena,
"right_c1",
vec![vec![DataValue::Null]],
@@ -208,8 +207,10 @@ mod tests {
let (table_cache, view_cache, meta_cache, _temp_dir, storage) = build_test_storage()?;
let transaction = storage.transaction()?;
+ left.populate_output_schema_recursive(&mut plan_arena);
+ right.populate_output_schema_recursive(&mut plan_arena);
let tuples = try_collect(execute_input::<_, ScalarApply>(
- (ScalarApplyOperator, left, right),
+ (&ScalarApplyOperator, &left, &right),
crate::execution::empty_context(&table_cache, &view_cache, &meta_cache),
plan_arena,
&transaction,
diff --git a/src/execution/dql/scalar_subquery.rs b/src/execution/dql/scalar_subquery.rs
index d53fdfc4..f59b5115 100644
--- a/src/execution/dql/scalar_subquery.rs
+++ b/src/execution/dql/scalar_subquery.rs
@@ -29,16 +29,16 @@ pub struct ScalarSubquery {
}
impl<'a, T: Transaction + 'a> ReadExecutor<'a, T> for ScalarSubquery {
- type Input = (ScalarSubqueryOperator, LogicalPlan);
+ type Input = (&'a ScalarSubqueryOperator, &'a LogicalPlan);
fn into_executor(
- (_, mut input): Self::Input,
+ (_, input): Self::Input,
arena: &mut ExecArena<'a, T>,
plan_arena: &mut (dyn MetaArena + 'a),
cache: ExecutionContext<'_>,
transaction: &T,
) -> ExecId {
- let value_count = input.output_schema(plan_arena).len();
+ let value_count = input.read_schema().len();
let input = build_read(arena, plan_arena, input, cache, transaction);
arena.push(ExecNode::ScalarSubquery(Self {
input,
diff --git a/src/execution/dql/seq_scan.rs b/src/execution/dql/seq_scan.rs
index 91df1a59..6effc8e3 100644
--- a/src/execution/dql/seq_scan.rs
+++ b/src/execution/dql/seq_scan.rs
@@ -19,31 +19,21 @@ use crate::planner::MetaArena;
use crate::storage::{Iter, Transaction, TupleIter};
pub(crate) struct SeqScan<'a, T: Transaction + 'a> {
- op: Option,
+ op: &'a TableScanOperator,
iter: Option>,
}
-impl<'a, T: Transaction + 'a> From for SeqScan<'a, T> {
- fn from(op: TableScanOperator) -> Self {
- SeqScan {
- op: Some(op),
- iter: None,
- }
- }
-}
-
impl<'a, T: Transaction + 'a> ReadExecutor<'a, T> for SeqScan<'a, T> {
- type Input = Self;
+ type Input = &'a TableScanOperator;
fn into_executor(
- input: Self::Input,
+ op: Self::Input,
arena: &mut ExecArena<'a, T>,
_plan_arena: &mut (dyn MetaArena + 'a),
_: ExecutionContext<'_>,
_: &T,
) -> ExecId {
- let executor = input;
- arena.push(ExecNode::SeqScan(executor))
+ arena.push(ExecNode::SeqScan(SeqScan { op, iter: None }))
}
}
@@ -53,37 +43,30 @@ impl<'a, T: Transaction + 'a> ExecutorNode<'a, T> for SeqScan<'a, T> {
arena: &mut ExecArena<'a, T>,
plan_arena: &mut (dyn MetaArena + 'a),
) -> Result<(), DatabaseError> {
- if self.iter.is_none() {
- let Some(TableScanOperator {
- table_name,
- columns,
- limit,
- with_pk,
- ..
- }) = self.op.take()
- else {
- arena.finish();
- return Ok(());
- };
- let state = arena.local_state(plan_arena);
- self.iter = Some(state.transaction().read(
- state.table_codec,
- state.plan_arena,
- state.context.table_cache,
- table_name,
- limit,
- columns,
- with_pk,
- )?);
- }
-
let state = arena.local_state(plan_arena);
- if self
- .iter
- .as_mut()
- .expect("seq scan iterator initialized")
- .next_tuple_into(state.table_codec, &mut state.result.tuple)?
- {
+ let iter = match &mut self.iter {
+ Some(iter) => iter,
+ None => {
+ let TableScanOperator {
+ table_name,
+ columns,
+ limit,
+ with_pk,
+ ..
+ } = self.op;
+ self.iter.insert(state.transaction().read(
+ state.table_codec,
+ state.plan_arena,
+ state.context.table_cache,
+ table_name.clone(),
+ *limit,
+ columns,
+ *with_pk,
+ )?)
+ }
+ };
+
+ if iter.next_tuple_into(state.table_codec, &mut state.result.tuple)? {
arena.resume();
} else {
arena.finish();
diff --git a/src/execution/dql/set_membership.rs b/src/execution/dql/set_membership.rs
index fb2b6c55..7f64f148 100644
--- a/src/execution/dql/set_membership.rs
+++ b/src/execution/dql/set_membership.rs
@@ -25,56 +25,31 @@ use std::collections::HashMap;
pub struct SetMembership {
kind: SetMembershipKind,
- left_plan: LogicalPlan,
- right_plan: LogicalPlan,
left_input: ExecId,
right_input: ExecId,
right_counts: HashMap,
built: bool,
}
-impl From<(SetMembershipKind, LogicalPlan, LogicalPlan)> for SetMembership {
- fn from(
- (kind, left_input, right_input): (SetMembershipKind, LogicalPlan, LogicalPlan),
- ) -> Self {
- SetMembership {
- kind,
- left_plan: left_input,
- right_plan: right_input,
- left_input: 0,
- right_input: 0,
- right_counts: HashMap::new(),
- built: false,
- }
- }
-}
-
impl<'a, T: Transaction + 'a> ReadExecutor<'a, T> for SetMembership {
- type Input = Self;
+ type Input = (SetMembershipKind, &'a LogicalPlan, &'a LogicalPlan);
fn into_executor(
- input: Self::Input,
+ (kind, left_plan, right_plan): Self::Input,
arena: &mut ExecArena<'a, T>,
plan_arena: &mut (dyn MetaArena + 'a),
cache: ExecutionContext<'_>,
transaction: &T,
) -> ExecId {
- let mut executor = input;
- executor.left_input = build_read(
- arena,
- plan_arena,
- executor.left_plan.take(),
- cache,
- transaction,
- );
- executor.right_input = build_read(
- arena,
- plan_arena,
- executor.right_plan.take(),
- cache,
- transaction,
- );
- arena.push(ExecNode::SetMembership(executor))
+ let left_input = build_read(arena, plan_arena, left_plan, cache, transaction);
+ let right_input = build_read(arena, plan_arena, right_plan, cache, transaction);
+ arena.push(ExecNode::SetMembership(SetMembership {
+ kind,
+ left_input,
+ right_input,
+ right_counts: HashMap::new(),
+ built: false,
+ }))
}
}
diff --git a/src/execution/dql/show_table.rs b/src/execution/dql/show_table.rs
index e523df1c..9d0a2b60 100644
--- a/src/execution/dql/show_table.rs
+++ b/src/execution/dql/show_table.rs
@@ -24,16 +24,16 @@ pub struct ShowTables<'a, T: Transaction + 'a> {
}
impl<'a, T: Transaction + 'a> ReadExecutor<'a, T> for ShowTables<'a, T> {
- type Input = Self;
+ type Input = ();
fn into_executor(
- input: Self::Input,
+ _: Self::Input,
arena: &mut ExecArena<'a, T>,
_: &mut (dyn MetaArena + 'a),
_: ExecutionContext<'_>,
_: &T,
) -> ExecId {
- arena.push(ExecNode::ShowTables(input))
+ arena.push(ExecNode::ShowTables(ShowTables { metas: None }))
}
}
diff --git a/src/execution/dql/show_view.rs b/src/execution/dql/show_view.rs
index 9f6ca4a6..12f337a7 100644
--- a/src/execution/dql/show_view.rs
+++ b/src/execution/dql/show_view.rs
@@ -24,16 +24,16 @@ pub struct ShowViews<'a, T: Transaction + 'a> {
}
impl<'a, T: Transaction + 'a> ReadExecutor<'a, T> for ShowViews<'a, T> {
- type Input = Self;
+ type Input = ();
fn into_executor(
- input: Self::Input,
+ _: Self::Input,
arena: &mut ExecArena<'a, T>,
_: &mut (dyn MetaArena + 'a),
_: ExecutionContext<'_>,
_: &T,
) -> ExecId {
- arena.push(ExecNode::ShowViews(input))
+ arena.push(ExecNode::ShowViews(ShowViews { metas: None }))
}
}
diff --git a/src/execution/dql/sort.rs b/src/execution/dql/sort.rs
index 807a550f..569178f6 100644
--- a/src/execution/dql/sort.rs
+++ b/src/execution/dql/sort.rs
@@ -148,15 +148,15 @@ pub(crate) fn compare_sort_keys<'a>(
Ordering::Equal
}
-pub struct Sort {
+pub struct Sort<'a> {
rows: NullableVec<'static, (usize, Tuple)>,
_arena: Box,
- sort_fields: Vec,
+ sort_fields: &'a [SortField],
input: ExecId,
}
-impl<'a, T: Transaction + 'a> ReadExecutor<'a, T> for Sort {
- type Input = (SortOperator, LogicalPlan);
+impl<'a, T: Transaction + 'a> ReadExecutor<'a, T> for Sort<'a> {
+ type Input = (&'a SortOperator, &'a LogicalPlan);
fn into_executor(
(SortOperator { sort_fields }, input): Self::Input,
@@ -181,7 +181,7 @@ impl<'a, T: Transaction + 'a> ReadExecutor<'a, T> for Sort {
}
}
-impl<'a, T: Transaction + 'a> ExecutorNode<'a, T> for Sort {
+impl<'a, T: Transaction + 'a> ExecutorNode<'a, T> for Sort<'a> {
fn next_tuple(
&mut self,
arena: &mut ExecArena<'a, T>,
@@ -200,7 +200,7 @@ impl<'a, T: Transaction + 'a> ExecutorNode<'a, T> for Sort {
arena.finish();
return Ok(());
}
- sort_tuples(&self.sort_fields, &mut self.rows, plan_arena)?;
+ sort_tuples(self.sort_fields, &mut self.rows, plan_arena)?;
self.rows.reverse();
}
}
diff --git a/src/execution/dql/top_k.rs b/src/execution/dql/top_k.rs
index 23f1040f..df4eef8a 100644
--- a/src/execution/dql/top_k.rs
+++ b/src/execution/dql/top_k.rs
@@ -93,17 +93,17 @@ fn top_sort<'a>(
Ok(())
}
-pub struct TopK {
+pub struct TopK<'a> {
output: Option>>>,
arena: Box,
- sort_fields: Vec,
+ sort_fields: &'a [SortField],
limit: usize,
offset: Option,
input: ExecId,
}
-impl<'a, T: Transaction + 'a> ReadExecutor<'a, T> for TopK {
- type Input = (TopKOperator, LogicalPlan);
+impl<'a, T: Transaction + 'a> ReadExecutor<'a, T> for TopK<'a> {
+ type Input = (&'a TopKOperator, &'a LogicalPlan);
fn into_executor(
(
@@ -124,14 +124,14 @@ impl<'a, T: Transaction + 'a> ReadExecutor<'a, T> for TopK {
output: None,
arena: Box::::default(),
sort_fields,
- limit,
- offset,
+ limit: *limit,
+ offset: *offset,
input,
}))
}
}
-impl<'a, T: Transaction + 'a> ExecutorNode<'a, T> for TopK {
+impl<'a, T: Transaction + 'a> ExecutorNode<'a, T> for TopK<'a> {
fn next_tuple(
&mut self,
arena: &mut ExecArena<'a, T>,
@@ -145,7 +145,7 @@ impl<'a, T: Transaction + 'a> ExecutorNode<'a, T> for TopK {
while arena.next_tuple(self.input, plan_arena)? {
top_sort(
&self.arena,
- &self.sort_fields,
+ self.sort_fields,
&mut set,
arena.materialize_tuple(),
keep_count,
diff --git a/src/execution/dql/union.rs b/src/execution/dql/union.rs
index 75fa2365..12374e68 100644
--- a/src/execution/dql/union.rs
+++ b/src/execution/dql/union.rs
@@ -20,51 +20,28 @@ use crate::planner::LogicalPlan;
use crate::planner::MetaArena;
use crate::storage::Transaction;
pub struct Union {
- left_plan: LogicalPlan,
- right_plan: LogicalPlan,
left_input: ExecId,
right_input: ExecId,
reading_left: bool,
}
-impl From<(LogicalPlan, LogicalPlan)> for Union {
- fn from((left_input, right_input): (LogicalPlan, LogicalPlan)) -> Self {
- Union {
- left_plan: left_input,
- right_plan: right_input,
- left_input: 0,
- right_input: 0,
- reading_left: true,
- }
- }
-}
-
impl<'a, T: Transaction + 'a> ReadExecutor<'a, T> for Union {
- type Input = Self;
+ type Input = (&'a LogicalPlan, &'a LogicalPlan);
fn into_executor(
- input: Self::Input,
+ (left_plan, right_plan): Self::Input,
arena: &mut ExecArena<'a, T>,
plan_arena: &mut (dyn MetaArena + 'a),
cache: ExecutionContext<'_>,
transaction: &T,
) -> ExecId {
- let mut executor = input;
- executor.left_input = build_read(
- arena,
- plan_arena,
- executor.left_plan.take(),
- cache,
- transaction,
- );
- executor.right_input = build_read(
- arena,
- plan_arena,
- executor.right_plan.take(),
- cache,
- transaction,
- );
- arena.push(ExecNode::Union(executor))
+ let left_input = build_read(arena, plan_arena, left_plan, cache, transaction);
+ let right_input = build_read(arena, plan_arena, right_plan, cache, transaction);
+ arena.push(ExecNode::Union(Union {
+ left_input,
+ right_input,
+ reading_left: true,
+ }))
}
}
diff --git a/src/execution/dql/values.rs b/src/execution/dql/values.rs
index df7d81b1..d76df905 100644
--- a/src/execution/dql/values.rs
+++ b/src/execution/dql/values.rs
@@ -20,30 +20,30 @@ use crate::planner::MetaArena;
use crate::storage::Transaction;
use crate::types::tuple::Schema;
-pub struct Values {
- rows: std::vec::IntoIter,
+pub struct Values<'a> {
+ rows: std::slice::Iter<'a, ExprRef>,
remaining_rows: usize,
- schema_ref: Schema,
+ schema_ref: &'a Schema,
}
-impl From for Values {
+impl<'a> From<&'a ValuesOperator> for Values<'a> {
fn from(
ValuesOperator {
rows,
row_count,
schema_ref,
- }: ValuesOperator,
+ }: &'a ValuesOperator,
) -> Self {
Values {
- rows: rows.into_iter(),
- remaining_rows: row_count,
+ rows: rows.iter(),
+ remaining_rows: *row_count,
schema_ref,
}
}
}
-impl<'a, T: Transaction + 'a> ReadExecutor<'a, T> for Values {
- type Input = Self;
+impl<'a, T: Transaction + 'a> ReadExecutor<'a, T> for Values<'a> {
+ type Input = &'a ValuesOperator;
fn into_executor(
input: Self::Input,
@@ -52,12 +52,12 @@ impl<'a, T: Transaction + 'a> ReadExecutor<'a, T> for Values {
_: ExecutionContext<'_>,
_: &T,
) -> ExecId {
- let executor = input;
+ let executor = Values::from(input);
arena.push(ExecNode::Values(executor))
}
}
-impl<'a, T: Transaction + 'a> ExecutorNode<'a, T> for Values {
+impl<'a, T: Transaction + 'a> ExecutorNode<'a, T> for Values<'a> {
fn next_tuple(
&mut self,
arena: &mut ExecArena<'a, T>,
@@ -77,7 +77,7 @@ impl<'a, T: Transaction + 'a> ExecutorNode<'a, T> for Values {
let ty = plan_arena.column(self.schema_ref[i]).datatype();
output.values.push(
plan_arena
- .expression(expr)
+ .expression(*expr)
.eval(plan_arena, None)?
.into_owned()
.cast(ty)?,
diff --git a/src/execution/dql/window.rs b/src/execution/dql/window.rs
index e31db988..a2011b26 100644
--- a/src/execution/dql/window.rs
+++ b/src/execution/dql/window.rs
@@ -66,18 +66,18 @@ impl WindowState {
}
}
-pub struct Window {
+pub struct Window<'a> {
state: WindowState,
retention: Retention,
- sort_fields: Vec,
+ sort_fields: &'a [SortField],
partition_by_len: usize,
functions: Vec>,
input_exhausted: bool,
input: ExecId,
}
-impl<'a, T: Transaction + 'a> ReadExecutor<'a, T> for Window {
- type Input = (WindowOperator, LogicalPlan);
+impl<'a, T: Transaction + 'a> ReadExecutor<'a, T> for Window<'a> {
+ type Input = (&'a WindowOperator, &'a LogicalPlan);
fn into_executor(
(operator, input): Self::Input,
@@ -98,20 +98,22 @@ impl<'a, T: Transaction + 'a> ReadExecutor<'a, T> for Window {
.any(|function| matches!(function.kind, WindowFunctionKind::Aggregate(_)));
let retention = if !has_aggregate {
Retention::Row
- } else if sort_fields.len() == partition_by_len {
+ } else if sort_fields.len() == *partition_by_len {
Retention::Partition
} else {
Retention::Peer
};
let functions = window_functions
- .into_iter()
- .map(|function| function::new(function.kind, function.args, function.ty))
+ .iter()
+ .map(|function| {
+ function::new(function.kind, function.args.clone(), function.ty.clone())
+ })
.collect();
arena.push(ExecNode::Window(Window {
state: WindowState::default(),
retention,
sort_fields,
- partition_by_len,
+ partition_by_len: *partition_by_len,
functions,
input_exhausted: false,
input,
@@ -119,7 +121,7 @@ impl<'a, T: Transaction + 'a> ReadExecutor<'a, T> for Window {
}
}
-impl Window {
+impl<'a> Window<'a> {
fn update_keys(
&mut self,
tuple: &Tuple,
@@ -217,7 +219,7 @@ impl Window {
}
}
-impl<'a, T: Transaction + 'a> ExecutorNode<'a, T> for Window {
+impl<'a, T: Transaction + 'a> ExecutorNode<'a, T> for Window<'a> {
fn next_tuple(
&mut self,
arena: &mut ExecArena<'a, T>,
@@ -269,12 +271,12 @@ mod tests {
))
}
- fn window(
+ fn window<'a>(
retention: Retention,
- sort_fields: Vec,
+ sort_fields: &'a [SortField],
partition_by_len: usize,
functions: Vec>,
- ) -> Window {
+ ) -> Window<'a> {
Window {
state: WindowState::default(),
retention,
@@ -296,12 +298,13 @@ mod tests {
let mut plan_arena = crate::planner::PlanArena::new(&table_arena);
let partition = column(&mut plan_arena, 0);
let order = column(&mut plan_arena, 1);
+ let sort_fields = [
+ SortField::from(partition).asc(),
+ SortField::from(order).asc(),
+ ];
let mut window = window(
Retention::Row,
- vec![
- SortField::from(partition).asc(),
- SortField::from(order).asc(),
- ],
+ &sort_fields,
1,
vec![
function::new(
@@ -326,12 +329,13 @@ mod tests {
let mut plan_arena = crate::planner::PlanArena::new(&table_arena);
let partition = column(&mut plan_arena, 0);
let value = column(&mut plan_arena, 1);
+ let sort_fields = [
+ SortField::from(partition).asc(),
+ SortField::from(value).asc(),
+ ];
let mut window = window(
Retention::Peer,
- vec![
- SortField::from(partition).asc(),
- SortField::from(value).asc(),
- ],
+ &sort_fields,
1,
vec![function::new(
WindowFunctionKind::Aggregate(AggKind::Sum),
@@ -353,9 +357,10 @@ mod tests {
let mut plan_arena = crate::planner::PlanArena::new(&table_arena);
let partition = column(&mut plan_arena, 0);
let value = column(&mut plan_arena, 1);
+ let sort_fields = [SortField::from(partition).asc()];
let mut window = window(
Retention::Partition,
- vec![SortField::from(partition).asc()],
+ &sort_fields,
1,
vec![function::new(
WindowFunctionKind::Aggregate(AggKind::Sum),
diff --git a/src/execution/mod.rs b/src/execution/mod.rs
index dc339cf3..3176cfb8 100644
--- a/src/execution/mod.rs
+++ b/src/execution/mod.rs
@@ -76,7 +76,7 @@ use crate::expression::ScalarExpression;
use crate::planner::operator::join::JoinCondition;
use crate::planner::operator::{Operator, PhysicalOption, PlanImpl};
use crate::planner::MetaArena;
-use crate::planner::{ExprRef, LogicalPlan};
+use crate::planner::{ExprRef, LogicalPlan, PlanKeeper};
use crate::storage::table_codec::TableCodec;
use crate::storage::{StatisticsMetaCache, TableCache, Transaction, ViewCache};
use crate::types::index::RuntimeIndexProbe;
@@ -164,11 +164,19 @@ impl RewriteExpression for ScalarExpression {
pub struct Executor<'a, T: Transaction + 'a> {
arena: ExecArena<'a, T>,
root: ExecId,
+ // Never read: it only keeps the plan that `arena` borrows from alive, and must stay
+ // declared after `arena` so the executors are dropped before the plan is freed.
+ #[allow(dead_code)]
+ keeper: PlanKeeper<'a>,
}
impl<'a, T: Transaction + 'a> Executor<'a, T> {
- pub(crate) fn new(arena: ExecArena<'a, T>, root: ExecId) -> Self {
- Self { arena, root }
+ pub(crate) fn new(arena: ExecArena<'a, T>, root: ExecId, keeper: PlanKeeper<'a>) -> Self {
+ Self {
+ arena,
+ root,
+ keeper,
+ }
}
pub(crate) fn next_tuple(
@@ -188,36 +196,36 @@ impl<'a, T: Transaction + 'a> Executor<'a, T> {
#[allow(clippy::large_enum_variant)]
pub(crate) enum ExecNode<'a, T: Transaction + 'a> {
- AddColumn(AddColumn),
- Analyze(Analyze),
- ChangeColumn(ChangeColumn),
+ AddColumn(AddColumn<'a>),
+ Analyze(Analyze<'a>),
+ ChangeColumn(ChangeColumn<'a>),
#[cfg(feature = "copy")]
- CopyFromFile(CopyFromFile),
+ CopyFromFile(CopyFromFile<'a>),
#[cfg(feature = "copy")]
- CopyToFile(CopyToFile),
- CreateIndex(CreateIndex),
- CreateTable(CreateTable),
- CreateView(CreateView),
- Delete(Delete),
+ CopyToFile(CopyToFile<'a>),
+ CreateIndex(CreateIndex<'a>),
+ CreateTable(CreateTable<'a>),
+ CreateView(CreateView<'a>),
+ Delete(Delete<'a>),
Describe(Describe),
- DropColumn(DropColumn),
- DropIndex(DropIndex),
- DropTable(DropTable),
- DropView(DropView),
+ DropColumn(DropColumn<'a>),
+ DropIndex(DropIndex<'a>),
+ DropTable(DropTable<'a>),
+ DropView(DropView<'a>),
Dummy(Dummy),
- Explain(Explain),
+ Explain(Explain<'a>),
#[cfg(feature = "spill")]
- ExternalSort(ExternalSort),
+ ExternalSort(ExternalSort<'a>),
Filter(Filter),
- FunctionScan(FunctionScan),
- HashAgg(HashAggExecutor),
+ FunctionScan(FunctionScan<'a>),
+ HashAgg(HashAggExecutor<'a>),
HashJoin(HashJoin),
IndexScan(IndexScan<'a, T>),
- Insert(Insert),
+ Insert(Insert<'a>),
Limit(Limit),
- MarkApply(MarkApply),
- NestedLoopJoin(NestedLoopJoin),
- Projection(Projection),
+ MarkApply(MarkApply<'a>),
+ NestedLoopJoin(NestedLoopJoin<'a>),
+ Projection(Projection<'a>),
RecursiveCte(RecursiveCte<'a, T>),
RecursiveScan(RecursiveScan),
ScalarApply(ScalarApply),
@@ -226,16 +234,16 @@ pub(crate) enum ExecNode<'a, T: Transaction + 'a> {
SeqScan(SeqScan<'a, T>),
ShowTables(ShowTables<'a, T>),
ShowViews(ShowViews<'a, T>),
- SimpleAgg(SimpleAggExecutor),
- Sort(Sort),
- StreamAgg(StreamAggExecutor),
- StreamDistinct(StreamDistinctExecutor),
- TopK(TopK),
- Truncate(Truncate),
+ SimpleAgg(SimpleAggExecutor<'a>),
+ Sort(Sort<'a>),
+ StreamAgg(StreamAggExecutor<'a>),
+ StreamDistinct(StreamDistinctExecutor<'a>),
+ TopK(TopK<'a>),
+ Truncate(Truncate<'a>),
Union(Union),
- Update(Update),
- Values(Values),
- Window(Window),
+ Update(Update<'a>),
+ Values(Values<'a>),
+ Window(Window<'a>),
}
pub(crate) trait ExecutorNode<'a, T: Transaction + 'a>: Sized {
@@ -301,20 +309,20 @@ impl<'a, T: Transaction + 'a> ExecNode<'a, T> {
>::next_tuple(exec, arena, plan_arena)
}
ExecNode::Explain(exec) => {
- >::next_tuple(exec, arena, plan_arena)
+ as ExecutorNode<'a, T>>::next_tuple(exec, arena, plan_arena)
}
#[cfg(feature = "spill")]
ExecNode::ExternalSort(exec) => {
- >::next_tuple(exec, arena, plan_arena)
+ as ExecutorNode<'a, T>>::next_tuple(exec, arena, plan_arena)
}
ExecNode::Filter(exec) => {
>::next_tuple(exec, arena, plan_arena)
}
ExecNode::FunctionScan(exec) => {
- >::next_tuple(exec, arena, plan_arena)
+ as ExecutorNode<'a, T>>::next_tuple(exec, arena, plan_arena)
}
ExecNode::HashAgg(exec) => {
- >::next_tuple(exec, arena, plan_arena)
+ as ExecutorNode<'a, T>>::next_tuple(exec, arena, plan_arena)
}
ExecNode::HashJoin(exec) => {
>::next_tuple(exec, arena, plan_arena)
@@ -329,13 +337,13 @@ impl<'a, T: Transaction + 'a> ExecNode<'a, T> {
>::next_tuple(exec, arena, plan_arena)
}
ExecNode::MarkApply(exec) => {
- >::next_tuple(exec, arena, plan_arena)
+ as ExecutorNode<'a, T>>::next_tuple(exec, arena, plan_arena)
}
ExecNode::NestedLoopJoin(exec) => {
- >::next_tuple(exec, arena, plan_arena)
+ as ExecutorNode<'a, T>>::next_tuple(exec, arena, plan_arena)
}
ExecNode::Projection(exec) => {
- >::next_tuple(exec, arena, plan_arena)
+ as ExecutorNode<'a, T>>::next_tuple(exec, arena, plan_arena)
}
ExecNode::RecursiveCte(exec) => {
as ExecutorNode<'a, T>>::next_tuple(exec, arena, plan_arena)
@@ -362,19 +370,21 @@ impl<'a, T: Transaction + 'a> ExecNode<'a, T> {
as ExecutorNode<'a, T>>::next_tuple(exec, arena, plan_arena)
}
ExecNode::SimpleAgg(exec) => {
- >::next_tuple(exec, arena, plan_arena)
+ as ExecutorNode<'a, T>>::next_tuple(exec, arena, plan_arena)
}
ExecNode::Sort(exec) => {
- >::next_tuple(exec, arena, plan_arena)
+ as ExecutorNode<'a, T>>::next_tuple(exec, arena, plan_arena)
}
ExecNode::StreamAgg(exec) => {
- >::next_tuple(exec, arena, plan_arena)
+ as ExecutorNode<'a, T>>::next_tuple(exec, arena, plan_arena)
}
ExecNode::StreamDistinct(exec) => {
- >::next_tuple(exec, arena, plan_arena)
+ as ExecutorNode<'a, T>>::next_tuple(
+ exec, arena, plan_arena,
+ )
}
ExecNode::TopK(exec) => {
- >::next_tuple(exec, arena, plan_arena)
+ as ExecutorNode<'a, T>>::next_tuple(exec, arena, plan_arena)
}
ExecNode::Truncate(exec) => {
>::next_tuple(exec, arena, plan_arena)
@@ -383,13 +393,13 @@ impl<'a, T: Transaction + 'a> ExecNode<'a, T> {
>::next_tuple(exec, arena, plan_arena)
}
ExecNode::Update(exec) => {
- >::next_tuple(exec, arena, plan_arena)
+ as ExecutorNode<'a, T>>::next_tuple(exec, arena, plan_arena)
}
ExecNode::Values(exec) => {
- >::next_tuple(exec, arena, plan_arena)
+ as ExecutorNode<'a, T>>::next_tuple(exec, arena, plan_arena)
}
ExecNode::Window(exec) => {
- >::next_tuple(exec, arena, plan_arena)
+ as ExecutorNode<'a, T>>::next_tuple(exec, arena, plan_arena)
}
}
}
@@ -489,6 +499,10 @@ impl<'b, 'a, T: Transaction + 'a> ExecArenaLocalState<'b, 'a, T> {
}
impl<'a, T: Transaction + 'a> ExecArena<'a, T> {
+ pub(crate) fn set_statement_stamp(&mut self, stamp: u64) {
+ self.table_codec.set_stamp(stamp);
+ }
+
pub(crate) fn new() -> Self {
Self {
nodes: ExecNodes {
@@ -718,39 +732,33 @@ pub(crate) trait WriteExecutor<'a, T: Transaction + 'a>: Sized {
pub(crate) fn build_read<'a, T>(
arena: &mut ExecArena<'a, T>,
plan_arena: &mut (dyn MetaArena + 'a),
- plan: LogicalPlan,
+ plan: &'a LogicalPlan,
cache: ExecutionContext<'_>,
transaction: &T,
) -> ExecId
where
T: Transaction + 'a,
{
- let LogicalPlan {
- operator,
- childrens,
- physical_option,
- ..
- } = plan;
-
- match operator {
- Operator::Dummy => >::into_executor(
- Dummy::default(),
- arena,
- plan_arena,
- cache,
- transaction,
- ),
+ macro_rules! read {
+ ($executor:ty, $input:expr) => {
+ <$executor as ReadExecutor<'a, T>>::into_executor(
+ $input,
+ arena,
+ plan_arena,
+ cache,
+ transaction,
+ )
+ };
+ }
+
+ let physical_option = plan.physical_option.as_ref();
+ match &plan.operator {
+ Operator::Dummy => read!(Dummy, ()),
Operator::Aggregate(op) => {
- let input = childrens.pop_only();
+ let input = plan.childrens.only();
if op.groupby_exprs.is_empty() {
- >::into_executor(
- (op, input),
- arena,
- plan_arena,
- cache,
- transaction,
- )
+ read!(SimpleAggExecutor<'a>, (op, input))
} else if op.is_distinct
&& op.agg_calls.is_empty()
&& matches!(
@@ -761,13 +769,7 @@ where
})
)
{
- >::into_executor(
- (op, input),
- arena,
- plan_arena,
- cache,
- transaction,
- )
+ read!(StreamDistinctExecutor<'a>, (op, input))
} else if matches!(
physical_option,
Some(PhysicalOption {
@@ -775,49 +777,19 @@ where
..
})
) {
- >::into_executor(
- (op, input),
- arena,
- plan_arena,
- cache,
- transaction,
- )
+ read!(StreamAggExecutor<'a>, (op, input))
} else {
- >::into_executor(
- (op, input),
- arena,
- plan_arena,
- cache,
- transaction,
- )
+ read!(HashAggExecutor<'a>, (op, input))
}
}
- Operator::Filter(op) => >::into_executor(
- (op, childrens.pop_only()),
- arena,
- plan_arena,
- cache,
- transaction,
- ),
+ Operator::Filter(op) => read!(Filter, (op, plan.childrens.only())),
Operator::ScalarApply(op) => {
- let (left, right) = childrens.pop_twins();
- >::into_executor(
- (op, left, right),
- arena,
- plan_arena,
- cache,
- transaction,
- )
+ let (left, right) = plan.childrens.twins();
+ read!(ScalarApply, (op, left, right))
}
Operator::MarkApply(op) => {
- let (left, right) = childrens.pop_twins();
- >::into_executor(
- (op, left, right),
- arena,
- plan_arena,
- cache,
- transaction,
- )
+ let (left, right) = plan.childrens.twins();
+ read!(MarkApply<'a>, (op, left, right))
}
Operator::Join(op) => {
let use_hash_join = matches!(
@@ -830,186 +802,57 @@ where
..
})
);
- let (left, right) = childrens.pop_twins();
+ let (left, right) = plan.childrens.twins();
if use_hash_join {
- >::into_executor(
- HashJoin::from((op, left, right)),
- arena,
- plan_arena,
- cache,
- transaction,
- )
+ read!(HashJoin, (op, left, right))
} else {
- >::into_executor(
- NestedLoopJoin::from((op, left, right)),
- arena,
- plan_arena,
- cache,
- transaction,
- )
+ read!(NestedLoopJoin<'a>, (op, left, right))
}
}
- Operator::Project(op) => >::into_executor(
- (op, childrens.pop_only()),
- arena,
- plan_arena,
- cache,
- transaction,
- ),
- Operator::ScalarSubquery(op) => >::into_executor(
- (op, childrens.pop_only()),
- arena,
- plan_arena,
- cache,
- transaction,
- ),
+ Operator::Project(op) => read!(Projection<'a>, (op, plan.childrens.only())),
+ Operator::ScalarSubquery(op) => {
+ read!(ScalarSubquery, (op, plan.childrens.only()))
+ }
Operator::TableScan(op) => {
if let Some(PhysicalOption {
- plan: PlanImpl::IndexScan(index_info),
+ plan: PlanImpl::IndexScan(info),
..
}) = physical_option
{
- if let Some(lookup) = index_info.lookup.clone() {
- return as ReadExecutor<'a, T>>::into_executor(
- IndexScan::from((
- op,
- index_info.meta,
- lookup,
- index_info.covered_deserializers.clone(),
- index_info.cover_mapping.clone(),
- )),
- arena,
- plan_arena,
- cache,
- transaction,
- );
+ if let Some(lookup) = &info.lookup {
+ return read!(IndexScan<'a, T>, (op, info.as_ref(), lookup));
}
}
-
- as ReadExecutor<'a, T>>::into_executor(
- SeqScan::from(op),
- arena,
- plan_arena,
- cache,
- transaction,
- )
+ read!(SeqScan<'a, T>, op)
}
- Operator::FunctionScan(op) => >::into_executor(
- FunctionScan::from(op),
- arena,
- plan_arena,
- cache,
- transaction,
- ),
+ Operator::FunctionScan(op) => read!(FunctionScan<'a>, op),
Operator::Sort(op) => {
#[cfg(feature = "spill")]
{
- >::into_executor(
- (op, childrens.pop_only()),
- arena,
- plan_arena,
- cache,
- transaction,
- )
+ read!(ExternalSort<'a>, (op, plan.childrens.only()))
}
#[cfg(not(feature = "spill"))]
{
- >::into_executor(
- (op, childrens.pop_only()),
- arena,
- plan_arena,
- cache,
- transaction,
- )
+ read!(Sort<'a>, (op, plan.childrens.only()))
}
}
- Operator::Limit(op) => >::into_executor(
- (op, childrens.pop_only()),
- arena,
- plan_arena,
- cache,
- transaction,
- ),
- Operator::TopK(op) => >::into_executor(
- (op, childrens.pop_only()),
- arena,
- plan_arena,
- cache,
- transaction,
- ),
- Operator::Values(op) => >::into_executor(
- Values::from(op),
- arena,
- plan_arena,
- cache,
- transaction,
- ),
- Operator::Window(op) => >::into_executor(
- (op, childrens.pop_only()),
- arena,
- plan_arena,
- cache,
- transaction,
- ),
- Operator::ShowTable => as ReadExecutor<'a, T>>::into_executor(
- ShowTables { metas: None },
- arena,
- plan_arena,
- cache,
- transaction,
- ),
- Operator::ShowView => as ReadExecutor<'a, T>>::into_executor(
- ShowViews { metas: None },
- arena,
- plan_arena,
- cache,
- transaction,
- ),
- Operator::Explain => >::into_executor(
- Explain::from(childrens.pop_only()),
- arena,
- plan_arena,
- cache,
- transaction,
- ),
- Operator::Describe(op) => >::into_executor(
- Describe::from(op),
- arena,
- plan_arena,
- cache,
- transaction,
- ),
- Operator::Union(_) => >::into_executor(
- Union::from(childrens.pop_twins()),
- arena,
- plan_arena,
- cache,
- transaction,
- ),
- Operator::RecursiveCte(_) => as ReadExecutor<'a, T>>::into_executor(
- childrens.pop_twins(),
- arena,
- plan_arena,
- cache,
- transaction,
- ),
- Operator::RecursiveScan(op) => >::into_executor(
- op,
- arena,
- plan_arena,
- cache,
- transaction,
- ),
+ Operator::Limit(op) => read!(Limit, (op, plan.childrens.only())),
+ Operator::TopK(op) => read!(TopK<'a>, (op, plan.childrens.only())),
+ Operator::Values(op) => read!(Values<'a>, op),
+ Operator::Window(op) => read!(Window<'a>, (op, plan.childrens.only())),
+ Operator::ShowTable => read!(ShowTables<'a, T>, ()),
+ Operator::ShowView => read!(ShowViews<'a, T>, ()),
+ Operator::Explain => read!(Explain<'a>, plan.childrens.only()),
+ Operator::Describe(op) => read!(Describe, op),
+ Operator::Union(_) => read!(Union, plan.childrens.twins()),
+ Operator::RecursiveCte(_) => {
+ read!(RecursiveCte<'a, T>, plan.childrens.twins())
+ }
+ Operator::RecursiveScan(op) => read!(RecursiveScan, op),
Operator::SetMembership(op) => {
- let (left, right) = childrens.pop_twins();
- >::into_executor(
- SetMembership::from((op.kind, left, right)),
- arena,
- plan_arena,
- cache,
- transaction,
- )
+ let (left, right) = plan.childrens.twins();
+ read!(SetMembership, (op.kind, left, right))
}
_ => unreachable!(),
}
@@ -1018,7 +861,7 @@ where
pub(crate) fn build_write<'a, T>(
arena: &mut ExecArena<'a, T>,
plan_arena: &mut (dyn MetaArena + 'a),
- plan: LogicalPlan,
+ plan: &'a LogicalPlan,
cache: ExecutionContext<'a>,
transaction: &'a mut T,
) -> ExecId
@@ -1027,157 +870,44 @@ where
{
arena.init_context(cache, transaction);
let transaction_ref: &T = transaction;
- let LogicalPlan {
- operator,
- childrens,
- physical_option,
- ..
- } = plan;
-
- match operator {
- Operator::Insert(op) => {
- let input = childrens.pop_only();
-
- >::into_executor(
- Insert::from((op, input)),
+ macro_rules! write {
+ ($executor:ty, $input:expr) => {
+ <$executor as WriteExecutor<'a, T>>::into_executor(
+ $input,
arena,
plan_arena,
cache,
transaction_ref,
)
- }
- Operator::Update(op) => {
- let input = childrens.pop_only();
-
- >::into_executor(
- Update::from((op, input)),
- arena,
- plan_arena,
- cache,
- transaction_ref,
- )
- }
- Operator::Delete(op) => {
- let input = childrens.pop_only();
-
- >::into_executor(
- Delete::from((op, input)),
- arena,
- plan_arena,
- cache,
- transaction_ref,
- )
- }
- Operator::AddColumn(op) => >::into_executor(
- AddColumn::from(op),
- arena,
- plan_arena,
- cache,
- transaction_ref,
- ),
- Operator::ChangeColumn(op) => >::into_executor(
- ChangeColumn::from(op),
- arena,
- plan_arena,
- cache,
- transaction_ref,
- ),
- Operator::DropColumn(op) => >::into_executor(
- DropColumn::from(op),
- arena,
- plan_arena,
- cache,
- transaction_ref,
- ),
- Operator::CreateTable(op) => >::into_executor(
- CreateTable::from(op),
- arena,
- plan_arena,
- cache,
- transaction_ref,
- ),
- Operator::CreateIndex(op) => {
- let input = childrens.pop_only();
+ };
+ }
- >::into_executor(
- CreateIndex::from((op, input)),
- arena,
- plan_arena,
- cache,
- transaction_ref,
- )
- }
- Operator::CreateView(op) => >::into_executor(
- CreateView::from(op),
- arena,
- plan_arena,
- cache,
- transaction_ref,
- ),
- Operator::DropTable(op) => >::into_executor(
- DropTable::from(op),
- arena,
- plan_arena,
- cache,
- transaction_ref,
- ),
- Operator::DropView(op) => >::into_executor(
- DropView::from(op),
- arena,
- plan_arena,
- cache,
- transaction_ref,
- ),
- Operator::DropIndex(op) => >::into_executor(
- DropIndex::from(op),
- arena,
- plan_arena,
- cache,
- transaction_ref,
- ),
- Operator::Truncate(op) => >::into_executor(
- Truncate::from(op),
- arena,
- plan_arena,
- cache,
- transaction_ref,
- ),
+ match &plan.operator {
+ Operator::Insert(op) => write!(Insert<'a>, (op, plan.childrens.only())),
+ Operator::Update(op) => write!(Update<'a>, (op, plan.childrens.only())),
+ Operator::Delete(op) => write!(Delete<'a>, (op, plan.childrens.only())),
+ Operator::AddColumn(op) => write!(AddColumn<'a>, op),
+ Operator::ChangeColumn(op) => write!(ChangeColumn<'a>, op),
+ Operator::DropColumn(op) => write!(DropColumn<'a>, op),
+ Operator::CreateTable(op) => write!(CreateTable<'a>, op),
+ Operator::CreateIndex(op) => write!(CreateIndex<'a>, (op, plan.childrens.only())),
+ Operator::CreateView(op) => write!(CreateView<'a>, op),
+ Operator::DropTable(op) => write!(DropTable<'a>, op),
+ Operator::DropView(op) => write!(DropView<'a>, op),
+ Operator::DropIndex(op) => write!(DropIndex<'a>, op),
+ Operator::Truncate(op) => write!(Truncate<'a>, op),
+ #[cfg(feature = "copy")]
+ Operator::CopyFromFile(op) => write!(CopyFromFile<'a>, op),
#[cfg(feature = "copy")]
- Operator::CopyFromFile(op) => >::into_executor(
- CopyFromFile::from(op),
+ Operator::CopyToFile(op) => as ReadExecutor<'a, T>>::into_executor(
+ (op, plan.childrens.only()),
arena,
plan_arena,
cache,
transaction_ref,
),
- #[cfg(feature = "copy")]
- Operator::CopyToFile(op) => {
- let input = childrens.pop_only();
-
- >::into_executor(
- CopyToFile::from((op, input)),
- arena,
- plan_arena,
- cache,
- transaction_ref,
- )
- }
- Operator::Analyze(op) => {
- let input = childrens.pop_only();
-
- >::into_executor(
- Analyze::from((op, input)),
- arena,
- plan_arena,
- cache,
- transaction_ref,
- )
- }
- operator => {
- let mut plan = LogicalPlan::new(operator, *childrens);
- plan.physical_option = physical_option;
- build_read(arena, plan_arena, plan, cache, transaction_ref)
- }
+ Operator::Analyze(op) => write!(Analyze<'a>, (op, plan.childrens.only())),
+ _ => build_read(arena, plan_arena, plan, cache, transaction_ref),
}
}
@@ -1215,56 +945,6 @@ mod test_utils {
}
}
- pub(crate) fn execute<'a, T, E>(
- executor: E,
- cache: ExecutionContext<'a>,
- mut plan_arena: PlanArena<'a>,
- transaction: &'a T,
- ) -> TestExecutor<'a, T>
- where
- T: Transaction + 'a,
- E: ReadExecutor<'a, T, Input = E>,
- {
- let mut arena = ExecArena::new();
- arena.init_context(cache, transaction);
- let root = >::into_executor(
- executor,
- &mut arena,
- &mut plan_arena,
- cache,
- transaction,
- );
- TestExecutor {
- executor: Executor::new(arena, root),
- plan_arena,
- }
- }
-
- pub(crate) fn execute_mut<'a, T, E>(
- executor: E,
- cache: ExecutionContext<'a>,
- mut plan_arena: PlanArena<'a>,
- transaction: &'a T,
- ) -> TestExecutor<'a, T>
- where
- T: Transaction + 'a,
- E: WriteExecutor<'a, T, Input = E>,
- {
- let mut arena = ExecArena::new();
- arena.init_context(cache, transaction);
- let root = >::into_executor(
- executor,
- &mut arena,
- &mut plan_arena,
- cache,
- transaction,
- );
- TestExecutor {
- executor: Executor::new(arena, root),
- plan_arena,
- }
- }
-
pub(crate) fn execute_input<'a, T, E>(
input: E::Input,
cache: ExecutionContext<'a>,
@@ -1285,7 +965,7 @@ mod test_utils {
transaction,
);
TestExecutor {
- executor: Executor::new(arena, root),
+ executor: Executor::new(arena, root, PlanKeeper::empty()),
plan_arena,
}
}
@@ -1311,7 +991,7 @@ mod test_utils {
transaction,
);
TestExecutor {
- executor: Executor::new(arena, root),
+ executor: Executor::new(arena, root, PlanKeeper::empty()),
plan_arena,
}
}
@@ -1331,9 +1011,7 @@ mod test_utils {
#[cfg(all(test, not(target_arch = "wasm32")))]
#[allow(unused_imports)]
-pub(crate) use test_utils::{
- empty_context, execute, execute_input, execute_input_mut, execute_mut, try_collect,
-};
+pub(crate) use test_utils::{empty_context, execute_input, execute_input_mut, try_collect};
#[cfg(test)]
mod test {
diff --git a/src/expression/range_detacher.rs b/src/expression/range_detacher.rs
index a679a602..6aa3b966 100644
--- a/src/expression/range_detacher.rs
+++ b/src/expression/range_detacher.rs
@@ -200,22 +200,22 @@ fn build_tree<'a, T: Borrow + 'a, I: EqRangeInput + ?Sized>(
}
impl Range {
- pub(crate) fn bind_parameters(
- &mut self,
- params: &[(usize, DataValue)],
- ) -> Result<(), DatabaseError> {
+ pub(crate) fn bind_parameters<'p, F>(&mut self, get: &F) -> Result<(), DatabaseError>
+ where
+ F: Fn(usize) -> Option<&'p DataValue>,
+ {
match self {
Self::Scope { min, max } => {
for bound in [min, max] {
if let Bound::Included(value) | Bound::Excluded(value) = bound {
- value.bind_parameters(params)?;
+ value.bind_parameters(get)?;
}
}
}
- Self::Eq(value) => value.bind_parameters(params)?,
+ Self::Eq(value) => value.bind_parameters(get)?,
Self::SortedRanges(ranges) => {
for range in ranges {
- range.bind_parameters(params)?;
+ range.bind_parameters(get)?;
}
}
Self::Dummy => {}
diff --git a/src/optimizer/rule/normalization/elimination.rs b/src/optimizer/rule/normalization/elimination.rs
index 045a0e45..18d0a6b2 100644
--- a/src/optimizer/rule/normalization/elimination.rs
+++ b/src/optimizer/rule/normalization/elimination.rs
@@ -13,6 +13,7 @@
// limitations under the License.
use crate::errors::DatabaseError;
+use crate::expression::ScalarExpression;
use crate::optimizer::core::rule::NormalizationRule;
use crate::optimizer::plan_utils::{only_child_mut, replace_with_only_child, wrap_child_with};
use crate::planner::operator::limit::LimitOperator;
@@ -44,7 +45,7 @@ impl NormalizationRule for EliminateRedundantSort {
None => return Ok(false),
};
mark_sort_preserving_indexes(child, &sort_fields, arena)?;
- let can_remove = ensure_order(child, &sort_fields, arena);
+ let can_remove = ensure_order(child, &sort_fields, true, arena);
if !can_remove {
return Ok(false);
@@ -188,7 +189,10 @@ pub(crate) fn apply_scan_order_hint(
return Ok(());
}
for index_info in scan_op.index_infos.iter_mut() {
- if hint_covers(required, &index_info.sort_option, arena) {
+ if hint_covers(required, &index_info.sort_option, arena)
+ || (matches!(hint, OrderHintKind::SortElimination)
+ && hint_covers_reversed(required, &index_info.sort_option, arena))
+ {
let covered = hint_len(required);
match hint {
OrderHintKind::SortElimination => {
@@ -233,6 +237,19 @@ fn hint_covers(
}
}
+fn hint_covers_reversed(
+ required: ScanOrderHint<'_>,
+ provided: &SortOption,
+ arena: &crate::planner::PlanArena,
+) -> bool {
+ match required {
+ ScanOrderHint::SortFields(fields) => covers(fields, provided, |required, provided| {
+ sort_field_matches_reversed(required, provided, arena)
+ }),
+ ScanOrderHint::GroupBy(_) => false,
+ }
+}
+
pub(crate) fn groupby_sort_fields(groupby_exprs: &[ExprRef]) -> Vec {
groupby_exprs
.iter()
@@ -275,7 +292,7 @@ impl NormalizationRule for UseStreamAggregate {
Some(child) => child,
None => return Ok(false),
};
- if !ensure_order(child, &required, arena) {
+ if !ensure_order(child, &required, false, arena) {
return Ok(false);
}
@@ -353,18 +370,34 @@ pub(crate) fn apply_annotated_post_rules(
fn ensure_order(
plan: &mut LogicalPlan,
required: &[SortField],
+ allow_reverse: bool,
arena: &crate::planner::PlanArena,
) -> bool {
if let Some(PhysicalOption {
plan: PlanImpl::IndexScan(index_info),
- ..
- }) = plan.physical_option.as_ref()
+ sort_option,
+ }) = plan.physical_option.as_mut()
{
if covers(required, &index_info.sort_option, |required, provided| {
sort_field_matches(required, provided, arena)
}) {
return true;
}
+ let scan_limited = matches!(
+ &plan.operator,
+ Operator::TableScan(scan) if scan.limit != (None, None)
+ );
+ if allow_reverse
+ && !scan_limited
+ && !index_info.is_reverse()
+ && covers(required, &index_info.sort_option, |required, provided| {
+ sort_field_matches_reversed(required, provided, arena)
+ })
+ {
+ index_info.reverse_order();
+ *sort_option = index_info.sort_option.clone();
+ return true;
+ }
}
if let Some(physical_option) = plan.physical_option.as_ref() {
@@ -377,8 +410,11 @@ fn ensure_order(
);
}
SortOption::Follow => {
+ // Limit keeps the first N rows in scan order. Reversing the scan below it
+ // would keep the last N rows instead, changing the result set.
+ let allow_reverse = allow_reverse && !matches!(plan.operator, Operator::Limit(_));
if let Childrens::Only(child) = plan.childrens.as_mut() {
- if ensure_order(child, required, arena) {
+ if ensure_order(child, required, allow_reverse, arena) {
return true;
}
}
@@ -400,6 +436,23 @@ fn sort_field_matches(
&& required.expr.eq_ignore_colref_pos(provided.expr, arena)
}
+fn sort_field_matches_reversed(
+ required: &SortField,
+ provided: &SortField,
+ arena: &crate::planner::PlanArena,
+) -> bool {
+ required.asc != provided.asc
+ && (required.nulls_first != provided.nulls_first || !expr_nullable(required.expr, arena))
+ && required.expr.eq_ignore_colref_pos(provided.expr, arena)
+}
+
+fn expr_nullable(expr: ExprRef, arena: &crate::planner::PlanArena) -> bool {
+ match arena.expression(expr) {
+ ScalarExpression::ColumnRef { column, .. } => arena.column(*column).nullable(),
+ _ => true,
+ }
+}
+
pub(crate) fn covers(
required: &[T],
provided: &SortOption,
@@ -449,6 +502,7 @@ mod tests {
use crate::optimizer::core::rule::NormalizationRule;
use crate::planner::operator::aggregate::AggregateOperator;
use crate::planner::operator::filter::FilterOperator;
+ use crate::planner::operator::limit::LimitOperator;
use crate::planner::operator::sort::{SortField, SortOperator};
use crate::planner::operator::table_scan::TableScanOperator;
use crate::planner::operator::top_k::TopKOperator;
@@ -764,6 +818,88 @@ mod tests {
Ok(())
}
+ fn desc_topk_plan(
+ arena: &mut crate::planner::PlanArena,
+ nullable: bool,
+ ) -> (LogicalPlan, SortField) {
+ let mut column = ColumnCatalog::new_dummy("c1".to_string());
+ column.set_nullable(nullable);
+ let column = arena.alloc_column(column);
+ let index_field = SortField::new(
+ arena.alloc_expression(ScalarExpression::column_expr(column, 0)),
+ true,
+ false,
+ );
+ let required = SortField::new(index_field.expr, false, false);
+ let mut plan = build_plan(arena, vec![required.clone()], vec![index_field], 0);
+ plan.operator = Operator::TopK(TopKOperator {
+ sort_fields: vec![required.clone()],
+ limit: 1,
+ offset: None,
+ });
+ (plan, required)
+ }
+
+ fn scan_is_reverse(plan: &LogicalPlan) -> bool {
+ let mut plan = plan;
+ loop {
+ if let Some(PhysicalOption {
+ plan: PlanImpl::IndexScan(info),
+ ..
+ }) = &plan.physical_option
+ {
+ return info.is_reverse();
+ }
+ match plan.childrens.as_ref() {
+ Childrens::Only(child) => plan = child,
+ _ => return false,
+ }
+ }
+ }
+
+ #[test]
+ fn remove_desc_topk_by_reversing_non_null_index() -> Result<(), DatabaseError> {
+ let table_arena = crate::planner::TableArenaCell::default();
+ let mut arena = crate::planner::PlanArena::new(&table_arena);
+ let (mut plan, _) = desc_topk_plan(&mut arena, false);
+
+ assert!(EliminateRedundantSort.apply(&mut plan, &mut arena)?);
+ assert!(matches!(plan.operator, Operator::Limit(_)));
+ assert!(scan_is_reverse(&plan));
+ Ok(())
+ }
+
+ #[test]
+ fn keep_desc_topk_on_nullable_index() -> Result<(), DatabaseError> {
+ let table_arena = crate::planner::TableArenaCell::default();
+ let mut arena = crate::planner::PlanArena::new(&table_arena);
+ let (mut plan, _) = desc_topk_plan(&mut arena, true);
+
+ assert!(!EliminateRedundantSort.apply(&mut plan, &mut arena)?);
+ assert!(matches!(plan.operator, Operator::TopK(_)));
+ assert!(!scan_is_reverse(&plan));
+ Ok(())
+ }
+
+ #[test]
+ fn keep_desc_topk_when_limit_is_below() -> Result<(), DatabaseError> {
+ let table_arena = crate::planner::TableArenaCell::default();
+ let mut arena = crate::planner::PlanArena::new(&table_arena);
+ let (mut plan, _) = desc_topk_plan(&mut arena, false);
+ let Childrens::Only(filter) = plan.childrens.as_mut() else {
+ unreachable!()
+ };
+ filter.operator = Operator::Limit(LimitOperator {
+ offset: None,
+ limit: Some(3),
+ });
+ filter.physical_option = Some(PhysicalOption::new(PlanImpl::Limit, SortOption::Follow));
+
+ assert!(!EliminateRedundantSort.apply(&mut plan, &mut arena)?);
+ assert!(!scan_is_reverse(&plan));
+ Ok(())
+ }
+
#[test]
fn remove_sort_when_prefix_can_be_ignored() -> Result<(), DatabaseError> {
let table_arena = crate::planner::TableArenaCell::default();
diff --git a/src/planner/arena.rs b/src/planner/arena.rs
index b96bb8e2..21b66dbb 100644
--- a/src/planner/arena.rs
+++ b/src/planner/arena.rs
@@ -145,6 +145,14 @@ pub trait MetaArena {
panic!("parent expressions are immutable")
}
+ fn has_bound_params(&self) -> bool {
+ false
+ }
+
+ fn bound_param(&self, _id: usize) -> Option<&DataValue> {
+ None
+ }
+
fn alloc_dummy(&mut self, name: &str) -> ColumnRef {
self.table_arena_cell().borrow().alloc_dummy(name)
}
@@ -192,6 +200,12 @@ impl MetaArena for Box {
{
(**self).table_arena_cell()
}
+ fn has_bound_params(&self) -> bool {
+ (**self).has_bound_params()
+ }
+ fn bound_param(&self, id: usize) -> Option<&DataValue> {
+ (**self).bound_param(id)
+ }
fn alloc_column(&mut self, column: ColumnCatalog) -> ColumnRef {
(**self).alloc_column(column)
}
@@ -562,7 +576,11 @@ impl<'a> PlanArena<'a> {
) -> Result<(), crate::errors::DatabaseError> {
for expression in &mut self.expressions {
if let ScalarExpression::Constant(value) = &mut *(ArenaExprMut { expr: expression }) {
- value.bind_parameters(params)?;
+ value.bind_parameters(&|id| {
+ params
+ .iter()
+ .find_map(|(candidate, value)| (*candidate == id).then_some(value))
+ })?;
}
}
Ok(())
@@ -825,7 +843,7 @@ impl MetaArena for PlanArena<'_> {
/// Execution-local parameter values and temporary expressions over an immutable plan arena.
pub(crate) struct ParamArena<'a> {
parent: &'a PlanArena<'a>,
- expressions: Vec,
+ expressions: Vec<(Option, ArenaExpr)>,
parameter_count: usize,
parent_end: usize,
}
@@ -839,12 +857,19 @@ impl<'a> ParamArena<'a> {
let mut expressions = Vec::with_capacity(parameter_expressions.len());
for (slot, &expr_ref) in parameter_expressions.iter().enumerate() {
debug_assert_eq!(parent.arena_expression(expr_ref).param, Some(slot));
- let ScalarExpression::Constant(value) = parent.expression(expr_ref) else {
- unreachable!("parameter expression must be a constant");
+ let ScalarExpression::Constant(value @ DataValue::Parameter { id, .. }) =
+ parent.expression(expr_ref)
+ else {
+ unreachable!("parameter expression must be a single parameter constant");
};
+ let id = *id;
let mut value = value.clone();
- value.bind_parameters(params)?;
- expressions.push(ArenaExpr::new(ScalarExpression::Constant(value)));
+ value.bind_parameters(&|id| {
+ params
+ .iter()
+ .find_map(|(candidate, value)| (*candidate == id).then_some(value))
+ })?;
+ expressions.push((Some(id), ArenaExpr::new(ScalarExpression::Constant(value))));
}
Ok(Self {
parent,
@@ -862,6 +887,17 @@ impl MetaArena for ParamArena<'_> {
{
self.parent.table_arena_cell()
}
+ fn has_bound_params(&self) -> bool {
+ true
+ }
+ fn bound_param(&self, id: usize) -> Option<&DataValue> {
+ self.expressions
+ .iter()
+ .find_map(|(slot, expr)| match (slot, &expr.expression) {
+ (Some(slot), ScalarExpression::Constant(value)) if *slot == id => Some(value),
+ _ => None,
+ })
+ }
fn column(&self, column: ColumnRef) -> &ColumnCatalog {
self.parent.column(column)
}
@@ -878,14 +914,16 @@ impl MetaArena for ParamArena<'_> {
if expr.pos() < self.parent_end {
let parent = self.parent.arena_expression(expr);
return match parent.param {
- Some(slot) => &self.expressions[slot].expression,
+ Some(slot) => &self.expressions[slot].1.expression,
None => &parent.expression,
};
}
- &self.expressions[self.parameter_count + expr.pos() - self.parent_end].expression
+ &self.expressions[self.parameter_count + expr.pos() - self.parent_end]
+ .1
+ .expression
}
fn expression_mut(&mut self, expr: ExprRef) -> ArenaExprMut<'_> {
- let index = if expr.pos() < self.parent_end {
+ let expr = if expr.pos() < self.parent_end {
self.parent
.arena_expression(expr)
.param
@@ -894,12 +932,12 @@ impl MetaArena for ParamArena<'_> {
self.parameter_count + expr.pos() - self.parent_end
};
ArenaExprMut {
- expr: &mut self.expressions[index],
+ expr: &mut self.expressions[expr].1,
}
}
fn alloc_expression(&mut self, expression: ScalarExpression) -> ExprRef {
let id = ExprRef::new(self.parent_end + self.expressions.len() - self.parameter_count);
- self.expressions.push(ArenaExpr::new(expression));
+ self.expressions.push((None, ArenaExpr::new(expression)));
id
}
fn alloc_column(&mut self, _: ColumnCatalog) -> ColumnRef {
@@ -951,11 +989,10 @@ mod tests {
let mut parent = PlanArena::new(&root);
let id = parent.alloc_expression(ScalarExpression::Constant(DataValue::Int32(1)));
assert_eq!(parent.arena_expression(id).param, None);
- *parent.expression_mut(id) =
- ScalarExpression::Constant(DataValue::Tuple(vec![DataValue::Parameter {
- id: 1,
- ty: LogicalType::Integer,
- }]));
+ *parent.expression_mut(id) = ScalarExpression::Constant(DataValue::Parameter {
+ id: 1,
+ ty: LogicalType::Integer,
+ });
let parameter_expressions = parent.parameter_expressions();
assert_eq!(parent.arena_expression(id).param, Some(0));
{
@@ -963,7 +1000,7 @@ mod tests {
ParamArena::new(&parent, ¶meter_expressions, &[(1, DataValue::Int32(7))])?;
assert_eq!(
bound.expression(id),
- &ScalarExpression::Constant(DataValue::Tuple(vec![DataValue::Int32(7)]))
+ &ScalarExpression::Constant(DataValue::Int32(7))
);
}
*parent.expression_mut(id) = ScalarExpression::Constant(DataValue::Int32(2));
diff --git a/src/planner/mod.rs b/src/planner/mod.rs
index 2e95e613..484e5585 100644
--- a/src/planner/mod.rs
+++ b/src/planner/mod.rs
@@ -14,6 +14,7 @@
mod arena;
pub mod operator;
+mod plan_keeper;
use crate::catalog::TableName;
use crate::errors::DatabaseError;
@@ -30,6 +31,7 @@ use std::hash::{Hash, Hasher};
pub use arena::{ExprRef, MetaArena, PlanArena, TableArena, TableArenaCell};
pub(crate) use arena::{ParamArena, PlanRef};
+pub(crate) use plan_keeper::{PlanInput, PlanKeeper};
pub(crate) trait Explain {
fn fmt(&self, arena: &(dyn MetaArena + '_), f: &mut fmt::Formatter<'_>) -> fmt::Result;
@@ -86,6 +88,20 @@ impl Childrens {
}
}
+ pub(crate) fn only(&self) -> &LogicalPlan {
+ match self {
+ Childrens::Only(plan) => plan,
+ _ => unreachable!(),
+ }
+ }
+
+ pub(crate) fn twins(&self) -> (&LogicalPlan, &LogicalPlan) {
+ match self {
+ Childrens::Twins { left, right } => (left, right),
+ _ => unreachable!(),
+ }
+ }
+
pub fn pop_only(self) -> LogicalPlan {
match self {
Childrens::Only(plan) => *plan,
@@ -213,18 +229,6 @@ impl LogicalPlan {
output_schema.get_or_insert_with(|| Self::compute_output_schema(operator, childrens, arena))
}
- pub fn take_schema(&mut self, arena: &mut (dyn MetaArena + '_)) -> crate::types::tuple::Schema {
- let LogicalPlan {
- operator,
- childrens,
- output_schema,
- ..
- } = self;
- output_schema
- .take()
- .unwrap_or_else(|| Self::compute_output_schema(operator, childrens, arena))
- }
-
fn compute_output_schema(
operator: &mut Operator,
childrens: &mut Childrens,
@@ -349,6 +353,24 @@ impl LogicalPlan {
.collect()
}
+ pub(crate) fn read_schema(&self) -> &crate::types::tuple::Schema {
+ self.output_schema
+ .as_ref()
+ .expect("output schema must be computed before it is read")
+ }
+
+ pub(crate) fn populate_output_schema_recursive(&mut self, arena: &mut (dyn MetaArena + '_)) {
+ match self.childrens.as_mut() {
+ Childrens::Only(child) => child.populate_output_schema_recursive(arena),
+ Childrens::Twins { left, right } => {
+ left.populate_output_schema_recursive(arena);
+ right.populate_output_schema_recursive(arena);
+ }
+ Childrens::None => (),
+ }
+ self.output_schema(arena);
+ }
+
pub fn reset_output_schema_cache(&mut self) {
self.output_schema = None;
}
diff --git a/src/planner/operator/copy_from_file.rs b/src/planner/operator/copy_from_file.rs
index 5e00ca92..a6c3418d 100644
--- a/src/planner/operator/copy_from_file.rs
+++ b/src/planner/operator/copy_from_file.rs
@@ -15,7 +15,7 @@
use crate::binder::copy::ExtSource;
use crate::catalog::TableName;
use crate::planner::MetaArena;
-use crate::planner::{fmt_explain_list, Explain, PlanArena};
+use crate::planner::{fmt_explain_list, Explain};
use crate::types::tuple::Schema;
use kite_sql_serde_macros::ReferenceSerialization;
diff --git a/src/planner/operator/mod.rs b/src/planner/operator/mod.rs
index 478947ac..c038066e 100644
--- a/src/planner/operator/mod.rs
+++ b/src/planner/operator/mod.rs
@@ -157,7 +157,7 @@ pub enum SortOption {
#[derive(Debug, PartialEq, Eq, Clone, Hash, ReferenceSerialization)]
pub struct PhysicalOption {
pub plan: PlanImpl,
- sort_option: SortOption,
+ pub(crate) sort_option: SortOption,
}
impl PhysicalOption {
diff --git a/src/planner/plan_keeper.rs b/src/planner/plan_keeper.rs
new file mode 100644
index 00000000..60e0dc2c
--- /dev/null
+++ b/src/planner/plan_keeper.rs
@@ -0,0 +1,73 @@
+// Copyright 2024 KipData/KiteSQL
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+use crate::planner::LogicalPlan;
+
+#[allow(clippy::large_enum_variant)]
+pub(crate) enum PlanInput<'p> {
+ Owned(LogicalPlan),
+ Borrowed(&'p LogicalPlan),
+}
+
+impl From for PlanInput<'_> {
+ fn from(plan: LogicalPlan) -> Self {
+ PlanInput::Owned(plan)
+ }
+}
+
+pub(crate) struct PlanKeeper<'p> {
+ owned: *mut LogicalPlan,
+ borrowed: Option<&'p LogicalPlan>,
+}
+
+impl<'p> PlanKeeper<'p> {
+ #[cfg(all(test, not(target_arch = "wasm32")))]
+ pub(crate) fn empty() -> Self {
+ Self {
+ owned: std::ptr::null_mut(),
+ borrowed: None,
+ }
+ }
+
+ pub(crate) fn new(input: PlanInput<'p>) -> Self {
+ match input {
+ PlanInput::Owned(plan) => Self {
+ owned: Box::into_raw(Box::new(plan)),
+ borrowed: None,
+ },
+ PlanInput::Borrowed(plan) => Self {
+ owned: std::ptr::null_mut(),
+ borrowed: Some(plan),
+ },
+ }
+ }
+
+ pub(crate) fn plan(&self) -> &'p LogicalPlan {
+ match self.borrowed {
+ Some(plan) => plan,
+ // SAFETY: `owned` is a heap allocation freed only in `Drop`, and is
+ // never moved or mutated afterwards.
+ None => unsafe { &*self.owned },
+ }
+ }
+}
+
+impl Drop for PlanKeeper<'_> {
+ fn drop(&mut self) {
+ if !self.owned.is_null() {
+ // SAFETY: `owned` comes from `Box::into_raw` and is only freed here.
+ drop(unsafe { Box::from_raw(self.owned) });
+ }
+ }
+}
diff --git a/src/storage/lmdb.rs b/src/storage/lmdb.rs
index 39659355..6cfa872c 100644
--- a/src/storage/lmdb.rs
+++ b/src/storage/lmdb.rs
@@ -15,14 +15,13 @@
use crate::errors::DatabaseError;
use crate::storage::table_codec::Bytes;
use crate::storage::{
- bytes_bound_as_slice, owned_bound, reuse_bound_as_excluded, InnerIter, KeyValueRef, Storage,
+ bounds_contain, bytes_bound_as_slice, owned_bound, InnerIter, KeyValueRef, Storage,
Transaction, TransactionIsolationLevel,
};
use lmdb::{
Cursor, Database, DatabaseFlags, Environment, EnvironmentFlags, RoCursor, RwTransaction,
Transaction as _, WriteFlags,
};
-use std::cmp::Ordering;
use std::collections::Bound;
use std::fmt::{self, Display, Formatter};
use std::fs;
@@ -133,7 +132,11 @@ impl Storage for LmdbStorage {
self.validate_transaction_isolation(isolation)?;
let tx = self.env.begin_rw_txn()?;
- Ok(LmdbTransaction { tx, db: self.db })
+ Ok(LmdbTransaction {
+ tx,
+ db: self.db,
+ statements: 0,
+ })
}
fn default_transaction_isolation(&self) -> TransactionIsolationLevel {
@@ -161,45 +164,115 @@ impl Storage for LmdbStorage {
pub struct LmdbTransaction<'env> {
tx: RwTransaction<'env>,
db: Database,
+ statements: u64,
}
pub struct LmdbIter<'txn> {
- _cursor: RoCursor<'txn>,
- iter: lmdb::Iter<'txn>,
- pending: Option<(&'txn [u8], &'txn [u8])>,
- max: Bound,
- done: bool,
+ cursor: RoCursor<'txn>,
+ scan: Scan,
}
-impl LmdbIter<'_> {
- fn next_visible(&mut self) -> Option<(&[u8], &[u8])> {
- if let Some(entry) = self.pending.take() {
- if within_upper_bound(entry.0, bytes_bound_as_slice(&self.max)) {
- return Some(entry);
- }
- self.done = true;
- return None;
+struct Scan {
+ step: Step,
+ range: BytesRange,
+ reverse: bool,
+}
+
+enum Step {
+ Seek,
+ Last,
+ Move,
+ Done,
+}
+
+type BytesRange = (Bound, Bound);
+
+impl Scan {
+ fn new(min: Bound<&[u8]>, max: Bound<&[u8]>, reverse: bool) -> Self {
+ Self {
+ step: Step::Seek,
+ range: (owned_bound(min), owned_bound(max)),
+ reverse,
+ }
+ }
+
+ fn start(&self) -> Bound<&[u8]> {
+ if self.reverse {
+ bytes_bound_as_slice(&self.range.1)
+ } else {
+ bytes_bound_as_slice(&self.range.0)
}
+ }
- if let Some((key, value)) = self.iter.next() {
- if !within_upper_bound(key, bytes_bound_as_slice(&self.max)) {
- self.done = true;
- return None;
- }
- return Some((key, value));
+ fn before_start(&self, key: &[u8]) -> bool {
+ if self.reverse {
+ !bounds_contain(Bound::Unbounded, self.start(), key)
+ } else {
+ !bounds_contain(self.start(), Bound::Unbounded, key)
}
+ }
- self.done = true;
- None
+ fn contains(&self, key: &[u8]) -> bool {
+ bounds_contain(
+ bytes_bound_as_slice(&self.range.0),
+ bytes_bound_as_slice(&self.range.1),
+ key,
+ )
+ }
+
+ fn next<'txn, C: Cursor<'txn>>(
+ &mut self,
+ cursor: &C,
+ ) -> Result