Skip to main content

session/
context.rs

1// Copyright 2023 Greptime Team
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7//     http://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14
15use 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    /// mapping of RegionId to SequenceNumber, for snapshot read, meaning that the read should only
69    /// container data that was committed before(and include) the given sequence number
70    snapshot_seqs: Arc<RwLock<HashMap<u64, u64>>>,
71    /// Mappings of the RegionId to the minimal sequence of SST file to scan.
72    sst_min_sequences: Arc<RwLock<HashMap<u64, u64>>>,
73    // we use Arc<RwLock>> for modifiable fields
74    #[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    /// The configuration parameter are used to store the parameters that are set by the user
82    #[builder(default)]
83    configuration_parameter: Arc<ConfigurationVariables>,
84    /// Local-only write batching selection; never transported in protobuf extensions.
85    #[builder(default)]
86    batching_enabled: bool,
87    /// Local-only opt-in for logical metric writes, independent of ordinary-table batching.
88    #[builder(default)]
89    logical_batching_enabled: bool,
90    /// Track which protocol the query comes from.
91    #[builder(default)]
92    channel: Channel,
93    /// Process-local admission for one database in this request. Never sent over the wire.
94    #[builder(setter(skip))]
95    admitted_write: Option<(String, String)>,
96    /// Process id for managing on-going queries
97    #[builder(default)]
98    process_id: u32,
99    /// Connection information
100    #[builder(default)]
101    conn_info: ConnInfo,
102    /// Protocol specific context
103    #[builder(default)]
104    protocol_ctx: ProtocolCtx,
105}
106
107/// This fields hold data that is only valid to current query context
108#[derive(Debug, Builder, Clone, Default)]
109pub struct QueryContextMutableFields {
110    warning: Option<String>,
111    // TODO: remove this when format is supported in datafusion
112    explain_format: Option<String>,
113    /// Explain options to control the verbose analyze output.
114    explain_options: Option<ExplainOptions>,
115}
116
117impl Display for QueryContext {
118    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
119        write!(
120            f,
121            "QueryContext{{catalog: {}, schema: {}}}",
122            self.current_catalog(),
123            self.current_schema()
124        )
125    }
126}
127
128impl QueryContextBuilder {
129    pub fn current_schema(mut self, schema: String) -> Self {
130        if self.mutable_session_data.is_none() {
131            self.mutable_session_data = Some(Arc::new(RwLock::new(MutableInner::default())));
132        }
133
134        // safe for unwrap because previous none check
135        self.mutable_session_data
136            .as_mut()
137            .unwrap()
138            .write()
139            .unwrap()
140            .schema = schema;
141        self
142    }
143
144    pub fn timezone(mut self, timezone: Timezone) -> Self {
145        if self.mutable_session_data.is_none() {
146            self.mutable_session_data = Some(Arc::new(RwLock::new(MutableInner::default())));
147        }
148
149        self.mutable_session_data
150            .as_mut()
151            .unwrap()
152            .write()
153            .unwrap()
154            .timezone = timezone;
155        self
156    }
157
158    pub fn explain_options(mut self, explain_options: Option<ExplainOptions>) -> Self {
159        self.mutable_query_context_data
160            .get_or_insert_default()
161            .write()
162            .unwrap()
163            .explain_options = explain_options;
164        self
165    }
166
167    pub fn skip_wal(mut self, skip_wal: bool) -> Self {
168        self.mutable_session_data
169            .get_or_insert_default()
170            .write()
171            .unwrap()
172            .skip_wal = skip_wal;
173        self
174    }
175
176    pub fn read_preference(mut self, read_preference: ReadPreference) -> Self {
177        self.mutable_session_data
178            .get_or_insert_default()
179            .write()
180            .unwrap()
181            .read_preference = read_preference;
182        self
183    }
184}
185
186impl TryFrom<&RegionRequestHeader> for QueryContext {
187    type Error = crate::session_config::Error;
188
189    fn try_from(value: &RegionRequestHeader) -> Result<Self, Self::Error> {
190        if let Some(ctx) = &value.query_context {
191            ctx.clone().try_into()
192        } else {
193            Ok(QueryContextBuilder::default()
194                .set_extension(
195                    REMOTE_QUERY_ID_EXTENSION_KEY.to_string(),
196                    generate_remote_query_id(),
197                )
198                .build())
199        }
200    }
201}
202
203impl TryFrom<api::v1::QueryContext> for QueryContext {
204    type Error = crate::session_config::Error;
205
206    fn try_from(mut ctx: api::v1::QueryContext) -> Result<Self, Self::Error> {
207        let read_preference = ctx.extensions.remove(READ_PREFERENCE_EXTENSION_KEY);
208        let read_preference = match read_preference {
209            Some(value) => ReadPreference::from_str(&value).map_err(|_| {
210                InvalidConfigValueSnafu {
211                    name: "read_preference",
212                    value,
213                    hint: "Expected a supported read preference",
214                }
215                .build()
216            })?,
217            None => ReadPreference::default(),
218        };
219        let sequences = ctx.snapshot_seqs.as_ref();
220        Ok(QueryContextBuilder::default()
221            .current_catalog(ctx.current_catalog)
222            .current_schema(ctx.current_schema)
223            .timezone(parse_timezone(Some(&ctx.timezone)))
224            .extensions(ctx.extensions)
225            .channel(ctx.channel.into())
226            .snapshot_seqs(Arc::new(RwLock::new(
227                sequences
228                    .map(|x| x.snapshot_seqs.clone())
229                    .unwrap_or_default(),
230            )))
231            .sst_min_sequences(Arc::new(RwLock::new(
232                sequences
233                    .map(|x| x.sst_min_sequences.clone())
234                    .unwrap_or_default(),
235            )))
236            .explain_options(ctx.explain)
237            .read_preference(read_preference)
238            .build())
239    }
240}
241
242impl From<QueryContext> for api::v1::QueryContext {
243    fn from(
244        QueryContext {
245            current_catalog,
246            mutable_session_data: mutable_inner,
247            extensions,
248            channel,
249            snapshot_seqs,
250            sst_min_sequences,
251            mutable_query_context_data,
252            ..
253        }: QueryContext,
254    ) -> Self {
255        let explain = mutable_query_context_data.read().unwrap().explain_options;
256        let mutable_inner = mutable_inner.read().unwrap();
257        let mut extensions = extensions;
258        extensions.remove(READ_PREFERENCE_EXTENSION_KEY);
259        if mutable_inner.read_preference != ReadPreference::Leader {
260            extensions.insert(
261                READ_PREFERENCE_EXTENSION_KEY.to_string(),
262                mutable_inner
263                    .read_preference
264                    .to_string()
265                    .to_ascii_lowercase(),
266            );
267        }
268        api::v1::QueryContext {
269            current_catalog,
270            current_schema: mutable_inner.schema.clone(),
271            timezone: mutable_inner.timezone.to_string(),
272            extensions,
273            channel: channel as u32,
274            snapshot_seqs: Some(api::v1::SnapshotSequences {
275                snapshot_seqs: snapshot_seqs.read().unwrap().clone(),
276                sst_min_sequences: sst_min_sequences.read().unwrap().clone(),
277            }),
278            explain,
279        }
280    }
281}
282
283impl From<&QueryContext> for api::v1::QueryContext {
284    fn from(ctx: &QueryContext) -> Self {
285        ctx.clone().into()
286    }
287}
288
289impl QueryContext {
290    /// Forks this context with an independent snapshot of mutable session data.
291    ///
292    /// Unlike [`Clone`], changes to the schema, user, timezone, and other fields
293    /// held in mutable session data do not affect this context.
294    pub fn fork(&self) -> Self {
295        let mut fork = self.clone();
296        fork.mutable_session_data = Arc::new(RwLock::new(
297            self.mutable_session_data.read().unwrap().clone(),
298        ));
299        fork
300    }
301
302    /// Forks a context for internal writes after admitting the complete request.
303    /// Call only after write admission succeeds for the current database.
304    pub fn with_write_admission(&self) -> Self {
305        let mut ctx = self.fork();
306        ctx.admitted_write = Some((ctx.current_catalog().to_string(), ctx.current_schema()));
307        ctx
308    }
309
310    /// Returns zero for writes covered by this request's database admission.
311    /// Usage accounting and the original protocol channel remain unchanged.
312    pub fn write_rows_to_admit(&self, catalog: &str, schema: &str, rows: u64) -> u64 {
313        if self
314            .admitted_write
315            .as_ref()
316            .is_some_and(|(c, s)| c == catalog && s == schema)
317        {
318            0
319        } else {
320            rows
321        }
322    }
323
324    pub fn arc() -> QueryContextRef {
325        Arc::new(
326            QueryContextBuilder::default()
327                .set_extension(
328                    REMOTE_QUERY_ID_EXTENSION_KEY.to_string(),
329                    generate_remote_query_id(),
330                )
331                .build(),
332        )
333    }
334
335    /// Create a new  datafusion's ConfigOptions instance based on the current QueryContext.
336    pub fn create_config_options(&self) -> ConfigOptions {
337        let mut config = ConfigOptions::default();
338        config.execution.time_zone = Some(self.timezone().to_string());
339        config
340    }
341
342    pub fn with(catalog: &str, schema: &str) -> QueryContext {
343        QueryContextBuilder::default()
344            .current_catalog(catalog.to_string())
345            .current_schema(schema.to_string())
346            .set_extension(
347                REMOTE_QUERY_ID_EXTENSION_KEY.to_string(),
348                generate_remote_query_id(),
349            )
350            .build()
351    }
352
353    pub fn with_channel(catalog: &str, schema: &str, channel: Channel) -> QueryContext {
354        QueryContextBuilder::default()
355            .current_catalog(catalog.to_string())
356            .current_schema(schema.to_string())
357            .channel(channel)
358            .set_extension(
359                REMOTE_QUERY_ID_EXTENSION_KEY.to_string(),
360                generate_remote_query_id(),
361            )
362            .build()
363    }
364
365    pub fn with_db_name(db_name: Option<&str>) -> QueryContext {
366        let (catalog, schema) = db_name
367            .map(|db| {
368                let (catalog, schema) = parse_catalog_and_schema_from_db_string(db);
369                (catalog, schema)
370            })
371            .unwrap_or_else(|| {
372                (
373                    DEFAULT_CATALOG_NAME.to_string(),
374                    DEFAULT_SCHEMA_NAME.to_string(),
375                )
376            });
377        QueryContextBuilder::default()
378            .current_catalog(catalog)
379            .current_schema(schema.clone())
380            .set_extension(
381                REMOTE_QUERY_ID_EXTENSION_KEY.to_string(),
382                generate_remote_query_id(),
383            )
384            .build()
385    }
386
387    pub fn current_schema(&self) -> String {
388        self.mutable_session_data.read().unwrap().schema.clone()
389    }
390
391    pub fn set_current_schema(&self, new_schema: &str) {
392        self.mutable_session_data.write().unwrap().schema = new_schema.to_string();
393    }
394
395    pub fn current_catalog(&self) -> &str {
396        &self.current_catalog
397    }
398
399    pub fn set_current_catalog(&mut self, new_catalog: &str) {
400        self.current_catalog = new_catalog.to_string();
401    }
402
403    pub fn sql_dialect(&self) -> &(dyn Dialect + Send + Sync) {
404        &*self.sql_dialect
405    }
406
407    pub fn get_db_string(&self) -> String {
408        let catalog = self.current_catalog();
409        let schema = self.current_schema();
410        build_db_string(catalog, &schema)
411    }
412
413    pub fn timezone(&self) -> Timezone {
414        self.mutable_session_data.read().unwrap().timezone.clone()
415    }
416
417    pub fn set_timezone(&self, timezone: Timezone) {
418        self.mutable_session_data.write().unwrap().timezone = timezone;
419    }
420
421    /// Returns whether ordinary inserts in this request should skip WAL.
422    pub fn skip_wal(&self) -> bool {
423        self.mutable_session_data.read().unwrap().skip_wal
424    }
425
426    pub fn set_skip_wal(&self, skip_wal: bool) {
427        self.mutable_session_data.write().unwrap().skip_wal = skip_wal;
428    }
429
430    pub fn read_preference(&self) -> ReadPreference {
431        self.mutable_session_data.read().unwrap().read_preference
432    }
433
434    pub fn set_read_preference(&self, read_preference: ReadPreference) {
435        self.mutable_session_data.write().unwrap().read_preference = read_preference;
436    }
437
438    pub fn current_user(&self) -> UserInfoRef {
439        self.mutable_session_data.read().unwrap().user_info.clone()
440    }
441
442    pub fn set_current_user(&self, user: UserInfoRef) {
443        self.mutable_session_data.write().unwrap().user_info = user;
444    }
445
446    pub fn set_extension<S1: Into<String>, S2: Into<String>>(&mut self, key: S1, value: S2) {
447        self.extensions.insert(key.into(), value.into());
448    }
449
450    #[cfg(feature = "enterprise")]
451    pub fn remove_extension(&mut self, key: &str) {
452        self.extensions.remove(key);
453    }
454
455    pub fn extension<S: AsRef<str>>(&self, key: S) -> Option<&str> {
456        self.extensions.get(key.as_ref()).map(|v| v.as_str())
457    }
458
459    pub fn remote_query_id(&self) -> Option<&str> {
460        self.extension(REMOTE_QUERY_ID_EXTENSION_KEY)
461    }
462
463    pub fn remote_query_id_value(&self) -> Option<QueryId> {
464        self.remote_query_id()
465            .and_then(|query_id| query_id.parse().ok())
466    }
467
468    pub fn enable_live_analyze_metrics(&mut self) {
469        if let Some(remote_query_id) = self.remote_query_id().map(str::to_string) {
470            self.set_extension(LIVE_ANALYZE_METRICS_EXTENSION_KEY, remote_query_id);
471        }
472    }
473
474    pub fn live_analyze_metrics_enabled(&self) -> bool {
475        self.remote_query_id()
476            .zip(self.extension(LIVE_ANALYZE_METRICS_EXTENSION_KEY))
477            .is_some_and(|(remote_query_id, value)| value == remote_query_id)
478    }
479
480    pub fn extensions(&self) -> HashMap<String, String> {
481        self.extensions.clone()
482    }
483
484    /// Default to double quote and fallback to back quote
485    pub fn quote_style(&self) -> char {
486        if self.sql_dialect().is_delimited_identifier_start('"') {
487            '"'
488        } else if self.sql_dialect().is_delimited_identifier_start('\'') {
489            '\''
490        } else {
491            '`'
492        }
493    }
494
495    pub fn configuration_parameter(&self) -> &ConfigurationVariables {
496        &self.configuration_parameter
497    }
498
499    /// Whether the local HTTP entry point selected logical-table batching.
500    pub fn logical_batching_enabled(&self) -> bool {
501        self.logical_batching_enabled
502    }
503
504    /// Sets local logical-table batching selection without adding a wire-visible extension.
505    pub fn set_logical_batching_enabled(&mut self, enabled: bool) {
506        self.logical_batching_enabled = enabled;
507    }
508
509    /// Whether the local protocol entry point selected ordinary-table batching.
510    pub fn batching_enabled(&self) -> bool {
511        self.batching_enabled
512    }
513
514    /// Sets local write batching selection without adding a wire-visible extension.
515    pub fn set_batching_enabled(&mut self, enabled: bool) {
516        self.batching_enabled = enabled;
517    }
518
519    pub fn channel(&self) -> Channel {
520        self.channel
521    }
522
523    pub fn set_channel(&mut self, channel: Channel) {
524        self.channel = channel;
525    }
526
527    pub fn warning(&self) -> Option<String> {
528        self.mutable_query_context_data
529            .read()
530            .unwrap()
531            .warning
532            .clone()
533    }
534
535    pub fn set_warning(&self, msg: String) {
536        self.mutable_query_context_data.write().unwrap().warning = Some(msg);
537    }
538
539    pub fn explain_format(&self) -> Option<String> {
540        self.mutable_query_context_data
541            .read()
542            .unwrap()
543            .explain_format
544            .clone()
545    }
546
547    pub fn set_explain_format(&self, format: String) {
548        self.mutable_query_context_data
549            .write()
550            .unwrap()
551            .explain_format = Some(format);
552    }
553
554    pub fn explain_verbose(&self) -> bool {
555        self.mutable_query_context_data
556            .read()
557            .unwrap()
558            .explain_options
559            .map(|opts| opts.verbose)
560            .unwrap_or(false)
561    }
562
563    pub fn set_explain_verbose(&self, verbose: bool) {
564        self.mutable_query_context_data
565            .write()
566            .unwrap()
567            .explain_options
568            .get_or_insert_default()
569            .verbose = verbose;
570    }
571
572    pub fn query_timeout(&self) -> Option<Duration> {
573        self.mutable_session_data.read().unwrap().query_timeout
574    }
575
576    pub fn query_timeout_as_millis(&self) -> u128 {
577        let timeout = self.mutable_session_data.read().unwrap().query_timeout;
578        if let Some(t) = timeout {
579            return t.as_millis();
580        }
581        0
582    }
583
584    pub fn set_query_timeout(&self, timeout: Duration) {
585        self.mutable_session_data.write().unwrap().query_timeout = Some(timeout);
586    }
587
588    pub fn insert_cursor(&self, name: String, rb: RecordBatchStreamCursor) {
589        let mut guard = self.mutable_session_data.write().unwrap();
590        guard.cursors.insert(name, Arc::new(rb));
591
592        let cursor_count = guard.cursors.len();
593        if cursor_count > CURSOR_COUNT_WARNING_LIMIT {
594            warn!("Current connection has {} open cursors", cursor_count);
595        }
596    }
597
598    pub fn remove_cursor(&self, name: &str) {
599        let mut guard = self.mutable_session_data.write().unwrap();
600        guard.cursors.remove(name);
601    }
602
603    pub fn get_cursor(&self, name: &str) -> Option<Arc<RecordBatchStreamCursor>> {
604        let guard = self.mutable_session_data.read().unwrap();
605        let rb = guard.cursors.get(name);
606        rb.cloned()
607    }
608
609    pub fn snapshots(&self) -> HashMap<u64, u64> {
610        self.snapshot_seqs.read().unwrap().clone()
611    }
612
613    pub fn sst_min_sequences(&self) -> HashMap<u64, u64> {
614        self.sst_min_sequences.read().unwrap().clone()
615    }
616
617    pub fn get_snapshot(&self, region_id: u64) -> Option<u64> {
618        self.snapshot_seqs.read().unwrap().get(&region_id).cloned()
619    }
620
621    pub fn set_snapshot(&self, region_id: u64, sequence: u64) {
622        self.snapshot_seqs
623            .write()
624            .unwrap()
625            .insert(region_id, sequence);
626    }
627
628    /// Returns `true` if the session can cast strings to numbers in MySQL style.
629    pub fn auto_string_to_numeric(&self) -> bool {
630        matches!(self.channel, Channel::Mysql)
631    }
632
633    /// Finds the minimal sequence of SST files to scan of a Region.
634    pub fn sst_min_sequence(&self, region_id: u64) -> Option<u64> {
635        self.sst_min_sequences
636            .read()
637            .unwrap()
638            .get(&region_id)
639            .copied()
640    }
641
642    pub fn process_id(&self) -> u32 {
643        self.process_id
644    }
645
646    /// Get client information
647    pub fn conn_info(&self) -> &ConnInfo {
648        &self.conn_info
649    }
650
651    pub fn protocol_ctx(&self) -> &ProtocolCtx {
652        &self.protocol_ctx
653    }
654
655    pub fn set_protocol_ctx(&mut self, protocol_ctx: ProtocolCtx) {
656        self.protocol_ctx = protocol_ctx;
657    }
658}
659
660impl QueryContextBuilder {
661    pub fn build(self) -> QueryContext {
662        let channel = self.channel.unwrap_or_default();
663        let mut extensions = self.extensions.unwrap_or_default();
664        extensions
665            .entry(REMOTE_QUERY_ID_EXTENSION_KEY.to_string())
666            .or_insert_with(generate_remote_query_id);
667        QueryContext {
668            current_catalog: self
669                .current_catalog
670                .unwrap_or_else(|| DEFAULT_CATALOG_NAME.to_string()),
671            snapshot_seqs: self.snapshot_seqs.unwrap_or_default(),
672            sst_min_sequences: self.sst_min_sequences.unwrap_or_default(),
673            mutable_session_data: self.mutable_session_data.unwrap_or_default(),
674            mutable_query_context_data: self.mutable_query_context_data.unwrap_or_default(),
675            sql_dialect: self
676                .sql_dialect
677                .unwrap_or_else(|| Arc::new(GreptimeDbDialect {})),
678            extensions,
679            configuration_parameter: self
680                .configuration_parameter
681                .unwrap_or_else(|| Arc::new(ConfigurationVariables::default())),
682            channel,
683            batching_enabled: self.batching_enabled.unwrap_or_default(),
684            admitted_write: None,
685            logical_batching_enabled: self.logical_batching_enabled.unwrap_or_default(),
686            process_id: self.process_id.unwrap_or_default(),
687            conn_info: self.conn_info.unwrap_or_default(),
688            protocol_ctx: self.protocol_ctx.unwrap_or_default(),
689        }
690    }
691
692    pub fn set_extension(mut self, key: String, value: String) -> Self {
693        self.extensions
694            .get_or_insert_with(HashMap::new)
695            .insert(key, value);
696        self
697    }
698}
699
700#[derive(Debug, Clone, Default)]
701pub struct ConnInfo {
702    pub client_addr: Option<SocketAddr>,
703    pub channel: Channel,
704}
705
706impl Display for ConnInfo {
707    fn fmt(&self, f: &mut Formatter) -> std::fmt::Result {
708        write!(
709            f,
710            "{}[{}]",
711            self.channel,
712            self.client_addr
713                .map(|addr| addr.to_string())
714                .as_deref()
715                .unwrap_or("unknown client addr")
716        )
717    }
718}
719
720impl ConnInfo {
721    pub fn new(client_addr: Option<SocketAddr>, channel: Channel) -> Self {
722        Self {
723            client_addr,
724            channel,
725        }
726    }
727}
728
729/// Returns the SQL dialect for the given query channel.
730pub fn dialect_for_channel(channel: Channel) -> Arc<dyn Dialect + Send + Sync> {
731    match channel {
732        Channel::Mysql => Arc::new(MySqlDialect {}),
733        Channel::Postgres => Arc::new(PostgreSqlDialect {}),
734        _ => Arc::new(GenericDialect {}),
735    }
736}
737
738#[derive(Default, Debug)]
739pub struct ConfigurationVariables {
740    postgres_bytea_output: ArcSwap<PGByteaOutputValue>,
741    pg_datestyle_format: ArcSwap<(PGDateTimeStyle, PGDateOrder)>,
742    pg_intervalstyle_format: ArcSwap<PGIntervalStyle>,
743    allow_query_fallback: ArcSwap<bool>,
744}
745
746impl Clone for ConfigurationVariables {
747    fn clone(&self) -> Self {
748        Self {
749            postgres_bytea_output: ArcSwap::new(self.postgres_bytea_output.load().clone()),
750            pg_datestyle_format: ArcSwap::new(self.pg_datestyle_format.load().clone()),
751            pg_intervalstyle_format: ArcSwap::new(self.pg_intervalstyle_format.load().clone()),
752            allow_query_fallback: ArcSwap::new(self.allow_query_fallback.load().clone()),
753        }
754    }
755}
756
757impl ConfigurationVariables {
758    pub fn new() -> Self {
759        Self::default()
760    }
761
762    pub fn set_postgres_bytea_output(&self, value: PGByteaOutputValue) {
763        let _ = self.postgres_bytea_output.swap(Arc::new(value));
764    }
765
766    pub fn postgres_bytea_output(&self) -> Arc<PGByteaOutputValue> {
767        self.postgres_bytea_output.load().clone()
768    }
769
770    pub fn pg_datetime_style(&self) -> Arc<(PGDateTimeStyle, PGDateOrder)> {
771        self.pg_datestyle_format.load().clone()
772    }
773
774    pub fn set_pg_datetime_style(&self, style: PGDateTimeStyle, order: PGDateOrder) {
775        self.pg_datestyle_format.swap(Arc::new((style, order)));
776    }
777
778    pub fn pg_intervalstyle_format(&self) -> Arc<PGIntervalStyle> {
779        self.pg_intervalstyle_format.load().clone()
780    }
781
782    pub fn set_pg_intervalstyle_format(&self, value: PGIntervalStyle) {
783        self.pg_intervalstyle_format.swap(Arc::new(value));
784    }
785
786    pub fn allow_query_fallback(&self) -> bool {
787        **self.allow_query_fallback.load()
788    }
789
790    pub fn set_allow_query_fallback(&self, allow: bool) {
791        self.allow_query_fallback.swap(Arc::new(allow));
792    }
793}
794
795#[cfg(test)]
796mod test {
797    use std::collections::HashMap;
798
799    use common_catalog::consts::DEFAULT_CATALOG_NAME;
800
801    use crate::Session;
802    use crate::context::{Channel, *};
803
804    #[test]
805    fn test_session() {
806        let session = Session::new(
807            Some("127.0.0.1:9000".parse().unwrap()),
808            Channel::Mysql,
809            Default::default(),
810            100,
811        );
812        // test user_info
813        assert_eq!(session.user_info().username(), "greptime");
814
815        // test channel
816        assert_eq!(session.conn_info().channel, Channel::Mysql);
817        let client_addr = session.conn_info().client_addr.as_ref().unwrap();
818        assert_eq!(client_addr.ip().to_string(), "127.0.0.1");
819        assert_eq!(client_addr.port(), 9000);
820
821        assert_eq!("mysql[127.0.0.1:9000]", session.conn_info().to_string());
822        assert_eq!(100, session.process_id());
823
824        let query_ctx = session.new_query_context();
825        assert!(query_ctx.remote_query_id().is_some());
826    }
827
828    #[test]
829    fn test_context_db_string() {
830        let context = QueryContext::with("a0b1c2d3", "test");
831        assert_eq!("a0b1c2d3-test", context.get_db_string());
832
833        let context = QueryContext::with(DEFAULT_CATALOG_NAME, "test");
834        assert_eq!("test", context.get_db_string());
835    }
836
837    #[test]
838    fn test_fork_has_independent_mutable_session_data() {
839        let context = QueryContext::with(DEFAULT_CATALOG_NAME, "public");
840        let fork = context.fork();
841
842        fork.set_current_schema("private");
843
844        assert_eq!(context.current_schema(), "public");
845        assert_eq!(fork.current_schema(), "private");
846    }
847
848    #[test]
849    fn test_skip_wal_default_builder_and_fork() {
850        let default_context = QueryContext::with(DEFAULT_CATALOG_NAME, "public");
851        assert!(!default_context.skip_wal());
852        assert!(!QueryContextBuilder::default().build().skip_wal());
853        let context = QueryContextBuilder::default().skip_wal(true).build();
854        assert!(context.skip_wal());
855        let fork = context.fork();
856        assert!(fork.skip_wal());
857        fork.set_skip_wal(false);
858        assert!(context.skip_wal());
859        assert!(!fork.skip_wal());
860        context.set_skip_wal(false);
861        fork.set_skip_wal(true);
862        assert!(!context.skip_wal());
863        assert!(fork.skip_wal());
864    }
865
866    #[test]
867    fn test_write_admission_is_local_and_database_scoped() {
868        let ctx = QueryContext::with_channel("greptime", "public", Channel::Otlp);
869        let admitted = ctx.with_write_admission();
870        assert_eq!(ctx.write_rows_to_admit("greptime", "public", 100), 100);
871        assert_eq!(admitted.write_rows_to_admit("greptime", "public", 100), 0);
872        assert_eq!(admitted.write_rows_to_admit("greptime", "other", 100), 100);
873        assert_eq!(admitted.write_rows_to_admit("other", "public", 100), 100);
874        assert_eq!(admitted.channel(), Channel::Otlp);
875        assert_eq!(
876            admitted
877                .fork()
878                .write_rows_to_admit("greptime", "public", 100),
879            0
880        );
881        let wire: api::v1::QueryContext = admitted.into();
882        let restored = QueryContext::try_from(wire).unwrap();
883        assert_eq!(restored.write_rows_to_admit("greptime", "public", 100), 100);
884    }
885
886    #[test]
887    fn test_skip_wal_is_not_serialized_in_query_context() {
888        let context = QueryContextBuilder::default().skip_wal(true).build();
889        let api_context: api::v1::QueryContext = context.into();
890        assert!(
891            !api_context
892                .extensions
893                .contains_key(crate::hints::INSERT_SKIP_WAL_HINT)
894        );
895        let restored: QueryContext = api_context.try_into().unwrap();
896        assert!(!restored.skip_wal());
897    }
898
899    #[test]
900    fn test_api_query_context_roundtrip_with_sequences() {
901        let api_ctx = api::v1::QueryContext {
902            current_catalog: "c1".to_string(),
903            current_schema: "s1".to_string(),
904            timezone: "UTC".to_string(),
905            extensions: HashMap::from([("flow.return_region_seq".to_string(), "true".to_string())]),
906            channel: Channel::Internal as u32,
907            snapshot_seqs: Some(api::v1::SnapshotSequences {
908                snapshot_seqs: HashMap::from([(1, 100)]),
909                sst_min_sequences: HashMap::from([(1, 90)]),
910            }),
911            explain: None,
912        };
913
914        let session_ctx: QueryContext = api_ctx.clone().try_into().unwrap();
915        let roundtrip_api: api::v1::QueryContext = session_ctx.into();
916
917        assert_eq!(roundtrip_api.current_catalog, api_ctx.current_catalog);
918        assert_eq!(roundtrip_api.current_schema, api_ctx.current_schema);
919        assert_eq!(roundtrip_api.timezone, api_ctx.timezone);
920        assert_eq!(
921            roundtrip_api.extensions.get("flow.return_region_seq"),
922            Some(&"true".to_string())
923        );
924        assert!(
925            roundtrip_api
926                .extensions
927                .contains_key(REMOTE_QUERY_ID_EXTENSION_KEY)
928        );
929        assert_eq!(roundtrip_api.channel, api_ctx.channel);
930        assert_eq!(roundtrip_api.snapshot_seqs, api_ctx.snapshot_seqs);
931    }
932
933    #[test]
934    fn test_query_context_remote_query_id_round_trip() {
935        let query_id = "0195f4fd-c503-7c54-8b8f-7dfb8f6f9c4a";
936        let ctx = QueryContextBuilder::default()
937            .current_catalog(DEFAULT_CATALOG_NAME.to_string())
938            .current_schema("public".to_string())
939            .set_extension(
940                REMOTE_QUERY_ID_EXTENSION_KEY.to_string(),
941                query_id.to_string(),
942            )
943            .build();
944
945        assert_eq!(ctx.remote_query_id(), Some(query_id));
946        assert_eq!(ctx.remote_query_id_value().unwrap().to_string(), query_id);
947
948        let proto: api::v1::QueryContext = (&ctx).into();
949        let restored = QueryContext::try_from(proto).unwrap();
950        assert_eq!(restored.remote_query_id(), Some(query_id));
951        assert_eq!(
952            restored.remote_query_id_value().unwrap().to_string(),
953            query_id
954        );
955    }
956
957    #[test]
958    fn test_query_context_read_preference_wire_values() {
959        let wire = |value: Option<&str>| api::v1::QueryContext {
960            current_catalog: DEFAULT_CATALOG_NAME.to_string(),
961            current_schema: DEFAULT_SCHEMA_NAME.to_string(),
962            timezone: "UTC".to_string(),
963            extensions: value
964                .map(|value| {
965                    HashMap::from([(READ_PREFERENCE_EXTENSION_KEY.to_string(), value.to_string())])
966                })
967                .unwrap_or_default(),
968            channel: Channel::Internal as u32,
969            snapshot_seqs: None,
970            explain: None,
971        };
972
973        let header = RegionRequestHeader::default();
974        let header_context = QueryContext::try_from(&header).unwrap();
975        assert_eq!(header_context.read_preference(), ReadPreference::Leader);
976
977        for value in [None, Some("leader"), Some("LEADER")] {
978            let ctx = QueryContext::try_from(wire(value)).unwrap();
979            assert_eq!(ctx.read_preference(), ReadPreference::Leader);
980            assert!(!ctx.extensions().contains_key(READ_PREFERENCE_EXTENSION_KEY));
981        }
982        for value in ["", "unknown"] {
983            let error = QueryContext::try_from(wire(Some(value))).unwrap_err();
984            assert!(matches!(
985                error,
986                crate::session_config::Error::InvalidConfigValue {
987                    name,
988                    value: actual,
989                    ..
990                } if name == "read_preference" && actual == value
991            ));
992        }
993    }
994
995    #[test]
996    fn test_legacy_query_context_wire_defaults_to_leader() {
997        use prost::Message;
998
999        let legacy = api::v1::QueryContext::decode(
1000            include_bytes!(
1001                "../../../tests/compatibility/wire/read_preference/legacy_query_context.pb"
1002            )
1003            .as_slice(),
1004        )
1005        .unwrap();
1006        assert_eq!(legacy.current_catalog, "c1");
1007        assert_eq!(legacy.current_schema, "s1");
1008        assert_eq!(legacy.timezone, "UTC");
1009        assert_eq!(legacy.channel, Channel::Internal as u32);
1010        assert_eq!(
1011            legacy
1012                .extensions
1013                .get("flow.return_region_seq")
1014                .map(String::as_str),
1015            Some("true")
1016        );
1017        assert!(
1018            !legacy
1019                .extensions
1020                .contains_key(READ_PREFERENCE_EXTENSION_KEY)
1021        );
1022
1023        let restored = QueryContext::try_from(legacy).unwrap();
1024        assert_eq!(restored.read_preference(), ReadPreference::Leader);
1025        assert_eq!(restored.current_catalog(), "c1");
1026        assert_eq!(restored.current_schema(), "s1");
1027        assert_eq!(restored.timezone().to_string(), "UTC");
1028        assert_eq!(restored.channel(), Channel::Internal);
1029        assert_eq!(restored.extension("flow.return_region_seq"), Some("true"));
1030
1031        let outbound: api::v1::QueryContext = restored.into();
1032        let roundtrip = api::v1::QueryContext::decode(outbound.encode_to_vec().as_slice()).unwrap();
1033        assert_eq!(roundtrip.current_catalog, "c1");
1034        assert_eq!(roundtrip.current_schema, "s1");
1035        assert_eq!(roundtrip.timezone, "UTC");
1036        assert_eq!(roundtrip.channel, Channel::Internal as u32);
1037        assert_eq!(
1038            roundtrip
1039                .extensions
1040                .get("flow.return_region_seq")
1041                .map(String::as_str),
1042            Some("true")
1043        );
1044        assert!(
1045            !roundtrip
1046                .extensions
1047                .contains_key(READ_PREFERENCE_EXTENSION_KEY)
1048        );
1049    }
1050
1051    #[test]
1052    fn test_outbound_read_preference_clears_stale_extension() {
1053        let context = QueryContextBuilder::default()
1054            .set_extension(
1055                READ_PREFERENCE_EXTENSION_KEY.to_string(),
1056                "stale".to_string(),
1057            )
1058            .set_extension("unrelated".to_string(), "preserved".to_string())
1059            .build();
1060        let wire: api::v1::QueryContext = context.into();
1061        assert!(!wire.extensions.contains_key(READ_PREFERENCE_EXTENSION_KEY));
1062        assert_eq!(
1063            wire.extensions.get("unrelated").map(String::as_str),
1064            Some("preserved")
1065        );
1066    }
1067
1068    #[test]
1069    fn test_live_analyze_metrics_requires_matching_remote_query_id() {
1070        let mut ctx = QueryContext::arc().as_ref().clone();
1071        assert!(!ctx.live_analyze_metrics_enabled());
1072
1073        ctx.enable_live_analyze_metrics();
1074        assert!(ctx.live_analyze_metrics_enabled());
1075
1076        ctx.set_extension(LIVE_ANALYZE_METRICS_EXTENSION_KEY, "true");
1077        assert!(!ctx.live_analyze_metrics_enabled());
1078
1079        ctx.set_extension(LIVE_ANALYZE_METRICS_EXTENSION_KEY, "another-query-id");
1080        assert!(!ctx.live_analyze_metrics_enabled());
1081    }
1082    #[test]
1083    fn test_batching_selection_is_local_only() {
1084        let mut ctx = QueryContextBuilder::default().build();
1085        assert!(!ctx.batching_enabled());
1086        assert!(!ctx.logical_batching_enabled());
1087        ctx.set_logical_batching_enabled(true);
1088        assert!(ctx.clone().logical_batching_enabled());
1089        assert!(ctx.fork().logical_batching_enabled());
1090        ctx.set_batching_enabled(true);
1091        assert!(ctx.clone().batching_enabled());
1092        assert!(ctx.fork().batching_enabled());
1093        let wire: api::v1::QueryContext = ctx.into();
1094        let restored = QueryContext::try_from(wire).unwrap();
1095        assert!(!restored.batching_enabled());
1096        assert!(!restored.logical_batching_enabled());
1097        let ctx = QueryContextBuilder::default()
1098            .set_extension("batching_enabled".to_string(), "true".to_string())
1099            .build();
1100        assert!(!ctx.batching_enabled());
1101    }
1102}