Skip to content

Commit 96d8f74

Browse files
authored
refactor: De-generic executor and refactor ORM stack (#349)
* refactor: streamline catalog state and execution caches * refactor: reuse table codec buffers * refactor: introduce metadata arenas for execution state * refactor: normalize catalog metadata arenas * refactor: use static evaluator dispatch refs * feat: show column refs in describe output * refactor: replace bincode and serde serialization * perf: reduce execution allocations and shell dependencies * perf: reserve column pruning removed positions * feat: make optional dependencies feature-gated * refactor: make binder consume owned statements * refactor: isolate parser and ORM binding APIs * refactor: bind ORM queries through binder plans * refactor: de-generic executor to reduce compile time Remove the transaction type parameter from executor nodes and route execution state through a runtime trait so executor code is compiled once instead of monomorphized per storage backend. * test: update runtime test helpers * fix: expose wasm ddl and analyze APIs * fix: resolve CI failures after executor de-generic refactor * refactor: unify execution context cache handling * fix: avoid cloning prepared statements during binding * Reduce projection and tuple serialization allocations * Update TPCC results and clean clippy warnings
1 parent c0e63a0 commit 96d8f74

254 files changed

Lines changed: 22478 additions & 20278 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

Cargo.lock

Lines changed: 4 additions & 598 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Cargo.toml

Lines changed: 24 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22

33
[package]
44
name = "kite_sql"
5-
version = "0.2.1"
5+
version = "0.3.0"
66
edition = "2021"
77
build = "build.rs"
88
authors = ["Kould <kould2333@gmail.com>", "Xwg <loloxwg@gmail.com>"]
@@ -13,32 +13,30 @@ repository = "https://github.com/KipData/KiteSQL"
1313
readme = "README.md"
1414
keywords = ["sql", "sqlite", "database", "mysql"]
1515
categories = ["development-tools", "database"]
16-
default-run = "kite_sql"
17-
18-
[[bin]]
19-
name = "kite_sql"
20-
path = "src/bin/server.rs"
21-
required-features = ["net", "rocksdb"]
2216

2317
[[bin]]
2418
name = "kitesql-shell"
2519
path = "src/bin/shell.rs"
26-
required-features = ["rocksdb"]
20+
required-features = ["rocksdb", "shell"]
2721

2822
[lib]
2923
doctest = false
3024
crate-type = ["cdylib", "rlib"]
3125

3226
[features]
33-
default = ["macros", "rocksdb"]
27+
default = ["time", "macros", "parser", "rocksdb"]
28+
time = ["dep:chrono"]
29+
copy = ["dep:csv"]
30+
decimal = ["dep:rust_decimal"]
3431
macros = []
35-
orm = []
32+
orm = ["macros"]
33+
parser = ["dep:sqlparser"]
3634
rocksdb = ["dep:rocksdb"]
3735
unsafe_txdb_checkpoint = ["rocksdb", "dep:librocksdb-sys"]
3836
lmdb = ["dep:lmdb", "dep:lmdb-sys"]
39-
net = ["rocksdb", "dep:pgwire", "dep:async-trait", "dep:clap", "dep:env_logger", "dep:futures", "dep:log", "dep:tokio"]
4037
pprof = ["pprof/criterion", "pprof/flamegraph"]
41-
python = ["dep:pyo3"]
38+
python = ["parser", "dep:pyo3"]
39+
shell = ["parser", "dep:comfy-table", "dep:rustyline"]
4240

4341
[[bench]]
4442
name = "query_bench"
@@ -48,36 +46,24 @@ required-features = ["pprof"]
4846

4947
[dependencies]
5048
ahash = { version = "0.8" }
51-
bincode = { version = "1" }
52-
bumpalo = { version = "3", features = ["allocator-api2", "collections", "std"] }
49+
bumpalo = { version = "3", default-features = false, features = ["collections"] }
5350
byteorder = { version = "1" }
54-
chrono = { version = "0.4" }
55-
comfy-table = { version = "7", default-features = false }
56-
csv = { version = "1" }
5751
fixedbitset = { version = "0.4" }
5852
itertools = { version = "0.12" }
59-
ordered-float = { version = "4", features = ["serde"] }
53+
ordered-float = { version = "4" }
6054
paste = { version = "1" }
61-
parking_lot = { version = "0.12", features = ["arc_lock"] }
62-
pyo3 = { version = "0.23", features = ["auto-initialize"], optional = true }
6355
recursive = { version = "0.1" }
64-
rust_decimal = { version = "1" }
65-
serde = { version = "1", features = ["derive", "rc"] }
6656
kite_sql_serde_macros = { version = "0.2.0", path = "kite_sql_serde_macros" }
67-
siphasher = { version = "1", features = ["serde"] }
68-
sqlparser = { version = "0.61", default-features = false, features = ["std"] }
69-
thiserror = { version = "1" }
70-
ulid = { version = "1", features = ["serde"] }
71-
72-
# Feature: net
73-
async-trait = { version = "0.1", optional = true }
74-
clap = { version = "4.5", features = ["derive"], optional = true }
75-
env_logger = { version = "0.11", optional = true }
76-
futures = { version = "0.3", optional = true }
77-
log = { version = "0.4", optional = true }
78-
pgwire = { version = "0.28.0", optional = true }
79-
tokio = { version = "1.36", features = ["full"], optional = true }
57+
siphasher = { version = "1" }
58+
ulid = { version = "1" }
8059

60+
# Optional dependencies for features
61+
comfy-table = { version = "7", default-features = false, optional = true }
62+
chrono = { version = "0.4", optional = true }
63+
csv = { version = "1", optional = true }
64+
pyo3 = { version = "0.23", features = ["auto-initialize"], optional = true }
65+
rust_decimal = { version = "1", default-features = false, features = ["std"], optional = true }
66+
sqlparser = { version = "0.61", default-features = false, features = ["std"], optional = true }
8167

8268
[target.'cfg(unix)'.dev-dependencies]
8369
pprof = { version = "0.15", features = ["flamegraph", "criterion"] }
@@ -90,11 +76,12 @@ tempfile = { version = "3.10" }
9076
sqlite = { version = "0.34" }
9177

9278
[target.'cfg(not(target_arch = "wasm32"))'.dependencies]
93-
rocksdb = { version = "0.23", optional = true, default-features = false, features = ["bindgen-runtime"] }
79+
# Optional dependencies for features
9480
librocksdb-sys = { version = "0.17.1", optional = true }
9581
lmdb = { version = "0.8.0", optional = true }
9682
lmdb-sys = { version = "0.8.0", optional = true }
97-
rustyline = { version = "14", default-features = false }
83+
rocksdb = { version = "0.23", optional = true, default-features = false, features = ["bindgen-runtime"] }
84+
rustyline = { version = "14", default-features = false, optional = true }
9885

9986
[target.'cfg(target_arch = "wasm32")'.dependencies]
10087
wasm-bindgen = { version = "0.2.106" }
@@ -106,7 +93,6 @@ base64 = { version = "0.21" }
10693
getrandom = { version = "0.2", features = ["js"] }
10794
getrandom_03 = { package = "getrandom", version = "0.3", features = ["wasm_js"] }
10895
js-sys = { version = "0.3.83" }
109-
serde-wasm-bindgen = { version = "0.6.5" }
11096
once_cell = { version = "1" }
11197

11298
[target.'cfg(target_arch = "wasm32")'.dev-dependencies]

Makefile

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ test:
1818

1919
## Run Python binding API tests implemented with pyo3.
2020
test-python:
21-
PYO3_PYTHON=$(PYO3_PYTHON) $(CARGO) test --features python test_python_
21+
PYO3_PYTHON=$(PYO3_PYTHON) $(CARGO) test --features python,decimal test_python_
2222

2323
## Perform a `cargo check` across the workspace.
2424
cargo-check:

README.md

Lines changed: 30 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -55,7 +55,8 @@ For the full ORM guide, see [`src/orm/README.md`](src/orm/README.md).
5555
```rust
5656
use kite_sql::db::DataBaseBuilder;
5757
use kite_sql::errors::DatabaseError;
58-
use kite_sql::{Model, Projection};
58+
use kite_sql::orm::OrmQueryResultExt;
59+
use kite_sql::Model;
5960

6061
#[derive(Default, Debug, PartialEq, Model)]
6162
#[model(table = "users")]
@@ -71,15 +72,8 @@ struct User {
7172
age: Option<i32>,
7273
}
7374

74-
#[derive(Default, Debug, PartialEq, Projection)]
75-
struct UserSummary {
76-
id: i32,
77-
#[projection(rename = "user_name")]
78-
display_name: String,
79-
}
80-
8175
fn main() -> Result<(), DatabaseError> {
82-
let database = DataBaseBuilder::path("./data").build_rocksdb()?;
76+
let mut database = DataBaseBuilder::path("./data").build_rocksdb()?;
8377
// Or: let database = DataBaseBuilder::path("./data").build_lmdb()?;
8478

8579
database.migrate::<User>()?;
@@ -100,24 +94,31 @@ fn main() -> Result<(), DatabaseError> {
10094
])?;
10195

10296
database
103-
.from::<User>()
104-
.eq(User::id(), 1)
105-
.update()
106-
.set(User::age(), Some(19))
107-
.execute()?;
97+
.bind(|ctx| {
98+
ctx.mutate::<User>()?
99+
.filter(|e| e.column(User::id())?.eq(1))?
100+
.update(|u| u.set_value(User::age(), Some(19)))
101+
})?
102+
.done()?;
108103

109104
database
110-
.from::<User>()
111-
.eq(User::id(), 2)
112-
.delete()?;
105+
.bind(|ctx| {
106+
ctx.mutate::<User>()?
107+
.filter(|e| e.column(User::id())?.eq(2))?
108+
.delete()
109+
})?
110+
.done()?;
113111

114112
let users = database
115-
.from::<User>()
116-
.gte(User::age(), 18)
117-
.project::<UserSummary>()
118-
.asc(User::name())
119-
.limit(10)
120-
.fetch()?;
113+
.bind(|ctx| {
114+
ctx.from::<User>()?
115+
.filter(|e| e.column(User::age())?.gte(18))?
116+
.project_scalars((User::id(), User::name()))?
117+
.order_by(User::name())?
118+
.limit(10)?
119+
.finish()
120+
})?
121+
.project_tuple::<(i32, String)>();
121122

122123
for user in users {
123124
println!("{:?}", user?);
@@ -138,6 +139,7 @@ fn main() -> Result<(), DatabaseError> {
138139
- Transaction isolation is documented in [`docs/transaction-isolation.md`](docs/transaction-isolation.md).
139140
- Cargo features:
140141
- `rocksdb` is enabled by default
142+
- `parser` is enabled by default and provides the SQL parser frontend
141143
- `lmdb` is optional
142144
- `unsafe_txdb_checkpoint` enables experimental checkpoint support for RocksDB `TransactionDB`
143145
- `cargo check --no-default-features --features lmdb` builds an LMDB-only native configuration
@@ -164,7 +166,7 @@ Checkpoint support and feature-gating details are documented in [docs/features.m
164166
import { WasmDatabase } from "./pkg/kite_sql.js";
165167

166168
const db = new WasmDatabase();
167-
await db.execute("create table demo(id int primary key, v int)");
169+
await db.ddl("create table demo(id int primary key, v int)");
168170
await db.execute("insert into demo values (1, 2), (2, 4)");
169171
const rows = db.run("select * from demo").rows();
170172
console.log(rows.map((r) => r.values.map((v) => v.Int32 ?? v)));
@@ -198,10 +200,10 @@ Recent 720-second local comparison on the machine above:
198200
199201
| Backend | TpmC | New-Order p90 | Payment p90 | Order-Status p90 | Delivery p90 | Stock-Level p90 |
200202
| --- | ---: | ---: | ---: | ---: | ---: | ---: |
201-
| KiteSQL LMDB | 68394 | 0.001s | 0.001s | 0.001s | 0.002s | 0.001s |
202-
| KiteSQL RocksDB | 30387 | 0.001s | 0.001s | 0.001s | 0.015s | 0.002s |
203-
| SQLite balanced | 41690 | 0.001s | 0.001s | 0.001s | 0.001s | 0.001s |
204-
| SQLite practical | 38861 | 0.001s | 0.001s | 0.001s | 0.001s | 0.001s |
203+
| KiteSQL LMDB | 61723 | 0.001s | 0.001s | 0.001s | 0.002s | 0.001s |
204+
| KiteSQL RocksDB | 30446 | 0.001s | 0.001s | 0.001s | 0.016s | 0.002s |
205+
| SQLite balanced | 42989 | 0.001s | 0.001s | 0.001s | 0.001s | 0.001s |
206+
| SQLite practical | 42276 | 0.001s | 0.001s | 0.001s | 0.001s | 0.001s |
205207
206208
The detailed raw outputs are recorded in [tpcc/README.md](tpcc/README.md).
207209
#### 👉[check more](tpcc/README.md)

examples/hello_world.rs

Lines changed: 38 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,7 @@ mod app {
4747
pub c2: String,
4848
}
4949

50-
fn run_with_database<S: Storage>(database: Database<S>) -> Result<(), DatabaseError> {
50+
fn run_with_database<S: Storage>(mut database: Database<S>) -> Result<(), DatabaseError> {
5151
database.create_table_if_not_exists::<MyStruct>()?;
5252
database.insert(&MyStruct {
5353
c1: 0,
@@ -63,22 +63,25 @@ mod app {
6363
})?;
6464

6565
database
66-
.from::<MyStruct>()
67-
.eq(MyStruct::c1(), 1)
68-
.update()
69-
.set(MyStruct::c2(), "ONE")
70-
.execute()?;
71-
database.from::<MyStruct>().eq(MyStruct::c1(), 2).delete()?;
66+
.bind(|ctx| {
67+
ctx.mutate::<MyStruct>()?
68+
.filter(|e| e.column(MyStruct::c1())?.eq(1))?
69+
.update(|u| u.set_value(MyStruct::c2(), "ONE"))
70+
})?
71+
.done()?;
72+
database
73+
.bind(|ctx| {
74+
ctx.mutate::<MyStruct>()?
75+
.filter(|e| e.column(MyStruct::c1())?.eq(2))?
76+
.delete()
77+
})?
78+
.done()?;
7279

7380
for row in database.fetch::<MyStruct>()? {
7481
println!("{:?}", row?);
7582
}
7683

77-
let mut agg = database.run("select count(*) from my_struct")?;
78-
if let Some(count_row) = agg.next() {
79-
println!("row count = {:?}", count_row?);
80-
}
81-
agg.done()?;
84+
println!("row count = {}", database.fetch::<MyStruct>()?.count());
8285

8386
database.drop_table::<MyStruct>()?;
8487

@@ -87,23 +90,39 @@ mod app {
8790

8891
pub fn run() -> Result<(), DatabaseError> {
8992
reset_example_dir()?;
90-
let backend = env::var("KITESQL_BACKEND").unwrap_or_else(|_| "rocksdb".to_string());
93+
let backend = env::var("KITESQL_BACKEND").unwrap_or_else(|_| {
94+
#[cfg(feature = "rocksdb")]
95+
{
96+
"rocksdb".to_string()
97+
}
98+
#[cfg(all(not(feature = "rocksdb"), feature = "lmdb"))]
99+
{
100+
"lmdb".to_string()
101+
}
102+
#[cfg(all(not(feature = "rocksdb"), not(feature = "lmdb")))]
103+
{
104+
"memory".to_string()
105+
}
106+
});
91107

92108
match backend.to_ascii_lowercase().as_str() {
109+
"memory" => {
110+
run_with_database(DataBaseBuilder::path(EXAMPLE_DB_PATH).build_in_memory()?)
111+
}
93112
#[cfg(feature = "rocksdb")]
94113
"rocksdb" => run_with_database(DataBaseBuilder::path(EXAMPLE_DB_PATH).build_rocksdb()?),
95114
#[cfg(feature = "lmdb")]
96115
"lmdb" => run_with_database(DataBaseBuilder::path(EXAMPLE_DB_PATH).build_lmdb()?),
97116
other => Err(DatabaseError::InvalidValue(format!(
98117
"unsupported example backend '{other}', expected {}",
99-
{
100-
let mut expected = Vec::new();
118+
[
119+
"memory",
101120
#[cfg(feature = "rocksdb")]
102-
expected.push("rocksdb");
121+
"rocksdb",
103122
#[cfg(feature = "lmdb")]
104-
expected.push("lmdb");
105-
expected.join(" or ")
106-
}
123+
"lmdb",
124+
]
125+
.join(" or ")
107126
))),
108127
}
109128
}

examples/transaction.rs

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -41,10 +41,8 @@ mod app {
4141
pub fn run() -> Result<(), DatabaseError> {
4242
reset_example_dir()?;
4343
// Optimistic transactions are currently backed by RocksDB.
44-
let database = DataBaseBuilder::path(EXAMPLE_DB_PATH).build_optimistic()?;
45-
database
46-
.run("create table if not exists t1 (c1 int primary key, c2 int)")?
47-
.done()?;
44+
let mut database = DataBaseBuilder::path(EXAMPLE_DB_PATH).build_optimistic()?;
45+
database.ddl("create table if not exists t1 (c1 int primary key, c2 int)")?;
4846
let mut transaction = database.new_transaction()?;
4947

5048
transaction
@@ -65,6 +63,7 @@ mod app {
6563
Tuple::new(None, vec![DataValue::Int32(1), DataValue::Int32(1)])
6664
);
6765
assert!(iter.next().is_none());
66+
iter.done()?;
6867

6968
let mut tx2 = database.new_transaction()?;
7069
tx2.run("update t1 set c2 = 99 where c1 = 0")?.done()?;
@@ -79,7 +78,7 @@ mod app {
7978
);
8079
drop(tx2);
8180

82-
database.run("drop table t1")?.done()?;
81+
database.ddl("drop table t1")?;
8382

8483
Ok(())
8584
}

examples/wasm_hello_world.test.mjs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -8,8 +8,8 @@ const { WasmDatabase } = require("../pkg/kite_sql.js");
88
async function main() {
99
const db = new WasmDatabase();
1010

11-
await db.execute("drop table if exists my_struct");
12-
await db.execute("create table my_struct (c1 int primary key, c2 int)");
11+
await db.ddl("drop table if exists my_struct");
12+
await db.ddl("create table my_struct (c1 int primary key, c2 int)");
1313
await db.execute("insert into my_struct values(0, 0), (1, 1)");
1414

1515
const iter = db.run("select * from my_struct");
@@ -48,7 +48,7 @@ async function main() {
4848
[1, 11],
4949
]);
5050

51-
await db.execute("drop table my_struct");
51+
await db.ddl("drop table my_struct");
5252
console.log("wasm hello_world test passed");
5353
}
5454

0 commit comments

Comments
 (0)