Skip to content

Commit

Permalink
refactor: Update GlueSQL and SurrealDB examples. (#1980)
Browse files Browse the repository at this point in the history
* refactor: Bump gluesql to 0.15
Relate to gluesql/gluesql#1438

* Use SQLParser to parse and replace placeholders.

* Use SQLParser for surrealdb demo.

* Transform the query by SQLParser.
  • Loading branch information
langyo authored Dec 8, 2023
1 parent 2844d03 commit 2bfd7d6
Show file tree
Hide file tree
Showing 4 changed files with 180 additions and 52 deletions.
6 changes: 2 additions & 4 deletions examples/proxy_gluesql_example/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -15,16 +15,14 @@ futures = { version = "0.3" }
async-stream = { version = "0.3" }
futures-util = { version = "0.3" }

sqlparser = "0.40"
sea-orm = { path = "../../", features = [
"sqlx-all",
"proxy",
"runtime-async-std-native-tls",
"debug-print",
] }
# Since it's newer version (0.14.0) locked the chrono's version to 0.4.23,
# we need to lock it on older version too.
# Related to https://github.com/gluesql/gluesql/pull/1427
gluesql = { version = "0.13", default-features = false, features = [
gluesql = { version = "0.15", default-features = false, features = [
"memory-storage",
] }

Expand Down
108 changes: 76 additions & 32 deletions examples/proxy_gluesql_example/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -33,52 +33,95 @@ impl ProxyDatabaseTrait for ProxyDb {
let sql = statement.sql.clone();

let mut ret: Vec<ProxyRow> = vec![];
for payload in self.mem.lock().unwrap().execute(sql).unwrap().iter() {
match payload {
gluesql::prelude::Payload::Select { labels, rows } => {
for row in rows.iter() {
let mut map = BTreeMap::new();
for (label, column) in labels.iter().zip(row.iter()) {
map.insert(
label.to_owned(),
match column {
gluesql::prelude::Value::I64(val) => {
sea_orm::Value::BigInt(Some(*val))
}
gluesql::prelude::Value::Str(val) => {
sea_orm::Value::String(Some(Box::new(val.to_owned())))
}
_ => unreachable!("Unsupported value: {:?}", column),
},
);
async_std::task::block_on(async {
for payload in self.mem.lock().unwrap().execute(sql).await.unwrap().iter() {
match payload {
gluesql::prelude::Payload::Select { labels, rows } => {
for row in rows.iter() {
let mut map = BTreeMap::new();
for (label, column) in labels.iter().zip(row.iter()) {
map.insert(
label.to_owned(),
match column {
gluesql::prelude::Value::I64(val) => {
sea_orm::Value::BigInt(Some(*val))
}
gluesql::prelude::Value::Str(val) => {
sea_orm::Value::String(Some(Box::new(val.to_owned())))
}
_ => unreachable!("Unsupported value: {:?}", column),
},
);
}
ret.push(map.into());
}
ret.push(map.into());
}
_ => unreachable!("Unsupported payload: {:?}", payload),
}
_ => unreachable!("Unsupported payload: {:?}", payload),
}
}
});

Ok(ret)
}

fn execute(&self, statement: Statement) -> Result<ProxyExecResult, DbErr> {
if let Some(values) = statement.values {
let sql = if let Some(values) = statement.values {
// Replace all the '?' with the statement values
let mut new_sql = statement.sql.clone();
let mark_count = new_sql.matches('?').count();
for (i, v) in values.0.iter().enumerate() {
if i >= mark_count {
break;
use sqlparser::ast::{Expr, Value};
use sqlparser::dialect::GenericDialect;
use sqlparser::parser::Parser;

let dialect = GenericDialect {};
let mut ast = Parser::parse_sql(&dialect, statement.sql.as_str()).unwrap();
match &mut ast[0] {
sqlparser::ast::Statement::Insert {
columns, source, ..
} => {
for item in columns.iter_mut() {
item.quote_style = Some('"');
}

if let Some(obj) = source {
match &mut *obj.body {
sqlparser::ast::SetExpr::Values(obj) => {
for (mut item, val) in obj.rows[0].iter_mut().zip(values.0.iter()) {
match &mut item {
Expr::Value(item) => {
*item = match val {
sea_orm::Value::String(val) => {
Value::SingleQuotedString(match val {
Some(val) => val.to_string(),
None => "".to_string(),
})
}
sea_orm::Value::BigInt(val) => Value::Number(
val.unwrap_or(0).to_string(),
false,
),
_ => todo!(),
};
}
_ => todo!(),
}
}
}
_ => todo!(),
}
}
}
new_sql = new_sql.replacen('?', &v.to_string(), 1);
_ => todo!(),
}
println!("SQL execute: {}", new_sql);

self.mem.lock().unwrap().execute(new_sql).unwrap();
let statement = &ast[0];
statement.to_string()
} else {
self.mem.lock().unwrap().execute(statement.sql).unwrap();
}
statement.sql
};

println!("SQL execute: {}", sql);
async_std::task::block_on(async {
self.mem.lock().unwrap().execute(sql).await.unwrap();
});

Ok(ProxyExecResult {
last_insert_id: 1,
Expand All @@ -101,6 +144,7 @@ async fn main() {
)
"#,
)
.await
.unwrap();

let db = Database::connect_proxy(
Expand Down
1 change: 1 addition & 0 deletions examples/proxy_surrealdb_example/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ futures = { version = "0.3" }
async-stream = { version = "0.3" }
futures-util = { version = "0.3" }

sqlparser = "0.40"
sea-orm = { path = "../../", features = [
"sqlx-all",
"proxy",
Expand Down
117 changes: 101 additions & 16 deletions examples/proxy_surrealdb_example/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -28,21 +28,58 @@ struct ProxyDb {
impl ProxyDatabaseTrait for ProxyDb {
fn query(&self, statement: Statement) -> Result<Vec<ProxyRow>, DbErr> {
println!("SQL query: {:?}", statement);
let sql = statement.sql.clone();
let mut ret = async_std::task::block_on(async {
// Surrealdb's grammar is not compatible with sea-orm's
// so we need to remove the extra clauses
// from "SELECT `from`.`col` FROM `from` WHERE `from`.`col` = xx"
// to "SELECT `col` FROM `from` WHERE `col` = xx"

// Get the first index of "FROM"
let from_index = sql.find("FROM").unwrap();
// Get the name after "FROM"
let from_name = sql[from_index + 5..].split(' ').next().unwrap();
// Delete the name before all the columns
let new_sql = sql.replace(&format!("{}.", from_name), "");
use sqlparser::ast::{Expr, SelectItem, SetExpr, TableFactor};
use sqlparser::dialect::GenericDialect;
use sqlparser::parser::Parser;

self.mem.query(new_sql).await
let dialect = GenericDialect {};
let mut ast = Parser::parse_sql(&dialect, statement.sql.as_str()).unwrap();
match &mut ast[0] {
sqlparser::ast::Statement::Query(query) => match &mut *query.body {
SetExpr::Select(body) => {
body.projection.iter_mut().for_each(|item| {
match item {
SelectItem::UnnamedExpr(expr) => {
match expr {
Expr::CompoundIdentifier(idents) => {
// Remove the head of the identifier
// e.g. `from`.`col` -> `col`
let ident = idents.pop().unwrap();
*expr = Expr::Identifier(ident);
}
_ => todo!(),
}
}
_ => todo!(),
}
});
body.from.iter_mut().for_each(|item| {
match &mut item.relation {
TableFactor::Table { name, .. } => {
// Remove the head of the identifier
// e.g. `from`.`col` -> `col`
let ident = name.0.pop().unwrap();
name.0 = vec![ident];
}
_ => todo!(),
}
});
}
_ => todo!(),
},
_ => todo!(),
};

let statement = &ast[0];
let sql = statement.to_string();
println!("SQL: {}", sql);
self.mem.query(sql).await
})
.unwrap();

Expand Down Expand Up @@ -116,17 +153,65 @@ impl ProxyDatabaseTrait for ProxyDb {
async_std::task::block_on(async {
if let Some(values) = statement.values {
// Replace all the '?' with the statement values
let mut new_sql = statement.sql.clone();
let mark_count = new_sql.matches('?').count();
for (i, v) in values.0.iter().enumerate() {
if i >= mark_count {
break;
use sqlparser::ast::{Expr, Value};
use sqlparser::dialect::GenericDialect;
use sqlparser::parser::Parser;

let dialect = GenericDialect {};
let mut ast = Parser::parse_sql(&dialect, statement.sql.as_str()).unwrap();
match &mut ast[0] {
sqlparser::ast::Statement::Insert {
table_name,
columns,
source,
..
} => {
// Replace the table name's quote style
table_name.0[0].quote_style = Some('`');

// Replace all the column names' quote style
for item in columns.iter_mut() {
item.quote_style = Some('`');
}

// Convert the values to sea-orm's format
if let Some(obj) = source {
match &mut *obj.body {
sqlparser::ast::SetExpr::Values(obj) => {
for (mut item, val) in
obj.rows[0].iter_mut().zip(values.0.iter())
{
match &mut item {
Expr::Value(item) => {
*item = match val {
sea_orm::Value::String(val) => {
Value::SingleQuotedString(match val {
Some(val) => val.to_string(),
None => "".to_string(),
})
}
sea_orm::Value::BigInt(val) => Value::Number(
val.unwrap_or(0).to_string(),
false,
),
_ => todo!(),
};
}
_ => todo!(),
}
}
}
_ => todo!(),
}
}
}
new_sql = new_sql.replacen('?', &v.to_string(), 1);
_ => todo!(),
}
println!("SQL execute: {}", new_sql);

self.mem.query(new_sql).await
let statement = &ast[0];
let sql = statement.to_string();
println!("SQL: {}", sql);
self.mem.query(sql).await
} else {
self.mem.query(statement.sql).await
}
Expand Down

0 comments on commit 2bfd7d6

Please sign in to comment.