Skip to content

Commit

Permalink
use new config structs in valve functions (WIP)
Browse files Browse the repository at this point in the history
The following functions now use the config structs in valve.rs:
get_path()
get_sorted_table_list()
table_has_changed()
get_setup_statements()

The following functions now use the config structs in lib.rs:
get_sql_for_text_view()
get_sql_type_for_global_config()
get_table_ddl()
  • Loading branch information
lmcmicu committed Feb 8, 2024
1 parent ef2426e commit 126c80d
Show file tree
Hide file tree
Showing 3 changed files with 204 additions and 226 deletions.
163 changes: 58 additions & 105 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1109,21 +1109,21 @@ pub fn get_sql_for_standard_view(table: &str, pool: &AnyPool) -> String {
/// errors. Like the function for generating a standard view, the SQL generated by this function is
/// returned in the form of a tuple of Strings, with the first string being a SQL statement
/// for dropping the view, and the second string being a SQL statement for creating it.
pub fn get_sql_for_text_view(tables_config: &SerdeMap, table: &str, pool: &AnyPool) -> String {
pub fn get_sql_for_text_view(
tables_config: &HashMap<String, ValveTableConfig>,
tables_config_old: &SerdeMap,
table: &str,
pool: &AnyPool,
) -> String {
let is_clause = if pool.any_kind() == AnyKind::Sqlite {
"IS"
} else {
"IS NOT DISTINCT FROM"
};

let real_columns = tables_config
let real_columns = &tables_config
.get(table)
.and_then(|t| t.as_object())
.and_then(|t| t.get("column"))
.and_then(|t| t.as_object())
.and_then(|t| Some(t.keys()))
.and_then(|k| Some(k.map(|k| k.to_string())))
.and_then(|t| Some(t.collect::<Vec<_>>()))
.and_then(|t| Some(t.column.keys().map(|k| k.to_string()).collect::<Vec<_>>()))
.unwrap();

// Add a second "text view" such that the datatypes of all values are TEXT and appear
Expand Down Expand Up @@ -2152,7 +2152,13 @@ pub async fn insert_new_row_tx(
}));
}

let sql_type = get_sql_type_from_global_config(global_config, table, column, pool);
let sql_type = get_sql_type_from_global_config(
&ValveConfig::default(),
global_config,
table,
column,
pool,
);
if is_sql_type_error(&sql_type, value) {
insert_values.push(String::from("NULL"));
} else {
Expand Down Expand Up @@ -2685,25 +2691,20 @@ pub fn get_sql_type(
/// Given the global config map, a table name, a column name, and a database connection pool
/// used to determine the database type return the column's SQL type.
pub fn get_sql_type_from_global_config(
global_config: &SerdeMap,
config: &ValveConfig,
config_old: &SerdeMap,
table: &str,
column: &str,
pool: &AnyPool,
) -> String {
let dt_config_old = global_config
.get("datatype")
.and_then(|d| d.as_object())
let dt_config = &config.datatype;
let dt = &config
.table
.get(table)
.and_then(|t| t.column.get(column))
.and_then(|c| Some(c.datatype.to_string()))
.unwrap();
let dt = global_config
.get("table")
.and_then(|t| t.get(table))
.and_then(|t| t.get("column"))
.and_then(|c| c.get(column))
.and_then(|c| c.get("datatype"))
.and_then(|d| d.as_str())
.and_then(|d| Some(d.to_string()))
.expect(&format!("Could not get datatype for {}.{}", table, column));
get_sql_type(&HashMap::new(), &dt_config_old, &dt, pool)
get_sql_type(dt_config, &SerdeMap::new(), &dt, pool)
}

/// Given a SQL type, return the appropriate CAST(...) statement for casting the SQL_PARAM
Expand Down Expand Up @@ -3125,7 +3126,9 @@ pub fn get_table_constraints(
/// database connection pool, return a list of DDL statements that can be used to create the
/// database tables.
pub fn get_table_ddl(
tables_config: &SerdeMap,
tables_config: &HashMap<String, ValveTableConfig>,
datatypes_config: &HashMap<String, ValveDatatypeConfig>,
tables_config_old: &SerdeMap,
datatypes_config_old: &SerdeMap,
parser: &StartParser,
table_name: &String,
Expand All @@ -3137,62 +3140,39 @@ pub fn get_table_ddl(
String::from(r#" "row_number" BIGINT,"#),
];

let colvals = {
let column_configs = {
let normal_table_name;
if let Some(s) = table_name.strip_suffix("_conflict") {
normal_table_name = String::from(s);
} else {
normal_table_name = table_name.to_string();
}
let column_order = tables_config
.get(&normal_table_name)
.and_then(|t| t.get("column_order"))
.and_then(|c| c.as_array())
.unwrap()
.iter()
.map(|v| v.as_str().unwrap().to_string())
.collect::<Vec<_>>();
let columns = tables_config
.get(&normal_table_name)
.and_then(|c| c.as_object())
.and_then(|o| o.get("column"))
.and_then(|c| c.as_object())
.unwrap();
let column_order = &tables_config.get(&normal_table_name).unwrap().column_order;
let columns = &tables_config.get(&normal_table_name).unwrap().column;

column_order
.iter()
.map(|column_name| {
columns
.get(column_name)
.and_then(|c| c.as_object())
.unwrap()
})
.map(|column_name| columns.get(column_name).unwrap())
.collect::<Vec<_>>()
};

let table_constraints = {
let (primaries, uniques, foreigns, trees, unders) = {
// Conflict tables have no database constraints:
if table_name.ends_with("_conflict") {
json!({"foreign": [], "unique": [], "primary": [], "tree": [], "under": [],})
(vec![], vec![], vec![], vec![], vec![])
} else {
// TODO: Here.
serde_json::Value::Object(SerdeMap::new())
//get_table_constraints(&HashMap::new(), &HashMap::new(), parser, &table_name, &pool)
//get_table_constraints(tables_config, datatypes_config_old, parser, &table_name, &pool)
get_table_constraints(tables_config, datatypes_config, parser, &table_name, &pool)
}
};

let c = colvals.len();
let c = column_configs.len();
let mut r = 0;
for row in colvals {
for column_config in column_configs {
r += 1;
let sql_type = get_sql_type(
&HashMap::new(),
datatypes_config,
datatypes_config_old,
&row.get("datatype")
.and_then(|d| d.as_str())
.and_then(|s| Some(s.to_string()))
.unwrap(),
&column_config.datatype,
pool,
);

Expand All @@ -3208,58 +3188,40 @@ pub fn get_table_ddl(
panic!(
"Unrecognized SQL type '{}' for datatype: '{}'. Accepted SQL types are: {}",
sql_type,
row.get("datatype").and_then(|d| d.as_str()).unwrap(),
column_config.datatype,
SQL_TYPES.join(", ")
);
}

let column_name = row.get("column").and_then(|s| s.as_str()).unwrap();
let column_name = &column_config.column;
let mut line = format!(r#" "{}" {}"#, column_name, sql_type);

// Check if the column is a primary key and indicate this in the DDL if so:
let primary_constraints = table_constraints
.get("primary")
.and_then(|v| v.as_array())
.unwrap();
if primary_constraints.contains(&json!(column_name)) {
if primaries.contains(&column_name) {
line.push_str(" PRIMARY KEY");
}

// Check if the column has a unique constraint and indicate this in the DDL if so:
let unique_constraints = table_constraints
.get("unique")
.and_then(|v| v.as_array())
.unwrap();
if unique_constraints.contains(&json!(column_name)) {
if uniques.contains(&column_name) {
line.push_str(" UNIQUE");
}

// If there are foreign constraints add a column to the end of the statement which we will
// finish after this for loop is done:
if !(r >= c
&& table_constraints
.get("foreign")
.and_then(|v| v.as_array())
.and_then(|v| Some(v.is_empty()))
.unwrap())
{
if !(r >= c && foreigns.is_empty()) {
line.push_str(",");
}
create_lines.push(line);
}

// Add the SQL to indicate any foreign constraints:
let foreign_keys = table_constraints
.get("foreign")
.and_then(|v| v.as_array())
.unwrap();
let num_fkeys = foreign_keys.len();
for (i, fkey) in foreign_keys.iter().enumerate() {
let num_fkeys = foreigns.len();
for (i, fkey) in foreigns.iter().enumerate() {
create_lines.push(format!(
r#" FOREIGN KEY ("{}") REFERENCES "{}"("{}"){}"#,
fkey.get("column").and_then(|s| s.as_str()).unwrap(),
fkey.get("ftable").and_then(|s| s.as_str()).unwrap(),
fkey.get("fcolumn").and_then(|s| s.as_str()).unwrap(),
fkey.column,
fkey.ftable,
fkey.fcolumn,
if i < (num_fkeys - 1) { "," } else { "" }
));
}
Expand All @@ -3270,26 +3232,11 @@ pub fn get_table_ddl(

// Loop through the tree constraints and if any of their associated child columns do not already
// have an associated unique or primary index, create one implicitly here:
let tree_constraints = table_constraints
.get("tree")
.and_then(|v| v.as_array())
.unwrap();
for tree in tree_constraints {
let unique_keys = table_constraints
.get("unique")
.and_then(|v| v.as_array())
.unwrap();
let primary_keys = table_constraints
.get("primary")
.and_then(|v| v.as_array())
.unwrap();
let tree_child = tree.get("child").and_then(|c| c.as_str()).unwrap();
if !unique_keys.contains(&SerdeValue::String(tree_child.to_string()))
&& !primary_keys.contains(&SerdeValue::String(tree_child.to_string()))
{
for tree in trees {
if !uniques.contains(&tree.child) && !primaries.contains(&tree.child) {
statements.push(format!(
r#"CREATE UNIQUE INDEX "{}_{}_idx" ON "{}"("{}");"#,
table_name, tree_child, table_name, tree_child
table_name, tree.child, table_name, tree.child
));
}
}
Expand Down Expand Up @@ -3497,7 +3444,13 @@ pub async fn make_inserts(
let cell = row.contents.get(column).unwrap();
// Insert the value of the cell into the column unless inserting it will cause a db
// error or it has the nulltype field set, in which case insert NULL:
let sql_type = get_sql_type_from_global_config(config, &main_table, column, pool);
let sql_type = get_sql_type_from_global_config(
&ValveConfig::default(),
config,
&main_table,
column,
pool,
);
if cell.nulltype != None || is_sql_type_error(&sql_type, &cell.value) {
row_values.push(String::from("NULL"));
} else {
Expand Down
Loading

0 comments on commit 126c80d

Please sign in to comment.