1
0

add entry generation and column type guessing

This commit is contained in:
Rokas Puzonas 2023-03-04 22:38:09 +02:00
parent f279900ac9
commit 1fa7c7bbc8
5 changed files with 649 additions and 0 deletions

276
Cargo.lock generated
View File

@ -17,6 +17,15 @@ dependencies = [
"memchr",
]
[[package]]
name = "android_system_properties"
version = "0.1.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "819e7219dbd41043ac279b19830f2efc897156490d7fd6ea916720117ee66311"
dependencies = [
"libc",
]
[[package]]
name = "anyhow"
version = "1.0.69"
@ -68,12 +77,43 @@ version = "1.4.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "14c189c53d098945499cdfa7ecc63567cf3886b3332b312a5b4585d8d3a6a610"
[[package]]
name = "cc"
version = "1.0.79"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "50d30906286121d95be3d479533b458f87493b30a4b5f79a607db8f5d11aa91f"
[[package]]
name = "cfg-if"
version = "1.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
[[package]]
name = "chrono"
version = "0.4.23"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "16b0a3d9ed01224b22057780a37bb8c5dbfe1be8ba48678e7bf57ec4b385411f"
dependencies = [
"iana-time-zone",
"js-sys",
"num-integer",
"num-traits",
"time",
"wasm-bindgen",
"winapi",
]
[[package]]
name = "codespan-reporting"
version = "0.11.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3538270d33cc669650c4b093848450d380def10c331d38c768e34cac80576e6e"
dependencies = [
"termcolor",
"unicode-width",
]
[[package]]
name = "console_error_panic_hook"
version = "0.1.7"
@ -84,6 +124,12 @@ dependencies = [
"wasm-bindgen",
]
[[package]]
name = "core-foundation-sys"
version = "0.8.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5827cebf4670468b8772dd191856768aedcb1b0278a04f989f7766351917b9dc"
[[package]]
name = "crc32fast"
version = "1.3.2"
@ -102,6 +148,59 @@ dependencies = [
"cfg-if",
]
[[package]]
name = "cxx"
version = "1.0.91"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "86d3488e7665a7a483b57e25bdd90d0aeb2bc7608c8d0346acf2ad3f1caf1d62"
dependencies = [
"cc",
"cxxbridge-flags",
"cxxbridge-macro",
"link-cplusplus",
]
[[package]]
name = "cxx-build"
version = "1.0.91"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "48fcaf066a053a41a81dfb14d57d99738b767febb8b735c3016e469fac5da690"
dependencies = [
"cc",
"codespan-reporting",
"once_cell",
"proc-macro2",
"quote",
"scratch",
"syn",
]
[[package]]
name = "cxxbridge-flags"
version = "1.0.91"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a2ef98b8b717a829ca5603af80e1f9e2e48013ab227b68ef37872ef84ee479bf"
[[package]]
name = "cxxbridge-macro"
version = "1.0.91"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "086c685979a698443656e5cf7856c95c642295a38599f12fb1ff76fb28d19892"
dependencies = [
"proc-macro2",
"quote",
"syn",
]
[[package]]
name = "fake"
version = "2.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4d68f517805463f3a896a9d29c1d6ff09d3579ded64a7201b4069f8f9c0d52fd"
dependencies = [
"rand",
]
[[package]]
name = "flate2"
version = "1.0.25"
@ -198,6 +297,17 @@ dependencies = [
"slab",
]
[[package]]
name = "getrandom"
version = "0.2.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c05aeb6a22b8f62540c194aac980f2115af067bfe15a0734d7277a768d396b31"
dependencies = [
"cfg-if",
"libc",
"wasi 0.11.0+wasi-snapshot-preview1",
]
[[package]]
name = "gloo"
version = "0.8.0"
@ -378,6 +488,30 @@ dependencies = [
"libc",
]
[[package]]
name = "iana-time-zone"
version = "0.1.53"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "64c122667b287044802d6ce17ee2ddf13207ed924c712de9a66a5814d5b64765"
dependencies = [
"android_system_properties",
"core-foundation-sys",
"iana-time-zone-haiku",
"js-sys",
"wasm-bindgen",
"winapi",
]
[[package]]
name = "iana-time-zone-haiku"
version = "0.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0703ae284fc167426161c2e3f1da3ea71d94b21bedbcc9494e92b28e334e3dca"
dependencies = [
"cxx",
"cxx-build",
]
[[package]]
name = "implicit-clone"
version = "0.3.5"
@ -441,6 +575,15 @@ version = "0.2.139"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "201de327520df007757c1f0adce6e827fe8562fbc28bfd9c15571c66ca1f5f79"
[[package]]
name = "link-cplusplus"
version = "1.0.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ecd207c9c713c34f95a097a5b029ac2ce6010530c7b49d7fea24d977dede04f5"
dependencies = [
"cc",
]
[[package]]
name = "log"
version = "0.4.17"
@ -456,9 +599,12 @@ version = "0.1.0"
dependencies = [
"anyhow",
"base64",
"chrono",
"fake",
"gloo",
"js-sys",
"lazy-regex",
"rand",
"serde",
"thiserror",
"web-sys",
@ -482,6 +628,25 @@ dependencies = [
"adler",
]
[[package]]
name = "num-integer"
version = "0.1.45"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "225d3389fb3509a24c93f5c29eb6bde2586b98d9f016636dff58d7c6f7569cd9"
dependencies = [
"autocfg",
"num-traits",
]
[[package]]
name = "num-traits"
version = "0.2.15"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "578ede34cf02f8924ab9447f50c28075b4d3e5b269972345e7e0372b38c6cdcd"
dependencies = [
"autocfg",
]
[[package]]
name = "num_cpus"
version = "1.15.0"
@ -547,6 +712,12 @@ dependencies = [
"thiserror",
]
[[package]]
name = "ppv-lite86"
version = "0.2.17"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5b40af805b3121feab8a3c29f04d8ad262fa8e0561883e7653e024ae4479e6de"
[[package]]
name = "prettyplease"
version = "0.1.23"
@ -616,6 +787,36 @@ dependencies = [
"proc-macro2",
]
[[package]]
name = "rand"
version = "0.8.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404"
dependencies = [
"libc",
"rand_chacha",
"rand_core",
]
[[package]]
name = "rand_chacha"
version = "0.3.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88"
dependencies = [
"ppv-lite86",
"rand_core",
]
[[package]]
name = "rand_core"
version = "0.6.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c"
dependencies = [
"getrandom",
]
[[package]]
name = "regex"
version = "1.7.1"
@ -645,6 +846,12 @@ version = "1.0.12"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7b4b9743ed687d4b4bcedf9ff5eaa7398495ae14e61cba0a295704edbc7decde"
[[package]]
name = "scratch"
version = "1.0.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ddccb15bcce173023b3fedd9436f882a0739b8dfb45e4f6b6002bee5929f61b2"
[[package]]
name = "serde"
version = "1.0.152"
@ -719,6 +926,15 @@ dependencies = [
"unicode-ident",
]
[[package]]
name = "termcolor"
version = "1.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "be55cf8942feac5c765c2c993422806843c9a9a45d4d5c407ad6dd2ea95eb9b6"
dependencies = [
"winapi-util",
]
[[package]]
name = "thiserror"
version = "1.0.38"
@ -739,6 +955,17 @@ dependencies = [
"syn",
]
[[package]]
name = "time"
version = "0.1.45"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1b797afad3f312d1c66a56d11d0316f916356d11bd158fbc6ca6389ff6bf805a"
dependencies = [
"libc",
"wasi 0.10.0+wasi-snapshot-preview1",
"winapi",
]
[[package]]
name = "tokio"
version = "1.25.0"
@ -799,12 +1026,30 @@ version = "1.0.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "84a22b9f218b40614adcb3f4ff08b703773ad44fa9423e4e0d346d5db86e4ebc"
[[package]]
name = "unicode-width"
version = "0.1.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c0edd1e5b14653f783770bce4a4dabb4a5108a5370a5f5d8cfe8710c361f6c8b"
[[package]]
name = "version_check"
version = "0.9.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f"
[[package]]
name = "wasi"
version = "0.10.0+wasi-snapshot-preview1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1a143597ca7c7793eff794def352d41792a93c481eb1042423ff7ff72ba2c31f"
[[package]]
name = "wasi"
version = "0.11.0+wasi-snapshot-preview1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423"
[[package]]
name = "wasm-bindgen"
version = "0.2.84"
@ -881,6 +1126,37 @@ dependencies = [
"wasm-bindgen",
]
[[package]]
name = "winapi"
version = "0.3.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419"
dependencies = [
"winapi-i686-pc-windows-gnu",
"winapi-x86_64-pc-windows-gnu",
]
[[package]]
name = "winapi-i686-pc-windows-gnu"
version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6"
[[package]]
name = "winapi-util"
version = "0.1.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "70ec6ce85bb158151cae5e5c87f95a8e97d2c0c4b001223f33a334e3ce5de178"
dependencies = [
"winapi",
]
[[package]]
name = "winapi-x86_64-pc-windows-gnu"
version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"
[[package]]
name = "windows-sys"
version = "0.42.0"

View File

@ -14,6 +14,9 @@ js-sys = "0.3"
base64 = "0.13.0"
gloo = "0.8"
serde = { version = "1.0", features = ["derive"] }
fake = "2.5"
rand = "0.8"
chrono = "0.4.23"
[dependencies.zip]
version = "0.6.4"

365
src/generate_sql.rs Normal file
View File

@ -0,0 +1,365 @@
use std::{rc::Rc, collections::HashSet};
use anyhow::{Result, bail};
use rand::{seq::SliceRandom, Rng, rngs::ThreadRng};
use chrono::{Local, NaiveDateTime, Days};
use fake::{faker::{lorem::en::*, name::en::{FirstName, LastName, Name}, phone_number::en::PhoneNumber, internet::en::{DomainSuffix, FreeEmail}, company::en::BsNoun, address::{en::{CityName, StreetName}}}, Fake};
use crate::magicdraw_parser::{SQLTable, SQLColumn, SQLType, SQLCheckConstraint};
const INDENT: &str = " ";
#[derive(Debug, PartialEq)]
pub enum SQLIntValueGuess {
Range(i32, i32),
AutoIncrement
}
#[derive(Debug, PartialEq)]
pub enum SQLTimeValueGuess {
Now,
Future,
Past
}
#[derive(Debug, PartialEq)]
pub enum SQLStringValueGuess {
LoremIpsum,
FirstName,
LastName,
FullName,
Empty,
PhoneNumber,
CityName,
Address,
Email,
URL,
RandomEnum(Vec<String>),
}
#[derive(Debug, PartialEq)]
pub enum SQLBoolValueGuess {
True,
False,
Random,
}
#[derive(Debug, PartialEq)]
pub enum SQLValueGuess {
Int(SQLIntValueGuess),
Date(SQLTimeValueGuess),
Time(SQLTimeValueGuess),
Datetime(SQLTimeValueGuess),
Float(f32, f32),
Bool(SQLBoolValueGuess),
String(usize, SQLStringValueGuess),
}
// TODO: Check primary key constraint
pub fn generate_fake_entries(
tables: &[Rc<SQLTable>],
value_guessess: &Vec<Vec<SQLValueGuess>>,
rows_per_table: u32
) -> Result<String> {
let mut lines = vec![];
let mut rng = rand::thread_rng();
let mut all_foreign_columns = vec![];
let mut all_entries = vec![];
for table in tables {
let mut entries = vec![];
for _ in 0..rows_per_table {
entries.push(vec![]);
}
all_entries.push(entries);
let mut foreign_columns = vec![];
for (i, column) in table.columns.iter().enumerate() {
if let Some((table_name, column_name)) = &column.foreign_key {
let (table_idx, table) = tables.iter()
.enumerate()
.find(|(_, table)| table.name.eq(table_name))
.expect("Foreign table not found");
let (column_idx, _) = table.columns
.iter()
.enumerate()
.find(|(_, column)| column.name.eq(column_name))
.expect("Foreign column not found");
foreign_columns.push((i, table_idx, column_idx));
}
}
all_foreign_columns.push(foreign_columns);
}
let mut entries_with_foreign_keys = HashSet::new();
for (table_idx, table) in tables.iter().enumerate() {
let entries = &mut all_entries[table_idx];
for (column_idx, column) in table.columns.iter().enumerate() {
let mut auto_increment_counter = 0;
let value_guess = &value_guessess[table_idx][column_idx];
for entry_idx in 0..(rows_per_table as usize) {
if let Some(_) = &column.foreign_key {
entries_with_foreign_keys.insert((table_idx, entry_idx));
entries[entry_idx].push("".into());
} else {
entries[entry_idx].push(generate_value(&mut rng, &value_guess, &mut auto_increment_counter));
}
}
}
}
while !entries_with_foreign_keys.is_empty() {
let entries_with_foreign_keys_copy = entries_with_foreign_keys.clone();
let before_retain = entries_with_foreign_keys.len();
entries_with_foreign_keys.retain(|(table_idx, entry_idx)| {
for (column_idx, foreign_table_idx, foreign_column_idx) in &all_foreign_columns[*table_idx] {
let available_values: Vec<&str>;
// If the foreign column, is also a foreign of the other table, ...
// Then we need to filter out available options which have not been filled in
if all_foreign_columns[*foreign_table_idx].iter().find(|(idx, _, _)| idx == foreign_column_idx).is_some() {
available_values = all_entries[*foreign_table_idx].iter()
.enumerate()
.filter(|(i, _)| entries_with_foreign_keys_copy.contains(&(*foreign_table_idx, *i)))
.map(|(_, entry)| entry[*foreign_column_idx].as_str())
.collect();
} else {
available_values = all_entries[*foreign_table_idx].iter()
.map(|entry| entry[*foreign_column_idx].as_str())
.collect();
}
if let Some(chosen_value) = available_values.choose(&mut rng) {
all_entries[*table_idx][*entry_idx][*column_idx] = chosen_value.to_string();
} else {
// Early break, thre are no currently available options
// Try next time
return true;
}
}
false
});
// This is to stop infnite loop, where during each iteration nothing gets removed
if before_retain == entries_with_foreign_keys.len() {
bail!("Failed to resolve foreign keys")
}
}
for (i, table) in tables.iter().enumerate() {
let mut column_names = vec![];
for column in &table.columns {
column_names.push(column.name.as_str());
}
let entries = &all_entries[i];
lines.push(format!("INSERT INTO {}", table.name));
lines.push(format!("{}({})", INDENT, column_names.join(", ")));
lines.push("VALUES".into());
let entries_str = entries.iter()
.map(|entry| format!("{}({})", INDENT, entry.join(", ")))
.collect::<Vec<_>>()
.join(",\n");
lines.push(format!("{};\n", entries_str));
}
Ok(lines.join("\n"))
}
fn generate_time_value(rng: &mut ThreadRng, guess: &SQLTimeValueGuess) -> NaiveDateTime {
let now = Local::now().naive_local();
match guess {
SQLTimeValueGuess::Now => now,
SQLTimeValueGuess::Future => {
let days = rng.gen_range(1..=30);
now.checked_add_days(Days::new(days)).unwrap()
},
SQLTimeValueGuess::Past => {
let days = rng.gen_range(7..=365);
now.checked_sub_days(Days::new(days)).unwrap()
}
}
}
fn generate_value(rng: &mut ThreadRng, guess: &SQLValueGuess, auto_increment_counter: &mut u32) -> String {
match guess {
SQLValueGuess::Int(int_guess) => {
match int_guess {
SQLIntValueGuess::Range(min, max) => {
rng.gen_range((*min)..=(*max)).to_string()
},
SQLIntValueGuess::AutoIncrement => {
let str = auto_increment_counter.to_string();
*auto_increment_counter += 1;
str
},
}
},
SQLValueGuess::Date(time_gues) => {
let datetime = generate_time_value(rng, &time_gues);
format!("'{}'", datetime.format("%Y-%m-%d"))
},
SQLValueGuess::Time(time_gues) => {
let datetime = generate_time_value(rng, &time_gues);
format!("'{}'", datetime.format("%H:%M:%S"))
},
SQLValueGuess::Datetime(time_gues) => {
let datetime = generate_time_value(rng, &time_gues);
format!("'{}'", datetime.format("%Y-%m-%d %H:%M:%S"))
},
SQLValueGuess::Bool(bool_guess) => {
match bool_guess {
SQLBoolValueGuess::True => "1".into(),
SQLBoolValueGuess::False => "0".into(),
SQLBoolValueGuess::Random => rng.gen_range(0..=1).to_string(),
}
},
SQLValueGuess::Float(min, max) => {
let value = rng.gen_range((*min)..(*max));
((value * 100.0 as f32).round() / 100.0).to_string()
},
SQLValueGuess::String(max_size, string_guess) => {
let mut str = match string_guess {
SQLStringValueGuess::LoremIpsum => {
let mut current_len = 0;
let mut text = vec![];
let words: Vec<String> = Words(3..10).fake_with_rng(rng);
for word in words {
current_len += word.len() + 1;
text.push(word);
if current_len > *max_size { break; }
}
text.join(" ").to_string()
},
SQLStringValueGuess::FirstName => {
FirstName().fake_with_rng(rng)
},
SQLStringValueGuess::LastName => {
LastName().fake_with_rng(rng)
},
SQLStringValueGuess::FullName => {
Name().fake_with_rng(rng)
},
SQLStringValueGuess::PhoneNumber => {
PhoneNumber().fake_with_rng(rng)
},
SQLStringValueGuess::CityName => {
CityName().fake_with_rng(rng)
},
SQLStringValueGuess::Address => {
StreetName().fake_with_rng(rng)
},
SQLStringValueGuess::Email => {
FreeEmail().fake_with_rng(rng)
},
SQLStringValueGuess::URL => {
let suffix: String = DomainSuffix().fake_with_rng(rng);
let noun: String = BsNoun().fake_with_rng(rng);
let noun: String = noun.to_lowercase()
.chars()
.map(|c| if c.is_whitespace() { '-' } else { c })
.collect();
format!("www.{}.{}", noun, suffix)
},
SQLStringValueGuess::RandomEnum(options) => {
options.choose(rng).unwrap().to_string()
},
SQLStringValueGuess::Empty => {
"".into()
}
};
str.truncate(*max_size);
format!("'{}'", str)
}
}
}
fn generate_string_guess(column: &SQLColumn) -> SQLStringValueGuess {
if let Some(constraint) = &column.check_constraint {
if let SQLCheckConstraint::OneOf(options) = constraint {
return SQLStringValueGuess::RandomEnum(options.clone())
} else {
return SQLStringValueGuess::LoremIpsum
}
}
let name = column.name.to_lowercase();
if name.contains("first") && name.contains("name") {
SQLStringValueGuess::FirstName
} else if (name.contains("last") && name.contains("name")) || name.contains("surname") {
SQLStringValueGuess::LastName
} else if name.contains("phone") && name.contains("number") {
SQLStringValueGuess::PhoneNumber
} else if name.contains("city") {
SQLStringValueGuess::CityName
} else if name.contains("address") {
SQLStringValueGuess::Address
} else if name.contains("email") {
SQLStringValueGuess::Email
} else if name.contains("homepage") || name.contains("website") || name.contains("url") {
SQLStringValueGuess::URL
} else {
SQLStringValueGuess::LoremIpsum
}
}
pub fn generate_guess(column: &SQLColumn) -> SQLValueGuess {
match column.sql_type {
SQLType::Int => {
if column.primary_key {
SQLValueGuess::Int(SQLIntValueGuess::AutoIncrement)
} else {
SQLValueGuess::Int(SQLIntValueGuess::Range(0, 100))
}
},
SQLType::Float | SQLType::Decimal => {
SQLValueGuess::Float(0.0, 100.0)
},
SQLType::Date => {
let name = column.name.to_lowercase();
if name.contains("create") || name.contains("update") {
SQLValueGuess::Date(SQLTimeValueGuess::Past)
} else {
SQLValueGuess::Date(SQLTimeValueGuess::Now)
}
},
SQLType::Time => {
let name = column.name.to_lowercase();
if name.contains("create") || name.contains("update") {
SQLValueGuess::Time(SQLTimeValueGuess::Past)
} else {
SQLValueGuess::Time(SQLTimeValueGuess::Now)
}
},
SQLType::Datetime => {
let name = column.name.to_lowercase();
if name.contains("create") || name.contains("update") {
SQLValueGuess::Datetime(SQLTimeValueGuess::Past)
} else {
SQLValueGuess::Datetime(SQLTimeValueGuess::Now)
}
},
SQLType::Bool => {
SQLValueGuess::Bool(SQLBoolValueGuess::Random)
},
SQLType::Varchar(max_size) => {
SQLValueGuess::String(max_size as usize, generate_string_guess(column))
},
SQLType::Char(max_size) => {
SQLValueGuess::String(max_size as usize, generate_string_guess(column))
}
}
}
pub fn generate_table_guessess(table: &SQLTable) -> Vec<SQLValueGuess> {
table.columns.iter()
.map(|column| generate_guess(column))
.collect()
}

View File

@ -18,6 +18,8 @@ pub enum SQLType {
Int,
Decimal,
Date,
Time,
Datetime,
Float,
Bool,
Char(u8),
@ -30,6 +32,8 @@ impl Display for SQLType {
SQLType::Int => write!(f, "INT"),
SQLType::Decimal => write!(f, "DECIMAL"),
SQLType::Date => write!(f, "DATE"),
SQLType::Time => write!(f, "TIME"),
SQLType::Datetime => write!(f, "DATETIME"),
SQLType::Float => write!(f, "FLOAT"),
SQLType::Bool => write!(f, "BOOL"),
SQLType::Char(size) => write!(f, "CHAR({})", size),

View File

@ -3,6 +3,7 @@ use app::App;
mod magicdraw_parser;
mod app;
mod components;
mod generate_sql;
// TODO: Make this work with enumation lookup tables
// TODO: Dark theme switch button