test(db): add logging to tests
This commit is contained in:
parent
6c2442a82b
commit
8ac1d77e06
1 changed files with 184 additions and 133 deletions
317
src/db.rs
317
src/db.rs
|
@ -1,13 +1,13 @@
|
|||
#[cfg(feature = "ssr")]
|
||||
mod db_impl {
|
||||
use crate::models::item::Item;
|
||||
use leptos::logging;
|
||||
use leptos::logging::log;
|
||||
use rusqlite::{Connection, Error};
|
||||
use serde::{Deserialize, Serialize};
|
||||
use std::collections::{HashMap, HashSet};
|
||||
use std::sync::Arc;
|
||||
use tokio::sync::Mutex;
|
||||
use leptos::logging;
|
||||
use std::collections::{HashMap, HashSet};
|
||||
use crate::models::item::Item;
|
||||
use leptos::logging::log;
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
|
@ -17,19 +17,29 @@ mod db_impl {
|
|||
|
||||
// Helper function to create test database
|
||||
async fn create_test_db() -> Database {
|
||||
log!("[TEST] Creating in-memory test database");
|
||||
let db = Database::new(":memory:").unwrap();
|
||||
db.create_schema().await.unwrap();
|
||||
log!("[TEST] Database schema created");
|
||||
db
|
||||
}
|
||||
|
||||
// Test database schema creation
|
||||
#[tokio::test]
|
||||
async fn test_schema_creation() {
|
||||
log!("[TEST] Starting test_schema_creation");
|
||||
let db = create_test_db().await;
|
||||
|
||||
// Verify tables exist
|
||||
let conn = db.conn.lock().await;
|
||||
let mut stmt = conn.prepare("SELECT name FROM sqlite_master WHERE type='table'").unwrap();
|
||||
let tables: Vec<String> = stmt.query_map([], |row| row.get(0)).unwrap().collect::<Result<_, _>>().unwrap();
|
||||
let mut stmt = conn
|
||||
.prepare("SELECT name FROM sqlite_master WHERE type='table'")
|
||||
.unwrap();
|
||||
let tables: Vec<String> = stmt
|
||||
.query_map([], |row| row.get(0))
|
||||
.unwrap()
|
||||
.collect::<Result<_, _>>()
|
||||
.unwrap();
|
||||
|
||||
assert!(tables.contains(&"urls".to_string()));
|
||||
assert!(tables.contains(&"items".to_string()));
|
||||
|
@ -41,6 +51,7 @@ mod db_impl {
|
|||
// Item Lifecycle Tests
|
||||
#[tokio::test]
|
||||
async fn test_full_item_lifecycle() {
|
||||
log!("[TEST] Starting test_full_item_lifecycle");
|
||||
let db = create_test_db().await;
|
||||
let test_url = "https://example.com";
|
||||
let test_item = Item {
|
||||
|
@ -50,62 +61,87 @@ mod db_impl {
|
|||
wikidata_id: Some("Q123".into()),
|
||||
custom_properties: vec![
|
||||
("price".into(), "100".into()),
|
||||
("color".into(), "red".into())
|
||||
].into_iter().collect(),
|
||||
("color".into(), "red".into()),
|
||||
]
|
||||
.into_iter()
|
||||
.collect(),
|
||||
};
|
||||
|
||||
|
||||
// Test insertion
|
||||
log!("[TEST] Testing item insertion");
|
||||
db.insert_item_by_url(test_url, &test_item).await.unwrap();
|
||||
log!("[TEST] Item insertion - PASSED");
|
||||
|
||||
// Test retrieval
|
||||
log!("[TEST] Testing item retrieval");
|
||||
let items = db.get_items_by_url(test_url).await.unwrap();
|
||||
assert_eq!(items.len(), 1);
|
||||
let stored_item = &items[0];
|
||||
assert_eq!(stored_item.name, test_item.name);
|
||||
assert_eq!(stored_item.custom_properties.len(), 2);
|
||||
|
||||
log!("[TEST] Item retrieval and validation - PASSED");
|
||||
|
||||
// Test update
|
||||
log!("[TEST] Testing item update");
|
||||
let mut updated_item = test_item.clone();
|
||||
updated_item.name = "Updated Name".into();
|
||||
db.insert_item_by_url(test_url, &updated_item).await.unwrap();
|
||||
db.insert_item_by_url(test_url, &updated_item)
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
// Verify update
|
||||
let items = db.get_items_by_url(test_url).await.unwrap();
|
||||
assert_eq!(items[0].name, "Updated Name");
|
||||
|
||||
log!("[TEST] Item update - PASSED");
|
||||
|
||||
// Test deletion
|
||||
db.delete_item_by_url(test_url, &test_item.id).await.unwrap();
|
||||
log!("[TEST] Testing item deletion");
|
||||
db.delete_item_by_url(test_url, &test_item.id)
|
||||
.await
|
||||
.unwrap();
|
||||
let items = db.get_items_by_url(test_url).await.unwrap();
|
||||
assert!(items.is_empty());
|
||||
log!("[TEST] Item deletion - PASSED");
|
||||
log!("[TEST] test_full_item_lifecycle completed successfully");
|
||||
}
|
||||
|
||||
//URL Management Tests
|
||||
#[tokio::test]
|
||||
async fn test_url_management() {
|
||||
log!("[TEST] Starting test_url_management");
|
||||
let db = create_test_db().await;
|
||||
let test_url = "https://test.com";
|
||||
|
||||
|
||||
// Test URL creation
|
||||
log!("[TEST] Testing URL creation");
|
||||
let url_id = db.insert_url(test_url).await.unwrap();
|
||||
assert!(url_id > 0);
|
||||
|
||||
log!("[TEST] URL creation - PASSED");
|
||||
|
||||
// Test duplicate URL handling
|
||||
log!("[TEST] Testing duplicate URL handling");
|
||||
let duplicate_id = db.insert_url(test_url).await.unwrap();
|
||||
assert_eq!(url_id, duplicate_id);
|
||||
|
||||
log!("[TEST] Duplicate URL handling - PASSED");
|
||||
|
||||
// Test URL retrieval
|
||||
log!("[TEST] Testing URL retrieval");
|
||||
let conn = db.conn.lock().await;
|
||||
let stored_url: String = conn.query_row(
|
||||
"SELECT url FROM urls WHERE id = ?",
|
||||
[url_id],
|
||||
|row| row.get(0)
|
||||
).unwrap();
|
||||
let stored_url: String = conn
|
||||
.query_row("SELECT url FROM urls WHERE id = ?", [url_id], |row| {
|
||||
row.get(0)
|
||||
})
|
||||
.unwrap();
|
||||
assert_eq!(stored_url, test_url);
|
||||
log!("[TEST] URL retrieval - PASSED");
|
||||
|
||||
log!("[TEST] test_url_management completed successfully");
|
||||
}
|
||||
|
||||
//property management tests
|
||||
#[tokio::test]
|
||||
async fn test_property_operations() {
|
||||
log!("[TEST] Starting test_property_operations");
|
||||
let db = create_test_db().await;
|
||||
let test_url = "https://props.com";
|
||||
let test_item = Item {
|
||||
|
@ -115,43 +151,59 @@ mod db_impl {
|
|||
wikidata_id: Some("Q123".into()),
|
||||
custom_properties: vec![
|
||||
("price".into(), "100".into()),
|
||||
("color".into(), "red".into())
|
||||
].into_iter().collect(),
|
||||
};
|
||||
("color".into(), "red".into()),
|
||||
]
|
||||
.into_iter()
|
||||
.collect(),
|
||||
};
|
||||
// Test property creation
|
||||
log!("[TEST] Testing property creation");
|
||||
db.insert_item_by_url(test_url, &test_item).await.unwrap();
|
||||
|
||||
// Verify properties stored
|
||||
let items = db.get_items_by_url(test_url).await.unwrap();
|
||||
assert_eq!(items[0].custom_properties.len(), 2);
|
||||
|
||||
log!("[TEST] Property creation - PASSED");
|
||||
|
||||
// Test property deletion
|
||||
log!("[TEST] Testing property deletion");
|
||||
db.delete_property_by_url(test_url, "price").await.unwrap();
|
||||
let items = db.get_items_by_url(test_url).await.unwrap();
|
||||
assert_eq!(items[0].custom_properties.len(), 1);
|
||||
assert!(!items[0].custom_properties.contains_key("price"));
|
||||
log!("[TEST] Property deletion - PASSED");
|
||||
|
||||
log!("[TEST] test_property_operations completed successfully");
|
||||
}
|
||||
|
||||
//selected properties test
|
||||
#[tokio::test]
|
||||
async fn test_selected_properties() {
|
||||
log!("[TEST] Starting test_selected_properties");
|
||||
let db = create_test_db().await;
|
||||
let test_url = "https://selected.com";
|
||||
|
||||
// Add test properties
|
||||
log!("[TEST] Adding selected properties");
|
||||
db.add_selected_property(test_url, "price").await.unwrap();
|
||||
db.add_selected_property(test_url, "weight").await.unwrap();
|
||||
|
||||
|
||||
// Test retrieval
|
||||
log!("[TEST] Testing property retrieval");
|
||||
let props = db.get_selected_properties(test_url).await.unwrap();
|
||||
assert_eq!(props.len(), 2);
|
||||
assert!(props.contains(&"price".to_string()));
|
||||
assert!(props.contains(&"weight".to_string()));
|
||||
|
||||
log!("[TEST] Property retrieval - PASSED");
|
||||
|
||||
// Test duplicate prevention
|
||||
log!("[TEST] Testing duplicate prevention");
|
||||
db.add_selected_property(test_url, "price").await.unwrap();
|
||||
let props = db.get_selected_properties(test_url).await.unwrap();
|
||||
assert_eq!(props.len(), 2); // No duplicate added
|
||||
log!("[TEST] Duplicate prevention - PASSED");
|
||||
|
||||
log!("[TEST] test_selected_properties completed successfully");
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -165,7 +217,7 @@ mod db_impl {
|
|||
// Create a new database connection
|
||||
pub fn new(db_path: &str) -> Result<Self, Error> {
|
||||
let conn = Connection::open(db_path)?;
|
||||
logging::log!("Database connection established at: {}", db_path);
|
||||
logging::log!("Database connection established at: {}", db_path);
|
||||
Ok(Database {
|
||||
conn: Arc::new(Mutex::new(conn)),
|
||||
})
|
||||
|
@ -174,15 +226,16 @@ mod db_impl {
|
|||
// Create the database schema
|
||||
pub async fn create_schema(&self) -> Result<(), Error> {
|
||||
let conn = self.conn.lock().await;
|
||||
|
||||
|
||||
// 1. Properties table
|
||||
conn.execute_batch(
|
||||
"CREATE TABLE IF NOT EXISTS properties (
|
||||
id INTEGER PRIMARY KEY,
|
||||
name TEXT NOT NULL UNIQUE,
|
||||
global_usage_count INTEGER DEFAULT 0
|
||||
);"
|
||||
).map_err(|e| {
|
||||
);",
|
||||
)
|
||||
.map_err(|e| {
|
||||
eprintln!("Failed creating properties table: {}", e);
|
||||
e
|
||||
})?;
|
||||
|
@ -194,7 +247,8 @@ mod db_impl {
|
|||
url TEXT NOT NULL UNIQUE,
|
||||
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
|
||||
);",
|
||||
).map_err(|e| {
|
||||
)
|
||||
.map_err(|e| {
|
||||
eprintln!("Failed creating urls table: {}", e);
|
||||
e
|
||||
})?;
|
||||
|
@ -209,7 +263,8 @@ mod db_impl {
|
|||
wikidata_id TEXT,
|
||||
FOREIGN KEY (url_id) REFERENCES urls(id) ON DELETE CASCADE
|
||||
);",
|
||||
).map_err(|e| {
|
||||
)
|
||||
.map_err(|e| {
|
||||
eprintln!("Failed creating items table: {}", e);
|
||||
e
|
||||
})?;
|
||||
|
@ -222,12 +277,13 @@ mod db_impl {
|
|||
PRIMARY KEY (url_id, property_id),
|
||||
FOREIGN KEY (url_id) REFERENCES urls(id) ON DELETE CASCADE,
|
||||
FOREIGN KEY (property_id) REFERENCES properties(id) ON DELETE CASCADE
|
||||
);"
|
||||
).map_err(|e| {
|
||||
);",
|
||||
)
|
||||
.map_err(|e| {
|
||||
eprintln!("Failed creating properties table: {}", e);
|
||||
e
|
||||
})?;
|
||||
|
||||
|
||||
// 5. Junction table for custom properties
|
||||
conn.execute_batch(
|
||||
"CREATE TABLE IF NOT EXISTS item_properties (
|
||||
|
@ -237,8 +293,9 @@ mod db_impl {
|
|||
PRIMARY KEY (item_id, property_id),
|
||||
FOREIGN KEY (item_id) REFERENCES items(id) ON DELETE CASCADE,
|
||||
FOREIGN KEY (property_id) REFERENCES properties(id) ON DELETE CASCADE
|
||||
);"
|
||||
).map_err(|e| {
|
||||
);",
|
||||
)
|
||||
.map_err(|e| {
|
||||
eprintln!("Failed creating item_properties table: {}", e);
|
||||
e
|
||||
})?;
|
||||
|
@ -249,19 +306,13 @@ mod db_impl {
|
|||
pub async fn insert_url(&self, url: &str) -> Result<i64, Error> {
|
||||
let mut conn = self.conn.lock().await;
|
||||
let tx = conn.transaction()?;
|
||||
|
||||
|
||||
// Use INSERT OR IGNORE to handle duplicates
|
||||
tx.execute(
|
||||
"INSERT OR IGNORE INTO urls (url) VALUES (?)",
|
||||
[url]
|
||||
)?;
|
||||
tx.execute("INSERT OR IGNORE INTO urls (url) VALUES (?)", [url])?;
|
||||
|
||||
// Get the URL ID whether it was inserted or already existed
|
||||
let url_id = tx.query_row(
|
||||
"SELECT id FROM urls WHERE url = ?",
|
||||
[url],
|
||||
|row| row.get(0)
|
||||
)?;
|
||||
let url_id =
|
||||
tx.query_row("SELECT id FROM urls WHERE url = ?", [url], |row| row.get(0))?;
|
||||
|
||||
tx.commit()?;
|
||||
logging::log!("URL inserted: {}", url);
|
||||
|
@ -277,12 +328,15 @@ mod db_impl {
|
|||
|
||||
pub async fn delete_property(&self, property: &str) -> Result<(), Error> {
|
||||
let conn = self.conn.lock().await;
|
||||
let query = format!("UPDATE items SET custom_properties = json_remove(custom_properties, '$.{}')", property);
|
||||
let query = format!(
|
||||
"UPDATE items SET custom_properties = json_remove(custom_properties, '$.{}')",
|
||||
property
|
||||
);
|
||||
conn.execute(&query, []).map_err(|e| Error::from(e))?;
|
||||
logging::log!("Property deleted: {}", property);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
|
||||
// Retrieve all items from the database
|
||||
pub async fn get_items(&self) -> Result<Vec<DbItem>, Error> {
|
||||
let conn = self.conn.lock().await;
|
||||
|
@ -306,16 +360,15 @@ mod db_impl {
|
|||
// Retrieve all items from the database for a specific URL
|
||||
pub async fn get_items_by_url(&self, url: &str) -> Result<Vec<Item>, Error> {
|
||||
let conn = self.conn.lock().await;
|
||||
let url_id: Option<i64> = match conn.query_row(
|
||||
"SELECT id FROM urls WHERE url = ?",
|
||||
&[url],
|
||||
|row| row.get(0)
|
||||
) {
|
||||
Ok(id) => Some(id),
|
||||
Err(rusqlite::Error::QueryReturnedNoRows) => None,
|
||||
Err(e) => return Err(e),
|
||||
};
|
||||
|
||||
let url_id: Option<i64> =
|
||||
match conn.query_row("SELECT id FROM urls WHERE url = ?", &[url], |row| {
|
||||
row.get(0)
|
||||
}) {
|
||||
Ok(id) => Some(id),
|
||||
Err(rusqlite::Error::QueryReturnedNoRows) => None,
|
||||
Err(e) => return Err(e),
|
||||
};
|
||||
|
||||
let url_id = match url_id {
|
||||
Some(id) => id,
|
||||
None => return Ok(Vec::new()), // Return empty list if URL not found
|
||||
|
@ -323,28 +376,27 @@ mod db_impl {
|
|||
|
||||
log!("Fetching items for URL '{}' (ID: {})", url, url_id);
|
||||
|
||||
|
||||
let mut stmt = conn.prepare(
|
||||
"SELECT i.id, i.name, i.description, i.wikidata_id,
|
||||
p.name AS prop_name, ip.value
|
||||
FROM items i
|
||||
LEFT JOIN item_properties ip ON i.id = ip.item_id
|
||||
LEFT JOIN properties p ON ip.property_id = p.id
|
||||
WHERE i.url_id = ?"
|
||||
WHERE i.url_id = ?",
|
||||
)?;
|
||||
let mut items: HashMap<String, Item> = HashMap::new();
|
||||
|
||||
|
||||
let rows = stmt.query_map([url_id], |row| {
|
||||
Ok((
|
||||
row.get::<_, String>(0)?, // id
|
||||
row.get::<_, String>(1)?, // name
|
||||
row.get::<_, String>(2)?, // description
|
||||
row.get::<_, Option<String>>(3)?, // wikidata_id
|
||||
row.get::<_, Option<String>>(4)?, // prop_name
|
||||
row.get::<_, Option<String>>(5)?, // value
|
||||
row.get::<_, String>(0)?, // id
|
||||
row.get::<_, String>(1)?, // name
|
||||
row.get::<_, String>(2)?, // description
|
||||
row.get::<_, Option<String>>(3)?, // wikidata_id
|
||||
row.get::<_, Option<String>>(4)?, // prop_name
|
||||
row.get::<_, Option<String>>(5)?, // value
|
||||
))
|
||||
})?;
|
||||
|
||||
|
||||
for row in rows {
|
||||
let (id, name, desc, wd_id, prop, val) = row?;
|
||||
let item = items.entry(id.clone()).or_insert(Item {
|
||||
|
@ -354,25 +406,23 @@ mod db_impl {
|
|||
wikidata_id: wd_id,
|
||||
custom_properties: HashMap::new(),
|
||||
});
|
||||
|
||||
|
||||
if let (Some(p), Some(v)) = (prop, val) {
|
||||
item.custom_properties.insert(p, v);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Ok(items.into_values().collect())
|
||||
}
|
||||
|
||||
async fn get_or_create_property(
|
||||
&self,
|
||||
tx: &mut rusqlite::Transaction<'_>,
|
||||
prop: &str
|
||||
&self,
|
||||
tx: &mut rusqlite::Transaction<'_>,
|
||||
prop: &str,
|
||||
) -> Result<i64, Error> {
|
||||
match tx.query_row(
|
||||
"SELECT id FROM properties WHERE name = ?",
|
||||
[prop],
|
||||
|row| row.get::<_, i64>(0)
|
||||
) {
|
||||
match tx.query_row("SELECT id FROM properties WHERE name = ?", [prop], |row| {
|
||||
row.get::<_, i64>(0)
|
||||
}) {
|
||||
Ok(id) => Ok(id),
|
||||
Err(rusqlite::Error::QueryReturnedNoRows) => {
|
||||
tx.execute("INSERT INTO properties (name) VALUES (?)", [prop])?;
|
||||
|
@ -385,30 +435,28 @@ mod db_impl {
|
|||
// Insert a new item into the database for a specific URL
|
||||
pub async fn insert_item_by_url(&self, url: &str, item: &Item) -> Result<(), Error> {
|
||||
log!("[DB] Starting insert for URL: {}, Item: {}", url, item.id);
|
||||
|
||||
|
||||
// 1. Check database lock acquisition
|
||||
let lock_start = std::time::Instant::now();
|
||||
let mut conn = self.conn.lock().await;
|
||||
log!("[DB] Lock acquired in {:?}", lock_start.elapsed());
|
||||
|
||||
|
||||
// 2. Transaction handling
|
||||
log!("[DB] Starting transaction");
|
||||
let mut tx = conn.transaction().map_err(|e| {
|
||||
log!("[DB] Transaction start failed: {:?}", e);
|
||||
e
|
||||
})?;
|
||||
|
||||
|
||||
// 3. URL handling
|
||||
log!("[DB] Checking URL existence: {}", url);
|
||||
let url_id = match tx.query_row(
|
||||
"SELECT id FROM urls WHERE url = ?",
|
||||
[url],
|
||||
|row| row.get::<_, i64>(0)
|
||||
) {
|
||||
let url_id = match tx.query_row("SELECT id FROM urls WHERE url = ?", [url], |row| {
|
||||
row.get::<_, i64>(0)
|
||||
}) {
|
||||
Ok(id) => {
|
||||
log!("[DB] Found existing URL ID: {}", id);
|
||||
id
|
||||
},
|
||||
}
|
||||
Err(rusqlite::Error::QueryReturnedNoRows) => {
|
||||
log!("[DB] Inserting new URL");
|
||||
tx.execute("INSERT INTO urls (url) VALUES (?)", [url])?;
|
||||
|
@ -418,7 +466,7 @@ mod db_impl {
|
|||
}
|
||||
Err(e) => return Err(e.into()),
|
||||
};
|
||||
|
||||
|
||||
// 4. Item insertion
|
||||
log!("[DB] Upserting item");
|
||||
tx.execute(
|
||||
|
@ -446,7 +494,7 @@ mod db_impl {
|
|||
"SELECT p.name, ip.value
|
||||
FROM item_properties ip
|
||||
JOIN properties p ON ip.property_id = p.id
|
||||
WHERE ip.item_id = ?"
|
||||
WHERE ip.item_id = ?",
|
||||
)?;
|
||||
|
||||
let mapped_rows = stmt.query_map([&item.id], |row| {
|
||||
|
@ -455,13 +503,18 @@ mod db_impl {
|
|||
|
||||
mapped_rows.collect::<Result<HashMap<String, String>, _>>()?
|
||||
};
|
||||
|
||||
|
||||
for (prop, value) in &item.custom_properties {
|
||||
// Update existing or insert new
|
||||
let prop_id = self.get_or_create_property(&mut tx, prop).await?;
|
||||
if let Some(existing_value) = existing_props.get(prop) {
|
||||
if existing_value != value {
|
||||
log!("[DB] Updating property {} from '{}' to '{}'", prop, existing_value, value);
|
||||
log!(
|
||||
"[DB] Updating property {} from '{}' to '{}'",
|
||||
prop,
|
||||
existing_value,
|
||||
value
|
||||
);
|
||||
tx.execute(
|
||||
"UPDATE item_properties
|
||||
SET value = ?
|
||||
|
@ -481,7 +534,8 @@ mod db_impl {
|
|||
}
|
||||
|
||||
// Remove deleted properties
|
||||
let current_props: HashSet<&str> = item.custom_properties.keys().map(|s| s.as_str()).collect();
|
||||
let current_props: HashSet<&str> =
|
||||
item.custom_properties.keys().map(|s| s.as_str()).collect();
|
||||
for (existing_prop, _) in existing_props {
|
||||
if !current_props.contains(existing_prop.as_str()) {
|
||||
log!("[DB] Removing deleted property {}", existing_prop);
|
||||
|
@ -504,18 +558,15 @@ mod db_impl {
|
|||
let tx = conn.transaction()?;
|
||||
|
||||
// Get URL ID
|
||||
let url_id: i64 = tx.query_row(
|
||||
"SELECT id FROM urls WHERE url = ?",
|
||||
[url],
|
||||
|row| row.get(0)
|
||||
)?;
|
||||
|
||||
let url_id: i64 =
|
||||
tx.query_row("SELECT id FROM urls WHERE url = ?", [url], |row| row.get(0))?;
|
||||
|
||||
// Delete item and properties
|
||||
tx.execute(
|
||||
"DELETE FROM items WHERE id = ? AND url_id = ?",
|
||||
[item_id, &url_id.to_string()],
|
||||
)?;
|
||||
|
||||
|
||||
tx.commit()?;
|
||||
Ok(())
|
||||
}
|
||||
|
@ -524,14 +575,11 @@ mod db_impl {
|
|||
pub async fn delete_property_by_url(&self, url: &str, property: &str) -> Result<(), Error> {
|
||||
let mut conn = self.conn.lock().await;
|
||||
let tx = conn.transaction()?;
|
||||
|
||||
|
||||
// Get URL ID
|
||||
let url_id: i64 = tx.query_row(
|
||||
"SELECT id FROM urls WHERE url = ?",
|
||||
[url],
|
||||
|row| row.get(0)
|
||||
)?;
|
||||
|
||||
let url_id: i64 =
|
||||
tx.query_row("SELECT id FROM urls WHERE url = ?", [url], |row| row.get(0))?;
|
||||
|
||||
// Delete property from all items in this URL
|
||||
tx.execute(
|
||||
"DELETE FROM item_properties
|
||||
|
@ -543,7 +591,7 @@ mod db_impl {
|
|||
)",
|
||||
[property, &url_id.to_string()],
|
||||
)?;
|
||||
|
||||
|
||||
tx.commit()?;
|
||||
Ok(())
|
||||
}
|
||||
|
@ -551,25 +599,20 @@ mod db_impl {
|
|||
pub async fn add_selected_property(&self, url: &str, property: &str) -> Result<(), Error> {
|
||||
let mut conn = self.conn.lock().await;
|
||||
let tx = conn.transaction()?;
|
||||
|
||||
|
||||
// Insert URL if it does not exists
|
||||
tx.execute(
|
||||
"INSERT OR IGNORE INTO urls (url) VALUES (?)",
|
||||
[url]
|
||||
)?;
|
||||
|
||||
tx.execute("INSERT OR IGNORE INTO urls (url) VALUES (?)", [url])?;
|
||||
|
||||
// Get URL ID
|
||||
let url_id = tx.query_row(
|
||||
"SELECT id FROM urls WHERE url = ?",
|
||||
[url],
|
||||
|row| row.get::<_, i64>(0)
|
||||
)?;
|
||||
|
||||
let url_id = tx.query_row("SELECT id FROM urls WHERE url = ?", [url], |row| {
|
||||
row.get::<_, i64>(0)
|
||||
})?;
|
||||
|
||||
// Get/Create property
|
||||
let prop_id = match tx.query_row(
|
||||
"SELECT id FROM properties WHERE name = ?",
|
||||
[property],
|
||||
|row| row.get::<_, i64>(0)
|
||||
|row| row.get::<_, i64>(0),
|
||||
) {
|
||||
Ok(id) => id,
|
||||
Err(_) => {
|
||||
|
@ -577,13 +620,13 @@ mod db_impl {
|
|||
tx.last_insert_rowid()
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
// Insert into selected_properties
|
||||
tx.execute(
|
||||
"INSERT OR IGNORE INTO selected_properties (url_id, property_id) VALUES (?, ?)",
|
||||
[url_id, prop_id]
|
||||
[url_id, prop_id],
|
||||
)?;
|
||||
|
||||
|
||||
tx.commit()?;
|
||||
Ok(())
|
||||
}
|
||||
|
@ -595,34 +638,42 @@ mod db_impl {
|
|||
FROM selected_properties sp
|
||||
JOIN properties p ON sp.property_id = p.id
|
||||
JOIN urls u ON sp.url_id = u.id
|
||||
WHERE u.url = ?"
|
||||
WHERE u.url = ?",
|
||||
)?;
|
||||
|
||||
|
||||
let properties = stmt.query_map([url], |row| row.get(0))?;
|
||||
properties.collect()
|
||||
}
|
||||
|
||||
|
||||
// function to log database state
|
||||
pub async fn debug_dump(&self) -> Result<(), Error> {
|
||||
let conn = self.conn.lock().await;
|
||||
log!("[DATABASE DEBUG] URLs:");
|
||||
let mut stmt = conn.prepare("SELECT id, url FROM urls")?;
|
||||
let urls = stmt.query_map([], |row| {
|
||||
Ok(format!("ID: {}, URL: {}", row.get::<_, i64>(0)?, row.get::<_, String>(1)?))
|
||||
Ok(format!(
|
||||
"ID: {}, URL: {}",
|
||||
row.get::<_, i64>(0)?,
|
||||
row.get::<_, String>(1)?
|
||||
))
|
||||
})?;
|
||||
for url in urls {
|
||||
log!("[DATABASE DEBUG] {}", url?);
|
||||
}
|
||||
|
||||
|
||||
log!("[DATABASE DEBUG] Items:");
|
||||
let mut stmt = conn.prepare("SELECT id, name FROM items")?;
|
||||
let items = stmt.query_map([], |row| {
|
||||
Ok(format!("ID: {}, Name: '{}'", row.get::<_, String>(0)?, row.get::<_, String>(1)?))
|
||||
Ok(format!(
|
||||
"ID: {}, Name: '{}'",
|
||||
row.get::<_, String>(0)?,
|
||||
row.get::<_, String>(1)?
|
||||
))
|
||||
})?;
|
||||
for item in items {
|
||||
log!("[DATABASE DEBUG] {}", item?);
|
||||
}
|
||||
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
@ -638,4 +689,4 @@ mod db_impl {
|
|||
}
|
||||
|
||||
#[cfg(feature = "ssr")]
|
||||
pub use db_impl::{Database, DbItem};
|
||||
pub use db_impl::{Database, DbItem};
|
||||
|
|
Loading…
Add table
Reference in a new issue