Skip to content

Commit

Permalink
test enhanced
Browse files Browse the repository at this point in the history
  • Loading branch information
Werner Braun committed Jun 4, 2024
1 parent 6f34574 commit d32caa1
Show file tree
Hide file tree
Showing 6 changed files with 182 additions and 77 deletions.
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@ and this project adheres to [Semantic Versioning](https://semver.org).

## Unreleased

- test enhanced

## 0.1.2 - 2024-06-04

- Documentation improvements
Expand Down
2 changes: 1 addition & 1 deletion src/create_index.rs
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ fn gen_struct(
if col_attr.constraint.starts_with("REFERENCES ") {
let col_name = ident.to_string();
create_index.push_str(&format!(
"CREATE INDEX IF NOT EXISTS {tab_name}_{col_name}_idx ON {tab_name}({col_name});"
"CREATE INDEX IF NOT EXISTS {tab_name}_{col_name}_idx ON {tab_name}({col_name}); "
));
}
}
Expand Down
4 changes: 2 additions & 2 deletions src/create_table_log.rs
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@ fn gen_struct(
}
if col_attr.constraint.starts_with("PRIMARY KEY") {
create_index = format!(
"CREATE INDEX IF NOT EXISTS {tab_log_name}_{col_name}_idx ON {tab_log_name}({col_name});"
"CREATE INDEX IF NOT EXISTS {tab_log_name}_{col_name}_idx ON {tab_log_name}({col_name}); "
);
}
}
Expand All @@ -63,7 +63,7 @@ fn gen_struct(
.make_pub()
.with_value(|b| {
b.push_parsed(format!(
"\"CREATE TABLE IF NOT EXISTS {tab_log_name} ({col_defs}) STRICT; {create_index} CREATE TRIGGER IF NOT EXISTS {tab_name}_update UPDATE ON {tab_name} BEGIN INSERT INTO {tab_log_name} ({columns}) VALUES ({log_values}); END; CREATE TRIGGER IF NOT EXISTS {tab_name}_delete DELETE ON {tab_name} BEGIN INSERT INTO {tab_log_name} ({columns}) VALUES ({log_values}); END;\""
"\"CREATE TABLE IF NOT EXISTS {tab_log_name} ({col_defs}) STRICT; {create_index}CREATE TRIGGER IF NOT EXISTS {tab_name}_update UPDATE ON {tab_name} BEGIN INSERT INTO {tab_log_name} ({columns}) VALUES ({log_values}); END; CREATE TRIGGER IF NOT EXISTS {tab_name}_delete DELETE ON {tab_name} BEGIN INSERT INTO {tab_log_name} ({columns}) VALUES ({log_values}); END;\""
))?;
Ok(())
})?;
Expand Down
42 changes: 22 additions & 20 deletions src/get.rs
Original file line number Diff line number Diff line change
Expand Up @@ -56,9 +56,10 @@ fn gen_struct(
}
columns.pop(); // get rid of the last ','

let mut gen_impl = generator.generate_impl();
if !pk.is_empty() {
gen_impl
if !pk.is_empty() || !unique.is_empty() {
let mut gen_impl = generator.generate_impl();
if !pk.is_empty() {
gen_impl
.generate_fn(&format!("get_by_{pk}"))
.as_async()
.with_arg(&pk, &pk_typ)
Expand All @@ -77,23 +78,24 @@ fn gen_struct(
fn_body.push_parsed(s)?;
Ok(())
})?;
}
if !unique.is_empty() {
unique.pop();
unique_typ.pop();
for (col, typ) in std::iter::zip(unique.split(','), unique_typ.split(',')) {
gen_impl
.generate_fn(&format!("get_by_{col}"))
.as_async()
.with_arg(col, typ)
.with_arg("exec", "impl ::sqlx::SqliteExecutor<'_>")
.with_return_type("Result<Self, ::sqlx::Error>")
.make_pub()
.body(|fn_body| {
let s = format!("::sqlx::query_as::<_, Self>(\"SELECT {columns} FROM {tab_name} WHERE {col}=?\").bind({col}).fetch_one(exec).await");
fn_body.push_parsed(s)?;
Ok(())
})?;
}
if !unique.is_empty() {
unique.pop();
unique_typ.pop();
for (col, typ) in std::iter::zip(unique.split(','), unique_typ.split(',')) {
gen_impl
.generate_fn(&format!("get_by_{col}"))
.as_async()
.with_arg(col, typ)
.with_arg("exec", "impl ::sqlx::SqliteExecutor<'_>")
.with_return_type("Result<Self, ::sqlx::Error>")
.make_pub()
.body(|fn_body| {
let s = format!("::sqlx::query_as::<_, Self>(\"SELECT {columns} FROM {tab_name} WHERE {col}=?\").bind({col}).fetch_one(exec).await");
fn_body.push_parsed(s)?;
Ok(())
})?;
}
}
}
Ok(())
Expand Down
4 changes: 2 additions & 2 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -35,8 +35,8 @@ use virtue::prelude::TokenStream;
/// assert_eq!(
/// Cat::CREATE_INDEX_SQL,
/// concat!(
/// "CREATE INDEX IF NOT EXISTS cat_mother_idx ON cat(mother);",
/// "CREATE INDEX IF NOT EXISTS cat_owner_idx ON cat(owner);"
/// "CREATE INDEX IF NOT EXISTS cat_mother_idx ON cat(mother); ",
/// "CREATE INDEX IF NOT EXISTS cat_owner_idx ON cat(owner); "
/// )
/// );
/// ```
Expand Down
205 changes: 153 additions & 52 deletions tests/test.rs
Original file line number Diff line number Diff line change
@@ -1,76 +1,177 @@
//! wb_sqlite derive macro test / example
#![allow(unused)]

use wb_sqlite::{
CreateIndexSql, CreateTableLogSql, CreateTableSql, Get, Insert, InsertSync, SelectAsSql,
SelectSql, Update, UpdateSync,
};

#[derive(
Debug,
Default,
CreateTableSql,
CreateTableLogSql,
SelectSql,
Get,
Insert,
InsertSync,
Update,
UpdateSync,
sqlx::FromRow,
)]
struct Record {
#[sql(constraint = "PRIMARY KEY")]
id: i64,
#[sql(constraint = "UNIQUE CHECK(name != '')")]
name: String,
ok: bool,
pos: u32,
num: i64,
sci_val: f64,
note: String,
data: Vec<u8>,
opt_ok: Option<bool>,
opt_pos: Option<u32>,
opt_num: Option<i64>,
opt_sci_val: Option<f64>,
opt_note: Option<String>,
opt_data: Option<Vec<u8>>,
#[sql(typ = "ANY")]
any_data: Option<Vec<u8>>,
}

#[derive(
Debug,
Default,
CreateTableSql,
SelectSql,
SelectAsSql,
Get,
Insert,
InsertSync,
Update,
UpdateSync,
sqlx::FromRow,
)]
#[sqlas(from = "record where id < 3 order by id desc")]
struct MapRecord {
#[sql(constraint = "PRIMARY KEY")]
#[sqlas(col = "0")]
id: i64,
name: String,
#[sqlas(col = "num * sci_val")]
val: f64,
#[sqlas(col = "concat(name,'; note: ',note)")]
note: String,
}

#[derive(
Debug,
Default,
CreateTableSql,
CreateIndexSql,
SelectSql,
Get, // no generate
Insert,
InsertSync,
Update, // no generate
UpdateSync, // no generate
sqlx::FromRow,
)]
struct NtoMrel {
#[sql(constraint = "REFERENCES record(id) ON UPDATE RESTRICT ON DELETE RESTRICT")]
n: i64,
#[sql(constraint = "REFERENCES map_record(id) ON UPDATE RESTRICT ON DELETE RESTRICT")]
m: i64,
}

#[test]
fn rusqlite() -> Result<(), rusqlite::Error> {
use wb_sqlite::CreateTableSql;
fn create() -> Result<(), rusqlite::Error> {
fn eq(l: &str, r: &str) {
assert_eq!(l, r)
}
eq(
Record::CREATE_TABLE_SQL,
"CREATE TABLE IF NOT EXISTS record (id INTEGER NOT NULL PRIMARY KEY, name TEXT NOT NULL UNIQUE CHECK(name != ''), ok INTEGER NOT NULL, pos INTEGER NOT NULL, num INTEGER NOT NULL, sci_val REAL NOT NULL, note TEXT NOT NULL, data BLOB NOT NULL, opt_ok INTEGER, opt_pos INTEGER, opt_num INTEGER, opt_sci_val REAL, opt_note TEXT, opt_data BLOB, any_data ANY) STRICT;"
);
eq(
Record::CREATE_TABLE_LOG_SQL,
"CREATE TABLE IF NOT EXISTS record_log (id INTEGER NOT NULL, name TEXT NOT NULL, ok INTEGER NOT NULL, pos INTEGER NOT NULL, num INTEGER NOT NULL, sci_val REAL NOT NULL, note TEXT NOT NULL, data BLOB NOT NULL, opt_ok INTEGER, opt_pos INTEGER, opt_num INTEGER, opt_sci_val REAL, opt_note TEXT, opt_data BLOB, any_data ANY) STRICT; CREATE INDEX IF NOT EXISTS record_log_id_idx ON record_log(id); CREATE TRIGGER IF NOT EXISTS record_update UPDATE ON record BEGIN INSERT INTO record_log (id,name,ok,pos,num,sci_val,note,data,opt_ok,opt_pos,opt_num,opt_sci_val,opt_note,opt_data,any_data) VALUES (OLD.id,OLD.name,OLD.ok,OLD.pos,OLD.num,OLD.sci_val,OLD.note,OLD.data,OLD.opt_ok,OLD.opt_pos,OLD.opt_num,OLD.opt_sci_val,OLD.opt_note,OLD.opt_data,OLD.any_data); END; CREATE TRIGGER IF NOT EXISTS record_delete DELETE ON record BEGIN INSERT INTO record_log (id,name,ok,pos,num,sci_val,note,data,opt_ok,opt_pos,opt_num,opt_sci_val,opt_note,opt_data,any_data) VALUES (OLD.id,OLD.name,OLD.ok,OLD.pos,OLD.num,OLD.sci_val,OLD.note,OLD.data,OLD.opt_ok,OLD.opt_pos,OLD.opt_num,OLD.opt_sci_val,OLD.opt_note,OLD.opt_data,OLD.any_data); END;"
);
eq(
MapRecord::CREATE_TABLE_SQL,
"CREATE TABLE IF NOT EXISTS map_record (id INTEGER NOT NULL PRIMARY KEY, name TEXT NOT NULL, val REAL NOT NULL, note TEXT NOT NULL) STRICT;"
);
eq(
NtoMrel::CREATE_TABLE_SQL,
"CREATE TABLE IF NOT EXISTS nto_mrel (n INTEGER NOT NULL REFERENCES record(id) ON UPDATE RESTRICT ON DELETE RESTRICT, m INTEGER NOT NULL REFERENCES map_record(id) ON UPDATE RESTRICT ON DELETE RESTRICT) STRICT;"
);
eq(
NtoMrel::CREATE_INDEX_SQL,
"CREATE INDEX IF NOT EXISTS nto_mrel_n_idx ON nto_mrel(n); CREATE INDEX IF NOT EXISTS nto_mrel_m_idx ON nto_mrel(m); "
);

fn c(sql: &str) {
let c = rusqlite::Connection::open_in_memory().unwrap();
let c = rusqlite::Connection::open_in_memory()?;
fn x(c: &rusqlite::Connection, sql: &str) {
match c.execute_batch(sql) {
Ok(_) => (),
Err(err) => panic!("{err}"),
};
}
x(&c, Record::CREATE_TABLE_SQL);
x(&c, Record::CREATE_TABLE_LOG_SQL);
x(&c, MapRecord::CREATE_TABLE_SQL);
x(&c, NtoMrel::CREATE_TABLE_SQL);
x(&c, NtoMrel::CREATE_INDEX_SQL);

fn eq(sql: &str, cmp: &str) {
assert_eq!(sql, cmp);
c(sql);
}
Ok(())
}

#[derive(CreateTableSql)]
#[sql(option = "WITHOUT ROWID")]
pub struct Person {
#[sql(constraint = "PRIMARY KEY")]
pub id: i64,
#[sql(constraint = "UNIQUE CHECK (name != '')")]
pub name: String, // email
#[sql(constraint = "DEFAULT ''")]
pub last_login: String,
#[sql(constraint = "DEFAULT ''")]
pub pw_hash: String,
#[sql(constraint = "DEFAULT NULL")]
pub roles: Option<String>,
#[sql(typ = "ANY", constraint = "DEFAULT NULL")]
pub timestamp: String,
}
eq(
Person::CREATE_TABLE_SQL,
"CREATE TABLE IF NOT EXISTS person (id INTEGER NOT NULL PRIMARY KEY, name TEXT NOT NULL UNIQUE CHECK (name != ''), last_login TEXT NOT NULL DEFAULT '', pw_hash TEXT NOT NULL DEFAULT '', roles TEXT DEFAULT NULL, timestamp ANY DEFAULT NULL) STRICT, WITHOUT ROWID;"
);
#[test]
fn rusqlite() -> Result<(), rusqlite::Error> {
let c = rusqlite::Connection::open_in_memory()?;
c.execute_batch(Record::CREATE_TABLE_SQL)?;
c.execute_batch(Record::CREATE_TABLE_LOG_SQL)?;
c.execute_batch(MapRecord::CREATE_TABLE_SQL)?;
c.execute_batch(NtoMrel::CREATE_TABLE_SQL)?;
c.execute_batch(NtoMrel::CREATE_INDEX_SQL)?;

let mut r = Record::default();

r.name = "me".to_owned();
let id = r.insert_sync(&c)?;
assert!(id == 1);

r.id = id;
r.name = "you".to_owned();
let ok = r.update_sync(&c)?;
assert!(ok);

// ToDo: MapRecord select as, TableLog auslesen

Ok(())
}

#[tokio::test]
async fn sqlx() -> Result<(), sqlx::Error> {
use wb_sqlite::{CreateTableSql, Insert, Update};

#[derive(CreateTableSql, Insert, Update)]
struct Person {
#[sql(constraint = "PRIMARY KEY")]
id: i64,
#[sql(constraint = "UNIQUE")]
name: String,
}

use sqlx::{Connection, Executor, SqliteConnection};
let mut c = SqliteConnection::connect(":memory:").await?;
c.execute(Record::CREATE_TABLE_SQL).await?;
c.execute(Record::CREATE_TABLE_LOG_SQL).await?;
c.execute(MapRecord::CREATE_TABLE_SQL).await?;
c.execute(NtoMrel::CREATE_TABLE_SQL).await?;
c.execute(NtoMrel::CREATE_INDEX_SQL).await?;

let mut conn = SqliteConnection::connect(":memory:").await?;

conn.execute(Person::CREATE_TABLE_SQL).await?;
let mut r = Record::default();

let p = Person {
id: 0,
name: "me".to_owned(),
};
let id = p.insert(&mut conn).await?;
assert!(id > 0);
r.name = "me".to_owned();
let id = r.insert(&mut c).await?;
assert!(id == 1);

let p2 = Person {
id,
name: "you".to_owned(),
};
let ok = p2.update(&mut conn).await?;
r.id = id;
r.name = "you".to_owned();
let ok = r.update(&mut c).await?;
assert!(ok);

Ok(())
Expand Down

0 comments on commit d32caa1

Please sign in to comment.