Skip to content

Commit

Permalink
refactor: prepared queries
Browse files Browse the repository at this point in the history
  • Loading branch information
Fyko committed Sep 10, 2023
1 parent b939ce1 commit 4441673
Show file tree
Hide file tree
Showing 21 changed files with 1,468 additions and 2,269 deletions.
8 changes: 8 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 2 additions & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
[workspace]
members = ["example", "scyllax-macros"]
members = ["example", "scyllax-macros", "scyllax-macros-core"]
resolver = "2"

[workspace.package]
Expand Down Expand Up @@ -34,6 +34,7 @@ mac_address = "1"
once_cell = "1"
scylla = { workspace = true }
scyllax-macros = { version = "0.1.4-alpha", path = "./scyllax-macros" }
scyllax-macros-core = { version = "0.1.4-alpha", path = "./scyllax-macros-core" }
thiserror = "1"
tracing = { workspace = true }
uuid = { workspace = true }
Expand Down
3,217 changes: 1,077 additions & 2,140 deletions example/expanded.rs

Large diffs are not rendered by default.

3 changes: 2 additions & 1 deletion example/src/entities/person/model.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
use super::queries::PersonQueries;

Check failure on line 1 in example/src/entities/person/model.rs

View workflow job for this annotation

GitHub Actions / Check Suite

unresolved import `super::queries::PersonQueries`
use scyllax::prelude::*;

/// Represents data from a person
Expand All @@ -10,7 +11,7 @@ pub struct PersonData {

/// Represents a person in the database
#[entity]
#[upsert_query(table = "person", name = UpsertPerson)]
// #[upsert_query(table = "person", name = UpsertPerson, query_cache = PersonQueries)]
pub struct PersonEntity {
/// The id of the person
#[pk]
Expand Down
89 changes: 79 additions & 10 deletions example/src/entities/person/queries.rs
Original file line number Diff line number Diff line change
@@ -1,18 +1,20 @@
use scyllax::PreparedStatement;

Check warning on line 1 in example/src/entities/person/queries.rs

View workflow job for this annotation

GitHub Actions / Check Suite

unused import: `scyllax::PreparedStatement`
#[allow(non_snake_case)]
use scyllax::{delete_query, prelude::*};
use uuid::Uuid;

/// Load all queries for this entity
#[tracing::instrument(skip(db))]
pub async fn load(db: &mut Executor) -> anyhow::Result<()> {
let _ = GetPersonById::prepare(db).await;
let _ = GetPeopleByIds::prepare(db).await;
let _ = GetPersonByEmail::prepare(db).await;
let _ = DeletePersonById::prepare(db).await;

Ok(())
}
prepare_queries!(
PersonEntityQueries,
[
GetPersonById,
GetPeopleByIds,
GetPersonByEmail,
// DeletePersonById
]
);

/// Get a [`super::model::PersonEntity`] by its [`uuid::Uuid`]
// #[derive(scylla::ValueList, std::fmt::Debug, std::clone::Clone, PartialEq, Hash)]
#[select_query(
query = "select * from person where id = ? limit 1",
entity_type = "super::model::PersonEntity"
Expand All @@ -22,6 +24,59 @@ pub struct GetPersonById {
pub id: Uuid,
}

// impl scyllax::GenericQuery<PersonEntity> for GetPersonById {
// fn query() -> String {
// "select * from person where id = ? limit 1"
// .replace("*", &PersonEntity::keys().join(", "))
// }
// }

// #[scyllax::async_trait]
// impl scyllax::SelectQuery<PersonEntity, PersonEntity, PersonQueries> for GetPersonById {
// async fn execute(self, db: &scyllax::Executor<PersonQueries>) -> anyhow::Result<scylla::QueryResult, scylla::transport::errors::QueryError> {
// let statement = db.queries.get::<GetPersonById>();
// tracing::debug!{
// target = "GetPersonById",
// "executing select"
// };

// db.session.execute(statement, self).await
// }

// async fn parse_response(res: scylla::QueryResult) -> Result<GetPersonById, scyllax::ScyllaxError> {
// todo!()
// }
// }

// pub struct PersonQueries {
// GetPersonById: PreparedStatement,
// }

// #[scyllax::async_trait]
// #[doc = "A collection of prepared statements."]
// impl scyllax::Queries for PersonQueries {
// async fn new(session: &scylla::Session) -> Result<Self, scyllax::ScyllaxError> {
// Ok(Self {
// GetPersonById: session.prepare(GetPersonById::query()).await?,
// })
// }

// #[doc = "Get a prepared statement."]
// fn get<T>(&self) -> &scylla::statement::prepared_statement::PreparedStatement
// where
// Self: scyllax::GetPreparedStatement<T>,
// {
// <Self as scyllax::GetPreparedStatement<T>>::get_prepared_statement(self)
// }
// }

// impl scyllax::GetPreparedStatement<GetPersonById> for PersonQueries {
// #[doc = "Get a prepared statement."]
// fn get_prepared_statement(&self) -> &scylla::statement::prepared_statement::PreparedStatement {
// &self.GetPersonById
// }
// }

/// Get many [`super::model::PersonEntity`] by many [`uuid::Uuid`]
#[select_query(
query = "select * from person where id in ? limit ?",
Expand Down Expand Up @@ -54,6 +109,20 @@ pub struct DeletePersonById {
pub id: Uuid,
}

// fn test() {
// use scylla::batch::*;

// let batch = Batch::new_with_statements(
// BatchType::Logged,
// vec![
// BatchStatement::Query(GenericQuery::<GetPersonById>::query()),
// BatchStatement::Query(GenericQuery::<GetPeopleByIds>::query()),
// BatchStatement::Query(GenericQuery::<GetPersonByEmail>::query()),
// BatchStatement::Query(GenericQuery::<DeletePersonById>::query()),
// ]
// );
// }

#[cfg(test)]
mod test {
use super::*;
Expand Down
8 changes: 5 additions & 3 deletions example/src/main.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
//! Example
use entities::person::{
model::{PersonData, UpsertPerson},

Check failure on line 3 in example/src/main.rs

View workflow job for this annotation

GitHub Actions / Check Suite

unresolved import `entities::person::model::UpsertPerson`
queries::{load, DeletePersonById, GetPeopleByIds, GetPersonByEmail, GetPersonById},
queries::{
DeletePersonById, GetPeopleByIds, GetPersonByEmail, GetPersonById, PersonEntityQueries,
},
};
use scyllax::prelude::*;
use scyllax::{executor::create_session, util::v1_uuid};
Expand All @@ -22,9 +24,9 @@ async fn main() -> anyhow::Result<()> {
let default_keyspace = std::env::var("SCYLLA_DEFAULT_KEYSPACE").ok();

let session = create_session(known_nodes, default_keyspace).await?;
let mut executor = Executor::with_session(session);
let queries = PersonEntityQueries::new(&session).await?;

load(&mut executor).await?;
let mut executor = Executor::with_session(session, queries);

let query = GetPersonByEmail {
email: "foo1@scyllax.local".to_string(),
Expand Down
17 changes: 17 additions & 0 deletions scyllax-macros-core/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
[package]
name = "scyllax-macros-core"
description = "Core macros for scyllax"
version = { workspace = true }
license = { workspace = true }
edition = { workspace = true }
authors = { workspace = true }
repository = { workspace = true }
homepage = { workspace = true }
readme = "../README.md"

[dependencies]
anyhow = { workspace = true }
darling = { version = "0.20", features = ["suggestions"] }
proc-macro2 = "1"
quote = "1"
syn = { version = "2", features = ["full", "derive", "extra-traits"] }
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
//! Entity derive macro.
use crate::token_stream_with_error;
use proc_macro2::TokenStream;
use quote::{quote, ToTokens};
use syn::{Expr, Field, ItemStruct};

/// Attribute expand
/// Just adds the dervie macro to the struct.
pub(crate) fn expand(input: TokenStream) -> TokenStream {
/// Expand the `Entity` derive macro.
pub fn expand(input: TokenStream) -> TokenStream {
let input: ItemStruct = match syn::parse2(input.clone()) {
Ok(it) => it,
Err(e) => return token_stream_with_error(input, e),
Expand Down Expand Up @@ -55,7 +55,7 @@ fn entity_impl(input: &ItemStruct, pks: &[&Field]) -> TokenStream {
///
/// Rename is usually used to support camelCase keys, which need to be wrapped
/// in quotes or scylla will snake_ify it.
pub(crate) fn get_field_name(field: &Field) -> String {
pub fn get_field_name(field: &Field) -> String {
let rename = field.attrs.iter().find(|a| a.path().is_ident("rename"));
if let Some(rename) = rename {
let expr = rename.parse_args::<Expr>().expect("Expected an expression");
Expand All @@ -73,7 +73,8 @@ pub(crate) fn get_field_name(field: &Field) -> String {
.to_string()
}

pub(crate) fn expand_attr(_args: TokenStream, input: TokenStream) -> TokenStream {
/// Expand the `Entity` attr macro.
pub fn expand_attr(_args: TokenStream, input: TokenStream) -> TokenStream {
quote! {
#[derive(Clone, Debug, PartialEq, scyllax::FromRow, scyllax::prelude::ValueList, scyllax::Entity)]
#input
Expand Down
Original file line number Diff line number Diff line change
@@ -1,16 +1,19 @@
//! Macros for working with JSON data.
use crate::token_stream_with_error;
use proc_macro2::TokenStream;
use quote::quote;
use syn::ItemStruct;

pub(crate) fn expand_attr(_args: TokenStream, input: TokenStream) -> TokenStream {
/// Expand the `JsonData` attr macro.
pub fn expand_attr(_args: TokenStream, input: TokenStream) -> TokenStream {
quote! {
#[derive(Clone, Debug, PartialEq, serde::Serialize, serde::Deserialize, scyllax::prelude::JsonData)]
#input
}
}

pub(crate) fn expand(input: TokenStream) -> TokenStream {
/// Expand the `JsonData` derive macro.
pub fn expand(input: TokenStream) -> TokenStream {
let input: ItemStruct = match syn::parse2(input.clone()) {
Ok(it) => it,
Err(e) => return token_stream_with_error(input, e),
Expand Down
17 changes: 17 additions & 0 deletions scyllax-macros-core/src/lib.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
//! Scyllax macros. See the scyllax for more information.
use proc_macro2::TokenStream;

pub mod entity;
pub mod json;
pub mod queries;

/// Throw an error with the tokens for better error messages.
pub fn token_stream_with_error(mut tokens: TokenStream, error: syn::Error) -> TokenStream {
tokens.extend(error.into_compile_error());
tokens
}

/// Expand the `prepare_queries!` macro.
pub fn prepare_queries(input: TokenStream) -> TokenStream {
queries::prepare::expand(input)
}
Original file line number Diff line number Diff line change
@@ -1,23 +1,28 @@
//! Macros for delete queries.
use darling::{export::NestedMeta, FromMeta};
use proc_macro2::TokenStream;
use quote::quote;
use syn::ItemStruct;

use crate::token_stream_with_error;

/// Options for the `#[delete_query]` attribute macro.
#[derive(FromMeta)]
pub(crate) struct SelectQueryOptions {
query: String,
entity_type: syn::Type,
pub struct DeleteQueryOptions {
/// The query to execute.
pub query: String,
/// The type of the entity to return.
pub entity_type: syn::Type,
}

pub(crate) fn expand(args: TokenStream, item: TokenStream) -> TokenStream {
/// Expand the `#[delete_query]` attribute macro.
pub fn expand(args: TokenStream, item: TokenStream) -> TokenStream {
let attr_args = match NestedMeta::parse_meta_list(args.clone()) {
Ok(args) => args,
Err(e) => return darling::Error::from(e).write_errors(),
};

let args = match SelectQueryOptions::from_list(&attr_args) {
let args = match DeleteQueryOptions::from_list(&attr_args) {
Ok(o) => o,
Err(e) => return e.write_errors(),
};
Expand All @@ -30,28 +35,21 @@ pub(crate) fn expand(args: TokenStream, item: TokenStream) -> TokenStream {
Err(e) => return token_stream_with_error(item, e),
};
let struct_ident = &input.ident;
let query_cache = concat!(stringify!(#inner_entity_type), "Queries");

quote! {
#[derive(scylla::ValueList, std::fmt::Debug, std::clone::Clone, PartialEq, Hash)]
#input

#[scyllax::async_trait]
impl scyllax::DeleteQuery<#entity_type> for #struct_ident {
impl scyllax::GenericQuery<#entity_type> for #struct_ident {
fn query() -> String {
#query.to_string()
}
}

async fn prepare(db: &Executor) -> Result<scylla::prepared_statement::PreparedStatement, scylla::transport::errors::QueryError> {
let query = Self::query();
tracing::debug!{
target = stringify!(#struct_ident),
query,
"preparing query"
};
db.session.add_prepared_statement(&scylla::query::Query::new(query)).await
}

async fn execute(self, db: &scyllax::Executor) -> anyhow::Result<scylla::QueryResult, scylla::transport::errors::QueryError> {
#[scyllax::async_trait]
impl scyllax::DeleteQuery<#entity_type, #query_cache> for #struct_ident {
async fn execute(self, db: &scyllax::Executor<#query_cache>) -> anyhow::Result<scylla::QueryResult, scylla::transport::errors::QueryError> {
let query = Self::query();
tracing::debug! {
query,
Expand Down
5 changes: 5 additions & 0 deletions scyllax-macros-core/src/queries/mod.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
//! Mostly `expand` functions for the macros.
pub mod delete;
pub mod prepare;
pub mod select;
pub mod upsert;
Loading

0 comments on commit 4441673

Please sign in to comment.