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;
25pub use common_base::protocol::Channel;
26use common_catalog::consts::{DEFAULT_CATALOG_NAME, DEFAULT_SCHEMA_NAME};
27use common_catalog::{build_db_string, parse_catalog_and_schema_from_db_string};
28use common_recordbatch::cursor::RecordBatchStreamCursor;
29use common_telemetry::warn;
30use common_time::Timezone;
31use common_time::timezone::parse_timezone;
32use datafusion_common::config::ConfigOptions;
33use derive_builder::Builder;
34use sql::dialect::{Dialect, GenericDialect, GreptimeDbDialect, MySqlDialect, PostgreSqlDialect};
35
36pub use crate::hints::{
37 LIVE_ANALYZE_METRICS_EXTENSION_KEY, REMOTE_QUERY_ID_EXTENSION_KEY,
38 SUPPORT_FLIGHT_METRICS_BEFORE_BATCH_EXTENSION_KEY,
39};
40use crate::protocol_ctx::ProtocolCtx;
41use crate::query_id::QueryId;
42use crate::session_config::{PGByteaOutputValue, PGDateOrder, PGDateTimeStyle, PGIntervalStyle};
43use crate::{MutableInner, ReadPreference};
44
45pub type QueryContextRef = Arc<QueryContext>;
46pub type ConnInfoRef = Arc<ConnInfo>;
47
48pub const FLIGHT_METRICS_HEARTBEAT_INTERVAL: Duration = Duration::from_secs(1);
49
50const CURSOR_COUNT_WARNING_LIMIT: usize = 10;
51
52pub fn generate_remote_query_id() -> String {
53 generate_remote_query_id_value().to_string()
54}
55
56pub fn generate_remote_query_id_value() -> QueryId {
57 QueryId::new()
58}
59
60#[derive(Debug, Builder, Clone)]
61#[builder(pattern = "owned")]
62#[builder(build_fn(skip))]
63pub struct QueryContext {
64 current_catalog: String,
65 snapshot_seqs: Arc<RwLock<HashMap<u64, u64>>>,
69 sst_min_sequences: Arc<RwLock<HashMap<u64, u64>>>,
71 #[builder(default)]
73 mutable_session_data: Arc<RwLock<MutableInner>>,
74 #[builder(default)]
75 mutable_query_context_data: Arc<RwLock<QueryContextMutableFields>>,
76 sql_dialect: Arc<dyn Dialect + Send + Sync>,
77 #[builder(default)]
78 extensions: HashMap<String, String>,
79 #[builder(default)]
81 configuration_parameter: Arc<ConfigurationVariables>,
82 #[builder(default)]
84 batching_enabled: bool,
85 #[builder(default)]
87 logical_batching_enabled: bool,
88 #[builder(default)]
90 channel: Channel,
91 #[builder(setter(skip))]
93 admitted_write: Option<(String, String)>,
94 #[builder(default)]
96 process_id: u32,
97 #[builder(default)]
99 conn_info: ConnInfo,
100 #[builder(default)]
102 protocol_ctx: ProtocolCtx,
103}
104
105#[derive(Debug, Builder, Clone, Default)]
107pub struct QueryContextMutableFields {
108 warning: Option<String>,
109 explain_format: Option<String>,
111 explain_options: Option<ExplainOptions>,
113}
114
115impl Display for QueryContext {
116 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
117 write!(
118 f,
119 "QueryContext{{catalog: {}, schema: {}}}",
120 self.current_catalog(),
121 self.current_schema()
122 )
123 }
124}
125
126impl QueryContextBuilder {
127 pub fn current_schema(mut self, schema: String) -> Self {
128 if self.mutable_session_data.is_none() {
129 self.mutable_session_data = Some(Arc::new(RwLock::new(MutableInner::default())));
130 }
131
132 self.mutable_session_data
134 .as_mut()
135 .unwrap()
136 .write()
137 .unwrap()
138 .schema = schema;
139 self
140 }
141
142 pub fn timezone(mut self, timezone: Timezone) -> Self {
143 if self.mutable_session_data.is_none() {
144 self.mutable_session_data = Some(Arc::new(RwLock::new(MutableInner::default())));
145 }
146
147 self.mutable_session_data
148 .as_mut()
149 .unwrap()
150 .write()
151 .unwrap()
152 .timezone = timezone;
153 self
154 }
155
156 pub fn explain_options(mut self, explain_options: Option<ExplainOptions>) -> Self {
157 self.mutable_query_context_data
158 .get_or_insert_default()
159 .write()
160 .unwrap()
161 .explain_options = explain_options;
162 self
163 }
164
165 pub fn skip_wal(mut self, skip_wal: bool) -> Self {
166 self.mutable_session_data
167 .get_or_insert_default()
168 .write()
169 .unwrap()
170 .skip_wal = skip_wal;
171 self
172 }
173
174 pub fn read_preference(mut self, read_preference: ReadPreference) -> Self {
175 self.mutable_session_data
176 .get_or_insert_default()
177 .write()
178 .unwrap()
179 .read_preference = read_preference;
180 self
181 }
182}
183
184impl From<&RegionRequestHeader> for QueryContext {
185 fn from(value: &RegionRequestHeader) -> Self {
186 if let Some(ctx) = &value.query_context {
187 ctx.clone().into()
188 } else {
189 QueryContextBuilder::default()
190 .set_extension(
191 REMOTE_QUERY_ID_EXTENSION_KEY.to_string(),
192 generate_remote_query_id(),
193 )
194 .build()
195 }
196 }
197}
198
199impl From<api::v1::QueryContext> for QueryContext {
200 fn from(ctx: api::v1::QueryContext) -> Self {
201 let sequences = ctx.snapshot_seqs.as_ref();
202 QueryContextBuilder::default()
203 .current_catalog(ctx.current_catalog)
204 .current_schema(ctx.current_schema)
205 .timezone(parse_timezone(Some(&ctx.timezone)))
206 .extensions(ctx.extensions)
207 .channel(ctx.channel.into())
208 .snapshot_seqs(Arc::new(RwLock::new(
209 sequences
210 .map(|x| x.snapshot_seqs.clone())
211 .unwrap_or_default(),
212 )))
213 .sst_min_sequences(Arc::new(RwLock::new(
214 sequences
215 .map(|x| x.sst_min_sequences.clone())
216 .unwrap_or_default(),
217 )))
218 .explain_options(ctx.explain)
219 .build()
220 }
221}
222
223impl From<QueryContext> for api::v1::QueryContext {
224 fn from(
225 QueryContext {
226 current_catalog,
227 mutable_session_data: mutable_inner,
228 extensions,
229 channel,
230 snapshot_seqs,
231 sst_min_sequences,
232 mutable_query_context_data,
233 ..
234 }: QueryContext,
235 ) -> Self {
236 let explain = mutable_query_context_data.read().unwrap().explain_options;
237 let mutable_inner = mutable_inner.read().unwrap();
238 api::v1::QueryContext {
239 current_catalog,
240 current_schema: mutable_inner.schema.clone(),
241 timezone: mutable_inner.timezone.to_string(),
242 extensions,
243 channel: channel as u32,
244 snapshot_seqs: Some(api::v1::SnapshotSequences {
245 snapshot_seqs: snapshot_seqs.read().unwrap().clone(),
246 sst_min_sequences: sst_min_sequences.read().unwrap().clone(),
247 }),
248 explain,
249 }
250 }
251}
252
253impl From<&QueryContext> for api::v1::QueryContext {
254 fn from(ctx: &QueryContext) -> Self {
255 ctx.clone().into()
256 }
257}
258
259impl QueryContext {
260 pub fn fork(&self) -> Self {
265 let mut fork = self.clone();
266 fork.mutable_session_data = Arc::new(RwLock::new(
267 self.mutable_session_data.read().unwrap().clone(),
268 ));
269 fork
270 }
271
272 pub fn with_write_admission(&self) -> Self {
275 let mut ctx = self.fork();
276 ctx.admitted_write = Some((ctx.current_catalog().to_string(), ctx.current_schema()));
277 ctx
278 }
279
280 pub fn write_rows_to_admit(&self, catalog: &str, schema: &str, rows: u64) -> u64 {
283 if self
284 .admitted_write
285 .as_ref()
286 .is_some_and(|(c, s)| c == catalog && s == schema)
287 {
288 0
289 } else {
290 rows
291 }
292 }
293
294 pub fn arc() -> QueryContextRef {
295 Arc::new(
296 QueryContextBuilder::default()
297 .set_extension(
298 REMOTE_QUERY_ID_EXTENSION_KEY.to_string(),
299 generate_remote_query_id(),
300 )
301 .build(),
302 )
303 }
304
305 pub fn create_config_options(&self) -> ConfigOptions {
307 let mut config = ConfigOptions::default();
308 config.execution.time_zone = Some(self.timezone().to_string());
309 config
310 }
311
312 pub fn with(catalog: &str, schema: &str) -> QueryContext {
313 QueryContextBuilder::default()
314 .current_catalog(catalog.to_string())
315 .current_schema(schema.to_string())
316 .set_extension(
317 REMOTE_QUERY_ID_EXTENSION_KEY.to_string(),
318 generate_remote_query_id(),
319 )
320 .build()
321 }
322
323 pub fn with_channel(catalog: &str, schema: &str, channel: Channel) -> QueryContext {
324 QueryContextBuilder::default()
325 .current_catalog(catalog.to_string())
326 .current_schema(schema.to_string())
327 .channel(channel)
328 .set_extension(
329 REMOTE_QUERY_ID_EXTENSION_KEY.to_string(),
330 generate_remote_query_id(),
331 )
332 .build()
333 }
334
335 pub fn with_db_name(db_name: Option<&str>) -> QueryContext {
336 let (catalog, schema) = db_name
337 .map(|db| {
338 let (catalog, schema) = parse_catalog_and_schema_from_db_string(db);
339 (catalog, schema)
340 })
341 .unwrap_or_else(|| {
342 (
343 DEFAULT_CATALOG_NAME.to_string(),
344 DEFAULT_SCHEMA_NAME.to_string(),
345 )
346 });
347 QueryContextBuilder::default()
348 .current_catalog(catalog)
349 .current_schema(schema.clone())
350 .set_extension(
351 REMOTE_QUERY_ID_EXTENSION_KEY.to_string(),
352 generate_remote_query_id(),
353 )
354 .build()
355 }
356
357 pub fn current_schema(&self) -> String {
358 self.mutable_session_data.read().unwrap().schema.clone()
359 }
360
361 pub fn set_current_schema(&self, new_schema: &str) {
362 self.mutable_session_data.write().unwrap().schema = new_schema.to_string();
363 }
364
365 pub fn current_catalog(&self) -> &str {
366 &self.current_catalog
367 }
368
369 pub fn set_current_catalog(&mut self, new_catalog: &str) {
370 self.current_catalog = new_catalog.to_string();
371 }
372
373 pub fn sql_dialect(&self) -> &(dyn Dialect + Send + Sync) {
374 &*self.sql_dialect
375 }
376
377 pub fn get_db_string(&self) -> String {
378 let catalog = self.current_catalog();
379 let schema = self.current_schema();
380 build_db_string(catalog, &schema)
381 }
382
383 pub fn timezone(&self) -> Timezone {
384 self.mutable_session_data.read().unwrap().timezone.clone()
385 }
386
387 pub fn set_timezone(&self, timezone: Timezone) {
388 self.mutable_session_data.write().unwrap().timezone = timezone;
389 }
390
391 pub fn skip_wal(&self) -> bool {
393 self.mutable_session_data.read().unwrap().skip_wal
394 }
395
396 pub fn set_skip_wal(&self, skip_wal: bool) {
397 self.mutable_session_data.write().unwrap().skip_wal = skip_wal;
398 }
399
400 pub fn read_preference(&self) -> ReadPreference {
401 self.mutable_session_data.read().unwrap().read_preference
402 }
403
404 pub fn set_read_preference(&self, read_preference: ReadPreference) {
405 self.mutable_session_data.write().unwrap().read_preference = read_preference;
406 }
407
408 pub fn current_user(&self) -> UserInfoRef {
409 self.mutable_session_data.read().unwrap().user_info.clone()
410 }
411
412 pub fn set_current_user(&self, user: UserInfoRef) {
413 self.mutable_session_data.write().unwrap().user_info = user;
414 }
415
416 pub fn set_extension<S1: Into<String>, S2: Into<String>>(&mut self, key: S1, value: S2) {
417 self.extensions.insert(key.into(), value.into());
418 }
419
420 pub fn extension<S: AsRef<str>>(&self, key: S) -> Option<&str> {
421 self.extensions.get(key.as_ref()).map(|v| v.as_str())
422 }
423
424 pub fn remote_query_id(&self) -> Option<&str> {
425 self.extension(REMOTE_QUERY_ID_EXTENSION_KEY)
426 }
427
428 pub fn remote_query_id_value(&self) -> Option<QueryId> {
429 self.remote_query_id()
430 .and_then(|query_id| query_id.parse().ok())
431 }
432
433 pub fn enable_live_analyze_metrics(&mut self) {
434 if let Some(remote_query_id) = self.remote_query_id().map(str::to_string) {
435 self.set_extension(LIVE_ANALYZE_METRICS_EXTENSION_KEY, remote_query_id);
436 }
437 }
438
439 pub fn live_analyze_metrics_enabled(&self) -> bool {
440 self.remote_query_id()
441 .zip(self.extension(LIVE_ANALYZE_METRICS_EXTENSION_KEY))
442 .is_some_and(|(remote_query_id, value)| value == remote_query_id)
443 }
444
445 pub fn extensions(&self) -> HashMap<String, String> {
446 self.extensions.clone()
447 }
448
449 pub fn quote_style(&self) -> char {
451 if self.sql_dialect().is_delimited_identifier_start('"') {
452 '"'
453 } else if self.sql_dialect().is_delimited_identifier_start('\'') {
454 '\''
455 } else {
456 '`'
457 }
458 }
459
460 pub fn configuration_parameter(&self) -> &ConfigurationVariables {
461 &self.configuration_parameter
462 }
463
464 pub fn logical_batching_enabled(&self) -> bool {
466 self.logical_batching_enabled
467 }
468
469 pub fn set_logical_batching_enabled(&mut self, enabled: bool) {
471 self.logical_batching_enabled = enabled;
472 }
473
474 pub fn batching_enabled(&self) -> bool {
476 self.batching_enabled
477 }
478
479 pub fn set_batching_enabled(&mut self, enabled: bool) {
481 self.batching_enabled = enabled;
482 }
483
484 pub fn channel(&self) -> Channel {
485 self.channel
486 }
487
488 pub fn set_channel(&mut self, channel: Channel) {
489 self.channel = channel;
490 }
491
492 pub fn warning(&self) -> Option<String> {
493 self.mutable_query_context_data
494 .read()
495 .unwrap()
496 .warning
497 .clone()
498 }
499
500 pub fn set_warning(&self, msg: String) {
501 self.mutable_query_context_data.write().unwrap().warning = Some(msg);
502 }
503
504 pub fn explain_format(&self) -> Option<String> {
505 self.mutable_query_context_data
506 .read()
507 .unwrap()
508 .explain_format
509 .clone()
510 }
511
512 pub fn set_explain_format(&self, format: String) {
513 self.mutable_query_context_data
514 .write()
515 .unwrap()
516 .explain_format = Some(format);
517 }
518
519 pub fn explain_verbose(&self) -> bool {
520 self.mutable_query_context_data
521 .read()
522 .unwrap()
523 .explain_options
524 .map(|opts| opts.verbose)
525 .unwrap_or(false)
526 }
527
528 pub fn set_explain_verbose(&self, verbose: bool) {
529 self.mutable_query_context_data
530 .write()
531 .unwrap()
532 .explain_options
533 .get_or_insert_default()
534 .verbose = verbose;
535 }
536
537 pub fn query_timeout(&self) -> Option<Duration> {
538 self.mutable_session_data.read().unwrap().query_timeout
539 }
540
541 pub fn query_timeout_as_millis(&self) -> u128 {
542 let timeout = self.mutable_session_data.read().unwrap().query_timeout;
543 if let Some(t) = timeout {
544 return t.as_millis();
545 }
546 0
547 }
548
549 pub fn set_query_timeout(&self, timeout: Duration) {
550 self.mutable_session_data.write().unwrap().query_timeout = Some(timeout);
551 }
552
553 pub fn insert_cursor(&self, name: String, rb: RecordBatchStreamCursor) {
554 let mut guard = self.mutable_session_data.write().unwrap();
555 guard.cursors.insert(name, Arc::new(rb));
556
557 let cursor_count = guard.cursors.len();
558 if cursor_count > CURSOR_COUNT_WARNING_LIMIT {
559 warn!("Current connection has {} open cursors", cursor_count);
560 }
561 }
562
563 pub fn remove_cursor(&self, name: &str) {
564 let mut guard = self.mutable_session_data.write().unwrap();
565 guard.cursors.remove(name);
566 }
567
568 pub fn get_cursor(&self, name: &str) -> Option<Arc<RecordBatchStreamCursor>> {
569 let guard = self.mutable_session_data.read().unwrap();
570 let rb = guard.cursors.get(name);
571 rb.cloned()
572 }
573
574 pub fn snapshots(&self) -> HashMap<u64, u64> {
575 self.snapshot_seqs.read().unwrap().clone()
576 }
577
578 pub fn sst_min_sequences(&self) -> HashMap<u64, u64> {
579 self.sst_min_sequences.read().unwrap().clone()
580 }
581
582 pub fn get_snapshot(&self, region_id: u64) -> Option<u64> {
583 self.snapshot_seqs.read().unwrap().get(®ion_id).cloned()
584 }
585
586 pub fn set_snapshot(&self, region_id: u64, sequence: u64) {
587 self.snapshot_seqs
588 .write()
589 .unwrap()
590 .insert(region_id, sequence);
591 }
592
593 pub fn auto_string_to_numeric(&self) -> bool {
595 matches!(self.channel, Channel::Mysql)
596 }
597
598 pub fn sst_min_sequence(&self, region_id: u64) -> Option<u64> {
600 self.sst_min_sequences
601 .read()
602 .unwrap()
603 .get(®ion_id)
604 .copied()
605 }
606
607 pub fn process_id(&self) -> u32 {
608 self.process_id
609 }
610
611 pub fn conn_info(&self) -> &ConnInfo {
613 &self.conn_info
614 }
615
616 pub fn protocol_ctx(&self) -> &ProtocolCtx {
617 &self.protocol_ctx
618 }
619
620 pub fn set_protocol_ctx(&mut self, protocol_ctx: ProtocolCtx) {
621 self.protocol_ctx = protocol_ctx;
622 }
623}
624
625impl QueryContextBuilder {
626 pub fn build(self) -> QueryContext {
627 let channel = self.channel.unwrap_or_default();
628 let mut extensions = self.extensions.unwrap_or_default();
629 extensions
630 .entry(REMOTE_QUERY_ID_EXTENSION_KEY.to_string())
631 .or_insert_with(generate_remote_query_id);
632 QueryContext {
633 current_catalog: self
634 .current_catalog
635 .unwrap_or_else(|| DEFAULT_CATALOG_NAME.to_string()),
636 snapshot_seqs: self.snapshot_seqs.unwrap_or_default(),
637 sst_min_sequences: self.sst_min_sequences.unwrap_or_default(),
638 mutable_session_data: self.mutable_session_data.unwrap_or_default(),
639 mutable_query_context_data: self.mutable_query_context_data.unwrap_or_default(),
640 sql_dialect: self
641 .sql_dialect
642 .unwrap_or_else(|| Arc::new(GreptimeDbDialect {})),
643 extensions,
644 configuration_parameter: self
645 .configuration_parameter
646 .unwrap_or_else(|| Arc::new(ConfigurationVariables::default())),
647 channel,
648 batching_enabled: self.batching_enabled.unwrap_or_default(),
649 admitted_write: None,
650 logical_batching_enabled: self.logical_batching_enabled.unwrap_or_default(),
651 process_id: self.process_id.unwrap_or_default(),
652 conn_info: self.conn_info.unwrap_or_default(),
653 protocol_ctx: self.protocol_ctx.unwrap_or_default(),
654 }
655 }
656
657 pub fn set_extension(mut self, key: String, value: String) -> Self {
658 self.extensions
659 .get_or_insert_with(HashMap::new)
660 .insert(key, value);
661 self
662 }
663}
664
665#[derive(Debug, Clone, Default)]
666pub struct ConnInfo {
667 pub client_addr: Option<SocketAddr>,
668 pub channel: Channel,
669}
670
671impl Display for ConnInfo {
672 fn fmt(&self, f: &mut Formatter) -> std::fmt::Result {
673 write!(
674 f,
675 "{}[{}]",
676 self.channel,
677 self.client_addr
678 .map(|addr| addr.to_string())
679 .as_deref()
680 .unwrap_or("unknown client addr")
681 )
682 }
683}
684
685impl ConnInfo {
686 pub fn new(client_addr: Option<SocketAddr>, channel: Channel) -> Self {
687 Self {
688 client_addr,
689 channel,
690 }
691 }
692}
693
694pub fn dialect_for_channel(channel: Channel) -> Arc<dyn Dialect + Send + Sync> {
696 match channel {
697 Channel::Mysql => Arc::new(MySqlDialect {}),
698 Channel::Postgres => Arc::new(PostgreSqlDialect {}),
699 _ => Arc::new(GenericDialect {}),
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 crate::Session;
767 use crate::context::{Channel, *};
768
769 #[test]
770 fn test_session() {
771 let session = Session::new(
772 Some("127.0.0.1:9000".parse().unwrap()),
773 Channel::Mysql,
774 Default::default(),
775 100,
776 );
777 assert_eq!(session.user_info().username(), "greptime");
779
780 assert_eq!(session.conn_info().channel, Channel::Mysql);
782 let client_addr = session.conn_info().client_addr.as_ref().unwrap();
783 assert_eq!(client_addr.ip().to_string(), "127.0.0.1");
784 assert_eq!(client_addr.port(), 9000);
785
786 assert_eq!("mysql[127.0.0.1:9000]", session.conn_info().to_string());
787 assert_eq!(100, session.process_id());
788
789 let query_ctx = session.new_query_context();
790 assert!(query_ctx.remote_query_id().is_some());
791 }
792
793 #[test]
794 fn test_context_db_string() {
795 let context = QueryContext::with("a0b1c2d3", "test");
796 assert_eq!("a0b1c2d3-test", context.get_db_string());
797
798 let context = QueryContext::with(DEFAULT_CATALOG_NAME, "test");
799 assert_eq!("test", context.get_db_string());
800 }
801
802 #[test]
803 fn test_fork_has_independent_mutable_session_data() {
804 let context = QueryContext::with(DEFAULT_CATALOG_NAME, "public");
805 let fork = context.fork();
806
807 fork.set_current_schema("private");
808
809 assert_eq!(context.current_schema(), "public");
810 assert_eq!(fork.current_schema(), "private");
811 }
812
813 #[test]
814 fn test_skip_wal_default_builder_and_fork() {
815 let default_context = QueryContext::with(DEFAULT_CATALOG_NAME, "public");
816 assert!(!default_context.skip_wal());
817 assert!(!QueryContextBuilder::default().build().skip_wal());
818 let context = QueryContextBuilder::default().skip_wal(true).build();
819 assert!(context.skip_wal());
820 let fork = context.fork();
821 assert!(fork.skip_wal());
822 fork.set_skip_wal(false);
823 assert!(context.skip_wal());
824 assert!(!fork.skip_wal());
825 context.set_skip_wal(false);
826 fork.set_skip_wal(true);
827 assert!(!context.skip_wal());
828 assert!(fork.skip_wal());
829 }
830
831 #[test]
832 fn test_write_admission_is_local_and_database_scoped() {
833 let ctx = QueryContext::with_channel("greptime", "public", Channel::Otlp);
834 let admitted = ctx.with_write_admission();
835 assert_eq!(ctx.write_rows_to_admit("greptime", "public", 100), 100);
836 assert_eq!(admitted.write_rows_to_admit("greptime", "public", 100), 0);
837 assert_eq!(admitted.write_rows_to_admit("greptime", "other", 100), 100);
838 assert_eq!(admitted.write_rows_to_admit("other", "public", 100), 100);
839 assert_eq!(admitted.channel(), Channel::Otlp);
840 assert_eq!(
841 admitted
842 .fork()
843 .write_rows_to_admit("greptime", "public", 100),
844 0
845 );
846 let wire: api::v1::QueryContext = admitted.into();
847 let restored = QueryContext::from(wire);
848 assert_eq!(restored.write_rows_to_admit("greptime", "public", 100), 100);
849 }
850
851 #[test]
852 fn test_skip_wal_is_not_serialized_in_query_context() {
853 let context = QueryContextBuilder::default().skip_wal(true).build();
854 let api_context: api::v1::QueryContext = context.into();
855 assert!(
856 !api_context
857 .extensions
858 .contains_key(crate::hints::INSERT_SKIP_WAL_HINT)
859 );
860 let restored: QueryContext = api_context.into();
861 assert!(!restored.skip_wal());
862 }
863
864 #[test]
865 fn test_api_query_context_roundtrip_with_sequences() {
866 let api_ctx = api::v1::QueryContext {
867 current_catalog: "c1".to_string(),
868 current_schema: "s1".to_string(),
869 timezone: "UTC".to_string(),
870 extensions: HashMap::from([("flow.return_region_seq".to_string(), "true".to_string())]),
871 channel: Channel::Internal as u32,
872 snapshot_seqs: Some(api::v1::SnapshotSequences {
873 snapshot_seqs: HashMap::from([(1, 100)]),
874 sst_min_sequences: HashMap::from([(1, 90)]),
875 }),
876 explain: None,
877 };
878
879 let session_ctx: QueryContext = api_ctx.clone().into();
880 let roundtrip_api: api::v1::QueryContext = session_ctx.into();
881
882 assert_eq!(roundtrip_api.current_catalog, api_ctx.current_catalog);
883 assert_eq!(roundtrip_api.current_schema, api_ctx.current_schema);
884 assert_eq!(roundtrip_api.timezone, api_ctx.timezone);
885 assert_eq!(
886 roundtrip_api.extensions.get("flow.return_region_seq"),
887 Some(&"true".to_string())
888 );
889 assert!(
890 roundtrip_api
891 .extensions
892 .contains_key(REMOTE_QUERY_ID_EXTENSION_KEY)
893 );
894 assert_eq!(roundtrip_api.channel, api_ctx.channel);
895 assert_eq!(roundtrip_api.snapshot_seqs, api_ctx.snapshot_seqs);
896 }
897
898 #[test]
899 fn test_query_context_remote_query_id_round_trip() {
900 let query_id = "0195f4fd-c503-7c54-8b8f-7dfb8f6f9c4a";
901 let ctx = QueryContextBuilder::default()
902 .current_catalog(DEFAULT_CATALOG_NAME.to_string())
903 .current_schema("public".to_string())
904 .set_extension(
905 REMOTE_QUERY_ID_EXTENSION_KEY.to_string(),
906 query_id.to_string(),
907 )
908 .build();
909
910 assert_eq!(ctx.remote_query_id(), Some(query_id));
911 assert_eq!(ctx.remote_query_id_value().unwrap().to_string(), query_id);
912
913 let proto: api::v1::QueryContext = (&ctx).into();
914 let restored = QueryContext::from(proto);
915 assert_eq!(restored.remote_query_id(), Some(query_id));
916 assert_eq!(
917 restored.remote_query_id_value().unwrap().to_string(),
918 query_id
919 );
920 }
921
922 #[test]
923 fn test_live_analyze_metrics_requires_matching_remote_query_id() {
924 let mut ctx = QueryContext::arc().as_ref().clone();
925 assert!(!ctx.live_analyze_metrics_enabled());
926
927 ctx.enable_live_analyze_metrics();
928 assert!(ctx.live_analyze_metrics_enabled());
929
930 ctx.set_extension(LIVE_ANALYZE_METRICS_EXTENSION_KEY, "true");
931 assert!(!ctx.live_analyze_metrics_enabled());
932
933 ctx.set_extension(LIVE_ANALYZE_METRICS_EXTENSION_KEY, "another-query-id");
934 assert!(!ctx.live_analyze_metrics_enabled());
935 }
936 #[test]
937 fn test_batching_selection_is_local_only() {
938 let mut ctx = QueryContextBuilder::default().build();
939 assert!(!ctx.batching_enabled());
940 assert!(!ctx.logical_batching_enabled());
941 ctx.set_logical_batching_enabled(true);
942 assert!(ctx.clone().logical_batching_enabled());
943 assert!(ctx.fork().logical_batching_enabled());
944 ctx.set_batching_enabled(true);
945 assert!(ctx.clone().batching_enabled());
946 assert!(ctx.fork().batching_enabled());
947 let wire: api::v1::QueryContext = ctx.into();
948 let restored = QueryContext::from(wire);
949 assert!(!restored.batching_enabled());
950 assert!(!restored.logical_batching_enabled());
951 let ctx = QueryContextBuilder::default()
952 .set_extension("batching_enabled".to_string(), "true".to_string())
953 .build();
954 assert!(!ctx.batching_enabled());
955 }
956}