@@ -38,32 +38,48 @@ public SQLiteDialect()
38
38
39
39
protected virtual void RegisterColumnTypes ( )
40
40
{
41
+ // SQLite really has only five types, and a very lax typing system, see https://www.sqlite.org/datatype3.html
42
+ // Please do not map (again) fancy types that do not actually exist in SQLite, as this is kind of supported by
43
+ // SQLite but creates bugs in convert operations.
41
44
RegisterColumnType ( DbType . Binary , "BLOB" ) ;
42
- RegisterColumnType ( DbType . Byte , "TINYINT " ) ;
43
- RegisterColumnType ( DbType . Int16 , "SMALLINT " ) ;
44
- RegisterColumnType ( DbType . Int32 , "INT " ) ;
45
- RegisterColumnType ( DbType . Int64 , "BIGINT " ) ;
45
+ RegisterColumnType ( DbType . Byte , "INTEGER " ) ;
46
+ RegisterColumnType ( DbType . Int16 , "INTEGER " ) ;
47
+ RegisterColumnType ( DbType . Int32 , "INTEGER " ) ;
48
+ RegisterColumnType ( DbType . Int64 , "INTEGER " ) ;
46
49
RegisterColumnType ( DbType . SByte , "INTEGER" ) ;
47
50
RegisterColumnType ( DbType . UInt16 , "INTEGER" ) ;
48
51
RegisterColumnType ( DbType . UInt32 , "INTEGER" ) ;
49
52
RegisterColumnType ( DbType . UInt64 , "INTEGER" ) ;
50
- RegisterColumnType ( DbType . Currency , "NUMERIC" ) ;
51
- RegisterColumnType ( DbType . Decimal , "NUMERIC" ) ;
52
- RegisterColumnType ( DbType . Double , "DOUBLE" ) ;
53
- RegisterColumnType ( DbType . Single , "DOUBLE" ) ;
54
- RegisterColumnType ( DbType . VarNumeric , "NUMERIC" ) ;
53
+
54
+ // NUMERIC and REAL are almost the same, they are binary floating point numbers. There is only a slight difference
55
+ // for values without a floating part. They will be represented as integers with numeric, but still as floating
56
+ // values with real. The side-effect of this is numeric being able of storing exactly bigger integers than real.
57
+ // But it also creates bugs in division, when dividing two numeric happening to be integers, the result is then
58
+ // never fractional. So we use "REAL" for all.
59
+ RegisterColumnType ( DbType . Currency , "REAL" ) ;
60
+ RegisterColumnType ( DbType . Decimal , "REAL" ) ;
61
+ RegisterColumnType ( DbType . Double , "REAL" ) ;
62
+ RegisterColumnType ( DbType . Single , "REAL" ) ;
63
+ RegisterColumnType ( DbType . VarNumeric , "REAL" ) ;
64
+
55
65
RegisterColumnType ( DbType . AnsiString , "TEXT" ) ;
56
66
RegisterColumnType ( DbType . String , "TEXT" ) ;
57
67
RegisterColumnType ( DbType . AnsiStringFixedLength , "TEXT" ) ;
58
68
RegisterColumnType ( DbType . StringFixedLength , "TEXT" ) ;
59
69
60
- RegisterColumnType ( DbType . Date , "DATE" ) ;
61
- RegisterColumnType ( DbType . DateTime , "DATETIME" ) ;
62
- RegisterColumnType ( DbType . Time , "TIME" ) ;
63
- RegisterColumnType ( DbType . Boolean , "BOOL" ) ;
64
- // UNIQUEIDENTIFIER is not a SQLite type, but SQLite does not care much, see
65
- // https://www.sqlite.org/datatype3.html
66
- RegisterColumnType ( DbType . Guid , "UNIQUEIDENTIFIER" ) ;
70
+ // https://www.sqlite.org/datatype3.html#boolean_datatype
71
+ RegisterColumnType ( DbType . Boolean , "INTEGER" ) ;
72
+
73
+ // See https://www.sqlite.org/datatype3.html#date_and_time_datatype, we have three choices for date and time
74
+ // The one causing the less issues in case of an explicit cast is text. Beware, System.Data.SQLite has an
75
+ // internal use only "DATETIME" type. Using it causes it to directly convert the text stored into SQLite to
76
+ // a .Net DateTime, but also causes columns in SQLite to have numeric affinity and convert to destroy the
77
+ // value. As said in their chm documentation, this "DATETIME" type is for System.Data.SQLite internal use only.
78
+ RegisterColumnType ( DbType . Date , "TEXT" ) ;
79
+ RegisterColumnType ( DbType . DateTime , "TEXT" ) ;
80
+ RegisterColumnType ( DbType . Time , "TEXT" ) ;
81
+
82
+ RegisterColumnType ( DbType . Guid , _binaryGuid ? "BLOB" : "TEXT" ) ;
67
83
}
68
84
69
85
protected virtual void RegisterFunctions ( )
@@ -98,8 +114,6 @@ protected virtual void RegisterFunctions()
98
114
99
115
RegisterFunction ( "iif" , new SQLFunctionTemplate ( null , "case when ?1 then ?2 else ?3 end" ) ) ;
100
116
101
- RegisterFunction ( "cast" , new SQLiteCastFunction ( ) ) ;
102
-
103
117
RegisterFunction ( "round" , new StandardSQLFunction ( "round" ) ) ;
104
118
105
119
// SQLite has no built-in support of bitwise xor, but can emulate it.
@@ -112,7 +126,7 @@ protected virtual void RegisterFunctions()
112
126
if ( _binaryGuid )
113
127
RegisterFunction ( "strguid" , new SQLFunctionTemplate ( NHibernateUtil . String , "substr(hex(?1), 7, 2) || substr(hex(?1), 5, 2) || substr(hex(?1), 3, 2) || substr(hex(?1), 1, 2) || '-' || substr(hex(?1), 11, 2) || substr(hex(?1), 9, 2) || '-' || substr(hex(?1), 15, 2) || substr(hex(?1), 13, 2) || '-' || substr(hex(?1), 17, 4) || '-' || substr(hex(?1), 21) " ) ) ;
114
128
else
115
- RegisterFunction ( "strguid" , new SQLFunctionTemplate ( NHibernateUtil . String , "cast(?1 as char )" ) ) ;
129
+ RegisterFunction ( "strguid" , new SQLFunctionTemplate ( NHibernateUtil . String , "cast(?1 as text )" ) ) ;
116
130
117
131
// SQLite random function yields a long, ranging form MinValue to MaxValue. (-9223372036854775808 to
118
132
// 9223372036854775807). HQL random requires a float from 0 inclusive to 1 exclusive, so we divide by
@@ -131,7 +145,8 @@ public override void Configure(IDictionary<string, string> settings)
131
145
132
146
ConfigureBinaryGuid ( settings ) ;
133
147
134
- // Re-register functions depending on settings.
148
+ // Re-register functions and types depending on settings.
149
+ RegisterColumnTypes ( ) ;
135
150
RegisterFunctions ( ) ;
136
151
}
137
152
@@ -485,13 +500,15 @@ public override bool SupportsForeignKeyConstraintInAlterTable
485
500
/// <inheritdoc />
486
501
public override int MaxAliasLength => 128 ;
487
502
503
+ // Since v5.3
504
+ [ Obsolete ( "This class has no usage in NHibernate anymore and will be removed in a future version. Use or extend CastFunction instead." ) ]
488
505
[ Serializable ]
489
506
protected class SQLiteCastFunction : CastFunction
490
507
{
491
508
protected override bool CastingIsRequired ( string sqlType )
492
509
{
493
- // SQLite doesn't support casting to datetime types. It assumes you want an integer and destroys the date string.
494
- if ( StringHelper . ContainsCaseInsensitive ( sqlType , "date" ) || StringHelper . ContainsCaseInsensitive ( sqlType , "time" ) )
510
+ if ( StringHelper . ContainsCaseInsensitive ( sqlType , " date" ) ||
511
+ StringHelper . ContainsCaseInsensitive ( sqlType , "time" ) )
495
512
return false ;
496
513
return true ;
497
514
}
0 commit comments