「Rust 重写 sqlite」SQL模块:CREATE

「这是我参与11月更文挑战的第 14 天,活动详情查看:2021最后一次更文挑战


SQL模块

到这正式进入sql模块。不过这实际与之前的 meta command 模块没有什么不同,至少在结构上是这样。我们有一个枚举,定义了我们最初计划支持的查询类型。然后是一个带有 fn new() 的 impl block,同样作为构造函数。

然后是一个 fn process_command(),返回 Result<String, SQLRiteError>,如果你还记得,这个函数是由 main.rs 调用的。在这个函数中,神奇的事情开始发生。

你会注意到,在 fn process_command() 的开头,我们使用了 sqlparser-rs crate,它为Rust建立了一个可扩展的SQL Lexer和解析器,并有许多不同的SQL方言,包括SQLite方言,所以我决定暂时使用它们而不是编写一个全新的SQL Lexer。

通过调用 Parser::parse_sql(),我得到了一个 Result<Vec<Statement>, ParserError>,我做了一些基本的检查,并把它传递给一个匹配语句,以确定输入的是哪种类型的SQL语句,或者在这个过程中是否有错误,如果有,我就返回这个错误。返回的语句是 sqlparser::ast::Statement,它是一个所有可能语句结果的枚举,你可以从 sqlparser文档 中看到。

目前,我实际建立的解析器的唯一SQL语句是 CREATE TABLE,对于其余的语句,我们只是识别SQL语句的类型并返回给用户。在与CREATE TABLE匹配的block中,我们调用另一个模块 Parser::create,它包含了CREATE TABLE的所有逻辑。

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
rust复制代码#[derive(Debug,PartialEq)]
pub enum SQLCommand {
Insert(String),
Delete(String),
Update(String),
CreateTable(String),
Select(String),
Unknown(String),
}

impl SQLCommand {
pub fn new(command: String) -> SQLCommand {
let v = command.split(" ").collect::<Vec<&str>>();
match v[0] {
"insert" => SQLCommand::Insert(command),
"update" => SQLCommand::Update(command),
"delete" => SQLCommand::Delete(command),
"create" => SQLCommand::CreateTable(command),
"select" => SQLCommand::Select(command),
_ => SQLCommand::Unknown(command),
}
}
}

// 使用sqlparser-rs执行SQL语句的初始解析
pub fn process_command(query: &str) -> Result<String> {
let dialect = SQLiteDialect{};
let message: String;
let mut ast = Parser::parse_sql(&dialect, &query).map_err(SQLRiteError::from)?;

if ast.len() > 1 {
return Err(SQLRiteError::SqlError(ParserError::ParserError(format!(
"Expected a single query statement, but there are {}",
ast.len()
))));
}

let query = ast.pop().unwrap();

// 最初只实现一些基本的SQL语句
match query {
Statement::CreateTable{..} => {
let result = CreateQuery::new(&query);
match result {
Ok(payload) => {
println!("Table name: {}", payload.table_name);
for col in payload.columns {
println!("Column Name: {}, Column Type: {}", col.name, col.datatype);
}
},
Err(err) => return Err(err),
}
message = String::from("CREATE TABLE Statement executed.");
// TODO: Push table to DB
},
Statement::Query(_query) => message = String::from("SELECT Statement executed."),
Statement::Insert {..} => message = String::from("INSERT Statement executed."),
Statement::Delete{..} => message = String::from("DELETE Statement executed."),
_ => {
return Err(SQLRiteError::NotImplemented(
"SQL Statement not supported yet.".to_string()))
}
};

Ok(message)
}

这是我们的 sql::parser::create 模块。

这里我们有两个结构类型的定义。第一个是 ParsedColumn:代表表中的一个列,第二个是 CreateQuery:代表一个表。正如你所看到的,CreateQuery有一个名为columns的属性,这是一个ParsedColumns的集合。我们在这个模块上的主要方法,也就是 fn new(),返回一个 Result<CreateTable, SQLRiteError>,然后将其插入到我们的数据库数据结构中,该结构仍有待于在代码中定义。

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
rust复制代码// Represents Columns in a table
#[derive(PartialEq, Debug)]
pub struct ParsedColumn {
pub name: String,
pub datatype: String,
pub is_pk: bool,
pub is_nullable: bool,
}

// Represents a SQL Statement CREATE TABLE
#[derive(Debug)]
pub struct CreateQuery {
pub table_name: String, // table name
pub columns: Vec<ParsedColumn>, // columns
}

impl CreateQuery {
pub fn new(statement: &Statement) -> Result<CreateQuery> {
match statement {
Statement::CreateTable {
name,
columns,
constraints: _constraints,
with_options: _with_options,
external: _external,
file_format: _file_format,
location: _location,
..
} => {
let table_name = name;
let mut parsed_columns: Vec<ParsedColumn> = vec![];

// 遍历 columns
for col in columns {
let name = col.name.to_string();
// TODO: 目前只能解析基础类型,没有时间戳和日期
let datatype = match &col.data_type {
DataType::SmallInt => "int",
DataType::Int => "int",
DataType::BigInt => "int",
DataType::Boolean => "bool",
DataType::Text => "string",
DataType::Varchar(_bytes) => "string",
DataType::Float(_precision) => "float",
DataType::Double => "float",
DataType::Decimal(_precision1, _precision2) => "float",
_ => {
println!("not matched on custom type");
"invalid"
}
};

let mut is_pk: bool = false;
for column_option in &col.options {
is_pk = match column_option.option {
ColumnOption::Unique { is_primary } => is_primary,
_ => false,
};
}

parsed_columns.push(ParsedColumn {
name,
datatype: datatype.to_string(),
is_pk,
is_nullable: false,
});
}
// TODO: 处理表约束,比如主键外键等
for constraint in _constraints {
println!("{:?}", constraint);
}
return Ok(CreateQuery {
table_name: table_name.to_string(),
columns: parsed_columns,
});
}

_ => return Err(SQLRiteError::Internal("Error parsing query".to_string())),
}
}
}

本文转载自: 掘金

开发者博客 – 和开发相关的 这里全都有

0%