diff --git a/changes/en-us/develop.md b/changes/en-us/develop.md
index dcf297f77fa..da1c53be175 100644
--- a/changes/en-us/develop.md
+++ b/changes/en-us/develop.md
@@ -15,6 +15,7 @@ Add changes here for all PR submitted to the develop branch.
- [[#5245](https://github.com/seata/seata/pull/5245)] fix the incomplete dependency of distribution module
- [[#5239](https://github.com/seata/seata/pull/5239)] fix `getConfig` throw `ClassCastException` when use JDK proxy
- [[#5281](https://github.com/seata/seata/pull/5281)] parallel request handle throw IndexOutOfBoundsException
+- [[#5287](https://github.com/seata/seata/pull/5287)] fix auto-increment of pk columns in PostgreSQL in AT mode
### optimize:
- [[#5208](https://github.com/seata/seata/pull/5208)] optimize throwable getCause once more
@@ -46,6 +47,7 @@ Thanks to these contributors for their code commits. Please report an unintended
- [wangliang181230](https://github.com/wangliang181230)
- [GoodBoyCoder](https://github.com/GoodBoyCoder)
- [a364176773](https://github.com/a364176773)
+- [isharpever](https://github.com/isharpever)
Also, we receive many valuable issues, questions and advices from our community. Thanks for you all.
diff --git a/changes/zh-cn/develop.md b/changes/zh-cn/develop.md
index 74646d2bb0b..238f6492182 100644
--- a/changes/zh-cn/develop.md
+++ b/changes/zh-cn/develop.md
@@ -15,6 +15,7 @@
- [[#5245](https://github.com/seata/seata/pull/5245)] 修复不完整的distribution模块依赖
- [[#5239](https://github.com/seata/seata/pull/5239)] 修复当使用JDK代理时,`getConfig` 方法获取部分配置时抛出 `ClassCastException` 异常的问题
- [[#5281](https://github.com/seata/seata/pull/5281)] 修复并行rm请求处理时数组索引越界问题
+- [[#5287](https://github.com/seata/seata/pull/5287)] 修复AT模式下pgsql的主键列自增的问题
### optimize:
- [[#5208](https://github.com/seata/seata/pull/5208)] 优化多次重复获取Throwable#getCause问题
@@ -46,6 +47,7 @@
- [wangliang181230](https://github.com/wangliang181230)
- [GoodBoyCoder](https://github.com/GoodBoyCoder)
- [a364176773](https://github.com/a364176773)
+- [isharpever](https://github.com/isharpever)
同时,我们收到了社区反馈的很多有价值的issue和建议,非常感谢大家。
diff --git a/rm-datasource/src/main/java/io/seata/rm/datasource/exec/postgresql/PostgresqlInsertExecutor.java b/rm-datasource/src/main/java/io/seata/rm/datasource/exec/postgresql/PostgresqlInsertExecutor.java
index 5610b86f0cd..9f6ff0f8ef9 100644
--- a/rm-datasource/src/main/java/io/seata/rm/datasource/exec/postgresql/PostgresqlInsertExecutor.java
+++ b/rm-datasource/src/main/java/io/seata/rm/datasource/exec/postgresql/PostgresqlInsertExecutor.java
@@ -18,22 +18,26 @@
import io.seata.common.exception.ShouldNeverHappenException;
import io.seata.common.loader.LoadLevel;
import io.seata.common.loader.Scope;
+import io.seata.common.util.CollectionUtils;
import io.seata.common.util.StringUtils;
import io.seata.rm.datasource.StatementProxy;
import io.seata.rm.datasource.exec.BaseInsertExecutor;
import io.seata.rm.datasource.exec.StatementCallback;
import io.seata.rm.datasource.sql.struct.ColumnMeta;
+import io.seata.sqlparser.SQLInsertRecognizer;
import io.seata.sqlparser.SQLRecognizer;
import io.seata.sqlparser.struct.Defaultable;
import io.seata.sqlparser.struct.Sequenceable;
import io.seata.sqlparser.struct.SqlMethodExpr;
import io.seata.sqlparser.struct.SqlSequenceExpr;
import io.seata.sqlparser.struct.SqlDefaultExpr;
+import io.seata.sqlparser.util.ColumnUtils;
import io.seata.sqlparser.util.JdbcConstants;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.sql.SQLException;
+import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
@@ -60,9 +64,55 @@ public PostgresqlInsertExecutor(StatementProxy statementProxy, StatementCallback
super(statementProxy, statementCallback, sqlRecognizer);
}
+ /**
+ * 1. If the insert columns are not empty and do not contain any pk columns,
+ * it means that there is no pk value in the insert rows, then all the pk values should come from auto-increment.
+ *
+ * 2. The pk value exists in insert rows. The possible situations are:
+ *
+ * - The insert columns are empty: all pk values can be obtained from insert rows
+ * - The insert columns contain at least one pk column: first obtain the existing pk value from the insert rows, and other from auto-increment
+ *
+ *
+ * @return {@link Map}<{@link String}, {@link List}<{@link Object}>>
+ * @throws SQLException the sql exception
+ */
@Override
public Map> getPkValues() throws SQLException {
- return getPkValuesByColumn();
+ List pkColumnNameList = getTableMeta().getPrimaryKeyOnlyName();
+ Map> pkValuesMap = new HashMap<>(pkColumnNameList.size());
+
+ // first obtain the existing pk value from the insert rows (if exists)
+ if (!containsColumns() || containsAnyPk()) {
+ pkValuesMap.putAll(getPkValuesByColumn());
+ }
+ // other from auto-increment
+ for (String columnName : pkColumnNameList) {
+ if (!pkValuesMap.containsKey(columnName)) {
+ pkValuesMap.put(columnName, getGeneratedKeys(columnName));
+ }
+ }
+ return pkValuesMap;
+ }
+
+ /**
+ * Whether the insert columns contain any pk columns
+ *
+ * @return true: contain at least one pk column. false: do not contain any pk columns
+ */
+ public boolean containsAnyPk() {
+ SQLInsertRecognizer recognizer = (SQLInsertRecognizer)sqlRecognizer;
+ List insertColumns = recognizer.getInsertColumns();
+ if (CollectionUtils.isEmpty(insertColumns)) {
+ return false;
+ }
+ List pkColumnNameList = getTableMeta().getPrimaryKeyOnlyName();
+ if (CollectionUtils.isEmpty(pkColumnNameList)) {
+ return false;
+ }
+ List newColumns = ColumnUtils.delEscape(insertColumns, getDbType());
+ return pkColumnNameList.stream().anyMatch(pkColumn -> newColumns.contains(pkColumn)
+ || CollectionUtils.toUpperList(newColumns).contains(pkColumn.toUpperCase()));
}
@Override
diff --git a/rm-datasource/src/test/java/io/seata/rm/datasource/exec/PostgresqlInsertExecutorTest.java b/rm-datasource/src/test/java/io/seata/rm/datasource/exec/PostgresqlInsertExecutorTest.java
index 198604f52ff..1acad8d40ab 100644
--- a/rm-datasource/src/test/java/io/seata/rm/datasource/exec/PostgresqlInsertExecutorTest.java
+++ b/rm-datasource/src/test/java/io/seata/rm/datasource/exec/PostgresqlInsertExecutorTest.java
@@ -29,7 +29,13 @@
import org.junit.jupiter.api.Test;
import org.mockito.Mockito;
-import java.util.*;
+import java.sql.SQLException;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.mock;
@@ -145,6 +151,145 @@ public void testInsertDefault_ByDefault_MultiPk() throws Exception {
Assertions.assertEquals(pkValuesMap.get(USER_ID_COLUMN), pkValuesAutoUserId);
}
+ @Test
+ public void testGetPkValues_SinglePk() throws SQLException {
+ doReturn(tableMeta).when(insertExecutor).getTableMeta();
+
+ List pkColumns = new ArrayList<>();
+ pkColumns.add(ID_COLUMN);
+ doReturn(pkColumns).when(tableMeta).getPrimaryKeyOnlyName();
+
+ // mock pk values from insert rows
+ Map> mockPkValuesFromColumn = new HashMap<>();
+ mockPkValuesFromColumn.put(ID_COLUMN, Collections.singletonList(PK_VALUE_ID + 1));
+ doReturn(mockPkValuesFromColumn).when(insertExecutor).getPkValuesByColumn();
+
+ // mock pk values from auto increment
+ List