@@ -53,11 +53,31 @@ public override void AddForeignKey(
5353 string [ ] parentColumns ,
5454 ForeignKeyConstraintType constraint )
5555 {
56+ if ( string . IsNullOrWhiteSpace ( name ) )
57+ {
58+ throw new Exception ( "A FK name is mandatory" ) ;
59+ }
60+
5661 var sqliteTableInfo = GetSQLiteTableInfo ( childTable ) ;
5762
63+ // Get all unique constraint names if available
64+ var uniqueConstraintNames = sqliteTableInfo . Uniques . Select ( x => x . Name ) . ToList ( ) ;
65+
66+ // Get all FK constraint names if available
67+ var foreignKeyNames = sqliteTableInfo . ForeignKeys . Select ( x => x . Name ) . ToList ( ) ;
68+
69+ var names = uniqueConstraintNames . Concat ( foreignKeyNames )
70+ . Distinct ( )
71+ . Where ( x => ! string . IsNullOrWhiteSpace ( x ) )
72+ . ToList ( ) ;
73+
74+ if ( names . Any ( x => x . Equals ( name , StringComparison . OrdinalIgnoreCase ) ) )
75+ {
76+ throw new Exception ( $ "Constraint name { name } already exists") ;
77+ }
78+
5879 var foreignKey = new ForeignKeyConstraint
5980 {
60- // SQLite does not support FK names
6181 ChildColumns = childColumns ,
6282 ChildTable = childTable ,
6383 Name = name ,
@@ -149,6 +169,7 @@ public override ForeignKeyConstraint[] GetForeignKeyConstraints(string tableName
149169 var foreignKeyExtract = new ForeignKeyExtract ( )
150170 {
151171 ChildColumnNames = parenthesisContents [ 0 ] . Split ( ',' ) . Select ( x => x . Trim ( ) ) . ToList ( ) ,
172+ ForeignKeyString = fkPart ,
152173 ParentColumnNames = parenthesisContents [ 1 ] . Split ( ',' ) . Select ( x => x . Trim ( ) ) . ToList ( ) ,
153174 } ;
154175
@@ -816,12 +837,40 @@ public override List<string> GetDatabases()
816837
817838 public override bool ConstraintExists ( string table , string name )
818839 {
819- throw new NotSupportedException ( "SQLite does not offer constraint names e.g. for unique, check constraints. You need to use alternative ways." ) ;
840+ var constraintNames = GetConstraints ( table ) ;
841+
842+ var exists = constraintNames . Any ( x => x . Equals ( name , StringComparison . OrdinalIgnoreCase ) ) ;
843+
844+ return exists ;
820845 }
821846
822847 public override string [ ] GetConstraints ( string table )
823848 {
824- throw new NotSupportedException ( "SQLite does not offer constraint names e.g. for unique, check constraints You need to drop them using alternative ways." ) ;
849+ var sqliteInfo = GetSQLiteTableInfo ( table ) ;
850+
851+ var foreignKeyNames = sqliteInfo . ForeignKeys
852+ . Select ( x => x . Name )
853+ . ToList ( ) ;
854+
855+ var uniqueConstraints = sqliteInfo . Uniques
856+ . Select ( x => x . Name )
857+ . ToList ( ) ;
858+
859+ // TODO add PK and CHECK
860+
861+ var names = foreignKeyNames . Concat ( uniqueConstraints )
862+ . Where ( x => ! string . IsNullOrWhiteSpace ( x ) )
863+ . ToArray ( ) ;
864+
865+ var distinctNames = names . Distinct ( StringComparer . OrdinalIgnoreCase )
866+ . ToArray ( ) ;
867+
868+ if ( names . Length != distinctNames . Length )
869+ {
870+ throw new Exception ( $ "There are duplicate constraint names in table { table } '") ;
871+ }
872+
873+ return distinctNames ;
825874 }
826875
827876 public override string [ ] GetTables ( )
@@ -1059,11 +1108,15 @@ public override void AddTable(string name, string engine, params IDbField[] fiel
10591108 {
10601109 if ( ! string . IsNullOrEmpty ( u . Name ) )
10611110 {
1062- stringBuilder . Append ( $ " CONSTRAINT { u . Name } ") ;
1111+ stringBuilder . Append ( $ ", CONSTRAINT { u . Name } ") ;
1112+ }
1113+ else
1114+ {
1115+ stringBuilder . Append ( ", " ) ;
10631116 }
10641117
10651118 var uniqueColumnsCommaSeparated = string . Join ( ", " , u . KeyColumns ) ;
1066- stringBuilder . Append ( $ ", UNIQUE ({ uniqueColumnsCommaSeparated } )") ;
1119+ stringBuilder . Append ( $ " UNIQUE ({ uniqueColumnsCommaSeparated } )") ;
10671120 }
10681121
10691122 var foreignKeys = fields . Where ( x => x is ForeignKeyConstraint ) . Cast < ForeignKeyConstraint > ( ) . ToArray ( ) ;
@@ -1169,14 +1222,18 @@ public override void RemoveAllIndexes(string tableName)
11691222
11701223 public List < Unique > GetUniques ( string tableName )
11711224 {
1225+ var regEx = new Regex ( @"(?<=,)\s*(CONSTRAINT\s+\w+\s+)?UNIQUE\s*\(\s*[\w\s,]+\s*\)\s*(?=,|\s*\))" ) ;
1226+ var regExConstraintName = new Regex ( @"(?<=CONSTRAINT\s+)\w+(?=\s+)" ) ;
1227+ var regExParenthesis = new Regex ( @"(?<=\().+(?=\))" ) ;
1228+
11721229 List < Unique > uniques = [ ] ;
11731230
11741231 var pragmaIndexListItems = GetPragmaIndexListItems ( tableName ) ;
11751232
11761233 // Here we filter for origin u and unique while in "GetIndexes()" we exclude them.
1177- // If pk is set then it was added by using a primary key. If so this is handled by "GetColumns()".
1178- // If c is set it was created by using CREATE INDEX. At this moment in time this migrator does not support UNIQUE indexes but only normal indexes
1179- // so u should never be set 30.06.2025).
1234+ // If "pk" is set then it was added by using a primary key. If so this is handled by "GetColumns()".
1235+ // If "c" is set it was created by using CREATE INDEX. At this moment in time this migrator does not support UNIQUE indexes but only normal indexes
1236+ // so "u" should never be set 30.06.2025).
11801237 var uniqueConstraints = pragmaIndexListItems . Where ( x => x . Unique && x . Origin == "u" )
11811238 . ToList ( ) ;
11821239
@@ -1197,6 +1254,41 @@ public List<Unique> GetUniques(string tableName)
11971254 uniques . Add ( unique ) ;
11981255 }
11991256
1257+ var createScript = GetSqlCreateTableScript ( tableName ) ;
1258+
1259+ var matches = regEx . Matches ( createScript ) . Cast < Match > ( ) . Where ( x => x . Success ) . Select ( x => x . Value . Trim ( ) ) . ToList ( ) ;
1260+
1261+ // We can only use the ones containing a starting with CONSTRAINT
1262+ var matchesHavingName = matches . Where ( x => x . StartsWith ( "CONSTRAINT" ) ) . ToList ( ) ;
1263+
1264+ foreach ( var constraintString in matchesHavingName )
1265+ {
1266+ var constraintNameMatch = regExConstraintName . Match ( constraintString ) ;
1267+
1268+ if ( ! constraintNameMatch . Success )
1269+ {
1270+ throw new Exception ( "Cannot extract constraint name - severe issue. Please file an issue" ) ;
1271+ }
1272+
1273+ var constraintName = constraintNameMatch . Value ;
1274+
1275+ var parenthesisMatch = regExParenthesis . Match ( constraintString ) ;
1276+
1277+ if ( ! parenthesisMatch . Success )
1278+ {
1279+ throw new Exception ( "Cannot extract parenthesis content for UNIQUE constraint - severe issue. Please file an issue" ) ;
1280+ }
1281+
1282+ var columns = parenthesisMatch . Value . Split ( ',' ) . Select ( x => x . Trim ( ) ) . ToList ( ) ;
1283+
1284+ var unique = uniques . Where ( x => x . KeyColumns . SequenceEqual ( columns ) ) . SingleOrDefault ( ) ;
1285+
1286+ if ( unique != null )
1287+ {
1288+ unique . Name = constraintName ;
1289+ }
1290+ }
1291+
12001292 return uniques ;
12011293 }
12021294
0 commit comments