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