From f475a26966645f38ee73e750992bbb1bd3907b39 Mon Sep 17 00:00:00 2001 From: Michael Cuffaro Date: Sun, 14 Jan 2024 14:56:01 -0500 Subject: [PATCH] add old get_matching_values() --- src/validate.rs | 196 ++++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 190 insertions(+), 6 deletions(-) diff --git a/src/validate.rs b/src/validate.rs index f4c2b35d..5d4ac4d5 100644 --- a/src/validate.rs +++ b/src/validate.rs @@ -1,4 +1,10 @@ +use crate::{ + ast::Expression, cast_column_sql_to_text, cast_sql_param_from_text, error, get_column_value, + get_sql_type_from_global_config, is_sql_type_error, local_sql_syntax, ColumnRule, + CompiledCondition, ParsedStructure, SerdeMap, ValveError, ValveRow, SQL_PARAM, +}; use chrono::Utc; +use enquote::unquote; use indexmap::IndexMap; use serde_json::{json, Value as SerdeValue}; use sqlx::{ @@ -7,12 +13,6 @@ use sqlx::{ }; use std::collections::HashMap; -use crate::{ - cast_sql_param_from_text, error, get_column_value, get_sql_type_from_global_config, - is_sql_type_error, local_sql_syntax, ColumnRule, CompiledCondition, SerdeMap, ValveError, - ValveRow, -}; - /// Represents a particular cell in a particular row of data with vaildation results. #[derive(Clone, Debug)] pub struct ResultCell { @@ -49,6 +49,190 @@ pub struct QueryAsIf { pub row: Option, } +pub async fn get_matching_values_old( + config: &SerdeMap, + compiled_datatype_conditions: &HashMap, + parsed_structure_conditions: &HashMap, + pool: &AnyPool, + table_name: &str, + column_name: &str, + matching_string: Option<&str>, +) -> Result { + let dt_name = config + .get("table") + .and_then(|t| t.as_object()) + .and_then(|t| t.get(table_name)) + .and_then(|t| t.as_object()) + .and_then(|t| t.get("column")) + .and_then(|c| c.as_object()) + .and_then(|c| c.get(column_name)) + .and_then(|c| c.as_object()) + .and_then(|c| c.get("datatype")) + .and_then(|d| d.as_str()) + .unwrap(); + + let dt_condition = compiled_datatype_conditions + .get(dt_name) + .and_then(|d| Some(d.parsed.clone())); + + let mut values = vec![]; + match dt_condition { + Some(Expression::Function(name, args)) if name == "in" => { + for arg in args { + if let Expression::Label(arg) = *arg { + // Remove the enclosing quotes from the values being returned: + let label = unquote(&arg).unwrap_or_else(|_| arg); + if let Some(s) = matching_string { + if label.contains(s) { + values.push(label); + } + } + } + } + } + _ => { + // If the datatype for the column does not correspond to an `in(...)` function, then we + // check the column's structure constraints. If they include a + // `from(foreign_table.foreign_column)` condition, then the values are taken from the + // foreign column. Otherwise if the structure includes an + // `under(tree_table.tree_column, value)` condition, then get the values from the tree + // column that are under `value`. + let structure = parsed_structure_conditions.get( + config + .get("table") + .and_then(|t| t.as_object()) + .and_then(|t| t.get(table_name)) + .and_then(|t| t.as_object()) + .and_then(|t| t.get("column")) + .and_then(|c| c.as_object()) + .and_then(|c| c.get(column_name)) + .and_then(|c| c.as_object()) + .and_then(|c| c.get("structure")) + .and_then(|d| d.as_str()) + .unwrap_or_else(|| ""), + ); + + let sql_type = + get_sql_type_from_global_config(&config, table_name, &column_name, pool).unwrap(); + + match structure { + Some(ParsedStructure { original, parsed }) => { + let matching_string = { + match matching_string { + None => "%".to_string(), + Some(s) => format!("%{}%", s), + } + }; + + match parsed { + Expression::Function(name, args) if name == "from" => { + let foreign_key = &args[0]; + if let Expression::Field(ftable, fcolumn) = &**foreign_key { + let fcolumn_text = cast_column_sql_to_text(&fcolumn, &sql_type); + let sql = local_sql_syntax( + &pool, + &format!( + r#"SELECT "{}" FROM "{}" WHERE {} LIKE {}"#, + fcolumn, ftable, fcolumn_text, SQL_PARAM + ), + ); + let rows = sqlx_query(&sql) + .bind(&matching_string) + .fetch_all(pool) + .await?; + for row in rows.iter() { + values.push(get_column_value(&row, &fcolumn, &sql_type)); + } + } + } + Expression::Function(name, args) if name == "under" || name == "tree" => { + let mut tree_col = "not set"; + let mut under_val = Some("not set".to_string()); + if name == "under" { + if let Expression::Field(_, column) = &**&args[0] { + tree_col = column; + } + if let Expression::Label(label) = &**&args[1] { + under_val = Some(label.to_string()); + } + } else { + let tree_key = &args[0]; + if let Expression::Label(label) = &**tree_key { + tree_col = label; + under_val = None; + } + } + + let tree = config + .get("constraints") + .and_then(|c| c.as_object()) + .and_then(|c| c.get("tree")) + .and_then(|t| t.as_object()) + .and_then(|t| t.get(table_name)) + .and_then(|t| t.as_array()) + .and_then(|t| { + t.iter().find(|o| o.get("child").unwrap() == tree_col) + }) + .expect( + format!("No tree: '{}.{}' found", table_name, tree_col) + .as_str(), + ) + .as_object() + .unwrap(); + let child_column = tree.get("child").and_then(|c| c.as_str()).unwrap(); + + let (tree_sql, mut params) = with_tree_sql( + &config, + tree, + &table_name.to_string(), + &table_name.to_string(), + under_val.as_ref(), + None, + pool, + ); + let child_column_text = + cast_column_sql_to_text(&child_column, &sql_type); + let sql = local_sql_syntax( + &pool, + &format!( + r#"{} SELECT "{}" FROM "tree" WHERE {} LIKE {}"#, + tree_sql, child_column, child_column_text, SQL_PARAM + ), + ); + params.push(matching_string); + + let mut query = sqlx_query(&sql); + for param in ¶ms { + query = query.bind(param); + } + + let rows = query.fetch_all(pool).await?; + for row in rows.iter() { + values.push(get_column_value(&row, &child_column, &sql_type)); + } + } + _ => panic!("Unrecognised structure: {}", original), + }; + } + None => (), + }; + } + }; + + let mut typeahead_values = vec![]; + for (i, v) in values.iter().enumerate() { + // enumerate() begins at 0 but we need to begin at 1: + let i = i + 1; + typeahead_values.push(json!({ + "id": v, + "label": v, + "order": i, + })); + } + + Ok(json!(typeahead_values)) +} + pub async fn validate_row_old( config: &SerdeMap, compiled_datatype_conditions: &HashMap,