Skip to content

feature: add SQL Server composite primary keys (#8041)#8050

Open
UokyI wants to merge 12 commits into
apache:2.xfrom
UokyI:feature/sqlserver-composite-pk-8041
Open

feature: add SQL Server composite primary keys (#8041)#8050
UokyI wants to merge 12 commits into
apache:2.xfrom
UokyI:feature/sqlserver-composite-pk-8041

Conversation

@UokyI
Copy link
Copy Markdown

@UokyI UokyI commented Apr 10, 2026

Ⅰ. Describe what this PR did

This PR adds support for SQL Server composite primary keys in Seata. The changes include:

  1. AbstractConnectionProxy.java: Fixed SQL Server compatibility issue - SQL Server does not support array of column names for getGeneratedKeys, now uses Statement.RETURN_GENERATED_KEYS instead.

  2. SqlGenerateUtils.java: Added buildWhereConditionListByPKsForSqlServer() method to handle composite primary keys. SQL Server does not support tuple IN syntax (col1,col2) IN ((?,?),(?,?)), so it now uses AND/OR syntax (col1=? AND col2=?) OR (col1=? AND col2=?).

  3. SqlServerInsertExecutor.java: Implemented composite primary key support for SQL Server INSERT operations. Handles both manually assigned primary keys and auto-increment columns.

  4. Unit Tests: Added test cases for SQL Server composite primary key scenarios in SqlGenerateUtilsTest and SqlServerInsertExecutorTest.

Ⅱ. Does this pull request fix one issue?

fixes #8041

Ⅲ. Why don't you add test cases (unit test/integration test)?

Tests are included in this PR.

  • SqlGenerateUtilsTest.testBuildWhereConditionListByPKsForSqlServer() - Tests composite PK WHERE clause generation
  • SqlGenerateUtilsTest.testBuildWhereConditionListByPKsForSqlServerWithSinglePk() - Tests single PK WHERE clause generation
  • SqlServerInsertExecutorTest.testGetPkValues_compositePrimaryKey_withAllPkInInsert() - Tests composite PK with manual values
  • SqlServerInsertExecutorTest.testGetPkValues_compositePrimaryKey_withAutoIncrement() - Tests composite PK with auto-increment
  • SqlServerInsertExecutorTest.testGetPkValues_compositePrimaryKey_nonAutoIncrementThrowsException() - Tests exception handling

Ⅳ. Describe how to verify it

  1. Create a SQL Server table with composite primary key
  2. Execute INSERT/UPDATE/DELETE operations through Seata AT mode
  3. Verify that undo_log is correctly generated with AND/OR syntax for WHERE conditions
  4. Verify that primary key values are correctly retrieved for both manual and auto-increment scenarios

Ⅴ. Special notes for reviews

  • SQL Server tuple IN syntax limitation is the key difference from other databases
  • The implementation maintains backward compatibility with existing single primary key scenarios

@funky-eyes funky-eyes added this to the 2.x Backlog milestone Apr 15, 2026
@funky-eyes funky-eyes added mode: AT AT transaction mode module/rm-datasource rm-datasource module DB: SQLServer Relate to seata SqlServer first-time contributor first-time contributor labels Apr 15, 2026
Copy link
Copy Markdown
Contributor

@funky-eyes funky-eyes left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Run 'mvn spotless:apply' to fix these violations.

@codecov
Copy link
Copy Markdown

codecov Bot commented Apr 15, 2026

Codecov Report

❌ Patch coverage is 80.00000% with 9 lines in your changes missing coverage. Please review.
✅ Project coverage is 72.27%. Comparing base (956bce8) to head (324bf3f).
⚠️ Report is 1 commits behind head on 2.x.

Files with missing lines Patch % Lines
...e/seata/rm/datasource/AbstractConnectionProxy.java 0.00% 7 Missing ⚠️
...source/exec/sqlserver/SqlServerInsertExecutor.java 89.47% 1 Missing and 1 partial ⚠️
Additional details and impacted files
@@             Coverage Diff              @@
##                2.x    #8050      +/-   ##
============================================
+ Coverage     72.10%   72.27%   +0.17%     
- Complexity      872      876       +4     
============================================
  Files          1320     1320              
  Lines         50550    50590      +40     
  Branches       6026     6040      +14     
============================================
+ Hits          36447    36566     +119     
+ Misses        11108    11039      -69     
+ Partials       2995     2985      -10     
Files with missing lines Coverage Δ
...g/apache/seata/rm/datasource/SqlGenerateUtils.java 100.00% <100.00%> (ø)
...source/exec/sqlserver/SqlServerInsertExecutor.java 39.17% <89.47%> (+13.85%) ⬆️
...e/seata/rm/datasource/AbstractConnectionProxy.java 15.84% <0.00%> (-0.49%) ⬇️

... and 41 files with indirect coverage changes

Impacted file tree graph

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

@funky-eyes funky-eyes requested a review from Copilot April 16, 2026 08:40
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Adds SQL Server support for composite primary keys in Seata AT mode, primarily by generating SQL Server-compatible PK WHERE clauses and enhancing SQL Server INSERT PK value extraction.

Changes:

  • Adjusts SQL Server INSERT PreparedStatement creation to use Statement.RETURN_GENERATED_KEYS instead of column-name arrays.
  • Adds SQL Server-specific composite-PK WHERE clause generation using AND/OR syntax instead of tuple IN.
  • Extends SqlServerInsertExecutor and unit tests to support composite primary keys.

Reviewed changes

Copilot reviewed 7 out of 7 changed files in this pull request and generated 7 comments.

Show a summary per file
File Description
rm-datasource/src/main/java/org/apache/seata/rm/datasource/AbstractConnectionProxy.java Uses SQL Server-compatible generated-keys mode for INSERT prepared statements.
rm-datasource/src/main/java/org/apache/seata/rm/datasource/SqlGenerateUtils.java Adds SQL Server PK WHERE generation via AND/OR instead of tuple IN.
rm-datasource/src/main/java/org/apache/seata/rm/datasource/exec/sqlserver/SqlServerInsertExecutor.java Implements composite PK handling for SQL Server INSERT PK extraction.
rm-datasource/src/test/java/org/apache/seata/rm/datasource/SqlGenerateUtilsTest.java Adds unit tests for SQL Server composite/single PK WHERE clause generation.
rm-datasource/src/test/java/org/apache/seata/rm/datasource/exec/SqlServerInsertExecutorTest.java Adds unit tests for SQL Server composite PK PK-value retrieval scenarios.
changes/en-us/2.x.md Adds changelog entry for SQL Server composite PK support.
changes/zh-cn/2.x.md Adds changelog entry for SQL Server composite PK support (Chinese).

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread changes/zh-cn/2.x.md
- [[#8041](https://github.com/apache/incubator-seata/pull/8041)] 新增SQL Server 多主键支持
- [[#8046](https://github.com/apache/incubator-seata/pull/8046)] 添加了 fastjson2 和 jackson3


Copy link

Copilot AI Apr 16, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There is an extra blank line introduced after the new #8041 entry, which leaves unnecessary vertical spacing in the rendered changelog. Consider removing the additional empty line to keep formatting consistent with surrounding entries.

Suggested change

Copilot uses AI. Check for mistakes.
Assertions.assertEquals(Arrays.asList(PK_VALUE), pkValues.get(USER_ID_COLUMN));
}

@Test
Copy link

Copilot AI Apr 16, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The new composite-PK tests cover (1) all PK columns provided and (2) all PK columns marked autoincrement, but they don't cover the common mixed case where some PK columns are provided in the INSERT and the remaining PK column is identity/auto-generated. Adding a test for that scenario would catch the current behavior where getPkValues() throws instead of merging manual + generated PK values.

Suggested change
@Test
@Test
public void testGetPkValues_compositePrimaryKey_withPartialPkInInsertAndAutoIncrement() throws Exception {
// Mock composite primary key where one PK is provided in INSERT and the other is auto-increment
List<String> compositePkList = Arrays.asList(ID_COLUMN, USER_ID_COLUMN);
when(tableMeta.getPrimaryKeyOnlyName()).thenReturn(compositePkList);
Map<String, ColumnMeta> pkMap = new HashMap<>();
ColumnMeta idMeta = mock(ColumnMeta.class);
when(idMeta.isAutoincrement()).thenReturn(false); // Provided in INSERT
pkMap.put(ID_COLUMN, idMeta);
ColumnMeta userIdMeta = mock(ColumnMeta.class);
when(userIdMeta.isAutoincrement()).thenReturn(true); // Generated by database
pkMap.put(USER_ID_COLUMN, userIdMeta);
when(tableMeta.getPrimaryKeyMap()).thenReturn(pkMap);
mockParametersForCompositePk();
doReturn(tableMeta).when(insertExecutor).getTableMeta();
doReturn(true).when(insertExecutor).containsPK(); // One primary key column is present in INSERT
doReturn(Arrays.asList(PK_VALUE)).when(insertExecutor).getGeneratedKeys();
Map<String, List<Object>> pkValues = insertExecutor.getPkValues();
// Verify manual and generated PK values are merged correctly
Assertions.assertEquals(Arrays.asList(1), pkValues.get(ID_COLUMN));
Assertions.assertEquals(Arrays.asList(PK_VALUE), pkValues.get(USER_ID_COLUMN));
}
@Test

Copilot uses AI. Check for mistakes.
Comment on lines +89 to +110
// when there is a composite primary key - Fix: Support SQL Server composite primary keys.
if (isContainsPk) {
// All primary key columns are manually assigned in the INSERT statement.
pkValuesMap = getPkValuesByColumn();
} else {
// Some or all primary key columns are auto-generated.
// Get metadata for all primary key columns.
Map<String, ColumnMeta> primaryKeyMap = getTableMeta().getPrimaryKeyMap();

// Iterate over each primary key column.
for (String pkColumnName : pkColumnNameList) {
ColumnMeta pkMeta = primaryKeyMap.get(pkColumnName);
if (pkMeta.isAutoincrement()) {
// Auto-increment column: get from generated keys.
pkValuesMap.put(pkColumnName, getGeneratedKeys());
} else {
// Non-auto-increment column: requires manual assignment, throw exception if not supported.
throw new NotSupportYetException(
"composite primary key with non-autoincrement column is not supported in sqlserver: "
+ pkColumnName);
}
}
Copy link

Copilot AI Apr 16, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

getPkValues() for SQL Server composite PKs incorrectly assumes that when containsPK() is false, none of the PK columns are provided. For a common composite PK case where one PK column is provided in the INSERT and another is identity/auto-generated, this branch will throw for the provided non-autoincrement column instead of merging manual values with generated keys. Consider following the MySQLInsertExecutor pattern: start from getPkValuesByColumn() (for any PKs present) and then fill any missing PK columns that are autoincrement from getGeneratedKeys(); only throw if a required PK column is still missing and not autoincrement.

Copilot uses AI. Check for mistakes.
Comment on lines +83 to +84
// Use AND/OR syntax instead
if (JdbcConstants.SQLSERVER.equalsIgnoreCase(dbType)) {
Copy link

Copilot AI Apr 16, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The SQL Server special-case in buildWhereConditionListByPKs triggers for all PK sizes, including single-column PKs. This changes SQL Server WHERE generation from the existing tuple-IN form to an OR chain (e.g. (id=?) OR (id=?)), which is longer and can be less efficient for large batches. If the limitation only applies to composite PKs, gate this branch on pkNameList.size() > 1 and let the existing implementation handle single-column PKs.

Suggested change
// Use AND/OR syntax instead
if (JdbcConstants.SQLSERVER.equalsIgnoreCase(dbType)) {
// Use AND/OR syntax instead for composite primary keys
if (JdbcConstants.SQLSERVER.equalsIgnoreCase(dbType) && pkNameList.size() > 1) {

Copilot uses AI. Check for mistakes.
Comment on lines +122 to +132
String[] pkNameArray =
new String[tableMeta.getPrimaryKeyOnlyName().size()];
tableMeta.getPrimaryKeyOnlyName().toArray(pkNameArray);
targetPreparedStatement = getTargetConnection().prepareStatement(sql, pkNameArray);
// Fix: SQL Server does not support array of column names for getGeneratedKeys, use
// RETURN_GENERATED_KEYS instead.
if (JdbcConstants.SQLSERVER.equalsIgnoreCase(dbType)) {
targetPreparedStatement =
getTargetConnection().prepareStatement(sql, Statement.RETURN_GENERATED_KEYS);
} else {
targetPreparedStatement = getTargetConnection().prepareStatement(sql, pkNameArray);
}
Copy link

Copilot AI Apr 16, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

pkNameArray is built unconditionally, but for SQL Server the code path now ignores it and uses Statement.RETURN_GENERATED_KEYS. Consider moving the pkNameArray allocation/population into the non-SQLServer branch to avoid unnecessary work on SQL Server inserts.

Copilot uses AI. Check for mistakes.
Comment on lines +287 to +308

private void mockParametersForCompositePk() {
Map<Integer, ArrayList<Object>> parameters = new HashMap<>(4);
ArrayList<Object> arrayList0 = new ArrayList<>();
arrayList0.add(1); // id value
ArrayList<Object> arrayList1 = new ArrayList<>();
arrayList1.add("userId1");
ArrayList<Object> arrayList2 = new ArrayList<>();
arrayList2.add("userName1");
ArrayList<Object> arrayList3 = new ArrayList<>();
arrayList3.add("userStatus1");
parameters.put(1, arrayList0);
parameters.put(2, arrayList1);
parameters.put(3, arrayList2);
parameters.put(4, arrayList3);
PreparedStatementProxy psp = (PreparedStatementProxy) this.statementProxy;
when(psp.getParameters()).thenReturn(parameters);

List<List<Object>> rows = new ArrayList<>();
rows.add(Arrays.asList("?", "?", "?", "?"));
when(sqlInsertRecognizer.getInsertRows(pkIndexMap.values())).thenReturn(rows);
}
Copy link

Copilot AI Apr 16, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

mockParametersForCompositePk() is added but never called in this test class. Unused helpers make the test harder to maintain; either use it in the composite PK tests or remove it.

Suggested change
private void mockParametersForCompositePk() {
Map<Integer, ArrayList<Object>> parameters = new HashMap<>(4);
ArrayList<Object> arrayList0 = new ArrayList<>();
arrayList0.add(1); // id value
ArrayList<Object> arrayList1 = new ArrayList<>();
arrayList1.add("userId1");
ArrayList<Object> arrayList2 = new ArrayList<>();
arrayList2.add("userName1");
ArrayList<Object> arrayList3 = new ArrayList<>();
arrayList3.add("userStatus1");
parameters.put(1, arrayList0);
parameters.put(2, arrayList1);
parameters.put(3, arrayList2);
parameters.put(4, arrayList3);
PreparedStatementProxy psp = (PreparedStatementProxy) this.statementProxy;
when(psp.getParameters()).thenReturn(parameters);
List<List<Object>> rows = new ArrayList<>();
rows.add(Arrays.asList("?", "?", "?", "?"));
when(sqlInsertRecognizer.getInsertRows(pkIndexMap.values())).thenReturn(rows);
}

Copilot uses AI. Check for mistakes.
Comment thread changes/en-us/2.x.md
- [[#8041](https://github.com/apache/incubator-seata/pull/8041)] add SQL Server composite primary keys
- [[#8046](https://github.com/apache/incubator-seata/pull/8046)] add fastjson2 and jackson3


Copy link

Copilot AI Apr 16, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There is an extra blank line introduced after the new #8041 entry, which leaves an empty list item spacing in the rendered changelog. Consider removing the additional empty line to keep formatting consistent with surrounding entries.

Suggested change

Copilot uses AI. Check for mistakes.
Comment thread changes/en-us/2.x.md
- [[#8041](https://github.com/apache/incubator-seata/pull/8041)] add SQL Server composite primary keys
- [[#8046](https://github.com/apache/incubator-seata/pull/8046)] add fastjson2 and jackson3


Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please add your GitHub information to the contributors list at the bottom. Thank you.

Comment thread changes/zh-cn/2.x.md
- [[#8041](https://github.com/apache/incubator-seata/pull/8041)] 新增SQL Server 多主键支持
- [[#8046](https://github.com/apache/incubator-seata/pull/8046)] 添加了 fastjson2 和 jackson3


Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please add your GitHub information to the contributors list at the bottom. Thank you.

UokyI and others added 2 commits April 17, 2026 11:53
…pache#8050)

- Fix SqlServerInsertExecutor getPkValues() to handle mixed PK scenario
  where some PK columns are in INSERT and others are auto-increment
- Gate SqlGenerateUtils SQL Server special branch on pkNameList.size() > 1
  to avoid affecting single PK scenarios
- Move pkNameArray allocation into non-SQLServer branch in
  AbstractConnectionProxy to avoid unnecessary overhead
- Update unit tests to match new implementation logic
  and add mixed PK test case
- Fix changelog PR link from apache#8041 (issue) to apache#8050 (PR)
- Add UokyI to contributors list
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 7 out of 7 changed files in this pull request and generated 6 comments.


💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread changes/en-us/2.x.md Outdated
Comment on lines 1 to 3
<!--
Licensed to the Apache Software Foundation (ASF) under one or more
contributor license agreements. See the NOTICE file distributed with
Copy link

Copilot AI Apr 20, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The very first character on this file appears to be a UTF-8 BOM / invisible character (line starts with "\uFEFF<!--"). This can create noisy diffs and occasionally breaks tooling; please remove the BOM so the file starts with a plain "<!--" like the other changelog files.

Copilot uses AI. Check for mistakes.
Comment on lines +124 to +136
/**
* Build where condition list by PKs for SQL Server.
* SQL Server does not support tuple IN syntax: (col1,col2) IN ((?,?),(?,?))
* Use AND/OR syntax instead: (col1=? AND col2=?) OR (col1=? AND col2=?)
*
* @param pkNameList pk column name list
* @param rowSize the row size of records
* @param maxInSize the max in size
* @return where condition sql list for SQL Server
*/
private static List<WhereSql> buildWhereConditionListByPKsForSqlServer(
List<String> pkNameList, int rowSize, int maxInSize, String dbType) {
List<WhereSql> whereSqls = new ArrayList<>();
Copy link

Copilot AI Apr 20, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Javadoc for buildWhereConditionListByPKsForSqlServer() is missing an @param entry for the dbType argument, even though it is part of the signature and used for escaping. Please either document dbType or remove it from the signature (since this method is SQL Server-specific).

Copilot uses AI. Check for mistakes.
Comment on lines +89 to +120
// when there is a composite primary key - Fix: Support SQL Server composite primary keys.
// SQL Server allows only one IDENTITY column per table.
// So composite PK can have at most one auto-increment column.
// Strategy: parse PK values from INSERT columns, then fill missing auto-increment PK from generated keys.
if (!getPkIndex().isEmpty()) {
// At least one PK column is in the INSERT statement.
pkValuesMap = getPkValuesByColumn();
Map<String, ColumnMeta> primaryKeyMap = getTableMeta().getPrimaryKeyMap();

// Fill any missing auto-increment PK columns from generated keys.
for (String pkColumnName : pkColumnNameList) {
if (!pkValuesMap.containsKey(pkColumnName)) {
ColumnMeta pkMeta = primaryKeyMap.get(pkColumnName);
if (pkMeta.isAutoincrement()) {
pkValuesMap.put(pkColumnName, getGeneratedKeys());
} else {
throw new NotSupportYetException(
"composite primary key with non-autoincrement column not in INSERT is not supported in sqlserver: "
+ pkColumnName);
}
}
}
} else {
// No PK columns in INSERT statement.
// For composite PK, this means all PK columns must have values from elsewhere.
// Since SQL Server only supports one IDENTITY column, non-identity PK columns would fail.
Map<String, ColumnMeta> primaryKeyMap = getTableMeta().getPrimaryKeyMap();
for (String pkColumnName : pkColumnNameList) {
ColumnMeta pkMeta = primaryKeyMap.get(pkColumnName);
if (pkMeta.isAutoincrement()) {
pkValuesMap.put(pkColumnName, getGeneratedKeys());
} else {
Copy link

Copilot AI Apr 20, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In composite-PK branches, this calls getGeneratedKeys() inside a loop and may invoke it multiple times (e.g., when more than one PK column is missing or marked autoincrement). SqlServerInsertExecutor.getGeneratedKeys() consumes the generated-keys ResultSet and relies on beforeFirst(), which may fail on some drivers; repeated calls can therefore return empty and throw NotSupportYetException or produce inconsistent results. Consider fetching generated keys once and reusing the list, and/or explicitly enforcing that at most one PK column can be autoincrement (otherwise fail fast).

Copilot uses AI. Check for mistakes.
Comment on lines +239 to +262
public void testGetPkValues_compositePrimaryKey_withAutoIncrement() throws Exception {
// Mock composite primary key with auto-increment columns
List<String> compositePkList = Arrays.asList(ID_COLUMN, USER_ID_COLUMN);
when(tableMeta.getPrimaryKeyOnlyName()).thenReturn(compositePkList);

Map<String, ColumnMeta> pkMap = new HashMap<>();
ColumnMeta idMeta = mock(ColumnMeta.class);
when(idMeta.isAutoincrement()).thenReturn(true); // Auto-increment
pkMap.put(ID_COLUMN, idMeta);

ColumnMeta userIdMeta = mock(ColumnMeta.class);
when(userIdMeta.isAutoincrement()).thenReturn(true); // Auto-increment
pkMap.put(USER_ID_COLUMN, userIdMeta);
when(tableMeta.getPrimaryKeyMap()).thenReturn(pkMap);

doReturn(tableMeta).when(insertExecutor).getTableMeta();
doReturn(new HashMap<String, Integer>()).when(insertExecutor).getPkIndex(); // No PK columns in INSERT
doReturn(Arrays.asList(PK_VALUE)).when(insertExecutor).getGeneratedKeys();

Map<String, List<Object>> pkValues = insertExecutor.getPkValues();

// Verify auto-increment column gets value from generated keys
Assertions.assertEquals(Arrays.asList(PK_VALUE), pkValues.get(ID_COLUMN));
Assertions.assertEquals(Arrays.asList(PK_VALUE), pkValues.get(USER_ID_COLUMN));
Copy link

Copilot AI Apr 20, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This test models a composite PK where both columns are autoincrement and expects the same generated key to be applied to both. SQL Server allows only one IDENTITY column per table (and the production code comment states that), so this scenario is not valid and could mask real behavior. Please adjust the test to a realistic composite-PK case (e.g., one provided PK + one IDENTITY, which you already test) or assert that multiple autoincrement PK columns are rejected.

Suggested change
public void testGetPkValues_compositePrimaryKey_withAutoIncrement() throws Exception {
// Mock composite primary key with auto-increment columns
List<String> compositePkList = Arrays.asList(ID_COLUMN, USER_ID_COLUMN);
when(tableMeta.getPrimaryKeyOnlyName()).thenReturn(compositePkList);
Map<String, ColumnMeta> pkMap = new HashMap<>();
ColumnMeta idMeta = mock(ColumnMeta.class);
when(idMeta.isAutoincrement()).thenReturn(true); // Auto-increment
pkMap.put(ID_COLUMN, idMeta);
ColumnMeta userIdMeta = mock(ColumnMeta.class);
when(userIdMeta.isAutoincrement()).thenReturn(true); // Auto-increment
pkMap.put(USER_ID_COLUMN, userIdMeta);
when(tableMeta.getPrimaryKeyMap()).thenReturn(pkMap);
doReturn(tableMeta).when(insertExecutor).getTableMeta();
doReturn(new HashMap<String, Integer>()).when(insertExecutor).getPkIndex(); // No PK columns in INSERT
doReturn(Arrays.asList(PK_VALUE)).when(insertExecutor).getGeneratedKeys();
Map<String, List<Object>> pkValues = insertExecutor.getPkValues();
// Verify auto-increment column gets value from generated keys
Assertions.assertEquals(Arrays.asList(PK_VALUE), pkValues.get(ID_COLUMN));
Assertions.assertEquals(Arrays.asList(PK_VALUE), pkValues.get(USER_ID_COLUMN));
public void testGetPkValues_compositePrimaryKey_withOneAutoIncrementAndOnePkInInsert() throws Exception {
// Mock realistic SQL Server composite primary key: one IDENTITY column + one provided PK column
List<String> compositePkList = Arrays.asList(ID_COLUMN, USER_ID_COLUMN);
when(tableMeta.getPrimaryKeyOnlyName()).thenReturn(compositePkList);
Map<String, ColumnMeta> pkMap = new HashMap<>();
ColumnMeta idMeta = mock(ColumnMeta.class);
when(idMeta.isAutoincrement()).thenReturn(true);
pkMap.put(ID_COLUMN, idMeta);
ColumnMeta userIdMeta = mock(ColumnMeta.class);
when(userIdMeta.isAutoincrement()).thenReturn(false);
pkMap.put(USER_ID_COLUMN, userIdMeta);
when(tableMeta.getPrimaryKeyMap()).thenReturn(pkMap);
doReturn(tableMeta).when(insertExecutor).getTableMeta();
Map<String, Integer> pkIndex = new HashMap<>();
pkIndex.put(USER_ID_COLUMN, 0);
doReturn(pkIndex).when(insertExecutor).getPkIndex();
Map<String, List<Object>> pkValuesByColumn = new HashMap<>();
pkValuesByColumn.put(USER_ID_COLUMN, Arrays.asList("user1"));
doReturn(pkValuesByColumn).when(insertExecutor).getPkValuesByColumn();
doReturn(Arrays.asList(PK_VALUE)).when(insertExecutor).getGeneratedKeys();
Map<String, List<Object>> pkValues = insertExecutor.getPkValues();
Assertions.assertEquals(Arrays.asList(PK_VALUE), pkValues.get(ID_COLUMN));
Assertions.assertEquals(Arrays.asList("user1"), pkValues.get(USER_ID_COLUMN));

Copilot uses AI. Check for mistakes.
Comment thread changes/zh-cn/2.x.md
Comment on lines 33 to 35
- [[#8044](https://github.com/apache/incubator-seata/pull/8044)] 为 UnregisterRM 协议添加 protobuf 序列化支持
- [[#8050](https://github.com/apache/incubator-seata/pull/8050)] 新增SQL Server 多主键支持
- [[#8046](https://github.com/apache/incubator-seata/pull/8046)] 添加了 fastjson2 和 jackson3
Copy link

Copilot AI Apr 20, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The changelog entry links to PR #8050, but this PR/issue context is #8041. Please update the PR number/link in the changelog entry so it points to the correct pull request.

Copilot uses AI. Check for mistakes.
Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@funky-eyes 我这个地址,是应该写 [#8050] ?我看其他人的格式都是这样写的,还是按照Copilot AI的反馈,使用 [#8041] ?

Comment thread changes/en-us/2.x.md
Comment on lines 31 to 34
- [[#8020](https://github.com/apache/incubator-seata/pull/8020)] add UnregisterRM protocol to notify server on client destroy
- [[#8044](https://github.com/apache/incubator-seata/pull/8044)] add protobuf serialization support for UnregisterRM protocol
- [[#8050](https://github.com/apache/incubator-seata/pull/8050)] add SQL Server composite primary keys
- [[#8046](https://github.com/apache/incubator-seata/pull/8046)] add fastjson2 and jackson3
Copy link

Copilot AI Apr 20, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The changelog entry links to PR #8050, but this PR/issue context is #8041. Please update the PR number/link in the changelog entry so it points to the correct pull request.

Copilot uses AI. Check for mistakes.
@funky-eyes
Copy link
Copy Markdown
Contributor

Hi, could you please take a look at the comment Copilot left above?

… primary keys (apache#8041)

- Add @param dbType to buildWhereConditionListByPKsForSqlServer Javadoc
- Cache getGeneratedKeys() result before loop to avoid repeated ResultSet consumption
- Fix unrealistic test: replace dual-autoincrement composite PK test with single IDENTITY + non-autoincrement scenario
- Remove UTF-8 BOM from changes/en-us/2.x.md
@UokyI
Copy link
Copy Markdown
Author

UokyI commented Apr 29, 2026

Hi, could you please take a look at the comment Copilot left above?

I've checked the Copilot comment about the changelog entry links. The AI suggested changing #8050 to #8041, but looking at the existing entries in the changelog (e.g., #8020, #8044, #8046), they all use the PR number rather than the issue number. So #8050 follows the project convention. This appears to be a false positive from the AI review.

@funky-eyes funky-eyes modified the milestones: 2.x Backlog, 2.7.0 May 16, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

DB: SQLServer Relate to seata SqlServer first-time contributor first-time contributor mode: AT AT transaction mode module/rm-datasource rm-datasource module

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Seata 2.2.0 - AT模式 - SQL Server - 联合主键 Update报错

3 participants