77using System . Linq ;
88using System . Text ;
99using System . Text . RegularExpressions ;
10+ using System . Xml . Linq ;
1011using ForeignKeyConstraint = DotNetProjects . Migrator . Framework . ForeignKeyConstraint ;
1112using Index = DotNetProjects . Migrator . Framework . Index ;
1213
@@ -108,7 +109,7 @@ public string GetSqlCreateTableScript(string table)
108109 {
109110 if ( reader . Read ( ) )
110111 {
111- sqlCreateTableScript = ( string ) reader [ 0 ] ;
112+ sqlCreateTableScript = reader . IsDBNull ( 0 ) ? null : ( string ) reader [ 0 ] ;
112113 }
113114 }
114115
@@ -340,8 +341,10 @@ public DbType ExtractTypeFromColumnDef(string columnDef)
340341
341342 public override void RemoveForeignKey ( string table , string name )
342343 {
343- //Check the impl...
344- return ;
344+ var sqliteTableInfo = GetSQLiteTableInfo ( table ) ;
345+ sqliteTableInfo . ForeignKeys . RemoveAll ( x => x . Name . Equals ( name , StringComparison . OrdinalIgnoreCase ) ) ;
346+
347+ RecreateTable ( sqliteTableInfo ) ;
345348 }
346349
347350 public string [ ] GetCreateIndexSqlStrings ( string table )
@@ -404,6 +407,11 @@ public override void RemoveColumn(string tableName, string column)
404407
405408 var sqliteInfoMainTable = GetSQLiteTableInfo ( tableName ) ;
406409
410+ if ( sqliteInfoMainTable . CheckConstraints . Any ( x => x . CheckConstraintString . Equals ( column , StringComparison . OrdinalIgnoreCase ) ) )
411+ {
412+ throw new Exception ( "A check constraint contains the column you want to remove. Remove the check constraint first" ) ;
413+ }
414+
407415 if ( ! sqliteInfoMainTable . ColumnMappings . Any ( x => x . OldName == column ) )
408416 {
409417 throw new MigrationException ( "Column not found" ) ;
@@ -645,19 +653,26 @@ public override void RemoveConstraint(string table, string name)
645653 {
646654 var sqliteTableInfo = GetSQLiteTableInfo ( table ) ;
647655 sqliteTableInfo . Uniques . RemoveAll ( x => x . Name . Equals ( name , StringComparison . OrdinalIgnoreCase ) ) ;
656+ sqliteTableInfo . CheckConstraints . RemoveAll ( x => x . Name . Equals ( name , StringComparison . OrdinalIgnoreCase ) ) ;
648657
649658 RecreateTable ( sqliteTableInfo ) ;
650659 }
651660
652661 public SQLiteTableInfo GetSQLiteTableInfo ( string tableName )
653662 {
663+ if ( ! TableExists ( tableName ) )
664+ {
665+ return null ;
666+ }
667+
654668 var sqliteTable = new SQLiteTableInfo
655669 {
656670 TableNameMapping = new MappingInfo { OldName = tableName , NewName = tableName } ,
657671 Columns = GetColumns ( tableName ) . ToList ( ) ,
658672 ForeignKeys = GetForeignKeyConstraints ( tableName ) . ToList ( ) ,
659673 Indexes = GetIndexes ( tableName ) . ToList ( ) ,
660- Uniques = GetUniques ( tableName ) . ToList ( )
674+ Uniques = GetUniques ( tableName ) . ToList ( ) ,
675+ CheckConstraints = GetCheckConstraints ( tableName )
661676 } ;
662677
663678 sqliteTable . ColumnMappings = sqliteTable . Columns
@@ -715,6 +730,7 @@ public void RecreateTable(SQLiteTableInfo sqliteTableInfo)
715730 var foreignKeyDbFields = sqliteTableInfo . ForeignKeys . Cast < IDbField > ( ) ;
716731 var indexDbFields = sqliteTableInfo . Indexes . Cast < IDbField > ( ) ;
717732 var uniqueDbFields = sqliteTableInfo . Uniques . Cast < IDbField > ( ) ;
733+ var checkConstraintDbFields = sqliteTableInfo . CheckConstraints . Cast < IDbField > ( ) ;
718734
719735 var dbFields = columnDbFields . Concat ( foreignKeyDbFields )
720736 . Concat ( uniqueDbFields )
@@ -848,6 +864,11 @@ public override List<string> GetDatabases()
848864
849865 public override bool ConstraintExists ( string table , string name )
850866 {
867+ if ( ! TableExists ( table ) )
868+ {
869+ throw new Exception ( $ "Table '{ table } ' does not exist.") ;
870+ }
871+
851872 var constraintNames = GetConstraints ( table ) ;
852873
853874 var exists = constraintNames . Any ( x => x . Equals ( name , StringComparison . OrdinalIgnoreCase ) ) ;
@@ -857,6 +878,11 @@ public override bool ConstraintExists(string table, string name)
857878
858879 public override string [ ] GetConstraints ( string table )
859880 {
881+ if ( ! TableExists ( table ) )
882+ {
883+ throw new Exception ( $ "Table '{ table } ' does not exist.") ;
884+ }
885+
860886 var sqliteInfo = GetSQLiteTableInfo ( table ) ;
861887
862888 var foreignKeyNames = sqliteInfo . ForeignKeys
@@ -1131,6 +1157,8 @@ public override void AddTable(string name, string engine, params IDbField[] fiel
11311157 stringBuilder . Append ( string . Format ( ", PRIMARY KEY ({0})" , string . Join ( ", " , pks . ToArray ( ) ) ) ) ;
11321158 }
11331159
1160+
1161+ // Uniques
11341162 var uniques = fields . Where ( x => x is Unique ) . Cast < Unique > ( ) . ToArray ( ) ;
11351163
11361164 foreach ( var u in uniques )
@@ -1148,6 +1176,7 @@ public override void AddTable(string name, string engine, params IDbField[] fiel
11481176 stringBuilder . Append ( $ " UNIQUE ({ uniqueColumnsCommaSeparated } )") ;
11491177 }
11501178
1179+ // Foreign keys
11511180 var foreignKeys = fields . Where ( x => x is ForeignKeyConstraint ) . Cast < ForeignKeyConstraint > ( ) . ToArray ( ) ;
11521181
11531182 List < string > foreignKeyStrings = [ ] ;
@@ -1166,12 +1195,25 @@ public override void AddTable(string name, string engine, params IDbField[] fiel
11661195 foreignKeyStrings . Add ( $ "CONSTRAINT { fk . Name } FOREIGN KEY ({ sourceColumnNamesQuotedString } ) REFERENCES { parentTableNameQuoted } ({ parentColumnNamesQuotedString } )") ;
11671196 }
11681197
1169- if ( foreignKeyStrings . Count != 0 )
1198+ if ( foreignKeyStrings . Count > 0 )
11701199 {
11711200 stringBuilder . Append ( ", " ) ;
11721201 stringBuilder . Append ( string . Join ( ", " , foreignKeyStrings ) ) ;
11731202 }
11741203
1204+ // Check Constraints
1205+ var checkConstraints = fields . Where ( x => x is CheckConstraint ) . OfType < CheckConstraint > ( ) . ToArray ( ) ;
1206+ List < string > checkConstraintStrings = [ ] ;
1207+
1208+ foreach ( var checkConstraint in checkConstraints )
1209+ {
1210+ checkConstraintStrings . Add ( $ "CONSTRAINT { checkConstraint . Name } CHECK ({ checkConstraint . CheckConstraintString } )") ;
1211+ }
1212+
1213+ if ( checkConstraintStrings . Count > 0 )
1214+ {
1215+ stringBuilder . Append ( $ ", { string . Join ( ", " , checkConstraintStrings ) } ") ;
1216+ }
11751217
11761218 stringBuilder . Append ( ')' ) ;
11771219
@@ -1251,6 +1293,11 @@ public override void RemoveAllIndexes(string tableName)
12511293
12521294 public List < Unique > GetUniques ( string tableName )
12531295 {
1296+ if ( ! TableExists ( tableName ) )
1297+ {
1298+ throw new Exception ( $ "Table '{ tableName } ' does not exist.") ;
1299+ }
1300+
12541301 var regEx = new Regex ( @"(?<=,)\s*(CONSTRAINT\s+\w+\s+)?UNIQUE\s*\(\s*[\w\s,]+\s*\)\s*(?=,|\s*\))" ) ;
12551302 var regExConstraintName = new Regex ( @"(?<=CONSTRAINT\s+)\w+(?=\s+)" ) ;
12561303 var regExParenthesis = new Regex ( @"(?<=\().+(?=\))" ) ;
@@ -1285,10 +1332,20 @@ public List<Unique> GetUniques(string tableName)
12851332
12861333 var createScript = GetSqlCreateTableScript ( tableName ) ;
12871334
1288- var matches = regEx . Matches ( createScript ) . Cast < Match > ( ) . Where ( x => x . Success ) . Select ( x => x . Value . Trim ( ) ) . ToList ( ) ;
1335+ var matches = regEx . Matches ( createScript ) ;
1336+ if ( matches . Count == 0 )
1337+ {
1338+ return [ ] ;
1339+ }
1340+
1341+ var constraintNames = matches
1342+ . OfType < Match > ( )
1343+ . Where ( x => x . Success && ! string . IsNullOrWhiteSpace ( x . Value ) )
1344+ . Select ( x => x . Value . Trim ( ) )
1345+ . ToList ( ) ;
12891346
12901347 // We can only use the ones containing a starting with CONSTRAINT
1291- var matchesHavingName = matches . Where ( x => x . StartsWith ( "CONSTRAINT" ) ) . ToList ( ) ;
1348+ var matchesHavingName = constraintNames . Where ( x => x . StartsWith ( "CONSTRAINT" ) ) . ToList ( ) ;
12921349
12931350 foreach ( var constraintString in matchesHavingName )
12941351 {
@@ -1397,6 +1454,58 @@ public List<PragmaTableInfoItem> GetPragmaTableInfoItems(string tableNameNotQuot
13971454 return pragmaTableInfoItems ;
13981455 }
13991456
1457+ public List < CheckConstraint > GetCheckConstraints ( string tableName )
1458+ {
1459+ if ( ! TableExists ( tableName ) )
1460+ {
1461+ throw new Exception ( $ "Table '{ tableName } ' does not exist.") ;
1462+ }
1463+
1464+ var checkConstraintRegex = new Regex ( @"(?<=,)[^,]+\s+[^,]+check[^,]+(?=[,|\)])" , RegexOptions . IgnoreCase ) ;
1465+ var braceContentRegex = new Regex ( @"(?<=^\().+(?=\)$)" ) ;
1466+
1467+ var script = GetSqlCreateTableScript ( tableName ) ;
1468+
1469+ var matches = checkConstraintRegex . Matches ( script ) ;
1470+
1471+ if ( matches == null )
1472+ {
1473+ return [ ] ;
1474+ }
1475+
1476+ var checkStrings = matches . OfType < Match > ( )
1477+ . Where ( x => x . Success )
1478+ . Select ( x => x . Value )
1479+ . ToList ( ) ;
1480+
1481+ List < CheckConstraint > checkConstraints = [ ] ;
1482+
1483+ foreach ( var checkString in checkStrings )
1484+ {
1485+ var splitted = checkString . Trim ( ) . Split ( ' ' )
1486+ . Select ( x => x . Trim ( ) )
1487+ . ToList ( ) ;
1488+
1489+ if ( ! splitted [ 0 ] . Equals ( "CONSTRAINT" , StringComparison . OrdinalIgnoreCase ) || ! splitted [ 2 ] . Equals ( "CHECK" , StringComparison . OrdinalIgnoreCase ) )
1490+ {
1491+ throw new Exception ( $ "Cannot parse check constraint in table { tableName } ") ;
1492+ }
1493+
1494+ var checkConstraintStringWithBraces = string . Join ( " " , splitted . Skip ( 3 ) ) . Trim ( ) ;
1495+ var checkConstraintString = braceContentRegex . Match ( checkConstraintStringWithBraces ) ;
1496+
1497+ var checkConstraint = new CheckConstraint
1498+ {
1499+ Name = splitted [ 1 ] ,
1500+ CheckConstraintString = checkConstraintString . Value
1501+ } ;
1502+
1503+ checkConstraints . Add ( checkConstraint ) ;
1504+ }
1505+
1506+ return checkConstraints ;
1507+ }
1508+
14001509 protected override void ConfigureParameterWithValue ( IDbDataParameter parameter , int index , object value )
14011510 {
14021511 if ( value is ushort )
0 commit comments