Skip to content

Commit

Permalink
Rework SQLite type mapping
Browse files Browse the repository at this point in the history
  • Loading branch information
tyt2y3 authored and billy1624 committed Jan 23, 2024
1 parent b46dbd6 commit 699711f
Show file tree
Hide file tree
Showing 4 changed files with 110 additions and 196 deletions.
4 changes: 2 additions & 2 deletions src/sqlite/def/column.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
use super::{DefaultType, Type};
use super::{parse_type, DefaultType, Type};
use sea_query::{
foreign_key::ForeignKeyAction as SeaQueryForeignKeyAction, Alias, Index, IndexCreateStatement,
};
Expand Down Expand Up @@ -28,7 +28,7 @@ impl ColumnInfo {
Ok(ColumnInfo {
cid: row.get(0),
name: row.get(1),
r#type: Type::to_type(row.get(2))?,
r#type: parse_type(row.get(2))?,
not_null: col_not_null != 0,
default_value: if default_value == "NULL" {
DefaultType::Null
Expand Down
5 changes: 2 additions & 3 deletions src/sqlite/def/table.rs
Original file line number Diff line number Diff line change
Expand Up @@ -224,7 +224,8 @@ impl TableDef {
new_table.table(Alias::new(&self.name));

self.columns.iter().for_each(|column_info| {
let mut new_column = ColumnDef::new(Alias::new(&column_info.name));
let mut new_column =
ColumnDef::new_with_type(Alias::new(&column_info.name), column_info.r#type.clone());
if column_info.not_null {
new_column.not_null();
}
Expand All @@ -235,8 +236,6 @@ impl TableDef {
primary_keys.push(column_info.name.clone());
}

column_info.r#type.write_type(&mut new_column);

match &column_info.default_value {
DefaultType::Integer(integer_value) => {
new_column.default(Value::Int(Some(*integer_value)));
Expand Down
205 changes: 49 additions & 156 deletions src/sqlite/def/types.rs
Original file line number Diff line number Diff line change
@@ -1,166 +1,59 @@
use sea_query::ColumnDef;
use sea_query::{BlobSize, ColumnType};
use std::num::ParseIntError;

/// A list of the offical SQLite types as outline at the official [SQLite Docs](https://www.sqlite.org/datatype3.html)
#[derive(Debug, PartialEq, Eq, Clone, Copy)]
pub enum Type {
Int,
Integer,
TinyInt,
SmallInt,
MediumInt,
BigInt,
UnsignedBigInt,
Int2,
Int8,
Character { length: u8 },
VarChar { length: u8 },
VaryingCharacter { length: u8 },
Nchar { length: u8 },
NativeCharacter { length: u8 },
NvarChar { length: u8 },
Text,
Clob,
Blob, //No datatype specified
Real,
Double,
DoublePrecision,
Float,
Numeric,
Decimal { integral: u8, fractional: u8 },
Boolean,
Date,
DateTime,
Timestamp,
}

impl Type {
/// Maps a string type from an `SqliteRow` into a [Type]
pub fn to_type(data_type: &str) -> Result<Type, ParseIntError> {
let data_type = data_type.to_uppercase();

let split_type: Vec<&str> = data_type.split('(').collect();
let type_result = match split_type[0] {
"INT" => Type::Int,
"INTEGER" => Type::Integer,
"TINY INT" | "TINYINT" => Type::TinyInt,
"SMALL INT" | "SMALLINT" => Type::SmallInt,
"MEDIUM INT" | "MEDIUMINT" => Type::MediumInt,
"BIG INT" | "BIGINT" => Type::BigInt,
"UNSIGNED INT" | "UNSIGNEDBIGINT" => Type::UnsignedBigInt,
"INT2" => Type::Int2,
"INT8" => Type::Int8,
"TEXT" => Type::Text,
"CLOB" => Type::Clob,
"BLOB" => Type::Blob,
"REAL" => Type::Real,
"DOUBLE" => Type::Double,
"DOUBLE PRECISION" => Type::DoublePrecision,
"FLOAT" => Type::Float,
"NUMERIC" => Type::Numeric,
"DECIMAL" => {
let decimals = split_type[1].chars().collect::<Vec<_>>();

let integral = decimals[0].to_string().parse::<u8>()?;
let fractional = decimals[2].to_string().parse::<u8>()?;
pub type Type = ColumnType;

Type::Decimal {
integral,
fractional,
pub fn parse_type(data_type: &str) -> Result<ColumnType, ParseIntError> {
let mut type_name = data_type;
let mut parts: Vec<u32> = Vec::new();
if let Some((prefix, suffix)) = data_type.split_once('(') {
if let Some(suffix) = suffix.strip_suffix(')') {
type_name = prefix;
for part in suffix.split(",") {
if let Ok(part) = part.trim().parse() {
parts.push(part);
} else {
break;
}
}
"BOOLEAN" => Type::Boolean,
"DATE" => Type::Date,
"DATETIME" => Type::DateTime,
"TIMESTAMP" => Type::Timestamp,
_ => Type::variable_types(&split_type)?,
};

Ok(type_result)
}

/// Write a [Type] to a [ColumnDef]
pub fn write_type(&self, column_def: &mut ColumnDef) {
match self {
Self::Int | Self::Integer | Self::MediumInt | Self::Int2 | Self::Int8 => {
column_def.integer();
}
Self::TinyInt => {
column_def.tiny_integer();
}
Self::SmallInt => {
column_def.small_integer();
}
Self::BigInt | Self::UnsignedBigInt => {
column_def.big_integer();
}
Self::Character { .. }
| Self::VarChar { .. }
| Self::VaryingCharacter { .. }
| Self::Nchar { .. }
| Self::NativeCharacter { .. }
| Self::NvarChar { .. }
| Self::Text
| Self::Clob => {
column_def.string();
}
Self::Blob => {
column_def.binary();
}
Self::Real | Self::Double | Self::DoublePrecision | Self::Float | Self::Numeric => {
column_def.double();
}
Self::Decimal {
integral,
fractional,
} => {
column_def.decimal_len((*integral) as u32, (*fractional) as u32);
}
Self::Boolean => {
column_def.boolean();
}
Self::Date => {
column_def.date();
}
Self::DateTime => {
column_def.date_time();
}
Self::Timestamp => {
column_def.timestamp();
}
}
}

#[allow(dead_code)]
fn concat_type(&self, type_name: &str, length: &u8) -> String {
let mut value = String::default();
value.push_str(type_name);
value.push('(');
value.push_str(&length.to_string());
value.push(')');

value
}

fn variable_types(split_type: &[&str]) -> Result<Type, ParseIntError> {
let length = if !split_type.len() == 1 {
let maybe_size = split_type[1].replace(')', "");
maybe_size.parse::<u8>()?
Ok(match type_name.to_lowercase().as_str() {
"char" => ColumnType::Char(parts.into_iter().next()),
"varchar" => ColumnType::String(parts.into_iter().next()),
"text" => ColumnType::Text,
"tinyint" => ColumnType::TinyInteger,
"smallint" => ColumnType::SmallInteger,
"integer" => ColumnType::Integer,
"bigint" => ColumnType::BigInteger,
"float" => ColumnType::Float,
"double" => ColumnType::Double,
"decimal" => ColumnType::Decimal(if parts.len() == 2 {
Some((parts[0], parts[1]))
} else {
255_u8
};

let type_result = match split_type[0] {
"VARCHAR" => Type::VarChar { length },
"CHARACTER" => Type::Character { length },
"VARYING CHARACTER" => Type::VaryingCharacter { length },
"NCHAR" => Type::Nchar { length },
"NATIVE CHARACTER" => Type::NativeCharacter { length },
"NVARCHAR" => Type::NvarChar { length },
_ => Type::Blob,
};
Ok(type_result)
}
None
}),
"datetime_text" => ColumnType::DateTime,
"timestamp_text" => ColumnType::Timestamp,
"timestamp_with_timezone_text" => ColumnType::TimestampWithTimeZone,
"time_text" => ColumnType::Time,
"date_text" => ColumnType::Date,
"tinyblob" => ColumnType::Binary(BlobSize::Tiny),
"mediumblob" => ColumnType::Binary(BlobSize::Medium),
"longblob" => ColumnType::Binary(BlobSize::Long),
"blob" => ColumnType::Binary(BlobSize::Blob(parts.into_iter().next())),
"varbinary_blob" if parts.len() == 1 => ColumnType::VarBinary(parts[0]),
"boolean" => ColumnType::Boolean,
"money" => ColumnType::Money(if parts.len() == 2 {
Some((parts[0], parts[1]))
} else {
None
}),
"json_text" => ColumnType::Json,
"jsonb_text" => ColumnType::JsonBinary,
"uuid_text" => ColumnType::Uuid,
_ => ColumnType::custom(data_type),
})
}

/// The default types for an SQLite `dflt_value`
Expand All @@ -170,6 +63,6 @@ pub enum DefaultType {
Float(f32),
String(String),
Null,
Unspecified, //FIXME For other types
Unspecified,
CurrentTimestamp,
}
Loading

0 comments on commit 699711f

Please sign in to comment.