Repository: apache/shardingsphere (2026-07-15T04:04:01Z)

Issue:
MySQL dialect exception mapper does not map ColumnNotFoundException to a MySQL error
## Bug Report

### Which version of ShardingSphere did you use?
master @ 6778bfbe08c

### Which project did you use? ShardingSphere-JDBC or ShardingSphere-Proxy?
Proxy

### Expected behavior
When a `ColumnNotFoundException` reaches the MySQL frontend, the client should receive MySQL error 1054 (SQLState `42S22`) with message `Unknown column '%s' in '%s'`, matching the native server and the PostgreSQL dialect mapper.

### Actual behavior
`MySQLDialectExceptionMapper.convert()` has no branch for `ColumnNotFoundException`, so it falls back to `UnknownSQLException`, surfacing a generic HY000 error with vendor code 30000 instead of the proper column-not-found error.

### Reason analyze (If you can)
The core `ColumnNotFoundException` is a `SQLDialectException` thrown on a normal path (e.g. `MySQLComStmtPrepareParameterMarkerExtractor`). The sibling PostgreSQL mapper already maps it to `UNDEFINED_COLUMN`; only MySQL is missing the mapping, so it reaches the fallback in `MySQLDialectExceptionMapper.convert()`.

### Steps to reproduce the behavior, such as: SQL to execute, sharding rule configuration, when exception occur etc.
Over ShardingSphere-Proxy (MySQL), prepare a statement whose parameter marker references an unknown column so `ColumnNotFoundException` is raised; observe the generic HY000 error instead of 1054/`42S22`.

### Example codes for reproduce this issue (such as a github link).
N/A



Discussion:

Patch:
diff --git a/RELEASE-NOTES.md b/RELEASE-NOTES.md
index 62001a23bdef9..851a44dd22826 100644
--- a/RELEASE-NOTES.md
+++ b/RELEASE-NOTES.md
@@ -33,6 +33,7 @@
 1. Proxy: Fix incorrect generated key handling for explicit auto-increment values - [#38810](https://github.com/apache/shardingsphere/pull/38810)
 1. Proxy: Fix microseconds decoded as nanoseconds in MySQL binary TIME value - [#39138](https://github.com/apache/shardingsphere/pull/39138)
 1. Proxy: Fix MySQL BLOB data corruption when string-like prepared statement parameters target BLOB columns - [#39072](https://github.com/apache/shardingsphere/pull/39072)
+1. Proxy: Add MySQL exception mapping for ColumnNotFoundException - [#39126](https://github.com/apache/shardingsphere/pull/39126)
 1. Proxy: Fix MySQL prepared statement parameter signedness decoding - [#39204](https://github.com/apache/shardingsphere/pull/39204)
 1. JDBC & Proxy: Remove default MySQL prepared statement query properties when creating data sources - [#38593](https://github.com/apache/shardingsphere/pull/38593)
 1. Mode: Fix rule metadata not removed from memory after dropping rules in Etcd cluster mode - [#38561](https://github.com/apache/shardingsphere/pull/38561)
diff --git a/database/exception/dialect/mysql/src/main/java/org/apache/shardingsphere/database/exception/mysql/mapper/MySQLDialectExceptionMapper.java b/database/exception/dialect/mysql/src/main/java/org/apache/shardingsphere/database/exception/mysql/mapper/MySQLDialectExceptionMapper.java
index a768080dbe13b..7da2c3f705e0f 100644
--- a/database/exception/dialect/mysql/src/main/java/org/apache/shardingsphere/database/exception/mysql/mapper/MySQLDialectExceptionMapper.java
+++ b/database/exception/dialect/mysql/src/main/java/org/apache/shardingsphere/database/exception/mysql/mapper/MySQLDialectExceptionMapper.java
@@ -21,6 +21,7 @@
 import org.apache.shardingsphere.database.exception.core.exception.connection.AccessDeniedException;
 import org.apache.shardingsphere.database.exception.core.exception.connection.TooManyConnectionsException;
 import org.apache.shardingsphere.database.exception.core.exception.data.InsertColumnsAndValuesMismatchedException;
+import org.apache.shardingsphere.database.exception.core.exception.syntax.column.ColumnNotFoundException;
 import org.apache.shardingsphere.database.exception.core.exception.syntax.database.DatabaseCreateExistsException;
 import org.apache.shardingsphere.database.exception.core.exception.syntax.database.DatabaseDropNotExistsException;
 import org.apache.shardingsphere.database.exception.core.exception.syntax.database.NoDatabaseSelectedException;
@@ -122,6 +123,9 @@ public SQLException convert(final SQLDialectException sqlDialectException) {
             IncorrectGlobalLocalVariableException ex = (IncorrectGlobalLocalVariableException) sqlDialectException;
             return toSQLException(MySQLVendorError.ER_INCORRECT_GLOBAL_LOCAL_VAR, ex.getVariableName(), ex.getScope());
         }
+        if (sqlDialectException instanceof ColumnNotFoundException) {
+            return toSQLException(MySQLVendorError.ER_BAD_FIELD_ERROR, ((ColumnNotFoundException) sqlDialectException).getColumnName(), "field list");
+        }
         return new UnknownSQLException(sqlDialectException).toSQLException();
     }
     
diff --git a/database/exception/dialect/mysql/src/main/java/org/apache/shardingsphere/database/exception/mysql/vendor/MySQLVendorError.java b/database/exception/dialect/mysql/src/main/java/org/apache/shardingsphere/database/exception/mysql/vendor/MySQLVendorError.java
index 2126b8250ad6f..515ce88952f9d 100644
--- a/database/exception/dialect/mysql/src/main/java/org/apache/shardingsphere/database/exception/mysql/vendor/MySQLVendorError.java
+++ b/database/exception/dialect/mysql/src/main/java/org/apache/shardingsphere/database/exception/mysql/vendor/MySQLVendorError.java
@@ -50,6 +50,8 @@ public enum MySQLVendorError implements VendorError {
     
     ER_TABLE_EXISTS_ERROR(XOpenSQLState.DUPLICATE, 1050, "Table '%s' already exists"),
     
+    ER_BAD_FIELD_ERROR(XOpenSQLState.COLUMN_NOT_FOUND, 1054, "Unknown column '%s' in '%s'"),
+    
     ER_DUP_ENTRY(XOpenSQLState.INTEGRITY_CONSTRAINT_VIOLATION, 1062, "Duplicate entry '%s' for key %d"),
     
     ER_PARSE_ERROR(XOpenSQLState.SYNTAX_ERROR, 1064, "%s near '%s' at line %d"),
diff --git a/infra/exception/src/main/java/org/apache/shardingsphere/infra/exception/external/sql/sqlstate/XOpenSQLState.java b/infra/exception/src/main/java/org/apache/shardingsphere/infra/exception/external/sql/sqlstate/XOpenSQLState.java
index e67762648c7f0..266a8d230d055 100644
--- a/infra/exception/src/main/java/org/apache/shardingsphere/infra/exception/external/sql/sqlstate/XOpenSQLState.java
+++ b/infra/exception/src/main/java/org/apache/shardingsphere/infra/exception/external/sql/sqlstate/XOpenSQLState.java
@@ -63,6 +63,8 @@ public enum XOpenSQLState implements SQLState {
     
     NOT_FOUND("42S02"),
     
+    COLUMN_NOT_FOUND("42S22"),
+    
     CHECK_OPTION_VIOLATION("44000"),
     
     INVALID_COLUMN_NUMBER("HV008"),


Test patch:
diff --git a/database/exception/dialect/mysql/src/test/java/org/apache/shardingsphere/database/exception/mysql/mapper/MySQLDialectExceptionMapperTest.java b/database/exception/dialect/mysql/src/test/java/org/apache/shardingsphere/database/exception/mysql/mapper/MySQLDialectExceptionMapperTest.java
index c82eafab354dd..0a290b89b2da6 100644
--- a/database/exception/dialect/mysql/src/test/java/org/apache/shardingsphere/database/exception/mysql/mapper/MySQLDialectExceptionMapperTest.java
+++ b/database/exception/dialect/mysql/src/test/java/org/apache/shardingsphere/database/exception/mysql/mapper/MySQLDialectExceptionMapperTest.java
@@ -23,6 +23,7 @@
 import org.apache.shardingsphere.database.exception.core.exception.connection.AccessDeniedException;
 import org.apache.shardingsphere.database.exception.core.exception.connection.TooManyConnectionsException;
 import org.apache.shardingsphere.database.exception.core.exception.data.InsertColumnsAndValuesMismatchedException;
+import org.apache.shardingsphere.database.exception.core.exception.syntax.column.ColumnNotFoundException;
 import org.apache.shardingsphere.database.exception.core.exception.syntax.database.DatabaseCreateExistsException;
 import org.apache.shardingsphere.database.exception.core.exception.syntax.database.DatabaseDropNotExistsException;
 import org.apache.shardingsphere.database.exception.core.exception.syntax.database.NoDatabaseSelectedException;
@@ -85,6 +86,11 @@ void assertConvertWithUsingPassword() {
         assertSQLExceptionWithMessage(mapper.convert(new AccessDeniedException("root", "127.0.0.1", true)), MySQLVendorError.ER_ACCESS_DENIED_ERROR, "root", "127.0.0.1", "YES");
     }
     
+    @Test
+    void assertConvertWithColumnNotFound() {
+        assertSQLExceptionWithMessage(mapper.convert(new ColumnNotFoundException("t_order", "order_id")), MySQLVendorError.ER_BAD_FIELD_ERROR, "order_id", "field list");
+    }
+    
     private void assertSQLExceptionWithMessage(final SQLException actual, final VendorError vendorError, final Object... messageArgs) {
         assertSQLException(actual, vendorError);
         assertThat(actual.getMessage(), is(String.format(vendorError.getReason(), messageArgs)));
@@ -123,6 +129,7 @@ private static Stream<Arguments> convertArguments() {
                 Arguments.of("unknown_system_variable", UnknownSystemVariableException.class, MySQLVendorError.ER_UNKNOWN_SYSTEM_VARIABLE),
                 Arguments.of("error_local_variable", ErrorLocalVariableException.class, MySQLVendorError.ER_LOCAL_VARIABLE),
                 Arguments.of("error_global_variable", ErrorGlobalVariableException.class, MySQLVendorError.ER_GLOBAL_VARIABLE),
-                Arguments.of("incorrect_global_local_variable", IncorrectGlobalLocalVariableException.class, MySQLVendorError.ER_INCORRECT_GLOBAL_LOCAL_VAR));
+                Arguments.of("incorrect_global_local_variable", IncorrectGlobalLocalVariableException.class, MySQLVendorError.ER_INCORRECT_GLOBAL_LOCAL_VAR),
+                Arguments.of("column_not_found", ColumnNotFoundException.class, MySQLVendorError.ER_BAD_FIELD_ERROR));
     }
 }
diff --git a/database/exception/dialect/mysql/src/test/java/org/apache/shardingsphere/database/exception/mysql/vendor/MySQLVendorErrorTest.java b/database/exception/dialect/mysql/src/test/java/org/apache/shardingsphere/database/exception/mysql/vendor/MySQLVendorErrorTest.java
index fc0220c588f0e..89a4e49133d83 100644
--- a/database/exception/dialect/mysql/src/test/java/org/apache/shardingsphere/database/exception/mysql/vendor/MySQLVendorErrorTest.java
+++ b/database/exception/dialect/mysql/src/test/java/org/apache/shardingsphere/database/exception/mysql/vendor/MySQLVendorErrorTest.java
@@ -38,6 +38,13 @@ void assertBadDbError() {
         assertThat(MySQLVendorError.ER_BAD_DB_ERROR.getReason(), is("Unknown database '%s'"));
     }
     
+    @Test
+    void assertBadFieldError() {
+        assertThat(MySQLVendorError.ER_BAD_FIELD_ERROR.getVendorCode(), is(1054));
+        assertThat(MySQLVendorError.ER_BAD_FIELD_ERROR.getSqlState().getValue(), is("42S22"));
+        assertThat(MySQLVendorError.ER_BAD_FIELD_ERROR.getReason(), is("Unknown column '%s' in '%s'"));
+    }
+    
     @Test
     void assertErrorOnModifyingGtidExecutedTable() {
         assertThat(MySQLVendorError.ER_ERROR_ON_MODIFYING_GTID_EXECUTED_TABLE.getVendorCode(), is(3176));