implement image persistence db features, minor corrections

This commit is contained in:
yggverse 2026-01-09 18:27:27 +02:00
parent b5dd30dafb
commit 221b43e4cf
2 changed files with 110 additions and 7 deletions

View file

@ -1,5 +1,5 @@
-- MySQL Script generated by MySQL Workbench -- MySQL Script generated by MySQL Workbench
-- Thu 08 Jan 2026 12:40:45 AM EET -- пт, 09-січ-2026 17:57:03 +0200
-- Model: New Model Version: 1.0 -- Model: New Model Version: 1.0
-- MySQL Workbench Forward Engineering -- MySQL Workbench Forward Engineering
@ -65,7 +65,7 @@ ENGINE = InnoDB;
-- Table `rssto`.`content` -- Table `rssto`.`content`
-- ----------------------------------------------------- -- -----------------------------------------------------
CREATE TABLE IF NOT EXISTS `rssto`.`content` ( CREATE TABLE IF NOT EXISTS `rssto`.`content` (
`content_id` BIGINT NOT NULL AUTO_INCREMENT, `content_id` BIGINT UNSIGNED NOT NULL AUTO_INCREMENT,
`channel_item_id` INT NOT NULL, `channel_item_id` INT NOT NULL,
`provider_id` INT NULL, `provider_id` INT NULL,
`title` VARCHAR(255) NOT NULL, `title` VARCHAR(255) NOT NULL,
@ -87,6 +87,41 @@ CREATE TABLE IF NOT EXISTS `rssto`.`content` (
ENGINE = InnoDB; ENGINE = InnoDB;
-- -----------------------------------------------------
-- Table `rssto`.`image`
-- -----------------------------------------------------
CREATE TABLE IF NOT EXISTS `rssto`.`image` (
`image_id` BIGINT UNSIGNED NOT NULL AUTO_INCREMENT,
`source` VARCHAR(2048) NOT NULL,
`data` MEDIUMBLOB NOT NULL,
PRIMARY KEY (`image_id`),
UNIQUE INDEX `source_UNIQUE` (`source` ASC) VISIBLE)
ENGINE = InnoDB;
-- -----------------------------------------------------
-- Table `rssto`.`content_image`
-- -----------------------------------------------------
CREATE TABLE IF NOT EXISTS `rssto`.`content_image` (
`content_image_id` BIGINT UNSIGNED NOT NULL AUTO_INCREMENT,
`content_id` BIGINT UNSIGNED NOT NULL,
`image_id` BIGINT UNSIGNED NOT NULL,
PRIMARY KEY (`content_image_id`),
INDEX `fk_content_image_content_idx` (`content_id` ASC) VISIBLE,
INDEX `fk_content_image_image_idx` (`image_id` ASC) VISIBLE,
CONSTRAINT `fk_content_image_content`
FOREIGN KEY (`content_id`)
REFERENCES `rssto`.`content` (`content_id`)
ON DELETE NO ACTION
ON UPDATE NO ACTION,
CONSTRAINT `fk_content_image_image`
FOREIGN KEY (`image_id`)
REFERENCES `rssto`.`image` (`image_id`)
ON DELETE NO ACTION
ON UPDATE NO ACTION)
ENGINE = InnoDB;
SET SQL_MODE=@OLD_SQL_MODE; SET SQL_MODE=@OLD_SQL_MODE;
SET FOREIGN_KEY_CHECKS=@OLD_FOREIGN_KEY_CHECKS; SET FOREIGN_KEY_CHECKS=@OLD_FOREIGN_KEY_CHECKS;
SET UNIQUE_CHECKS=@OLD_UNIQUE_CHECKS; SET UNIQUE_CHECKS=@OLD_UNIQUE_CHECKS;

View file

@ -23,13 +23,12 @@ impl Mysql {
} }
pub fn channels_by_url(&self, url: &str, limit: Option<usize>) -> Result<Vec<Channel>, Error> { pub fn channels_by_url(&self, url: &str, limit: Option<usize>) -> Result<Vec<Channel>, Error> {
self.pool.get_conn()?.exec_map( self.pool.get_conn()?.exec(
format!( format!(
"SELECT `channel_id`, `url` FROM `channel` WHERE `url` = ? LIMIT {}", "SELECT `channel_id`, `url` FROM `channel` WHERE `url` = ? LIMIT {}",
limit.unwrap_or(DEFAULT_LIMIT) limit.unwrap_or(DEFAULT_LIMIT)
), ),
(url,), (url,),
|(channel_id, url)| Channel { channel_id, url },
) )
} }
@ -171,7 +170,8 @@ impl Mysql {
`channel_item_id`, `channel_item_id`,
`provider_id`, `provider_id`,
`title`, `title`,
`description` FROM `content` WHERE `channel_item_id` = ? AND `provider_id` <=> ? LIMIT {}", `description` FROM `content`
WHERE `channel_item_id` = ? AND `provider_id` <=> ? LIMIT {}",
limit.unwrap_or(DEFAULT_LIMIT) limit.unwrap_or(DEFAULT_LIMIT)
), ),
(channel_item_id, provider_id), (channel_item_id, provider_id),
@ -187,8 +187,58 @@ impl Mysql {
) -> Result<u64, Error> { ) -> Result<u64, Error> {
let mut c = self.pool.get_conn()?; let mut c = self.pool.get_conn()?;
c.exec_drop( c.exec_drop(
"INSERT INTO `content` SET `channel_item_id` = ?, `provider_id` = ?, `title` = ?, `description` = ?", "INSERT INTO `content` SET `channel_item_id` = ?,
(channel_item_id, provider_id, title, description ), `provider_id` = ?,
`title` = ?,
`description` = ?",
(channel_item_id, provider_id, title, description),
)?;
Ok(c.last_insert_id())
}
pub fn content_image(&self, content_image_id: u64) -> Result<Option<ContentImage>, Error> {
self.pool.get_conn()?.exec_first(
"SELECT `content_image_id`,
`content_id`,
`image_id`,
`data`,
`source` FROM `content_image`
JOIN `image` ON (`image`.`image_id` = `content_image`.`image_id`)
WHERE `content_image_id` = ? LIMIT 1",
(content_image_id,),
)
}
pub fn insert_content_image(&self, content_id: u64, image_id: u64) -> Result<u64, Error> {
let mut c = self.pool.get_conn()?;
c.exec_drop(
"INSERT INTO `content_image` SET `content_id` = ?, `image_id` = ?",
(content_id, image_id),
)?;
Ok(c.last_insert_id())
}
pub fn image_by_source(&self, source: &str) -> Result<Option<Image>, Error> {
self.pool.get_conn()?.exec_first(
"SELECT `image_id`,
`source`,
`data` FROM `image` WHERE `source` = ? LIMIT 1",
(source,),
)
}
pub fn images(&self, limit: Option<usize>) -> Result<Vec<Image>, Error> {
self.pool.get_conn()?.query(format!(
"SELECT `image_id`, `source`, `data` FROM `image` LIMIT {}",
limit.unwrap_or(DEFAULT_LIMIT)
))
}
pub fn insert_image(&self, source: &str, data: &[u8]) -> Result<u64, Error> {
let mut c = self.pool.get_conn()?;
c.exec_drop(
"INSERT INTO `image` SET `source` = ?, `data` = ?",
(source, data),
)?; )?;
Ok(c.last_insert_id()) Ok(c.last_insert_id())
} }
@ -243,6 +293,24 @@ pub struct Provider {
pub name: String, pub name: String,
} }
#[derive(Debug, PartialEq, Eq, FromRow)]
pub struct Image {
pub image_id: u64,
pub source: String,
pub data: Vec<u8>,
}
/// Includes joined `image` table members
#[derive(Debug, PartialEq, Eq, FromRow)]
pub struct ContentImage {
pub content_image_id: u64,
pub content_id: u64,
pub image_id: u64,
// Image members (JOIN)
pub data: Vec<u8>,
pub source: String,
}
pub enum Sort { pub enum Sort {
Asc, Asc,
Desc, Desc,