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::write_progress::WriteProgress;
47use crate::{MutableInner, ReadPreference};
48
49pub type QueryContextRef = Arc<QueryContext>;
50pub type ConnInfoRef = Arc<ConnInfo>;
51
52pub const FLIGHT_METRICS_HEARTBEAT_INTERVAL: Duration = Duration::from_secs(1);
53
54const CURSOR_COUNT_WARNING_LIMIT: usize = 10;
55
56pub fn generate_remote_query_id() -> String {
57 generate_remote_query_id_value().to_string()
58}
59
60pub fn generate_remote_query_id_value() -> QueryId {
61 QueryId::new()
62}
63
64#[derive(Debug, Builder, Clone)]
65#[builder(pattern = "owned")]
66#[builder(build_fn(skip))]
67pub struct QueryContext {
68 current_catalog: String,
69 snapshot_seqs: Arc<RwLock<HashMap<u64, u64>>>,
72 sst_min_sequences: Arc<RwLock<HashMap<u64, u64>>>,
74 #[builder(default)]
76 mutable_session_data: Arc<RwLock<MutableInner>>,
77 #[builder(default)]
78 mutable_query_context_data: Arc<RwLock<QueryContextMutableFields>>,
79 sql_dialect: Arc<dyn Dialect + Send + Sync>,
80 #[builder(default)]
81 extensions: HashMap<String, String>,
82 #[builder(setter(skip))]
83 query_options_snapshotted: bool,
84 #[builder(default)]
86 configuration_parameter: Arc<ConfigurationVariables>,
87 #[builder(default)]
89 batching_enabled: bool,
90 #[builder(default)]
92 logical_batching_enabled: bool,
93 #[builder(default)]
95 channel: Channel,
96 #[builder(setter(skip))]
98 admitted_write: Option<(String, String)>,
99 #[builder(setter(skip))]
101 write_progress: Option<Arc<WriteProgress>>,
102 #[builder(setter(skip))]
104 confirmed_writes: bool,
105 #[builder(default)]
107 process_id: u32,
108 #[builder(default)]
110 conn_info: ConnInfo,
111 #[builder(default)]
113 protocol_ctx: ProtocolCtx,
114}
115
116#[derive(Debug, Builder, Clone, Default)]
118pub struct QueryContextMutableFields {
119 warning: Option<String>,
120 explain_format: Option<String>,
122 explain_options: Option<ExplainOptions>,
124}
125
126impl Display for QueryContext {
127 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
128 write!(
129 f,
130 "QueryContext{{catalog: {}, schema: {}}}",
131 self.current_catalog(),
132 self.current_schema()
133 )
134 }
135}
136
137impl QueryContextBuilder {
138 pub fn current_schema(mut self, schema: String) -> Self {
139 if self.mutable_session_data.is_none() {
140 self.mutable_session_data = Some(Arc::new(RwLock::new(MutableInner::default())));
141 }
142
143 self.mutable_session_data
145 .as_mut()
146 .unwrap()
147 .write()
148 .unwrap()
149 .schema = schema;
150 self
151 }
152
153 pub fn timezone(mut self, timezone: Timezone) -> Self {
154 if self.mutable_session_data.is_none() {
155 self.mutable_session_data = Some(Arc::new(RwLock::new(MutableInner::default())));
156 }
157
158 self.mutable_session_data
159 .as_mut()
160 .unwrap()
161 .write()
162 .unwrap()
163 .timezone = timezone;
164 self
165 }
166
167 pub fn explain_options(mut self, explain_options: Option<ExplainOptions>) -> Self {
168 self.mutable_query_context_data
169 .get_or_insert_default()
170 .write()
171 .unwrap()
172 .explain_options = explain_options;
173 self
174 }
175
176 pub fn skip_wal(mut self, skip_wal: bool) -> Self {
177 self.mutable_session_data
178 .get_or_insert_default()
179 .write()
180 .unwrap()
181 .skip_wal = skip_wal;
182 self
183 }
184
185 pub fn read_preference(mut self, read_preference: ReadPreference) -> Self {
186 self.mutable_session_data
187 .get_or_insert_default()
188 .write()
189 .unwrap()
190 .read_preference = read_preference;
191 self
192 }
193}
194
195impl TryFrom<&RegionRequestHeader> for QueryContext {
196 type Error = crate::session_config::Error;
197
198 fn try_from(value: &RegionRequestHeader) -> Result<Self, Self::Error> {
199 if let Some(ctx) = &value.query_context {
200 ctx.clone().try_into()
201 } else {
202 Ok(QueryContextBuilder::default()
203 .set_extension(
204 REMOTE_QUERY_ID_EXTENSION_KEY.to_string(),
205 generate_remote_query_id(),
206 )
207 .build())
208 }
209 }
210}
211
212impl TryFrom<api::v1::QueryContext> for QueryContext {
213 type Error = crate::session_config::Error;
214
215 fn try_from(mut ctx: api::v1::QueryContext) -> Result<Self, Self::Error> {
216 let read_preference = ctx.extensions.remove(READ_PREFERENCE_EXTENSION_KEY);
217 let read_preference = match read_preference {
218 Some(value) => ReadPreference::from_str(&value).map_err(|_| {
219 InvalidConfigValueSnafu {
220 name: "read_preference",
221 value,
222 hint: "Expected a supported read preference",
223 }
224 .build()
225 })?,
226 None => ReadPreference::default(),
227 };
228 let sequences = ctx.snapshot_seqs.as_ref();
229 Ok(QueryContextBuilder::default()
230 .current_catalog(ctx.current_catalog)
231 .current_schema(ctx.current_schema)
232 .timezone(parse_timezone(Some(&ctx.timezone)))
233 .extensions(ctx.extensions)
234 .channel(ctx.channel.into())
235 .snapshot_seqs(Arc::new(RwLock::new(
236 sequences
237 .map(|x| x.snapshot_seqs.clone())
238 .unwrap_or_default(),
239 )))
240 .sst_min_sequences(Arc::new(RwLock::new(
241 sequences
242 .map(|x| x.sst_min_sequences.clone())
243 .unwrap_or_default(),
244 )))
245 .explain_options(ctx.explain)
246 .read_preference(read_preference)
247 .build())
248 }
249}
250
251impl From<QueryContext> for api::v1::QueryContext {
252 fn from(
253 QueryContext {
254 current_catalog,
255 mutable_session_data: mutable_inner,
256 extensions,
257 channel,
258 snapshot_seqs,
259 sst_min_sequences,
260 mutable_query_context_data,
261 ..
262 }: QueryContext,
263 ) -> Self {
264 let explain = mutable_query_context_data.read().unwrap().explain_options;
265 let mutable_inner = mutable_inner.read().unwrap();
266 let mut extensions = extensions;
267 extensions.remove(READ_PREFERENCE_EXTENSION_KEY);
268 if mutable_inner.read_preference != ReadPreference::Leader {
269 extensions.insert(
270 READ_PREFERENCE_EXTENSION_KEY.to_string(),
271 mutable_inner
272 .read_preference
273 .to_string()
274 .to_ascii_lowercase(),
275 );
276 }
277 api::v1::QueryContext {
278 current_catalog,
279 current_schema: mutable_inner.schema.clone(),
280 timezone: mutable_inner.timezone.to_string(),
281 extensions,
282 channel: channel as u32,
283 snapshot_seqs: Some(api::v1::SnapshotSequences {
284 snapshot_seqs: snapshot_seqs.read().unwrap().clone(),
285 sst_min_sequences: sst_min_sequences.read().unwrap().clone(),
286 }),
287 explain,
288 }
289 }
290}
291
292impl From<&QueryContext> for api::v1::QueryContext {
293 fn from(ctx: &QueryContext) -> Self {
294 ctx.clone().into()
295 }
296}
297
298impl QueryContext {
299 pub fn write_progress(&self) -> Option<&Arc<WriteProgress>> {
300 self.write_progress.as_ref()
301 }
302
303 pub fn set_write_progress(&mut self, progress: Arc<WriteProgress>) {
304 self.write_progress = Some(progress);
305 }
306
307 pub fn with_confirmed_writes(&self) -> Self {
314 let mut ctx = self.fork();
315 ctx.confirmed_writes = true;
316 ctx
317 }
318
319 pub fn confirmed_writes(&self) -> bool {
322 self.confirmed_writes
323 }
324
325 pub fn fork(&self) -> Self {
330 let mut fork = self.clone();
331 fork.mutable_session_data = Arc::new(RwLock::new(
332 self.mutable_session_data.read().unwrap().clone(),
333 ));
334 fork
335 }
336
337 pub fn with_write_admission(&self) -> Self {
340 let mut ctx = self.fork();
341 ctx.admitted_write = Some((ctx.current_catalog().to_string(), ctx.current_schema()));
342 ctx
343 }
344
345 pub fn write_rows_to_admit(&self, catalog: &str, schema: &str, rows: u64) -> u64 {
348 if self
349 .admitted_write
350 .as_ref()
351 .is_some_and(|(c, s)| c == catalog && s == schema)
352 {
353 0
354 } else {
355 rows
356 }
357 }
358
359 pub fn arc() -> QueryContextRef {
360 Arc::new(
361 QueryContextBuilder::default()
362 .set_extension(
363 REMOTE_QUERY_ID_EXTENSION_KEY.to_string(),
364 generate_remote_query_id(),
365 )
366 .build(),
367 )
368 }
369
370 pub fn create_config_options(&self) -> ConfigOptions {
372 let mut config = ConfigOptions::default();
373 config.execution.time_zone = Some(self.timezone().to_string());
374 config
375 }
376
377 pub fn with(catalog: &str, schema: &str) -> QueryContext {
378 QueryContextBuilder::default()
379 .current_catalog(catalog.to_string())
380 .current_schema(schema.to_string())
381 .set_extension(
382 REMOTE_QUERY_ID_EXTENSION_KEY.to_string(),
383 generate_remote_query_id(),
384 )
385 .build()
386 }
387
388 pub fn with_channel(catalog: &str, schema: &str, channel: Channel) -> QueryContext {
389 QueryContextBuilder::default()
390 .current_catalog(catalog.to_string())
391 .current_schema(schema.to_string())
392 .channel(channel)
393 .set_extension(
394 REMOTE_QUERY_ID_EXTENSION_KEY.to_string(),
395 generate_remote_query_id(),
396 )
397 .build()
398 }
399
400 pub fn with_db_name(db_name: Option<&str>) -> QueryContext {
401 let (catalog, schema) = db_name
402 .map(|db| {
403 let (catalog, schema) = parse_catalog_and_schema_from_db_string(db);
404 (catalog, schema)
405 })
406 .unwrap_or_else(|| {
407 (
408 DEFAULT_CATALOG_NAME.to_string(),
409 DEFAULT_SCHEMA_NAME.to_string(),
410 )
411 });
412 QueryContextBuilder::default()
413 .current_catalog(catalog)
414 .current_schema(schema.clone())
415 .set_extension(
416 REMOTE_QUERY_ID_EXTENSION_KEY.to_string(),
417 generate_remote_query_id(),
418 )
419 .build()
420 }
421
422 pub fn current_schema(&self) -> String {
423 self.mutable_session_data.read().unwrap().schema.clone()
424 }
425
426 pub fn set_current_schema(&self, new_schema: &str) {
427 self.mutable_session_data.write().unwrap().schema = new_schema.to_string();
428 }
429
430 pub fn current_catalog(&self) -> &str {
431 &self.current_catalog
432 }
433
434 pub fn set_current_catalog(&mut self, new_catalog: &str) {
435 self.current_catalog = new_catalog.to_string();
436 }
437
438 pub fn sql_dialect(&self) -> &(dyn Dialect + Send + Sync) {
439 &*self.sql_dialect
440 }
441
442 pub fn get_db_string(&self) -> String {
443 let catalog = self.current_catalog();
444 let schema = self.current_schema();
445 build_db_string(catalog, &schema)
446 }
447
448 pub fn timezone(&self) -> Timezone {
449 self.mutable_session_data.read().unwrap().timezone.clone()
450 }
451
452 pub fn set_timezone(&self, timezone: Timezone) {
453 self.mutable_session_data.write().unwrap().timezone = timezone;
454 }
455
456 pub fn skip_wal(&self) -> bool {
458 self.mutable_session_data.read().unwrap().skip_wal
459 }
460
461 pub fn set_skip_wal(&self, skip_wal: bool) {
462 self.mutable_session_data.write().unwrap().skip_wal = skip_wal;
463 }
464
465 pub fn read_preference(&self) -> ReadPreference {
466 self.mutable_session_data.read().unwrap().read_preference
467 }
468
469 pub fn set_read_preference(&self, read_preference: ReadPreference) {
470 self.mutable_session_data.write().unwrap().read_preference = read_preference;
471 }
472
473 pub fn last_procedure_id(&self) -> Option<String> {
475 self.mutable_session_data
476 .read()
477 .unwrap()
478 .last_procedure_id
479 .clone()
480 }
481
482 pub fn set_last_procedure_id(&self, procedure_id: String) {
488 self.mutable_session_data.write().unwrap().last_procedure_id = Some(procedure_id);
489 }
490
491 pub fn current_user(&self) -> UserInfoRef {
492 self.mutable_session_data.read().unwrap().user_info.clone()
493 }
494
495 pub fn set_current_user(&self, user: UserInfoRef) {
496 self.mutable_session_data.write().unwrap().user_info = user;
497 }
498
499 pub fn set_extension<S1: Into<String>, S2: Into<String>>(&mut self, key: S1, value: S2) {
500 self.extensions.insert(key.into(), value.into());
501 }
502
503 pub fn extension<S: AsRef<str>>(&self, key: S) -> Option<&str> {
504 self.extensions.get(key.as_ref()).map(|v| v.as_str())
505 }
506
507 pub fn remote_query_id(&self) -> Option<&str> {
508 self.extension(REMOTE_QUERY_ID_EXTENSION_KEY)
509 }
510
511 pub fn remote_query_id_value(&self) -> Option<QueryId> {
512 self.remote_query_id()
513 .and_then(|query_id| query_id.parse().ok())
514 }
515
516 pub fn enable_live_analyze_metrics(&mut self) {
517 if let Some(remote_query_id) = self.remote_query_id().map(str::to_string) {
518 self.set_extension(LIVE_ANALYZE_METRICS_EXTENSION_KEY, remote_query_id);
519 }
520 }
521
522 pub fn live_analyze_metrics_enabled(&self) -> bool {
523 self.remote_query_id()
524 .zip(self.extension(LIVE_ANALYZE_METRICS_EXTENSION_KEY))
525 .is_some_and(|(remote_query_id, value)| value == remote_query_id)
526 }
527
528 pub fn extensions(&self) -> HashMap<String, String> {
529 self.extensions.clone()
530 }
531
532 pub fn effective_query_options(
534 &self,
535 ) -> Result<HashMap<String, String>, crate::session_config::Error> {
536 let mut request_options = HashMap::new();
537 for (key, value) in &self.extensions {
538 if crate::hints::is_reserved_extension_key(key) {
539 continue;
540 }
541 if key.eq_ignore_ascii_case("query_parallelism") && value == "0" {
547 continue;
548 }
549 if let Some((canonical, normalized)) =
551 crate::query_options::parse_query_option(key, value, u16::MAX)?
552 {
553 if request_options
554 .get(&canonical)
555 .is_some_and(|previous| previous != &normalized)
556 {
557 return crate::session_config::InvalidConfigValueSnafu {
558 name: key,
559 value,
560 hint: "Conflicting aliases for the same query option",
561 }
562 .fail();
563 }
564 request_options.insert(canonical, normalized);
565 }
566 }
567 let mut effective = if self.query_options_snapshotted {
568 HashMap::new()
569 } else {
570 self.configuration_parameter.query_options()
571 };
572 effective.extend(request_options);
573 Ok(effective)
574 }
575
576 pub fn query_option_snapshot(&self) -> Result<Self, crate::session_config::Error> {
578 let mut snapshot = self.clone();
579 let options = snapshot.effective_query_options()?;
580 snapshot
581 .extensions
582 .retain(|key, _| crate::query_options::canonical_query_option_name(key).is_none());
583 let legacy_parallelism = options
587 .get("query.parallelism")
588 .map(|parallelism| ("query_parallelism".to_string(), parallelism.clone()));
589 snapshot.extensions.extend(options);
590 if let Some((key, value)) = legacy_parallelism {
591 snapshot.extensions.entry(key).or_insert(value);
592 }
593 snapshot.query_options_snapshotted = true;
594 Ok(snapshot)
595 }
596
597 pub fn quote_style(&self) -> char {
599 if self.sql_dialect().is_delimited_identifier_start('"') {
600 '"'
601 } else if self.sql_dialect().is_delimited_identifier_start('\'') {
602 '\''
603 } else {
604 '`'
605 }
606 }
607
608 pub fn configuration_parameter(&self) -> &ConfigurationVariables {
609 &self.configuration_parameter
610 }
611
612 pub fn logical_batching_enabled(&self) -> bool {
614 self.logical_batching_enabled
615 }
616
617 pub fn set_logical_batching_enabled(&mut self, enabled: bool) {
619 self.logical_batching_enabled = enabled;
620 }
621
622 pub fn batching_enabled(&self) -> bool {
624 self.batching_enabled
625 }
626
627 pub fn set_batching_enabled(&mut self, enabled: bool) {
629 self.batching_enabled = enabled;
630 }
631
632 pub fn channel(&self) -> Channel {
633 self.channel
634 }
635
636 pub fn set_channel(&mut self, channel: Channel) {
637 self.channel = channel;
638 }
639
640 pub fn warning(&self) -> Option<String> {
641 self.mutable_query_context_data
642 .read()
643 .unwrap()
644 .warning
645 .clone()
646 }
647
648 pub fn set_warning(&self, msg: String) {
649 self.mutable_query_context_data.write().unwrap().warning = Some(msg);
650 }
651
652 pub fn explain_format(&self) -> Option<String> {
653 self.mutable_query_context_data
654 .read()
655 .unwrap()
656 .explain_format
657 .clone()
658 }
659
660 pub fn set_explain_format(&self, format: String) {
661 self.mutable_query_context_data
662 .write()
663 .unwrap()
664 .explain_format = Some(format);
665 }
666
667 pub fn explain_verbose(&self) -> bool {
668 self.mutable_query_context_data
669 .read()
670 .unwrap()
671 .explain_options
672 .map(|opts| opts.verbose)
673 .unwrap_or(false)
674 }
675
676 pub fn set_explain_verbose(&self, verbose: bool) {
677 self.mutable_query_context_data
678 .write()
679 .unwrap()
680 .explain_options
681 .get_or_insert_default()
682 .verbose = verbose;
683 }
684
685 pub fn query_timeout(&self) -> Option<Duration> {
686 self.mutable_session_data.read().unwrap().query_timeout
687 }
688
689 pub fn query_timeout_as_millis(&self) -> u128 {
690 let timeout = self.mutable_session_data.read().unwrap().query_timeout;
691 if let Some(t) = timeout {
692 return t.as_millis();
693 }
694 0
695 }
696
697 pub fn set_query_timeout(&self, timeout: Duration) {
698 self.mutable_session_data.write().unwrap().query_timeout = Some(timeout);
699 }
700
701 pub fn insert_cursor(&self, name: String, rb: RecordBatchStreamCursor) {
702 let mut guard = self.mutable_session_data.write().unwrap();
703 guard.cursors.insert(name, Arc::new(rb));
704
705 let cursor_count = guard.cursors.len();
706 if cursor_count > CURSOR_COUNT_WARNING_LIMIT {
707 warn!("Current connection has {} open cursors", cursor_count);
708 }
709 }
710
711 pub fn remove_cursor(&self, name: &str) {
712 let mut guard = self.mutable_session_data.write().unwrap();
713 guard.cursors.remove(name);
714 }
715
716 pub fn get_cursor(&self, name: &str) -> Option<Arc<RecordBatchStreamCursor>> {
717 let guard = self.mutable_session_data.read().unwrap();
718 let rb = guard.cursors.get(name);
719 rb.cloned()
720 }
721
722 pub fn snapshots(&self) -> HashMap<u64, u64> {
723 self.snapshot_seqs.read().unwrap().clone()
724 }
725
726 pub fn sst_min_sequences(&self) -> HashMap<u64, u64> {
727 self.sst_min_sequences.read().unwrap().clone()
728 }
729
730 pub fn get_snapshot(&self, region_id: u64) -> Option<u64> {
731 self.snapshot_seqs.read().unwrap().get(®ion_id).cloned()
732 }
733
734 pub fn set_snapshot(&self, region_id: u64, sequence: u64) {
735 self.snapshot_seqs
736 .write()
737 .unwrap()
738 .insert(region_id, sequence);
739 }
740
741 pub fn auto_string_to_numeric(&self) -> bool {
743 matches!(self.channel, Channel::Mysql)
744 }
745
746 pub fn sst_min_sequence(&self, region_id: u64) -> Option<u64> {
748 self.sst_min_sequences
749 .read()
750 .unwrap()
751 .get(®ion_id)
752 .copied()
753 }
754
755 pub fn process_id(&self) -> u32 {
756 self.process_id
757 }
758
759 pub fn conn_info(&self) -> &ConnInfo {
761 &self.conn_info
762 }
763
764 pub fn protocol_ctx(&self) -> &ProtocolCtx {
765 &self.protocol_ctx
766 }
767
768 pub fn set_protocol_ctx(&mut self, protocol_ctx: ProtocolCtx) {
769 self.protocol_ctx = protocol_ctx;
770 }
771}
772
773impl QueryContextBuilder {
774 pub fn build(self) -> QueryContext {
775 let channel = self.channel.unwrap_or_default();
776 let mut extensions = self.extensions.unwrap_or_default();
777 extensions
778 .entry(REMOTE_QUERY_ID_EXTENSION_KEY.to_string())
779 .or_insert_with(generate_remote_query_id);
780 QueryContext {
781 current_catalog: self
782 .current_catalog
783 .unwrap_or_else(|| DEFAULT_CATALOG_NAME.to_string()),
784 snapshot_seqs: self.snapshot_seqs.unwrap_or_default(),
785 sst_min_sequences: self.sst_min_sequences.unwrap_or_default(),
786 mutable_session_data: self.mutable_session_data.unwrap_or_default(),
787 mutable_query_context_data: self.mutable_query_context_data.unwrap_or_default(),
788 sql_dialect: self
789 .sql_dialect
790 .unwrap_or_else(|| Arc::new(GreptimeDbDialect {})),
791 extensions,
792 query_options_snapshotted: false,
793 configuration_parameter: self
794 .configuration_parameter
795 .unwrap_or_else(|| Arc::new(ConfigurationVariables::default())),
796 channel,
797 batching_enabled: self.batching_enabled.unwrap_or_default(),
798 admitted_write: None,
799 write_progress: None,
800 confirmed_writes: false,
801 logical_batching_enabled: self.logical_batching_enabled.unwrap_or_default(),
802 process_id: self.process_id.unwrap_or_default(),
803 conn_info: self.conn_info.unwrap_or_default(),
804 protocol_ctx: self.protocol_ctx.unwrap_or_default(),
805 }
806 }
807
808 pub fn set_extension(mut self, key: String, value: String) -> Self {
809 self.extensions
810 .get_or_insert_with(HashMap::new)
811 .insert(key, value);
812 self
813 }
814}
815
816#[derive(Debug, Clone, Default)]
817pub struct ConnInfo {
818 pub client_addr: Option<SocketAddr>,
819 pub channel: Channel,
820}
821
822impl Display for ConnInfo {
823 fn fmt(&self, f: &mut Formatter) -> std::fmt::Result {
824 write!(
825 f,
826 "{}[{}]",
827 self.channel,
828 self.client_addr
829 .map(|addr| addr.to_string())
830 .as_deref()
831 .unwrap_or("unknown client addr")
832 )
833 }
834}
835
836impl ConnInfo {
837 pub fn new(client_addr: Option<SocketAddr>, channel: Channel) -> Self {
838 Self {
839 client_addr,
840 channel,
841 }
842 }
843}
844
845pub fn dialect_for_channel(channel: Channel) -> Arc<dyn Dialect + Send + Sync> {
847 match channel {
848 Channel::Mysql => Arc::new(MySqlDialect {}),
849 Channel::Postgres => Arc::new(PostgreSqlDialect {}),
850 _ => Arc::new(GenericDialect {}),
851 }
852}
853
854#[derive(Default, Debug)]
855pub struct ConfigurationVariables {
856 postgres_bytea_output: ArcSwap<PGByteaOutputValue>,
857 pg_datestyle_format: ArcSwap<(PGDateTimeStyle, PGDateOrder)>,
858 pg_intervalstyle_format: ArcSwap<PGIntervalStyle>,
859 query_options: RwLock<HashMap<String, String>>,
860}
861
862impl Clone for ConfigurationVariables {
863 fn clone(&self) -> Self {
864 Self {
865 postgres_bytea_output: ArcSwap::new(self.postgres_bytea_output.load().clone()),
866 pg_datestyle_format: ArcSwap::new(self.pg_datestyle_format.load().clone()),
867 pg_intervalstyle_format: ArcSwap::new(self.pg_intervalstyle_format.load().clone()),
868 query_options: RwLock::new(self.query_options.read().unwrap().clone()),
869 }
870 }
871}
872
873impl ConfigurationVariables {
874 pub fn new() -> Self {
875 Self::default()
876 }
877
878 pub fn set_postgres_bytea_output(&self, value: PGByteaOutputValue) {
879 let _ = self.postgres_bytea_output.swap(Arc::new(value));
880 }
881
882 pub fn postgres_bytea_output(&self) -> Arc<PGByteaOutputValue> {
883 self.postgres_bytea_output.load().clone()
884 }
885
886 pub fn pg_datetime_style(&self) -> Arc<(PGDateTimeStyle, PGDateOrder)> {
887 self.pg_datestyle_format.load().clone()
888 }
889
890 pub fn set_pg_datetime_style(&self, style: PGDateTimeStyle, order: PGDateOrder) {
891 self.pg_datestyle_format.swap(Arc::new((style, order)));
892 }
893
894 pub fn pg_intervalstyle_format(&self) -> Arc<PGIntervalStyle> {
895 self.pg_intervalstyle_format.load().clone()
896 }
897
898 pub fn set_pg_intervalstyle_format(&self, value: PGIntervalStyle) {
899 self.pg_intervalstyle_format.swap(Arc::new(value));
900 }
901
902 pub fn allow_query_fallback(&self) -> bool {
903 self.query_options().get("query.allow_query_fallback") == Some(&"true".to_string())
904 }
905
906 pub fn set_allow_query_fallback(&self, allow: bool) {
907 self.query_options
908 .write()
909 .unwrap()
910 .insert("query.allow_query_fallback".to_string(), allow.to_string());
911 }
912
913 pub fn set_query_option(
915 &self,
916 key: &str,
917 value: &str,
918 ) -> Result<(), crate::session_config::Error> {
919 let (canonical, normalized) = crate::query_options::parse_query_option(
920 key,
921 value,
922 crate::query_options::MAX_PUBLIC_PARALLELISM,
923 )?
924 .ok_or_else(|| crate::session_config::Error::InvalidConfigValue {
925 name: key.to_string(),
926 value: value.to_string(),
927 hint: "Not a supported query option".to_string(),
928 location: snafu::Location::new(file!(), line!(), column!()),
929 })?;
930 self.query_options
931 .write()
932 .unwrap()
933 .insert(canonical, normalized);
934 Ok(())
935 }
936
937 pub fn query_options(&self) -> HashMap<String, String> {
939 self.query_options.read().unwrap().clone()
940 }
941}
942
943#[cfg(test)]
944mod test {
945 use std::collections::HashMap;
946
947 use common_catalog::consts::DEFAULT_CATALOG_NAME;
948
949 use crate::Session;
950 use crate::context::{Channel, *};
951
952 #[test]
953 fn test_query_parallelism_wire_accepts_legacy_value() {
954 let mut context = QueryContext::with(DEFAULT_CATALOG_NAME, "public");
955 context.set_extension("query_parallelism", "2048");
956
957 assert_eq!(
958 context
959 .effective_query_options()
960 .unwrap()
961 .get("query.parallelism"),
962 Some(&"2048".to_string())
963 );
964 }
965
966 #[test]
967 fn test_set_query_parallelism_public_limit() {
968 let context = QueryContext::with(DEFAULT_CATALOG_NAME, "public");
969 assert!(
970 context
971 .configuration_parameter()
972 .set_query_option("query.parallelism", "1025")
973 .is_err()
974 );
975 assert!(
976 context
977 .configuration_parameter()
978 .set_query_option("query.parallelism", "1024")
979 .is_ok()
980 );
981 }
982
983 #[test]
984 fn test_session() {
985 let session = Session::new(
986 Some("127.0.0.1:9000".parse().unwrap()),
987 Channel::Mysql,
988 Default::default(),
989 100,
990 );
991 assert_eq!(session.user_info().username(), "greptime");
993
994 assert_eq!(session.conn_info().channel, Channel::Mysql);
996 let client_addr = session.conn_info().client_addr.as_ref().unwrap();
997 assert_eq!(client_addr.ip().to_string(), "127.0.0.1");
998 assert_eq!(client_addr.port(), 9000);
999
1000 assert_eq!("mysql[127.0.0.1:9000]", session.conn_info().to_string());
1001 assert_eq!(100, session.process_id());
1002
1003 let query_ctx = session.new_query_context();
1004 assert!(query_ctx.remote_query_id().is_some());
1005 }
1006
1007 #[test]
1008 fn test_context_db_string() {
1009 let context = QueryContext::with("a0b1c2d3", "test");
1010 assert_eq!("a0b1c2d3-test", context.get_db_string());
1011
1012 let context = QueryContext::with(DEFAULT_CATALOG_NAME, "test");
1013 assert_eq!("test", context.get_db_string());
1014 }
1015
1016 #[test]
1017 fn test_fork_has_independent_mutable_session_data() {
1018 let context = QueryContext::with(DEFAULT_CATALOG_NAME, "public");
1019 let fork = context.fork();
1020
1021 fork.set_current_schema("private");
1022
1023 assert_eq!(context.current_schema(), "public");
1024 assert_eq!(fork.current_schema(), "private");
1025 }
1026
1027 #[test]
1028 fn test_skip_wal_default_builder_and_fork() {
1029 let default_context = QueryContext::with(DEFAULT_CATALOG_NAME, "public");
1030 assert!(!default_context.skip_wal());
1031 assert!(!QueryContextBuilder::default().build().skip_wal());
1032 let context = QueryContextBuilder::default().skip_wal(true).build();
1033 assert!(context.skip_wal());
1034 let fork = context.fork();
1035 assert!(fork.skip_wal());
1036 fork.set_skip_wal(false);
1037 assert!(context.skip_wal());
1038 assert!(!fork.skip_wal());
1039 context.set_skip_wal(false);
1040 fork.set_skip_wal(true);
1041 assert!(!context.skip_wal());
1042 assert!(fork.skip_wal());
1043 }
1044
1045 #[test]
1046 fn test_write_admission_is_local_and_database_scoped() {
1047 let ctx = QueryContext::with_channel("greptime", "public", Channel::Otlp);
1048 let admitted = ctx.with_write_admission();
1049 assert_eq!(ctx.write_rows_to_admit("greptime", "public", 100), 100);
1050 assert_eq!(admitted.write_rows_to_admit("greptime", "public", 100), 0);
1051 assert_eq!(admitted.write_rows_to_admit("greptime", "other", 100), 100);
1052 assert_eq!(admitted.write_rows_to_admit("other", "public", 100), 100);
1053 assert_eq!(admitted.channel(), Channel::Otlp);
1054 assert_eq!(
1055 admitted
1056 .fork()
1057 .write_rows_to_admit("greptime", "public", 100),
1058 0
1059 );
1060 let wire: api::v1::QueryContext = admitted.into();
1061 let restored = QueryContext::try_from(wire).unwrap();
1062 assert_eq!(restored.write_rows_to_admit("greptime", "public", 100), 100);
1063 }
1064
1065 #[test]
1066 fn test_skip_wal_is_not_serialized_in_query_context() {
1067 let context = QueryContextBuilder::default().skip_wal(true).build();
1068 let api_context: api::v1::QueryContext = context.into();
1069 assert!(
1070 !api_context
1071 .extensions
1072 .contains_key(crate::hints::INSERT_SKIP_WAL_HINT)
1073 );
1074 let restored: QueryContext = api_context.try_into().unwrap();
1075 assert!(!restored.skip_wal());
1076 }
1077
1078 #[test]
1079 fn test_api_query_context_roundtrip_with_sequences() {
1080 let api_ctx = api::v1::QueryContext {
1081 current_catalog: "c1".to_string(),
1082 current_schema: "s1".to_string(),
1083 timezone: "UTC".to_string(),
1084 extensions: HashMap::from([("flow.return_region_seq".to_string(), "true".to_string())]),
1085 channel: Channel::Internal as u32,
1086 snapshot_seqs: Some(api::v1::SnapshotSequences {
1087 snapshot_seqs: HashMap::from([(1, 100)]),
1088 sst_min_sequences: HashMap::from([(1, 90)]),
1089 }),
1090 explain: None,
1091 };
1092
1093 let session_ctx: QueryContext = api_ctx.clone().try_into().unwrap();
1094 let roundtrip_api: api::v1::QueryContext = session_ctx.into();
1095
1096 assert_eq!(roundtrip_api.current_catalog, api_ctx.current_catalog);
1097 assert_eq!(roundtrip_api.current_schema, api_ctx.current_schema);
1098 assert_eq!(roundtrip_api.timezone, api_ctx.timezone);
1099 assert_eq!(
1100 roundtrip_api.extensions.get("flow.return_region_seq"),
1101 Some(&"true".to_string())
1102 );
1103 assert!(
1104 roundtrip_api
1105 .extensions
1106 .contains_key(REMOTE_QUERY_ID_EXTENSION_KEY)
1107 );
1108 assert_eq!(roundtrip_api.channel, api_ctx.channel);
1109 assert_eq!(roundtrip_api.snapshot_seqs, api_ctx.snapshot_seqs);
1110 }
1111
1112 #[test]
1113 fn test_query_context_remote_query_id_round_trip() {
1114 let query_id = "0195f4fd-c503-7c54-8b8f-7dfb8f6f9c4a";
1115 let ctx = QueryContextBuilder::default()
1116 .current_catalog(DEFAULT_CATALOG_NAME.to_string())
1117 .current_schema("public".to_string())
1118 .set_extension(
1119 REMOTE_QUERY_ID_EXTENSION_KEY.to_string(),
1120 query_id.to_string(),
1121 )
1122 .build();
1123
1124 assert_eq!(ctx.remote_query_id(), Some(query_id));
1125 assert_eq!(ctx.remote_query_id_value().unwrap().to_string(), query_id);
1126
1127 let proto: api::v1::QueryContext = (&ctx).into();
1128 let restored = QueryContext::try_from(proto).unwrap();
1129 assert_eq!(restored.remote_query_id(), Some(query_id));
1130 assert_eq!(
1131 restored.remote_query_id_value().unwrap().to_string(),
1132 query_id
1133 );
1134 }
1135
1136 #[test]
1137 fn test_query_context_read_preference_wire_values() {
1138 let wire = |value: Option<&str>| api::v1::QueryContext {
1139 current_catalog: DEFAULT_CATALOG_NAME.to_string(),
1140 current_schema: DEFAULT_SCHEMA_NAME.to_string(),
1141 timezone: "UTC".to_string(),
1142 extensions: value
1143 .map(|value| {
1144 HashMap::from([(READ_PREFERENCE_EXTENSION_KEY.to_string(), value.to_string())])
1145 })
1146 .unwrap_or_default(),
1147 channel: Channel::Internal as u32,
1148 snapshot_seqs: None,
1149 explain: None,
1150 };
1151
1152 let header = RegionRequestHeader::default();
1153 let header_context = QueryContext::try_from(&header).unwrap();
1154 assert_eq!(header_context.read_preference(), ReadPreference::Leader);
1155
1156 for value in [None, Some("leader"), Some("LEADER")] {
1157 let ctx = QueryContext::try_from(wire(value)).unwrap();
1158 assert_eq!(ctx.read_preference(), ReadPreference::Leader);
1159 assert!(!ctx.extensions().contains_key(READ_PREFERENCE_EXTENSION_KEY));
1160 }
1161 for value in ["", "unknown"] {
1162 let error = QueryContext::try_from(wire(Some(value))).unwrap_err();
1163 assert!(matches!(
1164 error,
1165 crate::session_config::Error::InvalidConfigValue {
1166 name,
1167 value: actual,
1168 ..
1169 } if name == "read_preference" && actual == value
1170 ));
1171 }
1172 }
1173
1174 #[test]
1175 fn test_legacy_query_context_wire_defaults_to_leader() {
1176 use prost::Message;
1177
1178 let legacy = api::v1::QueryContext::decode(
1179 include_bytes!(
1180 "../../../tests/compatibility/wire/read_preference/legacy_query_context.pb"
1181 )
1182 .as_slice(),
1183 )
1184 .unwrap();
1185 assert_eq!(legacy.current_catalog, "c1");
1186 assert_eq!(legacy.current_schema, "s1");
1187 assert_eq!(legacy.timezone, "UTC");
1188 assert_eq!(legacy.channel, Channel::Internal as u32);
1189 assert_eq!(
1190 legacy
1191 .extensions
1192 .get("flow.return_region_seq")
1193 .map(String::as_str),
1194 Some("true")
1195 );
1196 assert!(
1197 !legacy
1198 .extensions
1199 .contains_key(READ_PREFERENCE_EXTENSION_KEY)
1200 );
1201
1202 let restored = QueryContext::try_from(legacy).unwrap();
1203 assert_eq!(restored.read_preference(), ReadPreference::Leader);
1204 assert_eq!(restored.current_catalog(), "c1");
1205 assert_eq!(restored.current_schema(), "s1");
1206 assert_eq!(restored.timezone().to_string(), "UTC");
1207 assert_eq!(restored.channel(), Channel::Internal);
1208 assert_eq!(restored.extension("flow.return_region_seq"), Some("true"));
1209
1210 let outbound: api::v1::QueryContext = restored.into();
1211 let roundtrip = api::v1::QueryContext::decode(outbound.encode_to_vec().as_slice()).unwrap();
1212 assert_eq!(roundtrip.current_catalog, "c1");
1213 assert_eq!(roundtrip.current_schema, "s1");
1214 assert_eq!(roundtrip.timezone, "UTC");
1215 assert_eq!(roundtrip.channel, Channel::Internal as u32);
1216 assert_eq!(
1217 roundtrip
1218 .extensions
1219 .get("flow.return_region_seq")
1220 .map(String::as_str),
1221 Some("true")
1222 );
1223 assert!(
1224 !roundtrip
1225 .extensions
1226 .contains_key(READ_PREFERENCE_EXTENSION_KEY)
1227 );
1228 }
1229
1230 #[test]
1231 fn test_outbound_read_preference_clears_stale_extension() {
1232 let context = QueryContextBuilder::default()
1233 .set_extension(
1234 READ_PREFERENCE_EXTENSION_KEY.to_string(),
1235 "stale".to_string(),
1236 )
1237 .set_extension("unrelated".to_string(), "preserved".to_string())
1238 .build();
1239 let wire: api::v1::QueryContext = context.into();
1240 assert!(!wire.extensions.contains_key(READ_PREFERENCE_EXTENSION_KEY));
1241 assert_eq!(
1242 wire.extensions.get("unrelated").map(String::as_str),
1243 Some("preserved")
1244 );
1245 }
1246
1247 #[test]
1248 fn test_live_analyze_metrics_requires_matching_remote_query_id() {
1249 let mut ctx = QueryContext::arc().as_ref().clone();
1250 assert!(!ctx.live_analyze_metrics_enabled());
1251
1252 ctx.enable_live_analyze_metrics();
1253 assert!(ctx.live_analyze_metrics_enabled());
1254
1255 ctx.set_extension(LIVE_ANALYZE_METRICS_EXTENSION_KEY, "true");
1256 assert!(!ctx.live_analyze_metrics_enabled());
1257
1258 ctx.set_extension(LIVE_ANALYZE_METRICS_EXTENSION_KEY, "another-query-id");
1259 assert!(!ctx.live_analyze_metrics_enabled());
1260 }
1261 #[test]
1262 fn test_batching_selection_is_local_only() {
1263 let mut ctx = QueryContextBuilder::default().build();
1264 assert!(!ctx.batching_enabled());
1265 assert!(!ctx.logical_batching_enabled());
1266 ctx.set_logical_batching_enabled(true);
1267 assert!(ctx.clone().logical_batching_enabled());
1268 assert!(ctx.fork().logical_batching_enabled());
1269 ctx.set_batching_enabled(true);
1270 assert!(ctx.clone().batching_enabled());
1271 assert!(ctx.fork().batching_enabled());
1272 let wire: api::v1::QueryContext = ctx.into();
1273 let restored = QueryContext::try_from(wire).unwrap();
1274 assert!(!restored.batching_enabled());
1275 assert!(!restored.logical_batching_enabled());
1276 let ctx = QueryContextBuilder::default()
1277 .set_extension("batching_enabled".to_string(), "true".to_string())
1278 .build();
1279 assert!(!ctx.batching_enabled());
1280 }
1281}