diff --git a/src/report/sqlite/models.rs b/src/report/sqlite/models.rs index bff17d2..64c957e 100644 --- a/src/report/sqlite/models.rs +++ b/src/report/sqlite/models.rs @@ -71,6 +71,11 @@ pub trait Insertable { I: Iterator + ExactSizeIterator, Self: 'a, { + let model_count = models.len(); + if model_count == 0 { + return Ok(()); + } + let var_limit = conn.limit(rusqlite::limits::Limit::SQLITE_LIMIT_VARIABLE_NUMBER) as usize; // If each model takes up `FIELD_COUNT` variables, we can fit `var_limit / // FIELD_COUNT` complete models in each "page" of our query @@ -78,36 +83,37 @@ pub trait Insertable { // Integer division tells us how many full pages there are. If there is a // non-zero remainder, there is one final incomplete page. - let model_count = models.len(); let page_count = match (model_count / page_size, model_count % page_size) { (page_count, 0) => page_count, (page_count, _) => page_count + 1, }; - let (mut query, mut previous_page_size) = (String::new(), 0); + // Helper function for creating query strings for differently-sized pages + let build_query_for_page = |page_size| { + let mut query = format!(" {},", Self::INSERT_PLACEHOLDER).repeat(page_size); + query.insert_str(0, Self::INSERT_QUERY_PRELUDE); + // Remove trailing comma + query.pop(); + query + }; + + let first_page_size = std::cmp::min(model_count, page_size); + let mut stmt = conn.prepare_cached(build_query_for_page(first_page_size).as_str())?; + for _ in 0..page_count { // If there are fewer than `page_size` pages left, the iterator will just take // everything. let page_iter = models.by_ref().take(page_size); - // We can reuse our query string if the current page is the same size as the - // last one. If not, we have to rebuild the query string. let current_page_size = page_iter.len(); - if current_page_size != previous_page_size { - query = format!(" {},", Self::INSERT_PLACEHOLDER).repeat(current_page_size); - query.insert_str(0, Self::INSERT_QUERY_PRELUDE); - // Remove trailing comma - query.pop(); - previous_page_size = current_page_size; + if current_page_size != first_page_size { + stmt = conn.prepare_cached(build_query_for_page(current_page_size).as_str())?; } - let mut stmt = conn.prepare_cached(query.as_str())?; let params = page_iter.flat_map(|model| model.param_bindings()); stmt.execute(rusqlite::params_from_iter(params))?; } - conn.flush_prepared_statement_cache(); - Ok(()) } }