Skip to main content

risingwave_frontend/session/
cursor_manager.rs

1// Copyright 2024 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 core::mem;
16use core::time::Duration;
17use std::collections::{HashMap, HashSet, VecDeque};
18use std::fmt::{Display, Formatter};
19use std::sync::Arc;
20use std::time::Instant;
21
22use anyhow::anyhow;
23use bytes::Bytes;
24use futures::StreamExt;
25use itertools::Itertools;
26use pgwire::pg_field_descriptor::PgFieldDescriptor;
27use pgwire::pg_response::StatementType;
28use pgwire::types::{Format, Row};
29use risingwave_batch::task::{ShutdownSender, ShutdownToken};
30use risingwave_common::catalog::{ColumnCatalog, Field};
31use risingwave_common::error::BoxedError;
32use risingwave_common::session_config::QueryMode;
33use risingwave_common::types::{DataType, ScalarImpl, StructType, StructValue};
34use risingwave_common::util::iter_util::ZipEqFast;
35use risingwave_hummock_sdk::HummockVersionId;
36
37use super::SessionImpl;
38use crate::catalog::TableId;
39use crate::catalog::subscription_catalog::SubscriptionCatalog;
40use crate::error::{ErrorCode, Result};
41use crate::expr::{ExprType, FunctionCall, InputRef, Literal};
42use crate::handler::HandlerArgs;
43use crate::handler::declare_cursor::create_chunk_stream_for_cursor;
44use crate::handler::query::{RwBatchQueryPlanResult, gen_batch_plan_fragmenter};
45use crate::handler::util::{
46    DataChunkToRowSetAdapter, StaticSessionData, convert_logstore_u64_to_unix_millis,
47    pg_value_format, to_pg_field,
48};
49use crate::monitor::{CursorMetrics, PeriodicCursorMetrics};
50use crate::optimizer::PlanRoot;
51use crate::optimizer::plan_node::{BatchFilter, BatchLogSeqScan, BatchSeqScan, generic};
52use crate::optimizer::property::{Order, RequiredDist};
53use crate::scheduler::{DistributedQueryStream, LocalQueryStream, ReadSnapshot, SchedulerError};
54use crate::utils::Condition;
55use crate::{OptimizerContext, OptimizerContextRef, PgResponseStream, TableCatalog};
56
57pub enum CursorDataChunkStream {
58    LocalDataChunk(Option<LocalQueryStream>),
59    DistributedDataChunk(Option<DistributedQueryStream>),
60    PgResponse(PgResponseStream),
61}
62
63pub struct FetchCursorCancelHandle {
64    cancel_tx: ShutdownSender,
65    cancel_rx: ShutdownToken,
66}
67
68impl FetchCursorCancelHandle {
69    pub fn new() -> Self {
70        let (cancel_tx, cancel_rx) = ShutdownToken::new();
71        Self {
72            cancel_tx,
73            cancel_rx,
74        }
75    }
76
77    fn register(&self, session: &SessionImpl) {
78        session.set_cancel_query_flag(self.cancel_tx.clone());
79    }
80
81    async fn cancelled(&mut self) {
82        self.cancel_rx.cancelled().await;
83    }
84
85    fn is_cancelled(&self) -> bool {
86        self.cancel_rx.is_cancelled()
87    }
88}
89
90impl CursorDataChunkStream {
91    pub fn init_row_stream(
92        &mut self,
93        fields: &Vec<Field>,
94        formats: &Vec<Format>,
95        session: Arc<SessionImpl>,
96    ) {
97        let columns_type = fields.iter().map(|f| f.data_type()).collect();
98        match self {
99            CursorDataChunkStream::LocalDataChunk(data_chunk) => {
100                let data_chunk = mem::take(data_chunk).unwrap();
101                let row_stream = PgResponseStream::LocalQuery(DataChunkToRowSetAdapter::new(
102                    data_chunk,
103                    columns_type,
104                    formats.clone(),
105                    session,
106                ));
107                *self = CursorDataChunkStream::PgResponse(row_stream);
108            }
109            CursorDataChunkStream::DistributedDataChunk(data_chunk) => {
110                let data_chunk = mem::take(data_chunk).unwrap();
111                let row_stream = PgResponseStream::DistributedQuery(DataChunkToRowSetAdapter::new(
112                    data_chunk,
113                    columns_type,
114                    formats.clone(),
115                    session,
116                ));
117                *self = CursorDataChunkStream::PgResponse(row_stream);
118            }
119            _ => {}
120        }
121    }
122
123    pub async fn next(&mut self) -> Result<Option<std::result::Result<Vec<Row>, BoxedError>>> {
124        match self {
125            CursorDataChunkStream::PgResponse(row_stream) => Ok(row_stream.next().await),
126            _ => Err(ErrorCode::InternalError(
127                "Only 'CursorDataChunkStream' can call next and return rows".to_owned(),
128            )
129            .into()),
130        }
131    }
132}
133pub enum Cursor {
134    Subscription(SubscriptionCursor),
135    Query(QueryCursor),
136}
137impl Cursor {
138    pub async fn next(
139        &mut self,
140        count: u32,
141        handler_args: HandlerArgs,
142        formats: &Vec<Format>,
143        timeout_seconds: Option<u64>,
144        cancel_handle: &mut FetchCursorCancelHandle,
145    ) -> Result<(Vec<Row>, Vec<PgFieldDescriptor>)> {
146        match self {
147            Cursor::Subscription(cursor) => cursor
148                .next(count, handler_args, formats, timeout_seconds, cancel_handle)
149                .await
150                .inspect_err(|_| cursor.cursor_metrics.subscription_cursor_error_count.inc()),
151            Cursor::Query(cursor) => {
152                cursor
153                    .next(count, formats, handler_args, timeout_seconds)
154                    .await
155            }
156        }
157    }
158
159    pub fn get_fields(&mut self) -> Vec<Field> {
160        match self {
161            Cursor::Subscription(cursor) => cursor.fields_manager.get_output_fields(),
162            Cursor::Query(cursor) => cursor.fields.clone(),
163        }
164    }
165}
166
167pub struct QueryCursor {
168    chunk_stream: CursorDataChunkStream,
169    fields: Vec<Field>,
170    remaining_rows: VecDeque<Row>,
171}
172
173impl QueryCursor {
174    pub fn new(chunk_stream: CursorDataChunkStream, fields: Vec<Field>) -> Result<Self> {
175        Ok(Self {
176            chunk_stream,
177            fields,
178            remaining_rows: VecDeque::<Row>::new(),
179        })
180    }
181
182    pub async fn next_once(&mut self) -> Result<Option<Row>> {
183        while self.remaining_rows.is_empty() {
184            let rows = self.chunk_stream.next().await?;
185            let rows = match rows {
186                None => return Ok(None),
187                Some(row) => row?,
188            };
189            self.remaining_rows = rows.into_iter().collect();
190        }
191        let row = self.remaining_rows.pop_front().unwrap();
192        Ok(Some(row))
193    }
194
195    pub async fn next(
196        &mut self,
197        count: u32,
198        formats: &Vec<Format>,
199        handler_args: HandlerArgs,
200        timeout_seconds: Option<u64>,
201    ) -> Result<(Vec<Row>, Vec<PgFieldDescriptor>)> {
202        // `FETCH NEXT` is equivalent to `FETCH 1`.
203        // min with 100 to avoid allocating too many memory at once.
204        let timeout_instant = timeout_seconds.map(|s| Instant::now() + Duration::from_secs(s));
205        let session = handler_args.session;
206        let mut ans = Vec::with_capacity(std::cmp::min(100, count) as usize);
207        let mut cur = 0;
208        let desc = self.fields.iter().map(to_pg_field).collect();
209        self.chunk_stream
210            .init_row_stream(&self.fields, formats, session);
211        while cur < count
212            && let Some(row) = self.next_once().await?
213        {
214            cur += 1;
215            ans.push(row);
216            if let Some(timeout_instant) = timeout_instant
217                && Instant::now() > timeout_instant
218            {
219                break;
220            }
221        }
222        Ok((ans, desc))
223    }
224}
225
226enum State {
227    InitLogStoreQuery {
228        // The rw_timestamp used to initiate the query to read from subscription logstore.
229        seek_timestamp: u64,
230
231        // If specified, the expected_timestamp must be an exact match for the next rw_timestamp.
232        expected_timestamp: Option<u64>,
233    },
234    Fetch {
235        // Whether the query is reading from snapshot
236        // true: read from the upstream table snapshot
237        // false: read from subscription logstore
238        from_snapshot: bool,
239
240        // The rw_timestamp used to initiate the query to read from subscription logstore.
241        rw_timestamp: u64,
242
243        // The row stream to from the batch query read.
244        // It is returned from the batch execution.
245        chunk_stream: CursorDataChunkStream,
246
247        // A cache to store the remaining rows from the row stream.
248        remaining_rows: VecDeque<Row>,
249
250        expected_timestamp: Option<u64>,
251
252        init_query_timer: Instant,
253    },
254    Invalid,
255}
256
257impl Display for State {
258    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
259        match self {
260            State::InitLogStoreQuery {
261                seek_timestamp,
262                expected_timestamp,
263            } => write!(
264                f,
265                "InitLogStoreQuery {{ seek_timestamp: {}, expected_timestamp: {:?} }}",
266                seek_timestamp, expected_timestamp
267            ),
268            State::Fetch {
269                from_snapshot,
270                rw_timestamp,
271                expected_timestamp,
272                remaining_rows,
273                init_query_timer,
274                ..
275            } => write!(
276                f,
277                "Fetch {{ from_snapshot: {}, rw_timestamp: {}, expected_timestamp: {:?}, cached rows: {}, query init at {}ms before }}",
278                from_snapshot,
279                rw_timestamp,
280                expected_timestamp,
281                remaining_rows.len(),
282                init_query_timer.elapsed().as_millis()
283            ),
284            State::Invalid => write!(f, "Invalid"),
285        }
286    }
287}
288
289struct FieldsManager {
290    columns_catalog: Vec<ColumnCatalog>,
291    // All row fields, including hidden pk, op, rw_timestamp and all non-hidden columns in the upstream table.
292    row_fields: Vec<Field>,
293    // Row output column indices based on `row_fields`.
294    row_output_col_indices: Vec<usize>,
295    // Row pk indices based on `row_fields`.
296    row_pk_indices: Vec<usize>,
297    // Stream chunk row indices based on `row_fields`.
298    stream_chunk_row_indices: Vec<usize>,
299    // The op index based on `row_fields`.
300    op_index: usize,
301}
302
303impl FieldsManager {
304    // pub const OP_FIELD: Field = Field::with_name(DataType::Varchar, "op".to_owned());
305    // pub const RW_TIMESTAMP_FIELD: Field = Field::with_name(DataType::Int64, "rw_timestamp".to_owned());
306
307    pub fn new(catalog: &TableCatalog) -> Self {
308        let mut row_fields = Vec::new();
309        let mut row_output_col_indices = Vec::new();
310        let mut row_pk_indices = Vec::new();
311        let mut stream_chunk_row_indices = Vec::new();
312        let mut output_idx = 0_usize;
313        let pk_set: HashSet<usize> = catalog
314            .pk
315            .iter()
316            .map(|col_order| col_order.column_index)
317            .collect();
318
319        for (index, v) in catalog.columns.iter().enumerate() {
320            if pk_set.contains(&index) {
321                row_pk_indices.push(output_idx);
322                stream_chunk_row_indices.push(output_idx);
323                row_fields.push(Field::with_name(v.data_type().clone(), v.name()));
324                if !v.is_hidden {
325                    row_output_col_indices.push(output_idx);
326                }
327                output_idx += 1;
328            } else if !v.is_hidden {
329                row_output_col_indices.push(output_idx);
330                stream_chunk_row_indices.push(output_idx);
331                row_fields.push(Field::with_name(v.data_type().clone(), v.name()));
332                output_idx += 1;
333            }
334        }
335
336        row_fields.push(Field::with_name(DataType::Varchar, "op".to_owned()));
337        row_output_col_indices.push(output_idx);
338        let op_index = output_idx;
339        output_idx += 1;
340        row_fields.push(Field::with_name(DataType::Int64, "rw_timestamp".to_owned()));
341        row_output_col_indices.push(output_idx);
342        Self {
343            columns_catalog: catalog.columns.clone(),
344            row_fields,
345            row_output_col_indices,
346            row_pk_indices,
347            stream_chunk_row_indices,
348            op_index,
349        }
350    }
351
352    pub fn try_refill_fields(&mut self, catalog: &TableCatalog) -> bool {
353        if self.columns_catalog.ne(&catalog.columns) {
354            *self = Self::new(catalog);
355            true
356        } else {
357            false
358        }
359    }
360
361    pub fn process_output_desc_row(&self, mut rows: Vec<Row>) -> (Vec<Row>, Option<Row>) {
362        let last_row = rows.last_mut().map(|row| {
363            let mut row = row.clone();
364            row.project(&self.row_pk_indices)
365        });
366        let rows = rows
367            .iter_mut()
368            .map(|row| row.project(&self.row_output_col_indices))
369            .collect();
370        (rows, last_row)
371    }
372
373    pub fn get_output_fields(&self) -> Vec<Field> {
374        self.row_output_col_indices
375            .iter()
376            .map(|&idx| self.row_fields[idx].clone())
377            .collect()
378    }
379
380    // In the beginning (declare cur), we will give it an empty formats,
381    // this formats is not a real, when we fetch, We fill it with the formats returned from the pg client.
382    pub fn get_row_stream_fields_and_formats(
383        &self,
384        formats: &Vec<Format>,
385        from_snapshot: bool,
386    ) -> (Vec<Field>, Vec<Format>) {
387        let mut fields = Vec::new();
388        let need_format = !(formats.is_empty() || formats.len() == 1);
389        let mut new_formats = formats.clone();
390        let stream_chunk_row_indices_iter = if from_snapshot {
391            self.stream_chunk_row_indices.iter().chain(None)
392        } else {
393            self.stream_chunk_row_indices
394                .iter()
395                .chain(Some(&self.op_index))
396        };
397        for index in stream_chunk_row_indices_iter {
398            fields.push(self.row_fields[*index].clone());
399            if need_format && !self.row_output_col_indices.contains(index) {
400                new_formats.insert(*index, Format::Text);
401            }
402        }
403        (fields, new_formats)
404    }
405}
406
407pub struct SubscriptionCursor {
408    cursor_name: String,
409    subscription: Arc<SubscriptionCatalog>,
410    dependent_table_id: TableId,
411    cursor_need_drop_time: Instant,
412    state: State,
413    // fields will be set in the table's catalog when the cursor is created,
414    // and will be reset each time it is created chunk_stream, this is to avoid changes in the catalog due to alter.
415    fields_manager: FieldsManager,
416    cursor_metrics: Arc<CursorMetrics>,
417    last_fetch: Instant,
418    seek_pk_row: Option<Row>,
419}
420
421impl SubscriptionCursor {
422    pub async fn new(
423        cursor_name: String,
424        start_timestamp: Option<u64>,
425        subscription: Arc<SubscriptionCatalog>,
426        dependent_table_id: TableId,
427        handler_args: &HandlerArgs,
428        cursor_metrics: Arc<CursorMetrics>,
429    ) -> Result<Self> {
430        let (state, fields_manager) = if let Some(start_timestamp) = start_timestamp {
431            let table_catalog = handler_args.session.get_table_by_id(dependent_table_id)?;
432            (
433                State::InitLogStoreQuery {
434                    seek_timestamp: start_timestamp,
435                    expected_timestamp: None,
436                },
437                FieldsManager::new(&table_catalog),
438            )
439        } else {
440            // The query stream needs to initiated on cursor creation to make sure
441            // future fetch on the cursor starts from the snapshot when the cursor is declared.
442            //
443            // TODO: is this the right behavior? Should we delay the query stream initiation till the first fetch?
444            let (chunk_stream, init_query_timer, table_catalog) =
445                Self::initiate_query(None, dependent_table_id, handler_args.clone(), None).await?;
446            let pinned_epoch = match handler_args.session.get_pinned_snapshot().ok_or_else(
447                || ErrorCode::InternalError("Fetch Cursor can't find snapshot epoch".to_owned()),
448            )? {
449                ReadSnapshot::FrontendPinned { snapshot, .. } => {
450                    snapshot
451                        .version()
452                        .state_table_info
453                        .info()
454                        .get(&dependent_table_id)
455                        .ok_or_else(|| {
456                            anyhow!("dependent_table_id {dependent_table_id} not exists")
457                        })?
458                        .committed_epoch
459                }
460                ReadSnapshot::Other(_) => {
461                    return Err(ErrorCode::InternalError("Fetch Cursor can't start from specified query epoch. May run `set query_epoch = 0;`".to_owned()).into());
462                }
463                ReadSnapshot::ReadUncommitted => {
464                    return Err(ErrorCode::InternalError(
465                        "Fetch Cursor don't support read uncommitted".to_owned(),
466                    )
467                    .into());
468                }
469            };
470            let start_timestamp = pinned_epoch;
471
472            (
473                State::Fetch {
474                    from_snapshot: true,
475                    rw_timestamp: start_timestamp,
476                    chunk_stream,
477                    remaining_rows: VecDeque::new(),
478                    expected_timestamp: None,
479                    init_query_timer,
480                },
481                FieldsManager::new(&table_catalog),
482            )
483        };
484
485        let cursor_need_drop_time =
486            Instant::now() + Duration::from_secs(subscription.retention_seconds);
487        Ok(Self {
488            cursor_name,
489            subscription,
490            dependent_table_id,
491            cursor_need_drop_time,
492            state,
493            fields_manager,
494            cursor_metrics,
495            last_fetch: Instant::now(),
496            seek_pk_row: None,
497        })
498    }
499
500    async fn next_row(
501        &mut self,
502        handler_args: &HandlerArgs,
503        formats: &Vec<Format>,
504    ) -> Result<Option<Row>> {
505        loop {
506            match &mut self.state {
507                State::InitLogStoreQuery {
508                    seek_timestamp,
509                    expected_timestamp,
510                } => {
511                    let from_snapshot = false;
512
513                    // Initiate a new batch query to continue fetching
514                    match Self::get_next_rw_timestamp(
515                        *seek_timestamp,
516                        self.dependent_table_id,
517                        *expected_timestamp,
518                        handler_args.clone(),
519                        &self.subscription,
520                    )
521                    .await
522                    {
523                        Ok((Some(rw_timestamp), expected_timestamp)) => {
524                            let (mut chunk_stream, init_query_timer, catalog) =
525                                Self::initiate_query(
526                                    Some(rw_timestamp),
527                                    self.dependent_table_id,
528                                    handler_args.clone(),
529                                    None,
530                                )
531                                .await?;
532                            let table_schema_changed =
533                                self.fields_manager.try_refill_fields(&catalog);
534                            let (fields, formats) = self
535                                .fields_manager
536                                .get_row_stream_fields_and_formats(formats, from_snapshot);
537                            chunk_stream.init_row_stream(
538                                &fields,
539                                &formats,
540                                handler_args.session.clone(),
541                            );
542
543                            self.cursor_need_drop_time = Instant::now()
544                                + Duration::from_secs(self.subscription.retention_seconds);
545                            let mut remaining_rows = VecDeque::new();
546                            Self::try_refill_remaining_rows(&mut chunk_stream, &mut remaining_rows)
547                                .await?;
548                            // Transition to the Fetch state
549                            self.state = State::Fetch {
550                                from_snapshot,
551                                rw_timestamp,
552                                chunk_stream,
553                                remaining_rows,
554                                expected_timestamp,
555                                init_query_timer,
556                            };
557                            if table_schema_changed {
558                                return Ok(None);
559                            }
560                        }
561                        Ok((None, _)) => return Ok(None),
562                        Err(e) => {
563                            self.state = State::Invalid;
564                            return Err(e);
565                        }
566                    }
567                }
568                State::Fetch {
569                    from_snapshot,
570                    rw_timestamp,
571                    chunk_stream,
572                    remaining_rows,
573                    expected_timestamp,
574                    init_query_timer,
575                } => {
576                    let session_data = StaticSessionData {
577                        timezone: handler_args.session.config().timezone(),
578                    };
579                    let from_snapshot = *from_snapshot;
580                    let rw_timestamp = *rw_timestamp;
581
582                    // Try refill remaining rows
583                    Self::try_refill_remaining_rows(chunk_stream, remaining_rows).await?;
584
585                    if let Some(row) = remaining_rows.pop_front() {
586                        // 1. Fetch the next row
587                        if from_snapshot {
588                            return Ok(Some(Self::build_row(
589                                row.take(),
590                                None,
591                                formats,
592                                &session_data,
593                            )?));
594                        } else {
595                            return Ok(Some(Self::build_row(
596                                row.take(),
597                                Some(rw_timestamp),
598                                formats,
599                                &session_data,
600                            )?));
601                        }
602                    } else {
603                        self.cursor_metrics
604                            .subscription_cursor_query_duration
605                            .with_label_values(&[&self.subscription.name])
606                            .observe(init_query_timer.elapsed().as_millis() as _);
607                        // 2. Reach EOF for the current query.
608                        if let Some(expected_timestamp) = expected_timestamp {
609                            self.state = State::InitLogStoreQuery {
610                                seek_timestamp: *expected_timestamp,
611                                expected_timestamp: Some(*expected_timestamp),
612                            };
613                        } else {
614                            self.state = State::InitLogStoreQuery {
615                                seek_timestamp: rw_timestamp + 1,
616                                expected_timestamp: None,
617                            };
618                        }
619                    }
620                }
621                State::Invalid => {
622                    // TODO: auto close invalid cursor?
623                    return Err(ErrorCode::InternalError(
624                        "Cursor is in invalid state. Please close and re-create the cursor."
625                            .to_owned(),
626                    )
627                    .into());
628                }
629            }
630        }
631    }
632
633    pub async fn next(
634        &mut self,
635        count: u32,
636        handler_args: HandlerArgs,
637        formats: &Vec<Format>,
638        timeout_seconds: Option<u64>,
639        cancel_handle: &mut FetchCursorCancelHandle,
640    ) -> Result<(Vec<Row>, Vec<PgFieldDescriptor>)> {
641        let timeout_instant = timeout_seconds.map(|s| Instant::now() + Duration::from_secs(s));
642        if Instant::now() > self.cursor_need_drop_time {
643            return Err(ErrorCode::InternalError(
644                "The cursor has exceeded its maximum lifetime, please recreate it (close then declare cursor).".to_owned(),
645            )
646            .into());
647        }
648
649        let session = &handler_args.session;
650        let mut ans = Vec::with_capacity(std::cmp::min(100, count) as usize);
651        let mut cur = 0;
652        if let State::Fetch {
653            from_snapshot,
654            chunk_stream,
655            ..
656        } = &mut self.state
657        {
658            let (fields, fotmats) = self
659                .fields_manager
660                .get_row_stream_fields_and_formats(formats, *from_snapshot);
661            chunk_stream.init_row_stream(&fields, &fotmats, session.clone());
662        }
663        while cur < count {
664            if cancel_handle.is_cancelled() {
665                return Err(SchedulerError::QueryCancelled("Cancelled by user".to_owned()).into());
666            }
667            let fetch_cursor_timer = Instant::now();
668            let row = self.next_row(&handler_args, formats).await?;
669            self.cursor_metrics
670                .subscription_cursor_fetch_duration
671                .with_label_values(&[&self.subscription.name])
672                .observe(fetch_cursor_timer.elapsed().as_millis() as _);
673            match row {
674                Some(row) => {
675                    cur += 1;
676                    ans.push(row);
677                }
678                None => {
679                    let timeout_seconds = timeout_seconds.unwrap_or(0);
680                    if cur > 0 || timeout_seconds == 0 {
681                        break;
682                    }
683                    let State::InitLogStoreQuery { seek_timestamp, .. } = &self.state else {
684                        // Triggered when previous next_row returns None while self.state is State::Fetch.
685                        continue;
686                    };
687                    // This is the only point where subscription cursor fetch waits without an
688                    // inner query. Register the FETCH-level cancel token so CancelRequest can
689                    // interrupt this wait. The token also marks the whole FETCH as cancelled, so
690                    // we won't start another inner query after a cancellation.
691                    cancel_handle.register(session);
692                    let timeout = tokio::time::sleep(Duration::from_secs(timeout_seconds));
693                    tokio::pin!(timeout);
694                    tokio::select! {
695                        biased;
696                        _ = cancel_handle.cancelled() => {
697                            return Err(SchedulerError::QueryCancelled(
698                                "Cancelled by user".to_owned(),
699                            )
700                            .into());
701                        }
702                        result = session
703                            .env
704                            .hummock_snapshot_manager()
705                            .wait_table_change_log_notification(
706                                self.dependent_table_id,
707                                *seek_timestamp,
708                            ) => {
709                            result?;
710                        }
711                        _ = &mut timeout => {
712                            tracing::debug!("Cursor wait next epoch timeout");
713                            break;
714                        }
715                    }
716                    if cancel_handle.is_cancelled() {
717                        return Err(
718                            SchedulerError::QueryCancelled("Cancelled by user".to_owned()).into(),
719                        );
720                    }
721                }
722            }
723            // Timeout, return with current value
724            if let Some(timeout_instant) = timeout_instant
725                && Instant::now() > timeout_instant
726            {
727                break;
728            }
729        }
730        self.last_fetch = Instant::now();
731        let (rows, seek_pk_row) = self.fields_manager.process_output_desc_row(ans);
732        if let Some(seek_pk_row) = seek_pk_row {
733            self.seek_pk_row = Some(seek_pk_row);
734        }
735        let desc = self
736            .fields_manager
737            .get_output_fields()
738            .iter()
739            .map(to_pg_field)
740            .collect();
741
742        Ok((rows, desc))
743    }
744
745    async fn get_next_rw_timestamp(
746        seek_timestamp: u64,
747        table_id: TableId,
748        expected_timestamp: Option<u64>,
749        handler_args: HandlerArgs,
750        dependent_subscription: &SubscriptionCatalog,
751    ) -> Result<(Option<u64>, Option<u64>)> {
752        let session = handler_args.session;
753        // Test subscription existence
754        session.get_subscription_by_schema_id_name(
755            dependent_subscription.schema_id,
756            &dependent_subscription.name,
757        )?;
758
759        // The epoch here must be pulled every time, otherwise there will be cache consistency issues
760        let new_epochs = session
761            .list_change_log_epochs(table_id, seek_timestamp, 2)
762            .await?;
763        if let Some(expected_timestamp) = expected_timestamp
764            && (new_epochs.is_empty() || &expected_timestamp != new_epochs.first().unwrap())
765        {
766            return Err(ErrorCode::CatalogError(
767                format!(
768                    " No data found for rw_timestamp {:?}, data may have been recycled, please recreate cursor",
769                    convert_logstore_u64_to_unix_millis(expected_timestamp)
770                )
771                .into(),
772            )
773            .into());
774        }
775        Ok((new_epochs.get(0).cloned(), new_epochs.get(1).cloned()))
776    }
777
778    pub fn gen_batch_plan_result(
779        &self,
780        handler_args: HandlerArgs,
781    ) -> Result<RwBatchQueryPlanResult> {
782        match self.state {
783            // Only used to return generated plans, so rw_timestamp are meaningless
784            State::InitLogStoreQuery { .. } => Self::init_batch_plan_for_subscription_cursor(
785                Some(0),
786                self.dependent_table_id,
787                handler_args,
788                self.seek_pk_row.clone(),
789            ),
790            State::Fetch {
791                from_snapshot,
792                rw_timestamp,
793                ..
794            } => {
795                if from_snapshot {
796                    Self::init_batch_plan_for_subscription_cursor(
797                        None,
798                        self.dependent_table_id,
799                        handler_args,
800                        self.seek_pk_row.clone(),
801                    )
802                } else {
803                    Self::init_batch_plan_for_subscription_cursor(
804                        Some(rw_timestamp),
805                        self.dependent_table_id,
806                        handler_args,
807                        self.seek_pk_row.clone(),
808                    )
809                }
810            }
811            State::Invalid => Err(ErrorCode::InternalError(
812                "Cursor is in invalid state. Please close and re-create the cursor.".to_owned(),
813            )
814            .into()),
815        }
816    }
817
818    fn init_batch_plan_for_subscription_cursor(
819        rw_timestamp: Option<u64>,
820        dependent_table_id: TableId,
821        handler_args: HandlerArgs,
822        seek_pk_row: Option<Row>,
823    ) -> Result<RwBatchQueryPlanResult> {
824        let session = handler_args.clone().session;
825        let table_catalog = session.get_table_by_id(dependent_table_id)?;
826        let context = OptimizerContext::from_handler_args(handler_args);
827        let version_id = {
828            let version = session.env.hummock_snapshot_manager.acquire();
829            let version = version.version();
830            if !version
831                .state_table_info
832                .info()
833                .contains_key(&dependent_table_id)
834            {
835                return Err(anyhow!("table id {dependent_table_id} has been dropped").into());
836            }
837            version.id
838        };
839        Self::create_batch_plan_for_cursor(
840            table_catalog,
841            &session,
842            context.into(),
843            rw_timestamp.map(|rw_timestamp| (rw_timestamp, rw_timestamp)),
844            version_id,
845            seek_pk_row,
846        )
847    }
848
849    async fn initiate_query(
850        rw_timestamp: Option<u64>,
851        dependent_table_id: TableId,
852        handler_args: HandlerArgs,
853        seek_pk_row: Option<Row>,
854    ) -> Result<(CursorDataChunkStream, Instant, Arc<TableCatalog>)> {
855        let init_query_timer = Instant::now();
856        let session = handler_args.clone().session;
857        let table_catalog = session.get_table_by_id(dependent_table_id)?;
858        let plan_result = Self::init_batch_plan_for_subscription_cursor(
859            rw_timestamp,
860            dependent_table_id,
861            handler_args.clone(),
862            seek_pk_row,
863        )?;
864        let plan_fragmenter_result = gen_batch_plan_fragmenter(&handler_args.session, plan_result)?;
865        let (chunk_stream, _) =
866            create_chunk_stream_for_cursor(handler_args.session, plan_fragmenter_result).await?;
867        Ok((chunk_stream, init_query_timer, table_catalog))
868    }
869
870    async fn try_refill_remaining_rows(
871        chunk_stream: &mut CursorDataChunkStream,
872        remaining_rows: &mut VecDeque<Row>,
873    ) -> Result<()> {
874        if remaining_rows.is_empty()
875            && let Some(row_set) = chunk_stream.next().await?
876        {
877            remaining_rows.extend(row_set?);
878        }
879        Ok(())
880    }
881
882    pub fn build_row(
883        mut row: Vec<Option<Bytes>>,
884        rw_timestamp: Option<u64>,
885        formats: &Vec<Format>,
886        session_data: &StaticSessionData,
887    ) -> Result<Row> {
888        let row_len = row.len();
889        let new_row = if let Some(rw_timestamp) = rw_timestamp {
890            let rw_timestamp_formats = formats.get(row_len).unwrap_or(&Format::Text);
891            let rw_timestamp = convert_logstore_u64_to_unix_millis(rw_timestamp);
892            let rw_timestamp = pg_value_format(
893                &DataType::Int64,
894                risingwave_common::types::ScalarRefImpl::Int64(rw_timestamp as i64),
895                *rw_timestamp_formats,
896                session_data,
897            )?;
898            vec![Some(rw_timestamp)]
899        } else {
900            let op_formats = formats.get(row_len).unwrap_or(&Format::Text);
901            let op = pg_value_format(
902                &DataType::Varchar,
903                risingwave_common::types::ScalarRefImpl::Utf8("Insert"),
904                *op_formats,
905                session_data,
906            )?;
907            vec![Some(op), None]
908        };
909        row.extend(new_row);
910        Ok(Row::new(row))
911    }
912
913    pub fn build_desc(mut descs: Vec<Field>, from_snapshot: bool) -> Vec<Field> {
914        if from_snapshot {
915            descs.push(Field::with_name(DataType::Varchar, "op"));
916        }
917        descs.push(Field::with_name(DataType::Int64, "rw_timestamp"));
918        descs
919    }
920
921    pub fn create_batch_plan_for_cursor(
922        table_catalog: Arc<TableCatalog>,
923        session: &SessionImpl,
924        context: OptimizerContextRef,
925        epoch_range: Option<(u64, u64)>,
926        version_id: HummockVersionId,
927        seek_pk_rows: Option<Row>,
928    ) -> Result<RwBatchQueryPlanResult> {
929        // pk + all column without hidden
930        let output_col_idx = table_catalog
931            .columns
932            .iter()
933            .enumerate()
934            .filter_map(|(index, v)| {
935                if !v.is_hidden || table_catalog.pk.iter().any(|pk| pk.column_index == index) {
936                    Some(index)
937                } else {
938                    None
939                }
940            })
941            .collect::<Vec<_>>();
942        let max_split_range_gap = context.session_ctx().config().max_split_range_gap() as u64;
943        let pks = table_catalog.pk();
944        let pks = pks
945            .iter()
946            .map(|f| {
947                let pk = table_catalog.columns.get(f.column_index).unwrap();
948                (pk.data_type(), f.column_index)
949            })
950            .collect_vec();
951        let (scan, predicate) = if let Some(seek_pk_rows) = seek_pk_rows {
952            let mut pk_rows = vec![];
953            let mut values = vec![];
954            for (seek_pk, (data_type, column_index)) in
955                seek_pk_rows.take().into_iter().zip_eq_fast(pks.into_iter())
956            {
957                if let Some(seek_pk) = seek_pk {
958                    pk_rows.push(InputRef {
959                        index: column_index,
960                        data_type: data_type.clone(),
961                    });
962                    let value_string = String::from_utf8(seek_pk.clone().into()).unwrap();
963                    let value_data = ScalarImpl::from_text(&value_string, data_type).unwrap();
964                    values.push((Some(value_data), data_type.clone()));
965                }
966            }
967            if pk_rows.is_empty() {
968                (None, None)
969            } else {
970                let (right_data, right_types): (Vec<_>, Vec<_>) = values.into_iter().unzip();
971                let right_data = ScalarImpl::Struct(StructValue::new(right_data));
972                let right_type = DataType::Struct(StructType::row_expr_type(right_types));
973                let left = FunctionCall::new_unchecked(
974                    ExprType::Row,
975                    pk_rows.into_iter().map(|pk| pk.into()).collect(),
976                    right_type.clone(),
977                );
978                let right = Literal::new(Some(right_data), right_type);
979                let (scan, predicate) = Condition {
980                    conjunctions: vec![
981                        FunctionCall::new(ExprType::GreaterThan, vec![left.into(), right.into()])?
982                            .into(),
983                    ],
984                }
985                .split_to_scan_ranges(&table_catalog, max_split_range_gap)?;
986                if scan.len() > 1 {
987                    return Err(ErrorCode::InternalError(
988                        "Seek pk row should only generate one scan range".to_owned(),
989                    )
990                    .into());
991                }
992                (scan.first().cloned(), Some(predicate))
993            }
994        } else {
995            (None, None)
996        };
997
998        let (seq_scan, out_fields, out_names) = if let Some(epoch_range) = epoch_range {
999            let core = generic::LogScan::new(
1000                table_catalog.name.clone(),
1001                output_col_idx,
1002                table_catalog.clone(),
1003                context,
1004                epoch_range,
1005                version_id,
1006            );
1007            let batch_log_seq_scan = BatchLogSeqScan::new(core, scan);
1008            let out_fields = batch_log_seq_scan.core().out_fields();
1009            let out_names = batch_log_seq_scan.core().column_names();
1010            (batch_log_seq_scan.into(), out_fields, out_names)
1011        } else {
1012            let core = generic::TableScan::new(
1013                output_col_idx,
1014                table_catalog.clone(),
1015                vec![],
1016                vec![],
1017                context,
1018                Condition {
1019                    conjunctions: vec![],
1020                },
1021                None,
1022            );
1023            let scans = match scan {
1024                Some(scan) => vec![scan],
1025                None => vec![],
1026            };
1027            let table_scan = BatchSeqScan::new(core, scans, None);
1028            let out_fields = table_scan.core().out_fields();
1029            let out_names = table_scan.core().column_names();
1030            (table_scan.into(), out_fields, out_names)
1031        };
1032
1033        let plan = if let Some(predicate) = predicate
1034            && !predicate.always_true()
1035        {
1036            BatchFilter::new(generic::Filter::new(predicate, seq_scan)).into()
1037        } else {
1038            seq_scan
1039        };
1040
1041        // order by pk, so don't need to sort
1042        let order = Order::new(table_catalog.pk().to_vec());
1043
1044        // Here we just need a plan_root to call the method, only out_fields and out_names will be used
1045        let plan_root = PlanRoot::new_with_batch_plan(
1046            plan,
1047            RequiredDist::single(),
1048            order,
1049            out_fields,
1050            out_names,
1051        );
1052        let schema = plan_root.schema();
1053        let (batch_log_seq_scan, query_mode) = match session.config().query_mode() {
1054            QueryMode::Auto | QueryMode::Local => {
1055                (plan_root.gen_batch_local_plan()?, QueryMode::Local)
1056            }
1057            QueryMode::Distributed => (
1058                plan_root.gen_batch_distributed_plan()?,
1059                QueryMode::Distributed,
1060            ),
1061        };
1062        Ok(RwBatchQueryPlanResult {
1063            plan: batch_log_seq_scan,
1064            query_mode,
1065            schema,
1066            stmt_type: StatementType::SELECT,
1067            dependent_relations: vec![],
1068            dependent_secrets: vec![],
1069        })
1070    }
1071
1072    pub fn idle_duration(&self) -> Duration {
1073        self.last_fetch.elapsed()
1074    }
1075
1076    pub fn subscription_name(&self) -> &str {
1077        self.subscription.name.as_str()
1078    }
1079
1080    pub fn state_info_string(&self) -> String {
1081        format!("{}", self.state)
1082    }
1083}
1084
1085pub struct CursorManager {
1086    cursor_map: tokio::sync::Mutex<HashMap<String, Cursor>>,
1087    cursor_metrics: Arc<CursorMetrics>,
1088}
1089
1090impl CursorManager {
1091    pub fn new(cursor_metrics: Arc<CursorMetrics>) -> Self {
1092        Self {
1093            cursor_map: tokio::sync::Mutex::new(HashMap::new()),
1094            cursor_metrics,
1095        }
1096    }
1097
1098    pub async fn add_subscription_cursor(
1099        &self,
1100        cursor_name: String,
1101        start_timestamp: Option<u64>,
1102        dependent_table_id: TableId,
1103        subscription: Arc<SubscriptionCatalog>,
1104        handler_args: &HandlerArgs,
1105    ) -> Result<()> {
1106        let create_cursor_timer = Instant::now();
1107        let subscription_name = subscription.name.clone();
1108        let cursor = SubscriptionCursor::new(
1109            cursor_name,
1110            start_timestamp,
1111            subscription,
1112            dependent_table_id,
1113            handler_args,
1114            self.cursor_metrics.clone(),
1115        )
1116        .await?;
1117        let mut cursor_map = self.cursor_map.lock().await;
1118        self.cursor_metrics
1119            .subscription_cursor_declare_duration
1120            .with_label_values(&[&subscription_name])
1121            .observe(create_cursor_timer.elapsed().as_millis() as _);
1122
1123        cursor_map.retain(|_, v| {
1124            if let Cursor::Subscription(cursor) = v
1125                && matches!(cursor.state, State::Invalid)
1126            {
1127                false
1128            } else {
1129                true
1130            }
1131        });
1132
1133        cursor_map
1134            .try_insert(cursor.cursor_name.clone(), Cursor::Subscription(cursor))
1135            .map_err(|error| {
1136                ErrorCode::CatalogError(
1137                    format!("cursor `{}` already exists", error.entry.key()).into(),
1138                )
1139            })?;
1140        Ok(())
1141    }
1142
1143    pub async fn add_query_cursor(
1144        &self,
1145        cursor_name: String,
1146        chunk_stream: CursorDataChunkStream,
1147        fields: Vec<Field>,
1148    ) -> Result<()> {
1149        let cursor = QueryCursor::new(chunk_stream, fields)?;
1150        self.cursor_map
1151            .lock()
1152            .await
1153            .try_insert(cursor_name, Cursor::Query(cursor))
1154            .map_err(|error| {
1155                ErrorCode::CatalogError(
1156                    format!("cursor `{}` already exists", error.entry.key()).into(),
1157                )
1158            })?;
1159
1160        Ok(())
1161    }
1162
1163    pub async fn remove_cursor(&self, cursor_name: &str) -> Result<()> {
1164        self.cursor_map
1165            .lock()
1166            .await
1167            .remove(cursor_name)
1168            .ok_or_else(|| {
1169                ErrorCode::CatalogError(format!("cursor `{}` don't exists", cursor_name).into())
1170            })?;
1171        Ok(())
1172    }
1173
1174    pub async fn remove_all_cursor(&self) {
1175        self.cursor_map.lock().await.clear();
1176    }
1177
1178    pub async fn remove_all_query_cursor(&self) {
1179        self.cursor_map
1180            .lock()
1181            .await
1182            .retain(|_, v| matches!(v, Cursor::Subscription(_)));
1183    }
1184
1185    pub async fn get_rows_with_cursor(
1186        &self,
1187        cursor_name: &str,
1188        count: u32,
1189        handler_args: HandlerArgs,
1190        formats: &Vec<Format>,
1191        timeout_seconds: Option<u64>,
1192        cancel_handle: &mut FetchCursorCancelHandle,
1193    ) -> Result<(Vec<Row>, Vec<PgFieldDescriptor>)> {
1194        if let Some(cursor) = self.cursor_map.lock().await.get_mut(cursor_name) {
1195            cursor
1196                .next(count, handler_args, formats, timeout_seconds, cancel_handle)
1197                .await
1198        } else {
1199            Err(ErrorCode::InternalError(format!("Cannot find cursor `{}`", cursor_name)).into())
1200        }
1201    }
1202
1203    pub async fn get_fields_with_cursor(&self, cursor_name: &str) -> Result<Vec<Field>> {
1204        if let Some(cursor) = self.cursor_map.lock().await.get_mut(cursor_name) {
1205            Ok(cursor.get_fields())
1206        } else {
1207            Err(ErrorCode::InternalError(format!("Cannot find cursor `{}`", cursor_name)).into())
1208        }
1209    }
1210
1211    pub async fn get_periodic_cursor_metrics(&self) -> PeriodicCursorMetrics {
1212        let mut subscription_cursor_nums = 0;
1213        let mut invalid_subscription_cursor_nums = 0;
1214        let mut subscription_cursor_last_fetch_duration = HashMap::new();
1215        for cursor in self.cursor_map.lock().await.values() {
1216            if let Cursor::Subscription(subscription_cursor) = cursor {
1217                subscription_cursor_nums += 1;
1218                if matches!(subscription_cursor.state, State::Invalid) {
1219                    invalid_subscription_cursor_nums += 1;
1220                } else {
1221                    let fetch_duration =
1222                        subscription_cursor.last_fetch.elapsed().as_millis() as f64;
1223                    subscription_cursor_last_fetch_duration.insert(
1224                        subscription_cursor.subscription.name.clone(),
1225                        fetch_duration,
1226                    );
1227                }
1228            }
1229        }
1230        PeriodicCursorMetrics {
1231            subscription_cursor_nums,
1232            invalid_subscription_cursor_nums,
1233            subscription_cursor_last_fetch_duration,
1234        }
1235    }
1236
1237    pub async fn iter_query_cursors(&self, mut f: impl FnMut(&String, &QueryCursor)) {
1238        self.cursor_map
1239            .lock()
1240            .await
1241            .iter()
1242            .for_each(|(cursor_name, cursor)| {
1243                if let Cursor::Query(cursor) = cursor {
1244                    f(cursor_name, cursor)
1245                }
1246            });
1247    }
1248
1249    pub async fn iter_subscription_cursors(&self, mut f: impl FnMut(&String, &SubscriptionCursor)) {
1250        self.cursor_map
1251            .lock()
1252            .await
1253            .iter()
1254            .for_each(|(cursor_name, cursor)| {
1255                if let Cursor::Subscription(cursor) = cursor {
1256                    f(cursor_name, cursor)
1257                }
1258            });
1259    }
1260
1261    pub async fn gen_batch_plan_with_subscription_cursor(
1262        &self,
1263        cursor_name: &str,
1264        handler_args: HandlerArgs,
1265    ) -> Result<RwBatchQueryPlanResult> {
1266        match self.cursor_map.lock().await.get(cursor_name).ok_or_else(|| {
1267            ErrorCode::InternalError(format!("Cannot find cursor `{}`", cursor_name))
1268        })? {
1269            Cursor::Subscription(cursor) => {
1270                cursor.gen_batch_plan_result(handler_args.clone())
1271            },
1272            Cursor::Query(_) => Err(ErrorCode::InternalError("The plan of the cursor is the same as the query statement of the as when it was created.".to_owned()).into()),
1273        }
1274    }
1275}