1#[cfg(feature = "enterprise")]
16pub mod trigger;
17
18use std::collections::{HashMap, HashSet};
19use std::result;
20use std::time::Duration;
21
22use api::helper::{from_pb_time_ranges, to_pb_time_ranges};
23use api::v1::alter_database_expr::Kind as PbAlterDatabaseKind;
24use api::v1::meta::ddl_task_request::Task;
25use api::v1::meta::{
26 AlterDatabaseTask as PbAlterDatabaseTask, AlterTableTask as PbAlterTableTask,
27 AlterTableTasks as PbAlterTableTasks, CommentOnTask as PbCommentOnTask,
28 CreateDatabaseTask as PbCreateDatabaseTask, CreateFlowTask as PbCreateFlowTask,
29 CreateTableTask as PbCreateTableTask, CreateTableTasks as PbCreateTableTasks,
30 CreateViewTask as PbCreateViewTask, DdlTaskRequest as PbDdlTaskRequest,
31 DdlTaskResponse as PbDdlTaskResponse, DropDatabaseTask as PbDropDatabaseTask,
32 DropFlowTask as PbDropFlowTask, DropTableTask as PbDropTableTask,
33 DropTableTasks as PbDropTableTasks, DropViewTask as PbDropViewTask, Partition, ProcedureId,
34 PurgeDroppedTableTask as PbPurgeDroppedTableTask, TruncateTableTask as PbTruncateTableTask,
35 UndropTableTask as PbUndropTableTask,
36};
37use api::v1::{
38 AlterDatabaseExpr, AlterTableExpr, CommentObjectType as PbCommentObjectType, CommentOnExpr,
39 CreateDatabaseExpr, CreateFlowExpr, CreateTableExpr, CreateViewExpr, DropDatabaseExpr,
40 DropFlowExpr, DropTableExpr, DropViewExpr, EvalInterval, ExpireAfter, Option as PbOption,
41 QueryContext as PbQueryContext, TruncateTableExpr,
42};
43use base64::Engine as _;
44use base64::engine::general_purpose;
45use common_base::protocol::Channel;
46use common_catalog::{format_full_flow_name, format_full_table_name};
47use common_error::ext::BoxedError;
48pub use common_event_recorder::TriggerReason;
49use common_time::{DatabaseTimeToLive, Timestamp};
50use prost::Message;
51use serde::{Deserialize, Serialize};
52use serde_with::{DefaultOnNull, serde_as};
53use snafu::{OptionExt, ResultExt};
54use table::metadata::{TableId, TableInfo};
55use table::requests::validate_database_option;
56use table::table_name::TableName;
57use table::table_reference::TableReference;
58
59use crate::error::{
60 self, ConvertTimeRangesSnafu, ExternalSnafu, InvalidSetDatabaseOptionSnafu,
61 InvalidUnsetDatabaseOptionSnafu, Result,
62};
63use crate::flow_name::FlowName;
64use crate::instruction::CacheIdent;
65use crate::key::FlowId;
66use crate::key::flow::flow_name::FlowNameManager;
67use crate::key::table_name::{TableNameKey, TableNameManager};
68
69pub const ORIGIN_FRONTEND_ADDR_EXTENSION_KEY: &str = "__greptime_origin_frontend.addr";
71pub const CREATE_DATABASE_CREATOR_EXTENSION_KEY: &str = "__greptime_create_database.creator";
73pub const CREATE_DATABASE_CREATOR_METADATA_KEY: &str =
75 "x-greptime-internal-create-database-creator-bin";
76
77#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
78pub struct CreatorGrantIntent {
79 pub username: String,
80 pub created_at_ns: i64,
81}
82
83#[derive(Debug, Clone)]
85pub enum DdlTask {
86 CreateTable(CreateTableTask),
87 DropTable(DropTableTask),
88 UndropTable(UndropTableTask),
89 PurgeDroppedTable(PurgeDroppedTableTask),
90 AlterTable(AlterTableTask),
91 TruncateTable(TruncateTableTask),
92 CreateLogicalTables(Vec<CreateTableTask>),
93 DropLogicalTables(Vec<DropTableTask>),
94 AlterLogicalTables(Vec<AlterTableTask>),
95 CreateDatabase(CreateDatabaseTask),
96 DropDatabase(DropDatabaseTask),
97 AlterDatabase(AlterDatabaseTask),
98 CreateFlow(CreateFlowTask),
99 DropFlow(DropFlowTask),
100 #[cfg(feature = "enterprise")]
101 DropTrigger(trigger::DropTriggerTask),
102 CreateView(CreateViewTask),
103 DropView(DropViewTask),
104 #[cfg(feature = "enterprise")]
105 CreateTrigger(trigger::CreateTriggerTask),
106 CommentOn(CommentOnTask),
107}
108
109impl DdlTask {
110 pub fn new_create_flow(expr: CreateFlowTask) -> Self {
112 DdlTask::CreateFlow(expr)
113 }
114
115 pub fn new_drop_flow(expr: DropFlowTask) -> Self {
117 DdlTask::DropFlow(expr)
118 }
119
120 pub fn new_drop_view(expr: DropViewTask) -> Self {
122 DdlTask::DropView(expr)
123 }
124
125 pub fn new_create_table(
127 expr: CreateTableExpr,
128 partitions: Vec<Partition>,
129 table_info: TableInfo,
130 ) -> Self {
131 DdlTask::CreateTable(CreateTableTask::new(expr, partitions, table_info))
132 }
133
134 pub fn new_create_logical_tables(table_data: Vec<(CreateTableExpr, TableInfo)>) -> Self {
136 DdlTask::CreateLogicalTables(
137 table_data
138 .into_iter()
139 .map(|(expr, table_info)| CreateTableTask::new(expr, Vec::new(), table_info))
140 .collect(),
141 )
142 }
143
144 pub fn new_alter_logical_tables(table_data: Vec<AlterTableExpr>) -> Self {
146 DdlTask::AlterLogicalTables(
147 table_data
148 .into_iter()
149 .map(|alter_table| AlterTableTask { alter_table })
150 .collect(),
151 )
152 }
153
154 pub fn new_drop_table(
156 catalog: String,
157 schema: String,
158 table: String,
159 table_id: TableId,
160 drop_if_exists: bool,
161 ) -> Self {
162 DdlTask::DropTable(DropTableTask {
163 catalog,
164 schema,
165 table,
166 table_id,
167 drop_if_exists,
168 })
169 }
170
171 pub fn new_undrop_table(table_id: TableId) -> Self {
173 DdlTask::UndropTable(UndropTableTask { table_id })
174 }
175
176 pub fn new_purge_dropped_table(table_id: TableId) -> Self {
178 DdlTask::PurgeDroppedTable(PurgeDroppedTableTask { table_id })
179 }
180
181 pub fn new_create_database(
183 catalog: String,
184 schema: String,
185 create_if_not_exists: bool,
186 options: HashMap<String, String>,
187 creator: Option<CreatorGrantIntent>,
188 ) -> Self {
189 DdlTask::CreateDatabase(CreateDatabaseTask {
190 catalog,
191 schema,
192 create_if_not_exists,
193 options,
194 creator,
195 })
196 }
197
198 pub fn new_drop_database(catalog: String, schema: String, drop_if_exists: bool) -> Self {
200 DdlTask::DropDatabase(DropDatabaseTask {
201 catalog,
202 schema,
203 drop_if_exists,
204 })
205 }
206
207 pub fn new_alter_database(alter_expr: AlterDatabaseExpr) -> Self {
209 DdlTask::AlterDatabase(AlterDatabaseTask { alter_expr })
210 }
211
212 pub fn new_alter_table(alter_table: AlterTableExpr) -> Self {
214 DdlTask::AlterTable(AlterTableTask { alter_table })
215 }
216
217 pub fn new_truncate_table(
219 catalog: String,
220 schema: String,
221 table: String,
222 table_id: TableId,
223 time_ranges: Vec<(Timestamp, Timestamp)>,
224 ) -> Self {
225 DdlTask::TruncateTable(TruncateTableTask {
226 catalog,
227 schema,
228 table,
229 table_id,
230 time_ranges,
231 })
232 }
233
234 pub fn new_create_view(create_view: CreateViewExpr, view_info: TableInfo) -> Self {
236 DdlTask::CreateView(CreateViewTask {
237 create_view,
238 view_info,
239 })
240 }
241
242 pub fn new_comment_on(task: CommentOnTask) -> Self {
244 DdlTask::CommentOn(task)
245 }
246}
247
248impl TryFrom<Task> for DdlTask {
249 type Error = error::Error;
250 fn try_from(task: Task) -> Result<Self> {
251 match task {
252 Task::CreateTableTask(create_table) => {
253 Ok(DdlTask::CreateTable(create_table.try_into()?))
254 }
255 Task::DropTableTask(drop_table) => Ok(DdlTask::DropTable(drop_table.try_into()?)),
256 Task::UndropTableTask(undrop_table) => {
257 Ok(DdlTask::UndropTable(undrop_table.try_into()?))
258 }
259 Task::PurgeDroppedTableTask(purge_dropped_table) => {
260 Ok(DdlTask::PurgeDroppedTable(purge_dropped_table.try_into()?))
261 }
262 Task::AlterTableTask(alter_table) => Ok(DdlTask::AlterTable(alter_table.try_into()?)),
263 Task::TruncateTableTask(truncate_table) => {
264 Ok(DdlTask::TruncateTable(truncate_table.try_into()?))
265 }
266 Task::CreateTableTasks(create_tables) => {
267 let tasks = create_tables
268 .tasks
269 .into_iter()
270 .map(|task| task.try_into())
271 .collect::<Result<Vec<_>>>()?;
272
273 Ok(DdlTask::CreateLogicalTables(tasks))
274 }
275 Task::DropTableTasks(drop_tables) => {
276 let tasks = drop_tables
277 .tasks
278 .into_iter()
279 .map(|task| task.try_into())
280 .collect::<Result<Vec<_>>>()?;
281
282 Ok(DdlTask::DropLogicalTables(tasks))
283 }
284 Task::AlterTableTasks(alter_tables) => {
285 let tasks = alter_tables
286 .tasks
287 .into_iter()
288 .map(|task| task.try_into())
289 .collect::<Result<Vec<_>>>()?;
290
291 Ok(DdlTask::AlterLogicalTables(tasks))
292 }
293 Task::CreateDatabaseTask(create_database) => {
294 Ok(DdlTask::CreateDatabase(create_database.try_into()?))
295 }
296 Task::DropDatabaseTask(drop_database) => {
297 Ok(DdlTask::DropDatabase(drop_database.try_into()?))
298 }
299 Task::AlterDatabaseTask(alter_database) => {
300 Ok(DdlTask::AlterDatabase(alter_database.try_into()?))
301 }
302 Task::CreateFlowTask(create_flow) => Ok(DdlTask::CreateFlow(create_flow.try_into()?)),
303 Task::DropFlowTask(drop_flow) => Ok(DdlTask::DropFlow(drop_flow.try_into()?)),
304 Task::CreateViewTask(create_view) => Ok(DdlTask::CreateView(create_view.try_into()?)),
305 Task::DropViewTask(drop_view) => Ok(DdlTask::DropView(drop_view.try_into()?)),
306 Task::CreateTriggerTask(create_trigger) => {
307 #[cfg(feature = "enterprise")]
308 return Ok(DdlTask::CreateTrigger(create_trigger.try_into()?));
309 #[cfg(not(feature = "enterprise"))]
310 {
311 let _ = create_trigger;
312 crate::error::UnsupportedSnafu {
313 operation: "create trigger",
314 }
315 .fail()
316 }
317 }
318 Task::DropTriggerTask(drop_trigger) => {
319 #[cfg(feature = "enterprise")]
320 return Ok(DdlTask::DropTrigger(drop_trigger.try_into()?));
321 #[cfg(not(feature = "enterprise"))]
322 {
323 let _ = drop_trigger;
324 crate::error::UnsupportedSnafu {
325 operation: "drop trigger",
326 }
327 .fail()
328 }
329 }
330 Task::CommentOnTask(comment_on) => Ok(DdlTask::CommentOn(comment_on.try_into()?)),
331 }
332 }
333}
334
335#[derive(Clone)]
336pub struct SubmitDdlTaskRequest {
337 pub wait: bool,
338 pub timeout: Duration,
339 pub task: DdlTask,
340}
341
342impl SubmitDdlTaskRequest {
343 pub fn new(task: DdlTask) -> Self {
345 Self {
346 wait: Self::default_wait(),
347 timeout: Self::default_timeout(),
348 task,
349 }
350 }
351
352 pub fn default_timeout() -> Duration {
354 Duration::from_secs(60)
355 }
356
357 pub fn default_wait() -> bool {
359 true
360 }
361}
362
363fn ddl_timeout_secs(timeout: Duration) -> u32 {
364 timeout
365 .as_nanos()
366 .div_ceil(Duration::from_secs(1).as_nanos())
367 .try_into()
368 .unwrap_or(u32::MAX)
369}
370
371impl TryFrom<SubmitDdlTaskRequest> for PbDdlTaskRequest {
372 type Error = error::Error;
373
374 fn try_from(request: SubmitDdlTaskRequest) -> Result<Self> {
375 let SubmitDdlTaskRequest {
376 wait,
377 timeout,
378 task,
379 } = request;
380
381 let task = match task {
382 DdlTask::CreateTable(task) => Task::CreateTableTask(task.try_into()?),
383 DdlTask::DropTable(task) => Task::DropTableTask(task.into()),
384 DdlTask::UndropTable(task) => Task::UndropTableTask(task.into()),
385 DdlTask::PurgeDroppedTable(task) => Task::PurgeDroppedTableTask(task.into()),
386 DdlTask::AlterTable(task) => Task::AlterTableTask(task.try_into()?),
387 DdlTask::TruncateTable(task) => Task::TruncateTableTask(task.try_into()?),
388 DdlTask::CreateLogicalTables(tasks) => {
389 let tasks = tasks
390 .into_iter()
391 .map(|task| task.try_into())
392 .collect::<Result<Vec<_>>>()?;
393
394 Task::CreateTableTasks(PbCreateTableTasks { tasks })
395 }
396 DdlTask::DropLogicalTables(tasks) => {
397 let tasks = tasks
398 .into_iter()
399 .map(|task| task.into())
400 .collect::<Vec<_>>();
401
402 Task::DropTableTasks(PbDropTableTasks { tasks })
403 }
404 DdlTask::AlterLogicalTables(tasks) => {
405 let tasks = tasks
406 .into_iter()
407 .map(|task| task.try_into())
408 .collect::<Result<Vec<_>>>()?;
409
410 Task::AlterTableTasks(PbAlterTableTasks { tasks })
411 }
412 DdlTask::CreateDatabase(task) => Task::CreateDatabaseTask(task.try_into()?),
413 DdlTask::DropDatabase(task) => Task::DropDatabaseTask(task.try_into()?),
414 DdlTask::AlterDatabase(task) => Task::AlterDatabaseTask(task.try_into()?),
415 DdlTask::CreateFlow(task) => Task::CreateFlowTask(task.into()),
416 DdlTask::DropFlow(task) => Task::DropFlowTask(task.into()),
417 DdlTask::CreateView(task) => Task::CreateViewTask(task.try_into()?),
418 DdlTask::DropView(task) => Task::DropViewTask(task.into()),
419 #[cfg(feature = "enterprise")]
420 DdlTask::CreateTrigger(task) => Task::CreateTriggerTask(task.try_into()?),
421 #[cfg(feature = "enterprise")]
422 DdlTask::DropTrigger(task) => Task::DropTriggerTask(task.into()),
423 DdlTask::CommentOn(task) => Task::CommentOnTask(task.into()),
424 };
425
426 Ok(Self {
427 header: None,
428 query_context: None,
429 timeout_secs: ddl_timeout_secs(timeout),
430 wait,
431 task: Some(task),
432 event_context: None,
433 actor: None,
434 })
435 }
436}
437
438#[derive(Debug, Default)]
439pub struct SubmitDdlTaskResponse {
440 pub key: Vec<u8>,
441 pub table_ids: Vec<TableId>,
443}
444
445impl TryFrom<PbDdlTaskResponse> for SubmitDdlTaskResponse {
446 type Error = error::Error;
447
448 fn try_from(resp: PbDdlTaskResponse) -> Result<Self> {
449 let table_ids = resp.table_ids.into_iter().map(|t| t.id).collect();
450 Ok(Self {
451 key: resp.pid.map(|pid| pid.key).unwrap_or_default(),
452 table_ids,
453 })
454 }
455}
456
457impl From<SubmitDdlTaskResponse> for PbDdlTaskResponse {
458 fn from(val: SubmitDdlTaskResponse) -> Self {
459 Self {
460 pid: Some(ProcedureId { key: val.key }),
461 table_ids: val
462 .table_ids
463 .into_iter()
464 .map(|id| api::v1::TableId { id })
465 .collect(),
466 ..Default::default()
467 }
468 }
469}
470
471#[derive(Debug, PartialEq, Clone)]
473pub struct CreateViewTask {
474 pub create_view: CreateViewExpr,
475 pub view_info: TableInfo,
476}
477
478impl CreateViewTask {
479 pub fn table_ref(&self) -> TableReference<'_> {
481 TableReference {
482 catalog: &self.create_view.catalog_name,
483 schema: &self.create_view.schema_name,
484 table: &self.create_view.view_name,
485 }
486 }
487
488 pub fn raw_logical_plan(&self) -> &Vec<u8> {
490 &self.create_view.logical_plan
491 }
492
493 pub fn view_definition(&self) -> &str {
495 &self.create_view.definition
496 }
497
498 pub fn table_names(&self) -> HashSet<TableName> {
500 self.create_view
501 .table_names
502 .iter()
503 .map(|t| t.clone().into())
504 .collect()
505 }
506
507 pub fn columns(&self) -> &Vec<String> {
509 &self.create_view.columns
510 }
511
512 pub fn plan_columns(&self) -> &Vec<String> {
514 &self.create_view.plan_columns
515 }
516}
517
518impl TryFrom<PbCreateViewTask> for CreateViewTask {
519 type Error = error::Error;
520
521 fn try_from(pb: PbCreateViewTask) -> Result<Self> {
522 let view_info = serde_json::from_slice(&pb.view_info).context(error::SerdeJsonSnafu)?;
523
524 Ok(CreateViewTask {
525 create_view: pb.create_view.context(error::InvalidProtoMsgSnafu {
526 err_msg: "expected create view",
527 })?,
528 view_info,
529 })
530 }
531}
532
533impl TryFrom<CreateViewTask> for PbCreateViewTask {
534 type Error = error::Error;
535
536 fn try_from(task: CreateViewTask) -> Result<PbCreateViewTask> {
537 Ok(PbCreateViewTask {
538 create_view: Some(task.create_view),
539 view_info: serde_json::to_vec(&task.view_info).context(error::SerdeJsonSnafu)?,
540 })
541 }
542}
543
544impl Serialize for CreateViewTask {
545 fn serialize<S>(&self, serializer: S) -> result::Result<S::Ok, S::Error>
546 where
547 S: serde::Serializer,
548 {
549 let view_info = serde_json::to_vec(&self.view_info)
550 .map_err(|err| serde::ser::Error::custom(err.to_string()))?;
551
552 let pb = PbCreateViewTask {
553 create_view: Some(self.create_view.clone()),
554 view_info,
555 };
556 let buf = pb.encode_to_vec();
557 let encoded = general_purpose::STANDARD_NO_PAD.encode(buf);
558 serializer.serialize_str(&encoded)
559 }
560}
561
562impl<'de> Deserialize<'de> for CreateViewTask {
563 fn deserialize<D>(deserializer: D) -> result::Result<Self, D::Error>
564 where
565 D: serde::Deserializer<'de>,
566 {
567 let encoded = String::deserialize(deserializer)?;
568 let buf = general_purpose::STANDARD_NO_PAD
569 .decode(encoded)
570 .map_err(|err| serde::de::Error::custom(err.to_string()))?;
571 let expr: PbCreateViewTask = PbCreateViewTask::decode(&*buf)
572 .map_err(|err| serde::de::Error::custom(err.to_string()))?;
573
574 let expr = CreateViewTask::try_from(expr)
575 .map_err(|err| serde::de::Error::custom(err.to_string()))?;
576
577 Ok(expr)
578 }
579}
580
581#[derive(Debug, PartialEq, Clone, Serialize, Deserialize)]
583pub struct DropViewTask {
584 pub catalog: String,
585 pub schema: String,
586 pub view: String,
587 pub view_id: TableId,
588 pub drop_if_exists: bool,
589}
590
591impl DropViewTask {
592 pub fn table_ref(&self) -> TableReference<'_> {
594 TableReference {
595 catalog: &self.catalog,
596 schema: &self.schema,
597 table: &self.view,
598 }
599 }
600}
601
602impl TryFrom<PbDropViewTask> for DropViewTask {
603 type Error = error::Error;
604
605 fn try_from(pb: PbDropViewTask) -> Result<Self> {
606 let expr = pb.drop_view.context(error::InvalidProtoMsgSnafu {
607 err_msg: "expected drop view",
608 })?;
609
610 Ok(DropViewTask {
611 catalog: expr.catalog_name,
612 schema: expr.schema_name,
613 view: expr.view_name,
614 view_id: expr
615 .view_id
616 .context(error::InvalidProtoMsgSnafu {
617 err_msg: "expected view_id",
618 })?
619 .id,
620 drop_if_exists: expr.drop_if_exists,
621 })
622 }
623}
624
625impl From<DropViewTask> for PbDropViewTask {
626 fn from(task: DropViewTask) -> Self {
627 PbDropViewTask {
628 drop_view: Some(DropViewExpr {
629 catalog_name: task.catalog,
630 schema_name: task.schema,
631 view_name: task.view,
632 view_id: Some(api::v1::TableId { id: task.view_id }),
633 drop_if_exists: task.drop_if_exists,
634 }),
635 }
636 }
637}
638
639#[derive(Debug, PartialEq, Serialize, Deserialize, Clone)]
640pub struct DropTableTask {
641 pub catalog: String,
642 pub schema: String,
643 pub table: String,
644 pub table_id: TableId,
645 #[serde(default)]
646 pub drop_if_exists: bool,
647}
648
649#[derive(Debug, PartialEq, Serialize, Deserialize, Clone)]
650pub struct UndropTableTask {
651 pub table_id: TableId,
652}
653
654#[derive(Debug, PartialEq, Serialize, Deserialize, Clone)]
655pub struct PurgeDroppedTableTask {
656 pub table_id: TableId,
657}
658
659impl TryFrom<PbUndropTableTask> for UndropTableTask {
660 type Error = error::Error;
661
662 fn try_from(pb: PbUndropTableTask) -> Result<Self> {
663 Ok(Self {
664 table_id: pb
665 .table_id
666 .context(error::InvalidProtoMsgSnafu {
667 err_msg: "expected table_id",
668 })?
669 .id,
670 })
671 }
672}
673
674impl From<UndropTableTask> for PbUndropTableTask {
675 fn from(task: UndropTableTask) -> Self {
676 Self {
677 table_id: Some(api::v1::TableId { id: task.table_id }),
678 }
679 }
680}
681
682impl TryFrom<PbPurgeDroppedTableTask> for PurgeDroppedTableTask {
683 type Error = error::Error;
684
685 fn try_from(pb: PbPurgeDroppedTableTask) -> Result<Self> {
686 Ok(Self {
687 table_id: pb
688 .table_id
689 .context(error::InvalidProtoMsgSnafu {
690 err_msg: "expected table_id",
691 })?
692 .id,
693 })
694 }
695}
696
697impl From<PurgeDroppedTableTask> for PbPurgeDroppedTableTask {
698 fn from(task: PurgeDroppedTableTask) -> Self {
699 Self {
700 table_id: Some(api::v1::TableId { id: task.table_id }),
701 }
702 }
703}
704
705impl DropTableTask {
706 pub fn table_ref(&self) -> TableReference<'_> {
707 TableReference {
708 catalog: &self.catalog,
709 schema: &self.schema,
710 table: &self.table,
711 }
712 }
713
714 pub fn table_name(&self) -> TableName {
715 TableName {
716 catalog_name: self.catalog.clone(),
717 schema_name: self.schema.clone(),
718 table_name: self.table.clone(),
719 }
720 }
721}
722
723impl TryFrom<PbDropTableTask> for DropTableTask {
724 type Error = error::Error;
725
726 fn try_from(pb: PbDropTableTask) -> Result<Self> {
727 let drop_table = pb.drop_table.context(error::InvalidProtoMsgSnafu {
728 err_msg: "expected drop table",
729 })?;
730
731 Ok(Self {
732 catalog: drop_table.catalog_name,
733 schema: drop_table.schema_name,
734 table: drop_table.table_name,
735 table_id: drop_table
736 .table_id
737 .context(error::InvalidProtoMsgSnafu {
738 err_msg: "expected table_id",
739 })?
740 .id,
741 drop_if_exists: drop_table.drop_if_exists,
742 })
743 }
744}
745
746impl From<DropTableTask> for PbDropTableTask {
747 fn from(task: DropTableTask) -> Self {
748 PbDropTableTask {
749 drop_table: Some(DropTableExpr {
750 catalog_name: task.catalog,
751 schema_name: task.schema,
752 table_name: task.table,
753 table_id: Some(api::v1::TableId { id: task.table_id }),
754 drop_if_exists: task.drop_if_exists,
755 }),
756 }
757 }
758}
759
760#[derive(Debug, PartialEq, Clone)]
761pub struct CreateTableTask {
762 pub create_table: CreateTableExpr,
763 pub partitions: Vec<Partition>,
764 pub table_info: TableInfo,
765}
766
767impl TryFrom<PbCreateTableTask> for CreateTableTask {
768 type Error = error::Error;
769
770 fn try_from(pb: PbCreateTableTask) -> Result<Self> {
771 let table_info = serde_json::from_slice(&pb.table_info).context(error::SerdeJsonSnafu)?;
772
773 Ok(CreateTableTask::new(
774 pb.create_table.context(error::InvalidProtoMsgSnafu {
775 err_msg: "expected create table",
776 })?,
777 pb.partitions,
778 table_info,
779 ))
780 }
781}
782
783impl TryFrom<CreateTableTask> for PbCreateTableTask {
784 type Error = error::Error;
785
786 fn try_from(task: CreateTableTask) -> Result<Self> {
787 Ok(PbCreateTableTask {
788 table_info: serde_json::to_vec(&task.table_info).context(error::SerdeJsonSnafu)?,
789 create_table: Some(task.create_table),
790 partitions: task.partitions,
791 })
792 }
793}
794
795impl CreateTableTask {
796 pub fn new(
797 expr: CreateTableExpr,
798 partitions: Vec<Partition>,
799 table_info: TableInfo,
800 ) -> CreateTableTask {
801 CreateTableTask {
802 create_table: expr,
803 partitions,
804 table_info,
805 }
806 }
807
808 pub fn table_name(&self) -> TableName {
809 let table = &self.create_table;
810
811 TableName {
812 catalog_name: table.catalog_name.clone(),
813 schema_name: table.schema_name.clone(),
814 table_name: table.table_name.clone(),
815 }
816 }
817
818 pub fn table_ref(&self) -> TableReference<'_> {
819 let table = &self.create_table;
820
821 TableReference {
822 catalog: &table.catalog_name,
823 schema: &table.schema_name,
824 table: &table.table_name,
825 }
826 }
827
828 pub fn set_table_id(&mut self, table_id: TableId) {
830 self.table_info.ident.table_id = table_id;
831 }
832
833 pub fn sort_columns(&mut self) {
838 self.create_table
841 .column_defs
842 .sort_unstable_by(|a, b| a.name.cmp(&b.name));
843
844 self.table_info.sort_columns();
845 }
846}
847
848impl Serialize for CreateTableTask {
849 fn serialize<S>(&self, serializer: S) -> result::Result<S::Ok, S::Error>
850 where
851 S: serde::Serializer,
852 {
853 let table_info = serde_json::to_vec(&self.table_info)
854 .map_err(|err| serde::ser::Error::custom(err.to_string()))?;
855
856 let pb = PbCreateTableTask {
857 create_table: Some(self.create_table.clone()),
858 partitions: self.partitions.clone(),
859 table_info,
860 };
861 let buf = pb.encode_to_vec();
862 let encoded = general_purpose::STANDARD_NO_PAD.encode(buf);
863 serializer.serialize_str(&encoded)
864 }
865}
866
867impl<'de> Deserialize<'de> for CreateTableTask {
868 fn deserialize<D>(deserializer: D) -> result::Result<Self, D::Error>
869 where
870 D: serde::Deserializer<'de>,
871 {
872 let encoded = String::deserialize(deserializer)?;
873 let buf = general_purpose::STANDARD_NO_PAD
874 .decode(encoded)
875 .map_err(|err| serde::de::Error::custom(err.to_string()))?;
876 let expr: PbCreateTableTask = PbCreateTableTask::decode(&*buf)
877 .map_err(|err| serde::de::Error::custom(err.to_string()))?;
878
879 let expr = CreateTableTask::try_from(expr)
880 .map_err(|err| serde::de::Error::custom(err.to_string()))?;
881
882 Ok(expr)
883 }
884}
885
886#[derive(Debug, PartialEq, Clone)]
887pub struct AlterTableTask {
888 pub alter_table: AlterTableExpr,
890}
891
892impl AlterTableTask {
893 pub fn validate(&self) -> Result<()> {
894 self.alter_table
895 .kind
896 .as_ref()
897 .context(error::UnexpectedSnafu {
898 err_msg: "'kind' is absent",
899 })?;
900 Ok(())
901 }
902
903 pub fn table_ref(&self) -> TableReference<'_> {
904 TableReference {
905 catalog: &self.alter_table.catalog_name,
906 schema: &self.alter_table.schema_name,
907 table: &self.alter_table.table_name,
908 }
909 }
910
911 pub fn table_name(&self) -> TableName {
912 let table = &self.alter_table;
913
914 TableName {
915 catalog_name: table.catalog_name.clone(),
916 schema_name: table.schema_name.clone(),
917 table_name: table.table_name.clone(),
918 }
919 }
920}
921
922impl TryFrom<PbAlterTableTask> for AlterTableTask {
923 type Error = error::Error;
924
925 fn try_from(pb: PbAlterTableTask) -> Result<Self> {
926 let alter_table = pb.alter_table.context(error::InvalidProtoMsgSnafu {
927 err_msg: "expected alter_table",
928 })?;
929
930 Ok(AlterTableTask { alter_table })
931 }
932}
933
934impl TryFrom<AlterTableTask> for PbAlterTableTask {
935 type Error = error::Error;
936
937 fn try_from(task: AlterTableTask) -> Result<Self> {
938 Ok(PbAlterTableTask {
939 alter_table: Some(task.alter_table),
940 })
941 }
942}
943
944impl Serialize for AlterTableTask {
945 fn serialize<S>(&self, serializer: S) -> result::Result<S::Ok, S::Error>
946 where
947 S: serde::Serializer,
948 {
949 let pb = PbAlterTableTask {
950 alter_table: Some(self.alter_table.clone()),
951 };
952 let buf = pb.encode_to_vec();
953 let encoded = general_purpose::STANDARD_NO_PAD.encode(buf);
954 serializer.serialize_str(&encoded)
955 }
956}
957
958impl<'de> Deserialize<'de> for AlterTableTask {
959 fn deserialize<D>(deserializer: D) -> result::Result<Self, D::Error>
960 where
961 D: serde::Deserializer<'de>,
962 {
963 let encoded = String::deserialize(deserializer)?;
964 let buf = general_purpose::STANDARD_NO_PAD
965 .decode(encoded)
966 .map_err(|err| serde::de::Error::custom(err.to_string()))?;
967 let expr: PbAlterTableTask = PbAlterTableTask::decode(&*buf)
968 .map_err(|err| serde::de::Error::custom(err.to_string()))?;
969
970 let expr = AlterTableTask::try_from(expr)
971 .map_err(|err| serde::de::Error::custom(err.to_string()))?;
972
973 Ok(expr)
974 }
975}
976
977#[derive(Debug, PartialEq, Serialize, Deserialize, Clone)]
978pub struct TruncateTableTask {
979 pub catalog: String,
980 pub schema: String,
981 pub table: String,
982 pub table_id: TableId,
983 pub time_ranges: Vec<(Timestamp, Timestamp)>,
984}
985
986impl TruncateTableTask {
987 pub fn table_ref(&self) -> TableReference<'_> {
988 TableReference {
989 catalog: &self.catalog,
990 schema: &self.schema,
991 table: &self.table,
992 }
993 }
994
995 pub fn table_name(&self) -> TableName {
996 TableName {
997 catalog_name: self.catalog.clone(),
998 schema_name: self.schema.clone(),
999 table_name: self.table.clone(),
1000 }
1001 }
1002}
1003
1004impl TryFrom<PbTruncateTableTask> for TruncateTableTask {
1005 type Error = error::Error;
1006
1007 fn try_from(pb: PbTruncateTableTask) -> Result<Self> {
1008 let truncate_table = pb.truncate_table.context(error::InvalidProtoMsgSnafu {
1009 err_msg: "expected truncate table",
1010 })?;
1011
1012 Ok(Self {
1013 catalog: truncate_table.catalog_name,
1014 schema: truncate_table.schema_name,
1015 table: truncate_table.table_name,
1016 table_id: truncate_table
1017 .table_id
1018 .context(error::InvalidProtoMsgSnafu {
1019 err_msg: "expected table_id",
1020 })?
1021 .id,
1022 time_ranges: truncate_table
1023 .time_ranges
1024 .map(from_pb_time_ranges)
1025 .transpose()
1026 .map_err(BoxedError::new)
1027 .context(ExternalSnafu)?
1028 .unwrap_or_default(),
1029 })
1030 }
1031}
1032
1033impl TryFrom<TruncateTableTask> for PbTruncateTableTask {
1034 type Error = error::Error;
1035
1036 fn try_from(task: TruncateTableTask) -> Result<Self> {
1037 Ok(PbTruncateTableTask {
1038 truncate_table: Some(TruncateTableExpr {
1039 catalog_name: task.catalog,
1040 schema_name: task.schema,
1041 table_name: task.table,
1042 table_id: Some(api::v1::TableId { id: task.table_id }),
1043 time_ranges: Some(
1044 to_pb_time_ranges(&task.time_ranges).context(ConvertTimeRangesSnafu)?,
1045 ),
1046 }),
1047 })
1048 }
1049}
1050
1051#[serde_as]
1052#[derive(Debug, PartialEq, Serialize, Deserialize, Clone)]
1053pub struct CreateDatabaseTask {
1054 pub catalog: String,
1055 pub schema: String,
1056 pub create_if_not_exists: bool,
1057 #[serde_as(deserialize_as = "DefaultOnNull")]
1058 pub options: HashMap<String, String>,
1059 #[serde(default)]
1060 pub creator: Option<CreatorGrantIntent>,
1061}
1062
1063impl TryFrom<PbCreateDatabaseTask> for CreateDatabaseTask {
1064 type Error = error::Error;
1065
1066 fn try_from(pb: PbCreateDatabaseTask) -> Result<Self> {
1067 let CreateDatabaseExpr {
1068 catalog_name,
1069 schema_name,
1070 create_if_not_exists,
1071 options,
1072 } = pb.create_database.context(error::InvalidProtoMsgSnafu {
1073 err_msg: "expected create database",
1074 })?;
1075
1076 Ok(CreateDatabaseTask {
1077 catalog: catalog_name,
1078 schema: schema_name,
1079 create_if_not_exists,
1080 options,
1081 creator: None,
1082 })
1083 }
1084}
1085
1086impl TryFrom<CreateDatabaseTask> for PbCreateDatabaseTask {
1087 type Error = error::Error;
1088
1089 fn try_from(
1090 CreateDatabaseTask {
1091 catalog,
1092 schema,
1093 create_if_not_exists,
1094 options,
1095 creator: _,
1096 }: CreateDatabaseTask,
1097 ) -> Result<Self> {
1098 Ok(PbCreateDatabaseTask {
1099 create_database: Some(CreateDatabaseExpr {
1100 catalog_name: catalog,
1101 schema_name: schema,
1102 create_if_not_exists,
1103 options,
1104 }),
1105 })
1106 }
1107}
1108
1109#[derive(Debug, PartialEq, Serialize, Deserialize, Clone)]
1110pub struct DropDatabaseTask {
1111 pub catalog: String,
1112 pub schema: String,
1113 pub drop_if_exists: bool,
1114}
1115
1116impl TryFrom<PbDropDatabaseTask> for DropDatabaseTask {
1117 type Error = error::Error;
1118
1119 fn try_from(pb: PbDropDatabaseTask) -> Result<Self> {
1120 let DropDatabaseExpr {
1121 catalog_name,
1122 schema_name,
1123 drop_if_exists,
1124 } = pb.drop_database.context(error::InvalidProtoMsgSnafu {
1125 err_msg: "expected drop database",
1126 })?;
1127
1128 Ok(DropDatabaseTask {
1129 catalog: catalog_name,
1130 schema: schema_name,
1131 drop_if_exists,
1132 })
1133 }
1134}
1135
1136impl TryFrom<DropDatabaseTask> for PbDropDatabaseTask {
1137 type Error = error::Error;
1138
1139 fn try_from(
1140 DropDatabaseTask {
1141 catalog,
1142 schema,
1143 drop_if_exists,
1144 }: DropDatabaseTask,
1145 ) -> Result<Self> {
1146 Ok(PbDropDatabaseTask {
1147 drop_database: Some(DropDatabaseExpr {
1148 catalog_name: catalog,
1149 schema_name: schema,
1150 drop_if_exists,
1151 }),
1152 })
1153 }
1154}
1155
1156#[derive(Debug, PartialEq, Clone)]
1157pub struct AlterDatabaseTask {
1158 pub alter_expr: AlterDatabaseExpr,
1159}
1160
1161impl TryFrom<AlterDatabaseTask> for PbAlterDatabaseTask {
1162 type Error = error::Error;
1163
1164 fn try_from(task: AlterDatabaseTask) -> Result<Self> {
1165 Ok(PbAlterDatabaseTask {
1166 task: Some(task.alter_expr),
1167 })
1168 }
1169}
1170
1171impl TryFrom<PbAlterDatabaseTask> for AlterDatabaseTask {
1172 type Error = error::Error;
1173
1174 fn try_from(pb: PbAlterDatabaseTask) -> Result<Self> {
1175 let alter_expr = pb.task.context(error::InvalidProtoMsgSnafu {
1176 err_msg: "expected alter database",
1177 })?;
1178
1179 Ok(AlterDatabaseTask { alter_expr })
1180 }
1181}
1182
1183impl TryFrom<PbAlterDatabaseKind> for AlterDatabaseKind {
1184 type Error = error::Error;
1185
1186 fn try_from(pb: PbAlterDatabaseKind) -> Result<Self> {
1187 match pb {
1188 PbAlterDatabaseKind::SetDatabaseOptions(options) => {
1189 Ok(AlterDatabaseKind::SetDatabaseOptions(SetDatabaseOptions(
1190 options
1191 .set_database_options
1192 .into_iter()
1193 .map(SetDatabaseOption::try_from)
1194 .collect::<Result<Vec<_>>>()?,
1195 )))
1196 }
1197 PbAlterDatabaseKind::UnsetDatabaseOptions(options) => Ok(
1198 AlterDatabaseKind::UnsetDatabaseOptions(UnsetDatabaseOptions(
1199 options
1200 .keys
1201 .iter()
1202 .map(|key| UnsetDatabaseOption::try_from(key.as_str()))
1203 .collect::<Result<Vec<_>>>()?,
1204 )),
1205 ),
1206 }
1207 }
1208}
1209
1210const TTL_KEY: &str = "ttl";
1211
1212impl TryFrom<PbOption> for SetDatabaseOption {
1213 type Error = error::Error;
1214
1215 fn try_from(PbOption { key, value }: PbOption) -> Result<Self> {
1216 let key_lower = key.to_ascii_lowercase();
1217 match key_lower.as_str() {
1218 TTL_KEY => {
1219 let ttl = DatabaseTimeToLive::from_humantime_or_str(&value)
1220 .map_err(|_| InvalidSetDatabaseOptionSnafu { key, value }.build())?;
1221
1222 Ok(SetDatabaseOption::Ttl(ttl))
1223 }
1224 _ => {
1225 if validate_database_option(&key_lower) {
1226 Ok(SetDatabaseOption::Other(key_lower, value))
1227 } else {
1228 InvalidSetDatabaseOptionSnafu { key, value }.fail()
1229 }
1230 }
1231 }
1232 }
1233}
1234
1235#[derive(Debug, PartialEq, Clone, Serialize, Deserialize)]
1236pub enum SetDatabaseOption {
1237 Ttl(DatabaseTimeToLive),
1238 Other(String, String),
1239}
1240
1241#[derive(Debug, PartialEq, Clone, Serialize, Deserialize)]
1242pub enum UnsetDatabaseOption {
1243 Ttl,
1244 Other(String),
1245}
1246
1247impl TryFrom<&str> for UnsetDatabaseOption {
1248 type Error = error::Error;
1249
1250 fn try_from(key: &str) -> Result<Self> {
1251 let key_lower = key.to_ascii_lowercase();
1252 match key_lower.as_str() {
1253 TTL_KEY => Ok(UnsetDatabaseOption::Ttl),
1254 _ => {
1255 if validate_database_option(&key_lower) {
1256 Ok(UnsetDatabaseOption::Other(key_lower))
1257 } else {
1258 InvalidUnsetDatabaseOptionSnafu { key }.fail()
1259 }
1260 }
1261 }
1262 }
1263}
1264
1265#[derive(Debug, PartialEq, Clone, Serialize, Deserialize)]
1266pub struct SetDatabaseOptions(pub Vec<SetDatabaseOption>);
1267
1268#[derive(Debug, PartialEq, Clone, Serialize, Deserialize)]
1269pub struct UnsetDatabaseOptions(pub Vec<UnsetDatabaseOption>);
1270
1271#[derive(Debug, PartialEq, Clone, Serialize, Deserialize)]
1272pub enum AlterDatabaseKind {
1273 SetDatabaseOptions(SetDatabaseOptions),
1274 UnsetDatabaseOptions(UnsetDatabaseOptions),
1275}
1276
1277impl AlterDatabaseTask {
1278 pub fn catalog(&self) -> &str {
1279 &self.alter_expr.catalog_name
1280 }
1281
1282 pub fn schema(&self) -> &str {
1283 &self.alter_expr.catalog_name
1284 }
1285}
1286
1287#[derive(Debug, Clone, Serialize, Deserialize)]
1289pub struct CreateFlowTask {
1290 pub catalog_name: String,
1291 pub flow_name: String,
1292 pub source_table_names: Vec<TableName>,
1293 pub sink_table_name: TableName,
1294 pub or_replace: bool,
1295 pub create_if_not_exists: bool,
1296 pub expire_after: Option<i64>,
1298 pub eval_interval_secs: Option<i64>,
1299 pub comment: String,
1300 pub sql: String,
1301 pub flow_options: HashMap<String, String>,
1302 #[serde(default)]
1306 pub eval_schedule: Option<crate::key::flow::flow_info::FlowScheduleConfig>,
1307}
1308
1309impl TryFrom<PbCreateFlowTask> for CreateFlowTask {
1310 type Error = error::Error;
1311
1312 fn try_from(pb: PbCreateFlowTask) -> Result<Self> {
1313 let CreateFlowExpr {
1314 catalog_name,
1315 flow_name,
1316 source_table_names,
1317 sink_table_name,
1318 or_replace,
1319 create_if_not_exists,
1320 expire_after,
1321 eval_interval,
1322 comment,
1323 sql,
1324 flow_options,
1325 } = pb.create_flow.context(error::InvalidProtoMsgSnafu {
1326 err_msg: "expected create_flow",
1327 })?;
1328
1329 Ok(CreateFlowTask {
1330 catalog_name,
1331 flow_name,
1332 source_table_names: source_table_names.into_iter().map(Into::into).collect(),
1333 sink_table_name: sink_table_name
1334 .context(error::InvalidProtoMsgSnafu {
1335 err_msg: "expected sink_table_name",
1336 })?
1337 .into(),
1338 or_replace,
1339 create_if_not_exists,
1340 expire_after: expire_after.map(|e| e.value),
1341 eval_interval_secs: eval_interval.map(|e| e.seconds),
1342 comment,
1343 sql,
1344 flow_options,
1345 eval_schedule: None,
1346 })
1347 }
1348}
1349
1350impl From<CreateFlowTask> for PbCreateFlowTask {
1351 fn from(
1352 CreateFlowTask {
1353 catalog_name,
1354 flow_name,
1355 source_table_names,
1356 sink_table_name,
1357 or_replace,
1358 create_if_not_exists,
1359 expire_after,
1360 eval_interval_secs: eval_interval,
1361 comment,
1362 sql,
1363 flow_options,
1364 ..
1365 }: CreateFlowTask,
1366 ) -> Self {
1367 PbCreateFlowTask {
1368 create_flow: Some(CreateFlowExpr {
1369 catalog_name,
1370 flow_name,
1371 source_table_names: source_table_names.into_iter().map(Into::into).collect(),
1372 sink_table_name: Some(sink_table_name.into()),
1373 or_replace,
1374 create_if_not_exists,
1375 expire_after: expire_after.map(|value| ExpireAfter { value }),
1376 eval_interval: eval_interval.map(|seconds| EvalInterval { seconds }),
1377 comment,
1378 sql,
1379 flow_options,
1380 }),
1381 }
1382 }
1383}
1384
1385#[derive(Debug, Clone, Serialize, Deserialize)]
1387pub struct DropFlowTask {
1388 pub catalog_name: String,
1389 pub flow_name: String,
1390 pub flow_id: FlowId,
1391 pub drop_if_exists: bool,
1392}
1393
1394impl TryFrom<PbDropFlowTask> for DropFlowTask {
1395 type Error = error::Error;
1396
1397 fn try_from(pb: PbDropFlowTask) -> Result<Self> {
1398 let DropFlowExpr {
1399 catalog_name,
1400 flow_name,
1401 flow_id,
1402 drop_if_exists,
1403 } = pb.drop_flow.context(error::InvalidProtoMsgSnafu {
1404 err_msg: "expected drop_flow",
1405 })?;
1406 let flow_id = flow_id
1407 .context(error::InvalidProtoMsgSnafu {
1408 err_msg: "expected flow_id",
1409 })?
1410 .id;
1411 Ok(DropFlowTask {
1412 catalog_name,
1413 flow_name,
1414 flow_id,
1415 drop_if_exists,
1416 })
1417 }
1418}
1419
1420impl From<DropFlowTask> for PbDropFlowTask {
1421 fn from(
1422 DropFlowTask {
1423 catalog_name,
1424 flow_name,
1425 flow_id,
1426 drop_if_exists,
1427 }: DropFlowTask,
1428 ) -> Self {
1429 PbDropFlowTask {
1430 drop_flow: Some(DropFlowExpr {
1431 catalog_name,
1432 flow_name,
1433 flow_id: Some(api::v1::FlowId { id: flow_id }),
1434 drop_if_exists,
1435 }),
1436 }
1437 }
1438}
1439
1440#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
1442pub enum CommentObjectId {
1443 Table(TableId),
1444 Flow(FlowId),
1445}
1446
1447#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
1449pub struct CommentOnTask {
1450 pub catalog_name: String,
1451 pub schema_name: String,
1452 pub object_type: CommentObjectType,
1453 pub object_name: String,
1454 pub column_name: Option<String>,
1456 pub object_id: Option<CommentObjectId>,
1458 pub comment: Option<String>,
1459}
1460
1461#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
1462pub enum CommentObjectType {
1463 Table,
1464 Column,
1465 Flow,
1466}
1467
1468impl CommentOnTask {
1469 pub fn table_id(&self) -> Option<TableId> {
1470 match self.object_id.as_ref() {
1471 Some(CommentObjectId::Table(table_id)) => Some(*table_id),
1472 _ => None,
1473 }
1474 }
1475
1476 pub fn flow_id(&self) -> Option<FlowId> {
1477 match self.object_id.as_ref() {
1478 Some(CommentObjectId::Flow(flow_id)) => Some(*flow_id),
1479 _ => None,
1480 }
1481 }
1482
1483 fn set_table_id(&mut self, table_id: TableId) {
1484 self.object_id = Some(CommentObjectId::Table(table_id));
1485 }
1486
1487 fn set_flow_id(&mut self, flow_id: FlowId) {
1488 self.object_id = Some(CommentObjectId::Flow(flow_id));
1489 }
1490
1491 pub fn cache_idents(&self) -> Vec<CacheIdent> {
1493 match self.object_type {
1494 CommentObjectType::Table | CommentObjectType::Column => {
1495 let mut cache_idents = Vec::with_capacity(2);
1496 if let Some(CommentObjectId::Table(table_id)) = self.object_id.as_ref() {
1497 cache_idents.push(CacheIdent::TableId(*table_id));
1498 }
1499 cache_idents.push(CacheIdent::TableName(TableName {
1500 catalog_name: self.catalog_name.clone(),
1501 schema_name: self.schema_name.clone(),
1502 table_name: self.object_name.clone(),
1503 }));
1504 cache_idents
1505 }
1506 CommentObjectType::Flow => {
1507 let mut cache_idents = Vec::with_capacity(2);
1508 if let Some(CommentObjectId::Flow(flow_id)) = self.object_id.as_ref() {
1509 cache_idents.push(CacheIdent::FlowId(*flow_id));
1510 }
1511 cache_idents.push(CacheIdent::FlowName(FlowName {
1512 catalog_name: self.catalog_name.clone(),
1513 flow_name: self.object_name.clone(),
1514 }));
1515 cache_idents
1516 }
1517 }
1518 }
1519
1520 pub async fn enrich_object_id(
1523 &mut self,
1524 table_name_manager: &TableNameManager,
1525 flow_name_manager: &FlowNameManager,
1526 ) -> Result<()> {
1527 match self.object_type {
1528 CommentObjectType::Table | CommentObjectType::Column => {
1529 let table_id = table_name_manager
1530 .get(TableNameKey::new(
1531 &self.catalog_name,
1532 &self.schema_name,
1533 &self.object_name,
1534 ))
1535 .await?
1536 .with_context(|| error::TableNotFoundSnafu {
1537 table_name: format_full_table_name(
1538 &self.catalog_name,
1539 &self.schema_name,
1540 &self.object_name,
1541 ),
1542 })?
1543 .table_id();
1544
1545 self.set_table_id(table_id);
1546 }
1547 CommentObjectType::Flow => {
1548 let flow_id = flow_name_manager
1549 .get(&self.catalog_name, &self.object_name)
1550 .await?
1551 .with_context(|| error::FlowNotFoundSnafu {
1552 flow_name: format_full_flow_name(&self.catalog_name, &self.object_name),
1553 })?
1554 .flow_id();
1555
1556 self.set_flow_id(flow_id);
1557 }
1558 }
1559
1560 Ok(())
1561 }
1562}
1563
1564impl From<CommentObjectType> for PbCommentObjectType {
1566 fn from(object_type: CommentObjectType) -> Self {
1567 match object_type {
1568 CommentObjectType::Table => PbCommentObjectType::Table,
1569 CommentObjectType::Column => PbCommentObjectType::Column,
1570 CommentObjectType::Flow => PbCommentObjectType::Flow,
1571 }
1572 }
1573}
1574
1575impl TryFrom<i32> for CommentObjectType {
1576 type Error = error::Error;
1577
1578 fn try_from(value: i32) -> Result<Self> {
1579 match value {
1580 0 => Ok(CommentObjectType::Table),
1581 1 => Ok(CommentObjectType::Column),
1582 2 => Ok(CommentObjectType::Flow),
1583 _ => error::InvalidProtoMsgSnafu {
1584 err_msg: format!(
1585 "Invalid CommentObjectType value: {}. Valid values are: 0 (Table), 1 (Column), 2 (Flow)",
1586 value
1587 ),
1588 }
1589 .fail(),
1590 }
1591 }
1592}
1593
1594impl TryFrom<PbCommentOnTask> for CommentOnTask {
1596 type Error = error::Error;
1597
1598 fn try_from(pb: PbCommentOnTask) -> Result<Self> {
1599 let comment_on = pb.comment_on.context(error::InvalidProtoMsgSnafu {
1600 err_msg: "expected comment_on",
1601 })?;
1602
1603 Ok(CommentOnTask {
1604 catalog_name: comment_on.catalog_name,
1605 schema_name: comment_on.schema_name,
1606 object_type: comment_on.object_type.try_into()?,
1607 object_name: comment_on.object_name,
1608 column_name: if comment_on.column_name.is_empty() {
1609 None
1610 } else {
1611 Some(comment_on.column_name)
1612 },
1613 comment: if comment_on.comment.is_empty() {
1614 None
1615 } else {
1616 Some(comment_on.comment)
1617 },
1618 object_id: None,
1619 })
1620 }
1621}
1622
1623impl From<CommentOnTask> for PbCommentOnTask {
1624 fn from(task: CommentOnTask) -> Self {
1625 let pb_object_type: PbCommentObjectType = task.object_type.into();
1626 PbCommentOnTask {
1627 comment_on: Some(CommentOnExpr {
1628 catalog_name: task.catalog_name,
1629 schema_name: task.schema_name,
1630 object_type: pb_object_type as i32,
1631 object_name: task.object_name,
1632 column_name: task.column_name.unwrap_or_default(),
1633 comment: task.comment.unwrap_or_default(),
1634 }),
1635 }
1636 }
1637}
1638
1639#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Default)]
1640pub struct QueryContext {
1641 pub current_catalog: String,
1642 pub current_schema: String,
1643 pub timezone: String,
1644 pub extensions: HashMap<String, String>,
1645 pub channel: u8,
1646 #[serde(default)]
1648 pub snapshot_seqs: HashMap<u64, u64>,
1649 #[serde(default)]
1651 pub sst_min_sequences: HashMap<u64, u64>,
1652}
1653
1654impl QueryContext {
1655 pub fn current_catalog(&self) -> &str {
1657 &self.current_catalog
1658 }
1659
1660 pub fn current_schema(&self) -> &str {
1662 &self.current_schema
1663 }
1664
1665 pub fn timezone(&self) -> &str {
1667 &self.timezone
1668 }
1669
1670 pub fn extensions(&self) -> &HashMap<String, String> {
1672 &self.extensions
1673 }
1674
1675 pub fn channel(&self) -> u8 {
1677 self.channel
1678 }
1679
1680 pub fn protocol(&self) -> Option<String> {
1682 let channel = Channel::from(u32::from(self.channel));
1683 (channel != Channel::Unknown).then(|| channel.as_ref().to_string())
1684 }
1685
1686 pub fn snapshot_seqs(&self) -> &HashMap<u64, u64> {
1687 &self.snapshot_seqs
1688 }
1689
1690 pub fn sst_min_sequences(&self) -> &HashMap<u64, u64> {
1691 &self.sst_min_sequences
1692 }
1693}
1694
1695#[derive(Debug, Clone, Serialize, PartialEq)]
1699pub struct FlowQueryContext {
1700 pub catalog: String,
1702 pub schema: String,
1704 pub timezone: String,
1706 #[serde(default)]
1708 pub extensions: HashMap<String, String>,
1709 #[serde(default)]
1711 pub channel: u8,
1712 #[serde(default)]
1714 pub snapshot_seqs: HashMap<u64, u64>,
1715 #[serde(default)]
1717 pub sst_min_sequences: HashMap<u64, u64>,
1718}
1719
1720impl<'de> Deserialize<'de> for FlowQueryContext {
1721 fn deserialize<D>(deserializer: D) -> result::Result<Self, D::Error>
1722 where
1723 D: serde::Deserializer<'de>,
1724 {
1725 #[derive(Deserialize)]
1727 #[serde(untagged)]
1728 enum ContextCompat {
1729 Flow(FlowQueryContextHelper),
1730 Full(QueryContext),
1731 }
1732
1733 #[derive(Deserialize)]
1734 struct FlowQueryContextHelper {
1735 catalog: String,
1736 schema: String,
1737 timezone: String,
1738 #[serde(default)]
1739 extensions: HashMap<String, String>,
1740 #[serde(default)]
1741 channel: u8,
1742 #[serde(default)]
1743 snapshot_seqs: HashMap<u64, u64>,
1744 #[serde(default)]
1745 sst_min_sequences: HashMap<u64, u64>,
1746 }
1747
1748 match ContextCompat::deserialize(deserializer)? {
1749 ContextCompat::Flow(helper) => Ok(FlowQueryContext {
1750 catalog: helper.catalog,
1751 schema: helper.schema,
1752 timezone: helper.timezone,
1753 extensions: helper.extensions,
1754 channel: helper.channel,
1755 snapshot_seqs: helper.snapshot_seqs,
1756 sst_min_sequences: helper.sst_min_sequences,
1757 }),
1758 ContextCompat::Full(full_ctx) => Ok(full_ctx.into()),
1759 }
1760 }
1761}
1762
1763impl From<PbQueryContext> for QueryContext {
1764 fn from(pb_ctx: PbQueryContext) -> Self {
1765 let (snapshot_seqs, sst_min_sequences) = pb_ctx
1766 .snapshot_seqs
1767 .map(|seqs| (seqs.snapshot_seqs, seqs.sst_min_sequences))
1768 .unwrap_or_default();
1769
1770 Self {
1771 current_catalog: pb_ctx.current_catalog,
1772 current_schema: pb_ctx.current_schema,
1773 timezone: pb_ctx.timezone,
1774 extensions: pb_ctx.extensions,
1775 channel: pb_ctx.channel as u8,
1776 snapshot_seqs,
1777 sst_min_sequences,
1778 }
1779 }
1780}
1781
1782impl From<QueryContext> for PbQueryContext {
1783 fn from(
1784 QueryContext {
1785 current_catalog,
1786 current_schema,
1787 timezone,
1788 extensions,
1789 channel,
1790 snapshot_seqs,
1791 sst_min_sequences,
1792 }: QueryContext,
1793 ) -> Self {
1794 PbQueryContext {
1795 current_catalog,
1796 current_schema,
1797 timezone,
1798 extensions,
1799 channel: channel as u32,
1800 snapshot_seqs: (!snapshot_seqs.is_empty() || !sst_min_sequences.is_empty()).then_some(
1801 api::v1::SnapshotSequences {
1802 snapshot_seqs,
1803 sst_min_sequences,
1804 },
1805 ),
1806 explain: None,
1807 }
1808 }
1809}
1810
1811impl From<QueryContext> for FlowQueryContext {
1812 fn from(ctx: QueryContext) -> Self {
1813 Self {
1814 catalog: ctx.current_catalog,
1815 schema: ctx.current_schema,
1816 timezone: ctx.timezone,
1817 extensions: ctx.extensions,
1818 channel: ctx.channel,
1819 snapshot_seqs: ctx.snapshot_seqs,
1820 sst_min_sequences: ctx.sst_min_sequences,
1821 }
1822 }
1823}
1824
1825impl From<FlowQueryContext> for QueryContext {
1826 fn from(flow_ctx: FlowQueryContext) -> Self {
1827 Self {
1828 current_catalog: flow_ctx.catalog,
1829 current_schema: flow_ctx.schema,
1830 timezone: flow_ctx.timezone,
1831 extensions: flow_ctx.extensions,
1832 channel: flow_ctx.channel,
1833 snapshot_seqs: flow_ctx.snapshot_seqs,
1834 sst_min_sequences: flow_ctx.sst_min_sequences,
1835 }
1836 }
1837}
1838
1839impl From<FlowQueryContext> for PbQueryContext {
1840 fn from(flow_ctx: FlowQueryContext) -> Self {
1841 let query_ctx: QueryContext = flow_ctx.into();
1842 query_ctx.into()
1843 }
1844}
1845
1846#[cfg(test)]
1847mod tests {
1848 use std::sync::Arc;
1849
1850 use api::v1::{AlterTableExpr, ColumnDef, CreateTableExpr, SemanticType};
1851 use datatypes::schema::{ColumnSchema, Schema, SchemaBuilder};
1852 use store_api::metric_engine_consts::METRIC_ENGINE_NAME;
1853 use store_api::storage::ConcreteDataType;
1854 use table::metadata::{TableInfo, TableMeta, TableType};
1855 use table::test_util::table_info::test_table_info;
1856
1857 use super::{AlterTableTask, CreateTableTask, *};
1858
1859 #[test]
1860 fn test_ddl_timeout_secs() {
1861 assert_eq!(ddl_timeout_secs(Duration::ZERO), 0);
1862 assert_eq!(ddl_timeout_secs(Duration::from_nanos(1)), 1);
1863 assert_eq!(ddl_timeout_secs(Duration::from_secs(1)), 1);
1864 assert_eq!(ddl_timeout_secs(Duration::from_millis(1500)), 2);
1865 assert_eq!(
1866 ddl_timeout_secs(Duration::from_secs(u32::MAX as u64 + 1)),
1867 u32::MAX
1868 );
1869 }
1870
1871 #[test]
1872 fn test_basic_ser_de_create_table_task() {
1873 let schema = SchemaBuilder::default().build().unwrap();
1874 let table_info = test_table_info(1025, "foo", "bar", "baz", Arc::new(schema));
1875 let task = CreateTableTask::new(CreateTableExpr::default(), Vec::new(), table_info);
1876
1877 let output = serde_json::to_vec(&task).unwrap();
1878
1879 let de = serde_json::from_slice(&output).unwrap();
1880 assert_eq!(task, de);
1881 }
1882
1883 #[test]
1884 fn test_basic_ser_de_alter_table_task() {
1885 let task = AlterTableTask {
1886 alter_table: AlterTableExpr::default(),
1887 };
1888
1889 let output = serde_json::to_vec(&task).unwrap();
1890
1891 let de = serde_json::from_slice(&output).unwrap();
1892 assert_eq!(task, de);
1893 }
1894
1895 #[test]
1896 fn test_undrop_table_task_pb_roundtrip() {
1897 let expected = UndropTableTask { table_id: 1024 };
1898 let request = SubmitDdlTaskRequest::new(DdlTask::UndropTable(expected.clone()));
1899
1900 let pb = PbDdlTaskRequest::try_from(request).unwrap();
1901 let pb_task = pb.task.unwrap();
1902 let de = DdlTask::try_from(pb_task).unwrap();
1903
1904 assert!(matches!(de, DdlTask::UndropTable(task) if task == expected));
1905 }
1906
1907 #[test]
1908 fn test_purge_dropped_table_task_pb_roundtrip() {
1909 let expected = PurgeDroppedTableTask { table_id: 1024 };
1910 let request = SubmitDdlTaskRequest::new(DdlTask::PurgeDroppedTable(expected.clone()));
1911
1912 let pb = PbDdlTaskRequest::try_from(request).unwrap();
1913 let pb_task = pb.task.unwrap();
1914 let de = DdlTask::try_from(pb_task).unwrap();
1915
1916 assert!(matches!(de, DdlTask::PurgeDroppedTable(task) if task == expected));
1917 }
1918
1919 #[test]
1920 fn test_undrop_table_task_json_roundtrip() {
1921 let task = UndropTableTask { table_id: 1024 };
1922
1923 let output = serde_json::to_vec(&task).unwrap();
1924
1925 let de = serde_json::from_slice(&output).unwrap();
1926 assert_eq!(task, de);
1927 }
1928
1929 #[test]
1930 fn test_purge_dropped_table_task_json_roundtrip() {
1931 let task = PurgeDroppedTableTask { table_id: 1024 };
1932
1933 let output = serde_json::to_vec(&task).unwrap();
1934
1935 let de = serde_json::from_slice(&output).unwrap();
1936 assert_eq!(task, de);
1937 }
1938
1939 #[test]
1940 fn test_sort_columns() {
1941 let schema = Arc::new(Schema::new(vec![
1943 ColumnSchema::new(
1944 "column3".to_string(),
1945 ConcreteDataType::string_datatype(),
1946 true,
1947 ),
1948 ColumnSchema::new(
1949 "column1".to_string(),
1950 ConcreteDataType::timestamp_millisecond_datatype(),
1951 false,
1952 )
1953 .with_time_index(true),
1954 ColumnSchema::new(
1955 "column2".to_string(),
1956 ConcreteDataType::float64_datatype(),
1957 true,
1958 ),
1959 ]));
1960
1961 let meta = TableMeta {
1963 schema,
1964 primary_key_indices: vec![0],
1965 value_indices: vec![2],
1966 engine: METRIC_ENGINE_NAME.to_string(),
1967 next_column_id: 0,
1968 options: Default::default(),
1969 created_on: Default::default(),
1970 updated_on: Default::default(),
1971 partition_key_indices: Default::default(),
1972 column_ids: Default::default(),
1973 };
1974
1975 let raw_table_info = TableInfo {
1977 ident: Default::default(),
1978 meta,
1979 name: Default::default(),
1980 desc: Default::default(),
1981 catalog_name: Default::default(),
1982 schema_name: Default::default(),
1983 table_type: TableType::Base,
1984 };
1985
1986 let create_table_expr = CreateTableExpr {
1988 column_defs: vec![
1989 ColumnDef {
1990 name: "column3".to_string(),
1991 semantic_type: SemanticType::Tag as i32,
1992 ..Default::default()
1993 },
1994 ColumnDef {
1995 name: "column1".to_string(),
1996 semantic_type: SemanticType::Timestamp as i32,
1997 ..Default::default()
1998 },
1999 ColumnDef {
2000 name: "column2".to_string(),
2001 semantic_type: SemanticType::Field as i32,
2002 ..Default::default()
2003 },
2004 ],
2005 primary_keys: vec!["column3".to_string()],
2006 ..Default::default()
2007 };
2008
2009 let mut create_table_task =
2010 CreateTableTask::new(create_table_expr, Vec::new(), raw_table_info);
2011
2012 create_table_task.sort_columns();
2014
2015 assert_eq!(
2017 create_table_task.create_table.column_defs[0].name,
2018 "column1".to_string()
2019 );
2020 assert_eq!(
2021 create_table_task.create_table.column_defs[1].name,
2022 "column2".to_string()
2023 );
2024 assert_eq!(
2025 create_table_task.create_table.column_defs[2].name,
2026 "column3".to_string()
2027 );
2028
2029 assert_eq!(
2031 create_table_task.table_info.meta.schema.timestamp_index(),
2032 Some(0)
2033 );
2034 assert_eq!(
2035 create_table_task.table_info.meta.primary_key_indices,
2036 vec![2]
2037 );
2038 assert_eq!(create_table_task.table_info.meta.value_indices, vec![0, 1]);
2039 }
2040
2041 #[test]
2042 fn test_flow_query_context_conversion_from_query_context() {
2043 use std::collections::HashMap;
2044 let mut extensions = HashMap::new();
2045 extensions.insert("key1".to_string(), "value1".to_string());
2046 extensions.insert("key2".to_string(), "value2".to_string());
2047
2048 let query_ctx = QueryContext {
2049 current_catalog: "test_catalog".to_string(),
2050 current_schema: "test_schema".to_string(),
2051 timezone: "UTC".to_string(),
2052 extensions,
2053 channel: 5,
2054 snapshot_seqs: HashMap::from([(10, 100)]),
2055 sst_min_sequences: HashMap::from([(10, 90)]),
2056 };
2057
2058 let flow_ctx: FlowQueryContext = query_ctx.into();
2059
2060 assert_eq!(flow_ctx.catalog, "test_catalog");
2061 assert_eq!(flow_ctx.schema, "test_schema");
2062 assert_eq!(flow_ctx.timezone, "UTC");
2063 assert_eq!(flow_ctx.channel, 5);
2064 assert_eq!(flow_ctx.snapshot_seqs, HashMap::from([(10, 100)]));
2065 assert_eq!(flow_ctx.sst_min_sequences, HashMap::from([(10, 90)]));
2066 }
2067
2068 #[test]
2069 fn test_flow_query_context_conversion_to_query_context() {
2070 let flow_ctx = FlowQueryContext {
2071 catalog: "prod_catalog".to_string(),
2072 schema: "public".to_string(),
2073 timezone: "America/New_York".to_string(),
2074 extensions: HashMap::from([("k".to_string(), "v".to_string())]),
2075 channel: 7,
2076 snapshot_seqs: HashMap::from([(11, 111)]),
2077 sst_min_sequences: HashMap::from([(11, 101)]),
2078 };
2079
2080 let query_ctx: QueryContext = flow_ctx.clone().into();
2081
2082 assert_eq!(query_ctx.current_catalog, "prod_catalog");
2083 assert_eq!(query_ctx.current_schema, "public");
2084 assert_eq!(query_ctx.timezone, "America/New_York");
2085 assert_eq!(
2086 query_ctx.extensions,
2087 HashMap::from([("k".to_string(), "v".to_string())])
2088 );
2089 assert_eq!(query_ctx.channel, 7);
2090 assert_eq!(query_ctx.snapshot_seqs, HashMap::from([(11, 111)]));
2091 assert_eq!(query_ctx.sst_min_sequences, HashMap::from([(11, 101)]));
2092
2093 let flow_ctx_roundtrip: FlowQueryContext = query_ctx.into();
2095 assert_eq!(flow_ctx, flow_ctx_roundtrip);
2096 }
2097
2098 #[test]
2099 fn test_flow_query_context_serialization() {
2100 let flow_ctx = FlowQueryContext {
2101 catalog: "test_catalog".to_string(),
2102 schema: "test_schema".to_string(),
2103 timezone: "UTC".to_string(),
2104 extensions: HashMap::new(),
2105 channel: 0,
2106 snapshot_seqs: HashMap::new(),
2107 sst_min_sequences: HashMap::new(),
2108 };
2109
2110 let serialized = serde_json::to_string(&flow_ctx).unwrap();
2111 let deserialized: FlowQueryContext = serde_json::from_str(&serialized).unwrap();
2112
2113 assert_eq!(flow_ctx, deserialized);
2114
2115 let json_value: serde_json::Value = serde_json::from_str(&serialized).unwrap();
2117 assert_eq!(json_value["catalog"], "test_catalog");
2118 assert_eq!(json_value["schema"], "test_schema");
2119 assert_eq!(json_value["timezone"], "UTC");
2120 }
2121
2122 #[test]
2123 fn test_flow_query_context_conversion_to_pb() {
2124 let flow_ctx = FlowQueryContext {
2125 catalog: "pb_catalog".to_string(),
2126 schema: "pb_schema".to_string(),
2127 timezone: "Asia/Tokyo".to_string(),
2128 extensions: HashMap::from([("x".to_string(), "y".to_string())]),
2129 channel: 6,
2130 snapshot_seqs: HashMap::from([(3, 30)]),
2131 sst_min_sequences: HashMap::from([(3, 21)]),
2132 };
2133
2134 let pb_ctx: PbQueryContext = flow_ctx.into();
2135
2136 assert_eq!(pb_ctx.current_catalog, "pb_catalog");
2137 assert_eq!(pb_ctx.current_schema, "pb_schema");
2138 assert_eq!(pb_ctx.timezone, "Asia/Tokyo");
2139 assert_eq!(
2140 pb_ctx.extensions,
2141 HashMap::from([("x".to_string(), "y".to_string())])
2142 );
2143 assert_eq!(pb_ctx.channel, 6);
2144 assert_eq!(
2145 pb_ctx.snapshot_seqs,
2146 Some(api::v1::SnapshotSequences {
2147 snapshot_seqs: HashMap::from([(3, 30)]),
2148 sst_min_sequences: HashMap::from([(3, 21)]),
2149 })
2150 );
2151 assert!(pb_ctx.explain.is_none());
2152 }
2153
2154 #[test]
2155 fn test_pb_query_context_roundtrip_with_snapshot_sequences() {
2156 let pb = PbQueryContext {
2157 current_catalog: "c1".to_string(),
2158 current_schema: "s1".to_string(),
2159 timezone: "UTC".to_string(),
2160 extensions: HashMap::from([("flow.return_region_seq".to_string(), "true".to_string())]),
2161 channel: 3,
2162 snapshot_seqs: Some(api::v1::SnapshotSequences {
2163 snapshot_seqs: HashMap::from([(1, 100)]),
2164 sst_min_sequences: HashMap::from([(1, 90)]),
2165 }),
2166 explain: None,
2167 };
2168
2169 let query_ctx: QueryContext = pb.clone().into();
2170 let pb_roundtrip: PbQueryContext = query_ctx.into();
2171
2172 assert_eq!(pb_roundtrip.current_catalog, pb.current_catalog);
2173 assert_eq!(pb_roundtrip.current_schema, pb.current_schema);
2174 assert_eq!(pb_roundtrip.timezone, pb.timezone);
2175 assert_eq!(pb_roundtrip.extensions, pb.extensions);
2176 assert_eq!(pb_roundtrip.channel, pb.channel);
2177 assert_eq!(pb_roundtrip.snapshot_seqs, pb.snapshot_seqs);
2178 }
2179
2180 #[test]
2181 fn test_trigger_reason_deserializes_unknown_value() {
2182 let reason: TriggerReason = serde_json::from_str("\"future_reason\"").unwrap();
2183 assert_eq!(TriggerReason::Unknown, reason);
2184 }
2185}