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