Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feat/meta db filter #46

Merged
merged 2 commits into from
Sep 27, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
136 changes: 95 additions & 41 deletions moss/src/db/meta/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,14 +4,77 @@

use std::path::Path;

use sqlx::SqliteConnection;
use sqlx::{sqlite::SqliteConnectOptions, Acquire, Pool, Sqlite};
use sqlx::{QueryBuilder, SqliteConnection};
use thiserror::Error;

use crate::db::Encoding;
use crate::package::{self, Meta};
use crate::Provider;

#[derive(Debug, Clone, Copy)]
enum Table {
Meta,
Licenses,
Dependencies,
Providers,
}

#[derive(Debug)]
pub enum Filter {
Provider(Provider),
Name(package::Name),
}

impl Filter {
fn append(&self, table: Table, query: &mut QueryBuilder<Sqlite>) {
match self {
Filter::Provider(p) => {
if let Table::Providers = table {
query
.push(
"
where provider =
",
)
.push_bind(p.encode());
} else {
query
.push(
"
where package in
(select distinct package from meta_providers where provider =
",
)
.push_bind(p.encode())
.push(")");
}
}
Filter::Name(n) => {
if let Table::Meta = table {
query
.push(
"
where name =
",
)
.push_bind(n.encode().to_string());
} else {
query
.push(
"
where package in
(select distinct package from meta where name =
",
)
.push_bind(n.encode().to_string())
.push(")");
}
}
}
}
}

#[derive(Debug, Clone)]
pub struct Database {
pool: Pool<Sqlite>,
Expand Down Expand Up @@ -42,9 +105,8 @@ impl Database {
Ok(())
}

// TODO: Replace with specialized query interfaces
pub async fn all(&self) -> Result<Vec<(package::Id, Meta)>, Error> {
let entry_query = sqlx::query_as::<_, encoding::Entry>(
pub async fn query(&self, filter: Option<Filter>) -> Result<Vec<(package::Id, Meta)>, Error> {
let mut entry_query = sqlx::QueryBuilder::new(
"
SELECT package,
name,
Expand All @@ -59,36 +121,51 @@ impl Database {
uri,
hash,
download_size
FROM meta;
FROM meta
",
);

let licenses_query = sqlx::query_as::<_, encoding::License>(
let mut licenses_query = sqlx::QueryBuilder::new(
"
SELECT package, license
FROM meta_licenses;
FROM meta_licenses
",
);

let dependencies_query = sqlx::query_as::<_, encoding::Dependency>(
let mut dependencies_query = sqlx::QueryBuilder::new(
"
SELECT package, dependency
FROM meta_dependencies;
FROM meta_dependencies
",
);

let providers_query = sqlx::query_as::<_, encoding::Provider>(
let mut providers_query = sqlx::QueryBuilder::new(
"
SELECT package, provider
FROM meta_providers;
FROM meta_providers
",
);

if let Some(filter) = filter {
filter.append(Table::Meta, &mut entry_query);
filter.append(Table::Licenses, &mut licenses_query);
filter.append(Table::Dependencies, &mut dependencies_query);
filter.append(Table::Providers, &mut providers_query);
}

let (entries, licenses, dependencies, providers) = futures::try_join!(
entry_query.fetch_all(&self.pool),
licenses_query.fetch_all(&self.pool),
dependencies_query.fetch_all(&self.pool),
providers_query.fetch_all(&self.pool),
entry_query
.build_query_as::<encoding::Entry>()
.fetch_all(&self.pool),
licenses_query
.build_query_as::<encoding::License>()
.fetch_all(&self.pool),
dependencies_query
.build_query_as::<encoding::Dependency>()
.fetch_all(&self.pool),
providers_query
.build_query_as::<encoding::Provider>()
.fetch_all(&self.pool),
)?;

Ok(entries
Expand Down Expand Up @@ -130,29 +207,6 @@ impl Database {
.collect())
}

/// Firstly find all matching providers - then map them back via .get() to the full package
pub async fn get_providers(
&self,
provider: &Provider,
) -> Result<Vec<(package::Id, Meta)>, Error> {
let entry_query = sqlx::query_as::<_, encoding::Provider>(
"SELECT package, provider
FROM meta_providers
WHERE provider = ?;
",
)
.bind(provider.encode());

let entries = entry_query.fetch_all(&self.pool).await?;
let mut results = vec![];
for entry in entries {
let id = entry.id.0;
let lookup = self.get(&id).await?;
results.push((id, lookup));
}
Ok(results)
}

pub async fn get(&self, package: &package::Id) -> Result<Meta, Error> {
let entry_query = sqlx::query_as::<_, encoding::Entry>(
"
Expand Down Expand Up @@ -493,11 +547,11 @@ mod test {
assert_eq!(&meta.name, &"bash-completion".to_string().into());

// Now retrieve by provider.
let lookup = Provider {
let lookup = Filter::Provider(Provider {
kind: Kind::PackageName,
name: "bash-completion".to_string(),
};
let fetched = database.get_providers(&lookup).await.unwrap();
});
let fetched = database.query(Some(lookup)).await.unwrap();
assert_eq!(fetched.len(), 1);

batch_remove([&id], &mut database.pool.acquire().await.unwrap())
Expand Down
32 changes: 8 additions & 24 deletions moss/src/registry/plugin/repository.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ use log::warn;

use crate::{
db,
package::{self, Meta, Package},
package::{self, Package},
repository, Provider,
};

Expand Down Expand Up @@ -41,10 +41,10 @@ impl Repository {
}
}

async fn query(&self, flags: package::Flags, filter: impl Fn(&Meta) -> bool) -> Vec<Package> {
async fn query(&self, flags: package::Flags, filter: Option<db::meta::Filter>) -> Vec<Package> {
if flags.contains(package::Flags::AVAILABLE) {
// TODO: Error handling
let packages = match self.active.db.all().await {
let packages = match self.active.db.query(filter).await {
Ok(packages) => packages,
Err(error) => {
warn!("failed to query repository packages: {error}");
Expand All @@ -54,7 +54,6 @@ impl Repository {

packages
.into_iter()
.filter(|(_, meta)| filter(meta))
.map(|(id, meta)| Package {
id,
meta,
Expand All @@ -67,37 +66,22 @@ impl Repository {
}

pub async fn list(&self, flags: package::Flags) -> Vec<Package> {
self.query(flags, |_| true).await
self.query(flags, None).await
}

/// Query all packages that match the given provider identity
pub async fn query_provider(&self, provider: &Provider, flags: package::Flags) -> Vec<Package> {
if !flags.contains(package::Flags::AVAILABLE) {
return vec![];
}

let packages = self.active.db.get_providers(provider).await;
if packages.is_err() {
vec![]
} else {
packages
.unwrap()
.into_iter()
.map(|(id, meta)| Package {
id,
meta,
flags: package::Flags::AVAILABLE,
})
.collect()
}
self.query(flags, Some(db::meta::Filter::Provider(provider.clone())))
.await
}

pub async fn query_name(
&self,
package_name: &package::Name,
flags: package::Flags,
) -> Vec<Package> {
self.query(flags, |meta| meta.name == *package_name).await
self.query(flags, Some(db::meta::Filter::Name(package_name.clone())))
.await
}
}

Expand Down