This is an automated email from the ASF dual-hosted git repository.

suxiaogang223 pushed a commit to branch feature/paimon-jni-write-v1
in repository https://gitbox.apache.org/repos/asf/doris.git

commit 7a303dbf9c3a0ea244cdd8c4cc4be355f3b899a5
Author: Socrates <[email protected]>
AuthorDate: Wed Jul 8 15:37:50 2026 +0800

    [feature](paimon) Step 6: FE Transaction commit coordinator
    
    Add Paimon transaction support:
    - PaimonTransaction: receives TPaimonCommitMessage from BE,
      deserializes DPCM payloads, executes StreamTableCommit.filterAndCommit()
      for idempotent commit, supports rollback via committer.abort()
    - PaimonTransactionManager + PaimonInsertExecutor: plug into the
      existing transaction framework (same pattern as Iceberg)
    - Wire paimon_commit_messages in Coordinator and LoadProcessor
    - Wire PaimonInsertExecutor in InsertIntoTableCommand
    - Add PAIMON to TransactionType enum
    
    Co-Authored-By: Claude <[email protected]>
---
 .../doris/datasource/paimon/PaimonTransaction.java | 318 +++++++++++++++++++++
 .../commands/insert/InsertIntoTableCommand.java    |  15 +
 .../insert/PaimonInsertCommandContext.java}        |  12 +-
 .../commands/insert/PaimonInsertExecutor.java      |  63 ++++
 .../main/java/org/apache/doris/qe/Coordinator.java |   6 +
 .../org/apache/doris/qe/runtime/LoadProcessor.java |   6 +
 ...tionType.java => PaimonTransactionManager.java} |  22 +-
 .../transaction/TransactionManagerFactory.java     |   5 +
 .../apache/doris/transaction/TransactionType.java  |   3 +-
 9 files changed, 436 insertions(+), 14 deletions(-)

diff --git 
a/fe/fe-core/src/main/java/org/apache/doris/datasource/paimon/PaimonTransaction.java
 
b/fe/fe-core/src/main/java/org/apache/doris/datasource/paimon/PaimonTransaction.java
new file mode 100644
index 00000000000..51f13f93008
--- /dev/null
+++ 
b/fe/fe-core/src/main/java/org/apache/doris/datasource/paimon/PaimonTransaction.java
@@ -0,0 +1,318 @@
+// Licensed to the Apache Software Foundation (ASF) under one
+// or more contributor license agreements.  See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership.  The ASF licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License.  You may obtain a copy of the License at
+//
+//   http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied.  See the License for the
+// specific language governing permissions and limitations
+// under the License.
+
+package org.apache.doris.datasource.paimon;
+
+import org.apache.doris.common.UserException;
+import org.apache.doris.common.security.authentication.ExecutionAuthenticator;
+import org.apache.doris.datasource.mvcc.MvccUtil;
+import 
org.apache.doris.nereids.trees.plans.commands.insert.InsertCommandContext;
+import org.apache.doris.thrift.TPaimonCommitMessage;
+import org.apache.doris.transaction.Transaction;
+
+import com.google.common.collect.Lists;
+import org.apache.logging.log4j.LogManager;
+import org.apache.logging.log4j.Logger;
+import org.apache.paimon.io.DataInputDeserializer;
+import org.apache.paimon.table.InnerTable;
+import org.apache.paimon.table.sink.CommitMessage;
+import org.apache.paimon.table.sink.CommitMessageSerializer;
+import org.apache.paimon.table.sink.StreamTableCommit;
+
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Base64;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Optional;
+import java.util.Set;
+
+/**
+ * Paimon transaction.
+ *
+ * Lifecycle:
+ *   1. beginInsert() — record target table
+ *   2. updateCommitMessages() — called multiple times as BE reports commit 
data
+ *   3. commit() — deserialize all CommitMessages, call 
StreamTableCommit.filterAndCommit()
+ *   4. rollback() — abort uncommitted data files
+ *
+ * CommitMessage wire format:
+ *   BE ← JNI ← Java: byte[] with DPCM header (magic + version + length) + 
Paimon
+ *   CommitMessageSerializer payload
+ */
+public class PaimonTransaction implements Transaction {
+    private static final Logger LOG = 
LogManager.getLogger(PaimonTransaction.class);
+
+    private static final int COMMIT_HEADER_SIZE = 12;
+    private static final byte[] COMMIT_MAGIC = new byte[] {'D', 'P', 'C', 'M'};
+
+    private final PaimonMetadataOps ops;
+    private PaimonExternalTable table;
+    private long transactionId = -1L;
+    private String commitUser = "";
+    private boolean committed = false;
+
+    private final List<byte[]> commitPayloads = Lists.newArrayList();
+    private final Set<String> commitPayloadSet = new HashSet<>();
+
+    public PaimonTransaction(PaimonMetadataOps ops) {
+        this.ops = ops;
+    }
+
+    // ────────────────────────────────────────────────────────────
+    // Transaction lifecycle
+    // ────────────────────────────────────────────────────────────
+
+    public void beginInsert(PaimonExternalTable table, 
Optional<InsertCommandContext> insertCtx) {
+        this.table = table;
+    }
+
+    public void finishInsert(PaimonExternalTable table, 
Optional<InsertCommandContext> insertCtx) {
+    }
+
+    @Override
+    public void commit() throws UserException {
+        List<byte[]> rawPayloads = snapshotPayloads();
+        if (rawPayloads.isEmpty()) {
+            LOG.info("Skip empty PaimonTransaction commit, txnId={}, table={}",
+                    transactionId, tableName());
+            return;
+        }
+        if (table == null) {
+            throw new UserException("Missing paimon table for transaction");
+        }
+        if (transactionId <= 0) {
+            throw new UserException("Missing transaction id for paimon 
commit");
+        }
+
+        try {
+            List<CommitMessage> allMessages = deserializePayloads(rawPayloads);
+            LOG.info("Commit PaimonTransaction, txnId={}, table={}, 
payloads={}, messages={}",
+                    transactionId, tableName(), rawPayloads.size(), 
allMessages.size());
+            if (allMessages.isEmpty()) {
+                throw new RuntimeException(
+                        "Paimon commit messages are empty, payloads=" + 
rawPayloads.size());
+            }
+            doCommit(allMessages);
+            synchronized (this) {
+                committed = true;
+            }
+        } catch (Exception e) {
+            throw new UserException("Failed to commit paimon transaction on 
FE", e);
+        }
+    }
+
+    @Override
+    public void rollback() {
+        if (isCommitted()) {
+            LOG.info("Skip rollback for committed PaimonTransaction, txnId={}, 
table={}",
+                    transactionId, tableName());
+            return;
+        }
+        List<byte[]> rawPayloads = snapshotPayloads();
+        if (rawPayloads.isEmpty()) {
+            LOG.info("Skip empty PaimonTransaction rollback, txnId={}, 
table={}",
+                    transactionId, tableName());
+            return;
+        }
+        if (table == null) {
+            LOG.warn("Skip PaimonTransaction rollback, table missing, 
txnId={}", transactionId);
+            return;
+        }
+        try {
+            List<CommitMessage> allMessages = deserializePayloads(rawPayloads);
+            if (allMessages.isEmpty()) {
+                LOG.info("Skip PaimonTransaction rollback with empty decoded 
messages, "
+                        + "txnId={}, table={}", transactionId, tableName());
+                return;
+            }
+            LOG.info("Rollback PaimonTransaction, txnId={}, table={}, 
payloads={}, messages={}",
+                    transactionId, tableName(), rawPayloads.size(), 
allMessages.size());
+            doAbort(allMessages);
+        } catch (Exception e) {
+            LOG.warn("Failed to rollback PaimonTransaction, txnId={}, 
table={}",
+                    transactionId, tableName(), e);
+        }
+    }
+
+    // ────────────────────────────────────────────────────────────
+    // CommitMessage collection (called from Coordinator)
+    // ────────────────────────────────────────────────────────────
+
+    public void updateCommitMessages(List<TPaimonCommitMessage> messages) {
+        if (messages == null || messages.isEmpty()) {
+            return;
+        }
+        synchronized (this) {
+            for (TPaimonCommitMessage msg : messages) {
+                addPayload(msg);
+            }
+        }
+    }
+
+    private void addPayload(TPaimonCommitMessage message) {
+        if (message == null || !message.isSetPayload()) {
+            return;
+        }
+        byte[] payload = message.getPayload();
+        if (payload == null || payload.length == 0) {
+            return;
+        }
+        String key = Base64.getEncoder().encodeToString(payload);
+        if (commitPayloadSet.add(key)) {
+            commitPayloads.add(Arrays.copyOf(payload, payload.length));
+        }
+    }
+
+    // ────────────────────────────────────────────────────────────
+    // Setters
+    // ────────────────────────────────────────────────────────────
+
+    public void setTransactionId(long transactionId) {
+        this.transactionId = transactionId;
+        this.commitUser = "doris_txn_" + transactionId;
+    }
+
+    public String getCommitUser() {
+        return commitUser;
+    }
+
+    public long getTransactionId() {
+        return transactionId;
+    }
+
+    boolean isCommitted() {
+        synchronized (this) {
+            return committed;
+        }
+    }
+
+    int getPayloadCount() {
+        synchronized (this) {
+            return commitPayloads.size();
+        }
+    }
+
+    // ────────────────────────────────────────────────────────────
+    // Internal commit logic
+    // ────────────────────────────────────────────────────────────
+
+    private void doCommit(List<CommitMessage> msgs) throws Exception {
+        ops.getCatalog();
+        ExecutionAuthenticator authenticator = 
ops.dorisCatalog.getExecutionAuthenticator();
+        authenticator.execute(() -> {
+            InnerTable paimonTable = getInnerTable();
+            StreamTableCommit committer = paimonTable.newCommit(commitUser);
+            try {
+                Map<Long, List<CommitMessage>> commitMap = new HashMap<>();
+                commitMap.put(transactionId, msgs);
+                committer.filterAndCommit(commitMap);
+                return null;
+            } finally {
+                committer.close();
+            }
+        });
+    }
+
+    private void doAbort(List<CommitMessage> msgs) throws Exception {
+        ExecutionAuthenticator authenticator = 
ops.dorisCatalog.getExecutionAuthenticator();
+        authenticator.execute(() -> {
+            InnerTable paimonTable = getInnerTable();
+            StreamTableCommit committer = paimonTable.newCommit(commitUser);
+            try {
+                committer.abort(msgs);
+                return null;
+            } finally {
+                committer.close();
+            }
+        });
+    }
+
+    private InnerTable getInnerTable() {
+        org.apache.paimon.table.Table paimonTable =
+                table.getPaimonTable(MvccUtil.getSnapshotFromContext(table));
+        if (!(paimonTable instanceof InnerTable)) {
+            throw new RuntimeException(
+                    "Paimon table does not support commit: " + 
paimonTable.getClass());
+        }
+        return (InnerTable) paimonTable;
+    }
+
+    // ────────────────────────────────────────────────────────────
+    // Serialization helpers
+    // ────────────────────────────────────────────────────────────
+
+    private List<byte[]> snapshotPayloads() {
+        synchronized (this) {
+            List<byte[]> copy = new ArrayList<>(commitPayloads.size());
+            for (byte[] p : commitPayloads) {
+                copy.add(Arrays.copyOf(p, p.length));
+            }
+            return copy;
+        }
+    }
+
+    static List<CommitMessage> deserializePayloads(List<byte[]> payloads) 
throws IOException {
+        List<CommitMessage> all = new ArrayList<>();
+        for (byte[] payload : payloads) {
+            all.addAll(deserializePayload(payload));
+        }
+        return all;
+    }
+
+    static List<CommitMessage> deserializePayload(byte[] payload) throws 
IOException {
+        if (payload == null || payload.length < COMMIT_HEADER_SIZE || 
!hasMagic(payload)) {
+            throw new IOException("Invalid paimon commit message payload 
header");
+        }
+        int version = readInt(payload, 4);
+        int len = readInt(payload, 8);
+        if (len < 0 || payload.length != COMMIT_HEADER_SIZE + len) {
+            throw new IOException("Invalid paimon commit message payload 
length");
+        }
+        byte[] raw = new byte[len];
+        System.arraycopy(payload, COMMIT_HEADER_SIZE, raw, 0, len);
+        List<CommitMessage> messages =
+                new CommitMessageSerializer().deserializeList(version, new 
DataInputDeserializer(raw));
+        if (messages == null) {
+            throw new IOException("Paimon commit message payload deserialized 
to null");
+        }
+        return messages;
+    }
+
+    private static boolean hasMagic(byte[] payload) {
+        for (int i = 0; i < COMMIT_MAGIC.length; i++) {
+            if (payload[i] != COMMIT_MAGIC[i]) {
+                return false;
+            }
+        }
+        return true;
+    }
+
+    private static int readInt(byte[] payload, int offset) {
+        return ((payload[offset] & 0xFF) << 24)
+                | ((payload[offset + 1] & 0xFF) << 16)
+                | ((payload[offset + 2] & 0xFF) << 8)
+                | (payload[offset + 3] & 0xFF);
+    }
+
+    private String tableName() {
+        return table == null ? "null" : table.getName();
+    }
+}
diff --git 
a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/commands/insert/InsertIntoTableCommand.java
 
b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/commands/insert/InsertIntoTableCommand.java
index 1372cce62e2..d64dc53b59c 100644
--- 
a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/commands/insert/InsertIntoTableCommand.java
+++ 
b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/commands/insert/InsertIntoTableCommand.java
@@ -37,6 +37,7 @@ import org.apache.doris.datasource.doris.RemoteOlapTable;
 import org.apache.doris.datasource.hive.HMSExternalTable;
 import org.apache.doris.datasource.iceberg.IcebergExternalTable;
 import org.apache.doris.datasource.maxcompute.MaxComputeExternalTable;
+import org.apache.doris.datasource.paimon.PaimonExternalTable;
 import org.apache.doris.dictionary.Dictionary;
 import org.apache.doris.load.loadv2.LoadJob;
 import org.apache.doris.load.loadv2.LoadStatistic;
@@ -74,6 +75,7 @@ import 
org.apache.doris.nereids.trees.plans.physical.PhysicalEmptyRelation;
 import org.apache.doris.nereids.trees.plans.physical.PhysicalHiveTableSink;
 import org.apache.doris.nereids.trees.plans.physical.PhysicalIcebergTableSink;
 import 
org.apache.doris.nereids.trees.plans.physical.PhysicalMaxComputeTableSink;
+import org.apache.doris.nereids.trees.plans.physical.PhysicalPaimonTableSink;
 import org.apache.doris.nereids.trees.plans.physical.PhysicalOlapTableSink;
 import org.apache.doris.nereids.trees.plans.physical.PhysicalSink;
 import org.apache.doris.nereids.trees.plans.visitor.PlanVisitor;
@@ -565,6 +567,19 @@ public class InsertIntoTableCommand extends Command 
implements NeedAuditEncrypti
                                 emptyInsert, jobId
                         )
                 );
+            } else if (physicalSink instanceof PhysicalPaimonTableSink) {
+                boolean emptyInsert = childIsEmptyRelation(physicalSink);
+                PaimonExternalTable paimonTable = (PaimonExternalTable) 
targetTableIf;
+                PaimonInsertCommandContext paimonCtx = insertCtx
+                        .map(ctx1 -> (PaimonInsertCommandContext) ctx1)
+                        .orElseGet(PaimonInsertCommandContext::new);
+                return ExecutorFactory.from(
+                        planner,
+                        dataSink,
+                        physicalSink,
+                        () -> new PaimonInsertExecutor(ctx, paimonTable, 
label, planner,
+                                Optional.of(paimonCtx), emptyInsert, jobId)
+                );
             } else if (physicalSink instanceof PhysicalConnectorTableSink) {
                 boolean emptyInsert = childIsEmptyRelation(physicalSink);
                 ExternalTable externalTable = (ExternalTable) targetTableIf;
diff --git 
a/fe/fe-core/src/main/java/org/apache/doris/transaction/TransactionType.java 
b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/commands/insert/PaimonInsertCommandContext.java
similarity index 79%
copy from 
fe/fe-core/src/main/java/org/apache/doris/transaction/TransactionType.java
copy to 
fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/commands/insert/PaimonInsertCommandContext.java
index 83e092c0ed7..f0bf011b793 100644
--- a/fe/fe-core/src/main/java/org/apache/doris/transaction/TransactionType.java
+++ 
b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/commands/insert/PaimonInsertCommandContext.java
@@ -15,12 +15,10 @@
 // specific language governing permissions and limitations
 // under the License.
 
-package org.apache.doris.transaction;
+package org.apache.doris.nereids.trees.plans.commands.insert;
 
-public enum TransactionType {
-    UNKNOWN,
-    HMS,
-    ICEBERG,
-    JDBC,
-    MAXCOMPUTE
+/**
+ * Insert command context for Paimon tables.
+ */
+public class PaimonInsertCommandContext extends 
BaseExternalTableInsertCommandContext {
 }
diff --git 
a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/commands/insert/PaimonInsertExecutor.java
 
b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/commands/insert/PaimonInsertExecutor.java
new file mode 100644
index 00000000000..eb4ce2f3279
--- /dev/null
+++ 
b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/commands/insert/PaimonInsertExecutor.java
@@ -0,0 +1,63 @@
+// Licensed to the Apache Software Foundation (ASF) under one
+// or more contributor license agreements.  See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership.  The ASF licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License.  You may obtain a copy of the License at
+//
+//   http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied.  See the License for the
+// specific language governing permissions and limitations
+// under the License.
+
+package org.apache.doris.nereids.trees.plans.commands.insert;
+
+import org.apache.doris.common.UserException;
+import org.apache.doris.datasource.paimon.PaimonExternalTable;
+import org.apache.doris.datasource.paimon.PaimonTransaction;
+import org.apache.doris.nereids.NereidsPlanner;
+import org.apache.doris.qe.ConnectContext;
+import org.apache.doris.transaction.TransactionType;
+
+import org.apache.logging.log4j.LogManager;
+import org.apache.logging.log4j.Logger;
+
+import java.util.Optional;
+
+/**
+ * Insert executor for Paimon tables.
+ */
+public class PaimonInsertExecutor extends BaseExternalTableInsertExecutor {
+    private static final Logger LOG = 
LogManager.getLogger(PaimonInsertExecutor.class);
+
+    public PaimonInsertExecutor(ConnectContext ctx, PaimonExternalTable table,
+            String labelName, NereidsPlanner planner,
+            Optional<InsertCommandContext> insertCtx,
+            boolean emptyInsert, long jobId) {
+        super(ctx, table, labelName, planner, insertCtx, emptyInsert, jobId);
+    }
+
+    @Override
+    protected void beforeExec() throws UserException {
+        PaimonTransaction transaction =
+                (PaimonTransaction) transactionManager.getTransaction(txnId);
+        transaction.beginInsert((PaimonExternalTable) table, insertCtx);
+    }
+
+    @Override
+    protected void doBeforeCommit() throws UserException {
+        PaimonTransaction transaction =
+                (PaimonTransaction) transactionManager.getTransaction(txnId);
+        transaction.finishInsert((PaimonExternalTable) table, insertCtx);
+    }
+
+    @Override
+    protected TransactionType transactionType() {
+        return TransactionType.PAIMON;
+    }
+}
diff --git a/fe/fe-core/src/main/java/org/apache/doris/qe/Coordinator.java 
b/fe/fe-core/src/main/java/org/apache/doris/qe/Coordinator.java
index 74e0128e8da..eaad250d2bd 100644
--- a/fe/fe-core/src/main/java/org/apache/doris/qe/Coordinator.java
+++ b/fe/fe-core/src/main/java/org/apache/doris/qe/Coordinator.java
@@ -40,6 +40,7 @@ import org.apache.doris.datasource.ExternalScanNode;
 import org.apache.doris.datasource.FileQueryScanNode;
 import org.apache.doris.datasource.hive.HMSTransaction;
 import org.apache.doris.datasource.iceberg.IcebergTransaction;
+import org.apache.doris.datasource.paimon.PaimonTransaction;
 import org.apache.doris.datasource.maxcompute.MCTransaction;
 import org.apache.doris.load.loadv2.LoadJob;
 import org.apache.doris.metric.MetricRepo;
@@ -2644,6 +2645,11 @@ public class Coordinator implements CoordInterface {
             ((MCTransaction) 
Env.getCurrentEnv().getGlobalExternalTransactionInfoMgr().getTxnById(txnId))
                 .updateMCCommitData(params.getMcCommitDatas());
         }
+        if (params.isSetPaimonCommitMessages()) {
+            ((PaimonTransaction) 
Env.getCurrentEnv().getGlobalExternalTransactionInfoMgr()
+                    .getTxnById(txnId))
+                    .updateCommitMessages(params.getPaimonCommitMessages());
+        }
 
         if (ctx.done) {
             if (LOG.isDebugEnabled()) {
diff --git 
a/fe/fe-core/src/main/java/org/apache/doris/qe/runtime/LoadProcessor.java 
b/fe/fe-core/src/main/java/org/apache/doris/qe/runtime/LoadProcessor.java
index 38ba2ebbc79..d2297fa8d57 100644
--- a/fe/fe-core/src/main/java/org/apache/doris/qe/runtime/LoadProcessor.java
+++ b/fe/fe-core/src/main/java/org/apache/doris/qe/runtime/LoadProcessor.java
@@ -23,6 +23,7 @@ import org.apache.doris.common.Status;
 import org.apache.doris.common.util.DebugUtil;
 import org.apache.doris.datasource.hive.HMSTransaction;
 import org.apache.doris.datasource.iceberg.IcebergTransaction;
+import org.apache.doris.datasource.paimon.PaimonTransaction;
 import org.apache.doris.datasource.maxcompute.MCTransaction;
 import org.apache.doris.nereids.util.Utils;
 import org.apache.doris.qe.AbstractJobProcessor;
@@ -240,6 +241,11 @@ public class LoadProcessor extends AbstractJobProcessor {
             ((MCTransaction) 
Env.getCurrentEnv().getGlobalExternalTransactionInfoMgr().getTxnById(txnId))
                     .updateMCCommitData(params.getMcCommitDatas());
         }
+        if (params.isSetPaimonCommitMessages()) {
+            ((PaimonTransaction) 
Env.getCurrentEnv().getGlobalExternalTransactionInfoMgr()
+                    .getTxnById(txnId))
+                    .updateCommitMessages(params.getPaimonCommitMessages());
+        }
 
         if (fragmentTask.isDone()) {
             if (LOG.isDebugEnabled()) {
diff --git 
a/fe/fe-core/src/main/java/org/apache/doris/transaction/TransactionType.java 
b/fe/fe-core/src/main/java/org/apache/doris/transaction/PaimonTransactionManager.java
similarity index 62%
copy from 
fe/fe-core/src/main/java/org/apache/doris/transaction/TransactionType.java
copy to 
fe/fe-core/src/main/java/org/apache/doris/transaction/PaimonTransactionManager.java
index 83e092c0ed7..e01dc674cc1 100644
--- a/fe/fe-core/src/main/java/org/apache/doris/transaction/TransactionType.java
+++ 
b/fe/fe-core/src/main/java/org/apache/doris/transaction/PaimonTransactionManager.java
@@ -17,10 +17,20 @@
 
 package org.apache.doris.transaction;
 
-public enum TransactionType {
-    UNKNOWN,
-    HMS,
-    ICEBERG,
-    JDBC,
-    MAXCOMPUTE
+import org.apache.doris.datasource.paimon.PaimonMetadataOps;
+import org.apache.doris.datasource.paimon.PaimonTransaction;
+
+/**
+ * Transaction manager for Paimon external tables.
+ */
+public class PaimonTransactionManager extends 
AbstractExternalTransactionManager<PaimonTransaction> {
+
+    public PaimonTransactionManager(PaimonMetadataOps ops) {
+        super(ops);
+    }
+
+    @Override
+    PaimonTransaction createTransaction() {
+        return new PaimonTransaction((PaimonMetadataOps) ops);
+    }
 }
diff --git 
a/fe/fe-core/src/main/java/org/apache/doris/transaction/TransactionManagerFactory.java
 
b/fe/fe-core/src/main/java/org/apache/doris/transaction/TransactionManagerFactory.java
index 9a5584a0601..1801b65b7f9 100644
--- 
a/fe/fe-core/src/main/java/org/apache/doris/transaction/TransactionManagerFactory.java
+++ 
b/fe/fe-core/src/main/java/org/apache/doris/transaction/TransactionManagerFactory.java
@@ -20,6 +20,7 @@ package org.apache.doris.transaction;
 import org.apache.doris.datasource.hive.HiveMetadataOps;
 import org.apache.doris.datasource.iceberg.IcebergMetadataOps;
 import org.apache.doris.datasource.maxcompute.MaxComputeExternalCatalog;
+import org.apache.doris.datasource.paimon.PaimonMetadataOps;
 import org.apache.doris.fs.SpiSwitchingFileSystem;
 
 import java.util.concurrent.Executor;
@@ -38,4 +39,8 @@ public class TransactionManagerFactory {
     public static TransactionManager 
createMCTransactionManager(MaxComputeExternalCatalog catalog) {
         return new MCTransactionManager(catalog);
     }
+
+    public static TransactionManager 
createPaimonTransactionManager(PaimonMetadataOps ops) {
+        return new PaimonTransactionManager(ops);
+    }
 }
diff --git 
a/fe/fe-core/src/main/java/org/apache/doris/transaction/TransactionType.java 
b/fe/fe-core/src/main/java/org/apache/doris/transaction/TransactionType.java
index 83e092c0ed7..d6a587eaa2e 100644
--- a/fe/fe-core/src/main/java/org/apache/doris/transaction/TransactionType.java
+++ b/fe/fe-core/src/main/java/org/apache/doris/transaction/TransactionType.java
@@ -22,5 +22,6 @@ public enum TransactionType {
     HMS,
     ICEBERG,
     JDBC,
-    MAXCOMPUTE
+    MAXCOMPUTE,
+    PAIMON
 }


---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]

Reply via email to