1use std::collections::HashMap;
16use std::fmt::{Display, Formatter};
17use std::net::SocketAddr;
18use std::sync::{Arc, RwLock};
19use std::time::Duration;
20
21use api::v1::ExplainOptions;
22use api::v1::region::RegionRequestHeader;
23use arc_swap::ArcSwap;
24use auth::UserInfoRef;
25use common_catalog::consts::{DEFAULT_CATALOG_NAME, DEFAULT_SCHEMA_NAME};
26use common_catalog::{build_db_string, parse_catalog_and_schema_from_db_string};
27use common_recordbatch::cursor::RecordBatchStreamCursor;
28use common_telemetry::warn;
29use common_time::Timezone;
30use common_time::timezone::parse_timezone;
31use datafusion_common::config::ConfigOptions;
32use derive_builder::Builder;
33use sql::dialect::{Dialect, GenericDialect, GreptimeDbDialect, MySqlDialect, PostgreSqlDialect};
34
35pub use crate::hints::{
36 LIVE_ANALYZE_METRICS_EXTENSION_KEY, REMOTE_QUERY_ID_EXTENSION_KEY,
37 SUPPORT_FLIGHT_METRICS_BEFORE_BATCH_EXTENSION_KEY,
38};
39use crate::protocol_ctx::ProtocolCtx;
40use crate::query_id::QueryId;
41use crate::session_config::{PGByteaOutputValue, PGDateOrder, PGDateTimeStyle, PGIntervalStyle};
42use crate::{MutableInner, ReadPreference};
43
44pub type QueryContextRef = Arc<QueryContext>;
45pub type ConnInfoRef = Arc<ConnInfo>;
46
47pub const FLIGHT_METRICS_HEARTBEAT_INTERVAL: Duration = Duration::from_secs(1);
48
49const CURSOR_COUNT_WARNING_LIMIT: usize = 10;
50
51pub fn generate_remote_query_id() -> String {
52 generate_remote_query_id_value().to_string()
53}
54
55pub fn generate_remote_query_id_value() -> QueryId {
56 QueryId::new()
57}
58
59#[derive(Debug, Builder, Clone)]
60#[builder(pattern = "owned")]
61#[builder(build_fn(skip))]
62pub struct QueryContext {
63 current_catalog: String,
64 snapshot_seqs: Arc<RwLock<HashMap<u64, u64>>>,
68 sst_min_sequences: Arc<RwLock<HashMap<u64, u64>>>,
70 #[builder(default)]
72 mutable_session_data: Arc<RwLock<MutableInner>>,
73 #[builder(default)]
74 mutable_query_context_data: Arc<RwLock<QueryContextMutableFields>>,
75 sql_dialect: Arc<dyn Dialect + Send + Sync>,
76 #[builder(default)]
77 extensions: HashMap<String, String>,
78 #[builder(default)]
80 configuration_parameter: Arc<ConfigurationVariables>,
81 #[builder(default)]
83 channel: Channel,
84 #[builder(default)]
86 process_id: u32,
87 #[builder(default)]
89 conn_info: ConnInfo,
90 #[builder(default)]
92 protocol_ctx: ProtocolCtx,
93}
94
95#[derive(Debug, Builder, Clone, Default)]
97pub struct QueryContextMutableFields {
98 warning: Option<String>,
99 explain_format: Option<String>,
101 explain_options: Option<ExplainOptions>,
103}
104
105impl Display for QueryContext {
106 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
107 write!(
108 f,
109 "QueryContext{{catalog: {}, schema: {}}}",
110 self.current_catalog(),
111 self.current_schema()
112 )
113 }
114}
115
116impl QueryContextBuilder {
117 pub fn current_schema(mut self, schema: String) -> Self {
118 if self.mutable_session_data.is_none() {
119 self.mutable_session_data = Some(Arc::new(RwLock::new(MutableInner::default())));
120 }
121
122 self.mutable_session_data
124 .as_mut()
125 .unwrap()
126 .write()
127 .unwrap()
128 .schema = schema;
129 self
130 }
131
132 pub fn timezone(mut self, timezone: Timezone) -> Self {
133 if self.mutable_session_data.is_none() {
134 self.mutable_session_data = Some(Arc::new(RwLock::new(MutableInner::default())));
135 }
136
137 self.mutable_session_data
138 .as_mut()
139 .unwrap()
140 .write()
141 .unwrap()
142 .timezone = timezone;
143 self
144 }
145
146 pub fn explain_options(mut self, explain_options: Option<ExplainOptions>) -> Self {
147 self.mutable_query_context_data
148 .get_or_insert_default()
149 .write()
150 .unwrap()
151 .explain_options = explain_options;
152 self
153 }
154
155 pub fn read_preference(mut self, read_preference: ReadPreference) -> Self {
156 self.mutable_session_data
157 .get_or_insert_default()
158 .write()
159 .unwrap()
160 .read_preference = read_preference;
161 self
162 }
163}
164
165impl From<&RegionRequestHeader> for QueryContext {
166 fn from(value: &RegionRequestHeader) -> Self {
167 if let Some(ctx) = &value.query_context {
168 ctx.clone().into()
169 } else {
170 QueryContextBuilder::default()
171 .set_extension(
172 REMOTE_QUERY_ID_EXTENSION_KEY.to_string(),
173 generate_remote_query_id(),
174 )
175 .build()
176 }
177 }
178}
179
180impl From<api::v1::QueryContext> for QueryContext {
181 fn from(ctx: api::v1::QueryContext) -> Self {
182 let sequences = ctx.snapshot_seqs.as_ref();
183 QueryContextBuilder::default()
184 .current_catalog(ctx.current_catalog)
185 .current_schema(ctx.current_schema)
186 .timezone(parse_timezone(Some(&ctx.timezone)))
187 .extensions(ctx.extensions)
188 .channel(ctx.channel.into())
189 .snapshot_seqs(Arc::new(RwLock::new(
190 sequences
191 .map(|x| x.snapshot_seqs.clone())
192 .unwrap_or_default(),
193 )))
194 .sst_min_sequences(Arc::new(RwLock::new(
195 sequences
196 .map(|x| x.sst_min_sequences.clone())
197 .unwrap_or_default(),
198 )))
199 .explain_options(ctx.explain)
200 .build()
201 }
202}
203
204impl From<QueryContext> for api::v1::QueryContext {
205 fn from(
206 QueryContext {
207 current_catalog,
208 mutable_session_data: mutable_inner,
209 extensions,
210 channel,
211 snapshot_seqs,
212 sst_min_sequences,
213 mutable_query_context_data,
214 ..
215 }: QueryContext,
216 ) -> Self {
217 let explain = mutable_query_context_data.read().unwrap().explain_options;
218 let mutable_inner = mutable_inner.read().unwrap();
219 api::v1::QueryContext {
220 current_catalog,
221 current_schema: mutable_inner.schema.clone(),
222 timezone: mutable_inner.timezone.to_string(),
223 extensions,
224 channel: channel as u32,
225 snapshot_seqs: Some(api::v1::SnapshotSequences {
226 snapshot_seqs: snapshot_seqs.read().unwrap().clone(),
227 sst_min_sequences: sst_min_sequences.read().unwrap().clone(),
228 }),
229 explain,
230 }
231 }
232}
233
234impl From<&QueryContext> for api::v1::QueryContext {
235 fn from(ctx: &QueryContext) -> Self {
236 ctx.clone().into()
237 }
238}
239
240impl QueryContext {
241 pub fn fork(&self) -> Self {
246 let mut fork = self.clone();
247 fork.mutable_session_data = Arc::new(RwLock::new(
248 self.mutable_session_data.read().unwrap().clone(),
249 ));
250 fork
251 }
252
253 pub fn arc() -> QueryContextRef {
254 Arc::new(
255 QueryContextBuilder::default()
256 .set_extension(
257 REMOTE_QUERY_ID_EXTENSION_KEY.to_string(),
258 generate_remote_query_id(),
259 )
260 .build(),
261 )
262 }
263
264 pub fn create_config_options(&self) -> ConfigOptions {
266 let mut config = ConfigOptions::default();
267 config.execution.time_zone = Some(self.timezone().to_string());
268 config
269 }
270
271 pub fn with(catalog: &str, schema: &str) -> QueryContext {
272 QueryContextBuilder::default()
273 .current_catalog(catalog.to_string())
274 .current_schema(schema.to_string())
275 .set_extension(
276 REMOTE_QUERY_ID_EXTENSION_KEY.to_string(),
277 generate_remote_query_id(),
278 )
279 .build()
280 }
281
282 pub fn with_channel(catalog: &str, schema: &str, channel: Channel) -> QueryContext {
283 QueryContextBuilder::default()
284 .current_catalog(catalog.to_string())
285 .current_schema(schema.to_string())
286 .channel(channel)
287 .set_extension(
288 REMOTE_QUERY_ID_EXTENSION_KEY.to_string(),
289 generate_remote_query_id(),
290 )
291 .build()
292 }
293
294 pub fn with_db_name(db_name: Option<&str>) -> QueryContext {
295 let (catalog, schema) = db_name
296 .map(|db| {
297 let (catalog, schema) = parse_catalog_and_schema_from_db_string(db);
298 (catalog, schema)
299 })
300 .unwrap_or_else(|| {
301 (
302 DEFAULT_CATALOG_NAME.to_string(),
303 DEFAULT_SCHEMA_NAME.to_string(),
304 )
305 });
306 QueryContextBuilder::default()
307 .current_catalog(catalog)
308 .current_schema(schema.clone())
309 .set_extension(
310 REMOTE_QUERY_ID_EXTENSION_KEY.to_string(),
311 generate_remote_query_id(),
312 )
313 .build()
314 }
315
316 pub fn current_schema(&self) -> String {
317 self.mutable_session_data.read().unwrap().schema.clone()
318 }
319
320 pub fn set_current_schema(&self, new_schema: &str) {
321 self.mutable_session_data.write().unwrap().schema = new_schema.to_string();
322 }
323
324 pub fn current_catalog(&self) -> &str {
325 &self.current_catalog
326 }
327
328 pub fn set_current_catalog(&mut self, new_catalog: &str) {
329 self.current_catalog = new_catalog.to_string();
330 }
331
332 pub fn sql_dialect(&self) -> &(dyn Dialect + Send + Sync) {
333 &*self.sql_dialect
334 }
335
336 pub fn get_db_string(&self) -> String {
337 let catalog = self.current_catalog();
338 let schema = self.current_schema();
339 build_db_string(catalog, &schema)
340 }
341
342 pub fn timezone(&self) -> Timezone {
343 self.mutable_session_data.read().unwrap().timezone.clone()
344 }
345
346 pub fn set_timezone(&self, timezone: Timezone) {
347 self.mutable_session_data.write().unwrap().timezone = timezone;
348 }
349
350 pub fn read_preference(&self) -> ReadPreference {
351 self.mutable_session_data.read().unwrap().read_preference
352 }
353
354 pub fn set_read_preference(&self, read_preference: ReadPreference) {
355 self.mutable_session_data.write().unwrap().read_preference = read_preference;
356 }
357
358 pub fn current_user(&self) -> UserInfoRef {
359 self.mutable_session_data.read().unwrap().user_info.clone()
360 }
361
362 pub fn set_current_user(&self, user: UserInfoRef) {
363 self.mutable_session_data.write().unwrap().user_info = user;
364 }
365
366 pub fn set_extension<S1: Into<String>, S2: Into<String>>(&mut self, key: S1, value: S2) {
367 self.extensions.insert(key.into(), value.into());
368 }
369
370 pub fn extension<S: AsRef<str>>(&self, key: S) -> Option<&str> {
371 self.extensions.get(key.as_ref()).map(|v| v.as_str())
372 }
373
374 pub fn remote_query_id(&self) -> Option<&str> {
375 self.extension(REMOTE_QUERY_ID_EXTENSION_KEY)
376 }
377
378 pub fn remote_query_id_value(&self) -> Option<QueryId> {
379 self.remote_query_id()
380 .and_then(|query_id| query_id.parse().ok())
381 }
382
383 pub fn enable_live_analyze_metrics(&mut self) {
384 if let Some(remote_query_id) = self.remote_query_id().map(str::to_string) {
385 self.set_extension(LIVE_ANALYZE_METRICS_EXTENSION_KEY, remote_query_id);
386 }
387 }
388
389 pub fn live_analyze_metrics_enabled(&self) -> bool {
390 self.remote_query_id()
391 .zip(self.extension(LIVE_ANALYZE_METRICS_EXTENSION_KEY))
392 .is_some_and(|(remote_query_id, value)| value == remote_query_id)
393 }
394
395 pub fn extensions(&self) -> HashMap<String, String> {
396 self.extensions.clone()
397 }
398
399 pub fn quote_style(&self) -> char {
401 if self.sql_dialect().is_delimited_identifier_start('"') {
402 '"'
403 } else if self.sql_dialect().is_delimited_identifier_start('\'') {
404 '\''
405 } else {
406 '`'
407 }
408 }
409
410 pub fn configuration_parameter(&self) -> &ConfigurationVariables {
411 &self.configuration_parameter
412 }
413
414 pub fn channel(&self) -> Channel {
415 self.channel
416 }
417
418 pub fn set_channel(&mut self, channel: Channel) {
419 self.channel = channel;
420 }
421
422 pub fn warning(&self) -> Option<String> {
423 self.mutable_query_context_data
424 .read()
425 .unwrap()
426 .warning
427 .clone()
428 }
429
430 pub fn set_warning(&self, msg: String) {
431 self.mutable_query_context_data.write().unwrap().warning = Some(msg);
432 }
433
434 pub fn explain_format(&self) -> Option<String> {
435 self.mutable_query_context_data
436 .read()
437 .unwrap()
438 .explain_format
439 .clone()
440 }
441
442 pub fn set_explain_format(&self, format: String) {
443 self.mutable_query_context_data
444 .write()
445 .unwrap()
446 .explain_format = Some(format);
447 }
448
449 pub fn explain_verbose(&self) -> bool {
450 self.mutable_query_context_data
451 .read()
452 .unwrap()
453 .explain_options
454 .map(|opts| opts.verbose)
455 .unwrap_or(false)
456 }
457
458 pub fn set_explain_verbose(&self, verbose: bool) {
459 self.mutable_query_context_data
460 .write()
461 .unwrap()
462 .explain_options
463 .get_or_insert_default()
464 .verbose = verbose;
465 }
466
467 pub fn query_timeout(&self) -> Option<Duration> {
468 self.mutable_session_data.read().unwrap().query_timeout
469 }
470
471 pub fn query_timeout_as_millis(&self) -> u128 {
472 let timeout = self.mutable_session_data.read().unwrap().query_timeout;
473 if let Some(t) = timeout {
474 return t.as_millis();
475 }
476 0
477 }
478
479 pub fn set_query_timeout(&self, timeout: Duration) {
480 self.mutable_session_data.write().unwrap().query_timeout = Some(timeout);
481 }
482
483 pub fn insert_cursor(&self, name: String, rb: RecordBatchStreamCursor) {
484 let mut guard = self.mutable_session_data.write().unwrap();
485 guard.cursors.insert(name, Arc::new(rb));
486
487 let cursor_count = guard.cursors.len();
488 if cursor_count > CURSOR_COUNT_WARNING_LIMIT {
489 warn!("Current connection has {} open cursors", cursor_count);
490 }
491 }
492
493 pub fn remove_cursor(&self, name: &str) {
494 let mut guard = self.mutable_session_data.write().unwrap();
495 guard.cursors.remove(name);
496 }
497
498 pub fn get_cursor(&self, name: &str) -> Option<Arc<RecordBatchStreamCursor>> {
499 let guard = self.mutable_session_data.read().unwrap();
500 let rb = guard.cursors.get(name);
501 rb.cloned()
502 }
503
504 pub fn snapshots(&self) -> HashMap<u64, u64> {
505 self.snapshot_seqs.read().unwrap().clone()
506 }
507
508 pub fn sst_min_sequences(&self) -> HashMap<u64, u64> {
509 self.sst_min_sequences.read().unwrap().clone()
510 }
511
512 pub fn get_snapshot(&self, region_id: u64) -> Option<u64> {
513 self.snapshot_seqs.read().unwrap().get(®ion_id).cloned()
514 }
515
516 pub fn set_snapshot(&self, region_id: u64, sequence: u64) {
517 self.snapshot_seqs
518 .write()
519 .unwrap()
520 .insert(region_id, sequence);
521 }
522
523 pub fn auto_string_to_numeric(&self) -> bool {
525 matches!(self.channel, Channel::Mysql)
526 }
527
528 pub fn sst_min_sequence(&self, region_id: u64) -> Option<u64> {
530 self.sst_min_sequences
531 .read()
532 .unwrap()
533 .get(®ion_id)
534 .copied()
535 }
536
537 pub fn process_id(&self) -> u32 {
538 self.process_id
539 }
540
541 pub fn conn_info(&self) -> &ConnInfo {
543 &self.conn_info
544 }
545
546 pub fn protocol_ctx(&self) -> &ProtocolCtx {
547 &self.protocol_ctx
548 }
549
550 pub fn set_protocol_ctx(&mut self, protocol_ctx: ProtocolCtx) {
551 self.protocol_ctx = protocol_ctx;
552 }
553}
554
555impl QueryContextBuilder {
556 pub fn build(self) -> QueryContext {
557 let channel = self.channel.unwrap_or_default();
558 let mut extensions = self.extensions.unwrap_or_default();
559 extensions
560 .entry(REMOTE_QUERY_ID_EXTENSION_KEY.to_string())
561 .or_insert_with(generate_remote_query_id);
562 QueryContext {
563 current_catalog: self
564 .current_catalog
565 .unwrap_or_else(|| DEFAULT_CATALOG_NAME.to_string()),
566 snapshot_seqs: self.snapshot_seqs.unwrap_or_default(),
567 sst_min_sequences: self.sst_min_sequences.unwrap_or_default(),
568 mutable_session_data: self.mutable_session_data.unwrap_or_default(),
569 mutable_query_context_data: self.mutable_query_context_data.unwrap_or_default(),
570 sql_dialect: self
571 .sql_dialect
572 .unwrap_or_else(|| Arc::new(GreptimeDbDialect {})),
573 extensions,
574 configuration_parameter: self
575 .configuration_parameter
576 .unwrap_or_else(|| Arc::new(ConfigurationVariables::default())),
577 channel,
578 process_id: self.process_id.unwrap_or_default(),
579 conn_info: self.conn_info.unwrap_or_default(),
580 protocol_ctx: self.protocol_ctx.unwrap_or_default(),
581 }
582 }
583
584 pub fn set_extension(mut self, key: String, value: String) -> Self {
585 self.extensions
586 .get_or_insert_with(HashMap::new)
587 .insert(key, value);
588 self
589 }
590}
591
592#[derive(Debug, Clone, Default)]
593pub struct ConnInfo {
594 pub client_addr: Option<SocketAddr>,
595 pub channel: Channel,
596}
597
598impl Display for ConnInfo {
599 fn fmt(&self, f: &mut Formatter) -> std::fmt::Result {
600 write!(
601 f,
602 "{}[{}]",
603 self.channel,
604 self.client_addr
605 .map(|addr| addr.to_string())
606 .as_deref()
607 .unwrap_or("unknown client addr")
608 )
609 }
610}
611
612impl ConnInfo {
613 pub fn new(client_addr: Option<SocketAddr>, channel: Channel) -> Self {
614 Self {
615 client_addr,
616 channel,
617 }
618 }
619}
620
621#[derive(Debug, PartialEq, Default, Clone, Copy)]
622#[repr(u8)]
623pub enum Channel {
624 #[default]
625 Unknown = 0,
626
627 Mysql = 1,
628 Postgres = 2,
629 HttpSql = 3,
630 Prometheus = 4,
631 Otlp = 5,
632 Grpc = 6,
633 Influx = 7,
634 Opentsdb = 8,
635 Loki = 9,
636 Elasticsearch = 10,
637 Jaeger = 11,
638 Log = 12,
639 Promql = 13,
640 Splunk = 14,
641}
642
643impl From<u32> for Channel {
644 fn from(value: u32) -> Self {
645 match value {
646 1 => Self::Mysql,
647 2 => Self::Postgres,
648 3 => Self::HttpSql,
649 4 => Self::Prometheus,
650 5 => Self::Otlp,
651 6 => Self::Grpc,
652 7 => Self::Influx,
653 8 => Self::Opentsdb,
654 9 => Self::Loki,
655 10 => Self::Elasticsearch,
656 11 => Self::Jaeger,
657 12 => Self::Log,
658 13 => Self::Promql,
659 14 => Self::Splunk,
660 _ => Self::Unknown,
661 }
662 }
663}
664
665impl Channel {
666 pub fn dialect(&self) -> Arc<dyn Dialect + Send + Sync> {
667 match self {
668 Channel::Mysql => Arc::new(MySqlDialect {}),
669 Channel::Postgres => Arc::new(PostgreSqlDialect {}),
670 _ => Arc::new(GenericDialect {}),
671 }
672 }
673}
674
675impl Display for Channel {
676 fn fmt(&self, f: &mut Formatter) -> std::fmt::Result {
677 write!(f, "{}", self.as_ref())
678 }
679}
680
681impl AsRef<str> for Channel {
682 fn as_ref(&self) -> &str {
683 match self {
684 Channel::Mysql => "mysql",
685 Channel::Postgres => "postgres",
686 Channel::HttpSql => "httpsql",
687 Channel::Prometheus => "prometheus",
688 Channel::Otlp => "otlp",
689 Channel::Grpc => "grpc",
690 Channel::Influx => "influx",
691 Channel::Opentsdb => "opentsdb",
692 Channel::Loki => "loki",
693 Channel::Elasticsearch => "elasticsearch",
694 Channel::Jaeger => "jaeger",
695 Channel::Log => "log",
696 Channel::Promql => "promql",
697 Channel::Splunk => "splunk",
698 Channel::Unknown => "unknown",
699 }
700 }
701}
702
703#[derive(Default, Debug)]
704pub struct ConfigurationVariables {
705 postgres_bytea_output: ArcSwap<PGByteaOutputValue>,
706 pg_datestyle_format: ArcSwap<(PGDateTimeStyle, PGDateOrder)>,
707 pg_intervalstyle_format: ArcSwap<PGIntervalStyle>,
708 allow_query_fallback: ArcSwap<bool>,
709}
710
711impl Clone for ConfigurationVariables {
712 fn clone(&self) -> Self {
713 Self {
714 postgres_bytea_output: ArcSwap::new(self.postgres_bytea_output.load().clone()),
715 pg_datestyle_format: ArcSwap::new(self.pg_datestyle_format.load().clone()),
716 pg_intervalstyle_format: ArcSwap::new(self.pg_intervalstyle_format.load().clone()),
717 allow_query_fallback: ArcSwap::new(self.allow_query_fallback.load().clone()),
718 }
719 }
720}
721
722impl ConfigurationVariables {
723 pub fn new() -> Self {
724 Self::default()
725 }
726
727 pub fn set_postgres_bytea_output(&self, value: PGByteaOutputValue) {
728 let _ = self.postgres_bytea_output.swap(Arc::new(value));
729 }
730
731 pub fn postgres_bytea_output(&self) -> Arc<PGByteaOutputValue> {
732 self.postgres_bytea_output.load().clone()
733 }
734
735 pub fn pg_datetime_style(&self) -> Arc<(PGDateTimeStyle, PGDateOrder)> {
736 self.pg_datestyle_format.load().clone()
737 }
738
739 pub fn set_pg_datetime_style(&self, style: PGDateTimeStyle, order: PGDateOrder) {
740 self.pg_datestyle_format.swap(Arc::new((style, order)));
741 }
742
743 pub fn pg_intervalstyle_format(&self) -> Arc<PGIntervalStyle> {
744 self.pg_intervalstyle_format.load().clone()
745 }
746
747 pub fn set_pg_intervalstyle_format(&self, value: PGIntervalStyle) {
748 self.pg_intervalstyle_format.swap(Arc::new(value));
749 }
750
751 pub fn allow_query_fallback(&self) -> bool {
752 **self.allow_query_fallback.load()
753 }
754
755 pub fn set_allow_query_fallback(&self, allow: bool) {
756 self.allow_query_fallback.swap(Arc::new(allow));
757 }
758}
759
760#[cfg(test)]
761mod test {
762 use std::collections::HashMap;
763
764 use common_catalog::consts::DEFAULT_CATALOG_NAME;
765
766 use super::*;
767 use crate::Session;
768 use crate::context::Channel;
769
770 #[test]
771 fn test_session() {
772 let session = Session::new(
773 Some("127.0.0.1:9000".parse().unwrap()),
774 Channel::Mysql,
775 Default::default(),
776 100,
777 );
778 assert_eq!(session.user_info().username(), "greptime");
780
781 assert_eq!(session.conn_info().channel, Channel::Mysql);
783 let client_addr = session.conn_info().client_addr.as_ref().unwrap();
784 assert_eq!(client_addr.ip().to_string(), "127.0.0.1");
785 assert_eq!(client_addr.port(), 9000);
786
787 assert_eq!("mysql[127.0.0.1:9000]", session.conn_info().to_string());
788 assert_eq!(100, session.process_id());
789
790 let query_ctx = session.new_query_context();
791 assert!(query_ctx.remote_query_id().is_some());
792 }
793
794 #[test]
795 fn test_context_db_string() {
796 let context = QueryContext::with("a0b1c2d3", "test");
797 assert_eq!("a0b1c2d3-test", context.get_db_string());
798
799 let context = QueryContext::with(DEFAULT_CATALOG_NAME, "test");
800 assert_eq!("test", context.get_db_string());
801 }
802
803 #[test]
804 fn test_fork_has_independent_mutable_session_data() {
805 let context = QueryContext::with(DEFAULT_CATALOG_NAME, "public");
806 let fork = context.fork();
807
808 fork.set_current_schema("private");
809
810 assert_eq!(context.current_schema(), "public");
811 assert_eq!(fork.current_schema(), "private");
812 }
813
814 #[test]
815 fn test_api_query_context_roundtrip_with_sequences() {
816 let api_ctx = api::v1::QueryContext {
817 current_catalog: "c1".to_string(),
818 current_schema: "s1".to_string(),
819 timezone: "UTC".to_string(),
820 extensions: HashMap::from([("flow.return_region_seq".to_string(), "true".to_string())]),
821 channel: Channel::Grpc as u32,
822 snapshot_seqs: Some(api::v1::SnapshotSequences {
823 snapshot_seqs: HashMap::from([(1, 100)]),
824 sst_min_sequences: HashMap::from([(1, 90)]),
825 }),
826 explain: None,
827 };
828
829 let session_ctx: QueryContext = api_ctx.clone().into();
830 let roundtrip_api: api::v1::QueryContext = session_ctx.into();
831
832 assert_eq!(roundtrip_api.current_catalog, api_ctx.current_catalog);
833 assert_eq!(roundtrip_api.current_schema, api_ctx.current_schema);
834 assert_eq!(roundtrip_api.timezone, api_ctx.timezone);
835 assert_eq!(
836 roundtrip_api.extensions.get("flow.return_region_seq"),
837 Some(&"true".to_string())
838 );
839 assert!(
840 roundtrip_api
841 .extensions
842 .contains_key(REMOTE_QUERY_ID_EXTENSION_KEY)
843 );
844 assert_eq!(roundtrip_api.channel, api_ctx.channel);
845 assert_eq!(roundtrip_api.snapshot_seqs, api_ctx.snapshot_seqs);
846 }
847
848 #[test]
849 fn test_query_context_remote_query_id_round_trip() {
850 let query_id = "0195f4fd-c503-7c54-8b8f-7dfb8f6f9c4a";
851 let ctx = QueryContextBuilder::default()
852 .current_catalog(DEFAULT_CATALOG_NAME.to_string())
853 .current_schema("public".to_string())
854 .set_extension(
855 REMOTE_QUERY_ID_EXTENSION_KEY.to_string(),
856 query_id.to_string(),
857 )
858 .build();
859
860 assert_eq!(ctx.remote_query_id(), Some(query_id));
861 assert_eq!(ctx.remote_query_id_value().unwrap().to_string(), query_id);
862
863 let proto: api::v1::QueryContext = (&ctx).into();
864 let restored = QueryContext::from(proto);
865 assert_eq!(restored.remote_query_id(), Some(query_id));
866 assert_eq!(
867 restored.remote_query_id_value().unwrap().to_string(),
868 query_id
869 );
870 }
871
872 #[test]
873 fn test_live_analyze_metrics_requires_matching_remote_query_id() {
874 let mut ctx = QueryContext::arc().as_ref().clone();
875 assert!(!ctx.live_analyze_metrics_enabled());
876
877 ctx.enable_live_analyze_metrics();
878 assert!(ctx.live_analyze_metrics_enabled());
879
880 ctx.set_extension(LIVE_ANALYZE_METRICS_EXTENSION_KEY, "true");
881 assert!(!ctx.live_analyze_metrics_enabled());
882
883 ctx.set_extension(LIVE_ANALYZE_METRICS_EXTENSION_KEY, "another-query-id");
884 assert!(!ctx.live_analyze_metrics_enabled());
885 }
886}