diff --git a/Makefile b/Makefile
index 42cd34dd..6b0d5365 100644
--- a/Makefile
+++ b/Makefile
@@ -5,7 +5,10 @@ PACKAGES_OUT=$(abspath PackagesOut)
all: nuget
-nuget: pclnuget basenuget sqlciphernuget staticnuget
+nuget: sourcegenerator pclnuget basenuget sqlciphernuget staticnuget
+
+sourcegenerator: nuget\Sqlite_net.SourceGenerator\Sqlite_net.SourceGenerator.csproj $(SRC)
+ dotnet build -c Release $<
pclnuget: nuget/SQLite-net-std/SQLite-net-std.csproj $(SRC)
dotnet pack -c Release -o $(PACKAGES_OUT) $<
diff --git a/SQLite.sln b/SQLite.sln
index a6329bc1..8faf72b9 100644
--- a/SQLite.sln
+++ b/SQLite.sln
@@ -1,8 +1,11 @@
-Microsoft Visual Studio Solution File, Format Version 11.00
-# Visual Studio 2010
+Microsoft Visual Studio Solution File, Format Version 12.00
+# Visual Studio Version 18
+VisualStudioVersion = 18.0.11012.119 d18.0
+MinimumVisualStudioVersion = 10.0.40219.1
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{A0E59A10-7BD0-4554-B133-66FA850159BE}"
ProjectSection(SolutionItems) = preProject
+ Directory.Build.props = Directory.Build.props
Makefile = Makefile
README.md = README.md
EndProjectSection
@@ -10,104 +13,136 @@ EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "tests", "tests", "{FECC0E44-E626-49CB-BD8B-0CFBD93FBEFF}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SQLite-net-std", "nuget\SQLite-net-std\SQLite-net-std.csproj", "{081D08D6-10F1-431B-88FE-469FD9FE898C}"
+ ProjectSection(ProjectDependencies) = postProject
+ {8AFC8450-42E6-41AF-8E39-17EC7C13D695} = {8AFC8450-42E6-41AF-8E39-17EC7C13D695}
+ EndProjectSection
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ApiDiff", "tests\ApiDiff\ApiDiff.csproj", "{1DEF735C-B973-4ED9-8446-7FFA6D0B410B}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SQLite-net-base", "nuget\SQLite-net-base\SQLite-net-base.csproj", "{53D1953C-3641-47D0-BE08-14DB853CC576}"
+ ProjectSection(ProjectDependencies) = postProject
+ {8AFC8450-42E6-41AF-8E39-17EC7C13D695} = {8AFC8450-42E6-41AF-8E39-17EC7C13D695}
+ EndProjectSection
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SQLite-net-sqlcipher", "nuget\SQLite-net-sqlcipher\SQLite-net-sqlcipher.csproj", "{59DB03EF-E28D-431E-9058-74AF316800EE}"
+ ProjectSection(ProjectDependencies) = postProject
+ {8AFC8450-42E6-41AF-8E39-17EC7C13D695} = {8AFC8450-42E6-41AF-8E39-17EC7C13D695}
+ EndProjectSection
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SQLite.Tests", "tests\SQLite.Tests\SQLite.Tests.csproj", "{80B66A43-B358-4438-BF06-6351B86B121A}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SQLite-net-static", "nuget\SQLite-net-static\SQLite-net-static.csproj", "{7CD60DAE-D505-4C2E-80B3-296556CE711E}"
+ ProjectSection(ProjectDependencies) = postProject
+ {8AFC8450-42E6-41AF-8E39-17EC7C13D695} = {8AFC8450-42E6-41AF-8E39-17EC7C13D695}
+ EndProjectSection
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Sqlite_net.SourceGenerator", "nuget\Sqlite_net.SourceGenerator\Sqlite_net.SourceGenerator.csproj", "{8AFC8450-42E6-41AF-8E39-17EC7C13D695}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
- Release|Any CPU = Release|Any CPU
+ Debug|iPhone = Debug|iPhone
Debug|iPhoneSimulator = Debug|iPhoneSimulator
+ Release|Any CPU = Release|Any CPU
Release|iPhone = Release|iPhone
Release|iPhoneSimulator = Release|iPhoneSimulator
- Debug|iPhone = Debug|iPhone
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
{081D08D6-10F1-431B-88FE-469FD9FE898C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{081D08D6-10F1-431B-88FE-469FD9FE898C}.Debug|Any CPU.Build.0 = Debug|Any CPU
- {081D08D6-10F1-431B-88FE-469FD9FE898C}.Release|Any CPU.ActiveCfg = Release|Any CPU
- {081D08D6-10F1-431B-88FE-469FD9FE898C}.Release|Any CPU.Build.0 = Release|Any CPU
+ {081D08D6-10F1-431B-88FE-469FD9FE898C}.Debug|iPhone.ActiveCfg = Debug|Any CPU
+ {081D08D6-10F1-431B-88FE-469FD9FE898C}.Debug|iPhone.Build.0 = Debug|Any CPU
{081D08D6-10F1-431B-88FE-469FD9FE898C}.Debug|iPhoneSimulator.ActiveCfg = Debug|Any CPU
{081D08D6-10F1-431B-88FE-469FD9FE898C}.Debug|iPhoneSimulator.Build.0 = Debug|Any CPU
+ {081D08D6-10F1-431B-88FE-469FD9FE898C}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {081D08D6-10F1-431B-88FE-469FD9FE898C}.Release|Any CPU.Build.0 = Release|Any CPU
{081D08D6-10F1-431B-88FE-469FD9FE898C}.Release|iPhone.ActiveCfg = Release|Any CPU
{081D08D6-10F1-431B-88FE-469FD9FE898C}.Release|iPhone.Build.0 = Release|Any CPU
{081D08D6-10F1-431B-88FE-469FD9FE898C}.Release|iPhoneSimulator.ActiveCfg = Release|Any CPU
{081D08D6-10F1-431B-88FE-469FD9FE898C}.Release|iPhoneSimulator.Build.0 = Release|Any CPU
- {081D08D6-10F1-431B-88FE-469FD9FE898C}.Debug|iPhone.ActiveCfg = Debug|Any CPU
- {081D08D6-10F1-431B-88FE-469FD9FE898C}.Debug|iPhone.Build.0 = Debug|Any CPU
{1DEF735C-B973-4ED9-8446-7FFA6D0B410B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{1DEF735C-B973-4ED9-8446-7FFA6D0B410B}.Debug|Any CPU.Build.0 = Debug|Any CPU
- {1DEF735C-B973-4ED9-8446-7FFA6D0B410B}.Release|Any CPU.ActiveCfg = Release|Any CPU
- {1DEF735C-B973-4ED9-8446-7FFA6D0B410B}.Release|Any CPU.Build.0 = Release|Any CPU
+ {1DEF735C-B973-4ED9-8446-7FFA6D0B410B}.Debug|iPhone.ActiveCfg = Debug|Any CPU
+ {1DEF735C-B973-4ED9-8446-7FFA6D0B410B}.Debug|iPhone.Build.0 = Debug|Any CPU
{1DEF735C-B973-4ED9-8446-7FFA6D0B410B}.Debug|iPhoneSimulator.ActiveCfg = Debug|Any CPU
{1DEF735C-B973-4ED9-8446-7FFA6D0B410B}.Debug|iPhoneSimulator.Build.0 = Debug|Any CPU
+ {1DEF735C-B973-4ED9-8446-7FFA6D0B410B}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {1DEF735C-B973-4ED9-8446-7FFA6D0B410B}.Release|Any CPU.Build.0 = Release|Any CPU
{1DEF735C-B973-4ED9-8446-7FFA6D0B410B}.Release|iPhone.ActiveCfg = Release|Any CPU
{1DEF735C-B973-4ED9-8446-7FFA6D0B410B}.Release|iPhone.Build.0 = Release|Any CPU
{1DEF735C-B973-4ED9-8446-7FFA6D0B410B}.Release|iPhoneSimulator.ActiveCfg = Release|Any CPU
{1DEF735C-B973-4ED9-8446-7FFA6D0B410B}.Release|iPhoneSimulator.Build.0 = Release|Any CPU
- {1DEF735C-B973-4ED9-8446-7FFA6D0B410B}.Debug|iPhone.ActiveCfg = Debug|Any CPU
- {1DEF735C-B973-4ED9-8446-7FFA6D0B410B}.Debug|iPhone.Build.0 = Debug|Any CPU
{53D1953C-3641-47D0-BE08-14DB853CC576}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{53D1953C-3641-47D0-BE08-14DB853CC576}.Debug|Any CPU.Build.0 = Debug|Any CPU
- {53D1953C-3641-47D0-BE08-14DB853CC576}.Release|Any CPU.ActiveCfg = Release|Any CPU
- {53D1953C-3641-47D0-BE08-14DB853CC576}.Release|Any CPU.Build.0 = Release|Any CPU
+ {53D1953C-3641-47D0-BE08-14DB853CC576}.Debug|iPhone.ActiveCfg = Debug|Any CPU
+ {53D1953C-3641-47D0-BE08-14DB853CC576}.Debug|iPhone.Build.0 = Debug|Any CPU
{53D1953C-3641-47D0-BE08-14DB853CC576}.Debug|iPhoneSimulator.ActiveCfg = Debug|Any CPU
{53D1953C-3641-47D0-BE08-14DB853CC576}.Debug|iPhoneSimulator.Build.0 = Debug|Any CPU
+ {53D1953C-3641-47D0-BE08-14DB853CC576}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {53D1953C-3641-47D0-BE08-14DB853CC576}.Release|Any CPU.Build.0 = Release|Any CPU
{53D1953C-3641-47D0-BE08-14DB853CC576}.Release|iPhone.ActiveCfg = Release|Any CPU
{53D1953C-3641-47D0-BE08-14DB853CC576}.Release|iPhone.Build.0 = Release|Any CPU
{53D1953C-3641-47D0-BE08-14DB853CC576}.Release|iPhoneSimulator.ActiveCfg = Release|Any CPU
{53D1953C-3641-47D0-BE08-14DB853CC576}.Release|iPhoneSimulator.Build.0 = Release|Any CPU
- {53D1953C-3641-47D0-BE08-14DB853CC576}.Debug|iPhone.ActiveCfg = Debug|Any CPU
- {53D1953C-3641-47D0-BE08-14DB853CC576}.Debug|iPhone.Build.0 = Debug|Any CPU
{59DB03EF-E28D-431E-9058-74AF316800EE}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{59DB03EF-E28D-431E-9058-74AF316800EE}.Debug|Any CPU.Build.0 = Debug|Any CPU
- {59DB03EF-E28D-431E-9058-74AF316800EE}.Release|Any CPU.ActiveCfg = Release|Any CPU
- {59DB03EF-E28D-431E-9058-74AF316800EE}.Release|Any CPU.Build.0 = Release|Any CPU
+ {59DB03EF-E28D-431E-9058-74AF316800EE}.Debug|iPhone.ActiveCfg = Debug|Any CPU
+ {59DB03EF-E28D-431E-9058-74AF316800EE}.Debug|iPhone.Build.0 = Debug|Any CPU
{59DB03EF-E28D-431E-9058-74AF316800EE}.Debug|iPhoneSimulator.ActiveCfg = Debug|Any CPU
{59DB03EF-E28D-431E-9058-74AF316800EE}.Debug|iPhoneSimulator.Build.0 = Debug|Any CPU
+ {59DB03EF-E28D-431E-9058-74AF316800EE}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {59DB03EF-E28D-431E-9058-74AF316800EE}.Release|Any CPU.Build.0 = Release|Any CPU
{59DB03EF-E28D-431E-9058-74AF316800EE}.Release|iPhone.ActiveCfg = Release|Any CPU
{59DB03EF-E28D-431E-9058-74AF316800EE}.Release|iPhone.Build.0 = Release|Any CPU
{59DB03EF-E28D-431E-9058-74AF316800EE}.Release|iPhoneSimulator.ActiveCfg = Release|Any CPU
{59DB03EF-E28D-431E-9058-74AF316800EE}.Release|iPhoneSimulator.Build.0 = Release|Any CPU
- {59DB03EF-E28D-431E-9058-74AF316800EE}.Debug|iPhone.ActiveCfg = Debug|Any CPU
- {59DB03EF-E28D-431E-9058-74AF316800EE}.Debug|iPhone.Build.0 = Debug|Any CPU
{80B66A43-B358-4438-BF06-6351B86B121A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{80B66A43-B358-4438-BF06-6351B86B121A}.Debug|Any CPU.Build.0 = Debug|Any CPU
- {80B66A43-B358-4438-BF06-6351B86B121A}.Release|Any CPU.ActiveCfg = Release|Any CPU
- {80B66A43-B358-4438-BF06-6351B86B121A}.Release|Any CPU.Build.0 = Release|Any CPU
+ {80B66A43-B358-4438-BF06-6351B86B121A}.Debug|iPhone.ActiveCfg = Debug|Any CPU
+ {80B66A43-B358-4438-BF06-6351B86B121A}.Debug|iPhone.Build.0 = Debug|Any CPU
{80B66A43-B358-4438-BF06-6351B86B121A}.Debug|iPhoneSimulator.ActiveCfg = Debug|Any CPU
{80B66A43-B358-4438-BF06-6351B86B121A}.Debug|iPhoneSimulator.Build.0 = Debug|Any CPU
+ {80B66A43-B358-4438-BF06-6351B86B121A}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {80B66A43-B358-4438-BF06-6351B86B121A}.Release|Any CPU.Build.0 = Release|Any CPU
{80B66A43-B358-4438-BF06-6351B86B121A}.Release|iPhone.ActiveCfg = Release|Any CPU
{80B66A43-B358-4438-BF06-6351B86B121A}.Release|iPhone.Build.0 = Release|Any CPU
{80B66A43-B358-4438-BF06-6351B86B121A}.Release|iPhoneSimulator.ActiveCfg = Release|Any CPU
{80B66A43-B358-4438-BF06-6351B86B121A}.Release|iPhoneSimulator.Build.0 = Release|Any CPU
- {80B66A43-B358-4438-BF06-6351B86B121A}.Debug|iPhone.ActiveCfg = Debug|Any CPU
- {80B66A43-B358-4438-BF06-6351B86B121A}.Debug|iPhone.Build.0 = Debug|Any CPU
{7CD60DAE-D505-4C2E-80B3-296556CE711E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{7CD60DAE-D505-4C2E-80B3-296556CE711E}.Debug|Any CPU.Build.0 = Debug|Any CPU
- {7CD60DAE-D505-4C2E-80B3-296556CE711E}.Release|Any CPU.ActiveCfg = Release|Any CPU
- {7CD60DAE-D505-4C2E-80B3-296556CE711E}.Release|Any CPU.Build.0 = Release|Any CPU
+ {7CD60DAE-D505-4C2E-80B3-296556CE711E}.Debug|iPhone.ActiveCfg = Debug|Any CPU
+ {7CD60DAE-D505-4C2E-80B3-296556CE711E}.Debug|iPhone.Build.0 = Debug|Any CPU
{7CD60DAE-D505-4C2E-80B3-296556CE711E}.Debug|iPhoneSimulator.ActiveCfg = Debug|Any CPU
{7CD60DAE-D505-4C2E-80B3-296556CE711E}.Debug|iPhoneSimulator.Build.0 = Debug|Any CPU
+ {7CD60DAE-D505-4C2E-80B3-296556CE711E}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {7CD60DAE-D505-4C2E-80B3-296556CE711E}.Release|Any CPU.Build.0 = Release|Any CPU
{7CD60DAE-D505-4C2E-80B3-296556CE711E}.Release|iPhone.ActiveCfg = Release|Any CPU
{7CD60DAE-D505-4C2E-80B3-296556CE711E}.Release|iPhone.Build.0 = Release|Any CPU
{7CD60DAE-D505-4C2E-80B3-296556CE711E}.Release|iPhoneSimulator.ActiveCfg = Release|Any CPU
{7CD60DAE-D505-4C2E-80B3-296556CE711E}.Release|iPhoneSimulator.Build.0 = Release|Any CPU
- {7CD60DAE-D505-4C2E-80B3-296556CE711E}.Debug|iPhone.ActiveCfg = Debug|Any CPU
- {7CD60DAE-D505-4C2E-80B3-296556CE711E}.Debug|iPhone.Build.0 = Debug|Any CPU
+ {8AFC8450-42E6-41AF-8E39-17EC7C13D695}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {8AFC8450-42E6-41AF-8E39-17EC7C13D695}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {8AFC8450-42E6-41AF-8E39-17EC7C13D695}.Debug|iPhone.ActiveCfg = Debug|Any CPU
+ {8AFC8450-42E6-41AF-8E39-17EC7C13D695}.Debug|iPhone.Build.0 = Debug|Any CPU
+ {8AFC8450-42E6-41AF-8E39-17EC7C13D695}.Debug|iPhoneSimulator.ActiveCfg = Debug|Any CPU
+ {8AFC8450-42E6-41AF-8E39-17EC7C13D695}.Debug|iPhoneSimulator.Build.0 = Debug|Any CPU
+ {8AFC8450-42E6-41AF-8E39-17EC7C13D695}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {8AFC8450-42E6-41AF-8E39-17EC7C13D695}.Release|Any CPU.Build.0 = Release|Any CPU
+ {8AFC8450-42E6-41AF-8E39-17EC7C13D695}.Release|iPhone.ActiveCfg = Release|Any CPU
+ {8AFC8450-42E6-41AF-8E39-17EC7C13D695}.Release|iPhone.Build.0 = Release|Any CPU
+ {8AFC8450-42E6-41AF-8E39-17EC7C13D695}.Release|iPhoneSimulator.ActiveCfg = Release|Any CPU
+ {8AFC8450-42E6-41AF-8E39-17EC7C13D695}.Release|iPhoneSimulator.Build.0 = Release|Any CPU
+ EndGlobalSection
+ GlobalSection(SolutionProperties) = preSolution
+ HideSolutionNode = FALSE
EndGlobalSection
GlobalSection(NestedProjects) = preSolution
{1DEF735C-B973-4ED9-8446-7FFA6D0B410B} = {FECC0E44-E626-49CB-BD8B-0CFBD93FBEFF}
{80B66A43-B358-4438-BF06-6351B86B121A} = {FECC0E44-E626-49CB-BD8B-0CFBD93FBEFF}
EndGlobalSection
+ GlobalSection(ExtensibilityGlobals) = postSolution
+ SolutionGuid = {E4388666-648A-41A5-B10B-2598523000A1}
+ EndGlobalSection
GlobalSection(MonoDevelopProperties) = preSolution
StartupItem = tests\SQLite.Tests.csproj
Policies = $0
diff --git a/global.json b/global.json
index b3fcac52..d5bf446d 100644
--- a/global.json
+++ b/global.json
@@ -1,6 +1,6 @@
-{
- "sdk": {
- "version": "9.0.100",
- "rollForward": "latestFeature"
- }
+{
+ "sdk": {
+ "version": "9.0.100",
+ "rollForward": "latestFeature"
+ }
}
\ No newline at end of file
diff --git a/nuget/SQLite-net-base/SQLite-net-base.csproj b/nuget/SQLite-net-base/SQLite-net-base.csproj
index 8fd9ae2e..d9a694ee 100644
--- a/nuget/SQLite-net-base/SQLite-net-base.csproj
+++ b/nuget/SQLite-net-base/SQLite-net-base.csproj
@@ -34,4 +34,10 @@
+
+
+
+
+
+
diff --git a/nuget/SQLite-net-sqlcipher/SQLite-net-sqlcipher.csproj b/nuget/SQLite-net-sqlcipher/SQLite-net-sqlcipher.csproj
index 714af08a..5354fbc6 100644
--- a/nuget/SQLite-net-sqlcipher/SQLite-net-sqlcipher.csproj
+++ b/nuget/SQLite-net-sqlcipher/SQLite-net-sqlcipher.csproj
@@ -35,4 +35,10 @@
+
+
+
+
+
+
diff --git a/nuget/SQLite-net-static/SQLite-net-static.csproj b/nuget/SQLite-net-static/SQLite-net-static.csproj
index f8c4d857..08a691d2 100644
--- a/nuget/SQLite-net-static/SQLite-net-static.csproj
+++ b/nuget/SQLite-net-static/SQLite-net-static.csproj
@@ -31,4 +31,10 @@
+
+
+
+
+
+
diff --git a/nuget/SQLite-net-std/SQLite-net-std.csproj b/nuget/SQLite-net-std/SQLite-net-std.csproj
index 4a1e6d1b..23d2f776 100644
--- a/nuget/SQLite-net-std/SQLite-net-std.csproj
+++ b/nuget/SQLite-net-std/SQLite-net-std.csproj
@@ -33,4 +33,10 @@
+
+
+
+
+
+
diff --git a/nuget/Sqlite_net.SourceGenerator/Pollyfill.cs b/nuget/Sqlite_net.SourceGenerator/Pollyfill.cs
new file mode 100644
index 00000000..dfa45ce1
--- /dev/null
+++ b/nuget/Sqlite_net.SourceGenerator/Pollyfill.cs
@@ -0,0 +1,5 @@
+// Polyfill for IsExternalInit to support record types in .NET Standard 2.0
+namespace System.Runtime.CompilerServices
+{
+ internal static class IsExternalInit { }
+}
diff --git a/nuget/Sqlite_net.SourceGenerator/SQLiteFastColumnSetterGenerator.cs b/nuget/Sqlite_net.SourceGenerator/SQLiteFastColumnSetterGenerator.cs
new file mode 100644
index 00000000..4985a350
--- /dev/null
+++ b/nuget/Sqlite_net.SourceGenerator/SQLiteFastColumnSetterGenerator.cs
@@ -0,0 +1,570 @@
+using System;
+using System.Collections.Concurrent;
+using Microsoft.CodeAnalysis;
+using Microsoft.CodeAnalysis.CSharp;
+using Microsoft.CodeAnalysis.CSharp.Syntax;
+using Microsoft.CodeAnalysis.Text;
+using System.Collections.Generic;
+using System.Collections.Immutable;
+using System.Linq;
+using System.Text;
+using Microsoft.CodeAnalysis.Diagnostics;
+
+[Generator]
+public class SQLiteFastColumnSetterGenerator : IIncrementalGenerator
+{
+ private static ConcurrentDictionary, bool> cachedHasSqliteAttribute = new ();
+ private static ConcurrentDictionary, bool> cachedHasTableAttribute = new ();
+ private static List SQLitePropertyAttributes = default!;
+ private static ImmutableHashSet SQLitePropertyFullAttributes = default!;
+
+ static SQLiteFastColumnSetterGenerator ()
+ {
+ SQLitePropertyAttributes = new() {
+ "Column",
+ "Indexed",
+ "Ignore",
+ "Unique",
+ "MaxLength",
+ "Collation",
+ "NotNull",
+ "StoreAsText",
+ "AutoIncrement",
+ "PrimaryKey",
+ "NotNull"
+ };
+
+ SQLitePropertyFullAttributes = SQLitePropertyAttributes.Select (f => f + "Attribute").ToImmutableHashSet();
+ }
+
+ public void Initialize(IncrementalGeneratorInitializationContext context)
+ {
+ // Launch Debugger for Debugging the Analyzer
+ // System.Diagnostics.Debugger.Launch();
+
+ // Find all classes with TableAttribute or properties with ColumnAttribute
+ var classDeclarations = context.SyntaxProvider
+ .CreateSyntaxProvider(
+ predicate: static (s, _) => IsCandidateClass(s),
+ transform: static (ctx, _) => GetClassInfo(ctx))
+ .Where(static m => m is not null);
+
+ // Get analyzer config options for accessing MSBuild properties
+ var configOptions = context.AnalyzerConfigOptionsProvider;
+
+ // Combine with compilation
+ var compilationAndClasses = context.CompilationProvider
+ .Combine(classDeclarations.Collect())
+ .Combine(configOptions);
+
+ context.RegisterSourceOutput(compilationAndClasses,
+ static (spc, source) => Execute(source.Left.Left, source.Left.Right!, source.Right, spc));
+ }
+
+ static bool IsCandidateClass(SyntaxNode node)
+ {
+ if (node is not ClassDeclarationSyntax classDecl)
+ return false;
+
+ // Check if class has TableAttribute
+ if (classDecl.AttributeLists.Any(attrList =>
+ attrList.Attributes.Any(attr => attr.Name.ToString ().Contains("Table"))))
+ {
+ return true;
+ }
+
+ // I need to analyse the base class in the semantic model
+ if (HasBaseClass (classDecl)) {
+ return true;
+ }
+
+ // Check if any property has SQLite Property Attribute
+ return classDecl.Members
+ .OfType ()
+ .Any (prop => prop.AttributeLists.Any (attrList =>
+ attrList.Attributes.Any (attr => {
+ var attributeName = attr.Name.ToString ();
+ return SQLitePropertyAttributes.Any (f => attributeName.Contains (f));
+ })));
+ }
+
+ static bool HasBaseClass (ClassDeclarationSyntax classDecl)
+ {
+ var baseList = classDecl.BaseList;
+ if (baseList == null)
+ return false;
+
+ return baseList.Types.Count > 0;
+ }
+
+ static ClassInfo? GetClassInfo (INamedTypeSymbol? classSymbol)
+ {
+ if (classSymbol is null)
+ return null;
+
+ // Return null if the class is private
+ if (classSymbol.DeclaredAccessibility == Accessibility.Private)
+ return null;
+
+ if (classSymbol.IsGenericType)
+ return null;
+
+ var hasSqliteAttributes = HasTableAttribute (classSymbol);
+ if (!hasSqliteAttributes) {
+ hasSqliteAttributes = HasSQLiteAttribute (classSymbol);
+ }
+
+ if (!hasSqliteAttributes) {
+ return null;
+ }
+
+ var properties = new List ();
+
+ // Iterate through the class hierarchy to get all properties
+ var currentType = classSymbol;
+ while (currentType != null) {
+ foreach (var member in currentType.GetMembers ().OfType ()) {
+ if (!member.IsReadOnly && !member.IsStatic && !member.IsIndexer &&
+ (member.DeclaredAccessibility != Accessibility.Private && member.DeclaredAccessibility != Accessibility.Protected) &&
+ (member.SetMethod?.DeclaredAccessibility != Accessibility.Private && member.SetMethod?.DeclaredAccessibility != Accessibility.Protected && member.SetMethod?.IsInitOnly != true)) {
+ var ignore = member.GetAttributes ()
+ .Any (attr => IsIgnoreAttribute (attr.AttributeClass));
+
+ // Include property if not ignored
+ if (!ignore) {
+ var columnName = GetColumnName (member);
+ properties.Add (new PropertyInfo (member.Name, member.Type.ToDisplayString (), columnName, GetEnumInfo(member)));
+ }
+ }
+ }
+
+ // Move to base type
+ currentType = currentType.BaseType;
+
+ // Stop at System.Object or if we hit a null base type
+ if (currentType?.SpecialType == SpecialType.System_Object)
+ break;
+ }
+
+ if (properties.Count == 0)
+ return null;
+
+ // Handle nested classes by building the full containing type path
+ var containingTypes = new List ();
+ var currentContaining = classSymbol.ContainingType;
+ while (currentContaining != null) {
+ containingTypes.Insert (0, currentContaining.Name);
+ currentContaining = currentContaining.ContainingType;
+ }
+
+ var fullClassName = containingTypes.Count > 0
+ ? $"{string.Join (".", containingTypes)}.{classSymbol.Name}"
+ : classSymbol.Name;
+
+ return new ClassInfo (
+ fullClassName,
+ classSymbol.ContainingNamespace?.ToDisplayString () ?? string.Empty,
+ properties);
+ }
+
+ private static EnumInfo? GetEnumInfo (IPropertySymbol member)
+ {
+ var type = member.Type;
+ if (type is INamedTypeSymbol named &&
+ named.OriginalDefinition.SpecialType == SpecialType.System_Nullable_T) {
+ type = named.TypeArguments[0];
+ }
+
+ if (type.TypeKind == TypeKind.Enum) {
+ var storeAsText = type.GetAttributes ()
+ .Any (attr => IsStoreAsTextAttribute(attr.AttributeClass));
+ return new EnumInfo (storeAsText);
+ }
+
+ return null;
+ }
+
+ private static bool HasSQLiteAttribute (INamedTypeSymbol? classSymbol)
+ {
+ if (classSymbol != null && cachedHasSqliteAttribute.TryGetValue(classSymbol, out var result)) {
+ return result;
+ }
+
+ while (true) {
+ if (classSymbol == null || classSymbol.SpecialType == SpecialType.System_Object) {
+ if (classSymbol != null) {
+ cachedHasSqliteAttribute[classSymbol] = false;
+ }
+ return false;
+ }
+
+ var members = classSymbol.GetMembers();
+ foreach (var member in members) {
+ if (member.GetAttributes().Any(attr => IsSQLiteAttribute (attr.AttributeClass)))
+ {
+ cachedHasSqliteAttribute[classSymbol] = true;
+ return true;
+ }
+ }
+
+ classSymbol = classSymbol.BaseType;
+ }
+ }
+
+ private static bool HasTableAttribute (INamedTypeSymbol? classSymbol)
+ {
+ if (classSymbol != null && cachedHasTableAttribute.TryGetValue (classSymbol, out var result)) {
+ return result;
+ }
+
+ while (true) {
+ if (classSymbol == null || classSymbol.SpecialType == SpecialType.System_Object) {
+ if (classSymbol != null) {
+ cachedHasTableAttribute[classSymbol] = false;
+ }
+ return false;
+ }
+
+ var hasTableAttribute = classSymbol.GetAttributes ()
+ .Any (attr => IsTableAttribute (attr.AttributeClass));
+ if (hasTableAttribute) {
+ cachedHasTableAttribute[classSymbol] = true;
+ return true;
+ }
+
+ classSymbol = classSymbol.BaseType;
+ }
+ }
+
+ static ClassInfo? GetClassInfo(GeneratorSyntaxContext context)
+ {
+ var classDecl = (ClassDeclarationSyntax)context.Node;
+ var semanticModel = context.SemanticModel;
+
+ var classSymbol = semanticModel.GetDeclaredSymbol(classDecl);
+ return GetClassInfo (classSymbol);
+ }
+
+ private static bool IsTableAttribute (INamedTypeSymbol? attributeClass)
+ {
+ while (true) {
+ if (attributeClass == null) {
+ return false;
+ }
+
+ if (IsSQLiteNamespace (attributeClass) && attributeClass.Name == "TableAttribute") {
+ return true;
+ }
+
+ attributeClass = attributeClass.BaseType;
+ }
+ }
+
+ private static bool IsStoreAsTextAttribute (INamedTypeSymbol? attributeClass)
+ {
+ while (true) {
+ if (attributeClass == null) {
+ return false;
+ }
+
+ if (IsSQLiteNamespace (attributeClass) && attributeClass.Name == "StoreAsTextAttribute") {
+ return true;
+ }
+
+ attributeClass = attributeClass.BaseType;
+ }
+ }
+
+ private static bool IsIgnoreAttribute (INamedTypeSymbol? attributeClass)
+ {
+ while (true) {
+ if (attributeClass == null) {
+ return false;
+ }
+
+ if (IsSQLiteNamespace (attributeClass) && attributeClass.Name == "IgnoreAttribute") {
+ return true;
+ }
+
+ attributeClass = attributeClass.BaseType;
+ }
+ }
+
+ private static bool IsSQLiteAttribute (INamedTypeSymbol? attributeClass)
+ {
+ while (true) {
+ if (attributeClass == null) {
+ return false;
+ }
+
+ if (IsSQLiteNamespace (attributeClass) && SQLitePropertyFullAttributes.Contains (attributeClass.Name)) {
+ return true;
+ }
+
+ attributeClass = attributeClass.BaseType;
+ }
+ }
+
+ private static bool IsSQLiteNamespace (INamedTypeSymbol attributeClass)
+ {
+ return attributeClass.ContainingNamespace.Name == "SQLite";
+ }
+
+ static string GetColumnName(IPropertySymbol property)
+ {
+ // Check for ColumnAttribute with name parameter
+ var columnAttr = property.GetAttributes()
+ .FirstOrDefault(attr => attr.AttributeClass?.ContainingNamespace.Name == "SQLite" && attr.AttributeClass?.Name == "ColumnAttribute");
+
+ if (columnAttr?.ConstructorArguments.Length > 0)
+ {
+ var nameArg = columnAttr.ConstructorArguments[0];
+ if (nameArg.Value is string columnName)
+ return columnName;
+ }
+
+ // Default to property name
+ return property.Name;
+ }
+
+ static void Execute(
+ Compilation compilation,
+ ImmutableArray classesDuplicates,
+ AnalyzerConfigOptionsProvider configOptionsProvider,
+ SourceProductionContext context)
+ {
+ if (classesDuplicates.IsDefaultOrEmpty)
+ return;
+
+ var classes = RemoveDuplicates(classesDuplicates);
+
+ // Get the assembly name/namespace from the compilation
+ var assemblyName = compilation.AssemblyName ?? "Generated";
+ var rootNamespace = GetRootNamespace(configOptionsProvider, compilation) ?? assemblyName;
+
+ var sb = new StringBuilder();
+ sb.AppendLine("// ");
+ sb.AppendLine("using System;");
+ sb.AppendLine("using SQLite;");
+ sb.AppendLine("#pragma warning disable CS0618 // Disable obsolete Warnings");
+ sb.AppendLine("#pragma warning disable CS0612 // Disable obsolete Warnings");
+ sb.AppendLine();
+ sb.AppendLine($"namespace {rootNamespace}");
+ sb.AppendLine("{");
+ sb.AppendLine(" /// SQLite Initializer Class ");
+ sb.AppendLine(" [SQLite.Preserve(AllMembers = true)]");
+ sb.AppendLine(" public static class SQLiteInitializer");
+ sb.AppendLine(" {");
+ sb.AppendLine(" private static bool initialized;");
+ sb.AppendLine(" /// Init SQLite Fast Column Setters ");
+ sb.AppendLine("#if NET5_0_OR_GREATER");
+ sb.AppendLine(" [System.Runtime.CompilerServices.ModuleInitializer]");
+ sb.AppendLine("#endif");
+ sb.AppendLine(" public static void Init()");
+ sb.AppendLine(" {");
+ sb.AppendLine(" if (initialized)");
+ sb.AppendLine(" return;");
+ sb.AppendLine(" initialized = true;");
+
+ foreach (var classInfo in classes) {
+ var fullTypeName = FullTypeName (classInfo);
+ var initName = "Init" + fullTypeName.Replace (".", "_");
+ sb.AppendLine($" {initName}();");
+ }
+
+ sb.AppendLine (" }");
+
+ foreach (var classInfo in classes) {
+ var fullTypeName = FullTypeName (classInfo);
+ var initName = "Init" + fullTypeName.Replace (".", "_");
+ sb.AppendLine($" /// Init SQLite Fast Column Setters for {fullTypeName}");
+ sb.AppendLine($" private static void {initName}()");
+ sb.AppendLine(" {");
+ foreach (var property in classInfo.Properties) {
+ sb.AppendLine($" SQLiteConnection.RegisterFastColumnSetter(");
+ sb.AppendLine($" typeof({fullTypeName}),");
+ sb.AppendLine($" \"{property.ColumnName}\",");
+ sb.Append($" (obj, stmt, index) => ");
+ GeneratePropertySetter ($"(({fullTypeName})obj)", sb, property);
+ }
+ sb.AppendLine (" }");
+ }
+
+ sb.AppendLine(" }");
+ sb.AppendLine("}");
+
+ context.AddSource("SQLiteInitializer.g.cs", SourceText.From(sb.ToString(), Encoding.UTF8));
+ }
+
+ private static ImmutableArray RemoveDuplicates (ImmutableArray classes)
+ {
+ Dictionary existing = new();
+
+ foreach (var it in classes) {
+ var fullTypeName = FullTypeName (it);
+ if (existing.TryGetValue (fullTypeName, out var current)) {
+ if (it.Properties.Count > current.Properties.Count) {
+ existing[fullTypeName] = it;
+ }
+ }
+ else {
+ existing[fullTypeName] = it;
+ }
+ }
+
+ return existing.Values.ToImmutableArray();
+ }
+
+ static string FullTypeName (ClassInfo classInfo)
+ {
+ return string.IsNullOrEmpty (classInfo.Namespace)
+ ? classInfo.ClassName
+ : $"{classInfo.Namespace}.{classInfo.ClassName}";
+}
+
+ static string? GetRootNamespace(AnalyzerConfigOptionsProvider configOptionsProvider, Compilation compilation)
+ {
+ if (configOptionsProvider.GlobalOptions.TryGetValue ("build_property.RootNamespace", out var rootNs)) {
+ return rootNs;
+ }
+
+ // Fallback to assembly name
+ return compilation.AssemblyName;
+ }
+
+
+ static void GeneratePropertySetter(string typedObject, StringBuilder sb, PropertyInfo property)
+ {
+ var propertyType = property.TypeName;
+
+ // Handle nullable types
+ var isNullable = propertyType.Contains("?");
+ if (isNullable)
+ propertyType = propertyType.Replace("?", "");
+
+ switch (propertyType) {
+ case "byte[]":
+ case "Byte[]":
+ case "System.Byte[]":
+ isNullable = true; // need to handle nullable for byte arrays
+ break;
+ }
+
+ if (isNullable) {
+ sb.AppendLine("");
+ sb.AppendLine($" {{");
+ sb.AppendLine($" if (SQLite3.ColumnType(stmt, index) != SQLite3.ColType.Null)");
+ sb.AppendLine($" {{");
+ sb.Append($" ");
+}
+
+ switch (propertyType)
+ {
+ case "string":
+ case "String":
+ case "System.String":
+ sb.Append($"{typedObject}.{property.PropertyName} = SQLite3.ColumnString(stmt, index)");
+ break;
+
+ case "byte":
+ case "Byte":
+ case "System.Byte":
+ sb.Append($"{typedObject}.{property.PropertyName} = (byte)SQLite3.ColumnInt(stmt, index)");
+ break;
+
+ case "short":
+ case "Int16":
+ case "System.Int16":
+ sb.Append($"{typedObject}.{property.PropertyName} = (short)SQLite3.ColumnInt(stmt, index)");
+ break;
+
+
+ case "int":
+ case "Int32":
+ case "System.Int32":
+ sb.Append($"{typedObject}.{property.PropertyName} = SQLite3.ColumnInt(stmt, index)");
+ break;
+
+ case "long":
+ case "Int64":
+ case "System.Int64":
+ sb.Append($"{typedObject}.{property.PropertyName} = SQLite3.ColumnInt64(stmt, index)");
+ break;
+
+ case "double":
+ case "Double":
+ case "System.Double":
+ sb.Append($"{typedObject}.{property.PropertyName} = SQLite3.ColumnDouble(stmt, index)");
+ break;
+
+ case "decimal":
+ case "Decimal":
+ case "System.Decimal":
+ sb.Append($"{typedObject}.{property.PropertyName} = System.Convert.ToDecimal(SQLite3.ColumnDouble(stmt, index))");
+ break;
+
+ case "float":
+ case "Single":
+ case "System.Single":
+ sb.Append($"{typedObject}.{property.PropertyName} = (float)SQLite3.ColumnDouble(stmt, index)");
+ break;
+
+ case "bool":
+ case "Boolean":
+ case "System.Boolean":
+ sb.Append($"{typedObject}.{property.PropertyName} = SQLite3.ColumnInt(stmt, index) == 1");
+ break;
+
+ case "DateTime":
+ case "System.DateTime":
+ sb.Append($"{typedObject}.{property.PropertyName} = new DateTime(SQLite3.ColumnInt64(stmt, index))");
+ break;
+
+ case "TimeSpan":
+ case "System.TimeSpan":
+ sb.Append($"{typedObject}.{property.PropertyName} = new TimeSpan(SQLite3.ColumnInt64(stmt, index))");
+ break;
+
+ case "Guid":
+ case "System.Guid":
+ sb.Append($"{typedObject}.{property.PropertyName} = new Guid(SQLite3.ColumnString(stmt, index))");
+ break;
+
+ case "byte[]":
+ case "Byte[]":
+ case "System.Byte[]":
+ sb.Append($"{typedObject}.{property.PropertyName} = SQLite3.ColumnByteArray(stmt, index)");
+ break;
+
+ default:
+ if (property.Enum != null) {
+ // For other types, try to use a generic approach
+ if (property.Enum.StoreAsText) {
+ sb.Append($"{typedObject}.{property.PropertyName} = ({propertyType})Enum.Parse(typeof({propertyType}), SQLite3.ColumnString(stmt, index), ignoreCase: true)");
+ }
+ else {
+ sb.Append($"{typedObject}.{property.PropertyName} = ({propertyType})SQLite3.ColumnInt(stmt, index)");
+ }
+ }
+ else {
+ // For other types, try to use a generic approach
+ sb.Append($"{typedObject}.{property.PropertyName} = ({propertyType})Convert.ChangeType(SQLite3.ColumnString(stmt, index), typeof({propertyType}))");
+ }
+
+ break;
+ }
+
+ if (isNullable) {
+ sb.AppendLine($";");
+ sb.AppendLine($" }}");
+ sb.AppendLine($" }});");
+ }
+ else {
+ sb.AppendLine(");");
+ }
+ }
+
+ record ClassInfo(string ClassName, string Namespace, List Properties);
+ record PropertyInfo(string PropertyName, string TypeName, string ColumnName, EnumInfo? Enum);
+ record EnumInfo (bool StoreAsText);
+}
diff --git a/nuget/Sqlite_net.SourceGenerator/Sqlite_net.SourceGenerator.csproj b/nuget/Sqlite_net.SourceGenerator/Sqlite_net.SourceGenerator.csproj
new file mode 100644
index 00000000..877156d9
--- /dev/null
+++ b/nuget/Sqlite_net.SourceGenerator/Sqlite_net.SourceGenerator.csproj
@@ -0,0 +1,17 @@
+
+
+
+ netstandard2.0
+ false
+ 9
+ enable
+ true
+ true
+
+
+
+
+
+
+
+
diff --git a/src/SQLite.cs b/src/SQLite.cs
index 72525c56..bd95aa8a 100644
--- a/src/SQLite.cs
+++ b/src/SQLite.cs
@@ -25,6 +25,7 @@
using System;
using System.Collections;
+using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Diagnostics;
#if NET8_0_OR_GREATER
@@ -41,6 +42,7 @@
#endif
using System.Text;
using System.Threading;
+using ExecutionEngineException = System.ExecutionEngineException;
#if USE_CSHARP_SQLITE
using Sqlite3 = Community.CsharpSqlite.Sqlite3;
@@ -2632,6 +2634,14 @@ void OnTableChanged (TableMapping table, NotifyTableChangedAction action)
}
public event EventHandler TableChanged;
+
+ public static void RegisterFastColumnSetter (
+ Type type,
+ string name,
+ Action