Skip to content

Commit 821760a

Browse files
committed
chore: Improve Virtual Column Block Meta Generation
1 parent d4f3935 commit 821760a

106 files changed

Lines changed: 5450 additions & 2224 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

Cargo.lock

Lines changed: 2 additions & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Cargo.toml

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -643,7 +643,9 @@ databend-meta = { git = "https://github.com/databendlabs/databend-meta.git", tag
643643
databend-meta-client = { git = "https://github.com/databendlabs/databend-meta.git", tag = "v260205.13.2" }
644644
databend-meta-test-harness = { git = "https://github.com/databendlabs/databend-meta.git", tag = "v260629.2.0" }
645645
deltalake = { git = "https://github.com/delta-io/delta-rs", rev = "9954bff" }
646-
jsonb = { git = "https://github.com/databendlabs/jsonb.git", rev = "a16344417d1fec6df4a62d8e77679211e909f86e" }
646+
#jsonb = { git = "https://github.com/databendlabs/jsonb.git", rev = "a16344417d1fec6df4a62d8e77679211e909f86e" }
647+
#jsonb = { git = "https://github.com/b41sh/jsonb.git", rev = "816c7ab394b5732ea621e9315723a34a96d9047c" }
648+
jsonb = { git = "https://github.com/b41sh/jsonb.git", rev = "ec1130a0f6a5e63c6a741e06bc4733e1a27c47d3" }
647649
lance-arrow = { git = "https://github.com/datafuse-extras/lance", rev = "85f9401d3246d52a287bca21457dbae0466858ee" }
648650
lance-core = { git = "https://github.com/datafuse-extras/lance", rev = "85f9401d3246d52a287bca21457dbae0466858ee" }
649651
lance-encoding = { git = "https://github.com/datafuse-extras/lance", rev = "85f9401d3246d52a287bca21457dbae0466858ee" }

src/query/catalog/src/plan/mod.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ mod projection;
2121
mod pruning_statistics;
2222
mod pushdown;
2323
mod stream_column;
24+
mod virtual_column;
2425

2526
pub use agg_index::*;
2627
pub use datasource::*;
@@ -31,3 +32,4 @@ pub use projection::Projection;
3132
pub use pruning_statistics::PruningStatistics;
3233
pub use pushdown::*;
3334
pub use stream_column::*;
35+
pub use virtual_column::*;

src/query/catalog/src/plan/partition.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@ use rand::thread_rng;
3434
use sha2::Digest;
3535

3636
use crate::plan::PartStatistics;
37+
use crate::plan::VirtualColumnLayout;
3738
use crate::table_context::TableContext;
3839

3940
/// Partition information.
@@ -441,6 +442,7 @@ pub struct ReclusterTask {
441442
// All input blocks in this task are already ordered by the current cluster key.
442443
#[serde(default)]
443444
pub all_ordered: bool,
445+
pub virtual_column_layout: Option<VirtualColumnLayout>,
444446
}
445447

446448
pub type BlockMetaWithHLL = (Arc<BlockMeta>, Option<RawBlockHLL>);

src/query/catalog/src/plan/pushdown.rs

Lines changed: 45 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ use databend_common_ast::ast::SampleConfig;
2121
use databend_common_exception::Result;
2222
use databend_common_expression::ColumnId;
2323
use databend_common_expression::DataSchema;
24+
use databend_common_expression::Expr;
2425
use databend_common_expression::FunctionRegistry;
2526
use databend_common_expression::RemoteExpr;
2627
use databend_common_expression::SEARCH_MATCHED_COL_NAME;
@@ -57,9 +58,11 @@ pub struct VirtualColumnField {
5758
pub source_column_id: u32,
5859
/// The source column name.
5960
pub source_name: String,
60-
/// The virtual column id
61-
pub column_id: u32,
62-
/// The virtual column name.
61+
/// Query-time temporary column id. This is not a persisted segment-local
62+
/// virtual column id; each segment may assign a different real column id
63+
/// for the same path, so readers must map this id through the segment schema.
64+
pub query_column_id: u32,
65+
/// Full query field name, including the source column, e.g. `v.user.name` or `v[0].id`.
6366
pub name: String,
6467
/// Paths to generate virtual column from source column.
6568
pub key_paths: OwnedKeyPaths,
@@ -69,6 +72,21 @@ pub struct VirtualColumnField {
6972
pub data_type: Box<TableDataType>,
7073
}
7174

75+
/// Query-time identity of a virtual column referenced by a pushed-down filter
76+
/// or ordering expression. It bridges the query column id to the persisted
77+
/// source-column/canonical-path identity used to locate segment-local stats.
78+
#[derive(Clone, Debug)]
79+
pub struct VirtualPredicateRef {
80+
/// Full query/pipeline field name used by expression column references.
81+
pub name: String,
82+
/// Id of the authoritative source Variant column.
83+
pub source_column_id: u32,
84+
/// Query-time temporary column id used by runtime filters and readers.
85+
pub query_column_id: u32,
86+
/// Compact canonical JSON path relative to the source column.
87+
pub encoded_path: String,
88+
}
89+
7290
/// Information about prewhere optimization.
7391
///
7492
/// Prewhere steps:
@@ -207,6 +225,30 @@ pub struct PushDownInfo {
207225
}
208226

209227
impl PushDownInfo {
228+
pub fn virtual_predicate_refs(
229+
&self,
230+
filter_expr: Option<&Expr<String>>,
231+
) -> Vec<VirtualPredicateRef> {
232+
let Some(virtual_column) = &self.virtual_column else {
233+
return Vec::new();
234+
};
235+
let mut referenced_columns = filter_expr.map(Expr::column_refs).unwrap_or_default();
236+
for (expr, _, _) in &self.order_by {
237+
referenced_columns.extend(expr.column_refs());
238+
}
239+
virtual_column
240+
.virtual_column_fields
241+
.iter()
242+
.filter(|field| referenced_columns.contains_key(&field.name))
243+
.map(|field| VirtualPredicateRef {
244+
name: field.name.clone(),
245+
source_column_id: field.source_column_id,
246+
query_column_id: field.query_column_id,
247+
encoded_path: field.key_paths.to_canonical_path(),
248+
})
249+
.collect()
250+
}
251+
210252
pub fn add_internal_column_dependencies<'a>(
211253
&mut self,
212254
schema: &TableSchema,
Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
// Copyright 2021 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 databend_common_expression::ColumnId;
16+
17+
#[derive(
18+
Clone,
19+
Debug,
20+
Default,
21+
PartialEq,
22+
Eq,
23+
PartialOrd,
24+
Ord,
25+
Hash,
26+
serde::Serialize,
27+
serde::Deserialize,
28+
)]
29+
pub struct VirtualColumnPath {
30+
pub source_column_id: ColumnId,
31+
pub path: String,
32+
}
33+
34+
#[derive(Clone, Debug, Default, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
35+
pub struct VirtualColumnLayout {
36+
pub direct_paths: Vec<VirtualColumnPath>,
37+
}
38+
39+
impl VirtualColumnLayout {
40+
pub fn contains(&self, source_column_id: ColumnId, path: &str) -> bool {
41+
self.direct_paths
42+
.iter()
43+
.any(|item| item.source_column_id == source_column_id && item.path == path)
44+
}
45+
}

src/query/expression/src/conversion.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -247,7 +247,7 @@ fn classify_decimal_conversion(src: DecimalSize, dest: DecimalSize) -> Conversio
247247
}
248248
}
249249

250-
fn number_common_type(left: NumberDataType, right: NumberDataType) -> DataType {
250+
pub fn number_common_type(left: NumberDataType, right: NumberDataType) -> DataType {
251251
if left == right {
252252
return DataType::Number(left);
253253
}

src/query/expression/src/expression.rs

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1103,6 +1103,29 @@ impl<Index: ColumnIndex> Expr<Index> {
11031103
}
11041104

11051105
impl<Index: ColumnIndex> RemoteExpr<Index> {
1106+
pub fn column_refs(&self) -> HashMap<Index, DataType> {
1107+
#[recursive::recursive]
1108+
fn walk<Index: ColumnIndex>(expr: &RemoteExpr<Index>, refs: &mut HashMap<Index, DataType>) {
1109+
match expr {
1110+
RemoteExpr::ColumnRef { id, data_type, .. } => {
1111+
refs.insert(id.clone(), data_type.clone());
1112+
}
1113+
RemoteExpr::Cast { expr, .. } => walk(expr, refs),
1114+
RemoteExpr::FunctionCall { args, .. }
1115+
| RemoteExpr::LambdaFunctionCall { args, .. } => {
1116+
for arg in args {
1117+
walk(arg, refs);
1118+
}
1119+
}
1120+
RemoteExpr::Constant { .. } => {}
1121+
}
1122+
}
1123+
1124+
let mut refs = HashMap::new();
1125+
walk(self, &mut refs);
1126+
refs
1127+
}
1128+
11061129
pub fn as_expr(&self, fn_registry: &FunctionRegistry) -> Expr<Index> {
11071130
match self {
11081131
RemoteExpr::Constant {

src/query/service/src/interpreters/common/table_option_validation.rs

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,11 @@ use databend_common_storages_fuse::FUSE_OPT_KEY_RECLUSTER_DEPTH;
4343
use databend_common_storages_fuse::FUSE_OPT_KEY_ROW_AVG_DEPTH_THRESHOLD;
4444
use databend_common_storages_fuse::FUSE_OPT_KEY_ROW_PER_BLOCK;
4545
use databend_common_storages_fuse::FUSE_OPT_KEY_ROW_PER_PAGE;
46+
use databend_common_storages_fuse::FUSE_OPT_KEY_VIRTUAL_COLUMN_MAX_DIRECT_COLUMNS;
47+
use databend_common_storages_fuse::FUSE_OPT_KEY_VIRTUAL_COLUMN_MAX_PATH_STATISTICS;
4648
use databend_common_storages_fuse::MAX_RECLUSTER_DEPTH;
49+
use databend_common_storages_fuse::MAX_VIRTUAL_COLUMN_DIRECT_COLUMNS;
50+
use databend_common_storages_fuse::MAX_VIRTUAL_COLUMN_PATH_STATISTICS;
4751
use databend_common_storages_fuse::MIN_RECLUSTER_DEPTH;
4852
use databend_storages_common_index::BloomIndex;
4953
use databend_storages_common_index::RangeIndex;
@@ -94,6 +98,8 @@ pub static CREATE_FUSE_OPTIONS: LazyLock<HashSet<&'static str>> = LazyLock::new(
9498
r.insert(FUSE_OPT_KEY_ENABLE_AUTO_VACUUM);
9599
r.insert(FUSE_OPT_KEY_ENABLE_AUTO_ANALYZE);
96100
r.insert(FUSE_OPT_KEY_ENABLE_VIRTUAL_COLUMN);
101+
r.insert(FUSE_OPT_KEY_VIRTUAL_COLUMN_MAX_DIRECT_COLUMNS);
102+
r.insert(FUSE_OPT_KEY_VIRTUAL_COLUMN_MAX_PATH_STATISTICS);
97103
r.insert(FUSE_OPT_KEY_AUTO_COMPACTION_IMPERFECT_BLOCKS_THRESHOLD);
98104

99105
r.insert(OPT_KEY_BLOOM_INDEX_COLUMNS);
@@ -219,6 +225,8 @@ pub static UNSET_TABLE_OPTIONS_WHITE_LIST: LazyLock<HashSet<&'static str>> = Laz
219225
r.insert(FUSE_OPT_KEY_DATA_RETENTION_NUM_SNAPSHOTS_TO_KEEP);
220226
r.insert(FUSE_OPT_KEY_AUTO_COMPACTION_IMPERFECT_BLOCKS_THRESHOLD);
221227
r.insert(FUSE_OPT_KEY_ENABLE_VIRTUAL_COLUMN);
228+
r.insert(FUSE_OPT_KEY_VIRTUAL_COLUMN_MAX_DIRECT_COLUMNS);
229+
r.insert(FUSE_OPT_KEY_VIRTUAL_COLUMN_MAX_PATH_STATISTICS);
222230
r.insert(OPT_KEY_ENABLE_COPY_DEDUP_FULL_PATH);
223231
r.insert(FUSE_OPT_KEY_DATA_PAGE_ROWS);
224232
r.insert(FUSE_OPT_KEY_DATA_PAGE_BYTES);
@@ -230,6 +238,28 @@ pub static UNSET_TABLE_OPTIONS_WHITE_LIST: LazyLock<HashSet<&'static str>> = Laz
230238
r
231239
});
232240

241+
pub fn is_valid_virtual_column_layout_options(
242+
options: &BTreeMap<String, String>,
243+
) -> databend_common_exception::Result<()> {
244+
if let Some(value) = options.get(FUSE_OPT_KEY_VIRTUAL_COLUMN_MAX_DIRECT_COLUMNS) {
245+
let value = value.parse::<usize>()?;
246+
if value > MAX_VIRTUAL_COLUMN_DIRECT_COLUMNS {
247+
return Err(ErrorCode::TableOptionInvalid(format!(
248+
"{FUSE_OPT_KEY_VIRTUAL_COLUMN_MAX_DIRECT_COLUMNS} must be between 0 and {MAX_VIRTUAL_COLUMN_DIRECT_COLUMNS}",
249+
)));
250+
}
251+
}
252+
if let Some(value) = options.get(FUSE_OPT_KEY_VIRTUAL_COLUMN_MAX_PATH_STATISTICS) {
253+
let value = value.parse::<usize>()?;
254+
if value > MAX_VIRTUAL_COLUMN_PATH_STATISTICS {
255+
return Err(ErrorCode::TableOptionInvalid(format!(
256+
"{FUSE_OPT_KEY_VIRTUAL_COLUMN_MAX_PATH_STATISTICS} must be between 0 and {MAX_VIRTUAL_COLUMN_PATH_STATISTICS}",
257+
)));
258+
}
259+
}
260+
Ok(())
261+
}
262+
233263
pub fn is_valid_create_opt<S: AsRef<str>>(opt_key: S, engine: &Engine) -> bool {
234264
let opt_key = opt_key.as_ref().to_lowercase();
235265
let opt_key = opt_key.as_str();

src/query/service/src/interpreters/interpreter_table_create.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -92,6 +92,7 @@ use crate::interpreters::common::table_option_validation::is_valid_option_of_typ
9292
use crate::interpreters::common::table_option_validation::is_valid_random_seed;
9393
use crate::interpreters::common::table_option_validation::is_valid_recluster_depth;
9494
use crate::interpreters::common::table_option_validation::is_valid_row_per_block;
95+
use crate::interpreters::common::table_option_validation::is_valid_virtual_column_layout_options;
9596
use crate::interpreters::hook::vacuum_hook::hook_clear_m_cte_temp_table;
9697
use crate::interpreters::hook::vacuum_hook::hook_disk_temp_dir;
9798
use crate::interpreters::hook::vacuum_hook::hook_vacuum_temp_files;
@@ -564,6 +565,7 @@ impl CreateTableInterpreter {
564565
is_valid_fuse_parquet_dictionary_opt(&table_meta.options)?;
565566
// check enable_virtual_column
566567
is_valid_fuse_virtual_column_opt(&table_meta.options)?;
568+
is_valid_virtual_column_layout_options(&table_meta.options)?;
567569
is_valid_data_page_rows(&table_meta.options)?;
568570
is_valid_data_page_bytes(&table_meta.options)?;
569571
is_valid_analyze_histogram_algorithm(&table_meta.options)?;

0 commit comments

Comments
 (0)