diff --git a/sqlite-compress/src/dict/train.rs b/sqlite-compress/src/dict/train.rs index 2bdaad5..1dda3d6 100644 --- a/sqlite-compress/src/dict/train.rs +++ b/sqlite-compress/src/dict/train.rs @@ -106,23 +106,16 @@ pub fn train_by_column( let state = ExtensionState::from_db(unsafe { connection.sqlite_handle() })?; - let (enough, available) = - has_enough_samples(&state, &table_name, &column_name, column.min_samples)?; + let available = has_enough_samples(&state, &table_name, &column_name, column.min_samples)?; - if !enough { - return Ok(None); - } - - if !has_retrain_required( + has_retrain_required( &state, &dict_store, key.table(), key.column(), available, column.retrain_growth, - )? { - return Ok(None); - } + )?; let dictionary = build_dictionary( &state, @@ -194,7 +187,7 @@ fn has_enough_samples>( table_name: &str, column_name: &str, min_samples: usize, -) -> Result<(bool, i64), SetupError> { +) -> Result { let connection = connection.as_ref(); let rows = connection @@ -209,7 +202,13 @@ fn has_enough_samples>( let count = first_value(&rows).and_then(SqlValue::as_i64).unwrap_or(0); - Ok((count >= min_samples as i64, count)) + if count >= min_samples as i64 { + return Ok(count); + } + + Err(SetupError::InvalidConfig( + "not enough samples available: {count} < {min_samples}", + )) } /// Check if the retrain is required. @@ -224,7 +223,7 @@ fn has_retrain_required>( column_name: &str, available: i64, retrain_growth: usize, -) -> Result { +) -> Result<(), SetupError> { let connection = connection.as_ref(); let rows = connection @@ -245,11 +244,13 @@ fn has_retrain_required>( let last = first_value(&rows).and_then(SqlValue::as_i64).unwrap_or(0); - if last < 0 { - return Ok(true); + if last < 0 || available >= last + retrain_growth as i64 { + return Ok(()); } - Ok(available >= last + retrain_growth as i64) + Err(SetupError::InvalidConfig( + "not enough samples available: {available} < {last} + {retrain_growth}", + )) } /// Samples fetched per random query. Scales with `max_samples` so we keep diff --git a/sqlite-compress/tests/dict_train.rs b/sqlite-compress/tests/dict_train.rs index 1cfbeb7..6d46a43 100644 --- a/sqlite-compress/tests/dict_train.rs +++ b/sqlite-compress/tests/dict_train.rs @@ -1,6 +1,6 @@ use sqlite_compress::{ DEFAULT_LEVEL, DEFAULT_RETRAIN_GROWTH, ExtensionState, Header, SchemaName, SetupColumn, - SetupConfig, SetupTable, TableName, TrainOptions, TrainProgress, setup, train_all, + SetupConfig, SetupError, SetupTable, TableName, TrainOptions, TrainProgress, setup, train_all, train_by_column, }; @@ -247,8 +247,11 @@ fn train_skips_column_without_enough_samples() { setup(&db, &config).unwrap(); - let dict_ids = train_all(&db, &config, 1024, TrainOptions::default()).unwrap(); - assert_eq!(dict_ids.len(), 1); + let err = train_all(&db, &config, 1024, TrainOptions::default()).unwrap_err(); + assert!(matches!( + err, + SetupError::InvalidConfig("not enough samples available: {count} < {min_samples}") + )); let rows = state .as_ref() @@ -460,8 +463,13 @@ fn train_skips_when_growth_below_threshold() { // +100 samples << RETRAIN_GROWTH (5000) insert_samples(&state, 100); - let second = train_all(&db, &config, 1024, TrainOptions::default()).unwrap(); - assert!(second.is_empty()); + let err = train_all(&db, &config, 1024, TrainOptions::default()).unwrap_err(); + assert!(matches!( + err, + SetupError::InvalidConfig( + "not enough samples available: {available} < {last} + {retrain_growth}" + ) + )); let rows = state .as_ref()