2424import org .apache .doris .datasource .ExternalTable ;
2525import org .apache .doris .datasource .NameMapping ;
2626import org .apache .doris .datasource .iceberg .helper .IcebergWriterHelper ;
27- import org .apache .doris .nereids .trees .plans .commands .insert .BaseExternalTableInsertCommandContext ;
27+ import org .apache .doris .nereids .trees .plans .commands .insert .IcebergInsertCommandContext ;
2828import org .apache .doris .nereids .trees .plans .commands .insert .InsertCommandContext ;
2929import org .apache .doris .thrift .TIcebergCommitData ;
3030import org .apache .doris .thrift .TUpdateMode ;
3838import org .apache .iceberg .OverwriteFiles ;
3939import org .apache .iceberg .PartitionSpec ;
4040import org .apache .iceberg .ReplacePartitions ;
41+ import org .apache .iceberg .SnapshotRef ;
4142import org .apache .iceberg .Table ;
4243import org .apache .iceberg .io .CloseableIterable ;
4344import org .apache .iceberg .io .WriteResult ;
@@ -61,6 +62,9 @@ public class IcebergTransaction implements Transaction {
6162 private org .apache .iceberg .Transaction transaction ;
6263 private final List <TIcebergCommitData > commitDataList = Lists .newArrayList ();
6364
65+ private IcebergInsertCommandContext insertCtx ;
66+ private String branchName ;
67+
6468 public IcebergTransaction (IcebergMetadataOps ops ) {
6569 this .ops = ops ;
6670 }
@@ -71,29 +75,43 @@ public void updateIcebergCommitData(List<TIcebergCommitData> commitDataList) {
7175 }
7276 }
7377
74- public void beginInsert (ExternalTable dorisTable ) throws UserException {
78+ public void beginInsert (ExternalTable dorisTable , Optional <InsertCommandContext > ctx ) throws UserException {
79+ ctx .ifPresent (c -> this .insertCtx = (IcebergInsertCommandContext ) c );
7580 try {
7681 ops .getPreExecutionAuthenticator ().execute (() -> {
7782 // create and start the iceberg transaction
7883 this .table = IcebergUtils .getIcebergTable (dorisTable );
84+ // check branch
85+ if (insertCtx != null && insertCtx .getBranchName ().isPresent ()) {
86+ this .branchName = insertCtx .getBranchName ().get ();
87+ SnapshotRef branchRef = table .refs ().get (branchName );
88+ if (branchRef == null ) {
89+ throw new RuntimeException (branchName + " is not founded in " + dorisTable .getName ());
90+ } else if (!branchRef .isBranch ()) {
91+ throw new RuntimeException (
92+ branchName
93+ + " is a tag, not a branch. Tags cannot be targets for producing snapshots" );
94+ }
95+ }
7996 this .transaction = table .newTransaction ();
8097 });
8198 } catch (Exception e ) {
82- throw new UserException ("Failed to begin insert for iceberg table " + dorisTable .getName (), e );
99+ throw new UserException ("Failed to begin insert for iceberg table " + dorisTable .getName ()
100+ + "because: " + e .getMessage (), e );
83101 }
84102
85103 }
86104
87- public void finishInsert (NameMapping nameMapping , Optional < InsertCommandContext > insertCtx ) {
105+ public void finishInsert (NameMapping nameMapping ) {
88106 if (LOG .isDebugEnabled ()) {
89107 LOG .info ("iceberg table {} insert table finished!" , nameMapping .getFullLocalName ());
90108 }
91109 try {
92110 ops .getPreExecutionAuthenticator ().execute (() -> {
93111 //create and start the iceberg transaction
94112 TUpdateMode updateMode = TUpdateMode .APPEND ;
95- if (insertCtx . isPresent () ) {
96- updateMode = (( BaseExternalTableInsertCommandContext ) insertCtx . get ()) .isOverwrite ()
113+ if (insertCtx != null ) {
114+ updateMode = insertCtx .isOverwrite ()
97115 ? TUpdateMode .OVERWRITE
98116 : TUpdateMode .APPEND ;
99117 }
@@ -146,6 +164,9 @@ public long getUpdateCnt() {
146164 private void commitAppendTxn (List <WriteResult > pendingResults ) {
147165 // commit append files.
148166 AppendFiles appendFiles = transaction .newAppend ().scanManifestsWith (ops .getThreadPoolWithPreAuth ());
167+ if (branchName != null ) {
168+ appendFiles = appendFiles .toBranch (branchName );
169+ }
149170 for (WriteResult result : pendingResults ) {
150171 Preconditions .checkState (result .referencedDataFiles ().length == 0 ,
151172 "Should have no referenced data files for append." );
@@ -161,11 +182,14 @@ private void commitReplaceTxn(List<WriteResult> pendingResults) {
161182 // 1. if dst_tb is a partitioned table, it will return directly.
162183 // 2. if dst_tb is an unpartitioned table, the `dst_tb` table will be emptied.
163184 if (!transaction .table ().spec ().isPartitioned ()) {
164- OverwriteFiles overwriteFiles = transaction
165- .newOverwrite ()
166- .scanManifestsWith (ops .getThreadPoolWithPreAuth ());
185+ OverwriteFiles overwriteFiles = transaction .newOverwrite ();
186+ if (branchName != null ) {
187+ overwriteFiles = overwriteFiles .toBranch (branchName );
188+ }
189+ overwriteFiles = overwriteFiles .scanManifestsWith (ops .getThreadPoolWithPreAuth ());
167190 try (CloseableIterable <FileScanTask > fileScanTasks = table .newScan ().planFiles ()) {
168- fileScanTasks .forEach (f -> overwriteFiles .deleteFile (f .file ()));
191+ OverwriteFiles finalOverwriteFiles = overwriteFiles ;
192+ fileScanTasks .forEach (f -> finalOverwriteFiles .deleteFile (f .file ()));
169193 } catch (IOException e ) {
170194 throw new RuntimeException (e );
171195 }
@@ -175,9 +199,11 @@ private void commitReplaceTxn(List<WriteResult> pendingResults) {
175199 }
176200
177201 // commit replace partitions
178- ReplacePartitions appendPartitionOp = transaction
179- .newReplacePartitions ()
180- .scanManifestsWith (ops .getThreadPoolWithPreAuth ());
202+ ReplacePartitions appendPartitionOp = transaction .newReplacePartitions ();
203+ if (branchName != null ) {
204+ appendPartitionOp = appendPartitionOp .toBranch (branchName );
205+ }
206+ appendPartitionOp = appendPartitionOp .scanManifestsWith (ops .getThreadPoolWithPreAuth ());
181207 for (WriteResult result : pendingResults ) {
182208 Preconditions .checkState (result .referencedDataFiles ().length == 0 ,
183209 "Should have no referenced data files." );
0 commit comments