From 3e2709482bb2a95535e4ae382d495025b8f7b712 Mon Sep 17 00:00:00 2001 From: Daniele Debernardi Date: Fri, 14 Aug 2026 21:28:50 +0200 Subject: [PATCH] Add support for index prefix lengths (MySQL/MariaDB) --- .../Entities/Database/ABaseDbIndex.cs | 10 ++++++++++ .../DatabaseProviders/ADatabaseProvider.cs | 1 + .../DatabaseProviders/MySqlDatabaseProvider.cs | 3 ++- .../SqlScripters/MySqlScripter.cs | 11 +++++++++-- .../Datasources/sakila-schema-mariadb.sql | 1 + .../Datasources/sakila-schema-mysql.sql | 2 ++ 6 files changed, 25 insertions(+), 3 deletions(-) diff --git a/SQLSchemaCompare.Core/Entities/Database/ABaseDbIndex.cs b/SQLSchemaCompare.Core/Entities/Database/ABaseDbIndex.cs index bb5247b9..7e571ef2 100644 --- a/SQLSchemaCompare.Core/Entities/Database/ABaseDbIndex.cs +++ b/SQLSchemaCompare.Core/Entities/Database/ABaseDbIndex.cs @@ -14,6 +14,11 @@ public class ABaseDbIndex : ABaseDbConstraint /// Used only by the DatabaseProvider to group the indexes and fill the ColumnDescending list public bool IsDescending { get; set; } + /// + /// Gets or sets the length of the prefix. + /// + public long? PrefixLength { get; set; } + /// /// Gets or sets a value indicating whether the index is included. /// @@ -24,6 +29,11 @@ public class ABaseDbIndex : ABaseDbConstraint /// public List ColumnDescending { get; } = []; + /// + /// Gets the column prefix lengths. + /// + public List ColumnPrefixLengths { get; } = []; + /// /// Gets the included columns. /// diff --git a/SQLSchemaCompare.Infrastructure/DatabaseProviders/ADatabaseProvider.cs b/SQLSchemaCompare.Infrastructure/DatabaseProviders/ADatabaseProvider.cs index 97f926b4..1daa6b2d 100644 --- a/SQLSchemaCompare.Infrastructure/DatabaseProviders/ADatabaseProvider.cs +++ b/SQLSchemaCompare.Infrastructure/DatabaseProviders/ADatabaseProvider.cs @@ -208,6 +208,7 @@ protected TDatabase DiscoverDatabase(TDatabaseContext context, TaskInfo taskInfo index.Database = db; index.ColumnNames.AddRange(indexGroup.Where(x => !x.IsIncluded).OrderBy(x => x.OrdinalPosition).Select(x => x.ColumnName)); index.ColumnDescending.AddRange(indexGroup.Where(x => !x.IsIncluded).OrderBy(x => x.OrdinalPosition).Select(x => x.IsDescending)); + index.ColumnPrefixLengths.AddRange(indexGroup.Where(x => !x.IsIncluded).OrderBy(x => x.OrdinalPosition).Select(x => x.PrefixLength)); index.IncludedColumns.AddRange(indexGroup.Where(x => x.IsIncluded).OrderBy(x => x.OrdinalPosition).Select(x => x.ColumnName)); db.Indexes.Add(index); } diff --git a/SQLSchemaCompare.Infrastructure/DatabaseProviders/MySqlDatabaseProvider.cs b/SQLSchemaCompare.Infrastructure/DatabaseProviders/MySqlDatabaseProvider.cs index e56c94f9..e2449bb5 100644 --- a/SQLSchemaCompare.Infrastructure/DatabaseProviders/MySqlDatabaseProvider.cs +++ b/SQLSchemaCompare.Infrastructure/DatabaseProviders/MySqlDatabaseProvider.cs @@ -137,7 +137,8 @@ protected override IEnumerable GetIndexes(MySqlDatabaseContext con query.AppendLine(" CAST(s.SEQ_IN_INDEX AS SIGNED) AS 'OrdinalPosition',"); query.AppendLine(" CASE WHEN s.COLLATION = 'D' THEN TRUE ELSE FALSE END as 'IsDescending',"); query.AppendLine(" s.INDEX_TYPE AS 'IndexType',"); - query.AppendLine(" COALESCE(tc.CONSTRAINT_TYPE, 'INDEX') AS 'ConstraintType'"); + query.AppendLine(" COALESCE(tc.CONSTRAINT_TYPE, 'INDEX') AS 'ConstraintType',"); + query.AppendLine(" s.SUB_PART AS 'PrefixLength'"); query.AppendLine("FROM INFORMATION_SCHEMA.STATISTICS s"); query.AppendLine("LEFT OUTER JOIN INFORMATION_SCHEMA.TABLE_CONSTRAINTS tc"); query.AppendLine(" ON tc.table_name = s.table_name AND tc.table_schema = s.table_schema AND tc.constraint_name = s.index_name"); diff --git a/SQLSchemaCompare.Infrastructure/SqlScripters/MySqlScripter.cs b/SQLSchemaCompare.Infrastructure/SqlScripters/MySqlScripter.cs index 29aafde1..4b080a31 100644 --- a/SQLSchemaCompare.Infrastructure/SqlScripters/MySqlScripter.cs +++ b/SQLSchemaCompare.Infrastructure/SqlScripters/MySqlScripter.cs @@ -291,12 +291,19 @@ protected override string ScriptCreateIndex(ABaseDbIndex index) var scriptOrder = index.ColumnDescending.Any(x => x); var columnList = index.ColumnNames.Select((x, i) => { + var column = $"{this.ScriptHelper.ScriptObjectName(x)}"; + + if (indexMySql.IndexType == "BTREE" && index.ColumnPrefixLengths[i] != null) + { + column += $"({index.ColumnPrefixLengths[i]})"; + } + if (scriptOrder) { - return $"{this.ScriptHelper.ScriptObjectName(x)} {(index.ColumnDescending[i] ? "DESC" : "ASC")}"; + column += $" {(index.ColumnDescending[i] ? "DESC" : "ASC")}"; } - return $"{this.ScriptHelper.ScriptObjectName(x)}"; + return column; }); sb.Append("CREATE "); diff --git a/SQLSchemaCompare.Test/Datasources/sakila-schema-mariadb.sql b/SQLSchemaCompare.Test/Datasources/sakila-schema-mariadb.sql index b10677f9..8837e002 100644 --- a/SQLSchemaCompare.Test/Datasources/sakila-schema-mariadb.sql +++ b/SQLSchemaCompare.Test/Datasources/sakila-schema-mariadb.sql @@ -25,6 +25,7 @@ CREATE TABLE actor ( last_update TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, deleted BIT(1) NOT NULL DEFAULT b'0', PRIMARY KEY (actor_id), + KEY idx_actor_name (first_name(10), last_name(15)), KEY idx_actor_last_name (last_name) )ENGINE=InnoDB DEFAULT CHARSET=utf8mb4; diff --git a/SQLSchemaCompare.Test/Datasources/sakila-schema-mysql.sql b/SQLSchemaCompare.Test/Datasources/sakila-schema-mysql.sql index 0e5d8cca..553f128e 100644 --- a/SQLSchemaCompare.Test/Datasources/sakila-schema-mysql.sql +++ b/SQLSchemaCompare.Test/Datasources/sakila-schema-mysql.sql @@ -23,7 +23,9 @@ CREATE TABLE actor ( first_name VARCHAR(45) NOT NULL, last_name VARCHAR(45) NOT NULL, last_update TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, + deleted BIT(1) NOT NULL DEFAULT b'0', PRIMARY KEY (actor_id), + KEY idx_actor_name (first_name(10), last_name(15)), KEY idx_actor_last_name (last_name) )ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;