use super::DefaultType;
use crate::{rusqlite_types::RusqliteRow, sqlite::def::ColumnVisibility};
use sea_query::{
Alias, ColumnType, Index, IndexCreateStatement,
foreign_key::ForeignKeyAction as SeaQueryForeignKeyAction,
};
use std::num::ParseIntError;
#[derive(Debug, PartialEq, Clone)]
pub struct ColumnInfo {
pub cid: i64,
pub name: String,
pub r#type: ColumnType,
pub not_null: bool,
pub default_value: DefaultType,
pub primary_key: bool,
pub hidden: ColumnVisibility,
}
#[cfg(feature = "rusqlite")]
impl ColumnInfo {
pub fn to_column_def(row: RusqliteRow) -> Result<ColumnInfo, ParseIntError> {
use crate::rusqlite_types::Row;
let row = row.sqlite();
let col_not_null: i8 = row.get(3);
let is_pk: i8 = row.get(5);
let default_value: Option<String> = row.get(4);
let default_value = default_value.unwrap_or_default();
let hidden: i8 = row.get(6);
Ok(ColumnInfo {
cid: row.get(0),
name: row.get(1),
r#type: super::parse_type(row.get(2))?,
not_null: col_not_null != 0 || is_pk == 1,
default_value: if default_value == "NULL" {
DefaultType::Null
} else if default_value.is_empty() {
DefaultType::Unspecified
} else {
let value = default_value.to_owned().replace('\'', "");
if let Ok(is_int) = value.parse::<i64>() {
DefaultType::Integer(is_int)
} else if let Ok(is_float) = value.parse::<f32>() {
DefaultType::Float(is_float)
} else if value == "CURRENT_TIMESTAMP" {
DefaultType::CurrentTimestamp
} else {
DefaultType::String(value)
}
},
primary_key: is_pk != 0,
hidden: ColumnVisibility::from_hidden(hidden),
})
}
#[inline]
pub fn is_hidden(&self) -> bool {
self.hidden == ColumnVisibility::HiddenVirtual
}
}
#[cfg(all(not(feature = "rusqlite"), not(feature = "sqlx-sqlite")))]
impl ColumnInfo {
pub fn to_column_def(_: RusqliteRow) -> Result<ColumnInfo, ParseIntError> {
unimplemented!()
}
#[inline]
pub fn is_hidden(&self) -> bool {
unimplemented!()
}
}
#[derive(Debug, Default, Clone, PartialEq)]
pub struct IndexInfo {
pub r#type: String,
pub index_name: String,
pub table_name: String,
pub unique: bool,
pub origin: String,
pub partial: i64,
pub columns: Vec<String>,
}
impl IndexInfo {
pub fn write(&self) -> IndexCreateStatement {
let mut new_index = Index::create();
if self.origin.as_str() == "c" {
new_index.name(&self.index_name);
}
new_index.table(Alias::new(&self.table_name));
if self.unique {
new_index.unique();
}
self.columns.iter().for_each(|column| {
new_index.col(Alias::new(column));
});
new_index
}
}
#[allow(dead_code)]
#[derive(Debug, Default, Clone)]
pub(crate) struct PartialIndexInfo {
pub(crate) seq: i64,
pub(crate) name: String,
pub(crate) unique: bool,
pub(crate) origin: String,
pub(crate) partial: i64,
}
#[cfg(feature = "rusqlite")]
impl From<RusqliteRow> for PartialIndexInfo {
fn from(row: RusqliteRow) -> Self {
use crate::rusqlite_types::Row;
let row = row.sqlite();
let is_unique: i8 = row.get(2);
Self {
seq: row.get(0),
name: row.get(1),
unique: is_unique != 0,
origin: row.get(3),
partial: row.get(4),
}
}
}
#[cfg(all(not(feature = "rusqlite"), not(feature = "sqlx-sqlite")))]
impl From<RusqliteRow> for PartialIndexInfo {
fn from(_: RusqliteRow) -> Self {
Self::default()
}
}
#[allow(dead_code)]
#[derive(Debug, Default, Clone)]
pub(crate) struct IndexedColumns {
pub(crate) r#type: String,
pub(crate) name: String,
pub(crate) table: String,
pub(crate) root_page: i64,
pub(crate) indexed_columns: Vec<String>,
}
#[cfg(feature = "rusqlite")]
impl From<(RusqliteRow, Vec<RusqliteRow>)> for IndexedColumns {
fn from((row, rows): (RusqliteRow, Vec<RusqliteRow>)) -> Self {
use crate::rusqlite_types::Row;
let row = row.sqlite();
let columns_to_index = rows
.into_iter()
.map(|r| r.sqlite().get(2))
.collect::<Vec<String>>();
Self {
r#type: row.get(0),
name: row.get(1),
table: row.get(2),
root_page: row.get(3),
indexed_columns: columns_to_index,
}
}
}
#[cfg(all(not(feature = "rusqlite"), not(feature = "sqlx-sqlite")))]
impl From<(RusqliteRow, Vec<RusqliteRow>)> for IndexedColumns {
fn from(_: (RusqliteRow, Vec<RusqliteRow>)) -> Self {
Self::default()
}
}
#[allow(dead_code)]
#[derive(Debug, Default, Clone)]
pub(crate) struct PrimaryKeyAutoincrement(pub(crate) u8);
#[cfg(feature = "rusqlite")]
impl From<RusqliteRow> for PrimaryKeyAutoincrement {
fn from(row: RusqliteRow) -> Self {
use crate::rusqlite_types::Row;
Self(row.sqlite().get(0))
}
}
#[cfg(all(not(feature = "rusqlite"), not(feature = "sqlx-sqlite")))]
impl From<RusqliteRow> for PrimaryKeyAutoincrement {
fn from(_: RusqliteRow) -> Self {
Self::default()
}
}
#[derive(Debug, Default, Clone, PartialEq)]
#[non_exhaustive]
pub struct ForeignKeysInfo {
pub id: i64,
pub seq: i64,
pub table: String,
pub from: Vec<String>,
pub to: Vec<String>,
pub on_update: ForeignKeyAction,
pub on_delete: ForeignKeyAction,
pub r#match: MatchAction,
}
#[cfg(feature = "rusqlite")]
impl From<RusqliteRow> for ForeignKeysInfo {
fn from(row: RusqliteRow) -> Self {
use crate::rusqlite_types::Row;
let row = row.sqlite();
Self {
id: row.get(0),
seq: row.get(1),
table: row.get(2),
from: vec![row.get(3)],
to: vec![row.get(4)],
on_update: {
let op: String = row.get(5);
op.as_str().into()
},
on_delete: {
let op: String = row.get(6);
op.as_str().into()
},
r#match: {
let op: String = row.get(7);
op.as_str().into()
},
}
}
}
#[cfg(all(not(feature = "rusqlite"), not(feature = "sqlx-sqlite")))]
impl From<RusqliteRow> for ForeignKeysInfo {
fn from(_: RusqliteRow) -> Self {
Self::default()
}
}
#[derive(Debug, Default, PartialEq, Eq, Clone)]
pub enum ForeignKeyAction {
#[default]
NoAction,
Restrict,
SetNull,
SetDefault,
Cascade,
}
impl From<&str> for ForeignKeyAction {
fn from(action: &str) -> Self {
match action {
"NO ACTION" => Self::NoAction,
"RESTRICT" => Self::Restrict,
"SET NULL" => Self::SetNull,
"SET DEFAULT" => Self::SetDefault,
"CASCADE" => Self::Cascade,
_ => Self::NoAction,
}
}
}
impl ForeignKeyAction {
pub(crate) fn to_seaquery_foreign_key_action(&self) -> SeaQueryForeignKeyAction {
match self {
Self::NoAction => SeaQueryForeignKeyAction::NoAction,
Self::Restrict => SeaQueryForeignKeyAction::Restrict,
Self::SetNull => SeaQueryForeignKeyAction::SetNull,
Self::SetDefault => SeaQueryForeignKeyAction::SetDefault,
Self::Cascade => SeaQueryForeignKeyAction::Cascade,
}
}
}
#[derive(Debug, Default, PartialEq, Eq, Clone)]
pub enum MatchAction {
Simple,
Partial,
Full,
#[default]
None,
}
impl From<&str> for MatchAction {
fn from(action: &str) -> Self {
match action {
"MATCH SIMPLE" => Self::Simple,
"MATCH PARTIAL" => Self::Partial,
"MATCH FULL" => Self::Full,
"MATCH NONE" => Self::None,
_ => Self::None,
}
}
}