Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
20 commits
Select commit Hold shift + click to select a range
1c5c7f6
feat(fuse): organize data by partition key
KKould Jul 12, 2026
a9fc817
feat(fuse): prune segments by partition values
KKould Jul 13, 2026
454c918
fix(fuse): preserve partitioned update statistics
KKould Jul 14, 2026
d19ad30
test(fuse): stabilize partition pruning assertions
KKould Jul 15, 2026
ae141fe
test(fuse): cover partition-aware recluster
KKould Jul 15, 2026
3cc8d45
fix(query): emit replayable partitioned table DDL
KKould Jul 15, 2026
dc10b08
fix(ast): preserve Iceberg partition syntax
KKould Jul 15, 2026
eae9496
fix(query): validate partition table options
KKould Jul 16, 2026
b872a8c
perf(fuse): scan partition boundaries by column type
KKould Jul 16, 2026
4ebc9d2
test(fuse): document fragmented partition writes
KKould Jul 16, 2026
d006b32
test(fuse): avoid topology-dependent segment count
KKould Jul 16, 2026
ffb1a09
Merge remote-tracking branch 'upstream/main' into feat/fuse-partition-by
KKould Jul 22, 2026
252913f
fix(fuse): prune day partitions from timestamp ranges
KKould Jul 22, 2026
53184f9
ci(sqllogic): update partition cluster expectation
KKould Jul 22, 2026
3988409
fix(fuse): flush final partition segment
KKould Jul 23, 2026
3e74585
Merge remote-tracking branch 'upstream/main' into feat/fuse-partition-by
KKould Jul 23, 2026
4beaa7a
ci(sqllogic): avoid topology-dependent block count
KKould Jul 23, 2026
073e2e5
test(fuse): add missing coverage for PARTITION BY
KKould Jul 24, 2026
48d21e0
ci(sqllogic): isolate unrelated partition filter
KKould Jul 24, 2026
0ee67ad
feat(fuse): add hash-distributed partition writes
KKould Jul 28, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -355,6 +355,7 @@ memchr = { version = "2", default-features = false }
micromarshal = "0.7.0"
mlua = { version = "0.11", features = ["lua54", "vendored", "async", "serialize"] }
mockall = "0.11.2"
murmur3 = "0.5.2"
mysql_async = { version = "0.34", default-features = false, features = ["native-tls-tls"] }
naive-cityhash = "0.2.0"
ndarray = "0.15.6"
Expand Down
14 changes: 7 additions & 7 deletions src/query/ast/src/ast/statements/table.rs
Original file line number Diff line number Diff line change
Expand Up @@ -162,7 +162,7 @@ pub struct CreateTableStmt {
pub uri_location: Option<UriLocation>,
pub cluster_by: Option<ClusterOption>,
pub table_options: BTreeMap<String, String>,
pub iceberg_table_partition: Option<Vec<Identifier>>,
pub partition_by: Option<Vec<Expr>>,
pub table_properties: Option<BTreeMap<String, String>>,
pub as_query: Option<Box<Query>>,
pub table_type: TableType,
Expand Down Expand Up @@ -211,6 +211,12 @@ impl Display for CreateTableStmt {
write!(f, " {uri_location}")?;
}

if let Some(partition_by) = &self.partition_by {
write!(f, " PARTITION BY(")?;
write_comma_separated_list(f, partition_by)?;
write!(f, ")")?;
}

if let Some(cluster_by) = &self.cluster_by {
write!(f, " {cluster_by}")?;
}
Expand All @@ -221,12 +227,6 @@ impl Display for CreateTableStmt {
write_space_separated_string_map(f, &self.table_options)?;
}

if let Some(iceberg_table_partition) = &self.iceberg_table_partition {
write!(f, " PARTITION BY(")?;
write_comma_separated_list(f, iceberg_table_partition)?;
write!(f, ")")?;
}

if let Some(table_properties) = &self.table_properties {
write!(f, " PROPERTIES(")?;
write_space_separated_string_map(f, table_properties)?;
Expand Down
19 changes: 14 additions & 5 deletions src/query/ast/src/parser/statement.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1131,16 +1131,22 @@ pub fn statement_body(i: Input) -> IResult<Statement> {
})
},
);
let create_table_partition_by = map(
rule! {
#table_option ~ PARTITION ~ ^BY ~ ^"(" ~ ^#comma_separated_list1(expr) ~ ^")"
},
|(table_options, _, _, _, exprs, _)| (table_options, exprs),
);
let create_table = map_res(
rule! {
CREATE ~ ( OR ~ ^REPLACE )? ~ (TEMP| TEMPORARY|TRANSIENT)? ~ TABLE ~ ( IF ~ ^NOT ~ ^EXISTS )?
~ #dot_separated_idents_1_to_3
~ #create_table_source?
~ ( #engine )?
~ ( #uri_location )?
~ #create_table_partition_by?
~ ( CLUSTER ~ ^BY ~ LINEAR? ~ ^"(" ~ ^#comma_separated_list1(expr) ~ ^")" )?
~ ( #table_option )?
~ ( PARTITION ~ ^BY ~ ^"(" ~ ^#comma_separated_list1(ident) ~ ^")" )?
~ ( PROPERTIES ~ #connection_options )?
~ ( AS ~ ^#query )?
},
Expand All @@ -1154,9 +1160,9 @@ pub fn statement_body(i: Input) -> IResult<Statement> {
source,
engine,
uri_location,
opt_partition_by,
opt_cluster_by,
opt_table_options,
opt_iceberg_table_partition_by,
opt_table_properties,
opt_as_query,
)| {
Expand All @@ -1168,6 +1174,10 @@ pub fn statement_body(i: Input) -> IResult<Statement> {
Some(TEMP) | Some(TEMPORARY) => TableType::Temporary,
_ => unreachable!(),
};
let (mut table_options, partition_by) = opt_partition_by
.map(|(options, exprs)| (options, Some(exprs)))
.unwrap_or_default();
table_options.extend(opt_table_options.unwrap_or_default());
Ok(Statement::CreateTable(CreateTableStmt {
create_option,
catalog,
Expand All @@ -1179,9 +1189,8 @@ pub fn statement_body(i: Input) -> IResult<Statement> {
cluster_by: opt_cluster_by.map(|(_, _, _, _, exprs, _)| ClusterOption {
cluster_exprs: exprs,
}),
table_options: opt_table_options.unwrap_or_default(),
iceberg_table_partition: opt_iceberg_table_partition_by
.map(|(_, _, _, cols, _)| cols),
table_options,
partition_by,
table_properties: opt_table_properties.map(|(_, properties)| properties),
as_query: opt_as_query.map(|(_, query)| Box::new(query)),
table_type,
Expand Down
12 changes: 6 additions & 6 deletions src/query/ast/src/visit/statement_table.rs
Original file line number Diff line number Diff line change
Expand Up @@ -149,9 +149,9 @@ impl Walk for CreateTableStmt {
if let Some(cluster_by) = &self.cluster_by {
try_walk!(cluster_by.walk(visitor));
}
if let Some(partitions) = &self.iceberg_table_partition {
for ident in partitions {
try_walk!(ident.walk(visitor));
if let Some(partitions) = &self.partition_by {
for expr in partitions {
try_walk!(expr.walk(visitor));
}
}
if let Some(as_query) = &self.as_query {
Expand All @@ -173,9 +173,9 @@ impl WalkMut for CreateTableStmt {
if let Some(cluster_by) = &mut self.cluster_by {
try_walk!(cluster_by.walk_mut(visitor));
}
if let Some(partitions) = &mut self.iceberg_table_partition {
for ident in partitions {
try_walk!(ident.walk_mut(visitor));
if let Some(partitions) = &mut self.partition_by {
for expr in partitions {
try_walk!(expr.walk_mut(visitor));
}
}
if let Some(as_query) = &mut self.as_query {
Expand Down
26 changes: 26 additions & 0 deletions src/query/ast/tests/it/parser.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1393,6 +1393,32 @@ fn test_file_format_trim_space_option() {
assert!(displayed.contains("TRIM_SPACE = true".to_uppercase().as_str()));
}

#[test]
fn test_create_table_options_before_partition_by() {
let cases = [
"CREATE TABLE t(c INT) ENGINE=ICEBERG LOCATION='s3://bucket/path' CONNECTION_NAME='conn' PARTITION BY (c)",
"CREATE TABLE iceberg.db.t(c INT) LOCATION='s3://bucket/path' PARTITION BY (c)",
"CREATE TABLE t(a INT) ENGINE=FUSE ROW_PER_BLOCK=1 PARTITION BY (a)",
"CREATE TABLE t(a INT) ROW_PER_BLOCK=1 PARTITION BY (a)",
];
for sql in cases {
let tokens = tokenize_sql(sql).unwrap();
let (stmt, _) = parse_sql(&tokens, Dialect::PostgreSQL).unwrap();

let displayed = stmt.to_string();
let displayed_uppercase = displayed.to_uppercase();
let partition_pos = displayed_uppercase.find("PARTITION BY").unwrap();
for option in ["LOCATION", "CONNECTION_NAME", "ROW_PER_BLOCK"] {
if let Some(option_pos) = displayed_uppercase.find(option) {
assert!(partition_pos < option_pos);
}
}

let tokens = tokenize_sql(&displayed).unwrap();
parse_sql(&tokens, Dialect::PostgreSQL).unwrap();
}
}

#[test]
fn test_stage_local_filesystem_uri_errors() {
let cases = [
Expand Down
6 changes: 4 additions & 2 deletions src/query/ast/tests/it/testdata/stmt-error.txt
Original file line number Diff line number Diff line change
Expand Up @@ -94,8 +94,10 @@ error:
--> SQL:1:55
|
1 | create table a (c1 decimal(38), c2 int) partition by ();
| ------ ^ unexpected `)`, expecting <Ident>, <LiteralString>, or `IDENTIFIER`
| |
| ------ ^
| | |
| | expecting `<LiteralString>`, '<LiteralCodeString>', '<LiteralInteger>', '<LiteralFloat>', 'TRUE', 'FALSE', or more ...
| | while parsing expression
| while parsing `CREATE [OR REPLACE] TABLE [IF NOT EXISTS] [<database>.]<table> [<source>] [<table_options>]`


Expand Down
Loading