Skip to content

Commit 9ba446d

Browse files
authored
fix(query): improve private task compatibility (#19918)
* fix(query): assign private task ids * fix(query): support private task scripts
1 parent 0c3cfb7 commit 9ba446d

21 files changed

Lines changed: 465 additions & 20 deletions

File tree

src/meta/app/src/id_generator.rs

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@ pub(crate) const ID_GEN_ROW_POLICY: &str = "row_access";
3131
pub(crate) const ID_GEN_BACKGROUND_JOB: &str = "background_job";
3232

3333
pub(crate) const ID_GEN_PROCEDURE: &str = "procedure_id";
34+
pub(crate) const ID_GEN_TASK: &str = "task_id";
3435

3536
/// Key for resource id generator
3637
///
@@ -109,6 +110,11 @@ impl IdGenerator {
109110
pub fn procedure_id() -> Self {
110111
Self::new(ID_GEN_PROCEDURE)
111112
}
113+
114+
/// Create a key for generating task id with kvapi::KVApi
115+
pub fn task_id() -> Self {
116+
Self::new(ID_GEN_TASK)
117+
}
112118
}
113119

114120
impl kvapi::Key for IdGenerator {
@@ -143,6 +149,7 @@ mod t {
143149
assert_round_trip(IdGenerator::data_mask_id(), "__fd_id_gen/data_mask");
144150
assert_round_trip(IdGenerator::table_lock_id(), "__fd_id_gen/table_lock_id");
145151
assert_round_trip(IdGenerator::procedure_id(), "__fd_id_gen/procedure_id");
152+
assert_round_trip(IdGenerator::task_id(), "__fd_id_gen/task_id");
146153
}
147154

148155
#[test]

src/meta/app/src/principal/mod.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -106,6 +106,7 @@ pub use task::Status;
106106
pub use task::Task;
107107
pub use task::TaskMessage;
108108
pub use task::TaskRun;
109+
pub use task::TaskSql;
109110
pub use task::WarehouseOptions;
110111
pub use task_ident::TaskIdent;
111112
pub use task_ident::TaskIdentRaw;

src/meta/app/src/principal/task.rs

Lines changed: 24 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -55,11 +55,34 @@ pub struct WarehouseOptions {
5555
pub using_warehouse_size: Option<String>,
5656
}
5757

58+
#[derive(Debug, Clone, PartialEq)]
59+
pub enum TaskSql {
60+
Sql(String),
61+
Script(Vec<String>),
62+
}
63+
64+
impl TaskSql {
65+
pub fn query_text(&self) -> String {
66+
match self {
67+
TaskSql::Sql(sql) => sql.clone(),
68+
TaskSql::Script(sqls) => {
69+
let mut text = String::from("BEGIN\n");
70+
for sql in sqls {
71+
text.push_str(sql);
72+
text.push_str(";\n");
73+
}
74+
text.push_str("END;");
75+
text
76+
}
77+
}
78+
}
79+
}
80+
5881
#[derive(Debug, Clone, PartialEq)]
5982
pub struct Task {
6083
pub task_id: u64,
6184
pub task_name: String,
62-
pub query_text: String,
85+
pub task_sql: TaskSql,
6386
pub when_condition: Option<String>,
6487
pub after: Vec<String>,
6588
pub comment: Option<String>,

src/meta/proto-conv/src/impls/task.rs

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -72,7 +72,11 @@ impl FromToProto for mt::Task {
7272
Ok(Self {
7373
task_id: p.task_id,
7474
task_name: p.task_name,
75-
query_text: p.query_text,
75+
task_sql: if p.script_sql.is_empty() {
76+
mt::TaskSql::Sql(p.query_text)
77+
} else {
78+
mt::TaskSql::Script(p.script_sql)
79+
},
7680
when_condition: p.when_condition.clone(),
7781
after: p.after,
7882
comment: p.comment,
@@ -97,7 +101,7 @@ impl FromToProto for mt::Task {
97101
min_reader_ver: MIN_READER_VER,
98102
task_id: self.task_id,
99103
task_name: self.task_name.clone(),
100-
query_text: self.query_text.clone(),
104+
query_text: self.task_sql.query_text(),
101105
comment: self.comment.clone(),
102106
owner: self.owner.clone(),
103107
schedule_options: self.schedule_options.as_ref().map(|s| pb::ScheduleOptions {
@@ -125,6 +129,10 @@ impl FromToProto for mt::Task {
125129
session_parameters: self.session_params.clone(),
126130
error_integration: self.error_integration.clone(),
127131
owner_user: self.owner_user.clone(),
132+
script_sql: match &self.task_sql {
133+
mt::TaskSql::Sql(_) => vec![],
134+
mt::TaskSql::Script(sqls) => sqls.clone(),
135+
},
128136
})
129137
}
130138
}

src/meta/proto-conv/src/util.rs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -204,7 +204,8 @@ const META_CHANGE_LOG: &[(u64, &str)] = &[
204204
(172, "2026-04-09: Update: user.proto/CsvFileFormatParams and TextFileFormatParams add trim_space"),
205205
(173, "2026-04-16: Update: file_format.proto/CsvFileFormatParams add quote_style"),
206206
(174, "2026-04-28: Add: AuthInfo::KeyPair for key-pair authentication"),
207-
(175, "2026-05-08: Add: field_stats_truncate_len per-column string stats truncation in TableMeta")
207+
(175, "2026-05-08: Add: field_stats_truncate_len per-column string stats truncation in TableMeta"),
208+
(176, "2026-05-25: Add: task.proto/Task.script_sql")
208209
// Dear developer:
209210
// If you're gonna add a new metadata version, you'll have to add a test for it.
210211
// You could just copy an existing test file(e.g., `../tests/it/v024_table_meta.rs`)

src/meta/proto-conv/tests/it/main.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -167,3 +167,4 @@ mod v172_file_format_trim_space;
167167
mod v173_csv_quote_style;
168168
mod v174_user_key_pair;
169169
mod v175_field_stats_truncate_len;
170+
mod v176_task_script_sql;

src/meta/proto-conv/tests/it/v136_add_task.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,7 @@ fn test_decode_v136_add_task() -> anyhow::Result<()> {
4141
let want = || mt::Task {
4242
task_id: 11,
4343
task_name: "task_c".to_string(),
44-
query_text: "SELECT * FROM t1".to_string(),
44+
task_sql: mt::TaskSql::Sql("SELECT * FROM t1".to_string()),
4545
when_condition: Some("c1 > 1".to_string()),
4646
after: vec!["task_a".to_string(), "task_b".to_string()],
4747
comment: Some("comment".to_string()),
@@ -78,7 +78,7 @@ fn test_decode_v136_task_message() -> anyhow::Result<()> {
7878
let want_task = || mt::Task {
7979
task_id: 11,
8080
task_name: "task_c".to_string(),
81-
query_text: "SELECT * FROM t1".to_string(),
81+
task_sql: mt::TaskSql::Sql("SELECT * FROM t1".to_string()),
8282
when_condition: Some("c1 > 1".to_string()),
8383
after: vec!["task_a".to_string(), "task_b".to_string()],
8484
comment: Some("comment".to_string()),

src/meta/proto-conv/tests/it/v140_task_message.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ fn test_decode_v140_task_message() -> anyhow::Result<()> {
2727
let want_task = || mt::Task {
2828
task_id: 11,
2929
task_name: "task_c".to_string(),
30-
query_text: "SELECT * FROM t1".to_string(),
30+
task_sql: mt::TaskSql::Sql("SELECT * FROM t1".to_string()),
3131
when_condition: Some("c1 > 1".to_string()),
3232
after: vec!["task_a".to_string(), "task_b".to_string()],
3333
comment: Some("comment".to_string()),
Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
// Copyright 2026 Datafuse 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+
15+
use chrono::DateTime;
16+
use databend_common_meta_app::principal as mt;
17+
use fastrace::func_name;
18+
use maplit::btreemap;
19+
20+
use crate::common;
21+
22+
#[test]
23+
fn test_decode_v176_task_script_sql() -> anyhow::Result<()> {
24+
let task_v176 = vec![
25+
8, 11, 18, 6, 116, 97, 115, 107, 95, 99, 34, 60, 66, 69, 71, 73, 78, 10, 73, 78, 83, 69,
26+
82, 84, 32, 73, 78, 84, 79, 32, 116, 32, 86, 65, 76, 85, 69, 83, 40, 49, 41, 59, 10, 73,
27+
78, 83, 69, 82, 84, 32, 73, 78, 84, 79, 32, 116, 32, 86, 65, 76, 85, 69, 83, 40, 50, 41,
28+
59, 10, 69, 78, 68, 59, 50, 6, 112, 117, 98, 108, 105, 99, 114, 23, 49, 57, 55, 48, 45, 48,
29+
49, 45, 48, 49, 32, 48, 48, 58, 48, 48, 58, 49, 49, 32, 85, 84, 67, 122, 23, 49, 57, 55,
30+
48, 45, 48, 49, 45, 48, 49, 32, 48, 48, 58, 48, 48, 58, 49, 50, 32, 85, 84, 67, 170, 1, 2,
31+
109, 101, 178, 1, 23, 73, 78, 83, 69, 82, 84, 32, 73, 78, 84, 79, 32, 116, 32, 86, 65, 76,
32+
85, 69, 83, 40, 49, 41, 178, 1, 23, 73, 78, 83, 69, 82, 84, 32, 73, 78, 84, 79, 32, 116,
33+
32, 86, 65, 76, 85, 69, 83, 40, 50, 41, 160, 6, 176, 1, 168, 6, 24,
34+
];
35+
36+
let want = mt::Task {
37+
task_id: 11,
38+
task_name: "task_c".to_string(),
39+
task_sql: mt::TaskSql::Script(vec![
40+
"INSERT INTO t VALUES(1)".to_string(),
41+
"INSERT INTO t VALUES(2)".to_string(),
42+
]),
43+
when_condition: None,
44+
after: vec![],
45+
comment: None,
46+
owner: mt::BUILTIN_ROLE_PUBLIC.to_string(),
47+
owner_user: "me".to_string(),
48+
schedule_options: None,
49+
warehouse_options: None,
50+
next_scheduled_at: None,
51+
suspend_task_after_num_failures: None,
52+
error_integration: None,
53+
status: mt::Status::Suspended,
54+
created_at: DateTime::from_timestamp(11, 0).unwrap(),
55+
updated_at: DateTime::from_timestamp(12, 0).unwrap(),
56+
last_suspended_at: None,
57+
session_params: btreemap! {},
58+
};
59+
60+
common::test_pb_from_to(func_name!(), want.clone())?;
61+
common::test_load_old(func_name!(), task_v176.as_slice(), 176, want)?;
62+
63+
Ok(())
64+
}

src/meta/protos/proto/task.proto

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,8 @@ message Task {
6161
}
6262
uint64 task_id = 1;
6363
string task_name = 2;
64+
// Kept for compatibility and display. For single-statement tasks this is the
65+
// SQL text; for script tasks it is derived from script_sql.
6466
string query_text = 4;
6567
optional string comment = 5;
6668
string owner = 6;
@@ -77,4 +79,7 @@ message Task {
7779
map<string, string> session_parameters = 19;
7880
optional string error_integration = 20;
7981
string owner_user = 21;
82+
// Non-empty only for script tasks created with `AS BEGIN ... END`.
83+
// Each element is one statement in execution order.
84+
repeated string script_sql = 22;
8085
}

0 commit comments

Comments
 (0)