mod common;
use common::{exec, row_count, setup_sql_context};
async fn setup_table_with_snapshots() -> (tempfile::TempDir, paimon_datafusion::SQLContext) {
let (tmp, sql_context) = setup_sql_context().await;
exec(
&sql_context,
"CREATE TABLE paimon.test_db.t1 (id INT, name VARCHAR(100), PRIMARY KEY (id))",
)
.await;
exec(
&sql_context,
"INSERT INTO paimon.test_db.t1 VALUES (1, 'alice')",
)
.await;
exec(
&sql_context,
"INSERT INTO paimon.test_db.t1 VALUES (2, 'bob')",
)
.await;
exec(
&sql_context,
"INSERT INTO paimon.test_db.t1 VALUES (3, 'charlie')",
)
.await;
(tmp, sql_context)
}
#[tokio::test]
async fn test_create_tag() {
let (_tmp, sql_context) = setup_table_with_snapshots().await;
exec(
&sql_context,
"CALL sys.create_tag(table => 'test_db.t1', tag => 'v1')",
)
.await;
let count = row_count(
&sql_context,
"SELECT * FROM paimon.test_db.`t1$tags` WHERE tag_name = 'v1'",
)
.await;
assert_eq!(count, 1);
}
#[tokio::test]
async fn test_create_tag_with_snapshot_id() {
let (_tmp, sql_context) = setup_table_with_snapshots().await;
exec(
&sql_context,
"CALL sys.create_tag(table => 'test_db.t1', tag => 'v1', snapshot_id => '1')",
)
.await;
let count = row_count(
&sql_context,
"SELECT * FROM paimon.test_db.`t1$tags` WHERE tag_name = 'v1'",
)
.await;
assert_eq!(count, 1);
}
#[tokio::test]
async fn test_create_tag_already_exists() {
let (_tmp, sql_context) = setup_table_with_snapshots().await;
exec(
&sql_context,
"CALL sys.create_tag(table => 'test_db.t1', tag => 'v1')",
)
.await;
let result = sql_context
.sql("CALL sys.create_tag(table => 'test_db.t1', tag => 'v1')")
.await;
assert!(result.is_err());
assert!(result.unwrap_err().to_string().contains("already exists"));
}
#[tokio::test]
async fn test_delete_tag() {
let (_tmp, sql_context) = setup_table_with_snapshots().await;
exec(
&sql_context,
"CALL sys.create_tag(table => 'test_db.t1', tag => 'v1')",
)
.await;
exec(
&sql_context,
"CALL sys.delete_tag(table => 'test_db.t1', tag => 'v1')",
)
.await;
let count = row_count(
&sql_context,
"SELECT * FROM paimon.test_db.`t1$tags` WHERE tag_name = 'v1'",
)
.await;
assert_eq!(count, 0);
}
#[tokio::test]
async fn test_delete_multiple_tags() {
let (_tmp, sql_context) = setup_table_with_snapshots().await;
exec(
&sql_context,
"CALL sys.create_tag(table => 'test_db.t1', tag => 'v1')",
)
.await;
exec(
&sql_context,
"CALL sys.create_tag(table => 'test_db.t1', tag => 'v2', snapshot_id => '1')",
)
.await;
exec(
&sql_context,
"CALL sys.delete_tag(table => 'test_db.t1', tag => 'v1,v2')",
)
.await;
let count = row_count(&sql_context, "SELECT * FROM paimon.test_db.`t1$tags`").await;
assert_eq!(count, 0);
}
#[tokio::test]
async fn test_rollback_to_snapshot() {
let (_tmp, sql_context) = setup_table_with_snapshots().await;
exec(
&sql_context,
"CALL sys.rollback_to(table => 'test_db.t1', snapshot_id => '1')",
)
.await;
let count = row_count(&sql_context, "SELECT * FROM paimon.test_db.t1").await;
assert_eq!(count, 1);
}
#[tokio::test]
async fn test_rollback_to_tag() {
let (_tmp, sql_context) = setup_table_with_snapshots().await;
exec(
&sql_context,
"CALL sys.create_tag(table => 'test_db.t1', tag => 'v1', snapshot_id => '1')",
)
.await;
exec(
&sql_context,
"CALL sys.rollback_to(table => 'test_db.t1', tag => 'v1')",
)
.await;
let count = row_count(&sql_context, "SELECT * FROM paimon.test_db.t1").await;
assert_eq!(count, 1);
}
#[tokio::test]
async fn test_rollback_to_timestamp() {
let (_tmp, sql_context) = setup_table_with_snapshots().await;
let batches = sql_context
.sql("SELECT snapshot_id, commit_time FROM paimon.test_db.`t1$snapshots` ORDER BY snapshot_id")
.await
.unwrap()
.collect()
.await
.unwrap();
let snap1_time = batches[0]
.column_by_name("commit_time")
.unwrap()
.as_any()
.downcast_ref::<datafusion::arrow::array::TimestampMillisecondArray>()
.unwrap()
.value(0);
exec(
&sql_context,
&format!(
"CALL sys.rollback_to_timestamp(table => 'test_db.t1', timestamp => '{snap1_time}')"
),
)
.await;
let count = row_count(&sql_context, "SELECT * FROM paimon.test_db.t1").await;
assert_eq!(count, 1);
}
#[tokio::test]
async fn test_create_tag_from_timestamp() {
let (_tmp, sql_context) = setup_table_with_snapshots().await;
let batches = sql_context
.sql("SELECT snapshot_id, commit_time FROM paimon.test_db.`t1$snapshots` ORDER BY snapshot_id")
.await
.unwrap()
.collect()
.await
.unwrap();
let snap2_time = batches[0]
.column_by_name("commit_time")
.unwrap()
.as_any()
.downcast_ref::<datafusion::arrow::array::TimestampMillisecondArray>()
.unwrap()
.value(1);
exec(
&sql_context,
&format!(
"CALL sys.create_tag_from_timestamp(table => 'test_db.t1', tag => 'ts_tag', timestamp => '{snap2_time}')"
),
)
.await;
let count = row_count(
&sql_context,
"SELECT * FROM paimon.test_db.`t1$tags` WHERE tag_name = 'ts_tag'",
)
.await;
assert_eq!(count, 1);
}
#[tokio::test]
async fn test_rollback_cleans_newer_tags() {
let (_tmp, sql_context) = setup_table_with_snapshots().await;
exec(
&sql_context,
"CALL sys.create_tag(table => 'test_db.t1', tag => 'v2', snapshot_id => '2')",
)
.await;
exec(
&sql_context,
"CALL sys.create_tag(table => 'test_db.t1', tag => 'v3', snapshot_id => '3')",
)
.await;
exec(
&sql_context,
"CALL sys.rollback_to(table => 'test_db.t1', snapshot_id => '1')",
)
.await;
let count = row_count(&sql_context, "SELECT * FROM paimon.test_db.`t1$tags`").await;
assert_eq!(count, 0);
}