Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
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
35 changes: 23 additions & 12 deletions crates/integrations/datafusion/src/procedures.rs
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@
//! - `CALL sys.create_global_index(table => '...', index_column => '...', index_type => 'btree')`
//! - `CALL sys.create_global_index(table => '...', index_column => '...', index_type => 'bitmap')`
//! - `CALL sys.create_global_index(table => '...', index_column => '...', index_type => 'ivf-pq')`
//! - `CALL sys.drop_global_index(table => '...', index_column => '...', index_type => 'btree')` (also 'bitmap', 'lumina', or a vindex type such as 'ivf-pq')
//! - `CALL sys.drop_global_index(table => '...', index_column => '...', index_type => 'btree')` (also 'bitmap', 'lumina', or a vindex type such as 'ivf-pq'; optional `dry_run => true` previews the matched count without committing)
//! - `CALL sys.create_lumina_index(table => '...', index_column => '...')`

use std::collections::HashMap;
Expand Down Expand Up @@ -599,17 +599,27 @@ async fn proc_drop_global_index(
"drop_global_index partitions are not supported yet".to_string(),
));
}
if args.contains_key("dry_run") {
return Err(DataFusionError::NotImplemented(
"drop_global_index dry_run is not supported yet".to_string(),
));
}
let dry_run = match args.get("dry_run") {
None => false,
Some(v) if v.eq_ignore_ascii_case("true") => true,
Some(v) if v.eq_ignore_ascii_case("false") => false,
Some(v) => {
return Err(DataFusionError::Plan(format!(
"drop_global_index dry_run must be 'true' or 'false', got '{v}'"
)));
}
};

let mut builder = table.new_global_index_drop_builder();
builder.with_index_column(index_column);
builder.with_index_type(index_type);
builder.execute().await.map_err(to_datafusion_error)?;
ok_result(ctx)
builder.with_dry_run(dry_run);
let matched = builder.execute().await.map_err(to_datafusion_error)?;
if dry_run {
message_result(ctx, format!("Would drop {matched} global index file(s)"))
} else {
ok_result(ctx)
}
}

fn is_sorted_global_index_type(index_type: &str) -> bool {
Expand All @@ -636,15 +646,16 @@ fn parse_key_value_options(options: &str) -> DFResult<HashMap<String, String>> {
}

fn ok_result(ctx: &SessionContext) -> DFResult<DataFrame> {
message_result(ctx, "OK".to_string())
}

fn message_result(ctx: &SessionContext, message: String) -> DFResult<DataFrame> {
let schema = Arc::new(Schema::new(vec![Field::new(
"result",
ArrowDataType::Utf8,
false,
)]));
let batch = RecordBatch::try_new(
schema.clone(),
vec![Arc::new(StringArray::from(vec!["OK"]))],
)?;
let batch = RecordBatch::try_new(schema, vec![Arc::new(StringArray::from(vec![message]))])?;
ctx.read_batch(batch)
}

Expand Down
54 changes: 54 additions & 0 deletions crates/integrations/datafusion/tests/procedures.rs
Original file line number Diff line number Diff line change
Expand Up @@ -325,6 +325,60 @@ async fn test_drop_global_index_removes_btree_and_reads_fallback() {
assert_eq!(rows, vec![(2, "bob".to_string()), (3, "carol".to_string())]);
}

#[tokio::test]
async fn test_drop_global_index_dry_run_previews_without_removing() {
let (_tmp, sql_context) = setup_btree_global_index_table("btree_dry_run").await;
exec(
&sql_context,
"INSERT INTO paimon.test_db.btree_dry_run (id, name) VALUES (1, 'alice'), (2, 'bob')",
)
.await;
exec(
&sql_context,
"CALL sys.create_global_index(table => 'test_db.btree_dry_run', index_column => 'id')",
)
.await;

// dry-run: must NOT remove the index.
exec(
&sql_context,
"CALL sys.drop_global_index(table => 'test_db.btree_dry_run', index_column => 'id', index_type => 'btree', dry_run => true)",
)
.await;
let after_dry = row_count(
&sql_context,
"SELECT * FROM paimon.test_db.`btree_dry_run$table_indexes` \
WHERE index_type = 'btree' AND index_field_name = 'id'",
)
.await;
assert_eq!(after_dry, 1);

// real drop removes it.
exec(
&sql_context,
"CALL sys.drop_global_index(table => 'test_db.btree_dry_run', index_column => 'id', index_type => 'btree')",
)
.await;
let after_real = row_count(
&sql_context,
"SELECT * FROM paimon.test_db.`btree_dry_run$table_indexes` \
WHERE index_type = 'btree' AND index_field_name = 'id'",
)
.await;
assert_eq!(after_real, 0);
}

#[tokio::test]
async fn test_drop_global_index_rejects_invalid_dry_run() {
let (_tmp, sql_context) = setup_btree_global_index_table("btree_bad_dry_run").await;
assert_sql_error(
&sql_context,
"CALL sys.drop_global_index(table => 'test_db.btree_bad_dry_run', index_column => 'id', dry_run => 'maybe')",
"dry_run",
)
.await;
}

#[tokio::test]
async fn test_drop_global_index_removes_bitmap() {
let (_tmp, sql_context) = setup_btree_global_index_table("bitmap_drop").await;
Expand Down
Loading
Loading