1616using System . Collections . Generic ;
1717using System . Data ;
1818using System . Globalization ;
19+ using System . Linq ;
20+ using System . Text . RegularExpressions ;
1921using Index = DotNetProjects . Migrator . Framework . Index ;
2022
2123namespace DotNetProjects . Migrator . Providers . Impl . SqlServer ;
@@ -339,12 +341,19 @@ public override Column[] GetColumns(string table)
339341 using (
340342 var reader =
341343 ExecuteQuery ( cmd ,
342- string . Format ( "select COLUMN_NAME, IS_NULLABLE, DATA_TYPE, ISNULL(CHARACTER_MAXIMUM_LENGTH, NUMERIC_PRECISION), COLUMN_DEFAULT, NUMERIC_SCALE from INFORMATION_SCHEMA.COLUMNS where table_name = '{0}'" , table ) ) )
344+ string . Format ( "SELECT COLUMN_NAME, IS_NULLABLE, DATA_TYPE, ISNULL(CHARACTER_MAXIMUM_LENGTH , NUMERIC_PRECISION), COLUMN_DEFAULT, NUMERIC_SCALE, CHARACTER_MAXIMUM_LENGTH from INFORMATION_SCHEMA.COLUMNS where table_name = '{0}'" , table ) ) )
343345 {
344346 while ( reader . Read ( ) )
345347 {
346348 var column = new Column ( reader . GetString ( 0 ) , DbType . String ) ;
347349
350+ var defaultValueOrdinal = reader . GetOrdinal ( "COLUMN_DEFAULT" ) ;
351+ var dataTypeOrdinal = reader . GetOrdinal ( "DATA_TYPE" ) ;
352+ var characterMaximumLengthOrdinal = reader . GetOrdinal ( "CHARACTER_MAXIMUM_LENGTH" ) ;
353+
354+ var defaultValueString = reader . IsDBNull ( defaultValueOrdinal ) ? null : reader . GetString ( defaultValueOrdinal ) . Trim ( ) ;
355+ var characterMaximumLength = reader . IsDBNull ( characterMaximumLengthOrdinal ) ? ( int ? ) null : reader . GetInt32 ( characterMaximumLengthOrdinal ) ;
356+
348357 if ( pkColumns . Contains ( column . Name ) )
349358 {
350359 column . ColumnProperty |= ColumnProperty . PrimaryKey ;
@@ -358,30 +367,78 @@ public override Column[] GetColumns(string table)
358367 var nullableStr = reader . GetString ( 1 ) ;
359368 var isNullable = nullableStr == "YES" ;
360369
361- if ( ! reader . IsDBNull ( 2 ) )
370+ var dataTypeString = reader . GetString ( dataTypeOrdinal ) ;
371+
372+ if ( dataTypeString == "date" )
373+ {
374+ column . MigratorDbType = MigratorDbType . Date ;
375+ }
376+ else if ( dataTypeString == "int" )
377+ {
378+ column . MigratorDbType = MigratorDbType . Int32 ;
379+ }
380+ else if ( dataTypeString == "smallint" )
381+ {
382+ column . MigratorDbType = MigratorDbType . Int16 ;
383+ }
384+ else if ( dataTypeString == "tinyint" )
385+ {
386+ column . MigratorDbType = MigratorDbType . Byte ;
387+ }
388+ else if ( dataTypeString == "bit" )
389+ {
390+ column . MigratorDbType = MigratorDbType . Boolean ;
391+ }
392+ else if ( dataTypeString == "money" )
393+ {
394+ column . MigratorDbType = MigratorDbType . Currency ;
395+ }
396+ else if ( dataTypeString == "float" )
397+ {
398+ column . MigratorDbType = MigratorDbType . Double ;
399+ }
400+ else if ( new [ ] { "text" , "nchar" , "ntext" , "varchar" , "nvarchar" } . Contains ( dataTypeString ) )
401+ {
402+ // We use string for all string-like data types.
403+ column . MigratorDbType = MigratorDbType . String ;
404+ column . Size = characterMaximumLength . Value ;
405+ }
406+ else if ( dataTypeString == "decimal" )
407+ {
408+ column . MigratorDbType = MigratorDbType . Decimal ;
409+ }
410+ else if ( dataTypeString == "datetime" )
362411 {
363- var type = reader . GetString ( 2 ) ;
364- column . Type = Dialect . GetDbTypeFromString ( type ) ;
412+ column . MigratorDbType = MigratorDbType . DateTime ;
413+ }
414+ else if ( dataTypeString == "datetime2" )
415+ {
416+ column . MigratorDbType = MigratorDbType . DateTime2 ;
417+ }
418+ else if ( dataTypeString == "datetimeoffset" )
419+ {
420+ column . MigratorDbType = MigratorDbType . DateTimeOffset ;
421+ }
422+ else if ( dataTypeString == "binary" || dataTypeString == "varbinary" )
423+ {
424+ column . MigratorDbType = MigratorDbType . Binary ;
425+ }
426+ else if ( dataTypeString == "uniqueidentifier" )
427+ {
428+ column . MigratorDbType = MigratorDbType . Guid ;
429+ }
430+ else
431+ {
432+ throw new NotImplementedException ( $ "The data type '{ dataTypeString } ' is not implemented yet. Please file an issue.") ;
365433 }
366434
367435 if ( ! reader . IsDBNull ( 3 ) )
368436 {
369437 column . Size = reader . GetInt32 ( 3 ) ;
370438 }
371439
372- if ( ! reader . IsDBNull ( 4 ) )
440+ if ( defaultValueString != null )
373441 {
374- column . DefaultValue = reader . GetValue ( 4 ) ;
375-
376- if ( column . DefaultValue . ToString ( ) [ 1 ] == '(' || column . DefaultValue . ToString ( ) [ 1 ] == '\' ' )
377- {
378- column . DefaultValue = column . DefaultValue . ToString ( ) . Substring ( 2 , column . DefaultValue . ToString ( ) . Length - 4 ) ; // Example "((10))" or "('false')"
379- }
380- else
381- {
382- column . DefaultValue = column . DefaultValue . ToString ( ) . Substring ( 1 , column . DefaultValue . ToString ( ) . Length - 2 ) ; // Example "(CONVERT([datetime],'20000101',(112)))"
383- }
384-
385442 if ( column . Type == DbType . Int16 || column . Type == DbType . Int32 || column . Type == DbType . Int64 )
386443 {
387444 column . DefaultValue = long . Parse ( column . DefaultValue . ToString ( ) ) ;
@@ -400,20 +457,27 @@ public override Column[] GetColumns(string table)
400457 }
401458 else if ( column . Type == DbType . DateTime || column . Type == DbType . DateTime2 )
402459 {
403- if ( column . DefaultValue is string defValCv && defValCv . StartsWith ( "CONVERT(" ) )
460+ // (CONVERT([datetime],'2000-01-02 03:04:05.000',(121)))
461+ // 121 is a pattern: it contains milliseconds
462+ // Search for 121 here: https://learn.microsoft.com/de-de/sql/t-sql/functions/cast-and-convert-transact-sql?view=sql-server-ver17
463+ var regexDateTimeConvert121 = new Regex ( @"(?<=^\(CONVERT\([\[]+datetime[\]]+,')[^']+(?='\s*,\s*\(121\s*\)\)\)$)" ) ;
464+ var match121 = regexDateTimeConvert121 . Match ( defaultValueString ) ;
465+
466+ if ( match121 . Success )
404467 {
405- var dt = defValCv . Substring ( ( defValCv . IndexOf ( "'" ) + 1 ) , defValCv . IndexOf ( "'" , defValCv . IndexOf ( "'" ) + 1 ) - defValCv . IndexOf ( "'" ) - 1 ) ;
406- var d = DateTime . ParseExact ( dt , "yyyy-MM-dd HH:mm:ss.fff" , CultureInfo . InvariantCulture , DateTimeStyles . AdjustToUniversal | DateTimeStyles . AssumeUniversal ) ;
407- column . DefaultValue = d ;
468+ // We convert to UTC since we restrict date time default values to UTC on default value definition.
469+ column . DefaultValue = DateTime . ParseExact ( match121 . Value , "yyyy-MM-dd HH:mm:ss.fff" , CultureInfo . InvariantCulture , DateTimeStyles . AdjustToUniversal | DateTimeStyles . AssumeUniversal ) ;
408470 }
409- else if ( column . DefaultValue is string defVal )
471+ else if ( defaultValueString is string defVal )
410472 {
473+ // Not tested
411474 var dt = defVal ;
412475 if ( defVal . StartsWith ( "'" ) )
413476 {
414477 dt = defVal . Substring ( 1 , defVal . Length - 2 ) ;
415478 }
416479
480+ // We convert to UTC since we restrict date time default values to UTC on default value definition.
417481 var d = DateTime . ParseExact ( dt , "yyyy-MM-dd HH:mm:ss" , CultureInfo . InvariantCulture , DateTimeStyles . AdjustToUniversal | DateTimeStyles . AssumeUniversal ) ;
418482 column . DefaultValue = d ;
419483 }
@@ -427,6 +491,7 @@ public override Column[] GetColumns(string table)
427491 if ( column . DefaultValue is string defVal )
428492 {
429493 var dt = defVal ;
494+
430495 if ( defVal . StartsWith ( "'" ) )
431496 {
432497 dt = defVal . Substring ( 1 , defVal . Length - 2 ) ;
@@ -436,6 +501,17 @@ public override Column[] GetColumns(string table)
436501 column . DefaultValue = d ;
437502 }
438503 }
504+ else if ( column . MigratorDbType == MigratorDbType . Decimal )
505+ {
506+ // We assume ((1.234))
507+ var decimalString = defaultValueString . Replace ( "(" , "" ) . Replace ( ")" , "" ) ;
508+
509+ column . DefaultValue = decimal . Parse ( decimalString , CultureInfo . InvariantCulture ) ;
510+ }
511+ else
512+ {
513+ throw new NotImplementedException ( $ "Cannot parse the default value of { column . Name } . Type '' is not implemented yet.") ;
514+ }
439515 }
440516 if ( ! reader . IsDBNull ( 5 ) )
441517 {
0 commit comments