Skip to main content

pgwire/
pg_protocol.rs

1// Copyright 2022 RisingWave Labs
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::any::Any;
16use std::collections::HashMap;
17use std::io::ErrorKind;
18use std::panic::AssertUnwindSafe;
19use std::pin::Pin;
20use std::str::Utf8Error;
21use std::sync::{Arc, LazyLock, Weak};
22use std::time::{Duration, Instant};
23use std::{io, str};
24
25use bytes::{Bytes, BytesMut};
26use futures::FutureExt;
27use futures::stream::StreamExt;
28use itertools::Itertools;
29use openssl::ssl::{SslAcceptor, SslContext, SslContextRef, SslMethod};
30use risingwave_common::types::DataType;
31use risingwave_common::util::deployment::Deployment;
32use risingwave_common::util::env_var::env_var_is_true;
33use risingwave_common::util::panic::FutureCatchUnwindExt;
34use risingwave_common::util::query_log::*;
35use risingwave_common::{PG_VERSION, SERVER_ENCODING, STANDARD_CONFORMING_STRINGS};
36use risingwave_sqlparser::ast::{RedactSqlOptionKeywordsRef, Statement};
37use risingwave_sqlparser::parser::Parser;
38use thiserror_ext::AsReport;
39use tokio::io::{AsyncRead, AsyncWrite, AsyncWriteExt};
40use tokio::sync::Mutex;
41use tokio_openssl::SslStream;
42use tracing::Instrument;
43
44use crate::error::{PsqlError, PsqlResult};
45use crate::error_or_notice::Severity;
46use crate::memory_manager::{MessageMemoryGuard, MessageMemoryManagerRef};
47use crate::net::AddressRef;
48use crate::pg_extended::ResultCache;
49use crate::pg_message::{
50    BeCommandCompleteMessage, BeMessage, BeParameterStatusMessage, FeBindMessage, FeCancelMessage,
51    FeCloseMessage, FeDescribeMessage, FeExecuteMessage, FeMessage, FeMessageHeader,
52    FeParseMessage, FePasswordMessage, FeStartupMessage, ServerThrottleReason, TransactionStatus,
53};
54use crate::pg_server::{Session, SessionManager, UserAuthenticator};
55use crate::types::Format;
56
57/// Truncates query log if it's longer than `RW_QUERY_LOG_TRUNCATE_LEN`, to avoid log file being too
58/// large.
59static RW_QUERY_LOG_TRUNCATE_LEN: LazyLock<usize> =
60    LazyLock::new(|| match std::env::var("RW_QUERY_LOG_TRUNCATE_LEN") {
61        Ok(len) if len.parse::<usize>().is_ok() => len.parse::<usize>().unwrap(),
62        _ => 65536,
63    });
64
65tokio::task_local! {
66    /// The current session. Concrete type is erased for different session implementations.
67    pub static CURRENT_SESSION: Weak<dyn Any + Send + Sync>
68}
69
70/// The state machine for each psql connection.
71/// Read pg messages from tcp stream and write results back.
72pub struct PgProtocol<S, SM>
73where
74    SM: SessionManager,
75{
76    /// Used for write/read pg messages.
77    stream: PgStream<S>,
78    /// Current states of pg connection.
79    state: PgProtocolState,
80    /// Whether the connection is terminated.
81    is_terminate: bool,
82
83    session_mgr: Arc<SM>,
84    session: Option<Arc<SM::Session>>,
85
86    result_cache: HashMap<String, ResultCache<<SM::Session as Session>::ValuesStream>>,
87    unnamed_prepare_statement:
88        Option<PreparedStatementData<<SM::Session as Session>::PreparedStatement>>,
89    prepare_statement_store:
90        HashMap<String, PreparedStatementData<<SM::Session as Session>::PreparedStatement>>,
91    unnamed_portal: Option<PortalData<<SM::Session as Session>::Portal>>,
92    portal_store: HashMap<String, PortalData<<SM::Session as Session>::Portal>>,
93    // Used to store the dependency of portal and prepare statement.
94    // When we close a prepare statement, we need to close all the portals that depend on it.
95    statement_portal_dependency: HashMap<String, Vec<String>>,
96
97    // Used for ssl connection.
98    // If None, not expected to build ssl connection (panic).
99    tls_context: Option<SslContext>,
100
101    // TLS configuration including SSL enforcement setting
102    tls_config: Option<TlsConfig>,
103
104    // Used in extended query protocol. When encounter error in extended query, we need to ignore
105    // the following message util sync message.
106    ignore_util_sync: bool,
107
108    // Client Address
109    peer_addr: AddressRef,
110
111    redact_sql_option_keywords: Option<RedactSqlOptionKeywordsRef>,
112    message_memory_manager: MessageMemoryManagerRef,
113}
114
115/// Configures TLS encryption for connections.
116#[derive(Debug, Clone)]
117pub struct TlsConfig {
118    /// The path to the TLS certificate.
119    pub cert: String,
120    /// The path to the TLS key.
121    pub key: String,
122    /// Whether to enforce SSL connections (reject non-SSL clients).
123    pub enforce_ssl: bool,
124}
125
126impl TlsConfig {
127    pub fn new_default() -> anyhow::Result<Option<Self>> {
128        let cert = std::env::var("RW_SSL_CERT").ok();
129        let key = std::env::var("RW_SSL_KEY").ok();
130        let enforce_ssl = env_var_is_true("RW_SSL_ENFORCE");
131
132        if cert.is_some() ^ key.is_some() {
133            return Err(anyhow::anyhow!(
134                "RW_SSL_CERT and RW_SSL_KEY must be set together"
135            ));
136        }
137
138        if enforce_ssl && cert.is_none() {
139            return Err(anyhow::anyhow!(
140                "RW_SSL_ENFORCE requires RW_SSL_CERT and RW_SSL_KEY to be set"
141            ));
142        }
143
144        let (Some(cert), Some(key)) = (cert, key) else {
145            return Ok(None);
146        };
147
148        tracing::info!(
149            "RW_SSL_CERT={}, RW_SSL_KEY={}, RW_SSL_ENFORCE={}",
150            cert,
151            key,
152            enforce_ssl
153        );
154        Ok(Some(Self {
155            cert,
156            key,
157            enforce_ssl,
158        }))
159    }
160}
161
162impl<S, SM> Drop for PgProtocol<S, SM>
163where
164    SM: SessionManager,
165{
166    fn drop(&mut self) {
167        if let Some(session) = &self.session {
168            // Clear the session in session manager.
169            self.session_mgr.end_session(session);
170        }
171    }
172}
173
174/// States flow happened from top to down.
175#[derive(PartialEq, Eq)]
176enum PgProtocolState {
177    Startup,
178    Authentication,
179    Regular,
180}
181
182#[derive(Clone)]
183struct PreparedStatementData<S> {
184    statement: S,
185    sql: Arc<str>,
186}
187
188#[derive(Clone)]
189struct PortalData<P> {
190    portal: P,
191    sql: Arc<str>,
192}
193
194/// Truncate 0 from C string in Bytes and stringify it (returns slice, no allocations).
195///
196/// PG protocol strings are always C strings.
197pub fn cstr_to_str(b: &Bytes) -> Result<&str, Utf8Error> {
198    let without_null = if b.last() == Some(&0) {
199        &b[..b.len() - 1]
200    } else {
201        &b[..]
202    };
203    std::str::from_utf8(without_null)
204}
205
206fn get_redacted_and_truncated_sql(
207    sql: &str,
208    redact_sql_option_keywords: Option<RedactSqlOptionKeywordsRef>,
209) -> String {
210    let redacted_sql = if let Some(keywords) = redact_sql_option_keywords
211        && !keywords.is_empty()
212    {
213        redact_sql(sql, keywords)
214    } else {
215        sql.to_owned()
216    };
217    let truncated = truncated_fmt::TruncatedFmt(&redacted_sql, *RW_QUERY_LOG_TRUNCATE_LEN);
218    truncated.to_string()
219}
220
221/// Record `sql` in the current tracing span.
222fn record_sql_in_span(
223    sql: &str,
224    redact_sql_option_keywords: Option<RedactSqlOptionKeywordsRef>,
225    span: &mut tracing::Span,
226) {
227    let redacted_and_truncated_sql =
228        get_redacted_and_truncated_sql(sql, redact_sql_option_keywords);
229    span.record("sql", tracing::field::display(&redacted_and_truncated_sql));
230}
231
232fn record_user_in_span(user: &str, span: &mut tracing::Span) {
233    span.record("user", tracing::field::display(user));
234}
235
236/// Redacts sensitive SQL fields. Data in DML is not redacted.
237fn redact_sql(sql: &str, keywords: RedactSqlOptionKeywordsRef) -> String {
238    match Parser::parse_sql(sql) {
239        Ok(sqls) => sqls
240            .into_iter()
241            .map(|sql| sql.to_redacted_string(keywords.clone()))
242            .join(";"),
243        Err(_) => sql.to_owned(),
244    }
245}
246
247#[derive(Clone)]
248pub struct ConnectionContext {
249    pub tls_config: Option<TlsConfig>,
250    pub redact_sql_option_keywords: Option<RedactSqlOptionKeywordsRef>,
251    pub message_memory_manager: MessageMemoryManagerRef,
252    pub stream_flush_threshold_bytes: usize,
253}
254
255impl<S, SM> PgProtocol<S, SM>
256where
257    S: PgByteStream,
258    SM: SessionManager,
259{
260    pub fn new(
261        stream: S,
262        session_mgr: Arc<SM>,
263        peer_addr: AddressRef,
264        context: ConnectionContext,
265    ) -> Self {
266        let ConnectionContext {
267            tls_config,
268            redact_sql_option_keywords,
269            message_memory_manager,
270            stream_flush_threshold_bytes,
271        } = context;
272        Self {
273            stream: PgStream::new(stream, stream_flush_threshold_bytes),
274            is_terminate: false,
275            state: PgProtocolState::Startup,
276            session_mgr,
277            session: None,
278            tls_context: tls_config
279                .as_ref()
280                .and_then(|e| build_ssl_ctx_from_config(e).ok()),
281            tls_config,
282            result_cache: Default::default(),
283            unnamed_prepare_statement: Default::default(),
284            prepare_statement_store: Default::default(),
285            unnamed_portal: Default::default(),
286            portal_store: Default::default(),
287            statement_portal_dependency: Default::default(),
288            ignore_util_sync: false,
289            peer_addr,
290            redact_sql_option_keywords,
291            message_memory_manager,
292        }
293    }
294
295    /// Run the protocol to serve the connection.
296    pub async fn run(&mut self) {
297        let mut notice_fut = None;
298
299        loop {
300            // Once a session is present, create a future to subscribe and send notices asynchronously.
301            if notice_fut.is_none()
302                && let Some(session) = self.session.clone()
303            {
304                let mut stream = self.stream.clone();
305                notice_fut = Some(Box::pin(async move {
306                    loop {
307                        let notice = session.next_notice().await;
308                        if let Err(e) = stream.write(BeMessage::NoticeResponse(&notice)).await {
309                            tracing::error!(error = %e.as_report(), notice, "failed to send notice");
310                        }
311                    }
312                }));
313            }
314
315            // Read and process messages.
316            let process = std::pin::pin!(async {
317                let (msg, _memory_guard) = match self.read_message().await {
318                    Ok(msg) => msg,
319                    Err(e) => {
320                        tracing::error!(error = %e.as_report(), "error when reading message");
321                        return true; // terminate the connection
322                    }
323                };
324                tracing::trace!(?msg, "received message");
325                self.process(msg).await
326            });
327
328            let terminated = if let Some(notice_fut) = notice_fut.as_mut() {
329                tokio::select! {
330                    _ = notice_fut => unreachable!(),
331                    terminated = process => terminated,
332                }
333            } else {
334                process.await
335            };
336
337            if terminated {
338                break;
339            }
340        }
341    }
342
343    /// Processes one message. Returns true if the connection is terminated.
344    pub async fn process(&mut self, msg: FeMessage) -> bool {
345        self.do_process(msg).await.is_none() || self.is_terminate
346    }
347
348    /// The root tracing span for processing a message. The target of the span is
349    /// [`PGWIRE_ROOT_SPAN_TARGET`].
350    ///
351    /// This is used to provide context for the (slow) query logs and traces.
352    ///
353    /// The span is only effective if there's a current session and the message is
354    /// query-related. Otherwise, `Span::none()` is returned.
355    fn root_span_for_msg(&self, msg: &FeMessage) -> tracing::Span {
356        let Some(session_id) = self.session.as_ref().map(|s| s.id().0) else {
357            return tracing::Span::none();
358        };
359
360        let mode = match msg {
361            FeMessage::Query(_) => "simple query",
362            FeMessage::Parse(_) => "extended query parse",
363            FeMessage::Execute(_) => "extended query execute",
364            _ => return tracing::Span::none(),
365        };
366
367        let mut span = tracing::info_span!(
368            target: PGWIRE_ROOT_SPAN_TARGET,
369            "handle_query",
370            mode,
371            session_id,
372            sql = tracing::field::Empty,
373            user = tracing::field::Empty,
374        );
375        match msg {
376            FeMessage::Execute(execute_msg) => {
377                if let Ok(portal_name) = cstr_to_str(&execute_msg.portal_name)
378                    && let Ok(sql) = self.get_portal_sql(portal_name)
379                {
380                    record_sql_in_span(&sql, self.redact_sql_option_keywords.clone(), &mut span);
381                }
382            }
383            _ => {
384                if let Ok(sql) = msg.get_sql()
385                    && let Some(sql) = sql
386                {
387                    record_sql_in_span(sql, self.redact_sql_option_keywords.clone(), &mut span);
388                }
389            }
390        }
391        if let Some(current_session) = self.session.as_ref() {
392            record_user_in_span(&current_session.user(), &mut span);
393        }
394        span
395    }
396
397    /// Return type `Option<()>` is essentially a bool, but allows `?` for early return.
398    /// - `None` means to terminate the current connection
399    /// - `Some(())` means to continue processing the next message
400    async fn do_process(&mut self, msg: FeMessage) -> Option<()> {
401        let span = self.root_span_for_msg(&msg);
402        let weak_session = self
403            .session
404            .as_ref()
405            .map(|s| Arc::downgrade(s) as Weak<dyn Any + Send + Sync>);
406
407        // Processing the message itself.
408        //
409        // Note: pin the future to avoid stack overflow as we'll wrap it multiple times
410        // in the following code.
411        let fut = Box::pin(self.do_process_inner(msg));
412
413        // Set the current session as the context when processing the message, if exists.
414        let fut = async move {
415            if let Some(session) = weak_session {
416                CURRENT_SESSION.scope(session, fut).await
417            } else {
418                fut.await
419            }
420        };
421
422        // Catch unwind.
423        let fut = async move {
424            AssertUnwindSafe(fut)
425                .rw_catch_unwind()
426                .await
427                .unwrap_or_else(|payload| {
428                    Err(PsqlError::Panic(
429                        panic_message::panic_message(&payload).to_owned(),
430                    ))
431                })
432        };
433
434        // Slow query log.
435        let fut = async move {
436            let period = *SLOW_QUERY_LOG_PERIOD;
437            let mut fut = std::pin::pin!(fut);
438            let mut elapsed = Duration::ZERO;
439
440            // Report the SQL in the log periodically if the query is slow.
441            loop {
442                match tokio::time::timeout(period, &mut fut).await {
443                    Ok(result) => break result,
444                    Err(_) => {
445                        elapsed += period;
446                        tracing::info!(
447                            target: PGWIRE_SLOW_QUERY_LOG,
448                            elapsed = %format_args!("{}ms", elapsed.as_millis()),
449                            "slow query"
450                        );
451                    }
452                }
453            }
454        };
455
456        // Query log.
457        let fut = async move {
458            if !tracing::Span::current().is_none() {
459                tracing::info!(
460                    target: PGWIRE_QUERY_LOG,
461                    status = "started",
462                );
463            }
464
465            let start = Instant::now();
466            let result = fut.await;
467            let elapsed = start.elapsed();
468
469            // Always log if an error occurs.
470            // Note: all messages will be processed through this code path, making it the
471            //       only necessary place to log errors.
472            if let Err(error) = &result {
473                if cfg!(debug_assertions) && !Deployment::current().is_ci() {
474                    // For local debugging, we print the error with backtrace.
475                    // It's useful only when:
476                    // - no additional context is added to the error
477                    // - backtrace is captured in the error
478                    // - backtrace is not printed in the middle
479                    tracing::error!(error = ?error.as_report(), "error when process message");
480                } else {
481                    tracing::error!(error = %error.as_report(), "error when process message");
482                }
483            }
484
485            // Log to optionally-enabled target `PGWIRE_QUERY_LOG`.
486            // Only log if we're currently in a tracing span set in `span_for_msg`.
487            if !tracing::Span::current().is_none() {
488                tracing::info!(
489                    target: PGWIRE_QUERY_LOG,
490                    status = if result.is_ok() { "ok" } else { "err" },
491                    time = %format_args!("{}ms", elapsed.as_millis()),
492                );
493            }
494
495            result
496        };
497
498        // Tracing span.
499        let fut = fut.instrument(span);
500
501        // Execute the future and handle the error.
502        match fut.await {
503            Ok(()) => Some(()),
504            Err(e) => {
505                match e {
506                    PsqlError::IoError(io_err) => {
507                        if io_err.kind() == std::io::ErrorKind::UnexpectedEof {
508                            return None;
509                        }
510                    }
511
512                    PsqlError::SslError(_) => {
513                        // For ssl error, because the stream has already been consumed, so there is
514                        // no way to write more message.
515                        return None;
516                    }
517
518                    PsqlError::StartupError(_)
519                    | PsqlError::PasswordError
520                    | PsqlError::ProtocolError(_) => {
521                        self.stream
522                            .write_no_flush(BeMessage::ErrorResponse {
523                                error: &e,
524                                // At this time we're not in a session, use compact error message for
525                                // better alignment with Postgres' UI.
526                                pretty: false,
527                                severity: Some(Severity::Fatal),
528                            })
529                            .ok()?;
530                        let _ = self.stream.flush().await;
531                        return None;
532                    }
533
534                    PsqlError::SimpleQueryError(_) | PsqlError::ServerThrottle(_) => {
535                        self.stream
536                            .write_no_flush(BeMessage::ErrorResponse {
537                                error: &e,
538                                pretty: true,
539                                severity: None,
540                            })
541                            .ok()?;
542                        self.ready_for_query().ok()?;
543                    }
544
545                    PsqlError::IdleInTxnTimeout | PsqlError::Panic(_) => {
546                        self.stream
547                            .write_no_flush(BeMessage::ErrorResponse {
548                                error: &e,
549                                pretty: true,
550                                severity: None,
551                            })
552                            .ok()?;
553                        let _ = self.stream.flush().await;
554
555                        // 1. Catching the panic during message processing may leave the session in an
556                        // inconsistent state. We forcefully close the connection (then end the
557                        // session) here for safety.
558                        // 2. Idle in transaction timeout should also close the connection.
559                        return None;
560                    }
561
562                    PsqlError::Uncategorized(_)
563                    | PsqlError::ExtendedPrepareError(_)
564                    | PsqlError::ExtendedExecuteError(_) => {
565                        self.stream
566                            .write_no_flush(BeMessage::ErrorResponse {
567                                error: &e,
568                                pretty: true,
569                                severity: None,
570                            })
571                            .ok()?;
572                    }
573                }
574                let _ = self.stream.flush().await;
575                Some(())
576            }
577        }
578    }
579
580    async fn do_process_inner(&mut self, msg: FeMessage) -> PsqlResult<()> {
581        if self.state == PgProtocolState::Authentication && !matches!(&msg, FeMessage::Password(_))
582        {
583            return Err(PsqlError::protocol_error(
584                "expected PasswordMessage during authentication",
585            ));
586        }
587
588        // Ignore util sync message.
589        if self.ignore_util_sync {
590            if let FeMessage::Sync = msg {
591            } else {
592                tracing::trace!("ignore message {:?} until sync.", msg);
593                return Ok(());
594            }
595        }
596
597        match msg {
598            FeMessage::Gss => self.process_gss_msg().await?,
599            FeMessage::Ssl => self.process_ssl_msg().await?,
600            FeMessage::Startup(msg) => self.process_startup_msg(msg).await?,
601            FeMessage::Password(msg) => self.process_password_msg(msg).await?,
602            FeMessage::Query(query_msg) => {
603                let sql = Arc::from(query_msg.get_sql()?);
604                // The process_query_msg can be slow. Release potential large FeQueryMessage early.
605                drop(query_msg);
606                self.process_query_msg(sql).await?
607            }
608            FeMessage::CancelQuery(m) => self.process_cancel_msg(m)?,
609            FeMessage::Terminate => self.process_terminate(),
610            FeMessage::Parse(m) => {
611                if let Err(err) = self.process_parse_msg(m).await {
612                    self.ignore_util_sync = true;
613                    return Err(err);
614                }
615            }
616            FeMessage::Bind(m) => {
617                if let Err(err) = self.process_bind_msg(m) {
618                    self.ignore_util_sync = true;
619                    return Err(err);
620                }
621            }
622            FeMessage::Execute(m) => {
623                if let Err(err) = self.process_execute_msg(m).await {
624                    self.ignore_util_sync = true;
625                    return Err(err);
626                }
627            }
628            FeMessage::Describe(m) => {
629                if let Err(err) = self.process_describe_msg(m) {
630                    self.ignore_util_sync = true;
631                    return Err(err);
632                }
633            }
634            FeMessage::Sync => {
635                self.ignore_util_sync = false;
636                self.ready_for_query()?
637            }
638            FeMessage::Close(m) => {
639                if let Err(err) = self.process_close_msg(m) {
640                    self.ignore_util_sync = true;
641                    return Err(err);
642                }
643            }
644            FeMessage::Flush => {
645                if let Err(err) = self.stream.flush().await {
646                    self.ignore_util_sync = true;
647                    return Err(err.into());
648                }
649            }
650            FeMessage::HealthCheck => self.process_health_check(),
651            FeMessage::ServerThrottle(reason) => match reason {
652                ServerThrottleReason::TooLargeMessage => {
653                    return Err(PsqlError::ServerThrottle(format!(
654                        "max_single_query_size_bytes {} has been exceeded, please either reduce the query size or increase the limit",
655                        self.message_memory_manager.max_filter_bytes
656                    )));
657                }
658                ServerThrottleReason::TooManyMemoryUsage => {
659                    return Err(PsqlError::ServerThrottle(format!(
660                        "max_total_query_size_bytes {} has been exceeded, please either retry or increase the limit",
661                        self.message_memory_manager.max_running_bytes
662                    )));
663                }
664            },
665        }
666        self.stream.flush().await?;
667        Ok(())
668    }
669
670    pub async fn read_message(&mut self) -> io::Result<(FeMessage, Option<MessageMemoryGuard>)> {
671        match self.state {
672            PgProtocolState::Startup => self
673                .stream
674                .read_startup()
675                .await
676                .map(|message: FeMessage| (message, None)),
677            PgProtocolState::Authentication | PgProtocolState::Regular => {
678                self.stream.read_header().await?;
679                let guard = if let Some(ref header) = self.stream.read_header {
680                    let payload_len = std::cmp::max(header.payload_len, 0) as u64;
681                    let (reason, guard) = self.message_memory_manager.add(payload_len);
682                    if let Some(reason) = reason {
683                        // Release the memory ASAP.
684                        drop(guard);
685                        self.stream.skip_body().await?;
686                        return Ok((FeMessage::ServerThrottle(reason), None));
687                    }
688                    guard
689                } else {
690                    None
691                };
692                let message = self.stream.read_body().await?;
693                Ok((message, guard))
694            }
695        }
696    }
697
698    /// Writes a `ReadyForQuery` message to the client without flushing.
699    fn ready_for_query(&mut self) -> io::Result<()> {
700        self.stream.write_no_flush(BeMessage::ReadyForQuery(
701            self.session
702                .as_ref()
703                .map(|s| s.transaction_status())
704                .unwrap_or(TransactionStatus::Idle),
705        ))
706    }
707
708    async fn process_gss_msg(&mut self) -> PsqlResult<()> {
709        // We don't support GSSAPI, so we just say no gracefully.
710        self.stream.write(BeMessage::EncryptionResponseNo).await?;
711        Ok(())
712    }
713
714    async fn process_ssl_msg(&mut self) -> PsqlResult<()> {
715        if let Some(context) = self.tls_context.as_ref() {
716            // If got and ssl context, say yes for ssl connection.
717            // Construct ssl stream and replace with current one.
718            self.stream.write(BeMessage::EncryptionResponseSsl).await?;
719            self.stream.upgrade_to_ssl(context).await?;
720        } else {
721            // If no, say no for encryption.
722            self.stream.write(BeMessage::EncryptionResponseNo).await?;
723        }
724
725        Ok(())
726    }
727
728    async fn process_startup_msg(&mut self, msg: FeStartupMessage) -> PsqlResult<()> {
729        // Check SSL enforcement: if SSL is enforced but connection is not using SSL, reject
730        if let Some(ref tls_config) = self.tls_config
731            && tls_config.enforce_ssl
732            && !self.stream.is_ssl_connection().await
733        {
734            return Err(PsqlError::StartupError(
735                "SSL connection is required but not established".into(),
736            ));
737        }
738
739        let db_name = msg
740            .config
741            .get("database")
742            .cloned()
743            .unwrap_or_else(|| "dev".to_owned());
744        let user_name = msg
745            .config
746            .get("user")
747            .cloned()
748            .unwrap_or_else(|| "root".to_owned());
749
750        let session = self
751            .session_mgr
752            .connect(&db_name, &user_name, self.peer_addr.clone())
753            .map_err(|e| PsqlError::StartupError(e.into()))?;
754
755        if let Some(options) = msg.config.get("options") {
756            for (key, value) in parse_options(options)? {
757                session
758                    .set_config(&key, value)
759                    .map_err(|e| PsqlError::StartupError(e.into()))?;
760            }
761        }
762        // dedicated `application_name` has higher priority than `options`
763        let application_name = msg.config.get("application_name");
764        if let Some(application_name) = application_name {
765            session
766                .set_config("application_name", application_name.clone())
767                .map_err(|e| PsqlError::StartupError(e.into()))?;
768        }
769
770        self.state = match session.user_authenticator() {
771            UserAuthenticator::None => {
772                self.stream.write_no_flush(BeMessage::AuthenticationOk)?;
773
774                // Cancel request need this for identify and verification. According to postgres
775                // doc, it should be written to buffer after receive AuthenticationOk.
776                self.stream
777                    .write_no_flush(BeMessage::BackendKeyData(session.id()))?;
778
779                self.stream.write_no_flush(BeMessage::ParameterStatus(
780                    BeParameterStatusMessage::TimeZone(
781                        &session
782                            .get_config("timezone")
783                            .map_err(|e| PsqlError::StartupError(e.into()))?,
784                    ),
785                ))?;
786                self.stream
787                    .write_parameter_status_msg_no_flush(&ParameterStatus {
788                        application_name: application_name.cloned(),
789                    })?;
790                self.ready_for_query()?;
791                PgProtocolState::Regular
792            }
793            UserAuthenticator::ClearText(_)
794            | UserAuthenticator::OAuth { .. }
795            | UserAuthenticator::Ldap(..) => {
796                self.stream
797                    .write_no_flush(BeMessage::AuthenticationCleartextPassword)?;
798                PgProtocolState::Authentication
799            }
800            UserAuthenticator::Md5WithSalt { salt, .. } => {
801                self.stream
802                    .write_no_flush(BeMessage::AuthenticationMd5Password(salt))?;
803                PgProtocolState::Authentication
804            }
805        };
806
807        self.session = Some(session);
808        Ok(())
809    }
810
811    async fn process_password_msg(&mut self, msg: FePasswordMessage) -> PsqlResult<()> {
812        let session = self.session.as_ref().unwrap();
813        let authenticator = session.user_authenticator();
814        authenticator.authenticate(&msg.password).await?;
815        self.stream.write_no_flush(BeMessage::AuthenticationOk)?;
816        let timezone = session
817            .get_config("timezone")
818            .map_err(|e| PsqlError::StartupError(e.into()))?;
819        self.stream.write_no_flush(BeMessage::ParameterStatus(
820            BeParameterStatusMessage::TimeZone(&timezone),
821        ))?;
822        self.stream
823            .write_parameter_status_msg_no_flush(&ParameterStatus::default())?;
824        self.ready_for_query()?;
825        self.state = PgProtocolState::Regular;
826        Ok(())
827    }
828
829    fn process_cancel_msg(&mut self, m: FeCancelMessage) -> PsqlResult<()> {
830        let session_id = (m.target_process_id, m.target_secret_key);
831        tracing::trace!("cancel query in session: {:?}", session_id);
832        self.session_mgr.cancel_queries_in_session(session_id);
833        self.session_mgr.cancel_creating_jobs_in_session(session_id);
834        self.is_terminate = true;
835        Ok(())
836    }
837
838    async fn process_query_msg(&mut self, sql: Arc<str>) -> PsqlResult<()> {
839        let truncated_sql =
840            get_redacted_and_truncated_sql(&sql, self.redact_sql_option_keywords.clone());
841        let session = self.session.clone().unwrap();
842
843        session.check_idle_in_transaction_timeout()?;
844        // Store only truncated SQL in context to prevent excessive memory usage from large SQL.
845        let _exec_context_guard = session.init_exec_context(truncated_sql.into());
846        self.inner_process_query_msg(sql, session.clone()).await
847    }
848
849    async fn inner_process_query_msg(
850        &mut self,
851        sql: Arc<str>,
852        session: Arc<SM::Session>,
853    ) -> PsqlResult<()> {
854        // Parse sql.
855        let stmts =
856            Parser::parse_sql(&sql).map_err(|err| PsqlError::SimpleQueryError(err.into()))?;
857        // The following inner_process_query_msg_one_stmt can be slow. Release potential large String early.
858        drop(sql);
859        if stmts.is_empty() {
860            self.stream.write_no_flush(BeMessage::EmptyQueryResponse)?;
861        }
862
863        // Execute multiple statements in simple query. KISS later.
864        for stmt in stmts {
865            self.inner_process_query_msg_one_stmt(stmt, session.clone())
866                .await?;
867        }
868        // Put this line inside the for loop above will lead to unfinished/stuck regress test...Not
869        // sure the reason.
870        self.ready_for_query()?;
871        Ok(())
872    }
873
874    async fn inner_process_query_msg_one_stmt(
875        &mut self,
876        stmt: Statement,
877        session: Arc<SM::Session>,
878    ) -> PsqlResult<()> {
879        let session = session.clone();
880
881        // execute query
882        let res = session.clone().run_one_query(stmt, Format::Text).await;
883
884        // Take all remaining notices (if any) and send them before `CommandComplete`.
885        while let Some(notice) = session.next_notice().now_or_never() {
886            self.stream
887                .write_no_flush(BeMessage::NoticeResponse(&notice))?;
888        }
889
890        let mut res = res.map_err(|e| PsqlError::SimpleQueryError(e.into()))?;
891
892        for notice in res.notices() {
893            self.stream
894                .write_no_flush(BeMessage::NoticeResponse(notice))?;
895        }
896
897        let status = res.status();
898        if let Some(ref application_name) = status.application_name {
899            self.stream.write_no_flush(BeMessage::ParameterStatus(
900                BeParameterStatusMessage::ApplicationName(application_name),
901            ))?;
902        }
903
904        if res.is_copy_query_to_stdout() {
905            self.stream
906                .write_no_flush(BeMessage::CopyOutResponse(res.row_desc().len()))?;
907            let mut count = 0;
908            while let Some(row_set) = res.values_stream().next().await {
909                let row_set = row_set.map_err(PsqlError::SimpleQueryError)?;
910                for row in row_set {
911                    self.stream
912                        .write_streaming(BeMessage::CopyData(&row))
913                        .await?;
914                    count += 1;
915                }
916            }
917
918            self.stream.write_no_flush(BeMessage::CopyDone)?;
919
920            // Run the callback before sending the `CommandComplete` message.
921            res.run_callback().await?;
922
923            self.stream
924                .write_no_flush(BeMessage::CommandComplete(BeCommandCompleteMessage {
925                    stmt_type: res.stmt_type(),
926                    rows_cnt: count,
927                }))?;
928        } else if res.is_query() {
929            self.stream
930                .write_no_flush(BeMessage::RowDescription(res.row_desc()))?;
931
932            let mut rows_cnt = 0;
933
934            while let Some(row_set) = res.values_stream().next().await {
935                let row_set = row_set.map_err(PsqlError::SimpleQueryError)?;
936                for row in row_set {
937                    self.stream
938                        .write_streaming(BeMessage::DataRow(&row))
939                        .await?;
940                    rows_cnt += 1;
941                }
942            }
943
944            // Run the callback before sending the `CommandComplete` message.
945            res.run_callback().await?;
946
947            self.stream
948                .write_no_flush(BeMessage::CommandComplete(BeCommandCompleteMessage {
949                    stmt_type: res.stmt_type(),
950                    rows_cnt,
951                }))?;
952        } else if res.stmt_type().is_dml() && !res.stmt_type().is_returning() {
953            let first_row_set = res.values_stream().next().await;
954            let first_row_set = match first_row_set {
955                None => {
956                    return Err(PsqlError::Uncategorized(
957                        anyhow::anyhow!("no affected rows in output").into(),
958                    ));
959                }
960                Some(row) => row.map_err(PsqlError::SimpleQueryError)?,
961            };
962            let affected_rows_str = first_row_set[0].values()[0]
963                .as_ref()
964                .expect("compute node should return affected rows in output");
965
966            assert!(matches!(res.row_cnt_format(), Some(Format::Text)));
967            let affected_rows_cnt = String::from_utf8(affected_rows_str.to_vec())
968                .unwrap()
969                .parse()
970                .unwrap_or_default();
971
972            // Run the callback before sending the `CommandComplete` message.
973            res.run_callback().await?;
974
975            self.stream
976                .write_no_flush(BeMessage::CommandComplete(BeCommandCompleteMessage {
977                    stmt_type: res.stmt_type(),
978                    rows_cnt: affected_rows_cnt,
979                }))?;
980        } else {
981            // Run the callback before sending the `CommandComplete` message.
982            res.run_callback().await?;
983
984            self.stream
985                .write_no_flush(BeMessage::CommandComplete(BeCommandCompleteMessage {
986                    stmt_type: res.stmt_type(),
987                    rows_cnt: 0,
988                }))?;
989        }
990
991        Ok(())
992    }
993
994    fn process_terminate(&mut self) {
995        self.is_terminate = true;
996    }
997
998    fn process_health_check(&mut self) {
999        tracing::debug!("health check");
1000        self.is_terminate = true;
1001    }
1002
1003    async fn process_parse_msg(&mut self, mut msg: FeParseMessage) -> PsqlResult<()> {
1004        let sql = Arc::from(cstr_to_str(&msg.sql_bytes).unwrap());
1005        let session = self.session.clone().unwrap();
1006        let statement_name = cstr_to_str(&msg.statement_name).unwrap().to_owned();
1007        let type_ids = std::mem::take(&mut msg.type_ids);
1008        // The inner_process_parse_msg can be slow. Release potential large FeParseMessage early.
1009        drop(msg);
1010        self.inner_process_parse_msg(session, sql, statement_name, type_ids)
1011            .await?;
1012        Ok(())
1013    }
1014
1015    async fn inner_process_parse_msg(
1016        &mut self,
1017        session: Arc<SM::Session>,
1018        sql: Arc<str>,
1019        statement_name: String,
1020        type_ids: Vec<i32>,
1021    ) -> PsqlResult<()> {
1022        if statement_name.is_empty() {
1023            // Remove the unnamed prepare statement first, in case the unsupported sql binds a wrong
1024            // prepare statement.
1025            self.unnamed_prepare_statement.take();
1026        } else if self.prepare_statement_store.contains_key(&statement_name) {
1027            return Err(PsqlError::ExtendedPrepareError(
1028                "Duplicated statement name".into(),
1029            ));
1030        }
1031
1032        let stmt = {
1033            let stmts = Parser::parse_sql(&sql)
1034                .map_err(|err| PsqlError::ExtendedPrepareError(err.into()))?;
1035            if stmts.len() > 1 {
1036                return Err(PsqlError::ExtendedPrepareError(
1037                    "Only one statement is allowed in extended query mode".into(),
1038                ));
1039            }
1040
1041            stmts.into_iter().next()
1042        };
1043
1044        let param_types: Vec<Option<DataType>> = type_ids
1045            .iter()
1046            .map(|&id| {
1047                // 0 means unspecified type
1048                // ref: https://www.postgresql.org/docs/15/protocol-message-formats.html#:~:text=Placing%20a%20zero%20here%20is%20equivalent%20to%20leaving%20the%20type%20unspecified.
1049                if id == 0 {
1050                    Ok(None)
1051                } else {
1052                    DataType::from_oid(id)
1053                        .map(Some)
1054                        .map_err(|e| PsqlError::ExtendedPrepareError(e.into()))
1055                }
1056            })
1057            .try_collect()?;
1058
1059        let prepare_statement = session
1060            .parse(stmt, param_types)
1061            .await
1062            .map_err(|e| PsqlError::ExtendedPrepareError(e.into()))?;
1063        let prepare_statement = PreparedStatementData {
1064            statement: prepare_statement,
1065            sql,
1066        };
1067
1068        if statement_name.is_empty() {
1069            self.unnamed_prepare_statement.replace(prepare_statement);
1070        } else {
1071            self.prepare_statement_store
1072                .insert(statement_name.clone(), prepare_statement);
1073        }
1074
1075        self.statement_portal_dependency
1076            .entry(statement_name)
1077            .or_default()
1078            .clear();
1079
1080        self.stream.write_no_flush(BeMessage::ParseComplete)?;
1081        Ok(())
1082    }
1083
1084    fn process_bind_msg(&mut self, msg: FeBindMessage) -> PsqlResult<()> {
1085        let statement_name = cstr_to_str(&msg.statement_name).unwrap().to_owned();
1086        let portal_name = cstr_to_str(&msg.portal_name).unwrap().to_owned();
1087        let session = self.session.clone().unwrap();
1088
1089        if self.portal_store.contains_key(&portal_name) {
1090            return Err(PsqlError::Uncategorized("Duplicated portal name".into()));
1091        }
1092
1093        let prepare_statement = self.get_statement_data(&statement_name)?.clone();
1094
1095        let result_formats = msg
1096            .result_format_codes
1097            .iter()
1098            .map(|&format_code| Format::from_i16(format_code))
1099            .try_collect()?;
1100        let param_formats = msg
1101            .param_format_codes
1102            .iter()
1103            .map(|&format_code| Format::from_i16(format_code))
1104            .try_collect()?;
1105
1106        let portal = session
1107            .bind(
1108                prepare_statement.statement,
1109                msg.params,
1110                param_formats,
1111                result_formats,
1112            )
1113            .map_err(|e| PsqlError::Uncategorized(e.into()))?;
1114        let portal = PortalData {
1115            portal,
1116            sql: prepare_statement.sql,
1117        };
1118
1119        if portal_name.is_empty() {
1120            self.result_cache.remove(&portal_name);
1121            self.unnamed_portal.replace(portal);
1122        } else {
1123            assert!(
1124                !self.result_cache.contains_key(&portal_name),
1125                "Named portal never can be overridden."
1126            );
1127            self.portal_store.insert(portal_name.clone(), portal);
1128        }
1129
1130        self.statement_portal_dependency
1131            .get_mut(&statement_name)
1132            .unwrap()
1133            .push(portal_name);
1134
1135        self.stream.write_no_flush(BeMessage::BindComplete)?;
1136        Ok(())
1137    }
1138
1139    async fn process_execute_msg(&mut self, msg: FeExecuteMessage) -> PsqlResult<()> {
1140        let portal_name = cstr_to_str(&msg.portal_name).unwrap().to_owned();
1141        let row_max = msg.max_rows as usize;
1142        drop(msg);
1143        let session = self.session.clone().unwrap();
1144
1145        match self.result_cache.remove(&portal_name) {
1146            Some(mut result_cache) => {
1147                assert!(self.portal_store.contains_key(&portal_name));
1148
1149                let is_consume_completed =
1150                    result_cache.consume::<S>(row_max, &mut self.stream).await?;
1151
1152                if !is_consume_completed {
1153                    self.result_cache.insert(portal_name, result_cache);
1154                }
1155            }
1156            _ => {
1157                let portal = self.get_portal_data(&portal_name)?.clone();
1158                let sql = format!("{}", portal.portal);
1159                let truncated_sql =
1160                    get_redacted_and_truncated_sql(&sql, self.redact_sql_option_keywords.clone());
1161                drop(sql);
1162
1163                session.check_idle_in_transaction_timeout()?;
1164                // Store only truncated SQL in context to prevent excessive memory usage from large SQL.
1165                let _exec_context_guard = session.init_exec_context(truncated_sql.into());
1166                let result = session.clone().execute(portal.portal).await;
1167
1168                let pg_response = result.map_err(|e| PsqlError::ExtendedExecuteError(e.into()))?;
1169                let mut result_cache = ResultCache::new(pg_response);
1170                let is_consume_completed =
1171                    result_cache.consume::<S>(row_max, &mut self.stream).await?;
1172                if !is_consume_completed {
1173                    self.result_cache.insert(portal_name, result_cache);
1174                }
1175            }
1176        }
1177
1178        Ok(())
1179    }
1180
1181    fn process_describe_msg(&mut self, msg: FeDescribeMessage) -> PsqlResult<()> {
1182        let name = cstr_to_str(&msg.name).unwrap().to_owned();
1183        let session = self.session.clone().unwrap();
1184        //  b'S' => Statement
1185        //  b'P' => Portal
1186
1187        assert!(msg.kind == b'S' || msg.kind == b'P');
1188        if msg.kind == b'S' {
1189            let prepare_statement = self.get_statement(&name)?;
1190
1191            let (param_types, row_descriptions) = self
1192                .session
1193                .clone()
1194                .unwrap()
1195                .describe_statement(prepare_statement)
1196                .map_err(|e| PsqlError::Uncategorized(e.into()))?;
1197            self.stream.write_no_flush(BeMessage::ParameterDescription(
1198                &param_types.iter().map(|t| t.to_oid()).collect_vec(),
1199            ))?;
1200
1201            if row_descriptions.is_empty() {
1202                // According https://www.postgresql.org/docs/current/protocol-flow.html#:~:text=The%20response%20is%20a%20RowDescri[…]0a%20query%20that%20will%20return%20rows%3B,
1203                // return NoData message if the statement is not a query.
1204                self.stream.write_no_flush(BeMessage::NoData)?;
1205            } else {
1206                self.stream
1207                    .write_no_flush(BeMessage::RowDescription(&row_descriptions))?;
1208            }
1209        } else if msg.kind == b'P' {
1210            let portal = self.get_portal(&name)?;
1211
1212            let row_descriptions = session
1213                .describe_portal(portal)
1214                .map_err(|e| PsqlError::Uncategorized(e.into()))?;
1215
1216            if row_descriptions.is_empty() {
1217                // According https://www.postgresql.org/docs/current/protocol-flow.html#:~:text=The%20response%20is%20a%20RowDescri[…]0a%20query%20that%20will%20return%20rows%3B,
1218                // return NoData message if the statement is not a query.
1219                self.stream.write_no_flush(BeMessage::NoData)?;
1220            } else {
1221                self.stream
1222                    .write_no_flush(BeMessage::RowDescription(&row_descriptions))?;
1223            }
1224        }
1225        Ok(())
1226    }
1227
1228    fn process_close_msg(&mut self, msg: FeCloseMessage) -> PsqlResult<()> {
1229        let name = cstr_to_str(&msg.name).unwrap().to_owned();
1230        assert!(msg.kind == b'S' || msg.kind == b'P');
1231        if msg.kind == b'S' {
1232            if name.is_empty() {
1233                self.unnamed_prepare_statement = None;
1234            } else {
1235                self.prepare_statement_store.remove(&name);
1236            }
1237            for portal_name in self
1238                .statement_portal_dependency
1239                .remove(&name)
1240                .unwrap_or_default()
1241            {
1242                self.remove_portal(&portal_name);
1243            }
1244        } else if msg.kind == b'P' {
1245            self.remove_portal(&name);
1246        }
1247        self.stream.write_no_flush(BeMessage::CloseComplete)?;
1248        Ok(())
1249    }
1250
1251    fn remove_portal(&mut self, portal_name: &str) {
1252        if portal_name.is_empty() {
1253            self.unnamed_portal = None;
1254        } else {
1255            self.portal_store.remove(portal_name);
1256        }
1257        self.result_cache.remove(portal_name);
1258    }
1259
1260    fn get_portal(&self, portal_name: &str) -> PsqlResult<<SM::Session as Session>::Portal> {
1261        Ok(self.get_portal_data(portal_name)?.portal.clone())
1262    }
1263
1264    fn get_portal_data(
1265        &self,
1266        portal_name: &str,
1267    ) -> PsqlResult<&PortalData<<SM::Session as Session>::Portal>> {
1268        if portal_name.is_empty() {
1269            self.unnamed_portal
1270                .as_ref()
1271                .ok_or_else(|| PsqlError::Uncategorized("unnamed portal not found".into()))
1272        } else {
1273            self.portal_store.get(portal_name).ok_or_else(|| {
1274                PsqlError::Uncategorized(format!("Portal {} not found", portal_name).into())
1275            })
1276        }
1277    }
1278
1279    fn get_statement(
1280        &self,
1281        statement_name: &str,
1282    ) -> PsqlResult<<SM::Session as Session>::PreparedStatement> {
1283        Ok(self.get_statement_data(statement_name)?.statement.clone())
1284    }
1285
1286    fn get_statement_data(
1287        &self,
1288        statement_name: &str,
1289    ) -> PsqlResult<&PreparedStatementData<<SM::Session as Session>::PreparedStatement>> {
1290        if statement_name.is_empty() {
1291            self.unnamed_prepare_statement.as_ref().ok_or_else(|| {
1292                PsqlError::Uncategorized("unnamed prepare statement not found".into())
1293            })
1294        } else {
1295            self.prepare_statement_store
1296                .get(statement_name)
1297                .ok_or_else(|| {
1298                    PsqlError::Uncategorized(
1299                        format!("Prepare statement {} not found", statement_name).into(),
1300                    )
1301                })
1302        }
1303    }
1304
1305    fn get_portal_sql(&self, portal_name: &str) -> PsqlResult<Arc<str>> {
1306        Ok(self.get_portal_data(portal_name)?.sql.clone())
1307    }
1308}
1309
1310enum PgStreamInner<S> {
1311    /// Used for the intermediate state when converting from unencrypted to ssl stream.
1312    Placeholder,
1313    /// An unencrypted stream.
1314    Unencrypted(S),
1315    /// An ssl stream.
1316    Ssl(SslStream<S>),
1317}
1318
1319/// Trait for a byte stream that can be used for pg protocol.
1320pub trait PgByteStream: AsyncWrite + AsyncRead + Unpin + Send + 'static {}
1321impl<S> PgByteStream for S where S: AsyncWrite + AsyncRead + Unpin + Send + 'static {}
1322
1323/// Wraps a byte stream and read/write pg messages.
1324///
1325/// Cloning a `PgStream` will share the same stream but a fresh & independent write buffer,
1326/// so that it can be used to write messages concurrently without interference.
1327pub struct PgStream<S> {
1328    /// The underlying stream.
1329    stream: Arc<Mutex<PgStreamInner<S>>>,
1330    /// Write into buffer before flush to stream.
1331    write_buf: BytesMut,
1332    stream_flush_threshold_bytes: usize,
1333    read_header: Option<FeMessageHeader>,
1334}
1335
1336impl<S> PgStream<S> {
1337    /// Create a new `PgStream` with the given stream and streaming flush threshold.
1338    pub fn new(stream: S, stream_flush_threshold_bytes: usize) -> Self {
1339        const DEFAULT_WRITE_BUF_CAPACITY: usize = 10 * 1024;
1340
1341        Self {
1342            stream: Arc::new(Mutex::new(PgStreamInner::Unencrypted(stream))),
1343            write_buf: BytesMut::with_capacity(DEFAULT_WRITE_BUF_CAPACITY),
1344            stream_flush_threshold_bytes,
1345            read_header: None,
1346        }
1347    }
1348
1349    /// Check if the current connection is using SSL
1350    async fn is_ssl_connection(&self) -> bool {
1351        let stream = self.stream.lock().await;
1352        matches!(*stream, PgStreamInner::Ssl(_))
1353    }
1354}
1355
1356impl<S> Clone for PgStream<S> {
1357    fn clone(&self) -> Self {
1358        Self {
1359            stream: Arc::clone(&self.stream),
1360            write_buf: BytesMut::with_capacity(self.write_buf.capacity()),
1361            stream_flush_threshold_bytes: self.stream_flush_threshold_bytes,
1362            read_header: self.read_header.clone(),
1363        }
1364    }
1365}
1366
1367/// At present there is a hard-wired set of parameters for which
1368/// ParameterStatus will be generated: they are:
1369///
1370///  * `server_version`
1371///  * `server_encoding`
1372///  * `client_encoding`
1373///  * `application_name`
1374///  * `is_superuser`
1375///  * `session_authorization`
1376///  * `DateStyle`
1377///  * `IntervalStyle`
1378///  * `TimeZone`
1379///  * `integer_datetimes`
1380///  * `standard_conforming_string`
1381///
1382/// See: <https://www.postgresql.org/docs/9.2/static/protocol-flow.html#PROTOCOL-ASYNC>.
1383#[derive(Debug, Default, Clone)]
1384pub struct ParameterStatus {
1385    pub application_name: Option<String>,
1386}
1387
1388impl<S> PgStream<S>
1389where
1390    S: PgByteStream,
1391{
1392    async fn read_startup(&mut self) -> io::Result<FeMessage> {
1393        let mut stream = self.stream.lock().await;
1394        match &mut *stream {
1395            PgStreamInner::Placeholder => unreachable!(),
1396            PgStreamInner::Unencrypted(stream) => FeStartupMessage::read(stream).await,
1397            PgStreamInner::Ssl(ssl_stream) => FeStartupMessage::read(ssl_stream).await,
1398        }
1399    }
1400
1401    async fn read_header(&mut self) -> io::Result<()> {
1402        let mut stream = self.stream.lock().await;
1403        match &mut *stream {
1404            PgStreamInner::Placeholder => unreachable!(),
1405            PgStreamInner::Unencrypted(stream) => {
1406                self.read_header = Some(FeMessage::read_header(stream).await?);
1407                Ok(())
1408            }
1409            PgStreamInner::Ssl(ssl_stream) => {
1410                self.read_header = Some(FeMessage::read_header(ssl_stream).await?);
1411                Ok(())
1412            }
1413        }
1414    }
1415
1416    async fn read_body(&mut self) -> io::Result<FeMessage> {
1417        let mut stream = self.stream.lock().await;
1418        let header = self
1419            .read_header
1420            .take()
1421            .ok_or_else(|| std::io::Error::new(ErrorKind::InvalidInput, "header not found"))?;
1422        match &mut *stream {
1423            PgStreamInner::Placeholder => unreachable!(),
1424            PgStreamInner::Unencrypted(stream) => FeMessage::read_body(stream, header).await,
1425            PgStreamInner::Ssl(ssl_stream) => FeMessage::read_body(ssl_stream, header).await,
1426        }
1427    }
1428
1429    async fn skip_body(&mut self) -> io::Result<()> {
1430        let mut stream = self.stream.lock().await;
1431        let header = self
1432            .read_header
1433            .take()
1434            .ok_or_else(|| std::io::Error::new(ErrorKind::InvalidInput, "header not found"))?;
1435        match &mut *stream {
1436            PgStreamInner::Placeholder => unreachable!(),
1437            PgStreamInner::Unencrypted(stream) => FeMessage::skip_body(stream, header).await,
1438            PgStreamInner::Ssl(ssl_stream) => FeMessage::skip_body(ssl_stream, header).await,
1439        }
1440    }
1441
1442    fn write_parameter_status_msg_no_flush(&mut self, status: &ParameterStatus) -> io::Result<()> {
1443        self.write_no_flush(BeMessage::ParameterStatus(
1444            BeParameterStatusMessage::ClientEncoding(SERVER_ENCODING),
1445        ))?;
1446        self.write_no_flush(BeMessage::ParameterStatus(
1447            BeParameterStatusMessage::StandardConformingString(STANDARD_CONFORMING_STRINGS),
1448        ))?;
1449        self.write_no_flush(BeMessage::ParameterStatus(
1450            BeParameterStatusMessage::ServerVersion(PG_VERSION),
1451        ))?;
1452        if let Some(application_name) = &status.application_name {
1453            self.write_no_flush(BeMessage::ParameterStatus(
1454                BeParameterStatusMessage::ApplicationName(application_name),
1455            ))?;
1456        }
1457        Ok(())
1458    }
1459
1460    pub fn write_no_flush(&mut self, message: BeMessage<'_>) -> io::Result<()> {
1461        BeMessage::write(&mut self.write_buf, message)
1462    }
1463
1464    /// Write a message that is part of a potentially large response, flushing periodically to
1465    /// bound the write buffer and propagate network backpressure to the result stream.
1466    pub(crate) async fn write_streaming(&mut self, message: BeMessage<'_>) -> io::Result<()> {
1467        self.write_no_flush(message)?;
1468        if self.write_buf.len() >= self.stream_flush_threshold_bytes {
1469            self.flush().await?;
1470        }
1471        Ok(())
1472    }
1473
1474    async fn write(&mut self, message: BeMessage<'_>) -> io::Result<()> {
1475        self.write_no_flush(message)?;
1476        self.flush().await?;
1477        Ok(())
1478    }
1479
1480    async fn flush(&mut self) -> io::Result<()> {
1481        let mut stream = self.stream.lock().await;
1482        match &mut *stream {
1483            PgStreamInner::Placeholder => unreachable!(),
1484            PgStreamInner::Unencrypted(stream) => {
1485                stream.write_all(&self.write_buf).await?;
1486                stream.flush().await?;
1487            }
1488            PgStreamInner::Ssl(ssl_stream) => {
1489                ssl_stream.write_all(&self.write_buf).await?;
1490                ssl_stream.flush().await?;
1491            }
1492        }
1493        self.write_buf.clear();
1494        Ok(())
1495    }
1496}
1497
1498impl<S> PgStream<S>
1499where
1500    S: PgByteStream,
1501{
1502    /// Convert the underlying stream to ssl stream based on the given context.
1503    async fn upgrade_to_ssl(&mut self, ssl_ctx: &SslContextRef) -> PsqlResult<()> {
1504        let mut stream = self.stream.lock().await;
1505
1506        match std::mem::replace(&mut *stream, PgStreamInner::Placeholder) {
1507            PgStreamInner::Unencrypted(unencrypted_stream) => {
1508                let ssl = openssl::ssl::Ssl::new(ssl_ctx).unwrap();
1509                let mut ssl_stream =
1510                    tokio_openssl::SslStream::new(ssl, unencrypted_stream).unwrap();
1511
1512                if let Err(e) = Pin::new(&mut ssl_stream).accept().await {
1513                    tracing::warn!(error = %e.as_report(), "Unable to set up an ssl connection");
1514                    let _ = ssl_stream.shutdown().await;
1515                    return Err(e.into());
1516                }
1517
1518                *stream = PgStreamInner::Ssl(ssl_stream);
1519            }
1520            PgStreamInner::Ssl(_) => panic!("the stream is already ssl"),
1521            PgStreamInner::Placeholder => unreachable!(),
1522        }
1523
1524        Ok(())
1525    }
1526}
1527
1528fn build_ssl_ctx_from_config(tls_config: &TlsConfig) -> PsqlResult<SslContext> {
1529    let mut acceptor = SslAcceptor::mozilla_intermediate_v5(SslMethod::tls()).unwrap();
1530
1531    let key_path = &tls_config.key;
1532    let cert_path = &tls_config.cert;
1533
1534    // Build ssl acceptor according to the config.
1535    // Now we set every verify to true.
1536    acceptor
1537        .set_private_key_file(key_path, openssl::ssl::SslFiletype::PEM)
1538        .map_err(|e| PsqlError::Uncategorized(e.into()))?;
1539    acceptor
1540        .set_ca_file(cert_path)
1541        .map_err(|e| PsqlError::Uncategorized(e.into()))?;
1542    acceptor
1543        .set_certificate_chain_file(cert_path)
1544        .map_err(|e| PsqlError::Uncategorized(e.into()))?;
1545    let acceptor = acceptor.build();
1546
1547    Ok(acceptor.into_context())
1548}
1549
1550pub mod truncated_fmt {
1551    use std::fmt::*;
1552
1553    struct TruncatedFormatter<'a, 'b> {
1554        remaining: usize,
1555        finished: bool,
1556        f: &'a mut Formatter<'b>,
1557    }
1558    impl Write for TruncatedFormatter<'_, '_> {
1559        fn write_str(&mut self, s: &str) -> Result {
1560            if self.finished {
1561                return Ok(());
1562            }
1563
1564            if self.remaining < s.len() {
1565                let actual = s.floor_char_boundary(self.remaining);
1566                self.f.write_str(&s[0..actual])?;
1567                self.remaining -= actual;
1568                self.f.write_str(&format!("...(truncated,{})", s.len()))?;
1569                self.finished = true; // so that ...(truncated) is printed exactly once
1570            } else {
1571                self.f.write_str(s)?;
1572                self.remaining -= s.len();
1573            }
1574            Ok(())
1575        }
1576    }
1577
1578    pub struct TruncatedFmt<'a, T>(pub &'a T, pub usize);
1579
1580    impl<T> Debug for TruncatedFmt<'_, T>
1581    where
1582        T: Debug,
1583    {
1584        fn fmt(&self, f: &mut Formatter<'_>) -> Result {
1585            TruncatedFormatter {
1586                remaining: self.1,
1587                finished: false,
1588                f,
1589            }
1590            .write_fmt(format_args!("{:?}", self.0))
1591        }
1592    }
1593
1594    impl<T> Display for TruncatedFmt<'_, T>
1595    where
1596        T: Display,
1597    {
1598        fn fmt(&self, f: &mut Formatter<'_>) -> Result {
1599            TruncatedFormatter {
1600                remaining: self.1,
1601                finished: false,
1602                f,
1603            }
1604            .write_fmt(format_args!("{}", self.0))
1605        }
1606    }
1607
1608    #[cfg(test)]
1609    mod tests {
1610        use super::*;
1611
1612        #[test]
1613        fn test_trunc_utf8() {
1614            assert_eq!(
1615                format!("{}", TruncatedFmt(&"select '🌊';", 10)),
1616                "select '...(truncated,14)",
1617            );
1618        }
1619    }
1620}
1621
1622/// Handle `options` in `StartupMessage` from client
1623///
1624/// It is like shell arguments but only respects backslash-escape and space;
1625/// quotes have no special meaning and are handled literally.
1626///
1627/// PostgreSQL allows both `-c key=value` and `--key=value`.
1628///
1629/// `key-name` is normalized as `key_name`.
1630///
1631/// * <https://github.com/postgres/postgres/blob/REL_18_1/src/backend/utils/init/postinit.c#L487>
1632/// * <https://github.com/postgres/postgres/blob/REL_18_1/src/backend/tcop/postgres.c#L3866>
1633/// * <https://github.com/postgres/postgres/blob/REL_18_1/src/backend/utils/misc/guc.c#L6361>
1634fn parse_options(options: &str) -> PsqlResult<Vec<(String, String)>> {
1635    let mut args = Vec::new();
1636    let mut current_arg = String::new();
1637    let mut chars = options.chars().peekable();
1638
1639    while let Some(c) = chars.next() {
1640        if c == '\\' {
1641            if let Some(next_c) = chars.next() {
1642                current_arg.push(next_c);
1643            }
1644        } else if c.is_ascii_whitespace() {
1645            if !current_arg.is_empty() {
1646                args.push(std::mem::take(&mut current_arg));
1647            }
1648        } else {
1649            current_arg.push(c);
1650        }
1651    }
1652    if !current_arg.is_empty() {
1653        args.push(current_arg);
1654    }
1655
1656    let mut args_iter = args.into_iter();
1657    let mut config = Vec::new();
1658
1659    while let Some(arg) = args_iter.next() {
1660        if arg == "-c" {
1661            if let Some(config_str) = args_iter.next() {
1662                if let Some((key, value)) = config_str.split_once('=') {
1663                    let key = key.replace("-", "_");
1664                    config.push((key, value.to_owned()));
1665                } else {
1666                    return Err(PsqlError::StartupError(
1667                        format!("invalid config format: {}", config_str).into(),
1668                    ));
1669                }
1670            } else {
1671                return Err(PsqlError::StartupError("missing argument for -c".into()));
1672            }
1673        } else if let Some(config_str) = arg.strip_prefix("--") {
1674            if let Some((key, value)) = config_str.split_once('=') {
1675                let key = key.replace("-", "_");
1676                config.push((key, value.to_owned()));
1677            } else {
1678                return Err(PsqlError::StartupError(
1679                    format!("invalid config format: {}", config_str).into(),
1680                ));
1681            }
1682        } else {
1683            tracing::warn!(
1684                arg,
1685                "ignoring unrecognized option for backward compatibility"
1686            );
1687        }
1688    }
1689    Ok(config)
1690}
1691
1692#[cfg(test)]
1693mod tests {
1694    use std::collections::HashSet;
1695
1696    use tokio::io::AsyncReadExt;
1697
1698    use super::*;
1699    use crate::types::Row;
1700
1701    #[tokio::test]
1702    async fn test_streaming_write_flushes_at_threshold() {
1703        const STREAM_FLUSH_THRESHOLD: usize = 64 * 1024;
1704
1705        let (server, mut client) = tokio::io::duplex(STREAM_FLUSH_THRESHOLD * 2);
1706        let mut stream = PgStream::new(server, STREAM_FLUSH_THRESHOLD);
1707
1708        let small_row = Row::new(vec![Some(Bytes::from_static(b"small"))]);
1709        stream
1710            .write_streaming(BeMessage::DataRow(&small_row))
1711            .await
1712            .unwrap();
1713        assert!(!stream.write_buf.is_empty());
1714
1715        let large_row = Row::new(vec![Some(Bytes::from(vec![0; STREAM_FLUSH_THRESHOLD]))]);
1716        stream
1717            .write_streaming(BeMessage::DataRow(&large_row))
1718            .await
1719            .unwrap();
1720        assert!(stream.write_buf.is_empty());
1721
1722        let mut message_tag = [0];
1723        client.read_exact(&mut message_tag).await.unwrap();
1724        assert_eq!(message_tag[0], b'D');
1725    }
1726
1727    #[test]
1728    fn test_redact_parsable_sql() {
1729        let keywords = Arc::new(HashSet::from(["v2".into(), "v4".into(), "b".into()]));
1730        let sql = r"
1731        create source temp (k bigint, v varchar) with (
1732            connector = 'datagen',
1733            v1 = 123,
1734            v2 = 'with',
1735            v3 = false,
1736            v4 = '',
1737        ) FORMAT plain ENCODE json (a='1',b='2')
1738        ";
1739        assert_eq!(
1740            redact_sql(sql, keywords),
1741            "CREATE SOURCE temp (k BIGINT, v CHARACTER VARYING) WITH (connector = 'datagen', v1 = 123, v2 = [REDACTED], v3 = false, v4 = [REDACTED]) FORMAT PLAIN ENCODE JSON (a = '1', b = [REDACTED])"
1742        );
1743    }
1744
1745    #[test]
1746    fn test_redact_user_password_sql() {
1747        let keywords = Arc::new(HashSet::from(["password".into()]));
1748
1749        assert_eq!(
1750            redact_sql("ALTER USER WITH PASSWORD 'rw_password_2'", keywords.clone()),
1751            "ALTER USER WITH PASSWORD [REDACTED]"
1752        );
1753        assert_eq!(
1754            redact_sql(
1755                "ALTER USER foo WITH ENCRYPTED PASSWORD 'md5827ccb0eea8a706c4c34a16891f84e7b'",
1756                keywords.clone(),
1757            ),
1758            "ALTER USER foo WITH ENCRYPTED PASSWORD [REDACTED]"
1759        );
1760        assert_eq!(
1761            redact_sql("CREATE USER foo WITH PASSWORD 'rw_password_2'", keywords),
1762            "CREATE USER foo WITH PASSWORD [REDACTED]"
1763        );
1764    }
1765
1766    #[test]
1767    fn test_parse_options() {
1768        assert_eq!(parse_options("").unwrap(), vec![]);
1769        assert_eq!(
1770            parse_options("-c a=1 -c b=2").unwrap(),
1771            vec![("a".into(), "1".into()), ("b".into(), "2".into())]
1772        );
1773        assert_eq!(
1774            parse_options("-c   key=value").unwrap(),
1775            vec![("key".into(), "value".into())]
1776        );
1777        // Custom parser treats quotes as normal characters, so they are included in value
1778        assert_eq!(
1779            parse_options("-c key='value'").unwrap(),
1780            vec![("key".into(), "'value'".into())]
1781        );
1782
1783        // Test backslash escaping for spaces (standard Postgres way)
1784        assert_eq!(
1785            parse_options(r#"-c key=value\ with\ spaces"#).unwrap(),
1786            vec![("key".into(), "value with spaces".into())]
1787        );
1788        assert_eq!(
1789            parse_options(r#"-c search_path=my\ schema"#).unwrap(),
1790            vec![("search_path".into(), "my schema".into())]
1791        );
1792
1793        assert!(parse_options("-c").is_err());
1794        assert!(parse_options("-c foo").is_err()); // missing =
1795        assert!(parse_options("--foo").is_err()); // missing = in -- option
1796
1797        assert_eq!(
1798            parse_options("--foo=bar").unwrap(),
1799            vec![("foo".into(), "bar".into())]
1800        );
1801        assert_eq!(
1802            parse_options(r#"--foo=bar\ baz"#).unwrap(),
1803            vec![("foo".into(), "bar baz".into())]
1804        );
1805        assert_eq!(
1806            parse_options("-c a=1 --b=2").unwrap(),
1807            vec![("a".into(), "1".into()), ("b".into(), "2".into())]
1808        );
1809        // Unpaired trailing backslash is silently dropped, same as PostgreSQL
1810        assert_eq!(
1811            parse_options(r#"-c a=b\"#).unwrap(),
1812            vec![("a".into(), "b".into())]
1813        );
1814    }
1815}