Skip to content

Commit 8bc563a

Browse files
authored
feat: added marco implement_from_tuple to implement ORM function (#73)
* fix: `is-terminal` error * feat: added marco `implement_from_tuple` to implement ORM function * doc: marco `implement_from_tuple` * config: version up * refactor: remove kip_marco crate and move to marco mod * fix(marco): unresolved import * docs: add orm mapping
1 parent 3c5603d commit 8bc563a

File tree

7 files changed

+239
-4
lines changed

7 files changed

+239
-4
lines changed

Cargo.toml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22

33
[package]
44
name = "kip-sql"
5-
version = "0.0.1-alpha.0"
5+
version = "0.0.1-alpha.3"
66
edition = "2021"
77
authors = ["Kould <kould2333@gmail.com>", "Xwg <loloxwg@gmail.com>"]
88
description = "build the SQL layer of KipDB database"
@@ -37,7 +37,7 @@ ahash = "0.8.3"
3737
lazy_static = "1.4.0"
3838
comfy-table = "7.0.1"
3939
bytes = "1.5.0"
40-
kip_db = "0.1.2-alpha.15"
40+
kip_db = "0.1.2-alpha.16"
4141
async-recursion = "1.0.5"
4242
rust_decimal = "1"
4343

README.md

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,33 @@ Storage Support:
4848
![demo](./static/images/demo.png)
4949

5050
### Features
51+
- ORM Mapping
52+
```rust
53+
#[derive(Debug, Clone, Default)]
54+
pub struct Post {
55+
pub post_title: String,
56+
pub post_date: NaiveDateTime,
57+
pub post_body: String,
58+
}
59+
60+
implement_from_tuple!(Post, (
61+
post_title: String => |post: &mut Post, value: DataValue| {
62+
if let Some(title) = value.utf8() {
63+
post.post_title = title;
64+
}
65+
},
66+
post_date: NaiveDateTime => |post: &mut Post, value: DataValue| {
67+
if let Some(date_time) = value.datetime() {
68+
post.post_date = date_time;
69+
}
70+
},
71+
post_body: String => |post: &mut Post, value: DataValue| {
72+
if let Some(body) = value.utf8() {
73+
post.post_body = body;
74+
}
75+
}
76+
));
77+
```
5178
- SQL field options
5279
- not null
5380
- null

rust-toolchain

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
nightly-2023-04-07
1+
nightly-2023-09-29

src/lib.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,6 @@
66
#![feature(slice_pattern)]
77
#![feature(bound_map)]
88
extern crate core;
9-
109
pub mod binder;
1110
pub mod catalog;
1211
pub mod db;
@@ -17,3 +16,4 @@ pub mod types;
1716
mod optimizer;
1817
pub mod execution;
1918
pub mod storage;
19+
pub mod marco;

src/marco/mod.rs

Lines changed: 121 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,121 @@
1+
/// # Examples
2+
///
3+
/// ```
4+
///struct MyStruct {
5+
/// c1: i32,
6+
/// c2: String,
7+
///}
8+
///
9+
///implement_from_tuple!(
10+
/// MyStruct, (
11+
/// c1: i32 => |inner: &mut MyStruct, value| {
12+
/// if let DataValue::Int32(Some(val)) = value {
13+
/// inner.c1 = val;
14+
/// }
15+
/// },
16+
/// c2: String => |inner: &mut MyStruct, value| {
17+
/// if let DataValue::Utf8(Some(val)) = value {
18+
/// inner.c2 = val;
19+
/// }
20+
/// }
21+
/// )
22+
/// );
23+
/// ```
24+
#[macro_export]
25+
macro_rules! implement_from_tuple {
26+
($struct_name:ident, ($($field_name:ident : $field_type:ty => $closure:expr),+)) => {
27+
impl From<Tuple> for $struct_name {
28+
fn from(tuple: Tuple) -> Self {
29+
fn try_get<T: 'static>(tuple: &Tuple, field_name: &str) -> Option<DataValue> {
30+
let ty = LogicalType::type_trans::<T>()?;
31+
let (idx, _) = tuple.columns
32+
.iter()
33+
.enumerate()
34+
.find(|(_, col)| &col.name == field_name)?;
35+
36+
DataValue::clone(&tuple.values[idx])
37+
.cast(&ty)
38+
.ok()
39+
}
40+
41+
let mut struct_instance = $struct_name::default();
42+
$(
43+
if let Some(value) = try_get::<$field_type>(&tuple, stringify!($field_name)) {
44+
$closure(
45+
&mut struct_instance,
46+
value
47+
);
48+
}
49+
)+
50+
struct_instance
51+
}
52+
}
53+
};
54+
}
55+
56+
#[cfg(test)]
57+
mod test {
58+
use std::sync::Arc;
59+
use crate::catalog::{ColumnCatalog, ColumnDesc};
60+
use crate::types::LogicalType;
61+
use crate::types::tuple::Tuple;
62+
use crate::types::value::DataValue;
63+
64+
fn build_tuple() -> Tuple {
65+
let columns = vec![
66+
Arc::new(ColumnCatalog::new(
67+
"c1".to_string(),
68+
false,
69+
ColumnDesc::new(LogicalType::Integer, true, false),
70+
None
71+
)),
72+
Arc::new(ColumnCatalog::new(
73+
"c2".to_string(),
74+
false,
75+
ColumnDesc::new(LogicalType::Varchar(None), false, false),
76+
None
77+
)),
78+
];
79+
let values = vec![
80+
Arc::new(DataValue::Int32(Some(9))),
81+
Arc::new(DataValue::Utf8(Some("LOL".to_string()))),
82+
];
83+
84+
Tuple {
85+
id: None,
86+
columns,
87+
values,
88+
}
89+
}
90+
91+
#[derive(Default, Debug, PartialEq)]
92+
struct MyStruct {
93+
c1: i32,
94+
c2: String,
95+
}
96+
97+
implement_from_tuple!(
98+
MyStruct, (
99+
c1: i32 => |inner: &mut MyStruct, value| {
100+
if let DataValue::Int32(Some(val)) = value {
101+
inner.c1 = val;
102+
}
103+
},
104+
c2: String => |inner: &mut MyStruct, value| {
105+
if let DataValue::Utf8(Some(val)) = value {
106+
inner.c2 = val;
107+
}
108+
}
109+
)
110+
);
111+
112+
#[test]
113+
fn test_from_tuple() {
114+
let my_struct = MyStruct::from(build_tuple());
115+
116+
println!("{:?}", my_struct);
117+
118+
assert_eq!(my_struct.c1, 9);
119+
assert_eq!(my_struct.c2, "LOL");
120+
}
121+
}

src/types/mod.rs

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,9 @@ pub mod value;
33
pub mod tuple;
44
pub mod index;
55

6+
use std::any::TypeId;
7+
use chrono::{NaiveDate, NaiveDateTime};
8+
use rust_decimal::Decimal;
69
use serde::{Deserialize, Serialize};
710

811
use sqlparser::ast::ExactNumberInfo;
@@ -37,6 +40,42 @@ pub enum LogicalType {
3740
}
3841

3942
impl LogicalType {
43+
pub fn type_trans<T: 'static>() -> Option<LogicalType> {
44+
let type_id = TypeId::of::<T>();
45+
46+
if type_id == TypeId::of::<i8>() {
47+
Some(LogicalType::Tinyint)
48+
} else if type_id == TypeId::of::<i16>() {
49+
Some(LogicalType::Smallint)
50+
} else if type_id == TypeId::of::<i32>() {
51+
Some(LogicalType::Integer)
52+
} else if type_id == TypeId::of::<i64>() {
53+
Some(LogicalType::Bigint)
54+
} else if type_id == TypeId::of::<u8>() {
55+
Some(LogicalType::UTinyint)
56+
} else if type_id == TypeId::of::<u16>() {
57+
Some(LogicalType::USmallint)
58+
} else if type_id == TypeId::of::<u32>() {
59+
Some(LogicalType::UInteger)
60+
} else if type_id == TypeId::of::<u64>() {
61+
Some(LogicalType::UBigint)
62+
} else if type_id == TypeId::of::<f32>() {
63+
Some(LogicalType::Float)
64+
} else if type_id == TypeId::of::<f64>() {
65+
Some(LogicalType::Double)
66+
} else if type_id == TypeId::of::<NaiveDate>() {
67+
Some(LogicalType::Date)
68+
} else if type_id == TypeId::of::<NaiveDateTime>() {
69+
Some(LogicalType::DateTime)
70+
} else if type_id == TypeId::of::<Decimal>() {
71+
Some(LogicalType::Decimal(None, None))
72+
} else if type_id == TypeId::of::<String>() {
73+
Some(LogicalType::Varchar(None))
74+
} else {
75+
None
76+
}
77+
}
78+
4079
pub fn raw_len(&self) -> Option<usize> {
4180
match self {
4281
LogicalType::Invalid => Some(0),

src/types/value.rs

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,38 @@ pub enum DataValue {
5353
Decimal(Option<Decimal>),
5454
}
5555

56+
macro_rules! generate_get_option {
57+
($data_value:ident, $($prefix:ident : $variant:ident($field:ty)),*) => {
58+
impl $data_value {
59+
$(
60+
pub fn $prefix(&self) -> $field {
61+
if let $data_value::$variant(Some(val)) = self {
62+
Some(val.clone())
63+
} else {
64+
None
65+
}
66+
}
67+
)*
68+
}
69+
};
70+
}
71+
72+
generate_get_option!(DataValue,
73+
bool : Boolean(Option<bool>),
74+
float : Float32(Option<f32>),
75+
double : Float64(Option<f64>),
76+
i8 : Int8(Option<i8>),
77+
i16 : Int16(Option<i16>),
78+
i32 : Int32(Option<i32>),
79+
i64 : Int64(Option<i64>),
80+
u8 : UInt8(Option<u8>),
81+
u16 : UInt16(Option<u16>),
82+
u32 : UInt32(Option<u32>),
83+
u64 : UInt64(Option<u64>),
84+
utf8 : Utf8(Option<String>),
85+
decimal : Decimal(Option<Decimal>)
86+
);
87+
5688
impl PartialEq for DataValue {
5789
fn eq(&self, other: &Self) -> bool {
5890
use DataValue::*;
@@ -202,6 +234,22 @@ macro_rules! varchar_cast {
202234
}
203235

204236
impl DataValue {
237+
pub fn date(&self) -> Option<NaiveDate> {
238+
if let DataValue::Date32(Some(val)) = self {
239+
NaiveDate::from_num_days_from_ce_opt(*val)
240+
} else {
241+
None
242+
}
243+
}
244+
245+
pub fn datetime(&self) -> Option<NaiveDateTime> {
246+
if let DataValue::Date64(Some(val)) = self {
247+
NaiveDateTime::from_timestamp_opt(*val, 0)
248+
} else {
249+
None
250+
}
251+
}
252+
205253
pub(crate) fn check_len(&self, logic_type: &LogicalType) -> Result<(), TypeError> {
206254
let is_over_len = match (logic_type, self) {
207255
(LogicalType::Varchar(Some(len)), DataValue::Utf8(Some(val))) => {

0 commit comments

Comments
 (0)