7 个版本

0.0.7 2024年7月30日
0.0.6 2024年1月20日
0.0.5 2023年12月10日
0.0.4 2023年11月18日
0.0.1 2023年4月13日

#1167数据库接口

Download history 140/week @ 2024-07-30

140 每月下载量

GPL-3.0 或更高版

59KB
1.5K SLoC

用于最小化 SQL 的 SQLite 的实验性库

Liter 在编译时根据普通 Rust struct 定义生成完整的 SQL 架构定义。它建立在 rusqlite 之上,利用强大的可由用户实现的特质来生成约束良好且类型感知的表定义。

SQL 不是由 derive 宏直接生成的:它们只能以 文本 方式操作,因此它们调用 const 函数代替。然后它们可以访问类型及其特质实现,这给了您控制 SQL 如何生成的权限。

基本示例

这里有一个只有一个表的非常简单的数据库示例。

 use liter::{database, Table};

 #[database]
 struct ShoppingList (
     Item
 );

 #[derive(Table, PartialEq, Debug)]
 struct Item {
     count: u32,
     name: String
 }

 let list = ShoppingList::create_in_memory()?;

 let oranges = Item {
     count: 3,
     name: "Orange".to_string(),
 };

 list.insert(&oranges)?;
 let items = list.get_all::<Item>()?;

 assert_eq!(oranges, items[0]);
 # Ok::<(), rusqlite::Error>(())

Liter 会为您生成以下 SQL 架构,以及 SELECT & INSERT 语句。

 BEGIN TRANSACTION;
 CREATE TABLE item (
     count INTEGER NOT NULL,
     name TEXT NOT NULL
 ) STRICT;
 END TRANSACTION;

架构生成概述

有几个特质组成了 liter 数据库,但实际上它非常直观。我们将从顶部(或根,如果您将其视为树的话)开始,逐步向下进行。

Schema 通过其组成部分 Table 定义 liter 数据库,也就是说,它通过包含的表来定义数据库。这个特质在声明数据库中包含的 Table 类型的一个 元组结构体 上由 #[database] proc-macro 实现。

Table 特质通过在常规结构体上使用 #[derive(Table)] 实现。生成的 SQL 表的每一行(以结构体命名)将存储该结构体的一个实例。您可能会认为结构体的每个字段代表其表中的一列,这几乎是正确的。

《Value》特质是一个中介层,代表一个或多个《Column》。因此,它为所有实现了《Column》的类型实现了 —— 这就像你可能猜到的,它代表了一个可以存储在单个SQL列中的原始数据类型 —— 但它也为需要 多个 SQL列的类型实现了(你也可以实现它)。实际上,一个《Value》不仅可以定义为一组《Column》,也可以定义为一组《Value》。

虽然它的名字可能有些通用,但《Value》特质是一个重要的抽象,它允许使用可重用和可组合的组件来定义数据库表,而不是仅仅使用列原始数据。例如,它通过泛型[《Ref》]结构体,甚至可以轻松实现外键引用,即使是对具有组合主键的表。

使用主键和外键的示例

这个稍微复杂一些的示例展示了外键引用和组合主键。

 use liter::{database, Table, Id, Ref};

 #[database]
 struct Dictionary (
     Language,
     Word
 );

 #[derive(Table)]
 struct Language {
     #[key]
     id: Id,
     name: String
 }

 #[derive(Table)]
 struct Word {
     #[key]
     language: Ref<Language>,
     #[key]
     word: String,
     definition: String
 }
 let dict = Dictionary::create_in_memory()?;

 let mut lang = Language {
     id: Id::NULL,
     name: "Latin".to_string()
 };
 dict.create(&mut lang)?; // assigns the newly created Id

 let word = Word {
     language: Ref::make_ref(&lang),
     word: "nunc".to_string(),
     definition:
         "now, at present, at this time, at this very moment".to_string()
 };
 dict.insert(&word)?;
 # Ok::<(), rusqlite::Error>(())

注意,这次,为《Language》和《Word》的《HasKey》实现生成了更多SQL语句。

下面是生成的模式

 BEGIN TRANSACTION;
 CREATE TABLE language (
     id INTEGER NOT NULL,
     name TEXT NOT NULL,
     PRIMARY KEY ( id )
 ) STRICT;
 CREATE TABLE word (
     language INTEGER NOT NULL,
     word TEXT NOT NULL,
     definition TEXT NOT NULL,
     PRIMARY KEY ( language, word ),
     FOREIGN KEY (language) REFERENCES language
         ON UPDATE RESTRICT
         ON DELETE RESTRICT
         DEFERRABLE INITIALLY DEFERRED
 ) STRICT;
 END TRANSACTION;

依赖项

~22MB
~434K SLoC