diff --git a/NATIVE.md b/NATIVE.md index fcd04ab..a0ca4ba 100644 --- a/NATIVE.md +++ b/NATIVE.md @@ -94,8 +94,9 @@ INTO ... ` and before `SELECT`. Settings travel as URL parameters: `max_insert_block_size = 1`, `min_insert_block_size_rows = 1`, `min_insert_block_size_bytes = 1`, `input_format_parallel_parsing = 0`, `max_block_size = 1`, `max_threads = 1`, `max_insert_threads = 1`, -`async_insert = 0`, and `max_query_size` set to the statement's byte length -plus 64. The server reads that many bytes before it parses, so the first row +`async_insert = 0`, `optimize_and_compare_chain = 0`, and `max_query_size` +set to the statement's byte length plus 64. The server reads that many bytes +before it parses, so the first row after the statement is padding, `tic = 0`, at least 128 bytes, and is filtered out. A statement error surfaces on the response only after the body closes, so the driver reads the response concurrently and treats an early response as diff --git a/native/src/resident/mod.rs b/native/src/resident/mod.rs index b885990..19e5263 100644 --- a/native/src/resident/mod.rs +++ b/native/src/resident/mod.rs @@ -15,7 +15,7 @@ pub mod settings; pub mod stream; pub mod url; -pub use settings::resident_settings; +pub use settings::{ANALYSIS_SETTINGS, resident_settings}; pub use stream::{ CLOSE_TIMEOUT, Endpoint, FIRST_TIC_TIMEOUT, FORMAT_CLAUSE, Resident, ResidentError, TIC_TIMEOUT, }; diff --git a/native/src/resident/settings.rs b/native/src/resident/settings.rs index cc7b809..5020076 100644 --- a/native/src/resident/settings.rs +++ b/native/src/resident/settings.rs @@ -8,6 +8,11 @@ /// transport appends to the statement and the newline after it. pub const QUERY_SIZE_SLACK: usize = 64; +/// Settings that change how the server analyses a tic statement. A +/// resident sends them, and so does a tic statement issued on its own, so +/// both are analysed the same way. +pub const ANALYSIS_SETTINGS: [(&str, &str); 1] = [("optimize_and_compare_chain", "0")]; + /// Every setting a resident statement needs, for a statement of /// `statement_bytes` bytes. /// @@ -16,7 +21,7 @@ pub const QUERY_SIZE_SLACK: usize = 64; /// `max_query_size` covers the statement text, which leads the request /// body. pub fn resident_settings(statement_bytes: usize) -> Vec<(&'static str, String)> { - vec![ + let mut settings = vec![ ("max_insert_block_size", "1".to_owned()), ("min_insert_block_size_rows", "1".to_owned()), ("min_insert_block_size_bytes", "1".to_owned()), @@ -31,7 +36,13 @@ pub fn resident_settings(statement_bytes: usize) -> Vec<(&'static str, String)> ), ("max_ast_elements", "4000000".to_owned()), ("max_expanded_ast_elements", "40000000".to_owned()), - ] + ]; + settings.extend( + ANALYSIS_SETTINGS + .iter() + .map(|(name, value)| (*name, (*value).to_owned())), + ); + settings } #[cfg(test)] diff --git a/native/src/sql/sim/tick.rs b/native/src/sql/sim/tick.rs index 1f7da74..277adf6 100644 --- a/native/src/sql/sim/tick.rs +++ b/native/src/sql/sim/tick.rs @@ -14,6 +14,7 @@ //! The transform is the same whether it runs inside the resident statement //! or over a single row, so a test issues exactly what a session runs. +use crate::resident::ANALYSIS_SETTINGS; use crate::sql::Statement; use super::{Tic, game, hud, lights, mobj, player, spec, specials, state_columns}; @@ -178,6 +179,7 @@ fn stage1_over(db: &str, rows: &[Input], cut: Option<&'static str>) -> Statement ) )) .with(&PARSE_SETTINGS) + .with(&ANALYSIS_SETTINGS) } /// Building the first statement cut short, for measuring what each of its @@ -328,7 +330,8 @@ pub fn run_statement(db: &str, rows: &[Input]) -> [Statement; 2] { ), ) )) - .with(&PARSE_SETTINGS); + .with(&PARSE_SETTINGS) + .with(&ANALYSIS_SETTINGS); [stage1, stage2] } @@ -611,6 +614,29 @@ fn row(state: &super::State) -> Vec<(&'static str, String)> { #[cfg(test)] mod tests { use super::*; + use crate::resident::resident_settings; + + /// A test's tic statements and a session's residents are analysed + /// under the same settings, so a test pays the analysis a session pays. + #[test] + fn a_test_s_statements_are_analysed_as_a_session_s_are() { + let setting = ("optimize_and_compare_chain", "0"); + let session = resident_settings(0); + assert!( + session.iter().any(|(n, v)| (*n, v.as_str()) == setting), + "a resident session does not send {setting:?}" + ); + let [stage1, stage2] = demo_statement("nat", 1, 2); + for (name, statement) in [("first", stage1), ("second", stage2)] { + assert!( + statement + .settings + .iter() + .any(|(n, v)| (n.as_str(), v.as_str()) == setting), + "the {name} statement run_statement builds does not carry {setting:?}" + ); + } + } #[test] fn the_resident_statements_read_the_session_s_rows() { diff --git a/native/src/sql/statement.rs b/native/src/sql/statement.rs index 7ce21e8..61b4304 100644 --- a/native/src/sql/statement.rs +++ b/native/src/sql/statement.rs @@ -23,12 +23,14 @@ impl Statement { } } - /// The same statement, to be issued under `settings`. + /// The same statement, to be issued under `settings` as well as the + /// ones it already has. pub fn with(mut self, settings: &[(&str, &str)]) -> Statement { - self.settings = settings - .iter() - .map(|(name, value)| ((*name).to_owned(), (*value).to_owned())) - .collect(); + self.settings.extend( + settings + .iter() + .map(|(name, value)| ((*name).to_owned(), (*value).to_owned())), + ); self }