From e6e58c3a56a8c77eb4ba3222690bdc49a3ec9ed9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jean-Philippe=20Qu=C3=A9m=C3=A9ner?= Date: Thu, 9 Oct 2025 21:17:50 +0200 Subject: [PATCH] fix: implement ColumnCheckSQL to make AddColumn idempotent (#112227) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit SQLite dialect now checks column existence via PRAGMA table_info, enabling IfColumnNotExistsCondition to work correctly. Previously, BaseDialect returned empty SQL, so AddColumn ran unconditionally and could fail with “duplicate column name” under parallel CI runs. - Prevents duplicate-column errors in SQLite migrations (e.g. unified storage adding previous_resource_version) when migration locking/logging don’t serialize execution. - No change for other dialects. --- pkg/services/sqlstore/migrator/sqlite_dialect.go | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/pkg/services/sqlstore/migrator/sqlite_dialect.go b/pkg/services/sqlstore/migrator/sqlite_dialect.go index d4302c18181..921ef9696c4 100644 --- a/pkg/services/sqlstore/migrator/sqlite_dialect.go +++ b/pkg/services/sqlstore/migrator/sqlite_dialect.go @@ -87,6 +87,17 @@ func (db *SQLite3) IndexCheckSQL(tableName, indexName string) (string, []any) { return sql, args } +func (db *SQLite3) ColumnCheckSQL(tableName, columnName string) (string, []any) { + // Use PRAGMA table_info to check if a column exists on a table. In SQLite, quoting with backticks inside + // pragma_table_info() can be interpreted as an identifier/column. Instead, pass the table name as a + // string literal to avoid ambiguity. We cannot parameterize identifiers, but pragma_table_info accepts string + // literals, so we embed a single-quoted literal safely by replacing single quotes if any. + // Note: tableName is expected to be a trusted identifier from migrations. + safeTable := strings.ReplaceAll(tableName, "'", "''") + sql := "SELECT 1 FROM pragma_table_info('" + safeTable + "') WHERE name = ?" + return sql, []any{columnName} +} + func (db *SQLite3) DropIndexSQL(tableName string, index *Index) string { quote := db.Quote // var unique string