2个不稳定版本

使用旧Rust 2015

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

数据库接口中排名第1038

MIT 许可证

47KB
717

TQL

:source-highlighter: pygments

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

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

link link link

需求

目前,tql仅支持PostgreSQL数据库(将来将支持更多数据库)。因此,您需要安装PostgreSQL才能使用此crate。

用法

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

[source,toml]

[dependencies]
chrono = "^0.4.0"
tql = { git = "https://github.com/antoyo/tql" }
tql_macros = { git = "https://github.com/antoyo/tql" }

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

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

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

[source,rust]

#![feature(proc_macro)]

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

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

在稳定Rust上使用

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

首先,删除以下行

[source,rust]

#![feature(proc_macro)]

//

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]

tql = { git = "https://github.com/antoyo/tql", default-features = false }

通过这个小小的更改,我们可以像以前一样使用sql!()宏。

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

过程宏目前不支持在稳定版本上特定位置发出错误,因此使用此版本时,您将得到一些不太有用的错误,如下面的输出所示

[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()

依赖

~2.2–6.5MB
~136K SLoC