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