use crate::compile::expr::ToSqlExpr;
use crate::dialect::{Dialect, FunctionTransformer};
use datafusion_common::DFSchema;
use datafusion_expr::Expr;
use sqlparser::ast::{
BinaryOperator as SqlBinaryOperator, Expr as SqlExpr, Function as SqlFunction,
FunctionArg as SqlFunctionArg, FunctionArgExpr as SqlFunctionArgExpr, Ident as SqlIdent,
ObjectName as SqlObjectName, Value as SqlValue,
};
use std::sync::Arc;
use vegafusion_common::error::{Result, VegaFusionError};
fn process_epoch_ms_to_utc_timestamp_args(
args: &[Expr],
dialect: &Dialect,
schema: &DFSchema,
) -> Result<SqlExpr> {
if args.len() != 1 {
return Err(VegaFusionError::sql_not_supported(
"epoch_utc_timestamp requires exactly one argument",
));
}
args[0].to_sql(dialect, schema)
}
#[derive(Clone, Debug)]
pub struct EpochMsToUtcTimestampBigQueryTransformer;
impl EpochMsToUtcTimestampBigQueryTransformer {
pub fn new_dyn() -> Arc<dyn FunctionTransformer> {
Arc::new(Self)
}
}
impl FunctionTransformer for EpochMsToUtcTimestampBigQueryTransformer {
fn transform(&self, args: &[Expr], dialect: &Dialect, schema: &DFSchema) -> Result<SqlExpr> {
let ms_expr = process_epoch_ms_to_utc_timestamp_args(args, dialect, schema)?;
let ts_millis_expr = SqlExpr::Function(SqlFunction {
name: SqlObjectName(vec![SqlIdent {
value: "timestamp_millis".to_string(),
quote_style: None,
}]),
args: vec![SqlFunctionArg::Unnamed(SqlFunctionArgExpr::Expr(ms_expr))],
over: None,
distinct: false,
special: false,
});
Ok(ts_millis_expr)
}
}
#[derive(Clone, Debug)]
pub struct EpochMsToUtcTimestampDatabricksTransformer;
impl EpochMsToUtcTimestampDatabricksTransformer {
pub fn new_dyn() -> Arc<dyn FunctionTransformer> {
Arc::new(Self)
}
}
impl FunctionTransformer for EpochMsToUtcTimestampDatabricksTransformer {
fn transform(&self, args: &[Expr], dialect: &Dialect, schema: &DFSchema) -> Result<SqlExpr> {
let ms_expr = process_epoch_ms_to_utc_timestamp_args(args, dialect, schema)?;
let mod_1000_expr = SqlExpr::BinaryOp {
left: Box::new(ms_expr.clone()),
op: SqlBinaryOperator::Modulo,
right: Box::new(SqlExpr::Value(SqlValue::Number("1000".to_string(), false))),
};
let div_1000_expr = SqlExpr::BinaryOp {
left: Box::new(ms_expr),
op: SqlBinaryOperator::Divide,
right: Box::new(SqlExpr::Value(SqlValue::Number("1000".to_string(), false))),
};
let floor_expr = SqlExpr::Function(SqlFunction {
name: SqlObjectName(vec![SqlIdent {
value: "floor".to_string(),
quote_style: None,
}]),
args: vec![SqlFunctionArg::Unnamed(SqlFunctionArgExpr::Expr(
div_1000_expr,
))],
over: None,
distinct: false,
special: false,
});
let from_unix_time_expr = SqlExpr::Function(SqlFunction {
name: SqlObjectName(vec![SqlIdent {
value: "from_unixtime".to_string(),
quote_style: None,
}]),
args: vec![SqlFunctionArg::Unnamed(SqlFunctionArgExpr::Expr(
floor_expr,
))],
over: None,
distinct: false,
special: false,
});
let dateadd_expr = SqlExpr::Function(SqlFunction {
name: SqlObjectName(vec![SqlIdent {
value: "dateadd".to_string(),
quote_style: None,
}]),
args: vec![
SqlFunctionArg::Unnamed(SqlFunctionArgExpr::Expr(SqlExpr::Identifier(SqlIdent {
value: "millisecond".to_string(),
quote_style: None,
}))),
SqlFunctionArg::Unnamed(SqlFunctionArgExpr::Expr(mod_1000_expr)),
SqlFunctionArg::Unnamed(SqlFunctionArgExpr::Expr(from_unix_time_expr)),
],
over: None,
distinct: false,
special: false,
});
Ok(dateadd_expr)
}
}
#[derive(Clone, Debug)]
pub struct EpochMsToUtcTimestampDuckDbTransformer;
impl EpochMsToUtcTimestampDuckDbTransformer {
pub fn new_dyn() -> Arc<dyn FunctionTransformer> {
Arc::new(Self)
}
}
impl FunctionTransformer for EpochMsToUtcTimestampDuckDbTransformer {
fn transform(&self, args: &[Expr], dialect: &Dialect, schema: &DFSchema) -> Result<SqlExpr> {
let ms_expr = process_epoch_ms_to_utc_timestamp_args(args, dialect, schema)?;
let epoch_ms_expr = SqlExpr::Function(SqlFunction {
name: SqlObjectName(vec![SqlIdent {
value: "epoch_ms".to_string(),
quote_style: None,
}]),
args: vec![SqlFunctionArg::Unnamed(SqlFunctionArgExpr::Expr(ms_expr))],
over: None,
distinct: false,
special: false,
});
Ok(epoch_ms_expr)
}
}
#[derive(Clone, Debug)]
pub struct EpochMsToUtcTimestampPostgresTransformer;
impl EpochMsToUtcTimestampPostgresTransformer {
pub fn new_dyn() -> Arc<dyn FunctionTransformer> {
Arc::new(Self)
}
}
impl FunctionTransformer for EpochMsToUtcTimestampPostgresTransformer {
fn transform(&self, args: &[Expr], dialect: &Dialect, schema: &DFSchema) -> Result<SqlExpr> {
let ms_expr = process_epoch_ms_to_utc_timestamp_args(args, dialect, schema)?;
let mod_1000_expr = SqlExpr::BinaryOp {
left: Box::new(ms_expr.clone()),
op: SqlBinaryOperator::Modulo,
right: Box::new(SqlExpr::Value(SqlValue::Number("1000".to_string(), false))),
};
let div_1000_expr = SqlExpr::BinaryOp {
left: Box::new(ms_expr),
op: SqlBinaryOperator::Divide,
right: Box::new(SqlExpr::Value(SqlValue::Number("1000".to_string(), false))),
};
let floor_expr = SqlExpr::Function(SqlFunction {
name: SqlObjectName(vec![SqlIdent {
value: "floor".to_string(),
quote_style: None,
}]),
args: vec![SqlFunctionArg::Unnamed(SqlFunctionArgExpr::Expr(
div_1000_expr,
))],
over: None,
distinct: false,
special: false,
});
let to_timestamp_expr = SqlExpr::Function(SqlFunction {
name: SqlObjectName(vec![SqlIdent {
value: "to_timestamp".to_string(),
quote_style: None,
}]),
args: vec![SqlFunctionArg::Unnamed(SqlFunctionArgExpr::Expr(
floor_expr,
))],
over: None,
distinct: false,
special: false,
});
let interval_expr = SqlExpr::Interval {
value: Box::new(SqlExpr::Value(SqlValue::SingleQuotedString(
"1 millisecond".to_string(),
))),
leading_field: None,
leading_precision: None,
last_field: None,
fractional_seconds_precision: None,
};
let interval_mult_expr = SqlExpr::BinaryOp {
left: Box::new(interval_expr),
op: SqlBinaryOperator::Multiply,
right: Box::new(SqlExpr::Nested(Box::new(mod_1000_expr))),
};
let interval_addition_expr = SqlExpr::BinaryOp {
left: Box::new(to_timestamp_expr),
op: SqlBinaryOperator::Plus,
right: Box::new(interval_mult_expr),
};
Ok(interval_addition_expr)
}
}
#[derive(Clone, Debug)]
pub struct EpochMsToUtcTimestampSnowflakeTransformer;
impl EpochMsToUtcTimestampSnowflakeTransformer {
pub fn new_dyn() -> Arc<dyn FunctionTransformer> {
Arc::new(Self)
}
}
impl FunctionTransformer for EpochMsToUtcTimestampSnowflakeTransformer {
fn transform(&self, args: &[Expr], dialect: &Dialect, schema: &DFSchema) -> Result<SqlExpr> {
let ms_expr = process_epoch_ms_to_utc_timestamp_args(args, dialect, schema)?;
let to_timestamp_expr = SqlExpr::Function(SqlFunction {
name: SqlObjectName(vec![SqlIdent {
value: "to_timestamp_ntz".to_string(),
quote_style: None,
}]),
args: vec![
SqlFunctionArg::Unnamed(SqlFunctionArgExpr::Expr(ms_expr)),
SqlFunctionArg::Unnamed(SqlFunctionArgExpr::Expr(SqlExpr::Value(
SqlValue::Number("3".to_string(), false),
))),
],
over: None,
distinct: false,
special: false,
});
Ok(to_timestamp_expr)
}
}