implement saving None values

This commit is contained in:
OMGeeky
2023-04-15 14:37:23 +02:00
parent 183a110acd
commit 9d6c719cbc
4 changed files with 155 additions and 107 deletions

View File

@@ -77,7 +77,7 @@ fn implement_get_all_params(ast: &DeriveInput, table_ident: &Ident) -> TokenStre
let field_ident = f.field_ident; let field_ident = f.field_ident;
let field_name = f.local_name; let field_name = f.local_name;
quote::quote! { quote::quote! {
#table_ident::get_parameter(&self.#field_ident, &#table_ident::get_field_param_name(&#field_name.to_string())?)? #table_ident::get_parameter(&self.#field_ident, &#table_ident::get_field_param_name(&#field_name.to_string())?)
} }
} }
let table_ident = &ast.ident; let table_ident = &ast.ident;
@@ -87,7 +87,7 @@ fn implement_get_all_params(ast: &DeriveInput, table_ident: &Ident) -> TokenStre
.map(|f| get_param_from_field(f, &table_ident)); .map(|f| get_param_from_field(f, &table_ident));
quote::quote! { quote::quote! {
fn get_all_params(&self) -> google_bigquery_v2::prelude::Result<Vec<google_bigquery_v2::data::QueryParameter>> { fn get_all_params(&self) -> google_bigquery_v2::prelude::Result<Vec<Option<google_bigquery_v2::data::QueryParameter>>> {
log::trace!("get_all_params() self:{:?}", self); log::trace!("get_all_params() self:{:?}", self);
Ok(vec![ Ok(vec![
#(#fields),* #(#fields),*
@@ -101,7 +101,7 @@ fn implement_get_parameter_from_field(ast: &DeriveInput, table_ident: &Ident) ->
let field_ident = f.field_ident; let field_ident = f.field_ident;
let field_name = f.local_name; let field_name = f.local_name;
quote::quote! { quote::quote! {
#field_name => #table_ident::get_parameter(&self.#field_ident, &#table_ident::get_field_param_name(&#field_name.to_string())?), #field_name => Ok(#table_ident::get_parameter(&self.#field_ident, &#table_ident::get_field_param_name(&#field_name.to_string())?)),
} }
} }
let table_ident = &ast.ident; let table_ident = &ast.ident;
@@ -111,7 +111,7 @@ fn implement_get_parameter_from_field(ast: &DeriveInput, table_ident: &Ident) ->
.map(|f| get_param_from_field(f, &table_ident)); .map(|f| get_param_from_field(f, &table_ident));
quote::quote! { quote::quote! {
fn get_parameter_from_field(&self, field_name: &str) -> google_bigquery_v2::prelude::Result<google_bigquery_v2::data::QueryParameter> { fn get_parameter_from_field(&self, field_name: &str) -> google_bigquery_v2::prelude::Result<Option<google_bigquery_v2::data::QueryParameter>> {
log::trace!("get_parameter_from_field(); field_name: '{}' self:{:?}", field_name, self); log::trace!("get_parameter_from_field(); field_name: '{}' self:{:?}", field_name, self);
match field_name { match field_name {
#(#fields)* #(#fields)*

View File

@@ -3,15 +3,15 @@ use std::fmt::{Debug, Display, Formatter};
use std::marker::PhantomData; use std::marker::PhantomData;
use async_trait::async_trait; use async_trait::async_trait;
pub use google_bigquery2::api::{QueryParameterType, QueryParameterValue};
pub use google_bigquery2::api::QueryParameter; pub use google_bigquery2::api::QueryParameter;
use google_bigquery2::api::QueryRequest; use google_bigquery2::api::QueryRequest;
pub use google_bigquery2::api::{QueryParameterType, QueryParameterValue};
use log::debug; use log::debug;
use log::trace; use log::trace;
use serde_json::Value; use serde_json::Value;
use crate::client::BigqueryClient; use crate::client::BigqueryClient;
use crate::data::param_conversion::{convert_value_to_string, BigDataValueType}; use crate::data::param_conversion::{BigDataValueType, convert_value_to_string};
use crate::data::query_builder::{ use crate::data::query_builder::{
NoClient, NoStartingData, QueryBuilder, QueryResultType, QueryTypeInsert, QueryTypeNoType, NoClient, NoStartingData, QueryBuilder, QueryResultType, QueryTypeInsert, QueryTypeNoType,
QueryTypeSelect, QueryTypeUpdate, QueryWasNotBuilt, QueryTypeSelect, QueryTypeUpdate, QueryWasNotBuilt,
@@ -20,8 +20,8 @@ use crate::prelude::*;
#[async_trait] #[async_trait]
pub trait BigQueryTableBase { pub trait BigQueryTableBase {
fn get_all_params(&self) -> Result<Vec<QueryParameter>>; fn get_all_params(&self) -> Result<Vec<Option<QueryParameter>>>;
fn get_parameter_from_field(&self, field_name: &str) -> Result<QueryParameter>; fn get_parameter_from_field(&self, field_name: &str) -> Result<Option<QueryParameter>>;
//region get infos //region get infos
/// Returns the name of the table in the database. /// Returns the name of the table in the database.
fn get_table_name() -> String; fn get_table_name() -> String;
@@ -53,12 +53,11 @@ pub trait BigQueryTableBase {
client: BigqueryClient, client: BigqueryClient,
row: &HashMap<String, Value>, row: &HashMap<String, Value>,
) -> Result<Self> ) -> Result<Self>
where where
Self: Sized; Self: Sized;
//region update //region update
//TODO: fn update(&mut self) -> Result<()>;
//TODO: fn delete(&mut self) -> Result<()>; //TODO: fn delete(&mut self) -> Result<()>;
//endregion //endregion
@@ -69,26 +68,26 @@ pub trait BigQueryTableBase {
#[async_trait] #[async_trait]
pub trait BigQueryTable: BigQueryTableBase { pub trait BigQueryTable: BigQueryTableBase {
fn select() -> QueryBuilder<Self, QueryTypeSelect, NoClient, QueryWasNotBuilt, NoStartingData> fn select() -> QueryBuilder<Self, QueryTypeSelect, NoClient, QueryWasNotBuilt, NoStartingData>
where where
Self: Sized, Self: Sized,
{ {
QueryBuilder::<Self, QueryTypeNoType, NoClient, QueryWasNotBuilt, NoStartingData>::select() QueryBuilder::<Self, QueryTypeNoType, NoClient, QueryWasNotBuilt, NoStartingData>::select()
} }
fn insert() -> QueryBuilder<Self, QueryTypeInsert, NoClient, QueryWasNotBuilt, NoStartingData> fn insert() -> QueryBuilder<Self, QueryTypeInsert, NoClient, QueryWasNotBuilt, NoStartingData>
where where
Self: Sized, Self: Sized,
{ {
QueryBuilder::<Self, QueryTypeNoType, NoClient, QueryWasNotBuilt, NoStartingData>::insert() QueryBuilder::<Self, QueryTypeNoType, NoClient, QueryWasNotBuilt, NoStartingData>::insert()
} }
fn update() -> QueryBuilder<Self, QueryTypeUpdate, NoClient, QueryWasNotBuilt, NoStartingData> fn update() -> QueryBuilder<Self, QueryTypeUpdate, NoClient, QueryWasNotBuilt, NoStartingData>
where where
Self: Sized, Self: Sized,
{ {
QueryBuilder::<Self, QueryTypeNoType, NoClient, QueryWasNotBuilt, NoStartingData>::update() QueryBuilder::<Self, QueryTypeNoType, NoClient, QueryWasNotBuilt, NoStartingData>::update()
} }
fn get_parameter<T>(value: &T, param_name: &String) -> Result<QueryParameter> fn get_parameter<T>(value: &T, param_name: &String) -> Option<QueryParameter>
where where
T: BigDataValueType + Debug, T: BigDataValueType + Debug,
{ {
trace!("get_parameter({:?}, {})", value, param_name); trace!("get_parameter({:?}, {})", value, param_name);
let value = value.to_param(); let value = value.to_param();
@@ -106,12 +105,7 @@ pub trait BigQueryTable: BigQueryTableBase {
value: Some(param_value), value: Some(param_value),
..Default::default() ..Default::default()
}), }),
Err(_) => todo!( Err(_) => return None,
"a parameter value probably of sort null is not yet \
implemented. Does this even make sense or should the code that's \
calling this react if there is an error returned from this function \
and modify the where to be 'is null' instead of '== @__PARAM_x'?"
),
}; };
debug!("param_value: {:?}", param_value); debug!("param_value: {:?}", param_value);
@@ -120,7 +114,7 @@ pub trait BigQueryTable: BigQueryTableBase {
parameter_value: param_value, parameter_value: param_value,
name: Some(param_name.clone()), name: Some(param_name.clone()),
}; };
Ok(param) Some(param)
} }
fn get_field_param_name(field_name: &str) -> Result<String> { fn get_field_param_name(field_name: &str) -> Result<String> {
trace!("get_field_param_name({})", field_name); trace!("get_field_param_name({})", field_name);
@@ -153,9 +147,9 @@ pub trait BigQueryTable: BigQueryTableBase {
} }
async fn get_by_pk<PK>(client: BigqueryClient, pk_value: &PK) -> Result<Self> async fn get_by_pk<PK>(client: BigqueryClient, pk_value: &PK) -> Result<Self>
where where
PK: BigDataValueType + Send + Sync + 'static, PK: BigDataValueType + Send + Sync + 'static,
Self: Sized + Debug, Self: Sized + Debug,
{ {
trace!("get_by_pk({:?}, {:?})", client, pk_value); trace!("get_by_pk({:?}, {:?})", client, pk_value);
let pk_field_name = Self::get_pk_field_name(); let pk_field_name = Self::get_pk_field_name();
@@ -173,7 +167,7 @@ pub trait BigQueryTable: BigQueryTableBase {
"something went wrong when getting for {} = {:?};\tresult: {:?}", "something went wrong when getting for {} = {:?};\tresult: {:?}",
pk_field_name, pk_value, success pk_field_name, pk_value, success
) )
.into()); .into());
} }
}; };
@@ -184,15 +178,15 @@ pub trait BigQueryTable: BigQueryTableBase {
"More than one entry found for {} = {:?}", "More than one entry found for {} = {:?}",
pk_db_name, pk_value pk_db_name, pk_value
) )
.into()) .into())
} else { } else {
Ok(rows.remove(0)) Ok(rows.remove(0))
} }
} }
async fn upsert(&mut self) -> Result<()> async fn upsert(&mut self) -> Result<()>
where where
Self: Sized + Clone + Send + Sync + Debug + Default, Self: Sized + Clone + Send + Sync + Debug + Default,
{ {
trace!("upsert()"); trace!("upsert()");
@@ -217,8 +211,8 @@ pub trait BigQueryTable: BigQueryTableBase {
/// proxy for update /// proxy for update
async fn save(&mut self) -> Result<()> async fn save(&mut self) -> Result<()>
where where
Self: Sized + Clone + Send + Sync + Debug + Default, Self: Sized + Clone + Send + Sync + Debug + Default,
{ {
trace!("save(): {:?}", self); trace!("save(): {:?}", self);
let result = Self::update() let result = Self::update()
@@ -238,7 +232,7 @@ pub trait BigQueryTable: BigQueryTableBase {
"save should return empty data, but returned {} rows.", "save should return empty data, but returned {} rows.",
count count
) )
.into()) .into())
} }
} }

View File

@@ -167,7 +167,7 @@ pub struct QueryBuilder<Table, QueryType, Client, QueryBuilt, StartingData> {
//region default implementation for QueryBuilder //region default implementation for QueryBuilder
impl<Table, QueryType, Client: Default, QueryBuilt, StartingData: Default> Default impl<Table, QueryType, Client: Default, QueryBuilt, StartingData: Default> Default
for QueryBuilder<Table, QueryType, Client, QueryBuilt, StartingData> for QueryBuilder<Table, QueryType, Client, QueryBuilt, StartingData>
{ {
fn default() -> Self { fn default() -> Self {
Self { Self {
@@ -189,7 +189,7 @@ impl<Table, QueryType, Client: Default, QueryBuilt, StartingData: Default> Defau
//region general QueryBuilder //region general QueryBuilder
//region functions for all queries //region functions for all queries
impl<Table: BigQueryTable, UnknownQueryType, Client, QueryBuilt, StartingData> impl<Table: BigQueryTable, UnknownQueryType, Client, QueryBuilt, StartingData>
QueryBuilder<Table, UnknownQueryType, Client, QueryBuilt, StartingData> QueryBuilder<Table, UnknownQueryType, Client, QueryBuilt, StartingData>
{ {
fn get_sorted_selected_fields(&self) -> Vec<(String, String)> { fn get_sorted_selected_fields(&self) -> Vec<(String, String)> {
trace!("get_sorted_selected_fields()"); trace!("get_sorted_selected_fields()");
@@ -214,22 +214,26 @@ impl<Table: BigQueryTable, UnknownQueryType, Client, QueryBuilt, StartingData>
//region functions for not built queries //region functions for not built queries
//region with Starting data //region with Starting data
impl<Table: BigQueryTable + Default, UnknownQueryType, Client> impl<Table: BigQueryTable + Default, UnknownQueryType, Client>
QueryBuilder<Table, UnknownQueryType, Client, QueryWasNotBuilt, HasStartingData<Table>> QueryBuilder<Table, UnknownQueryType, Client, QueryWasNotBuilt, HasStartingData<Table>>
{ {
pub fn add_field_where(self, field: &str) -> Result<Self> { pub fn add_field_where(self, field: &str) -> Result<Self> {
trace!("add_field_where(field: {})", field); trace!("add_field_where(field: {})", field);
let field_db_name = Table::get_field_db_name(field)?; let field_db_name = Table::get_field_db_name(field)?;
let param = Table::get_parameter_from_field(&self.starting_data.0, &field)?; let param = Table::get_parameter_from_field(&self.starting_data.0, &field)?;
let has_param_value = param.parameter_value.is_some();
let mut params = self.params; let mut params = self.params;
let mut wheres = self.where_clauses; let mut wheres = self.where_clauses;
if has_param_value { let mut has_param_value = false;
let param_name = param.name.as_ref().unwrap().to_string(); if let Some(param) = param {
params.push(param); if param.parameter_value.is_some() {
wheres.push(format!("{} = @{}", field_db_name, param_name)); has_param_value = true;
} else { let param_name = param.name.as_ref().unwrap().to_string();
params.push(param);
wheres.push(format!("{} = @{}", field_db_name, param_name));
}
}
if !has_param_value {
wheres.push(format!("{} is NULL", field_db_name)); wheres.push(format!("{} is NULL", field_db_name));
} }
Ok(Self { Ok(Self {
@@ -238,16 +242,38 @@ impl<Table: BigQueryTable + Default, UnknownQueryType, Client>
..self ..self
}) })
} }
fn add_params_for_table_query_fields(&mut self) -> Result<()> {
trace!("add_params_for_table_query_fields()");
let local_fields = Table::get_query_fields(true);
let starting_data = &self.starting_data.0;
for (local_field_name, _) in local_fields {
let para = Table::get_parameter_from_field(starting_data, &local_field_name)?;
if let Some(para) = para {
let mut has_param = false;
for existing_para in &self.params {
if existing_para.name == para.name {
has_param = true;
break;
}
}
if !has_param {
self.params.push(para);
}
}
}
Ok(())
}
} }
//endregion //endregion
impl<Table: BigQueryTable + Debug, UnknownQueryType: Debug, Client: Debug, StartingData: Debug> impl<Table: BigQueryTable + Debug, UnknownQueryType: Debug, Client: Debug, StartingData: Debug>
QueryBuilder<Table, UnknownQueryType, Client, QueryWasNotBuilt, StartingData> QueryBuilder<Table, UnknownQueryType, Client, QueryWasNotBuilt, StartingData>
{ {
//region set query content //region set query content
pub fn add_where_eq<T>(self, column: &str, value: Option<&T>) -> Result<Self> pub fn add_where_eq<T>(self, column: &str, value: Option<&T>) -> Result<Self>
where where
T: BigDataValueType + Debug, T: BigDataValueType + Debug,
{ {
trace!("add_where_eq({:?}, {:?})", column, value); trace!("add_where_eq({:?}, {:?})", column, value);
let column = Table::get_field_db_name(column)?; let column = Table::get_field_db_name(column)?;
@@ -255,19 +281,19 @@ impl<Table: BigQueryTable + Debug, UnknownQueryType: Debug, Client: Debug, Start
if let Some(value) = value { if let Some(value) = value {
let param_name = format!("__PARAM_{}", self.params.len()); let param_name = format!("__PARAM_{}", self.params.len());
let param = Table::get_parameter(value, &param_name);
if let Some(param) = param {
let mut required_params = self.params;
required_params.push(param);
let param = Table::get_parameter(value, &param_name)?; wheres.push(format!("{} = @{}", column, param_name));
let mut required_params = self.params; return Ok(Self {
required_params.push(param); where_clauses: wheres,
params: required_params,
wheres.push(format!("{} = @{}", column, param_name)); ..self
});
return Ok(Self { }
where_clauses: wheres,
params: required_params,
..self
});
} }
wheres.push(format!("{} is NULL", column)); wheres.push(format!("{} is NULL", column));
@@ -327,7 +353,7 @@ impl<Table: BigQueryTable + Debug, UnknownQueryType: Debug, Client: Debug, Start
//endregion //endregion
//region set_data //region set_data
impl<Table: BigQueryTable + Default + Debug, QueryType: HasQueryType, Client: Default> impl<Table: BigQueryTable + Default + Debug, QueryType: HasQueryType, Client: Default>
QueryBuilder<Table, QueryType, Client, QueryWasNotBuilt, NoStartingData> QueryBuilder<Table, QueryType, Client, QueryWasNotBuilt, NoStartingData>
{ {
pub fn set_data( pub fn set_data(
self, self,
@@ -352,7 +378,7 @@ impl<Table: BigQueryTable + Default + Debug, QueryType: HasQueryType, Client: De
//endregion //endregion
//region QueryTypeNoType //region QueryTypeNoType
impl<Table: BigQueryTable, Client: Default, StartingData: Default> impl<Table: BigQueryTable, Client: Default, StartingData: Default>
QueryBuilder<Table, QueryTypeNoType, Client, QueryWasNotBuilt, StartingData> QueryBuilder<Table, QueryTypeNoType, Client, QueryWasNotBuilt, StartingData>
{ {
pub fn select() -> QueryBuilder<Table, QueryTypeSelect, NoClient, QueryWasNotBuilt, StartingData> pub fn select() -> QueryBuilder<Table, QueryTypeSelect, NoClient, QueryWasNotBuilt, StartingData>
{ {
@@ -383,26 +409,20 @@ impl<Table: BigQueryTable, Client: Default, StartingData: Default>
//endregion //endregion
//region QueryTypeInsert //region QueryTypeInsert
impl<Table: BigQueryTable + Default + Debug> impl<Table: BigQueryTable + Default + Debug>
QueryBuilder<Table, QueryTypeInsert, HasClient, QueryWasNotBuilt, HasStartingData<Table>> QueryBuilder<Table, QueryTypeInsert, HasClient, QueryWasNotBuilt, HasStartingData<Table>>
{ {
pub fn build_query( pub fn build_query(
self, mut self,
) -> Result< ) -> Result<
QueryBuilder<Table, QueryTypeInsert, HasClient, QueryWasBuilt, HasStartingData<Table>>, QueryBuilder<Table, QueryTypeInsert, HasClient, QueryWasBuilt, HasStartingData<Table>>,
> { > {
trace!("build_query: insert: {:?}", self); trace!("build_query: insert: {:?}", self);
let table_identifier = Table::get_table_identifier_from_client(&self.client.0); let table_identifier = Table::get_table_identifier_from_client(&self.client.0);
let fields = self.get_fields_string();
let values = self.get_values_params_string()?;
let params = &self.params; let params = &self.params;
log::warn!("params are not used in insert query: {:?}", params); log::warn!("params are not used in insert query: {:?}", params);
let mut params = vec![]; self.add_params_for_table_query_fields()?;
let local_fields = Table::get_query_fields(true); let fields = self.get_fields_string();
let starting_data = &self.starting_data.0; let values = self.get_values_params_string()?;
for (local_field_name, _) in local_fields {
let para = Table::get_parameter_from_field(starting_data, &local_field_name)?;
params.push(para);
}
let query = format!( let query = format!(
"insert into {} ({}) values({})", "insert into {} ({}) values({})",
@@ -410,7 +430,7 @@ impl<Table: BigQueryTable + Default + Debug>
); );
Ok(QueryBuilder { Ok(QueryBuilder {
query, query,
params, params: self.params,
where_clauses: self.where_clauses, where_clauses: self.where_clauses,
order_by: self.order_by, order_by: self.order_by,
limit: self.limit, limit: self.limit,
@@ -423,20 +443,40 @@ impl<Table: BigQueryTable + Default + Debug>
} }
fn get_values_params_string(&self) -> Result<String> { fn get_values_params_string(&self) -> Result<String> {
let values = self.get_value_parameter_names()?; trace!("get_values_params_string\tself: {:?}", self);
let values: Vec<Option<String>> = self.get_value_parameter_names()?;
Ok(values Ok(values
.iter() .iter()
.map(|v| format!("@{}", v)) .map(|v| match v {
Some(v) => format!("@{}", v),
None => String::from("NULL"),
})
.collect::<Vec<String>>() .collect::<Vec<String>>()
.join(", ")) .join(", "))
} }
/// Returns a vector of parameter names for the values in the insert query.
fn get_value_parameter_names(&self) -> Result<Vec<String>> { ///
/// If the parameter for a field does not exists, it will just be NULL in
/// the query, not a parameter.
fn get_value_parameter_names(&self) -> Result<Vec<Option<String>>> {
trace!("get_value_parameter_names\tself: {:?}", self);
let mut values = self.get_sorted_selected_fields(); let mut values = self.get_sorted_selected_fields();
let existing_params: Vec<String> = self.params.iter().map(|p| p.name.clone().unwrap()).collect();
debug!("existing_params: len: {} params: {:?}", existing_params.len(), existing_params);
debug!("selected_fields: len: {} fields: {:?}", values.len(), values);
let res = values let res = values
.iter_mut() .iter_mut()
.map(|(field, _)| Table::get_field_param_name(field)) .map(|(field, _)| match Table::get_field_param_name(field) {
.collect::<Result<Vec<String>>>()?; Ok(param_name) => {
if existing_params.contains(&param_name) {
Ok(Some(param_name))
} else {
Ok(None)
}
}
Err(e) => Err(e),
})
.collect::<Result<Vec<Option<String>>>>()?;
Ok(res) Ok(res)
} }
} }
@@ -444,7 +484,7 @@ impl<Table: BigQueryTable + Default + Debug>
//endregion //endregion
//region QueryTypeUpdate //region QueryTypeUpdate
impl<Table: BigQueryTable + Default + Debug> impl<Table: BigQueryTable + Default + Debug>
QueryBuilder<Table, QueryTypeUpdate, HasClient, QueryWasNotBuilt, HasStartingData<Table>> QueryBuilder<Table, QueryTypeUpdate, HasClient, QueryWasNotBuilt, HasStartingData<Table>>
{ {
pub fn build_query( pub fn build_query(
mut self, mut self,
@@ -453,7 +493,6 @@ impl<Table: BigQueryTable + Default + Debug>
> { > {
trace!("build_query: update: {:?}", self); trace!("build_query: update: {:?}", self);
let table_identifier = Table::get_table_identifier_from_client(&self.client.0); let table_identifier = Table::get_table_identifier_from_client(&self.client.0);
let fields_str = self.build_update_fields_string()?;
if self.where_clauses.is_empty() { if self.where_clauses.is_empty() {
trace!("no where clause, adding pk field to where clause"); trace!("no where clause, adding pk field to where clause");
self = self.add_field_where(&Table::get_pk_field_name())?; self = self.add_field_where(&Table::get_pk_field_name())?;
@@ -461,13 +500,8 @@ impl<Table: BigQueryTable + Default + Debug>
let where_clause = self.build_where_string(); let where_clause = self.build_where_string();
let params = &self.params; let params = &self.params;
log::warn!("params are not used in update query: {:?}", params); log::warn!("params are not used in update query: {:?}", params);
let mut params = vec![]; self.add_params_for_table_query_fields()?;
let local_fields = Table::get_query_fields(true); let fields_str = self.build_update_fields_string()?;
let starting_data = &self.starting_data.0;
for (local_field_name, _) in local_fields {
let para = Table::get_parameter_from_field(starting_data, &local_field_name)?;
params.push(para);
}
let query = format!( let query = format!(
"update {} set {} {}", "update {} set {} {}",
@@ -475,7 +509,7 @@ impl<Table: BigQueryTable + Default + Debug>
); );
Ok(QueryBuilder { Ok(QueryBuilder {
query, query,
params, params: self.params,
where_clauses: self.where_clauses, where_clauses: self.where_clauses,
order_by: self.order_by, order_by: self.order_by,
limit: self.limit, limit: self.limit,
@@ -492,20 +526,28 @@ impl<Table: BigQueryTable + Default + Debug>
let result = self let result = self
.get_value_parameter_names()? .get_value_parameter_names()?
.into_iter() .into_iter()
.map(|(f, p)| format!("{} = @{}", f, p).to_string()) .map(|(f, p)| match p {
Some(p) => format!("{} = @{}", f, p),
None => format!("{} = NULL", f),
}
)
.collect::<Vec<String>>() .collect::<Vec<String>>()
.join(", "); .join(", ");
trace!("build_update_fields_string: result: {}", result); trace!("build_update_fields_string: result: {}", result);
Ok(result) Ok(result)
} }
fn get_value_parameter_names(&self) -> Result<Vec<(String, String)>> { fn get_value_parameter_names(&self) -> Result<Vec<(String, Option<String>)>> {
let mut values = self.get_sorted_selected_fields(); let mut values = self.get_sorted_selected_fields();
let existing_params: Vec<String> = self.params.iter().map(|p| p.name.clone().unwrap()).collect();
let mut res = vec![]; let mut res = vec![];
for (field, _) in values.iter_mut() { for (field, _) in values.iter_mut() {
res.push(( res.push((
Table::get_field_db_name(field)?, Table::get_field_db_name(field)?,
Table::get_field_param_name(field)?, match existing_params.contains(&Table::get_field_param_name(field)?) {
true => Some(Table::get_field_param_name(field)?),
false => None,
},
)); ));
} }
Ok(res) Ok(res)
@@ -516,7 +558,7 @@ impl<Table: BigQueryTable + Default + Debug>
//region QueryTypeSelect //region QueryTypeSelect
//region client not needed //region client not needed
impl<Table: BigQueryTable + Debug, Client: Debug, StartingData: Debug> impl<Table: BigQueryTable + Debug, Client: Debug, StartingData: Debug>
QueryBuilder<Table, QueryTypeSelect, Client, QueryWasNotBuilt, StartingData> QueryBuilder<Table, QueryTypeSelect, Client, QueryWasNotBuilt, StartingData>
{ {
pub fn add_order_by( pub fn add_order_by(
mut self, mut self,
@@ -531,7 +573,7 @@ impl<Table: BigQueryTable + Debug, Client: Debug, StartingData: Debug>
//endregion //endregion
//region client needed //region client needed
impl<Table: BigQueryTable + Debug, StartingData: Debug> impl<Table: BigQueryTable + Debug, StartingData: Debug>
QueryBuilder<Table, QueryTypeSelect, HasClient, QueryWasNotBuilt, StartingData> QueryBuilder<Table, QueryTypeSelect, HasClient, QueryWasNotBuilt, StartingData>
{ {
pub fn build_query( pub fn build_query(
self, self,
@@ -566,7 +608,7 @@ impl<Table: BigQueryTable + Debug, StartingData: Debug>
//endregion //endregion
//region with_client //region with_client
impl<Table: BigQueryTable, QueryType, StartingData> impl<Table: BigQueryTable, QueryType, StartingData>
QueryBuilder<Table, QueryType, NoClient, QueryWasNotBuilt, StartingData> QueryBuilder<Table, QueryType, NoClient, QueryWasNotBuilt, StartingData>
{ {
pub fn with_client( pub fn with_client(
self, self,
@@ -590,7 +632,7 @@ impl<Table: BigQueryTable, QueryType, StartingData>
//endregion //endregion
//region un_build & get query string //region un_build & get query string
impl<Table: BigQueryTable, QueryType, Client, StartingData> impl<Table: BigQueryTable, QueryType, Client, StartingData>
QueryBuilder<Table, QueryType, Client, QueryWasBuilt, StartingData> QueryBuilder<Table, QueryType, Client, QueryWasBuilt, StartingData>
{ {
pub fn un_build( pub fn un_build(
self, self,
@@ -616,7 +658,7 @@ impl<Table: BigQueryTable, QueryType, Client, StartingData>
//endregion //endregion
//region run //region run
impl<Table: BigQueryTable, QueryType: HasQueryType, StartingData> impl<Table: BigQueryTable, QueryType: HasQueryType, StartingData>
QueryBuilder<Table, QueryType, HasClient, QueryWasBuilt, StartingData> QueryBuilder<Table, QueryType, HasClient, QueryWasBuilt, StartingData>
{ {
pub async fn run(self) -> Result<QueryResultType<Table>> { pub async fn run(self) -> Result<QueryResultType<Table>> {
trace!("run query: {}", self.query); trace!("run query: {}", self.query);

View File

@@ -15,13 +15,13 @@ pub struct DbInfos {
#[primary_key] #[primary_key]
#[db_name("Id")] #[db_name("Id")]
row_id: i64, row_id: i64,
info1: Option<String>, info1: Option::<String>,
#[db_name("info")] #[db_name("info")]
info2: Option<String>, info2: Option::<String>,
info3: Option<String>, info3: Option::<String>,
info4i: Option<i32>, info4i: Option::<i32>,
#[db_name("yes")] #[db_name("yes")]
info4b: Option<bool>, info4b: Option::<bool>,
} }
pub struct DbInfos2 { pub struct DbInfos2 {
@@ -47,9 +47,9 @@ async fn test1() {
debug!("select result: {:?}", result); debug!("select result: {:?}", result);
let sample_data = DbInfos { let sample_data = DbInfos {
client: client.clone(), client: client.clone(),
row_id: 1, row_id: 9999,
info1: Some("test1".to_string()), info1: Some("test1".to_string()),
info2: Some("test2".to_string()), info2: None,
info3: Some("test3".to_string()), info3: Some("test3".to_string()),
info4i: Some(1), info4i: Some(1),
info4b: Some(true), info4b: Some(true),
@@ -75,10 +75,10 @@ async fn test_save() {
.expect("get_by_pk failed"); .expect("get_by_pk failed");
entry.info1 = Some("test1".to_string()); entry.info1 = Some("test1".to_string());
entry.info2 = Some("test2".to_string()); entry.info2 = Some("test2".to_string());
entry.info3 = Some("test3".to_string()); entry.info3 = None;
entry.info4i = Some(1); entry.info4i = Some(1);
entry.info4b = Some(true); entry.info4b = Some(true);
log::debug!("entry: {:?}", entry); debug!("entry: {:?}", entry);
debug!("========================================================================"); debug!("========================================================================");
debug!("starting save"); debug!("starting save");
debug!("========================================================================"); debug!("========================================================================");
@@ -86,6 +86,18 @@ async fn test_save() {
debug!("========================================================================"); debug!("========================================================================");
debug!("save done"); debug!("save done");
debug!("========================================================================"); debug!("========================================================================");
let info1 = entry.info1.clone().unwrap();
entry.info1 = Some("0987654321".to_string());
debug!("========================================================================");
debug!("starting reload");
debug!("========================================================================");
entry.reload().await.expect("reload failed");
debug!("========================================================================");
debug!("reload done");
debug!("========================================================================");
assert_eq!(info1, entry.info1.unwrap(), "reload failed");
assert_eq!(None, entry.info3, "Info 3 should be set to None before the save happened");
} }
#[tokio::test] #[tokio::test]