1use std::collections::HashMap;
16use std::fmt::{Display, Formatter};
17use std::net::SocketAddr;
18use std::str::FromStr;
19use std::sync::{Arc, RwLock};
20use std::time::Duration;
21
22use api::v1::ExplainOptions;
23use api::v1::region::RegionRequestHeader;
24use arc_swap::ArcSwap;
25use auth::UserInfoRef;
26pub use common_base::protocol::Channel;
27use common_catalog::consts::{DEFAULT_CATALOG_NAME, DEFAULT_SCHEMA_NAME};
28use common_catalog::{build_db_string, parse_catalog_and_schema_from_db_string};
29use common_recordbatch::cursor::RecordBatchStreamCursor;
30use common_telemetry::warn;
31use common_time::Timezone;
32use common_time::timezone::parse_timezone;
33use datafusion_common::config::ConfigOptions;
34use derive_builder::Builder;
35use sql::dialect::{Dialect, GenericDialect, GreptimeDbDialect, MySqlDialect, PostgreSqlDialect};
36
37pub use crate::hints::{
38 LIVE_ANALYZE_METRICS_EXTENSION_KEY, READ_PREFERENCE_EXTENSION_KEY,
39 REMOTE_QUERY_ID_EXTENSION_KEY, SUPPORT_FLIGHT_METRICS_BEFORE_BATCH_EXTENSION_KEY,
40};
41use crate::protocol_ctx::ProtocolCtx;
42use crate::query_id::QueryId;
43use crate::session_config::{
44 InvalidConfigValueSnafu, PGByteaOutputValue, PGDateOrder, PGDateTimeStyle, PGIntervalStyle,
45};
46use crate::{MutableInner, ReadPreference};
47
48pub type QueryContextRef = Arc<QueryContext>;
49pub type ConnInfoRef = Arc<ConnInfo>;
50
51pub const FLIGHT_METRICS_HEARTBEAT_INTERVAL: Duration = Duration::from_secs(1);
52
53const CURSOR_COUNT_WARNING_LIMIT: usize = 10;
54
55pub fn generate_remote_query_id() -> String {
56 generate_remote_query_id_value().to_string()
57}
58
59pub fn generate_remote_query_id_value() -> QueryId {
60 QueryId::new()
61}
62
63#[derive(Debug, Builder, Clone)]
64#[builder(pattern = "owned")]
65#[builder(build_fn(skip))]
66pub struct QueryContext {
67 current_catalog: String,
68 snapshot_seqs: Arc<RwLock<HashMap<u64, u64>>>,
71 sst_min_sequences: Arc<RwLock<HashMap<u64, u64>>>,
73 #[builder(default)]
75 mutable_session_data: Arc<RwLock<MutableInner>>,
76 #[builder(default)]
77 mutable_query_context_data: Arc<RwLock<QueryContextMutableFields>>,
78 sql_dialect: Arc<dyn Dialect + Send + Sync>,
79 #[builder(default)]
80 extensions: HashMap<String, String>,
81 #[builder(default)]
83 configuration_parameter: Arc<ConfigurationVariables>,
84 #[builder(default)]
86 batching_enabled: bool,
87 #[builder(default)]
89 logical_batching_enabled: bool,
90 #[builder(default)]
92 channel: Channel,
93 #[builder(setter(skip))]
95 admitted_write: Option<(String, String)>,
96 #[builder(default)]
98 process_id: u32,
99 #[builder(default)]
101 conn_info: ConnInfo,
102 #[builder(default)]
104 protocol_ctx: ProtocolCtx,
105}
106
107#[derive(Debug, Builder, Clone, Default)]
109pub struct QueryContextMutableFields {
110 warning: Option<String>,
111 explain_format: Option<String>,
113 explain_options: Option<ExplainOptions>,
115}
116
117impl Display for QueryContext {
118 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
119 write!(
120 f,
121 "QueryContext{{catalog: {}, schema: {}}}",
122 self.current_catalog(),
123 self.current_schema()
124 )
125 }
126}
127
128impl QueryContextBuilder {
129 pub fn current_schema(mut self, schema: String) -> Self {
130 if self.mutable_session_data.is_none() {
131 self.mutable_session_data = Some(Arc::new(RwLock::new(MutableInner::default())));
132 }
133
134 self.mutable_session_data
136 .as_mut()
137 .unwrap()
138 .write()
139 .unwrap()
140 .schema = schema;
141 self
142 }
143
144 pub fn timezone(mut self, timezone: Timezone) -> Self {
145 if self.mutable_session_data.is_none() {
146 self.mutable_session_data = Some(Arc::new(RwLock::new(MutableInner::default())));
147 }
148
149 self.mutable_session_data
150 .as_mut()
151 .unwrap()
152 .write()
153 .unwrap()
154 .timezone = timezone;
155 self
156 }
157
158 pub fn explain_options(mut self, explain_options: Option<ExplainOptions>) -> Self {
159 self.mutable_query_context_data
160 .get_or_insert_default()
161 .write()
162 .unwrap()
163 .explain_options = explain_options;
164 self
165 }
166
167 pub fn skip_wal(mut self, skip_wal: bool) -> Self {
168 self.mutable_session_data
169 .get_or_insert_default()
170 .write()
171 .unwrap()
172 .skip_wal = skip_wal;
173 self
174 }
175
176 pub fn read_preference(mut self, read_preference: ReadPreference) -> Self {
177 self.mutable_session_data
178 .get_or_insert_default()
179 .write()
180 .unwrap()
181 .read_preference = read_preference;
182 self
183 }
184}
185
186impl TryFrom<&RegionRequestHeader> for QueryContext {
187 type Error = crate::session_config::Error;
188
189 fn try_from(value: &RegionRequestHeader) -> Result<Self, Self::Error> {
190 if let Some(ctx) = &value.query_context {
191 ctx.clone().try_into()
192 } else {
193 Ok(QueryContextBuilder::default()
194 .set_extension(
195 REMOTE_QUERY_ID_EXTENSION_KEY.to_string(),
196 generate_remote_query_id(),
197 )
198 .build())
199 }
200 }
201}
202
203impl TryFrom<api::v1::QueryContext> for QueryContext {
204 type Error = crate::session_config::Error;
205
206 fn try_from(mut ctx: api::v1::QueryContext) -> Result<Self, Self::Error> {
207 let read_preference = ctx.extensions.remove(READ_PREFERENCE_EXTENSION_KEY);
208 let read_preference = match read_preference {
209 Some(value) => ReadPreference::from_str(&value).map_err(|_| {
210 InvalidConfigValueSnafu {
211 name: "read_preference",
212 value,
213 hint: "Expected a supported read preference",
214 }
215 .build()
216 })?,
217 None => ReadPreference::default(),
218 };
219 let sequences = ctx.snapshot_seqs.as_ref();
220 Ok(QueryContextBuilder::default()
221 .current_catalog(ctx.current_catalog)
222 .current_schema(ctx.current_schema)
223 .timezone(parse_timezone(Some(&ctx.timezone)))
224 .extensions(ctx.extensions)
225 .channel(ctx.channel.into())
226 .snapshot_seqs(Arc::new(RwLock::new(
227 sequences
228 .map(|x| x.snapshot_seqs.clone())
229 .unwrap_or_default(),
230 )))
231 .sst_min_sequences(Arc::new(RwLock::new(
232 sequences
233 .map(|x| x.sst_min_sequences.clone())
234 .unwrap_or_default(),
235 )))
236 .explain_options(ctx.explain)
237 .read_preference(read_preference)
238 .build())
239 }
240}
241
242impl From<QueryContext> for api::v1::QueryContext {
243 fn from(
244 QueryContext {
245 current_catalog,
246 mutable_session_data: mutable_inner,
247 extensions,
248 channel,
249 snapshot_seqs,
250 sst_min_sequences,
251 mutable_query_context_data,
252 ..
253 }: QueryContext,
254 ) -> Self {
255 let explain = mutable_query_context_data.read().unwrap().explain_options;
256 let mutable_inner = mutable_inner.read().unwrap();
257 let mut extensions = extensions;
258 extensions.remove(READ_PREFERENCE_EXTENSION_KEY);
259 if mutable_inner.read_preference != ReadPreference::Leader {
260 extensions.insert(
261 READ_PREFERENCE_EXTENSION_KEY.to_string(),
262 mutable_inner
263 .read_preference
264 .to_string()
265 .to_ascii_lowercase(),
266 );
267 }
268 api::v1::QueryContext {
269 current_catalog,
270 current_schema: mutable_inner.schema.clone(),
271 timezone: mutable_inner.timezone.to_string(),
272 extensions,
273 channel: channel as u32,
274 snapshot_seqs: Some(api::v1::SnapshotSequences {
275 snapshot_seqs: snapshot_seqs.read().unwrap().clone(),
276 sst_min_sequences: sst_min_sequences.read().unwrap().clone(),
277 }),
278 explain,
279 }
280 }
281}
282
283impl From<&QueryContext> for api::v1::QueryContext {
284 fn from(ctx: &QueryContext) -> Self {
285 ctx.clone().into()
286 }
287}
288
289impl QueryContext {
290 pub fn fork(&self) -> Self {
295 let mut fork = self.clone();
296 fork.mutable_session_data = Arc::new(RwLock::new(
297 self.mutable_session_data.read().unwrap().clone(),
298 ));
299 fork
300 }
301
302 pub fn with_write_admission(&self) -> Self {
305 let mut ctx = self.fork();
306 ctx.admitted_write = Some((ctx.current_catalog().to_string(), ctx.current_schema()));
307 ctx
308 }
309
310 pub fn write_rows_to_admit(&self, catalog: &str, schema: &str, rows: u64) -> u64 {
313 if self
314 .admitted_write
315 .as_ref()
316 .is_some_and(|(c, s)| c == catalog && s == schema)
317 {
318 0
319 } else {
320 rows
321 }
322 }
323
324 pub fn arc() -> QueryContextRef {
325 Arc::new(
326 QueryContextBuilder::default()
327 .set_extension(
328 REMOTE_QUERY_ID_EXTENSION_KEY.to_string(),
329 generate_remote_query_id(),
330 )
331 .build(),
332 )
333 }
334
335 pub fn create_config_options(&self) -> ConfigOptions {
337 let mut config = ConfigOptions::default();
338 config.execution.time_zone = Some(self.timezone().to_string());
339 config
340 }
341
342 pub fn with(catalog: &str, schema: &str) -> QueryContext {
343 QueryContextBuilder::default()
344 .current_catalog(catalog.to_string())
345 .current_schema(schema.to_string())
346 .set_extension(
347 REMOTE_QUERY_ID_EXTENSION_KEY.to_string(),
348 generate_remote_query_id(),
349 )
350 .build()
351 }
352
353 pub fn with_channel(catalog: &str, schema: &str, channel: Channel) -> QueryContext {
354 QueryContextBuilder::default()
355 .current_catalog(catalog.to_string())
356 .current_schema(schema.to_string())
357 .channel(channel)
358 .set_extension(
359 REMOTE_QUERY_ID_EXTENSION_KEY.to_string(),
360 generate_remote_query_id(),
361 )
362 .build()
363 }
364
365 pub fn with_db_name(db_name: Option<&str>) -> QueryContext {
366 let (catalog, schema) = db_name
367 .map(|db| {
368 let (catalog, schema) = parse_catalog_and_schema_from_db_string(db);
369 (catalog, schema)
370 })
371 .unwrap_or_else(|| {
372 (
373 DEFAULT_CATALOG_NAME.to_string(),
374 DEFAULT_SCHEMA_NAME.to_string(),
375 )
376 });
377 QueryContextBuilder::default()
378 .current_catalog(catalog)
379 .current_schema(schema.clone())
380 .set_extension(
381 REMOTE_QUERY_ID_EXTENSION_KEY.to_string(),
382 generate_remote_query_id(),
383 )
384 .build()
385 }
386
387 pub fn current_schema(&self) -> String {
388 self.mutable_session_data.read().unwrap().schema.clone()
389 }
390
391 pub fn set_current_schema(&self, new_schema: &str) {
392 self.mutable_session_data.write().unwrap().schema = new_schema.to_string();
393 }
394
395 pub fn current_catalog(&self) -> &str {
396 &self.current_catalog
397 }
398
399 pub fn set_current_catalog(&mut self, new_catalog: &str) {
400 self.current_catalog = new_catalog.to_string();
401 }
402
403 pub fn sql_dialect(&self) -> &(dyn Dialect + Send + Sync) {
404 &*self.sql_dialect
405 }
406
407 pub fn get_db_string(&self) -> String {
408 let catalog = self.current_catalog();
409 let schema = self.current_schema();
410 build_db_string(catalog, &schema)
411 }
412
413 pub fn timezone(&self) -> Timezone {
414 self.mutable_session_data.read().unwrap().timezone.clone()
415 }
416
417 pub fn set_timezone(&self, timezone: Timezone) {
418 self.mutable_session_data.write().unwrap().timezone = timezone;
419 }
420
421 pub fn skip_wal(&self) -> bool {
423 self.mutable_session_data.read().unwrap().skip_wal
424 }
425
426 pub fn set_skip_wal(&self, skip_wal: bool) {
427 self.mutable_session_data.write().unwrap().skip_wal = skip_wal;
428 }
429
430 pub fn read_preference(&self) -> ReadPreference {
431 self.mutable_session_data.read().unwrap().read_preference
432 }
433
434 pub fn set_read_preference(&self, read_preference: ReadPreference) {
435 self.mutable_session_data.write().unwrap().read_preference = read_preference;
436 }
437
438 pub fn current_user(&self) -> UserInfoRef {
439 self.mutable_session_data.read().unwrap().user_info.clone()
440 }
441
442 pub fn set_current_user(&self, user: UserInfoRef) {
443 self.mutable_session_data.write().unwrap().user_info = user;
444 }
445
446 pub fn set_extension<S1: Into<String>, S2: Into<String>>(&mut self, key: S1, value: S2) {
447 self.extensions.insert(key.into(), value.into());
448 }
449
450 #[cfg(feature = "enterprise")]
451 pub fn remove_extension(&mut self, key: &str) {
452 self.extensions.remove(key);
453 }
454
455 pub fn extension<S: AsRef<str>>(&self, key: S) -> Option<&str> {
456 self.extensions.get(key.as_ref()).map(|v| v.as_str())
457 }
458
459 pub fn remote_query_id(&self) -> Option<&str> {
460 self.extension(REMOTE_QUERY_ID_EXTENSION_KEY)
461 }
462
463 pub fn remote_query_id_value(&self) -> Option<QueryId> {
464 self.remote_query_id()
465 .and_then(|query_id| query_id.parse().ok())
466 }
467
468 pub fn enable_live_analyze_metrics(&mut self) {
469 if let Some(remote_query_id) = self.remote_query_id().map(str::to_string) {
470 self.set_extension(LIVE_ANALYZE_METRICS_EXTENSION_KEY, remote_query_id);
471 }
472 }
473
474 pub fn live_analyze_metrics_enabled(&self) -> bool {
475 self.remote_query_id()
476 .zip(self.extension(LIVE_ANALYZE_METRICS_EXTENSION_KEY))
477 .is_some_and(|(remote_query_id, value)| value == remote_query_id)
478 }
479
480 pub fn extensions(&self) -> HashMap<String, String> {
481 self.extensions.clone()
482 }
483
484 pub fn quote_style(&self) -> char {
486 if self.sql_dialect().is_delimited_identifier_start('"') {
487 '"'
488 } else if self.sql_dialect().is_delimited_identifier_start('\'') {
489 '\''
490 } else {
491 '`'
492 }
493 }
494
495 pub fn configuration_parameter(&self) -> &ConfigurationVariables {
496 &self.configuration_parameter
497 }
498
499 pub fn logical_batching_enabled(&self) -> bool {
501 self.logical_batching_enabled
502 }
503
504 pub fn set_logical_batching_enabled(&mut self, enabled: bool) {
506 self.logical_batching_enabled = enabled;
507 }
508
509 pub fn batching_enabled(&self) -> bool {
511 self.batching_enabled
512 }
513
514 pub fn set_batching_enabled(&mut self, enabled: bool) {
516 self.batching_enabled = enabled;
517 }
518
519 pub fn channel(&self) -> Channel {
520 self.channel
521 }
522
523 pub fn set_channel(&mut self, channel: Channel) {
524 self.channel = channel;
525 }
526
527 pub fn warning(&self) -> Option<String> {
528 self.mutable_query_context_data
529 .read()
530 .unwrap()
531 .warning
532 .clone()
533 }
534
535 pub fn set_warning(&self, msg: String) {
536 self.mutable_query_context_data.write().unwrap().warning = Some(msg);
537 }
538
539 pub fn explain_format(&self) -> Option<String> {
540 self.mutable_query_context_data
541 .read()
542 .unwrap()
543 .explain_format
544 .clone()
545 }
546
547 pub fn set_explain_format(&self, format: String) {
548 self.mutable_query_context_data
549 .write()
550 .unwrap()
551 .explain_format = Some(format);
552 }
553
554 pub fn explain_verbose(&self) -> bool {
555 self.mutable_query_context_data
556 .read()
557 .unwrap()
558 .explain_options
559 .map(|opts| opts.verbose)
560 .unwrap_or(false)
561 }
562
563 pub fn set_explain_verbose(&self, verbose: bool) {
564 self.mutable_query_context_data
565 .write()
566 .unwrap()
567 .explain_options
568 .get_or_insert_default()
569 .verbose = verbose;
570 }
571
572 pub fn query_timeout(&self) -> Option<Duration> {
573 self.mutable_session_data.read().unwrap().query_timeout
574 }
575
576 pub fn query_timeout_as_millis(&self) -> u128 {
577 let timeout = self.mutable_session_data.read().unwrap().query_timeout;
578 if let Some(t) = timeout {
579 return t.as_millis();
580 }
581 0
582 }
583
584 pub fn set_query_timeout(&self, timeout: Duration) {
585 self.mutable_session_data.write().unwrap().query_timeout = Some(timeout);
586 }
587
588 pub fn insert_cursor(&self, name: String, rb: RecordBatchStreamCursor) {
589 let mut guard = self.mutable_session_data.write().unwrap();
590 guard.cursors.insert(name, Arc::new(rb));
591
592 let cursor_count = guard.cursors.len();
593 if cursor_count > CURSOR_COUNT_WARNING_LIMIT {
594 warn!("Current connection has {} open cursors", cursor_count);
595 }
596 }
597
598 pub fn remove_cursor(&self, name: &str) {
599 let mut guard = self.mutable_session_data.write().unwrap();
600 guard.cursors.remove(name);
601 }
602
603 pub fn get_cursor(&self, name: &str) -> Option<Arc<RecordBatchStreamCursor>> {
604 let guard = self.mutable_session_data.read().unwrap();
605 let rb = guard.cursors.get(name);
606 rb.cloned()
607 }
608
609 pub fn snapshots(&self) -> HashMap<u64, u64> {
610 self.snapshot_seqs.read().unwrap().clone()
611 }
612
613 pub fn sst_min_sequences(&self) -> HashMap<u64, u64> {
614 self.sst_min_sequences.read().unwrap().clone()
615 }
616
617 pub fn get_snapshot(&self, region_id: u64) -> Option<u64> {
618 self.snapshot_seqs.read().unwrap().get(®ion_id).cloned()
619 }
620
621 pub fn set_snapshot(&self, region_id: u64, sequence: u64) {
622 self.snapshot_seqs
623 .write()
624 .unwrap()
625 .insert(region_id, sequence);
626 }
627
628 pub fn auto_string_to_numeric(&self) -> bool {
630 matches!(self.channel, Channel::Mysql)
631 }
632
633 pub fn sst_min_sequence(&self, region_id: u64) -> Option<u64> {
635 self.sst_min_sequences
636 .read()
637 .unwrap()
638 .get(®ion_id)
639 .copied()
640 }
641
642 pub fn process_id(&self) -> u32 {
643 self.process_id
644 }
645
646 pub fn conn_info(&self) -> &ConnInfo {
648 &self.conn_info
649 }
650
651 pub fn protocol_ctx(&self) -> &ProtocolCtx {
652 &self.protocol_ctx
653 }
654
655 pub fn set_protocol_ctx(&mut self, protocol_ctx: ProtocolCtx) {
656 self.protocol_ctx = protocol_ctx;
657 }
658}
659
660impl QueryContextBuilder {
661 pub fn build(self) -> QueryContext {
662 let channel = self.channel.unwrap_or_default();
663 let mut extensions = self.extensions.unwrap_or_default();
664 extensions
665 .entry(REMOTE_QUERY_ID_EXTENSION_KEY.to_string())
666 .or_insert_with(generate_remote_query_id);
667 QueryContext {
668 current_catalog: self
669 .current_catalog
670 .unwrap_or_else(|| DEFAULT_CATALOG_NAME.to_string()),
671 snapshot_seqs: self.snapshot_seqs.unwrap_or_default(),
672 sst_min_sequences: self.sst_min_sequences.unwrap_or_default(),
673 mutable_session_data: self.mutable_session_data.unwrap_or_default(),
674 mutable_query_context_data: self.mutable_query_context_data.unwrap_or_default(),
675 sql_dialect: self
676 .sql_dialect
677 .unwrap_or_else(|| Arc::new(GreptimeDbDialect {})),
678 extensions,
679 configuration_parameter: self
680 .configuration_parameter
681 .unwrap_or_else(|| Arc::new(ConfigurationVariables::default())),
682 channel,
683 batching_enabled: self.batching_enabled.unwrap_or_default(),
684 admitted_write: None,
685 logical_batching_enabled: self.logical_batching_enabled.unwrap_or_default(),
686 process_id: self.process_id.unwrap_or_default(),
687 conn_info: self.conn_info.unwrap_or_default(),
688 protocol_ctx: self.protocol_ctx.unwrap_or_default(),
689 }
690 }
691
692 pub fn set_extension(mut self, key: String, value: String) -> Self {
693 self.extensions
694 .get_or_insert_with(HashMap::new)
695 .insert(key, value);
696 self
697 }
698}
699
700#[derive(Debug, Clone, Default)]
701pub struct ConnInfo {
702 pub client_addr: Option<SocketAddr>,
703 pub channel: Channel,
704}
705
706impl Display for ConnInfo {
707 fn fmt(&self, f: &mut Formatter) -> std::fmt::Result {
708 write!(
709 f,
710 "{}[{}]",
711 self.channel,
712 self.client_addr
713 .map(|addr| addr.to_string())
714 .as_deref()
715 .unwrap_or("unknown client addr")
716 )
717 }
718}
719
720impl ConnInfo {
721 pub fn new(client_addr: Option<SocketAddr>, channel: Channel) -> Self {
722 Self {
723 client_addr,
724 channel,
725 }
726 }
727}
728
729pub fn dialect_for_channel(channel: Channel) -> Arc<dyn Dialect + Send + Sync> {
731 match channel {
732 Channel::Mysql => Arc::new(MySqlDialect {}),
733 Channel::Postgres => Arc::new(PostgreSqlDialect {}),
734 _ => Arc::new(GenericDialect {}),
735 }
736}
737
738#[derive(Default, Debug)]
739pub struct ConfigurationVariables {
740 postgres_bytea_output: ArcSwap<PGByteaOutputValue>,
741 pg_datestyle_format: ArcSwap<(PGDateTimeStyle, PGDateOrder)>,
742 pg_intervalstyle_format: ArcSwap<PGIntervalStyle>,
743 allow_query_fallback: ArcSwap<bool>,
744}
745
746impl Clone for ConfigurationVariables {
747 fn clone(&self) -> Self {
748 Self {
749 postgres_bytea_output: ArcSwap::new(self.postgres_bytea_output.load().clone()),
750 pg_datestyle_format: ArcSwap::new(self.pg_datestyle_format.load().clone()),
751 pg_intervalstyle_format: ArcSwap::new(self.pg_intervalstyle_format.load().clone()),
752 allow_query_fallback: ArcSwap::new(self.allow_query_fallback.load().clone()),
753 }
754 }
755}
756
757impl ConfigurationVariables {
758 pub fn new() -> Self {
759 Self::default()
760 }
761
762 pub fn set_postgres_bytea_output(&self, value: PGByteaOutputValue) {
763 let _ = self.postgres_bytea_output.swap(Arc::new(value));
764 }
765
766 pub fn postgres_bytea_output(&self) -> Arc<PGByteaOutputValue> {
767 self.postgres_bytea_output.load().clone()
768 }
769
770 pub fn pg_datetime_style(&self) -> Arc<(PGDateTimeStyle, PGDateOrder)> {
771 self.pg_datestyle_format.load().clone()
772 }
773
774 pub fn set_pg_datetime_style(&self, style: PGDateTimeStyle, order: PGDateOrder) {
775 self.pg_datestyle_format.swap(Arc::new((style, order)));
776 }
777
778 pub fn pg_intervalstyle_format(&self) -> Arc<PGIntervalStyle> {
779 self.pg_intervalstyle_format.load().clone()
780 }
781
782 pub fn set_pg_intervalstyle_format(&self, value: PGIntervalStyle) {
783 self.pg_intervalstyle_format.swap(Arc::new(value));
784 }
785
786 pub fn allow_query_fallback(&self) -> bool {
787 **self.allow_query_fallback.load()
788 }
789
790 pub fn set_allow_query_fallback(&self, allow: bool) {
791 self.allow_query_fallback.swap(Arc::new(allow));
792 }
793}
794
795#[cfg(test)]
796mod test {
797 use std::collections::HashMap;
798
799 use common_catalog::consts::DEFAULT_CATALOG_NAME;
800
801 use crate::Session;
802 use crate::context::{Channel, *};
803
804 #[test]
805 fn test_session() {
806 let session = Session::new(
807 Some("127.0.0.1:9000".parse().unwrap()),
808 Channel::Mysql,
809 Default::default(),
810 100,
811 );
812 assert_eq!(session.user_info().username(), "greptime");
814
815 assert_eq!(session.conn_info().channel, Channel::Mysql);
817 let client_addr = session.conn_info().client_addr.as_ref().unwrap();
818 assert_eq!(client_addr.ip().to_string(), "127.0.0.1");
819 assert_eq!(client_addr.port(), 9000);
820
821 assert_eq!("mysql[127.0.0.1:9000]", session.conn_info().to_string());
822 assert_eq!(100, session.process_id());
823
824 let query_ctx = session.new_query_context();
825 assert!(query_ctx.remote_query_id().is_some());
826 }
827
828 #[test]
829 fn test_context_db_string() {
830 let context = QueryContext::with("a0b1c2d3", "test");
831 assert_eq!("a0b1c2d3-test", context.get_db_string());
832
833 let context = QueryContext::with(DEFAULT_CATALOG_NAME, "test");
834 assert_eq!("test", context.get_db_string());
835 }
836
837 #[test]
838 fn test_fork_has_independent_mutable_session_data() {
839 let context = QueryContext::with(DEFAULT_CATALOG_NAME, "public");
840 let fork = context.fork();
841
842 fork.set_current_schema("private");
843
844 assert_eq!(context.current_schema(), "public");
845 assert_eq!(fork.current_schema(), "private");
846 }
847
848 #[test]
849 fn test_skip_wal_default_builder_and_fork() {
850 let default_context = QueryContext::with(DEFAULT_CATALOG_NAME, "public");
851 assert!(!default_context.skip_wal());
852 assert!(!QueryContextBuilder::default().build().skip_wal());
853 let context = QueryContextBuilder::default().skip_wal(true).build();
854 assert!(context.skip_wal());
855 let fork = context.fork();
856 assert!(fork.skip_wal());
857 fork.set_skip_wal(false);
858 assert!(context.skip_wal());
859 assert!(!fork.skip_wal());
860 context.set_skip_wal(false);
861 fork.set_skip_wal(true);
862 assert!(!context.skip_wal());
863 assert!(fork.skip_wal());
864 }
865
866 #[test]
867 fn test_write_admission_is_local_and_database_scoped() {
868 let ctx = QueryContext::with_channel("greptime", "public", Channel::Otlp);
869 let admitted = ctx.with_write_admission();
870 assert_eq!(ctx.write_rows_to_admit("greptime", "public", 100), 100);
871 assert_eq!(admitted.write_rows_to_admit("greptime", "public", 100), 0);
872 assert_eq!(admitted.write_rows_to_admit("greptime", "other", 100), 100);
873 assert_eq!(admitted.write_rows_to_admit("other", "public", 100), 100);
874 assert_eq!(admitted.channel(), Channel::Otlp);
875 assert_eq!(
876 admitted
877 .fork()
878 .write_rows_to_admit("greptime", "public", 100),
879 0
880 );
881 let wire: api::v1::QueryContext = admitted.into();
882 let restored = QueryContext::try_from(wire).unwrap();
883 assert_eq!(restored.write_rows_to_admit("greptime", "public", 100), 100);
884 }
885
886 #[test]
887 fn test_skip_wal_is_not_serialized_in_query_context() {
888 let context = QueryContextBuilder::default().skip_wal(true).build();
889 let api_context: api::v1::QueryContext = context.into();
890 assert!(
891 !api_context
892 .extensions
893 .contains_key(crate::hints::INSERT_SKIP_WAL_HINT)
894 );
895 let restored: QueryContext = api_context.try_into().unwrap();
896 assert!(!restored.skip_wal());
897 }
898
899 #[test]
900 fn test_api_query_context_roundtrip_with_sequences() {
901 let api_ctx = api::v1::QueryContext {
902 current_catalog: "c1".to_string(),
903 current_schema: "s1".to_string(),
904 timezone: "UTC".to_string(),
905 extensions: HashMap::from([("flow.return_region_seq".to_string(), "true".to_string())]),
906 channel: Channel::Internal as u32,
907 snapshot_seqs: Some(api::v1::SnapshotSequences {
908 snapshot_seqs: HashMap::from([(1, 100)]),
909 sst_min_sequences: HashMap::from([(1, 90)]),
910 }),
911 explain: None,
912 };
913
914 let session_ctx: QueryContext = api_ctx.clone().try_into().unwrap();
915 let roundtrip_api: api::v1::QueryContext = session_ctx.into();
916
917 assert_eq!(roundtrip_api.current_catalog, api_ctx.current_catalog);
918 assert_eq!(roundtrip_api.current_schema, api_ctx.current_schema);
919 assert_eq!(roundtrip_api.timezone, api_ctx.timezone);
920 assert_eq!(
921 roundtrip_api.extensions.get("flow.return_region_seq"),
922 Some(&"true".to_string())
923 );
924 assert!(
925 roundtrip_api
926 .extensions
927 .contains_key(REMOTE_QUERY_ID_EXTENSION_KEY)
928 );
929 assert_eq!(roundtrip_api.channel, api_ctx.channel);
930 assert_eq!(roundtrip_api.snapshot_seqs, api_ctx.snapshot_seqs);
931 }
932
933 #[test]
934 fn test_query_context_remote_query_id_round_trip() {
935 let query_id = "0195f4fd-c503-7c54-8b8f-7dfb8f6f9c4a";
936 let ctx = QueryContextBuilder::default()
937 .current_catalog(DEFAULT_CATALOG_NAME.to_string())
938 .current_schema("public".to_string())
939 .set_extension(
940 REMOTE_QUERY_ID_EXTENSION_KEY.to_string(),
941 query_id.to_string(),
942 )
943 .build();
944
945 assert_eq!(ctx.remote_query_id(), Some(query_id));
946 assert_eq!(ctx.remote_query_id_value().unwrap().to_string(), query_id);
947
948 let proto: api::v1::QueryContext = (&ctx).into();
949 let restored = QueryContext::try_from(proto).unwrap();
950 assert_eq!(restored.remote_query_id(), Some(query_id));
951 assert_eq!(
952 restored.remote_query_id_value().unwrap().to_string(),
953 query_id
954 );
955 }
956
957 #[test]
958 fn test_query_context_read_preference_wire_values() {
959 let wire = |value: Option<&str>| api::v1::QueryContext {
960 current_catalog: DEFAULT_CATALOG_NAME.to_string(),
961 current_schema: DEFAULT_SCHEMA_NAME.to_string(),
962 timezone: "UTC".to_string(),
963 extensions: value
964 .map(|value| {
965 HashMap::from([(READ_PREFERENCE_EXTENSION_KEY.to_string(), value.to_string())])
966 })
967 .unwrap_or_default(),
968 channel: Channel::Internal as u32,
969 snapshot_seqs: None,
970 explain: None,
971 };
972
973 let header = RegionRequestHeader::default();
974 let header_context = QueryContext::try_from(&header).unwrap();
975 assert_eq!(header_context.read_preference(), ReadPreference::Leader);
976
977 for value in [None, Some("leader"), Some("LEADER")] {
978 let ctx = QueryContext::try_from(wire(value)).unwrap();
979 assert_eq!(ctx.read_preference(), ReadPreference::Leader);
980 assert!(!ctx.extensions().contains_key(READ_PREFERENCE_EXTENSION_KEY));
981 }
982 for value in ["", "unknown"] {
983 let error = QueryContext::try_from(wire(Some(value))).unwrap_err();
984 assert!(matches!(
985 error,
986 crate::session_config::Error::InvalidConfigValue {
987 name,
988 value: actual,
989 ..
990 } if name == "read_preference" && actual == value
991 ));
992 }
993 }
994
995 #[test]
996 fn test_legacy_query_context_wire_defaults_to_leader() {
997 use prost::Message;
998
999 let legacy = api::v1::QueryContext::decode(
1000 include_bytes!(
1001 "../../../tests/compatibility/wire/read_preference/legacy_query_context.pb"
1002 )
1003 .as_slice(),
1004 )
1005 .unwrap();
1006 assert_eq!(legacy.current_catalog, "c1");
1007 assert_eq!(legacy.current_schema, "s1");
1008 assert_eq!(legacy.timezone, "UTC");
1009 assert_eq!(legacy.channel, Channel::Internal as u32);
1010 assert_eq!(
1011 legacy
1012 .extensions
1013 .get("flow.return_region_seq")
1014 .map(String::as_str),
1015 Some("true")
1016 );
1017 assert!(
1018 !legacy
1019 .extensions
1020 .contains_key(READ_PREFERENCE_EXTENSION_KEY)
1021 );
1022
1023 let restored = QueryContext::try_from(legacy).unwrap();
1024 assert_eq!(restored.read_preference(), ReadPreference::Leader);
1025 assert_eq!(restored.current_catalog(), "c1");
1026 assert_eq!(restored.current_schema(), "s1");
1027 assert_eq!(restored.timezone().to_string(), "UTC");
1028 assert_eq!(restored.channel(), Channel::Internal);
1029 assert_eq!(restored.extension("flow.return_region_seq"), Some("true"));
1030
1031 let outbound: api::v1::QueryContext = restored.into();
1032 let roundtrip = api::v1::QueryContext::decode(outbound.encode_to_vec().as_slice()).unwrap();
1033 assert_eq!(roundtrip.current_catalog, "c1");
1034 assert_eq!(roundtrip.current_schema, "s1");
1035 assert_eq!(roundtrip.timezone, "UTC");
1036 assert_eq!(roundtrip.channel, Channel::Internal as u32);
1037 assert_eq!(
1038 roundtrip
1039 .extensions
1040 .get("flow.return_region_seq")
1041 .map(String::as_str),
1042 Some("true")
1043 );
1044 assert!(
1045 !roundtrip
1046 .extensions
1047 .contains_key(READ_PREFERENCE_EXTENSION_KEY)
1048 );
1049 }
1050
1051 #[test]
1052 fn test_outbound_read_preference_clears_stale_extension() {
1053 let context = QueryContextBuilder::default()
1054 .set_extension(
1055 READ_PREFERENCE_EXTENSION_KEY.to_string(),
1056 "stale".to_string(),
1057 )
1058 .set_extension("unrelated".to_string(), "preserved".to_string())
1059 .build();
1060 let wire: api::v1::QueryContext = context.into();
1061 assert!(!wire.extensions.contains_key(READ_PREFERENCE_EXTENSION_KEY));
1062 assert_eq!(
1063 wire.extensions.get("unrelated").map(String::as_str),
1064 Some("preserved")
1065 );
1066 }
1067
1068 #[test]
1069 fn test_live_analyze_metrics_requires_matching_remote_query_id() {
1070 let mut ctx = QueryContext::arc().as_ref().clone();
1071 assert!(!ctx.live_analyze_metrics_enabled());
1072
1073 ctx.enable_live_analyze_metrics();
1074 assert!(ctx.live_analyze_metrics_enabled());
1075
1076 ctx.set_extension(LIVE_ANALYZE_METRICS_EXTENSION_KEY, "true");
1077 assert!(!ctx.live_analyze_metrics_enabled());
1078
1079 ctx.set_extension(LIVE_ANALYZE_METRICS_EXTENSION_KEY, "another-query-id");
1080 assert!(!ctx.live_analyze_metrics_enabled());
1081 }
1082 #[test]
1083 fn test_batching_selection_is_local_only() {
1084 let mut ctx = QueryContextBuilder::default().build();
1085 assert!(!ctx.batching_enabled());
1086 assert!(!ctx.logical_batching_enabled());
1087 ctx.set_logical_batching_enabled(true);
1088 assert!(ctx.clone().logical_batching_enabled());
1089 assert!(ctx.fork().logical_batching_enabled());
1090 ctx.set_batching_enabled(true);
1091 assert!(ctx.clone().batching_enabled());
1092 assert!(ctx.fork().batching_enabled());
1093 let wire: api::v1::QueryContext = ctx.into();
1094 let restored = QueryContext::try_from(wire).unwrap();
1095 assert!(!restored.batching_enabled());
1096 assert!(!restored.logical_batching_enabled());
1097 let ctx = QueryContextBuilder::default()
1098 .set_extension("batching_enabled".to_string(), "true".to_string())
1099 .build();
1100 assert!(!ctx.batching_enabled());
1101 }
1102}