From 6ece400c36181a5290d9ed21ba9a7e4d2703d6b2 Mon Sep 17 00:00:00 2001 From: elianddb Date: Tue, 24 Feb 2026 12:19:28 -0800 Subject: [PATCH 1/2] add `SQLError` and a MySQL-specific constructor to automatically initialize error code and SQLSTATE for `sql.CastSQLError` --- sql/errors.go | 42 +++++++++++++++++++++++++++++++++++++++--- 1 file changed, 39 insertions(+), 3 deletions(-) diff --git a/sql/errors.go b/sql/errors.go index f147d7e851..9eb25c45f9 100644 --- a/sql/errors.go +++ b/sql/errors.go @@ -970,6 +970,9 @@ var ( // ErrDistinctOnMatchOrderBy is returned when DISTINCT ON does not match the initial ORDER BY expressions ErrDistinctOnMatchOrderBy = errors.NewKind("SELECT DISTINCT ON expressions must match initial ORDER BY expressions") + + // ErrWrongDBName is returned for illegal database names with the [mysql.ERWrongDbName] error code and [mysql.SSClientError] SQLSTATE. + ErrWrongDBName = newMySQLKind("Incorrect database name '%s'", mysql.ERWrongDbName, mysql.SSClientError) ) // CastSQLError returns a *mysql.SQLError with the error code and in some cases, also a SQL state, populated for the @@ -983,10 +986,8 @@ func CastSQLError(err error) *mysql.SQLError { if mysqlErr, ok := err.(*mysql.SQLError); ok { return mysqlErr } - var code int - var sqlState string = "" - + var sqlState = "" if w, ok := err.(WrappedInsertError); ok { return CastSQLError(w.Cause) } @@ -995,6 +996,12 @@ func CastSQLError(err error) *mysql.SQLError { return CastSQLError(wm.Err) } + for _, mySQLErr := range mySQLErrors { + if mySQLErr.Kind.Is(err) { + return mysql.NewSQLError(mySQLErr.Code, mySQLErr.SQLState, "%s", err.Error()) + } + } + switch { case ErrTableNotFound.Is(err): code = mysql.ERNoSuchTable @@ -1064,6 +1071,35 @@ func CastSQLError(err error) *mysql.SQLError { return mysql.NewSQLError(code, sqlState, "%s", err.Error()) } +// mySQLErrors contain MySQL-specific [sql.SQLError] with their other metadata. +var mySQLErrors []SQLError + +// newMySQLKind creates [sql.SQLError] specifically for mySQLErrors that is automatically interpreted by +// [sql.CastSQLError]. If |SQLState| is omitted, an empty string takes its place. +func newMySQLKind(msg string, code int, sqlState ...string) *errors.Kind { + err := errors.NewKind(msg) + state := "" + if len(sqlState) > 0 { + state = sqlState[0] + } + mySQLErrors = append(mySQLErrors, SQLError{ + Kind: err, + Code: code, + SQLState: state, + }) + return err +} + +// SQLError identifies the error family and other metadata for SQL errors. +type SQLError struct { + // Kind identifies the engine error family. + Kind *errors.Kind + // Code is the numeric error code, and is implementation specific (e.g., MySQL error codes are not cross-platform). + Code int + // SQLState is the five-character string taken from ANSI SQL and ODBC. + SQLState string +} + // UnwrapError removes any wrapping errors (e.g. WrappedInsertError) around the specified error and // returns the first non-wrapped error type. func UnwrapError(err error) error { From 413043195516172c7959d403fb7ff7bbcde0fc12 Mon Sep 17 00:00:00 2001 From: elianddb Date: Wed, 25 Feb 2026 12:27:28 -0800 Subject: [PATCH 2/2] mv Dolt-specific `/` rune error handling to dolt repository --- enginetest/queries/script_queries.go | 4 ---- sql/planbuilder/ddl.go | 4 ---- 2 files changed, 8 deletions(-) diff --git a/enginetest/queries/script_queries.go b/enginetest/queries/script_queries.go index 081d024909..d1dd30fd59 100644 --- a/enginetest/queries/script_queries.go +++ b/enginetest/queries/script_queries.go @@ -16230,10 +16230,6 @@ var CreateDatabaseScripts = []ScriptTest{ { Name: "CREATE DATABASE error handling", Assertions: []ScriptTestAssertion{ - { - Query: "create database `abc/def`", - ExpectedErr: sql.ErrInvalidDatabaseName, - }, { Query: "CREATE DATABASE newtestdb CHARACTER SET utf8mb4 ENCRYPTION='N'", Expected: []sql.Row{{types.NewOkResult(1)}}, diff --git a/sql/planbuilder/ddl.go b/sql/planbuilder/ddl.go index a9cb2165b4..685f9e6b3d 100644 --- a/sql/planbuilder/ddl.go +++ b/sql/planbuilder/ddl.go @@ -1820,10 +1820,6 @@ func (b *Builder) buildDBDDL(inScope *scope, c *ast.DBDDL) (outScope *scope) { outScope = inScope.push() switch strings.ToLower(c.Action) { case ast.CreateStr: - if strings.ContainsRune(c.DBName, '/') { - b.handleErr(sql.ErrInvalidDatabaseName.New(c.DBName)) - } - var charsetStr, collationStr string if len(c.CharsetCollate) != 0 && b.ctx != nil && b.ctx.Session != nil { b.ctx.Session.Warn(&sql.Warning{