This is an automated email from the ASF dual-hosted git repository.
ayushtkn pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/hive.git
The following commit(s) were added to refs/heads/master by this push:
new ad791694626 HIVE-29815: Iceberg: [V3] Support ROW LINEAGE in
Copy-On-Write DELETE operations (#6698)
ad791694626 is described below
commit ad791694626f970727b1bcff76a784dcd7fad5c9
Author: Raghav Aggarwal <[email protected]>
AuthorDate: Wed Sep 9 20:32:38 2026 +0530
HIVE-29815: Iceberg: [V3] Support ROW LINEAGE in Copy-On-Write DELETE
operations (#6698)
---
.../src/test/queries/positive/row_lineage.q | 58 +++++++-
.../src/test/results/positive/row_lineage.q.out | 155 +++++++++++++++++++++
.../parse/rewrite/CopyOnWriteDeleteRewriter.java | 9 +-
3 files changed, 220 insertions(+), 2 deletions(-)
diff --git a/iceberg/iceberg-handler/src/test/queries/positive/row_lineage.q
b/iceberg/iceberg-handler/src/test/queries/positive/row_lineage.q
index 8e44b8a5ef6..b730a92d5c0 100644
--- a/iceberg/iceberg-handler/src/test/queries/positive/row_lineage.q
+++ b/iceberg/iceberg-handler/src/test/queries/positive/row_lineage.q
@@ -77,4 +77,60 @@ WHEN MATCHED THEN
SELECT id, data, ROW__LINEAGE__ID, LAST__UPDATED__SEQUENCE__NUMBER
FROM ice_merge_cow
-ORDER BY ROW__LINEAGE__ID;
\ No newline at end of file
+ORDER BY ROW__LINEAGE__ID;
+
+-- cow delete
+CREATE TABLE ice_cow_delete (
+ id INT,
+ data STRING
+)
+STORED BY iceberg
+TBLPROPERTIES ('format-version'='3', 'write.delete.mode'='copy-on-write');
+
+INSERT INTO ice_cow_delete VALUES
+ (1, 'apple'),
+ (2, 'banana'),
+ (3, 'cherry');
+
+SELECT id, data, ROW__LINEAGE__ID, LAST__UPDATED__SEQUENCE__NUMBER
+FROM ice_cow_delete
+ORDER BY id;
+
+DELETE FROM ice_cow_delete WHERE id = 2;
+
+SELECT id, data, ROW__LINEAGE__ID, LAST__UPDATED__SEQUENCE__NUMBER
+FROM ice_cow_delete
+ORDER BY id;
+
+-- cow delete partitioned
+CREATE TABLE ice_cow_delete_part (
+ id INT,
+ data STRING
+)
+PARTITIONED BY (part STRING)
+STORED BY iceberg
+TBLPROPERTIES ('format-version'='3', 'write.delete.mode'='copy-on-write');
+
+-- Snapshot 1: Sequence 1
+INSERT INTO ice_cow_delete_part VALUES
+ (1, 'apple', 'p1'),
+ (2, 'banana', 'p1'),
+ (3, 'cherry', 'p2'),
+ (4, 'date', 'p2');
+
+-- Snapshot 2: Sequence 2 (Adding more data to partition p1 to mix sequence
numbers)
+INSERT INTO ice_cow_delete_part VALUES
+ (5, 'elderberry', 'p1');
+
+SELECT id, data, part, ROW__LINEAGE__ID, LAST__UPDATED__SEQUENCE__NUMBER
+FROM ice_cow_delete_part
+ORDER BY id;
+
+-- Snapshot 3: Delete across partitions (Affects data files in both p1 and p2)
+DELETE FROM ice_cow_delete_part WHERE id = 2 OR id = 3;
+
+-- id=1 and id=4 should retain Sequence 1
+-- id=5 should retain Sequence 2
+SELECT id, data, part, ROW__LINEAGE__ID, LAST__UPDATED__SEQUENCE__NUMBER
+FROM ice_cow_delete_part
+ORDER BY id;
diff --git
a/iceberg/iceberg-handler/src/test/results/positive/row_lineage.q.out
b/iceberg/iceberg-handler/src/test/results/positive/row_lineage.q.out
index 046576fa9bf..59308a02872 100644
--- a/iceberg/iceberg-handler/src/test/results/positive/row_lineage.q.out
+++ b/iceberg/iceberg-handler/src/test/results/positive/row_lineage.q.out
@@ -314,3 +314,158 @@ POSTHOOK: Output: hdfs://### HDFS PATH ###
1 a 0 1
2 bb_changed 1 2
3 c 2 1
+PREHOOK: query: CREATE TABLE ice_cow_delete (
+ id INT,
+ data STRING
+)
+STORED BY iceberg
+TBLPROPERTIES ('format-version'='3', 'write.delete.mode'='copy-on-write')
+PREHOOK: type: CREATETABLE
+PREHOOK: Output: database:default
+PREHOOK: Output: default@ice_cow_delete
+POSTHOOK: query: CREATE TABLE ice_cow_delete (
+ id INT,
+ data STRING
+)
+STORED BY iceberg
+TBLPROPERTIES ('format-version'='3', 'write.delete.mode'='copy-on-write')
+POSTHOOK: type: CREATETABLE
+POSTHOOK: Output: database:default
+POSTHOOK: Output: default@ice_cow_delete
+PREHOOK: query: INSERT INTO ice_cow_delete VALUES
+ (1, 'apple'),
+ (2, 'banana'),
+ (3, 'cherry')
+PREHOOK: type: QUERY
+PREHOOK: Input: _dummy_database@_dummy_table
+PREHOOK: Output: default@ice_cow_delete
+POSTHOOK: query: INSERT INTO ice_cow_delete VALUES
+ (1, 'apple'),
+ (2, 'banana'),
+ (3, 'cherry')
+POSTHOOK: type: QUERY
+POSTHOOK: Input: _dummy_database@_dummy_table
+POSTHOOK: Output: default@ice_cow_delete
+PREHOOK: query: SELECT id, data, ROW__LINEAGE__ID,
LAST__UPDATED__SEQUENCE__NUMBER
+FROM ice_cow_delete
+ORDER BY id
+PREHOOK: type: QUERY
+PREHOOK: Input: default@ice_cow_delete
+PREHOOK: Output: hdfs://### HDFS PATH ###
+POSTHOOK: query: SELECT id, data, ROW__LINEAGE__ID,
LAST__UPDATED__SEQUENCE__NUMBER
+FROM ice_cow_delete
+ORDER BY id
+POSTHOOK: type: QUERY
+POSTHOOK: Input: default@ice_cow_delete
+POSTHOOK: Output: hdfs://### HDFS PATH ###
+1 apple 0 1
+2 banana 1 1
+3 cherry 2 1
+PREHOOK: query: DELETE FROM ice_cow_delete WHERE id = 2
+PREHOOK: type: QUERY
+PREHOOK: Input: default@ice_cow_delete
+PREHOOK: Output: default@ice_cow_delete
+POSTHOOK: query: DELETE FROM ice_cow_delete WHERE id = 2
+POSTHOOK: type: QUERY
+POSTHOOK: Input: default@ice_cow_delete
+POSTHOOK: Output: default@ice_cow_delete
+PREHOOK: query: SELECT id, data, ROW__LINEAGE__ID,
LAST__UPDATED__SEQUENCE__NUMBER
+FROM ice_cow_delete
+ORDER BY id
+PREHOOK: type: QUERY
+PREHOOK: Input: default@ice_cow_delete
+PREHOOK: Output: hdfs://### HDFS PATH ###
+POSTHOOK: query: SELECT id, data, ROW__LINEAGE__ID,
LAST__UPDATED__SEQUENCE__NUMBER
+FROM ice_cow_delete
+ORDER BY id
+POSTHOOK: type: QUERY
+POSTHOOK: Input: default@ice_cow_delete
+POSTHOOK: Output: hdfs://### HDFS PATH ###
+1 apple 0 1
+3 cherry 2 1
+PREHOOK: query: CREATE TABLE ice_cow_delete_part (
+ id INT,
+ data STRING
+)
+PARTITIONED BY (part STRING)
+STORED BY iceberg
+TBLPROPERTIES ('format-version'='3', 'write.delete.mode'='copy-on-write')
+PREHOOK: type: CREATETABLE
+PREHOOK: Output: database:default
+PREHOOK: Output: default@ice_cow_delete_part
+POSTHOOK: query: CREATE TABLE ice_cow_delete_part (
+ id INT,
+ data STRING
+)
+PARTITIONED BY (part STRING)
+STORED BY iceberg
+TBLPROPERTIES ('format-version'='3', 'write.delete.mode'='copy-on-write')
+POSTHOOK: type: CREATETABLE
+POSTHOOK: Output: database:default
+POSTHOOK: Output: default@ice_cow_delete_part
+PREHOOK: query: INSERT INTO ice_cow_delete_part VALUES
+ (1, 'apple', 'p1'),
+ (2, 'banana', 'p1'),
+ (3, 'cherry', 'p2'),
+ (4, 'date', 'p2')
+PREHOOK: type: QUERY
+PREHOOK: Input: _dummy_database@_dummy_table
+PREHOOK: Output: default@ice_cow_delete_part
+POSTHOOK: query: INSERT INTO ice_cow_delete_part VALUES
+ (1, 'apple', 'p1'),
+ (2, 'banana', 'p1'),
+ (3, 'cherry', 'p2'),
+ (4, 'date', 'p2')
+POSTHOOK: type: QUERY
+POSTHOOK: Input: _dummy_database@_dummy_table
+POSTHOOK: Output: default@ice_cow_delete_part
+PREHOOK: query: INSERT INTO ice_cow_delete_part VALUES
+ (5, 'elderberry', 'p1')
+PREHOOK: type: QUERY
+PREHOOK: Input: _dummy_database@_dummy_table
+PREHOOK: Output: default@ice_cow_delete_part
+POSTHOOK: query: INSERT INTO ice_cow_delete_part VALUES
+ (5, 'elderberry', 'p1')
+POSTHOOK: type: QUERY
+POSTHOOK: Input: _dummy_database@_dummy_table
+POSTHOOK: Output: default@ice_cow_delete_part
+PREHOOK: query: SELECT id, data, part, ROW__LINEAGE__ID,
LAST__UPDATED__SEQUENCE__NUMBER
+FROM ice_cow_delete_part
+ORDER BY id
+PREHOOK: type: QUERY
+PREHOOK: Input: default@ice_cow_delete_part
+PREHOOK: Output: hdfs://### HDFS PATH ###
+POSTHOOK: query: SELECT id, data, part, ROW__LINEAGE__ID,
LAST__UPDATED__SEQUENCE__NUMBER
+FROM ice_cow_delete_part
+ORDER BY id
+POSTHOOK: type: QUERY
+POSTHOOK: Input: default@ice_cow_delete_part
+POSTHOOK: Output: hdfs://### HDFS PATH ###
+1 apple p1 0 1
+2 banana p1 1 1
+3 cherry p2 2 1
+4 date p2 3 1
+5 elderberry p1 4 2
+PREHOOK: query: DELETE FROM ice_cow_delete_part WHERE id = 2 OR id = 3
+PREHOOK: type: QUERY
+PREHOOK: Input: default@ice_cow_delete_part
+PREHOOK: Output: default@ice_cow_delete_part
+POSTHOOK: query: DELETE FROM ice_cow_delete_part WHERE id = 2 OR id = 3
+POSTHOOK: type: QUERY
+POSTHOOK: Input: default@ice_cow_delete_part
+POSTHOOK: Output: default@ice_cow_delete_part
+PREHOOK: query: SELECT id, data, part, ROW__LINEAGE__ID,
LAST__UPDATED__SEQUENCE__NUMBER
+FROM ice_cow_delete_part
+ORDER BY id
+PREHOOK: type: QUERY
+PREHOOK: Input: default@ice_cow_delete_part
+PREHOOK: Output: hdfs://### HDFS PATH ###
+POSTHOOK: query: SELECT id, data, part, ROW__LINEAGE__ID,
LAST__UPDATED__SEQUENCE__NUMBER
+FROM ice_cow_delete_part
+ORDER BY id
+POSTHOOK: type: QUERY
+POSTHOOK: Input: default@ice_cow_delete_part
+POSTHOOK: Output: hdfs://### HDFS PATH ###
+1 apple p1 0 1
+4 date p2 3 1
+5 elderberry p1 4 2
diff --git
a/ql/src/java/org/apache/hadoop/hive/ql/parse/rewrite/CopyOnWriteDeleteRewriter.java
b/ql/src/java/org/apache/hadoop/hive/ql/parse/rewrite/CopyOnWriteDeleteRewriter.java
index 73bd1db4e6c..22e06234b52 100644
---
a/ql/src/java/org/apache/hadoop/hive/ql/parse/rewrite/CopyOnWriteDeleteRewriter.java
+++
b/ql/src/java/org/apache/hadoop/hive/ql/parse/rewrite/CopyOnWriteDeleteRewriter.java
@@ -30,6 +30,9 @@
import org.apache.hadoop.hive.ql.parse.rewrite.sql.MultiInsertSqlGenerator;
import org.apache.hadoop.hive.ql.parse.rewrite.sql.SqlGeneratorFactory;
+import static
org.apache.hadoop.hive.ql.metadata.RowLineageUtils.addSourceColumnsForRowLineage;
+import static
org.apache.hadoop.hive.ql.metadata.RowLineageUtils.supportsRowLineage;
+
public class CopyOnWriteDeleteRewriter implements Rewriter<DeleteStatement> {
private final HiveConf conf;
@@ -46,6 +49,8 @@ public CopyOnWriteDeleteRewriter(HiveConf conf,
SqlGeneratorFactory sqlGenerator
public ParseUtils.ReparseResult rewrite(Context context, DeleteStatement
deleteBlock)
throws SemanticException {
+ boolean isRowLineageSupported =
supportsRowLineage(deleteBlock.getTargetTable());
+
ASTNode whereTree = deleteBlock.getWhereTree();
String whereClause = "true";
if (whereTree != null) {
@@ -57,7 +62,8 @@ public ParseUtils.ReparseResult rewrite(Context context,
DeleteStatement deleteB
MultiInsertSqlGenerator sqlGenerator =
sqlGeneratorFactory.createSqlGenerator();
- cowWithClauseBuilder.appendWith(sqlGenerator, filePathCol, whereClause);
+ cowWithClauseBuilder.appendWith(
+ sqlGenerator, null, filePathCol, whereClause, true,
isRowLineageSupported, "");
sqlGenerator.append("insert into table ");
sqlGenerator.append(sqlGenerator.getTargetTableFullName());
@@ -66,6 +72,7 @@ public ParseUtils.ReparseResult rewrite(Context context,
DeleteStatement deleteB
sqlGenerator.append(" select ");
sqlGenerator.appendAcidSelectColumns(Context.Operation.DELETE);
sqlGenerator.removeLastChar();
+ addSourceColumnsForRowLineage(isRowLineageSupported, sqlGenerator, "",
conf);
sqlGenerator.append(" from ");
sqlGenerator.append(sqlGenerator.getTargetTableFullName());