regulus-db 0.2.0

A Simple API-only Database Application Written by Rust
Documentation
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
# RegulusDB

RegulusDB 是一个用 Rust 编写的高性能嵌入式数据库,支持内存存储和持久化存储两种模式。它提供了类 SQL 的 API 查询构建器、B+ 树索引、事务支持以及 WAL(预写日志)+ 快照的持久化机制。

## 特性

- **多种数据类型**: Null, Integer, Real, Text, Blob, Boolean, Date, Datetime
- **灵活的存储引擎**:
  - `MemoryEngine`: 纯内存存储,适用于临时数据和缓存场景
  - `PersistedEngine`: 持久化存储,支持崩溃恢复
- **WAL + 快照持久化**:
  - 预写日志(WAL)确保数据持久性
  - 定期快照(Snapshot)优化恢复速度
  - 自动检查点(Checkpoint)机制(WAL > 10MB 时触发)
- **查询构建器**:
  - SELECT, UPDATE, DELETE 操作
  - 比较操作符:=, !=, <, <=, >, >=, IN, LIKE
  - 逻辑操作符:AND, OR, NOT
  - 聚合函数:COUNT, SUM, AVG, MAX, MIN
  - ORDER BY, LIMIT, OFFSET
  - GROUP BY 和 HAVING 子句
  - INNER JOIN, LEFT JOIN, RIGHT JOIN, FULL OUTER JOIN
  - DISTINCT 去重查询
- **B+ 树索引**:
  - 单列索引和复合索引
  - 唯一索引支持
  - 自动索引选择优化查询
- **事务支持**: ACID 语义的事务操作
- **AUTO_INCREMENT 自增主键**: 支持主键自增,允许显式覆盖,继承最大值模式

## 安装

将以下依赖添加到你的 `Cargo.toml`:

```toml
[dependencies]
regulus-db = "0.1.0"
```

## 快速开始

### 内存模式

```rust
use regulus_db::{Database, Column, DataType, DbValue};
use std::path::Path;

fn main() -> Result<(), Box<dyn std::error::Error>> {
    // 内存模式
    let db = Database::new();

    // 创建表
    db.create_table("users", vec![
        Column::new("id", DataType::integer()).primary_key(),
        Column::new("name", DataType::text()),
        Column::new("age", DataType::integer()),
    ])?;

    // 插入数据
    db.insert("users", vec![
        ("id", DbValue::integer(1)),
        ("name", DbValue::text("Alice")),
        ("age", DbValue::integer(25)),
    ])?;

    // 查询数据
    let results = db.query("users")
        .eq("age", DbValue::integer(25))
        .execute()?;

    for row in results {
        println!("Name: {:?}", row.get("name"));
    }

    Ok(())
}
```

### 持久化模式

```rust
use regulus_db::{Database, Column, DataType, DbValue};
use std::path::Path;

fn main() -> Result<(), Box<dyn std::error::Error>> {
    // 打开持久化数据库
    let db = Database::open(Path::new("./data"))?;
    // 或创建新数据库(如果已存在则覆盖)
    let db = Database::create(Path::new("./data"))?;

    // 创建表
    db.create_table("users", vec![
        Column::new("id", DataType::integer()).primary_key(),
        Column::new("name", DataType::text()),
        Column::new("age", DataType::integer()),
    ])?;

    // 插入数据(自动记录 WAL)
    db.insert("users", vec![
        ("id", DbValue::integer(1)),
        ("name", DbValue::text("Alice")),
        ("age", DbValue::integer(25)),
    ])?;

    // 查询数据(持久化模式同样支持完整的查询构建器)
    let results = db.query("users")
        .ge("age", DbValue::integer(18))
        .execute()?;

    // 更新数据
    db.update("users")
        .eq("id", DbValue::integer(1))
        .set("age", DbValue::integer(26))
        .execute()?;

    // 删除数据
    db.delete("users")
        .eq("id", DbValue::integer(1))
        .execute()?;

    Ok(())
}
```

## 数据类型

| 类型 | 描述 | 示例 |
|------|------|------|
| `Null` | 空值 | `DbValue::Null` |
| `Integer` | 64 位有符号整数 | `DbValue::integer(42)` |
| `Real` | 64 位浮点数 | `DbValue::real(3.14)` |
| `Text` | UTF-8 字符串 | `DbValue::text("hello")` |
| `Blob` | 二进制数据 | `DbValue::blob(vec![1, 2, 3])` |
| `Boolean` | 布尔值 | `DbValue::boolean(true)` |
| `Date` | 日期(自 1970-01-01 的天数) | `DbValue::date(19000)` |
| `Datetime` | 日期时间(自 1970-01-01 的毫秒数) | `DbValue::datetime(1609459200000)` |

## 查询构建器

### 基本查询

```rust
// 查询所有用户
let all = db.query("users").execute()?;

// 条件查询
let adults = db.query("users")
    .ge("age", DbValue::integer(18))
    .execute()?;

// 多个条件
let result = db.query("users")
    .ge("age", DbValue::integer(18))
    .eq("active", DbValue::boolean(true))
    .execute()?;
```

### 比较操作符

```rust
db.query("users").eq("id", DbValue::integer(1));      // =
db.query("users").ne("id", DbValue::integer(1));      // !=
db.query("users").lt("age", DbValue::integer(18));    // <
db.query("users").le("age", DbValue::integer(18));    // <=
db.query("users").gt("age", DbValue::integer(18));    // >
db.query("users").ge("age", DbValue::integer(18));    // >=
db.query("users").in_list("id", vec![DbValue::integer(1), DbValue::integer(2)]);  // IN
db.query("users").contains("name", "Ali");            // LIKE (包含)
db.query("users").is_null("email");                   // IS NULL
db.query("users").is_not_null("email");               // IS NOT NULL
```

### 逻辑操作符

```rust
use regulus_db::FilterExpr;

// OR 操作符:age > 18 OR status = 'active'
let results = db.query("users")
    .or(
        FilterExpr::Gt { field: "age".to_string(), value: DbValue::integer(18) },
        FilterExpr::Eq { field: "status".to_string(), value: DbValue::text("active") }
    )
    .execute()?;

// NOT 操作符:简洁链式 API
// 简单条件:NOT (status = 'deleted')
let results = db.query("users")
    .not()
    .eq("status", DbValue::text("deleted"))
    .execute()?;

// 复杂条件:使用 expr() 传入 FilterExpr
let results = db.query("users")
    .not()
    .expr(FilterExpr::And(
        Box::new(FilterExpr::Gt { field: "age".to_string(), value: DbValue::integer(18) }),
        Box::new(FilterExpr::Eq { field: "status".to_string(), value: DbValue::text("active") })
    ))
    .execute()?;

// 便捷方法
let results = db.query("users")
    .or_simple(
        |q| q.gt("age", DbValue::integer(18)),
        |q| q.eq("status", DbValue::text("active"))
    )
    .execute()?;

let results = db.query("users")
    .not_simple(|q| q.eq("status", DbValue::text("deleted")))
    .execute()?;
```

### DISTINCT 去重

```rust
// 单列去重:查询不同的名字
let distinct_names = db.query("users")
    .select(&["name"])
    .distinct()
    .execute()?;

// 多列去重:查询不同的 (name, age) 组合
let distinct_rows = db.query("users")
    .select(&["name", "age"])
    .distinct()
    .execute()?;

// 与 WHERE 和 ORDER BY 组合
let results = db.query("users")
    .select(&["city"])
    .distinct()
    .gt("age", DbValue::integer(18))
    .order_by("city", Order::Asc)
    .execute()?;
```

### 排序和分页

```rust
// 按年龄降序排序
let sorted = db.query("users")
    .order_by("age", Order::Desc)
    .execute()?;

// 分页:每页 10 条,第 3 页
let page = db.query("users")
    .order_by("id", Order::Asc)
    .limit(10)
    .offset(20)  // 跳过前 20 条
    .execute()?;
```

### 聚合函数

```rust
use regulus_db::Aggregate;

// COUNT
let count = db.query("users")
    .select_with_aggregates(&[Aggregate::count("*")])
    .execute()?;

// AVG
let avg_age = db.query("users")
    .select_with_aggregates(&[Aggregate::avg("age")])
    .execute()?;

// SUM, MAX, MIN
let stats = db.query("orders")
    .select_with_aggregates(&[
        Aggregate::sum("amount"),
        Aggregate::max("amount"),
        Aggregate::min("amount"),
    ])
    .execute()?;
```

### GROUP BY 和 HAVING

```rust
use regulus_db::Aggregate;

// 按部门分组,统计每部门人数
let grouped = db.query("employees")
    .select(&["department"])
    .select_with_aggregates(&[Aggregate::count("*")])
    .group_by(&["department"])
    .execute()?;

// HAVING 子句过滤分组
let high_departments = db.query("employees")
    .select(&["department"])
    .select_with_aggregates(&[Aggregate::avg("salary")])
    .group_by(&["department"])
    .having(Aggregate::avg("salary").gt(DbValue::real(50000.0)))
    .execute()?;
```

### JOIN 操作

```rust
// INNER JOIN
let results = db.query("users")
    .inner_join("orders", "users.id", "orders.user_id")
    .execute()?;

// LEFT JOIN
let results = db.query("users")
    .left_join("orders", "users.id", "orders.user_id")
    .execute()?;

// RIGHT JOIN
let results = db.query("users")
    .right_join("orders", "users.id", "orders.user_id")
    .execute()?;

// FULL OUTER JOIN
let results = db.query("users")
    .full_outer_join("orders", "users.id", "orders.user_id")
    .execute()?;

// 带字段选择和过滤
let results = db.query("users")
    .inner_join("orders", "users.id", "orders.user_id")
    .select(&["users.name", "orders.product", "orders.amount"])
    .gt("orders.amount", DbValue::integer(100))
    .order_by("orders.amount", Order::Desc)
    .execute()?;
```

## 更新和删除

### UPDATE

```rust
// 更新单个字段
db.update("users")
    .eq("id", DbValue::integer(1))
    .set("age", DbValue::integer(26))
    .execute()?;

// 更新多个字段
db.update("users")
    .eq("id", DbValue::integer(1))
    .set("age", DbValue::integer(26))
    .set("name", DbValue::text("Alicia"))
    .execute()?;
```

### DELETE

```rust
// 删除单条
let deleted = db.delete("users")
    .eq("id", DbValue::integer(1))
    .execute()?;

// 批量删除
let deleted = db.delete("users")
    .lt("age", DbValue::integer(18))
    .execute()?;
```

### 事务中的更新和删除

在事务中,`update` 和 `delete` 使用函数式 API:

```rust
db.transaction(|tx| {
    // 更新:table, 条件闭包,更新字段列表
    tx.update("users",
        |row| row.get("id").and_then(|v| v.as_integer()) == Some(1),
        vec![("age", DbValue::integer(26))],
    )?;

    // 删除:table, 条件闭包
    tx.delete("users",
        |row| row.get("active").and_then(|v| v.as_boolean()) == Some(false),
    )?;

    Ok(())
})?;
```

## 索引

### 创建索引

```rust
// 单列索引
db.create_index("users", "name")?;

// 复合索引
db.create_composite_index("users", &["last_name", "first_name"])?;

// 唯一复合索引
db.create_unique_index("users", &["email"])?;
```

### 删除索引

```rust
// 删除单列索引
db.drop_index("users", "name")?;

// 删除复合索引
db.drop_composite_index("users", &["last_name", "first_name"])?;
```

### 检查索引

```rust
if db.has_index("users", "name") {
    println!("索引存在");
}

if db.has_composite_index("users", &["last_name", "first_name"]) {
    println!("复合索引存在");
}
```

## 默认值

在创建表时可以为列指定默认值:

```rust
db.create_table("users", vec![
    Column::new("id", DataType::integer()).primary_key(),
    Column::new("name", DataType::text()).not_null(),
    Column::new("status", DataType::text()).default(DbValue::text("active")),
    Column::new("age", DataType::integer()).default(DbValue::integer(0)),
    Column::new("active", DataType::boolean()).default(DbValue::boolean(true)),
])?;

// 插入时只提供部分字段,其他字段使用默认值
db.insert("users", vec![
    ("id", DbValue::integer(1)),
    ("name", DbValue::text("Alice")),
    // status, age, active 将使用默认值
])?;
```

## AUTO_INCREMENT 自增主键

为列添加 `.auto_increment()` 修饰器即可启用自增功能:

```rust
db.create_table("users", vec![
    Column::new("id", DataType::integer())
        .primary_key()
        .auto_increment(),  // 启用自增
    Column::new("name", DataType::text()),
])?;

// 插入时不需要提供 id,自动生成
db.insert("users", vec![
    ("name", DbValue::text("Alice")),
])?;

db.insert("users", vec![
    ("name", DbValue::text("Bob")),
])?;

// id 会自动生成 (1, 2, 3, ...)
```

### 行为特性

- **自动生成**:插入时未提供自增列的值时,自动生成下一个自增值
- **显式覆盖**:允许用户显式指定自增列的值
- **继承最大值**:用户显式提供值后,下一个自增值 = max(当前 next_id, 用户值) + 1
- **唯一性检查**:自增主键自动具有唯一性约束,重复值会报错
- **不回滚**:事务回滚后自增值不会重置(符合 MySQL/PostgreSQL 标准行为)

```rust
// 显式覆盖自增值
db.insert("users", vec![
    ("id", DbValue::integer(10)),  // 显式指定 id=10
    ("name", DbValue::text("Charlie")),
])?;

// 下一条自增记录从 11 开始
db.insert("users", vec![
    ("name", DbValue::text("David")),
])?;  // id 自动生成 11
```

## 事务

事务支持原子性操作和回滚功能:

```rust
// 基本事务
db.transaction(|tx| {
    // 插入数据
    tx.insert("users", vec![
        ("id", DbValue::integer(1)),
        ("name", DbValue::text("Alice")),
    ])?;

    tx.insert("users", vec![
        ("id", DbValue::integer(2)),
        ("name", DbValue::text("Bob")),
    ])?;

    // 查询事务内的数据
    let rows = tx.query_all("users")?;
    println!("事务内用户数:{}", rows.len());

    Ok(())  // 提交事务
})?;

// 回滚示例
db.transaction(|tx| {
    tx.insert("users", vec![
        ("id", DbValue::integer(3)),
        ("name", DbValue::text("Charlie")),
    ])?;

    // 手动回滚
    tx.rollback()?;
    Ok(())  // Charlie 不会被插入
})?;
```

## 持久化机制

### 文件结构

RegulusDB 使用两个文件进行持久化:

| 文件 | 说明 |
|------|------|
| `data.rdb` | 数据快照文件,包含完整的数据状态 |
| `data.rdb.wal` | 预写日志文件,记录所有数据变更操作 |

### 恢复流程

1. 加载 `data.rdb` 快照到内存
2. 重放 `data.rdb.wal` 中的所有操作
3. 数据恢复完成

### 检查点(Checkpoint)

- **自动触发**: 当 WAL 文件大小超过 10MB 时自动触发
- **手动触发**: 调用 `engine.force_checkpoint()?`
- **过程**: 保存快照 → 截断 WAL → 重置计数器

```rust
use regulus_db::{PersistedEngine, StorageEngine};

let mut engine = PersistedEngine::open(Path::new("./data"))?;

// 执行一些操作...

// 手动触发检查点
engine.force_checkpoint()?;

// 检查是否需要检查点
if engine.wal_size() > 0 {
    println!("WAL 大小:{} bytes", engine.wal_size());
}
```

## 测试

运行所有测试:

```bash
cargo test
```

运行特定测试:

```bash
cargo test test_persisted_recovery
```

运行基准测试:

```bash
cargo bench
```

## 许可证

本项目采用 MIT 许可证。详见 LICENSE 文件。

## 致谢

RegulusDB 的设计受到了以下数据库的启发:

- **SQLite**: WAL 模式和 B+ 树索引
- **Redis**: RDB 快照和 AOF 日志
- **PostgreSQL**: MVCC 和事务处理