2个不稳定版本

使用旧Rust 2015

0.1.0 2018年2月10日
0.0.1 2015年12月30日

#162#稳定版


用于 tql

MIT 许可证

235KB
5K SLoC

TQL

:source-highlighter: pygments

编译时ORM,受Django ORM启发,用Rust编写。Tql作为过程宏实现,甚至在Rust的稳定版本上也能工作(查看此示例了解如何在稳定版上使用tql)。

此库处于alpha阶段:它尚未经过充分测试,其API可能随时更改。

link link link link

要求

目前,tql仅支持PostgreSQLSQLite数据库(未来将支持更多数据库)。因此,您需要安装PostgreSQL(和/或libsqlite3-sys),以便使用此crate。

用法

首先,将以下内容添加到您的Cargo.toml

[source,toml]

[dependencies]
chrono = "^0.4.0"
tql_macros = "0.1"

[dependencies.tql]
features = ["chrono", "pg"]
version = "0.1"

[dependencies.postgres]
features = ["with-chrono"]
version = "^0.15.1"

(如果您不希望使用模型中的日期和时间类型,可以删除chrono相关内容。)

接下来,将以下内容添加到您的crate中

[source,rust]

#![feature(proc_macro_hygiene)]

extern crate chrono;
extern crate postgres;
extern crate tql;
#[macro_use]
extern crate tql_macros;

use postgres::{Connection, TlsMode};
use tql::PrimaryKey;
use tql_macros::sql;

然后,创建您的模型

[source,rust]

use chrono::DateTime;
use chrono::offset::Utc;

#[derive(SqlTable)]
struct Model {
    id: PrimaryKey,
    text: String,
    date_added: DateTime<Utc>,
    //
}

接下来,创建一个连接访问器

[source,rust]

fn get_connection() -> Connection {
    Connection::connect("postgres://test:test@localhost/database", TlsMode::None).unwrap()
}

最后,我们可以使用sql!宏执行一个SQL查询

[source,rust]

fn main() {
    let connection = get_connection();

    // We first create the table.
    // (You might not want to execute this query every time.)
    let _ = sql!(Model.create());

    // Insert a row in the table.
    let text = String::new();
    let id = sql!(Model.insert(text = text, date_added = Utc::now())).unwrap();

    // Update a row.
    let result = sql!(Model.get(id).update(text = "new-text"));

    // Delete a row.
    let result = sql!(Model.get(id).delete());

    // Query some rows from the table:
    // get the last 10 rows sorted by date_added descending.
    let items = sql!(Model.sort(-date_added)[..10]);
}

sql!()宏默认使用标识符connection

查看以下表格以获取更多示例。

使用SQLite的用法

首先,将postgres依赖项更改为以下内容

[source,toml]

rusqlite = "^0.13.0"

然后,更改tql依赖项的功能

[source,toml]

[dependencies.tql]
features = ["sqlite"]
version = "0.1"

在Rust代码中,连接现在需要来自rusqlite

[source,rust]

use rusqlite::Connection;

fn get_connection() -> Connection {
    Connection::open("database.db").unwrap()
}

其余部分相同。

在稳定Rust中使用

如果您想在稳定版Rust中使用tql,需要进行一些更改才能使其工作

首先,删除以下行

[source,rust]

#![feature(proc_macro_hygiene)]

//

use tql_macros::sql;

然后在extern crate tql之前添加以下行

[source,rust]

#[macro_use]

这是文件开始的样子

[source,rust]

extern crate chrono;
extern crate postgres;
#[macro_use]
extern crate tql;
#[macro_use]
extern crate tql_macros;

use postgres::{Connection, TlsMode};
use tql::PrimaryKey;

最后,通过更新tql依赖项来禁用unstable功能

[source,toml]

[dependencies.tql]
default-features = false
features = ["chrono", "pg"]
version = "0.1"

通过这个小小的改动,我们可以使用 sql!(),但现在您需要指定连接。

source.rust

let date_added = Utc::now();
let id = sql!(connection, Model.insert(text = text, date_added = date_added)).unwrap();

另外,由于稳定编译器的限制,您不能再使用表达式作为参数:这就是为什么我们现在创建了一个变量 date_added。目前,如果您在稳定版上使用 tql,您需要使用标识符或字面量作为参数。

为什么不总是使用稳定版呢?

过程宏目前不支持在稳定版中在特定位置生成错误,因此使用此版本时,您将获得不太有用的错误,如下所示

[source]

error[E0308]: mismatched types
  --> src/main.rs:47:18
   |
47 |     let result = sql!(Model.insert(text = text, date_added = Utc::now(), done = false));
   |                  ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected &str, found struct `std::string::String`
   |
   = note: expected type `&str`
              found type `std::string::String`
   = help: try with `&sql!(Model.insert(text = text, date_added = Utc::now(), done = false))`
   = note: this error originates in a macro outside of the current crate

而您将得到以下更友好的错误,当您使用Rust的夜间版本时

[source]

error[E0308]: mismatched types
  --> examples/todo.rs:49:46
   |
49 |     let result = sql!(Model.insert(text = text, date_added = Utc::now(), done = false));
   |                                           ^^^^
   |                                           |
   |                                           expected &str, found struct `std::string::String`
   |                                           help: consider borrowing here: `&text`
   |
   = note: expected type `&str`
              found type `std::string::String`

因此,一个良好的工作流程是在夜间版本上开发,然后在稳定版上发布。这样,您可以获得两全其美的好处:您有友好的错误,并且可以使用编译器的稳定版进行部署。这根本不是问题,因为当您准备部署时,不应该有任何编译器错误(您仍然可以看到错误)。

注意:使用以下命令编译以获取更好的错误消息 RUSTFLAGS="--cfg procmacro2_semver_exempt"

语法表

左侧显示生成的SQL,右侧显示您可以使用 tql 使用的语法。

[cols="1a,1a", options="header"] |=== | SQL | Rust

| [source, sql]

SELECT * FROM Table

| [source, rust]

Table.all()

| [source, sql]

SELECT * FROM Table WHERE field1 = 'value1'

| [source, rust]

Table.filter(field1 == "value1")

| [source, sql]

SELECT * FROM Table WHERE primary_key = 42

| [source, rust]

Table.get(42)

// Shortcut for:

Table.filter(primary_key == 42)[0..1];

| [source, sql]

SELECT * FROM Table WHERE field1 = 'value1'

| [source, rust]

Table.get(field1 == "value1")

// Shortcut for:

Table.filter(field1 == "value1")[0..1];

| [source, sql]

SELECT * FROM Table WHERE field1 = 'value1' AND field2 < 100

| [source, rust]

Table.filter(field1 == "value1" && field2 < 100)

| [source, sql]

SELECT * FROM Table WHERE field1 = 'value1' OR field2 < 100

| [source, rust]

Table.filter(field1 == "value1" \|\| field2 < 100)

| [source, sql]

SELECT * FROM Table ORDER BY field1

| [source, rust]

Table.sort(field1)

| [source, sql]

SELECT * FROM Table ORDER BY field1 DESC

| [source, rust]

Table.sort(-field1)

| [source, sql]

SELECT * FROM Table LIMIT 0, 20

| [source, rust]

Table[0..20]

| [source, sql]

SELECT * FROM Table
WHERE field1 = 'value1'
  AND field2 < 100
ORDER BY field2 DESC
LIMIT 10, 20

| [source, rust]

Table.filter(field1 == "value1" && field2 < 100)
    .sort(-field2)[10..20]

| [source, sql]

INSERT INTO Table(field1, field2) VALUES('value1', 55)

| [source, rust]

Table.insert(field1 = "value1", field2 = 55)

| [source, sql]

UPDATE Table SET field1 = 'value1', field2 = 55 WHERE id = 1

| [source, rust]

Table.get(1).update(field1 = "value1", field2 = 55);

// or

Table.filter(id == 1).update(field1 = "value1", field2 = 55);

| [source, sql]

DELETE FROM Table WHERE id = 1

| [source, rust]

Table.get(1).delete();

// ou

Table.filter(id == 1).delete()

| [source, sql]

SELECT AVG(field2) FROM Table

| [source, rust]

Table.aggregate(avg(field2))

| [source, sql]

SELECT AVG(field1) FROM Table1 GROUP BY field2

| [source, rust]

Table1.values(field2).annotate(avg(field1))

| [source, sql]

SELECT AVG(field1) as average FROM Table1
GROUP BY field2
HAVING average > 5

| [source, rust]

Table1.values(field2).annotate(average = avg(field1))
    .filter(average > 5)

| [source, sql]

SELECT AVG(field1) as average FROM Table1
WHERE field1 < 10
GROUP BY field2
HAVING average > 5

| [source, rust]

Table1.filter(field1 < 10).values(field2)
    .annotate(average = avg(field1)).filter(average > 5)

| [source, sql]

SELECT Table1.field1, Table2.field1 FROM Table1
INNER JOIN Table2 ON Table1.pk = Table2.fk

| [source, rust]

#[derive(SqlTable)]
struct Table1 {
    pk: PrimaryKey,
    field1: i32,
}

#[derive(SqlTable)]
struct Table2 {
    field1: i32,
    fk: ForeignKey<Table1>,
}

Table1.all().join(Table2)

| [source, sql]

SELECT * FROM Table1 WHERE YEAR(date) = 2015

| [source, rust]

Table1.filter(date.year() == 2015)

| [source, sql]

SELECT * FROM Table1 WHERE INSTR(field1, 'string') > 0

| [source, rust]

Table1.filter(field1.contains("string"))

| [source, sql]

SELECT * FROM Table1 WHERE field1 LIKE 'string%'

| [source, rust]

Table1.filter(field1.starts_with("string"))

| [source, sql]

SELECT * FROM Table1 WHERE field1 LIKE '%string'

| [source, rust]

Table1.filter(field1.ends_with("string"))

| [source, sql]

SELECT * FROM Table1 WHERE field1 IS NULL

| [source, rust]

Table1.filter(field1.is_none())

| [source, sql]

SELECT * FROM Table1 WHERE field1 REGEXP BINARY '\^[a-d]'

| [source, rust]

Table1.filter(field1.regex(r"\^[a-d]"))

| [source, sql]

SELECT * FROM Table1 WHERE field1 REGEXP '\^[a-d]'

| [source, rust]

Table1.filter(field1.iregex(r"\^[a-d]"))

| [source, sql]

CREATE TABLE IF NOT EXISTS Table1 (
    pk INTEGER NOT NULL AUTO_INCREMENT,
    field1 INTEGER,
    PRIMARY KEY (pk)
)

| [source, rust]

#[derive(SqlTable)]
struct Table1 {
    pk: PrimaryKey,
    field1: i32,
}

Table1.create()

|===

捐赠

如果您喜欢这个项目并且希望实现新功能,请在我在Patreon上支持我。

link

依赖关系

~2.5MB
~53K SLoC