Skip to content

Commit f4cba3b

Browse files
committed
feat(cubestore): Add XIRR aggregate function to Cube Store
1 parent d97273c commit f4cba3b

File tree

4 files changed

+670
-11
lines changed

4 files changed

+670
-11
lines changed

rust/cubestore/cubestore-sql-tests/src/tests.rs

Lines changed: 117 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -135,6 +135,7 @@ pub fn sql_tests(prefix: &str) -> Vec<(&'static str, TestFn)> {
135135
t("hyperloglog_postgres", hyperloglog_postgres),
136136
t("hyperloglog_snowflake", hyperloglog_snowflake),
137137
t("hyperloglog_databricks", hyperloglog_databricks),
138+
t("xirr", xirr),
138139
t(
139140
"aggregate_index_hll_databricks",
140141
aggregate_index_hll_databricks,
@@ -2809,6 +2810,122 @@ async fn hyperloglog_databricks(service: Box<dyn SqlClient>) {
28092810
assert_eq!(to_rows(&r), rows(&[(1, 4), (2, 4), (3, 20)]));
28102811
}
28112812

2813+
async fn xirr(service: Box<dyn SqlClient>) {
2814+
// XIRR result may differ between platforms, so we truncate the results with LEFT(_, 10).
2815+
let r = service
2816+
.exec_query(
2817+
r#"
2818+
SELECT LEFT(XIRR(payment, date)::varchar, 10) AS xirr
2819+
FROM (
2820+
SELECT '2014-01-01'::date AS date, -10000.0 AS payment
2821+
UNION ALL
2822+
SELECT '2014-03-01'::date AS date, 2750.0 AS payment
2823+
UNION ALL
2824+
SELECT '2014-10-30'::date AS date, 4250.0 AS payment
2825+
UNION ALL
2826+
SELECT '2015-02-15'::date AS date, 3250.0 AS payment
2827+
UNION ALL
2828+
SELECT '2015-04-01'::date AS date, 2750.0 AS payment
2829+
) AS "t"
2830+
"#,
2831+
)
2832+
.await
2833+
.unwrap();
2834+
2835+
assert_eq!(to_rows(&r), rows(&["0.37485859"]));
2836+
2837+
let r = service
2838+
.exec_query(
2839+
r#"
2840+
SELECT LEFT(XIRR(payment, date)::varchar, 10) AS xirr
2841+
FROM (
2842+
SELECT '2014-01-01'::date AS date, -10000.0 AS payment
2843+
) AS "t"
2844+
WHERE 0 = 1
2845+
"#,
2846+
)
2847+
.await
2848+
.unwrap_err();
2849+
assert_eq!(r.elide_backtrace(), CubeError::internal("Execution error: A result for XIRR couldn't be determined because the arguments are empty".to_owned()));
2850+
2851+
let r = service
2852+
.exec_query(
2853+
r#"
2854+
SELECT LEFT(XIRR(payment, date)::varchar, 10) AS xirr
2855+
FROM (
2856+
SELECT '2014-01-01'::date AS date, 10000.0 AS payment
2857+
) AS "t"
2858+
"#,
2859+
)
2860+
.await
2861+
.unwrap_err();
2862+
assert_eq!(r.elide_backtrace(), CubeError::internal("Execution error: The XIRR function couldn't find a solution".to_owned()));
2863+
2864+
// --- on_error testing ---
2865+
2866+
let r = service
2867+
.exec_query(
2868+
r#"
2869+
SELECT LEFT(XIRR(payment, date, 0, NULL::double)::varchar, 10) AS xirr
2870+
FROM (
2871+
SELECT '2014-01-01'::date AS date, -10000.0 AS payment
2872+
UNION ALL
2873+
SELECT '2014-03-01'::date AS date, 2750.0 AS payment
2874+
UNION ALL
2875+
SELECT '2014-10-30'::date AS date, 4250.0 AS payment
2876+
UNION ALL
2877+
SELECT '2015-02-15'::date AS date, 3250.0 AS payment
2878+
UNION ALL
2879+
SELECT '2015-04-01'::date AS date, 2750.0 AS payment
2880+
) AS "t"
2881+
"#,
2882+
)
2883+
.await
2884+
.unwrap();
2885+
2886+
assert_eq!(to_rows(&r), rows(&["0.37485859"]));
2887+
2888+
let r = service
2889+
.exec_query(
2890+
r#"
2891+
SELECT LEFT(XIRR(payment, date, 0, NULL::double)::varchar, 10) AS xirr
2892+
FROM (
2893+
SELECT '2014-01-01'::date AS date, -10000.0 AS payment
2894+
) AS "t"
2895+
WHERE 0 = 1
2896+
"#,
2897+
)
2898+
.await
2899+
.unwrap_err();
2900+
assert_eq!(r.elide_backtrace(), CubeError::internal("Execution error: A result for XIRR couldn't be determined because the arguments are empty".to_owned()));
2901+
2902+
let r = service
2903+
.exec_query(
2904+
r#"
2905+
SELECT LEFT(XIRR(payment, date, 0, NULL::double)::varchar, 10) AS xirr
2906+
FROM (
2907+
SELECT '2014-01-01'::date AS date, 10000.0 AS payment
2908+
) AS "t"
2909+
"#,
2910+
)
2911+
.await
2912+
.unwrap();
2913+
assert_eq!(to_rows(&r), rows(&[()]));
2914+
2915+
let r = service
2916+
.exec_query(
2917+
r#"
2918+
SELECT LEFT(XIRR(payment, date, 0, 12345)::varchar, 10) AS xirr
2919+
FROM (
2920+
SELECT '2014-01-01'::date AS date, 10000.0 AS payment
2921+
) AS "t"
2922+
"#,
2923+
)
2924+
.await
2925+
.unwrap();
2926+
assert_eq!(to_rows(&r), rows(&["12345.0"]));
2927+
}
2928+
28122929
async fn aggregate_index_hll_databricks(service: Box<dyn SqlClient>) {
28132930
service.exec_query("CREATE SCHEMA s").await.unwrap();
28142931
service

rust/cubestore/cubestore/src/queryplanner/mod.rs

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ pub mod trace_data_loaded;
1919
use rewrite_inlist_literals::RewriteInListLiterals;
2020
use serialized_plan::PreSerializedPlan;
2121
pub use topk::MIN_TOPK_STREAM_ROWS;
22+
use udf_xirr::XIRR_UDAF_NAME;
2223
use udfs::{registerable_aggregate_udfs, registerable_scalar_udfs};
2324
mod filter_by_key_range;
2425
mod flatten_union;
@@ -30,6 +31,7 @@ mod rewrite_inlist_literals;
3031
mod rolling;
3132
#[cfg(test)]
3233
mod test_utils;
34+
pub mod udf_xirr;
3335
pub mod udfs;
3436

3537
use crate::cachestore::CacheStore;
@@ -557,8 +559,8 @@ impl ContextProvider for MetaStoreSchemaProvider {
557559
}
558560

559561
fn udaf_names(&self) -> Vec<String> {
560-
// TODO upgrade DF: We shouldn't need "merge" here because we registered it (see get_aggregate_meta).
561-
let mut res = vec!["merge".to_string()];
562+
// TODO upgrade DF: We shouldn't need "merge" or "xirr" here because we registered it (see get_aggregate_meta).
563+
let mut res = vec!["merge".to_string(), XIRR_UDAF_NAME.to_string()];
562564
res.extend(self.session_state.aggregate_functions().keys().cloned());
563565
res
564566
}

0 commit comments

Comments
 (0)