catalog/system_schema/information_schema/
procedure_info.rs1use std::sync::{Arc, Weak};
16
17use arrow_schema::SchemaRef as ArrowSchemaRef;
18use common_catalog::consts::INFORMATION_SCHEMA_PROCEDURE_INFO_TABLE_ID;
19use common_error::ext::BoxedError;
20use common_procedure::ProcedureInfo;
21use common_recordbatch::adapter::RecordBatchStreamAdapter;
22use common_recordbatch::{RecordBatch, SendableRecordBatchStream};
23use common_time::timestamp::Timestamp;
24use datafusion::execution::TaskContext;
25use datafusion::physical_plan::SendableRecordBatchStream as DfSendableRecordBatchStream;
26use datafusion::physical_plan::stream::RecordBatchStreamAdapter as DfRecordBatchStreamAdapter;
27use datafusion::physical_plan::streaming::PartitionStream as DfPartitionStream;
28use datatypes::prelude::{ConcreteDataType, ScalarVectorBuilder, VectorRef};
29use datatypes::schema::{ColumnSchema, Schema, SchemaRef};
30use datatypes::timestamp::TimestampMillisecond;
31use datatypes::value::Value;
32use datatypes::vectors::{StringVectorBuilder, TimestampMillisecondVectorBuilder};
33use snafu::ResultExt;
34use store_api::storage::{ScanRequest, TableId};
35
36use crate::CatalogManager;
37use crate::error::{CreateRecordBatchSnafu, InternalSnafu, Result};
38use crate::system_schema::SystemTable;
39use crate::system_schema::information_schema::{
40 InformationSchemaTableScope, InformationTable, PROCEDURE_INFO, Predicates,
41};
42use crate::system_schema::utils;
43
44const PROCEDURE_ID: &str = "procedure_id";
45const PROCEDURE_TYPE: &str = "procedure_type";
46const START_TIME: &str = "start_time";
47const END_TIME: &str = "end_time";
48const STATUS: &str = "status";
49const LOCK_KEYS: &str = "lock_keys";
50
51const INIT_CAPACITY: usize = 42;
52
53#[derive(Debug)]
62pub(super) struct InformationSchemaProcedureInfo {
63 schema: SchemaRef,
64 catalog_manager: Weak<dyn CatalogManager>,
65}
66
67impl InformationSchemaProcedureInfo {
68 pub(super) fn new(catalog_manager: Weak<dyn CatalogManager>) -> Self {
69 Self {
70 schema: Self::schema(),
71 catalog_manager,
72 }
73 }
74
75 pub(crate) fn schema() -> SchemaRef {
76 Arc::new(Schema::new(vec![
77 ColumnSchema::new(PROCEDURE_ID, ConcreteDataType::string_datatype(), false),
78 ColumnSchema::new(PROCEDURE_TYPE, ConcreteDataType::string_datatype(), false),
79 ColumnSchema::new(
80 START_TIME,
81 ConcreteDataType::timestamp_millisecond_datatype(),
82 true,
83 ),
84 ColumnSchema::new(
85 END_TIME,
86 ConcreteDataType::timestamp_millisecond_datatype(),
87 true,
88 ),
89 ColumnSchema::new(STATUS, ConcreteDataType::string_datatype(), false),
90 ColumnSchema::new(LOCK_KEYS, ConcreteDataType::string_datatype(), true),
91 ]))
92 }
93
94 fn builder(&self) -> InformationSchemaProcedureInfoBuilder {
95 InformationSchemaProcedureInfoBuilder::new(
96 self.schema.clone(),
97 self.catalog_manager.clone(),
98 )
99 }
100}
101
102impl InformationTable for InformationSchemaProcedureInfo {
103 fn scope(&self) -> InformationSchemaTableScope {
104 InformationSchemaTableScope::Global
105 }
106}
107
108impl SystemTable for InformationSchemaProcedureInfo {
109 fn table_id(&self) -> TableId {
110 INFORMATION_SCHEMA_PROCEDURE_INFO_TABLE_ID
111 }
112
113 fn table_name(&self) -> &'static str {
114 PROCEDURE_INFO
115 }
116
117 fn schema(&self) -> SchemaRef {
118 self.schema.clone()
119 }
120
121 fn to_stream(&self, request: ScanRequest) -> Result<SendableRecordBatchStream> {
122 let schema = self.schema.arrow_schema().clone();
123 let mut builder = self.builder();
124 let stream = Box::pin(DfRecordBatchStreamAdapter::new(
125 schema,
126 futures::stream::once(async move {
127 builder
128 .make_procedure_info(Some(request))
129 .await
130 .map(|x| x.into_df_record_batch())
131 .map_err(Into::into)
132 }),
133 ));
134 Ok(Box::pin(
135 RecordBatchStreamAdapter::try_new(stream)
136 .map_err(BoxedError::new)
137 .context(InternalSnafu)?,
138 ))
139 }
140}
141
142struct InformationSchemaProcedureInfoBuilder {
143 schema: SchemaRef,
144 catalog_manager: Weak<dyn CatalogManager>,
145
146 procedure_ids: StringVectorBuilder,
147 procedure_types: StringVectorBuilder,
148 start_times: TimestampMillisecondVectorBuilder,
149 end_times: TimestampMillisecondVectorBuilder,
150 statuses: StringVectorBuilder,
151 lock_keys: StringVectorBuilder,
152}
153
154impl InformationSchemaProcedureInfoBuilder {
155 fn new(schema: SchemaRef, catalog_manager: Weak<dyn CatalogManager>) -> Self {
156 Self {
157 schema,
158 catalog_manager,
159 procedure_ids: StringVectorBuilder::with_capacity(INIT_CAPACITY),
160 procedure_types: StringVectorBuilder::with_capacity(INIT_CAPACITY),
161 start_times: TimestampMillisecondVectorBuilder::with_capacity(INIT_CAPACITY),
162 end_times: TimestampMillisecondVectorBuilder::with_capacity(INIT_CAPACITY),
163 statuses: StringVectorBuilder::with_capacity(INIT_CAPACITY),
164 lock_keys: StringVectorBuilder::with_capacity(INIT_CAPACITY),
165 }
166 }
167
168 async fn make_procedure_info(&mut self, request: Option<ScanRequest>) -> Result<RecordBatch> {
170 let predicates = Predicates::from_scan_request(&request);
171 let information_extension = utils::information_extension(&self.catalog_manager)?;
172 let procedures = information_extension.procedures().await?;
173 for (status, procedure_info) in procedures {
174 self.add_procedure(&predicates, status, procedure_info);
175 }
176 self.finish()
177 }
178
179 fn add_procedure(
180 &mut self,
181 predicates: &Predicates,
182 status: String,
183 procedure_info: ProcedureInfo,
184 ) {
185 let ProcedureInfo {
186 id,
187 type_name,
188 start_time_ms,
189 end_time_ms,
190 lock_keys,
191 ..
192 } = procedure_info;
193 let pid = id.to_string();
194 let start_time = TimestampMillisecond(Timestamp::new_millisecond(start_time_ms));
195 let end_time = TimestampMillisecond(Timestamp::new_millisecond(end_time_ms));
196 let lock_keys = lock_keys.join(",");
197
198 let row = [
199 (PROCEDURE_ID, &Value::from(pid.clone())),
200 (PROCEDURE_TYPE, &Value::from(type_name.clone())),
201 (START_TIME, &Value::from(start_time)),
202 (END_TIME, &Value::from(end_time)),
203 (STATUS, &Value::from(status.clone())),
204 (LOCK_KEYS, &Value::from(lock_keys.clone())),
205 ];
206 if !predicates.eval(&row) {
207 return;
208 }
209 self.procedure_ids.push(Some(&pid));
210 self.procedure_types.push(Some(&type_name));
211 self.start_times.push(Some(start_time));
212 self.end_times.push(Some(end_time));
213 self.statuses.push(Some(&status));
214 self.lock_keys.push(Some(&lock_keys));
215 }
216
217 fn finish(&mut self) -> Result<RecordBatch> {
218 let columns: Vec<VectorRef> = vec![
219 Arc::new(self.procedure_ids.finish()),
220 Arc::new(self.procedure_types.finish()),
221 Arc::new(self.start_times.finish()),
222 Arc::new(self.end_times.finish()),
223 Arc::new(self.statuses.finish()),
224 Arc::new(self.lock_keys.finish()),
225 ];
226 RecordBatch::new(self.schema.clone(), columns).context(CreateRecordBatchSnafu)
227 }
228}
229
230impl DfPartitionStream for InformationSchemaProcedureInfo {
231 fn schema(&self) -> &ArrowSchemaRef {
232 self.schema.arrow_schema()
233 }
234
235 fn execute(&self, _: Arc<TaskContext>) -> DfSendableRecordBatchStream {
236 let schema = self.schema.arrow_schema().clone();
237 let mut builder = self.builder();
238 Box::pin(DfRecordBatchStreamAdapter::new(
239 schema,
240 futures::stream::once(async move {
241 builder
242 .make_procedure_info(None)
243 .await
244 .map(|x| x.into_df_record_batch())
245 .map_err(Into::into)
246 }),
247 ))
248 }
249}