use serde::{Deserialize, Serialize};
mod detector;
pub use fraiseql_db::{introspector::DatabaseIntrospector, types::DatabaseType};
pub use self::detector::FactTableDetector;
#[cfg(test)]
mod tests;
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub struct FactTableMetadata {
pub table_name: String,
pub measures: Vec<MeasureColumn>,
pub dimensions: DimensionColumn,
pub denormalized_filters: Vec<FilterColumn>,
#[serde(default)]
pub calendar_dimensions: Vec<CalendarDimension>,
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub struct MeasureColumn {
pub name: String,
pub sql_type: SqlType,
pub nullable: bool,
}
#[non_exhaustive]
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub enum SqlType {
Int,
BigInt,
Decimal,
Float,
Jsonb,
Json,
Text,
Uuid,
Timestamp,
Date,
Boolean,
Other(String),
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub struct DimensionColumn {
pub name: String,
pub paths: Vec<DimensionPath>,
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub struct DimensionPath {
pub name: String,
pub json_path: String,
pub data_type: String,
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub struct CalendarDimension {
pub source_column: String,
pub granularities: Vec<CalendarGranularity>,
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub struct CalendarGranularity {
pub column_name: String,
pub buckets: Vec<CalendarBucket>,
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub struct CalendarBucket {
pub json_key: String,
pub bucket_type: crate::compiler::aggregate_types::TemporalBucket,
pub data_type: String,
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub struct FilterColumn {
pub name: String,
pub sql_type: SqlType,
pub indexed: bool,
}
#[non_exhaustive]
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, Default)]
pub enum AggregationStrategy {
#[serde(rename = "incremental")]
#[default]
Incremental,
#[serde(rename = "accumulating_snapshot")]
AccumulatingSnapshot,
#[serde(rename = "periodic_snapshot")]
PeriodicSnapshot,
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub struct FactTableDeclaration {
pub name: String,
pub measures: Vec<String>,
pub dimensions: Vec<String>,
pub primary_key: String,
pub metadata: Option<FactTableDeclarationMetadata>,
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub struct FactTableDeclarationMetadata {
#[serde(default)]
pub aggregation_strategy: AggregationStrategy,
pub grain: Vec<String>,
pub snapshot_date_column: Option<String>,
#[serde(default)]
pub is_slowly_changing_dimension: bool,
}
impl SqlType {
pub fn from_str_postgres(type_name: &str) -> Self {
match type_name.to_lowercase().as_str() {
"smallint" | "int" | "integer" | "int2" | "int4" => Self::Int,
"bigint" | "int8" => Self::BigInt,
"decimal" | "numeric" => Self::Decimal,
"real" | "float" | "double precision" | "float4" | "float8" => Self::Float,
"jsonb" => Self::Jsonb,
"json" => Self::Json,
"text" | "varchar" | "character varying" | "char" | "character" => Self::Text,
"uuid" => Self::Uuid,
"timestamp"
| "timestamptz"
| "timestamp with time zone"
| "timestamp without time zone" => Self::Timestamp,
"date" => Self::Date,
"boolean" | "bool" => Self::Boolean,
other => Self::Other(other.to_string()),
}
}
pub fn from_str_mysql(type_name: &str) -> Self {
match type_name.to_lowercase().as_str() {
"tinyint" | "smallint" | "mediumint" | "int" | "integer" => Self::Int,
"bigint" => Self::BigInt,
"decimal" | "numeric" => Self::Decimal,
"float" | "double" | "real" => Self::Float,
"json" => Self::Json,
"text" | "varchar" | "char" | "tinytext" | "mediumtext" | "longtext" => Self::Text,
"timestamp" | "datetime" => Self::Timestamp,
"date" => Self::Date,
"boolean" | "bool" | "tinyint(1)" => Self::Boolean,
other => Self::Other(other.to_string()),
}
}
pub fn from_str_sqlite(type_name: &str) -> Self {
match type_name.to_lowercase().as_str() {
"integer" | "int" => Self::BigInt, "real" | "double" | "float" => Self::Float,
"numeric" | "decimal" => Self::Decimal,
"text" | "varchar" | "char" => Self::Text,
"blob" => Self::Other("BLOB".to_string()),
other => Self::Other(other.to_string()),
}
}
pub fn from_str_sqlserver(type_name: &str) -> Self {
match type_name.to_lowercase().as_str() {
"tinyint" | "smallint" | "int" => Self::Int,
"bigint" => Self::BigInt,
"decimal" | "numeric" | "money" | "smallmoney" => Self::Decimal,
"float" | "real" => Self::Float,
"nvarchar" | "varchar" | "char" | "nchar" | "text" | "ntext" => Self::Text,
"uniqueidentifier" => Self::Uuid,
"datetime" | "datetime2" | "smalldatetime" | "datetimeoffset" => Self::Timestamp,
"date" => Self::Date,
"bit" => Self::Boolean,
other => Self::Other(other.to_string()),
}
}
}