From 884addd16d6f2e5ee2668c461b06239c656e0baa Mon Sep 17 00:00:00 2001 From: Sergei Dryganets Date: Sun, 4 Nov 2018 04:30:38 -0800 Subject: [PATCH] UWP implementation for SqliteStorage. (#302) --- src/windows/.gitignore | 78 ++ src/windows/SQLitePlugin.sln | 77 ++ src/windows/SQLitePlugin/AwaitingQueue.cs | 295 +++++ .../SQLitePlugin/Properties/AssemblyInfo.cs | 30 + .../Properties/SQLitePlugin.rd.xml | 28 + .../SQLite.NET/Interop/ColType.cs | 36 + .../SQLite.NET/Interop/ConfigOption.cs | 34 + .../SQLite.NET/Interop/ExtendedResult.cs | 78 ++ .../SQLite.NET/Interop/IDbBackupHandle.cs | 28 + .../SQLite.NET/Interop/IDbHandle.cs | 31 + .../SQLite.NET/Interop/IDbStatement.cs | 31 + .../SQLite.NET/Interop/ISQLiteApi.cs | 79 ++ .../SQLite.NET/Interop/ISQLiteApiExt.cs | 44 + .../SQLite.NET/Interop/Result.cs | 64 + .../SQLite.NET/Interop/SQLiteOpenFlags.cs | 44 + .../SQLite.NET/SQLiteException.cs | 46 + .../SQLiteApiWinRT.cs | 1031 +++++++++++++++++ src/windows/SQLitePlugin/SQLitePlugin.csproj | 209 ++++ .../SQLitePlugin/SQLitePluginModule.cs | 635 ++++++++++ .../SQLitePlugin/SQLitePluginPackage.cs | 53 + 20 files changed, 2951 insertions(+) create mode 100644 src/windows/.gitignore create mode 100644 src/windows/SQLitePlugin.sln create mode 100644 src/windows/SQLitePlugin/AwaitingQueue.cs create mode 100644 src/windows/SQLitePlugin/Properties/AssemblyInfo.cs create mode 100644 src/windows/SQLitePlugin/Properties/SQLitePlugin.rd.xml create mode 100644 src/windows/SQLitePlugin/SQLite.Net-PCL/SQLite.NET/Interop/ColType.cs create mode 100644 src/windows/SQLitePlugin/SQLite.Net-PCL/SQLite.NET/Interop/ConfigOption.cs create mode 100644 src/windows/SQLitePlugin/SQLite.Net-PCL/SQLite.NET/Interop/ExtendedResult.cs create mode 100644 src/windows/SQLitePlugin/SQLite.Net-PCL/SQLite.NET/Interop/IDbBackupHandle.cs create mode 100644 src/windows/SQLitePlugin/SQLite.Net-PCL/SQLite.NET/Interop/IDbHandle.cs create mode 100644 src/windows/SQLitePlugin/SQLite.Net-PCL/SQLite.NET/Interop/IDbStatement.cs create mode 100644 src/windows/SQLitePlugin/SQLite.Net-PCL/SQLite.NET/Interop/ISQLiteApi.cs create mode 100644 src/windows/SQLitePlugin/SQLite.Net-PCL/SQLite.NET/Interop/ISQLiteApiExt.cs create mode 100644 src/windows/SQLitePlugin/SQLite.Net-PCL/SQLite.NET/Interop/Result.cs create mode 100644 src/windows/SQLitePlugin/SQLite.Net-PCL/SQLite.NET/Interop/SQLiteOpenFlags.cs create mode 100644 src/windows/SQLitePlugin/SQLite.Net-PCL/SQLite.NET/SQLiteException.cs create mode 100644 src/windows/SQLitePlugin/SQLite.Net-PCL/SQLite.Net.Platform.WinRT/SQLiteApiWinRT.cs create mode 100644 src/windows/SQLitePlugin/SQLitePlugin.csproj create mode 100644 src/windows/SQLitePlugin/SQLitePluginModule.cs create mode 100644 src/windows/SQLitePlugin/SQLitePluginPackage.cs diff --git a/src/windows/.gitignore b/src/windows/.gitignore new file mode 100644 index 00000000..cbf7e7f4 --- /dev/null +++ b/src/windows/.gitignore @@ -0,0 +1,78 @@ +*AppPackages* +*BundleArtifacts* +*ReactAssets* + +#OS junk files +[Tt]humbs.db +*.DS_Store + +#Visual Studio files +*.[Oo]bj +*.user +*.aps +*.pch +*.vspscc +*.vssscc +*_i.c +*_p.c +*.ncb +*.suo +*.tlb +*.tlh +*.bak +*.[Cc]ache +*.ilk +*.log +*.lib +*.sbr +*.sdf +*.opensdf +*.opendb +*.unsuccessfulbuild +ipch/ +[Oo]bj/ +[Bb]in +[Dd]ebug*/ +[Rr]elease*/ +Ankh.NoLoad + +#MonoDevelop +*.pidb +*.userprefs + +#Tooling +_ReSharper*/ +*.resharper +[Tt]est[Rr]esult* +*.sass-cache + +#Project files +[Bb]uild/ + +#Subversion files +.svn + +# Office Temp Files +~$* + +# vim Temp Files +*~ + +#NuGet +packages/ +*.nupkg + +#ncrunch +*ncrunch* +*crunch*.local.xml + +# visual studio database projects +*.dbmdl + +#Test files +*.testsettings + +#Other files +*.DotSettings +.vs/ +*project.lock.json diff --git a/src/windows/SQLitePlugin.sln b/src/windows/SQLitePlugin.sln new file mode 100644 index 00000000..6e4b327a --- /dev/null +++ b/src/windows/SQLitePlugin.sln @@ -0,0 +1,77 @@ +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio 14 +VisualStudioVersion = 14.0.25123.0 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SQLitePlugin", "SQLitePlugin\SQLitePlugin.csproj", "{6A1F04D0-8750-11E6-AADE-EB9EAC8D95CF}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ReactNative", "..\node_modules\react-native-windows\ReactWindows\ReactNative\ReactNative.csproj", "{C7673AD5-E3AA-468C-A5FD-FA38154E205C}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Debug|ARM = Debug|ARM + Debug|x64 = Debug|x64 + Debug|x86 = Debug|x86 + Development|Any CPU = Development|Any CPU + Development|ARM = Development|ARM + Development|x64 = Development|x64 + Development|x86 = Development|x86 + Release|Any CPU = Release|Any CPU + Release|ARM = Release|ARM + Release|x64 = Release|x64 + Release|x86 = Release|x86 + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {6A1F04D0-8750-11E6-AADE-EB9EAC8D95CF}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {6A1F04D0-8750-11E6-AADE-EB9EAC8D95CF}.Debug|Any CPU.Build.0 = Debug|Any CPU + {6A1F04D0-8750-11E6-AADE-EB9EAC8D95CF}.Debug|ARM.ActiveCfg = Debug|ARM + {6A1F04D0-8750-11E6-AADE-EB9EAC8D95CF}.Debug|ARM.Build.0 = Debug|ARM + {6A1F04D0-8750-11E6-AADE-EB9EAC8D95CF}.Debug|x64.ActiveCfg = Debug|x64 + {6A1F04D0-8750-11E6-AADE-EB9EAC8D95CF}.Debug|x64.Build.0 = Debug|x64 + {6A1F04D0-8750-11E6-AADE-EB9EAC8D95CF}.Debug|x86.ActiveCfg = Debug|x86 + {6A1F04D0-8750-11E6-AADE-EB9EAC8D95CF}.Debug|x86.Build.0 = Debug|x86 + {6A1F04D0-8750-11E6-AADE-EB9EAC8D95CF}.Development|Any CPU.ActiveCfg = Development|Any CPU + {6A1F04D0-8750-11E6-AADE-EB9EAC8D95CF}.Development|Any CPU.Build.0 = Development|Any CPU + {6A1F04D0-8750-11E6-AADE-EB9EAC8D95CF}.Development|ARM.ActiveCfg = Development|ARM + {6A1F04D0-8750-11E6-AADE-EB9EAC8D95CF}.Development|ARM.Build.0 = Development|ARM + {6A1F04D0-8750-11E6-AADE-EB9EAC8D95CF}.Development|x64.ActiveCfg = Development|x64 + {6A1F04D0-8750-11E6-AADE-EB9EAC8D95CF}.Development|x64.Build.0 = Development|x64 + {6A1F04D0-8750-11E6-AADE-EB9EAC8D95CF}.Development|x86.ActiveCfg = Development|x86 + {6A1F04D0-8750-11E6-AADE-EB9EAC8D95CF}.Development|x86.Build.0 = Development|x86 + {6A1F04D0-8750-11E6-AADE-EB9EAC8D95CF}.Release|Any CPU.ActiveCfg = Release|Any CPU + {6A1F04D0-8750-11E6-AADE-EB9EAC8D95CF}.Release|Any CPU.Build.0 = Release|Any CPU + {6A1F04D0-8750-11E6-AADE-EB9EAC8D95CF}.Release|ARM.ActiveCfg = Release|ARM + {6A1F04D0-8750-11E6-AADE-EB9EAC8D95CF}.Release|ARM.Build.0 = Release|ARM + {6A1F04D0-8750-11E6-AADE-EB9EAC8D95CF}.Release|x64.ActiveCfg = Release|x64 + {6A1F04D0-8750-11E6-AADE-EB9EAC8D95CF}.Release|x64.Build.0 = Release|x64 + {6A1F04D0-8750-11E6-AADE-EB9EAC8D95CF}.Release|x86.ActiveCfg = Release|x86 + {6A1F04D0-8750-11E6-AADE-EB9EAC8D95CF}.Release|x86.Build.0 = Release|x86 + {C7673AD5-E3AA-468C-A5FD-FA38154E205C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {C7673AD5-E3AA-468C-A5FD-FA38154E205C}.Debug|Any CPU.Build.0 = Debug|Any CPU + {C7673AD5-E3AA-468C-A5FD-FA38154E205C}.Debug|ARM.ActiveCfg = Debug|ARM + {C7673AD5-E3AA-468C-A5FD-FA38154E205C}.Debug|ARM.Build.0 = Debug|ARM + {C7673AD5-E3AA-468C-A5FD-FA38154E205C}.Debug|x64.ActiveCfg = Debug|x64 + {C7673AD5-E3AA-468C-A5FD-FA38154E205C}.Debug|x64.Build.0 = Debug|x64 + {C7673AD5-E3AA-468C-A5FD-FA38154E205C}.Debug|x86.ActiveCfg = Debug|x86 + {C7673AD5-E3AA-468C-A5FD-FA38154E205C}.Debug|x86.Build.0 = Debug|x86 + {C7673AD5-E3AA-468C-A5FD-FA38154E205C}.Development|Any CPU.ActiveCfg = Debug|Any CPU + {C7673AD5-E3AA-468C-A5FD-FA38154E205C}.Development|Any CPU.Build.0 = Debug|Any CPU + {C7673AD5-E3AA-468C-A5FD-FA38154E205C}.Development|ARM.ActiveCfg = Debug|ARM + {C7673AD5-E3AA-468C-A5FD-FA38154E205C}.Development|ARM.Build.0 = Debug|ARM + {C7673AD5-E3AA-468C-A5FD-FA38154E205C}.Development|x64.ActiveCfg = Debug|x64 + {C7673AD5-E3AA-468C-A5FD-FA38154E205C}.Development|x64.Build.0 = Debug|x64 + {C7673AD5-E3AA-468C-A5FD-FA38154E205C}.Development|x86.ActiveCfg = Debug|x86 + {C7673AD5-E3AA-468C-A5FD-FA38154E205C}.Development|x86.Build.0 = Debug|x86 + {C7673AD5-E3AA-468C-A5FD-FA38154E205C}.Release|Any CPU.ActiveCfg = Release|Any CPU + {C7673AD5-E3AA-468C-A5FD-FA38154E205C}.Release|Any CPU.Build.0 = Release|Any CPU + {C7673AD5-E3AA-468C-A5FD-FA38154E205C}.Release|ARM.ActiveCfg = Release|ARM + {C7673AD5-E3AA-468C-A5FD-FA38154E205C}.Release|ARM.Build.0 = Release|ARM + {C7673AD5-E3AA-468C-A5FD-FA38154E205C}.Release|x64.ActiveCfg = Release|x64 + {C7673AD5-E3AA-468C-A5FD-FA38154E205C}.Release|x64.Build.0 = Release|x64 + {C7673AD5-E3AA-468C-A5FD-FA38154E205C}.Release|x86.ActiveCfg = Release|x86 + {C7673AD5-E3AA-468C-A5FD-FA38154E205C}.Release|x86.Build.0 = Release|x86 + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection +EndGlobal diff --git a/src/windows/SQLitePlugin/AwaitingQueue.cs b/src/windows/SQLitePlugin/AwaitingQueue.cs new file mode 100644 index 00000000..d191a648 --- /dev/null +++ b/src/windows/SQLitePlugin/AwaitingQueue.cs @@ -0,0 +1,295 @@ +/* +* AwaitingQueue.cs +* +* This class is copied directly into the react-native-sqlite-storage repo +* for now. When this gets checked into react-native-windows, we can remove +* this file and start depending on that RNW's copy. +* +* Serializes the work of all Tasks that are added to its queue. Awaits the +* Task returned by the current work item before moving on to the next work +* item. +* +* This class is not thread-safe. All methods should be called from the same thread +* or LimitedConcurrencyActionQueue. `await` must cause the continuation to run on +* the same thread or LimitedConcurrencyActionQueue. +* +* Motivation: +* When you `await` a Task, you have to consider all of the things that could have +* changed by the time your continuation runs. For example: +* +* class Recorder +* { +* private MediaCapture _captureMedia; +* +* public async Task StartRecording() +* { +* _captureMedia = new MediaCapture(); +* await _captureMedia.InitializeAsync(); +* // Lots of things could have changed by the time we get here. +* // For example, maybe `_captureMedia` is null! +* await _captureMedia.StartRecordToStreamAsync(...); +* } +* +* public async Task StopRecording() +* { +* // This code can run while `StartRecording` is in the middle +* // of running. +* +* if (_captureMedia != null) +* { +* // Code to clean up _captureMedia... +* _captureMedia = null; +* } +* } +* } +* +* Alternatively, you can use `AwaitingQueue` to serialize async work that +* interacts with each other to prevent any interleavings. Example: +* +* class Recorder +* { +* private AwaitingQueue _awaitingQueue = new AwaitingQueue(); +* private MediaCapture _captureMedia; +* +* public async Task StartRecording() +* { +* _awaitingQueue.RunOrDispatch(async () => +* { +* // This code won't run until all of the other Tasks +* // that were added to the `_awaitingQueue` before us +* // have already completed. +* +* _captureMedia = new MediaCapture(); +* await _captureMedia.InitializeAsync(captureInitSettings); +* // We can think of `StartRecording` as being atomic which +* // means we don't have to worry about anything we care about +* // changing by the time we get here. For example, `_captureMedia` +* // is guaranteed to be non-null by design. +* await _captureMedia.StartRecordToStreamAsync(...); +* }); +* } +* +* public async Task StopRecording() +* { +* _awaitingQueue.RunOrDispatch(() => +* { +* // This code won't run until all of the other Tasks +* // that were added to the `_awaitingQueue` before us +* // have already completed. This means this code can't +* // run while `StartRecording` is in the middle of running. +* +* if (_captureMedia != null) +* { +* // Code to clean up _captureMedia... +* _captureMedia = null; +* } +* }); +* } +* } +*/ + +using System; +using System.Collections.Generic; +using System.Reactive; +using System.Threading; +using System.Threading.Tasks; + +namespace Org.PGSQLite.SQLitePlugin +{ + /// + /// Serializes the work of all Tasks that are added to its queue. Awaits the + /// Task returned by the current work item before moving on to the next work + /// item. + /// + /// + /// This class is not thread-safe. All methods should be called from the same thread + /// or LimitedConcurrencyActionQueue. `await` must cause the continuation to run on + /// the same thread or LimitedConcurrencyActionQueue. + /// + /// The type of value yielded by each Task in the queue. + public class AwaitingQueue + { + private const string _tag = nameof(AwaitingQueue); + + private class WorkItemInfo + { + public readonly Func> WorkItem; + public readonly TaskCompletionSource CompletionSource; + public readonly CancellationToken CancellationToken; + + public WorkItemInfo(Func> workItem, TaskCompletionSource completionSource, CancellationToken cancellationToken) + { + WorkItem = workItem; + CompletionSource = completionSource; + CancellationToken = cancellationToken; + } + } + + private bool _running = false; + private readonly Queue _workQueue = new Queue(); + + private async void StartWorkLoopIfNeeded() + { + if (_running) + { + return; + } + + try + { + _running = true; + while (_workQueue.Count > 0) + { + var workItemInfo = _workQueue.Dequeue(); + + if (workItemInfo.CancellationToken.IsCancellationRequested) + { + workItemInfo.CompletionSource.SetCanceled(); + } + else + { + //RnLog.Info($"UI AwaitingQueue: Start {currentName}"); + try + { + var result = await workItemInfo.WorkItem(); + workItemInfo.CompletionSource.SetResult(result); + } + catch (Exception ex) + { + workItemInfo.CompletionSource.SetException(ex); + } + //RnLog.Info($"UI AwaitingQueue: End {currentName}"); + } + } + _running = false; // Ensure _running is updated before firing the event + QueueEmptied?.Invoke(this, null); + } + finally + { + // Before exiting this method, ensure _running is updated + _running = false; + } + } + + /// + /// Adds `workItem` to the queue. If the queue is currently empty and not + /// executing any work items, executes `workItem` immediately and synchronously. + /// + /// The work item to add to the queue. + /// + /// A Task which completes when `workItem` finishes executing. The returned + /// Task resolves to the result or exception from `workItem`. + /// + public Task RunOrQueue(Func> workItem) + { + return RunOrQueue(workItem, CancellationToken.None); + } + + /// + /// Adds `workItem` to the queue. If the queue is currently empty and not + /// executing any work items, executes `workItem` immediately and synchronously. + /// + /// The work item to add to the queue. + /// + /// The cancellation token associated with the work item. The work item will + /// be skipped if the cancellation token is canceled before the work item begins. + /// + /// + /// A Task which completes when `workItem` finishes executing. The returned + /// Task resolves to the result or exception from `workItem`. If the + /// cancellation token is canceled before `workItem` begins, Task is canceled. + /// + public Task RunOrQueue(Func> workItem, CancellationToken cancellationToken) + { + //RnLog.Info($"UI AwaitingQueue: Add {name}"); + TaskCompletionSource completionSource = new TaskCompletionSource(); + + _workQueue.Enqueue(new WorkItemInfo(workItem, completionSource, cancellationToken)); + StartWorkLoopIfNeeded(); + return completionSource.Task; + } + + + /// + /// Indicates that the AwaitingQueue has finished executing all of its + /// currently scheduled work items. + /// + /// + /// Fires on the thread or LimitedConcurrencyActionQueue of the code that + /// has been conusming the AwaitingQueue. + /// + public event EventHandler QueueEmptied; + } + + /// + /// Serializes the work of all Tasks that are added to its queue. Awaits the + /// Task returned by the current work item before moving on to the next work + /// item. + /// + /// + /// This class is not thread-safe. All methods should be called from the same thread + /// or LimitedConcurrencyActionQueue. `await` must cause the continuation to run on + /// the same thread or LimitedConcurrencyActionQueue. + /// + public class AwaitingQueue + { + private AwaitingQueue _awaitingQueue = new AwaitingQueue(); + + /// + /// Adds `workItem` to the queue. If the queue is currently empty and not + /// executing any work items, executes `workItem` immediately and synchronously. + /// + /// The work item to add to the queue. + /// + /// A Task which completes when `workItem` finishes executing. The returned + /// Task throws any exceptions that `workItem` may have thrown. + /// + public Task RunOrQueue(Func workItem) + { + return RunOrQueue(workItem, CancellationToken.None); + } + + /// + /// Adds `workItem` to the queue. If the queue is currently empty and not + /// executing any work items, executes `workItem` immediately and synchronously. + /// + /// The work item to add to the queue. + /// + /// The cancellation token associated with the work item. The work item will + /// be skipped if the cancellation token is canceled before the work item begins. + /// + /// + /// A Task which completes when `workItem` finishes executing. The returned + /// Task throws any exceptions that `workItem` may have thrown. If the + /// cancellation token is canceled before `workItem` begins, Task is canceled. + /// + public Task RunOrQueue(Func workItem, CancellationToken cancellationToken) + { + return _awaitingQueue.RunOrQueue(async () => + { + await workItem(); + return Unit.Default; + }, cancellationToken); + } + + /// + /// Indicates that the AwaitingQueue has finished executing all of its + /// currently scheduled work items. + /// + /// + /// Fires on the thread or LimitedConcurrencyActionQueue of the code that + /// has been conusming the AwaitingQueue. + /// + public event EventHandler QueueEmptied + { + add + { + _awaitingQueue.QueueEmptied += value; + } + remove + { + _awaitingQueue.QueueEmptied -= value; + } + } + } +} diff --git a/src/windows/SQLitePlugin/Properties/AssemblyInfo.cs b/src/windows/SQLitePlugin/Properties/AssemblyInfo.cs new file mode 100644 index 00000000..42f6b33f --- /dev/null +++ b/src/windows/SQLitePlugin/Properties/AssemblyInfo.cs @@ -0,0 +1,30 @@ +using System.Reflection; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +// General Information about an assembly is controlled through the following +// set of attributes. Change these attribute values to modify the information +// associated with an assembly. +[assembly: AssemblyTitle("SQLitePlugin")] +[assembly: AssemblyDescription("")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("")] +[assembly: AssemblyProduct("SQLitePlugin")] +[assembly: AssemblyCopyright("Copyright © 2016")] +[assembly: AssemblyTrademark("")] +[assembly: AssemblyCulture("")] + +// Version information for an assembly consists of the following four values: +// +// Major Version +// Minor Version +// Build Number +// Revision +// +// You can specify all the values or you can default the Build and Revision Numbers +// by using the '*' as shown below: +// [assembly: AssemblyVersion("1.0.*")] +[assembly: AssemblyVersion("1.0.0.0")] +[assembly: AssemblyFileVersion("1.0.0.0")] +[assembly: ComVisible(false)] + \ No newline at end of file diff --git a/src/windows/SQLitePlugin/Properties/SQLitePlugin.rd.xml b/src/windows/SQLitePlugin/Properties/SQLitePlugin.rd.xml new file mode 100644 index 00000000..3f697c9c --- /dev/null +++ b/src/windows/SQLitePlugin/Properties/SQLitePlugin.rd.xml @@ -0,0 +1,28 @@ + + + + + + + + + diff --git a/src/windows/SQLitePlugin/SQLite.Net-PCL/SQLite.NET/Interop/ColType.cs b/src/windows/SQLitePlugin/SQLite.Net-PCL/SQLite.NET/Interop/ColType.cs new file mode 100644 index 00000000..62cc635d --- /dev/null +++ b/src/windows/SQLitePlugin/SQLite.Net-PCL/SQLite.NET/Interop/ColType.cs @@ -0,0 +1,36 @@ +// +// Copyright (c) 2012 Krueger Systems, Inc. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. +// + +using JetBrains.Annotations; + +namespace SQLite.Net.Interop +{ + [PublicAPI] + public enum ColType + { + Integer = 1, + Float = 2, + Text = 3, + Blob = 4, + Null = 5 + } +} \ No newline at end of file diff --git a/src/windows/SQLitePlugin/SQLite.Net-PCL/SQLite.NET/Interop/ConfigOption.cs b/src/windows/SQLitePlugin/SQLite.Net-PCL/SQLite.NET/Interop/ConfigOption.cs new file mode 100644 index 00000000..9542b426 --- /dev/null +++ b/src/windows/SQLitePlugin/SQLite.Net-PCL/SQLite.NET/Interop/ConfigOption.cs @@ -0,0 +1,34 @@ +// +// Copyright (c) 2012 Krueger Systems, Inc. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. +// + +using JetBrains.Annotations; + +namespace SQLite.Net.Interop +{ + [PublicAPI] + public enum ConfigOption + { + SingleThread = 1, + MultiThread = 2, + Serialized = 3 + } +} \ No newline at end of file diff --git a/src/windows/SQLitePlugin/SQLite.Net-PCL/SQLite.NET/Interop/ExtendedResult.cs b/src/windows/SQLitePlugin/SQLite.Net-PCL/SQLite.NET/Interop/ExtendedResult.cs new file mode 100644 index 00000000..18d8ce66 --- /dev/null +++ b/src/windows/SQLitePlugin/SQLite.Net-PCL/SQLite.NET/Interop/ExtendedResult.cs @@ -0,0 +1,78 @@ +// +// Copyright (c) 2012 Krueger Systems, Inc. +// Copyright (c) 2013 Øystein Krog (oystein.krog@gmail.com) +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +using System.Diagnostics.CodeAnalysis; +using JetBrains.Annotations; + +namespace SQLite.Net.Interop +{ + [PublicAPI] + [SuppressMessage("ReSharper", "InconsistentNaming")] + public enum ExtendedResult + { + IOErrorRead = (Result.IOError | (1 << 8)), + IOErrorShortRead = (Result.IOError | (2 << 8)), + IOErrorWrite = (Result.IOError | (3 << 8)), + IOErrorFsync = (Result.IOError | (4 << 8)), + IOErrorDirFSync = (Result.IOError | (5 << 8)), + IOErrorTruncate = (Result.IOError | (6 << 8)), + IOErrorFStat = (Result.IOError | (7 << 8)), + IOErrorUnlock = (Result.IOError | (8 << 8)), + IOErrorRdlock = (Result.IOError | (9 << 8)), + IOErrorDelete = (Result.IOError | (10 << 8)), + IOErrorBlocked = (Result.IOError | (11 << 8)), + IOErrorNoMem = (Result.IOError | (12 << 8)), + IOErrorAccess = (Result.IOError | (13 << 8)), + IOErrorCheckReservedLock = (Result.IOError | (14 << 8)), + IOErrorLock = (Result.IOError | (15 << 8)), + IOErrorClose = (Result.IOError | (16 << 8)), + IOErrorDirClose = (Result.IOError | (17 << 8)), + IOErrorSHMOpen = (Result.IOError | (18 << 8)), + IOErrorSHMSize = (Result.IOError | (19 << 8)), + IOErrorSHMLock = (Result.IOError | (20 << 8)), + IOErrorSHMMap = (Result.IOError | (21 << 8)), + IOErrorSeek = (Result.IOError | (22 << 8)), + IOErrorDeleteNoEnt = (Result.IOError | (23 << 8)), + IOErrorMMap = (Result.IOError | (24 << 8)), + LockedSharedcache = (Result.Locked | (1 << 8)), + BusyRecovery = (Result.Busy | (1 << 8)), + CannottOpenNoTempDir = (Result.CannotOpen | (1 << 8)), + CannotOpenIsDir = (Result.CannotOpen | (2 << 8)), + CannotOpenFullPath = (Result.CannotOpen | (3 << 8)), + CorruptVTab = (Result.Corrupt | (1 << 8)), + ReadonlyRecovery = (Result.ReadOnly | (1 << 8)), + ReadonlyCannotLock = (Result.ReadOnly | (2 << 8)), + ReadonlyRollback = (Result.ReadOnly | (3 << 8)), + AbortRollback = (Result.Abort | (2 << 8)), + ConstraintCheck = (Result.Constraint | (1 << 8)), + ConstraintCommitHook = (Result.Constraint | (2 << 8)), + ConstraintForeignKey = (Result.Constraint | (3 << 8)), + ConstraintFunction = (Result.Constraint | (4 << 8)), + ConstraintNotNull = (Result.Constraint | (5 << 8)), + ConstraintPrimaryKey = (Result.Constraint | (6 << 8)), + ConstraintTrigger = (Result.Constraint | (7 << 8)), + ConstraintUnique = (Result.Constraint | (8 << 8)), + ConstraintVTab = (Result.Constraint | (9 << 8)), + NoticeRecoverWAL = (Result.Notice | (1 << 8)), + NoticeRecoverRollback = (Result.Notice | (2 << 8)) + } +} \ No newline at end of file diff --git a/src/windows/SQLitePlugin/SQLite.Net-PCL/SQLite.NET/Interop/IDbBackupHandle.cs b/src/windows/SQLitePlugin/SQLite.Net-PCL/SQLite.NET/Interop/IDbBackupHandle.cs new file mode 100644 index 00000000..93cce2ad --- /dev/null +++ b/src/windows/SQLitePlugin/SQLite.Net-PCL/SQLite.NET/Interop/IDbBackupHandle.cs @@ -0,0 +1,28 @@ +// +// Copyright (c) 2015 Wilson Meier (wilson.meier@gmail.com) +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. +// + +namespace SQLite.Net.Interop +{ + public interface IDbBackupHandle + { + } +} \ No newline at end of file diff --git a/src/windows/SQLitePlugin/SQLite.Net-PCL/SQLite.NET/Interop/IDbHandle.cs b/src/windows/SQLitePlugin/SQLite.Net-PCL/SQLite.NET/Interop/IDbHandle.cs new file mode 100644 index 00000000..a3d1c13b --- /dev/null +++ b/src/windows/SQLitePlugin/SQLite.Net-PCL/SQLite.NET/Interop/IDbHandle.cs @@ -0,0 +1,31 @@ +// +// Copyright (c) 2013 Øystein Krog (oystein.krog@gmail.com) +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. +// + +using JetBrains.Annotations; + +namespace SQLite.Net.Interop +{ + [PublicAPI] + public interface IDbHandle + { + } +} \ No newline at end of file diff --git a/src/windows/SQLitePlugin/SQLite.Net-PCL/SQLite.NET/Interop/IDbStatement.cs b/src/windows/SQLitePlugin/SQLite.Net-PCL/SQLite.NET/Interop/IDbStatement.cs new file mode 100644 index 00000000..ab063411 --- /dev/null +++ b/src/windows/SQLitePlugin/SQLite.Net-PCL/SQLite.NET/Interop/IDbStatement.cs @@ -0,0 +1,31 @@ +// +// Copyright (c) 2013 Øystein Krog (oystein.krog@gmail.com) +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. +// + +using JetBrains.Annotations; + +namespace SQLite.Net.Interop +{ + [PublicAPI] + public interface IDbStatement + { + } +} \ No newline at end of file diff --git a/src/windows/SQLitePlugin/SQLite.Net-PCL/SQLite.NET/Interop/ISQLiteApi.cs b/src/windows/SQLitePlugin/SQLite.Net-PCL/SQLite.NET/Interop/ISQLiteApi.cs new file mode 100644 index 00000000..9901d2ee --- /dev/null +++ b/src/windows/SQLitePlugin/SQLite.Net-PCL/SQLite.NET/Interop/ISQLiteApi.cs @@ -0,0 +1,79 @@ +// +// Copyright (c) 2013 Øystein Krog (oystein.krog@gmail.com) +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +using System; +using JetBrains.Annotations; + +namespace SQLite.Net.Interop +{ + [PublicAPI] + public interface ISQLiteApi + { + Result Open(byte[] filename, out IDbHandle db, int flags, IntPtr zvfs); + // Result Open16(string filename, out IDbHandle db); + + ExtendedResult ExtendedErrCode(IDbHandle db); + int LibVersionNumber(); + string SourceID(); + Result EnableLoadExtension(IDbHandle db, int onoff); + Result Close(IDbHandle db); + Result Initialize(); + Result Shutdown(); + Result Config(ConfigOption option); + // int SetDirectory(uint directoryType, string directoryPath); + + Result BusyTimeout(IDbHandle db, int milliseconds); + int Changes(IDbHandle db); + // Result Prepare2(IDbHandle db, string sql, int numBytes, out IDbStatement stmt, IntPtr pzTail); + + IDbStatement Prepare2(IDbHandle db, string query); + Result Step(IDbStatement stmt); + Result Reset(IDbStatement stmt); + Result Finalize(IDbStatement stmt); + long LastInsertRowid(IDbHandle db); + string Errmsg16(IDbHandle db); + // string GetErrmsg(IDbHandle db); + + int BindParameterIndex(IDbStatement stmt, string name); + int BindNull(IDbStatement stmt, int index); + int BindInt(IDbStatement stmt, int index, int val); + int BindInt64(IDbStatement stmt, int index, long val); + int BindDouble(IDbStatement stmt, int index, double val); + int BindText16(IDbStatement stmt, int index, string val, int n, IntPtr free); + int BindBlob(IDbStatement stmt, int index, byte[] val, int n, IntPtr free); + int ColumnCount(IDbStatement stmt); + // string ColumnName(IDbStatement stmt, int index); + + string ColumnName16(IDbStatement stmt, int index); + ColType ColumnType(IDbStatement stmt, int index); + int ColumnInt(IDbStatement stmt, int index); + long ColumnInt64(IDbStatement stmt, int index); + double ColumnDouble(IDbStatement stmt, int index); + // string ColumnText(IDbStatement stmt, int index); + + string ColumnText16(IDbStatement stmt, int index); + byte[] ColumnBlob(IDbStatement stmt, int index); + int ColumnBytes(IDbStatement stmt, int index); + // string ColumnText(IDbStatement stmt, int index); + + byte[] ColumnByteArray(IDbStatement stmt, int index); + } +} \ No newline at end of file diff --git a/src/windows/SQLitePlugin/SQLite.Net-PCL/SQLite.NET/Interop/ISQLiteApiExt.cs b/src/windows/SQLitePlugin/SQLite.Net-PCL/SQLite.NET/Interop/ISQLiteApiExt.cs new file mode 100644 index 00000000..6ba9cb28 --- /dev/null +++ b/src/windows/SQLitePlugin/SQLite.Net-PCL/SQLite.NET/Interop/ISQLiteApiExt.cs @@ -0,0 +1,44 @@ +// +// Copyright (c) 2013 Øystein Krog (oystein.krog@gmail.com) +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +using System; + +namespace SQLite.Net.Interop +{ + public interface ISQLiteApiExt : ISQLiteApi + { + #region Backup + + IDbBackupHandle BackupInit(IDbHandle destHandle, string destName, IDbHandle srcHandle, string srcName); + + Result BackupStep(IDbBackupHandle handle, int pageCount); + + Result BackupFinish(IDbBackupHandle handle); + + int BackupRemaining(IDbBackupHandle handle); + + int BackupPagecount(IDbBackupHandle handle); + + int Sleep(int millis); + + #endregion + } +} \ No newline at end of file diff --git a/src/windows/SQLitePlugin/SQLite.Net-PCL/SQLite.NET/Interop/Result.cs b/src/windows/SQLitePlugin/SQLite.Net-PCL/SQLite.NET/Interop/Result.cs new file mode 100644 index 00000000..5a6251c7 --- /dev/null +++ b/src/windows/SQLitePlugin/SQLite.Net-PCL/SQLite.NET/Interop/Result.cs @@ -0,0 +1,64 @@ +// +// Copyright (c) 2012 Krueger Systems, Inc. +// Copyright (c) 2013 Øystein Krog (oystein.krog@gmail.com) +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +using System.Diagnostics.CodeAnalysis; +using JetBrains.Annotations; + +namespace SQLite.Net.Interop +{ + [PublicAPI] + [SuppressMessage("ReSharper", "InconsistentNaming")] + public enum Result + { + OK = 0, + Error = 1, + Internal = 2, + Perm = 3, + Abort = 4, + Busy = 5, + Locked = 6, + NoMem = 7, + ReadOnly = 8, + Interrupt = 9, + IOError = 10, + Corrupt = 11, + NotFound = 12, + Full = 13, + CannotOpen = 14, + LockErr = 15, + Empty = 16, + SchemaChngd = 17, + TooBig = 18, + Constraint = 19, + Mismatch = 20, + Misuse = 21, + NotImplementedLFS = 22, + AccessDenied = 23, + Format = 24, + Range = 25, + NonDBFile = 26, + Notice = 27, + Warning = 28, + Row = 100, + Done = 101 + } +} \ No newline at end of file diff --git a/src/windows/SQLitePlugin/SQLite.Net-PCL/SQLite.NET/Interop/SQLiteOpenFlags.cs b/src/windows/SQLitePlugin/SQLite.Net-PCL/SQLite.NET/Interop/SQLiteOpenFlags.cs new file mode 100644 index 00000000..1721d942 --- /dev/null +++ b/src/windows/SQLitePlugin/SQLite.Net-PCL/SQLite.NET/Interop/SQLiteOpenFlags.cs @@ -0,0 +1,44 @@ +// +// Copyright (c) 2012 Krueger Systems, Inc. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. +// + +using System; +using JetBrains.Annotations; + +namespace SQLite.Net.Interop +{ + [PublicAPI] + [Flags] + public enum SQLiteOpenFlags + { + ReadOnly = 1, + ReadWrite = 2, + Create = 4, + NoMutex = 0x8000, + FullMutex = 0x10000, + SharedCache = 0x20000, + PrivateCache = 0x40000, + ProtectionComplete = 0x00100000, + ProtectionCompleteUnlessOpen = 0x00200000, + ProtectionCompleteUntilFirstUserAuthentication = 0x00300000, + ProtectionNone = 0x00400000 + } +} \ No newline at end of file diff --git a/src/windows/SQLitePlugin/SQLite.Net-PCL/SQLite.NET/SQLiteException.cs b/src/windows/SQLitePlugin/SQLite.Net-PCL/SQLite.NET/SQLiteException.cs new file mode 100644 index 00000000..b6174fdd --- /dev/null +++ b/src/windows/SQLitePlugin/SQLite.Net-PCL/SQLite.NET/SQLiteException.cs @@ -0,0 +1,46 @@ +// +// Copyright (c) 2012 Krueger Systems, Inc. +// Copyright (c) 2013 Øystein Krog (oystein.krog@gmail.com) +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +using System; +using JetBrains.Annotations; +using SQLite.Net.Interop; + +namespace SQLite.Net +{ + [PublicAPI] + public class SQLiteException : Exception + { + protected SQLiteException(Result r, string message) : base(message) + { + Result = r; + } + + [PublicAPI] + public Result Result { get; private set; } + + [PublicAPI] + public static SQLiteException New(Result r, string message) + { + return new SQLiteException(r, message); + } + } +} \ No newline at end of file diff --git a/src/windows/SQLitePlugin/SQLite.Net-PCL/SQLite.Net.Platform.WinRT/SQLiteApiWinRT.cs b/src/windows/SQLitePlugin/SQLite.Net-PCL/SQLite.Net.Platform.WinRT/SQLiteApiWinRT.cs new file mode 100644 index 00000000..05c14829 --- /dev/null +++ b/src/windows/SQLitePlugin/SQLite.Net-PCL/SQLite.Net.Platform.WinRT/SQLiteApiWinRT.cs @@ -0,0 +1,1031 @@ +// +// Copyright (c) 2012 Krueger Systems, Inc. +// Copyright (c) 2013 Øystein Krog (oystein.krog@gmail.com) +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +using System; +using SQLite.Net.Interop; +using System.Runtime.InteropServices; +using System.Text; +using Sqlite3DatabaseHandle = System.IntPtr; +using Sqlite3Statement = System.IntPtr; + +namespace SQLite.Net.Platform.WinRT +{ + public class SQLiteApiWinRT : ISQLiteApiExt + { + private readonly bool _useWinSqlite; + + /// + /// Creates a SQLite API object for use from WinRT. + /// + /// Optional: Temporary folder path. Defaults to + /// Optional: Whether to use WinSQLite instead of SQLite. WinSQLite is built-in to Windows 10.0.10586.0 and above. Using it can reduce app size and potentially increase SQLite load time. + public SQLiteApiWinRT(string tempFolderPath = null, bool useWinSqlite = false) + { + _useWinSqlite = useWinSqlite; + + if (_useWinSqlite) + { + WinSQLite3.SetDirectory(/*temp directory type*/2, tempFolderPath ?? Windows.Storage.ApplicationData.Current.TemporaryFolder.Path); + } + else + { + SQLite3.SetDirectory(/*temp directory type*/2, tempFolderPath ?? Windows.Storage.ApplicationData.Current.TemporaryFolder.Path); + } + } + + public int BindBlob(IDbStatement stmt, int index, byte[] val, int n, IntPtr free) + { + var dbStatement = (DbStatement)stmt; + + if (_useWinSqlite) + { + return WinSQLite3.BindBlob(dbStatement.InternalStmt, index, val, n, free); + } + else + { + return SQLite3.BindBlob(dbStatement.InternalStmt, index, val, n, free); + } + } + + public int BindDouble(IDbStatement stmt, int index, double val) + { + var dbStatement = (DbStatement)stmt; + + + if (_useWinSqlite) + { + return WinSQLite3.BindDouble(dbStatement.InternalStmt, index, val); + } + else + { + return SQLite3.BindDouble(dbStatement.InternalStmt, index, val); + } + } + + public int BindInt(IDbStatement stmt, int index, int val) + { + var dbStatement = (DbStatement)stmt; + + if (_useWinSqlite) + { + return WinSQLite3.BindInt(dbStatement.InternalStmt, index, val); + } + else + { + return SQLite3.BindInt(dbStatement.InternalStmt, index, val); + } + } + + public int BindInt64(IDbStatement stmt, int index, long val) + { + var dbStatement = (DbStatement)stmt; + + + if (_useWinSqlite) + { + return WinSQLite3.BindInt64(dbStatement.InternalStmt, index, val); + } + else + { + return SQLite3.BindInt64(dbStatement.InternalStmt, index, val); + } + } + + public int BindNull(IDbStatement stmt, int index) + { + var dbStatement = (DbStatement)stmt; + + + if (_useWinSqlite) + { + return WinSQLite3.BindNull(dbStatement.InternalStmt, index); + } + else + { + return SQLite3.BindNull(dbStatement.InternalStmt, index); + } + } + + public int BindParameterIndex(IDbStatement stmt, string name) + { + var dbStatement = (DbStatement)stmt; + + + if (_useWinSqlite) + { + return WinSQLite3.BindParameterIndex(dbStatement.InternalStmt, name); + } + else + { + return SQLite3.BindParameterIndex(dbStatement.InternalStmt, name); + } + } + + public int BindText16(IDbStatement stmt, int index, string val, int n, IntPtr free) + { + var dbStatement = (DbStatement)stmt; + + + if (_useWinSqlite) + { + return WinSQLite3.BindText(dbStatement.InternalStmt, index, val, n, free); + } + else + { + return SQLite3.BindText(dbStatement.InternalStmt, index, val, n, free); + } + } + + public Result BusyTimeout(IDbHandle db, int milliseconds) + { + var dbHandle = (DbHandle)db; + + if (_useWinSqlite) + { + return (Result)WinSQLite3.BusyTimeout(dbHandle.InternalDbHandle, milliseconds); + } + else + { + return (Result)SQLite3.BusyTimeout(dbHandle.InternalDbHandle, milliseconds); + } + } + + public int Changes(IDbHandle db) + { + var dbHandle = (DbHandle)db; + + if (_useWinSqlite) + { + return WinSQLite3.Changes(dbHandle.InternalDbHandle); + } + else + { + return SQLite3.Changes(dbHandle.InternalDbHandle); + } + } + + public int TotalChanges(SQLite.Net.Interop.IDbHandle db) + { + var dbHandle = (DbHandle)db; + + if (_useWinSqlite) + { + return WinSQLite3.TotalChanges(dbHandle.InternalDbHandle); + } + else + { + return SQLite3.TotalChanges(dbHandle.InternalDbHandle); + } + } + + public Result Close(IDbHandle db) + { + var dbHandle = (DbHandle)db; + + if (_useWinSqlite) + { + return (Result)WinSQLite3.Close(dbHandle.InternalDbHandle); + } + else + { + return (Result)SQLite3.Close(dbHandle.InternalDbHandle); + } + } + + public Result Initialize() + { + throw new NotSupportedException(); + } + public Result Shutdown() + { + throw new NotSupportedException(); + } + + public Result Config(ConfigOption option) + { + if (_useWinSqlite) + { + return (Result)WinSQLite3.Config(option); + } + else + { + return (Result)SQLite3.Config(option); + } + } + + + public byte[] ColumnBlob(IDbStatement stmt, int index) + { + var dbStatement = (DbStatement)stmt; + int length = ColumnBytes(stmt, index); + byte[] result = new byte[length]; + if (length > 0) + { + + if (_useWinSqlite) + { + Marshal.Copy(WinSQLite3.ColumnBlob(dbStatement.InternalStmt, index), result, 0, length); + } + else + { + Marshal.Copy(SQLite3.ColumnBlob(dbStatement.InternalStmt, index), result, 0, length); + } + } + return result; + } + + public byte[] ColumnByteArray(IDbStatement stmt, int index) + { + return ColumnBlob(stmt, index); + } + + public int ColumnBytes(IDbStatement stmt, int index) + { + var dbStatement = (DbStatement)stmt; + + if (_useWinSqlite) + { + return WinSQLite3.ColumnBytes(dbStatement.InternalStmt, index); + } + else + { + return SQLite3.ColumnBytes(dbStatement.InternalStmt, index); + } + } + + public int ColumnCount(IDbStatement stmt) + { + var dbStatement = (DbStatement)stmt; + + if (_useWinSqlite) + { + return WinSQLite3.ColumnCount(dbStatement.InternalStmt); + } + else + { + return SQLite3.ColumnCount(dbStatement.InternalStmt); + } + } + + public double ColumnDouble(IDbStatement stmt, int index) + { + var dbStatement = (DbStatement)stmt; + + if (_useWinSqlite) + { + return WinSQLite3.ColumnDouble(dbStatement.InternalStmt, index); + } + else + { + return SQLite3.ColumnDouble(dbStatement.InternalStmt, index); + } + } + + public int ColumnInt(IDbStatement stmt, int index) + { + var dbStatement = (DbStatement)stmt; + + if (_useWinSqlite) + { + return WinSQLite3.ColumnInt(dbStatement.InternalStmt, index); + } + else + { + return SQLite3.ColumnInt(dbStatement.InternalStmt, index); + } + } + + public long ColumnInt64(IDbStatement stmt, int index) + { + var dbStatement = (DbStatement)stmt; + + if (_useWinSqlite) + { + return WinSQLite3.ColumnInt64(dbStatement.InternalStmt, index); + } + else + { + return SQLite3.ColumnInt64(dbStatement.InternalStmt, index); + } + } + + public string ColumnName16(IDbStatement stmt, int index) + { + var dbStatement = (DbStatement)stmt; + + if (_useWinSqlite) + { + return WinSQLite3.ColumnName16(dbStatement.InternalStmt, index); + } + else + { + return SQLite3.ColumnName16(dbStatement.InternalStmt, index); + } + } + + public string ColumnText16(IDbStatement stmt, int index) + { + var dbStatement = (DbStatement)stmt; + + if (_useWinSqlite) + { + return Marshal.PtrToStringUni(WinSQLite3.ColumnText16(dbStatement.InternalStmt, index)); + } + else + { + return Marshal.PtrToStringUni(SQLite3.ColumnText16(dbStatement.InternalStmt, index)); + } + } + + public ColType ColumnType(IDbStatement stmt, int index) + { + var dbStatement = (DbStatement)stmt; + + if (_useWinSqlite) + { + return (ColType)WinSQLite3.ColumnType(dbStatement.InternalStmt, index); + } + else + { + return (ColType)SQLite3.ColumnType(dbStatement.InternalStmt, index); + } + } + + public int LibVersionNumber() + { + if (_useWinSqlite) + { + return WinSQLite3.sqlite3_libversion_number(); + } + else + { + return SQLite3.sqlite3_libversion_number(); + } + } + + public string SourceID() + { + + if (_useWinSqlite) + { + return Marshal.PtrToStringAnsi(WinSQLite3.sqlite3_sourceid()); + } + else + { + return Marshal.PtrToStringAnsi(SQLite3.sqlite3_sourceid()); + } + } + + public Result EnableLoadExtension(IDbHandle db, int onoff) + { + return (Result)1; + } + + public string Errmsg16(IDbHandle db) + { + var dbHandle = (DbHandle)db; + + if (_useWinSqlite) + { + return WinSQLite3.GetErrmsg(dbHandle.InternalDbHandle); + } + else + { + return SQLite3.GetErrmsg(dbHandle.InternalDbHandle); + } + } + + public Result Finalize(IDbStatement stmt) + { + var dbStatement = (DbStatement)stmt; + Sqlite3Statement internalStmt = dbStatement.InternalStmt; + + if (_useWinSqlite) + { + return (Result)WinSQLite3.Finalize(internalStmt); + } + else + { + return (Result)SQLite3.Finalize(internalStmt); + } + } + + public long LastInsertRowid(IDbHandle db) + { + var dbHandle = (DbHandle)db; + + if (_useWinSqlite) + { + return WinSQLite3.LastInsertRowid(dbHandle.InternalDbHandle); + } + else + { + return SQLite3.LastInsertRowid(dbHandle.InternalDbHandle); + } + } + + public Result Open(byte[] filename, out IDbHandle db, int flags, IntPtr zvfs) + { + Sqlite3DatabaseHandle internalDbHandle; + Result ret; + + if (_useWinSqlite) + { + ret = (Result)WinSQLite3.Open(filename, out internalDbHandle, flags, zvfs); + } + else + { + ret = (Result)SQLite3.Open(filename, out internalDbHandle, flags, zvfs); + } + + db = new DbHandle(internalDbHandle); + return ret; + } + + public Result ErrCode(IDbHandle db) + { + var dbHandle = (DbHandle)db; + + if (_useWinSqlite) + { + return WinSQLite3.sqlite3_errcode(dbHandle.InternalDbHandle); + } + else + { + return SQLite3.sqlite3_errcode(dbHandle.InternalDbHandle); + } + } + + public ExtendedResult ExtendedErrCode(IDbHandle db) + { + var dbHandle = (DbHandle)db; + + if (_useWinSqlite) + { + return WinSQLite3.sqlite3_extended_errcode(dbHandle.InternalDbHandle); + } + else + { + return SQLite3.sqlite3_extended_errcode(dbHandle.InternalDbHandle); + } + } + + public IDbStatement Prepare2(IDbHandle db, string query) + { + var dbHandle = (DbHandle)db; + var stmt = default(Sqlite3Statement); + + if (_useWinSqlite) + { + var r = WinSQLite3.Prepare2(dbHandle.InternalDbHandle, query, -1, out stmt, IntPtr.Zero); + if (r != Result.OK) + { + throw SQLiteException.New(r, WinSQLite3.GetErrmsg(dbHandle.InternalDbHandle)); + } + } + else + { + var r = SQLite3.Prepare2(dbHandle.InternalDbHandle, query, -1, out stmt, IntPtr.Zero); + if (r != Result.OK) + { + throw SQLiteException.New(r, SQLite3.GetErrmsg(dbHandle.InternalDbHandle)); + } + } + + return new DbStatement(stmt); + } + + public Result Reset(IDbStatement stmt) + { + var dbStatement = (DbStatement)stmt; + + if (_useWinSqlite) + { + return (Result)WinSQLite3.Reset(dbStatement.InternalStmt); + } + else + { + return (Result)SQLite3.Reset(dbStatement.InternalStmt); + } + } + + public Result Step(IDbStatement stmt) + { + var dbStatement = (DbStatement)stmt; + + if (_useWinSqlite) + { + return (Result)WinSQLite3.Step(dbStatement.InternalStmt); + } + else + { + return (Result)SQLite3.Step(dbStatement.InternalStmt); + } + } + + #region Backup + + public IDbBackupHandle BackupInit(IDbHandle destHandle, string destName, IDbHandle srcHandle, string srcName) + { + var internalDestDb = (DbHandle)destHandle; + var internalSrcDb = (DbHandle)srcHandle; + IntPtr p; + + if (_useWinSqlite) + { + p = WinSQLite3.sqlite3_backup_init(internalDestDb.InternalDbHandle, + destName, + internalSrcDb.InternalDbHandle, + srcName); + } + else + { + p = SQLite3.sqlite3_backup_init(internalDestDb.InternalDbHandle, + destName, + internalSrcDb.InternalDbHandle, + srcName); + } + + if (p == IntPtr.Zero) + { + return null; + } + else + { + return new DbBackupHandle(p); + } + } + + public Result BackupStep(IDbBackupHandle handle, int pageCount) + { + var internalBackup = (DbBackupHandle)handle; + + if (_useWinSqlite) + { + return WinSQLite3.sqlite3_backup_step(internalBackup.DbBackupPtr, pageCount); + } + else + { + return SQLite3.sqlite3_backup_step(internalBackup.DbBackupPtr, pageCount); + } + } + + public Result BackupFinish(IDbBackupHandle handle) + { + var internalBackup = (DbBackupHandle)handle; + + if (_useWinSqlite) + { + return WinSQLite3.sqlite3_backup_finish(internalBackup.DbBackupPtr); + } + else + { + return SQLite3.sqlite3_backup_finish(internalBackup.DbBackupPtr); + } + } + + public int BackupRemaining(IDbBackupHandle handle) + { + var internalBackup = (DbBackupHandle)handle; + + if (_useWinSqlite) + { + return WinSQLite3.sqlite3_backup_remaining(internalBackup.DbBackupPtr); + } + else + { + return SQLite3.sqlite3_backup_remaining(internalBackup.DbBackupPtr); + } + } + + public int BackupPagecount(IDbBackupHandle handle) + { + var internalBackup = (DbBackupHandle)handle; + + if (_useWinSqlite) + { + return WinSQLite3.sqlite3_backup_pagecount(internalBackup.DbBackupPtr); + } + else + { + return SQLite3.sqlite3_backup_pagecount(internalBackup.DbBackupPtr); + } + } + + public int Sleep(int millis) + { + + if (_useWinSqlite) + { + return WinSQLite3.sqlite3_sleep(millis); + } + else + { + return SQLite3.sqlite3_sleep(millis); + } + } + + private struct DbBackupHandle : IDbBackupHandle + { + public DbBackupHandle(IntPtr dbBackupPtr) + : this() + { + DbBackupPtr = dbBackupPtr; + } + + internal IntPtr DbBackupPtr { get; set; } + + public bool Equals(IDbBackupHandle other) + { + return other is DbBackupHandle && DbBackupPtr == ((DbBackupHandle)other).DbBackupPtr; + } + } + + #endregion + + private struct DbHandle : IDbHandle + { + public DbHandle(Sqlite3DatabaseHandle internalDbHandle) + : this() + { + InternalDbHandle = internalDbHandle; + } + + public Sqlite3DatabaseHandle InternalDbHandle { get; set; } + + public bool Equals(IDbHandle other) + { + return other is DbHandle && InternalDbHandle == ((DbHandle)other).InternalDbHandle; + } + } + + private struct DbStatement : IDbStatement + { + public DbStatement(Sqlite3Statement internalStmt) + : this() + { + InternalStmt = internalStmt; + } + + internal Sqlite3Statement InternalStmt { get; set; } + + public bool Equals(IDbStatement other) + { + return (other is DbStatement) && ((DbStatement)other).InternalStmt == InternalStmt; + } + } + } + + public static class SQLite3 + { + [DllImport("sqlite3", EntryPoint = "sqlite3_open", CallingConvention = CallingConvention.Cdecl)] + public static extern Result Open([MarshalAs(UnmanagedType.LPStr)] string filename, out IntPtr db); + + [DllImport("sqlite3", EntryPoint = "sqlite3_open_v2", CallingConvention = CallingConvention.Cdecl)] + public static extern Result Open([MarshalAs(UnmanagedType.LPStr)] string filename, out IntPtr db, int flags, IntPtr zvfs); + + [DllImport("sqlite3", EntryPoint = "sqlite3_open_v2", CallingConvention = CallingConvention.Cdecl)] + public static extern Result Open(byte[] filename, out IntPtr db, int flags, IntPtr zvfs); + + [DllImport("sqlite3", EntryPoint = "sqlite3_open16", CallingConvention = CallingConvention.Cdecl)] + public static extern Result Open16([MarshalAs(UnmanagedType.LPWStr)] string filename, out IntPtr db); + + [DllImport("sqlite3", EntryPoint = "sqlite3_close", CallingConvention = CallingConvention.Cdecl)] + public static extern Result Close(IntPtr db); + + [DllImport("sqlite3", EntryPoint = "sqlite3_config", CallingConvention = CallingConvention.Cdecl)] + public static extern Result Config(ConfigOption option); + + [DllImport("sqlite3", EntryPoint = "sqlite3_win32_set_directory", CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Unicode)] + public static extern int SetDirectory(uint directoryType, string directoryPath); + + [DllImport("sqlite3", EntryPoint = "sqlite3_busy_timeout", CallingConvention = CallingConvention.Cdecl)] + public static extern Result BusyTimeout(IntPtr db, int milliseconds); + + [DllImport("sqlite3", EntryPoint = "sqlite3_changes", CallingConvention = CallingConvention.Cdecl)] + public static extern int Changes(IntPtr db); + + [DllImport("sqlite3", EntryPoint = "sqlite3_total_changes", CallingConvention = CallingConvention.Cdecl)] + public static extern int TotalChanges(IntPtr db); + + [DllImport("sqlite3", EntryPoint = "sqlite3_prepare16_v2", CallingConvention = CallingConvention.Cdecl)] + public static extern Result Prepare2(IntPtr db, [MarshalAs(UnmanagedType.LPWStr)] string sql, int numBytes, out IntPtr stmt, IntPtr pzTail); + + [DllImport("sqlite3", EntryPoint = "sqlite3_step", CallingConvention = CallingConvention.Cdecl)] + public static extern Result Step(IntPtr stmt); + + [DllImport("sqlite3", EntryPoint = "sqlite3_reset", CallingConvention = CallingConvention.Cdecl)] + public static extern Result Reset(IntPtr stmt); + + [DllImport("sqlite3", EntryPoint = "sqlite3_finalize", CallingConvention = CallingConvention.Cdecl)] + public static extern Result Finalize(IntPtr stmt); + + [DllImport("sqlite3", EntryPoint = "sqlite3_last_insert_rowid", CallingConvention = CallingConvention.Cdecl)] + public static extern long LastInsertRowid(IntPtr db); + + [DllImport("sqlite3", EntryPoint = "sqlite3_errmsg16", CallingConvention = CallingConvention.Cdecl)] + public static extern IntPtr Errmsg(IntPtr db); + + public static string GetErrmsg(IntPtr db) + { + return Marshal.PtrToStringUni(Errmsg(db)); + } + + [DllImport("sqlite3", EntryPoint = "sqlite3_bind_parameter_index", CallingConvention = CallingConvention.Cdecl)] + public static extern int BindParameterIndex(IntPtr stmt, [MarshalAs(UnmanagedType.LPStr)] string name); + + [DllImport("sqlite3", EntryPoint = "sqlite3_bind_null", CallingConvention = CallingConvention.Cdecl)] + public static extern int BindNull(IntPtr stmt, int index); + + [DllImport("sqlite3", EntryPoint = "sqlite3_bind_int", CallingConvention = CallingConvention.Cdecl)] + public static extern int BindInt(IntPtr stmt, int index, int val); + + [DllImport("sqlite3", EntryPoint = "sqlite3_bind_int64", CallingConvention = CallingConvention.Cdecl)] + public static extern int BindInt64(IntPtr stmt, int index, long val); + + [DllImport("sqlite3", EntryPoint = "sqlite3_bind_double", CallingConvention = CallingConvention.Cdecl)] + public static extern int BindDouble(IntPtr stmt, int index, double val); + + [DllImport("sqlite3", EntryPoint = "sqlite3_bind_text16", CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Unicode)] + public static extern int BindText(IntPtr stmt, int index, [MarshalAs(UnmanagedType.LPWStr)] string val, int n, IntPtr free); + + [DllImport("sqlite3", EntryPoint = "sqlite3_bind_blob", CallingConvention = CallingConvention.Cdecl)] + public static extern int BindBlob(IntPtr stmt, int index, byte[] val, int n, IntPtr free); + + [DllImport("sqlite3", EntryPoint = "sqlite3_column_count", CallingConvention = CallingConvention.Cdecl)] + public static extern int ColumnCount(IntPtr stmt); + + [DllImport("sqlite3", EntryPoint = "sqlite3_column_name", CallingConvention = CallingConvention.Cdecl)] + public static extern IntPtr ColumnName(IntPtr stmt, int index); + + [DllImport("sqlite3", EntryPoint = "sqlite3_column_name16", CallingConvention = CallingConvention.Cdecl)] + private static extern IntPtr ColumnName16Internal(IntPtr stmt, int index); + public static string ColumnName16(IntPtr stmt, int index) + { + return Marshal.PtrToStringUni(ColumnName16Internal(stmt, index)); + } + + [DllImport("sqlite3", EntryPoint = "sqlite3_column_type", CallingConvention = CallingConvention.Cdecl)] + public static extern ColType ColumnType(IntPtr stmt, int index); + + [DllImport("sqlite3", EntryPoint = "sqlite3_column_int", CallingConvention = CallingConvention.Cdecl)] + public static extern int ColumnInt(IntPtr stmt, int index); + + [DllImport("sqlite3", EntryPoint = "sqlite3_column_int64", CallingConvention = CallingConvention.Cdecl)] + public static extern long ColumnInt64(IntPtr stmt, int index); + + [DllImport("sqlite3", EntryPoint = "sqlite3_column_double", CallingConvention = CallingConvention.Cdecl)] + public static extern double ColumnDouble(IntPtr stmt, int index); + + [DllImport("sqlite3", EntryPoint = "sqlite3_column_text", CallingConvention = CallingConvention.Cdecl)] + public static extern IntPtr ColumnText(IntPtr stmt, int index); + + [DllImport("sqlite3", EntryPoint = "sqlite3_column_text16", CallingConvention = CallingConvention.Cdecl)] + public static extern IntPtr ColumnText16(IntPtr stmt, int index); + + [DllImport("sqlite3", EntryPoint = "sqlite3_column_blob", CallingConvention = CallingConvention.Cdecl)] + public static extern IntPtr ColumnBlob(IntPtr stmt, int index); + + [DllImport("sqlite3", EntryPoint = "sqlite3_column_bytes", CallingConvention = CallingConvention.Cdecl)] + public static extern int ColumnBytes(IntPtr stmt, int index); + + public static string ColumnString(IntPtr stmt, int index) + { + return Marshal.PtrToStringUni(SQLite3.ColumnText16(stmt, index)); + } + + public static byte[] ColumnByteArray(IntPtr stmt, int index) + { + int length = ColumnBytes(stmt, index); + byte[] result = new byte[length]; + if (length > 0) + Marshal.Copy(ColumnBlob(stmt, index), result, 0, length); + return result; + } + + [DllImport("sqlite3", EntryPoint = "sqlite3_errcode", CallingConvention = CallingConvention.Cdecl)] + public static extern SQLite.Net.Interop.Result sqlite3_errcode(IntPtr db); + + [DllImport("sqlite3", EntryPoint = "sqlite3_extended_errcode", CallingConvention = CallingConvention.Cdecl)] + public static extern ExtendedResult sqlite3_extended_errcode(IntPtr db); + + [DllImport("sqlite3", EntryPoint = "sqlite3_libversion_number", CallingConvention = CallingConvention.Cdecl)] + public static extern int sqlite3_libversion_number(); + + [DllImport("sqlite3", EntryPoint = "sqlite3_sourceid", CallingConvention = CallingConvention.Cdecl)] + public static extern IntPtr sqlite3_sourceid(); + + #region Backup + + [DllImport("sqlite3", EntryPoint = "sqlite3_backup_init", CallingConvention = CallingConvention.Cdecl)] + public static extern IntPtr sqlite3_backup_init(IntPtr destDB, + [MarshalAs(UnmanagedType.LPStr)] string destName, + IntPtr srcDB, + [MarshalAs(UnmanagedType.LPStr)] string srcName); + + [DllImport("sqlite3", EntryPoint = "sqlite3_backup_step", CallingConvention = CallingConvention.Cdecl)] + public static extern Result sqlite3_backup_step(IntPtr backup, int pageCount); + + [DllImport("sqlite3", EntryPoint = "sqlite3_backup_finish", CallingConvention = CallingConvention.Cdecl)] + public static extern Result sqlite3_backup_finish(IntPtr backup); + + [DllImport("sqlite3", EntryPoint = "sqlite3_backup_remaining", CallingConvention = CallingConvention.Cdecl)] + public static extern int sqlite3_backup_remaining(IntPtr backup); + + [DllImport("sqlite3", EntryPoint = "sqlite3_backup_pagecount", CallingConvention = CallingConvention.Cdecl)] + public static extern int sqlite3_backup_pagecount(IntPtr backup); + + [DllImport("sqlite3", EntryPoint = "sqlite3_sleep", CallingConvention = CallingConvention.Cdecl)] + public static extern int sqlite3_sleep(int millis); + + #endregion + } + + /// + /// WinSQLite is built-in to Windows 10.0.10586.0 and above. Using it can reduce app size and potentially increase SQLite load time. + /// For more information see: + /// + public static class WinSQLite3 + { + [DllImport("winsqlite3", EntryPoint = "sqlite3_open", CallingConvention = CallingConvention.StdCall, ExactSpelling = true)] + public static extern Result Open([MarshalAs(UnmanagedType.LPStr)] string filename, out IntPtr db); + + [DllImport("winsqlite3", EntryPoint = "sqlite3_open_v2", CallingConvention = CallingConvention.StdCall, ExactSpelling = true)] + public static extern Result Open([MarshalAs(UnmanagedType.LPStr)] string filename, out IntPtr db, int flags, IntPtr zvfs); + + [DllImport("winsqlite3", EntryPoint = "sqlite3_open_v2", CallingConvention = CallingConvention.StdCall, ExactSpelling = true)] + public static extern Result Open(byte[] filename, out IntPtr db, int flags, IntPtr zvfs); + + [DllImport("winsqlite3", EntryPoint = "sqlite3_open16", CallingConvention = CallingConvention.StdCall, ExactSpelling = true)] + public static extern Result Open16([MarshalAs(UnmanagedType.LPWStr)] string filename, out IntPtr db); + + [DllImport("winsqlite3", EntryPoint = "sqlite3_close", CallingConvention = CallingConvention.StdCall, ExactSpelling = true)] + public static extern Result Close(IntPtr db); + + [DllImport("winsqlite3", EntryPoint = "sqlite3_config", CallingConvention = CallingConvention.StdCall, ExactSpelling = true)] + public static extern Result Config(ConfigOption option); + + [DllImport("winsqlite3", EntryPoint = "sqlite3_win32_set_directory", CallingConvention = CallingConvention.StdCall, ExactSpelling = true, CharSet = CharSet.Unicode)] + public static extern int SetDirectory(uint directoryType, string directoryPath); + + [DllImport("winsqlite3", EntryPoint = "sqlite3_busy_timeout", CallingConvention = CallingConvention.StdCall, ExactSpelling = true)] + public static extern Result BusyTimeout(IntPtr db, int milliseconds); + + [DllImport("winsqlite3", EntryPoint = "sqlite3_changes", CallingConvention = CallingConvention.StdCall, ExactSpelling = true)] + public static extern int Changes(IntPtr db); + + [DllImport("winsqlite3", EntryPoint = "sqlite3_total_changes", CallingConvention = CallingConvention.StdCall, ExactSpelling = true)] + public static extern int TotalChanges(IntPtr db); + + [DllImport("winsqlite3", EntryPoint = "sqlite3_prepare16_v2", CallingConvention = CallingConvention.StdCall, ExactSpelling = true)] + public static extern Result Prepare2(IntPtr db, [MarshalAs(UnmanagedType.LPWStr)] string sql, int numBytes, out IntPtr stmt, IntPtr pzTail); + + [DllImport("winsqlite3", EntryPoint = "sqlite3_step", CallingConvention = CallingConvention.StdCall, ExactSpelling = true)] + public static extern Result Step(IntPtr stmt); + + [DllImport("winsqlite3", EntryPoint = "sqlite3_reset", CallingConvention = CallingConvention.StdCall, ExactSpelling = true)] + public static extern Result Reset(IntPtr stmt); + + [DllImport("winsqlite3", EntryPoint = "sqlite3_finalize", CallingConvention = CallingConvention.StdCall, ExactSpelling = true)] + public static extern Result Finalize(IntPtr stmt); + + [DllImport("winsqlite3", EntryPoint = "sqlite3_last_insert_rowid", CallingConvention = CallingConvention.StdCall, ExactSpelling = true)] + public static extern long LastInsertRowid(IntPtr db); + + [DllImport("winsqlite3", EntryPoint = "sqlite3_errmsg16", CallingConvention = CallingConvention.StdCall, ExactSpelling = true)] + public static extern IntPtr Errmsg(IntPtr db); + + public static string GetErrmsg(IntPtr db) + { + return Marshal.PtrToStringUni(Errmsg(db)); + } + + [DllImport("winsqlite3", EntryPoint = "sqlite3_bind_parameter_index", CallingConvention = CallingConvention.StdCall, ExactSpelling = true)] + public static extern int BindParameterIndex(IntPtr stmt, [MarshalAs(UnmanagedType.LPStr)] string name); + + [DllImport("winsqlite3", EntryPoint = "sqlite3_bind_null", CallingConvention = CallingConvention.StdCall, ExactSpelling = true)] + public static extern int BindNull(IntPtr stmt, int index); + + [DllImport("winsqlite3", EntryPoint = "sqlite3_bind_int", CallingConvention = CallingConvention.StdCall, ExactSpelling = true)] + public static extern int BindInt(IntPtr stmt, int index, int val); + + [DllImport("winsqlite3", EntryPoint = "sqlite3_bind_int64", CallingConvention = CallingConvention.StdCall, ExactSpelling = true)] + public static extern int BindInt64(IntPtr stmt, int index, long val); + + [DllImport("winsqlite3", EntryPoint = "sqlite3_bind_double", CallingConvention = CallingConvention.StdCall, ExactSpelling = true)] + public static extern int BindDouble(IntPtr stmt, int index, double val); + + [DllImport("winsqlite3", EntryPoint = "sqlite3_bind_text16", CallingConvention = CallingConvention.StdCall, ExactSpelling = true, CharSet = CharSet.Unicode)] + public static extern int BindText(IntPtr stmt, int index, [MarshalAs(UnmanagedType.LPWStr)] string val, int n, IntPtr free); + + [DllImport("winsqlite3", EntryPoint = "sqlite3_bind_blob", CallingConvention = CallingConvention.StdCall, ExactSpelling = true)] + public static extern int BindBlob(IntPtr stmt, int index, byte[] val, int n, IntPtr free); + + [DllImport("winsqlite3", EntryPoint = "sqlite3_column_count", CallingConvention = CallingConvention.StdCall, ExactSpelling = true)] + public static extern int ColumnCount(IntPtr stmt); + + [DllImport("winsqlite3", EntryPoint = "sqlite3_column_name", CallingConvention = CallingConvention.StdCall, ExactSpelling = true)] + public static extern IntPtr ColumnName(IntPtr stmt, int index); + + [DllImport("winsqlite3", EntryPoint = "sqlite3_column_name16", CallingConvention = CallingConvention.StdCall, ExactSpelling = true)] + private static extern IntPtr ColumnName16Internal(IntPtr stmt, int index); + public static string ColumnName16(IntPtr stmt, int index) + { + return Marshal.PtrToStringUni(ColumnName16Internal(stmt, index)); + } + + [DllImport("winsqlite3", EntryPoint = "sqlite3_column_type", CallingConvention = CallingConvention.StdCall, ExactSpelling = true)] + public static extern ColType ColumnType(IntPtr stmt, int index); + + [DllImport("winsqlite3", EntryPoint = "sqlite3_column_int", CallingConvention = CallingConvention.StdCall, ExactSpelling = true)] + public static extern int ColumnInt(IntPtr stmt, int index); + + [DllImport("winsqlite3", EntryPoint = "sqlite3_column_int64", CallingConvention = CallingConvention.StdCall, ExactSpelling = true)] + public static extern long ColumnInt64(IntPtr stmt, int index); + + [DllImport("winsqlite3", EntryPoint = "sqlite3_column_double", CallingConvention = CallingConvention.StdCall, ExactSpelling = true)] + public static extern double ColumnDouble(IntPtr stmt, int index); + + [DllImport("winsqlite3", EntryPoint = "sqlite3_column_text", CallingConvention = CallingConvention.StdCall, ExactSpelling = true)] + public static extern IntPtr ColumnText(IntPtr stmt, int index); + + [DllImport("winsqlite3", EntryPoint = "sqlite3_column_text16", CallingConvention = CallingConvention.StdCall, ExactSpelling = true)] + public static extern IntPtr ColumnText16(IntPtr stmt, int index); + + [DllImport("winsqlite3", EntryPoint = "sqlite3_column_blob", CallingConvention = CallingConvention.StdCall, ExactSpelling = true)] + public static extern IntPtr ColumnBlob(IntPtr stmt, int index); + + [DllImport("winsqlite3", EntryPoint = "sqlite3_column_bytes", CallingConvention = CallingConvention.StdCall, ExactSpelling = true)] + public static extern int ColumnBytes(IntPtr stmt, int index); + + public static string ColumnString(IntPtr stmt, int index) + { + return Marshal.PtrToStringUni(WinSQLite3.ColumnText16(stmt, index)); + } + + public static byte[] ColumnByteArray(IntPtr stmt, int index) + { + int length = ColumnBytes(stmt, index); + byte[] result = new byte[length]; + if (length > 0) + Marshal.Copy(ColumnBlob(stmt, index), result, 0, length); + return result; + } + + [DllImport("winsqlite3", EntryPoint = "sqlite3_errcode", CallingConvention = CallingConvention.StdCall, ExactSpelling = true)] + public static extern SQLite.Net.Interop.Result sqlite3_errcode(IntPtr db); + + [DllImport("winsqlite3", EntryPoint = "sqlite3_extended_errcode", CallingConvention = CallingConvention.StdCall, ExactSpelling = true)] + public static extern ExtendedResult sqlite3_extended_errcode(IntPtr db); + + [DllImport("winsqlite3", EntryPoint = "sqlite3_libversion_number", CallingConvention = CallingConvention.StdCall, ExactSpelling = true)] + public static extern int sqlite3_libversion_number(); + + [DllImport("winsqlite3", EntryPoint = "sqlite3_sourceid", CallingConvention = CallingConvention.StdCall, ExactSpelling = true)] + public static extern IntPtr sqlite3_sourceid(); + + #region Backup + + [DllImport("winsqlite3", EntryPoint = "sqlite3_backup_init", CallingConvention = CallingConvention.StdCall, ExactSpelling = true)] + public static extern IntPtr sqlite3_backup_init(IntPtr destDB, + [MarshalAs(UnmanagedType.LPStr)] string destName, + IntPtr srcDB, + [MarshalAs(UnmanagedType.LPStr)] string srcName); + + [DllImport("winsqlite3", EntryPoint = "sqlite3_backup_step", CallingConvention = CallingConvention.StdCall, ExactSpelling = true)] + public static extern Result sqlite3_backup_step(IntPtr backup, int pageCount); + + [DllImport("winsqlite3", EntryPoint = "sqlite3_backup_finish", CallingConvention = CallingConvention.StdCall, ExactSpelling = true)] + public static extern Result sqlite3_backup_finish(IntPtr backup); + + [DllImport("winsqlite3", EntryPoint = "sqlite3_backup_remaining", CallingConvention = CallingConvention.StdCall, ExactSpelling = true)] + public static extern int sqlite3_backup_remaining(IntPtr backup); + + [DllImport("winsqlite3", EntryPoint = "sqlite3_backup_pagecount", CallingConvention = CallingConvention.StdCall, ExactSpelling = true)] + public static extern int sqlite3_backup_pagecount(IntPtr backup); + + [DllImport("winsqlite3", EntryPoint = "sqlite3_sleep", CallingConvention = CallingConvention.StdCall, ExactSpelling = true)] + public static extern int sqlite3_sleep(int millis); + + #endregion + } +} \ No newline at end of file diff --git a/src/windows/SQLitePlugin/SQLitePlugin.csproj b/src/windows/SQLitePlugin/SQLitePlugin.csproj new file mode 100644 index 00000000..16360a0c --- /dev/null +++ b/src/windows/SQLitePlugin/SQLitePlugin.csproj @@ -0,0 +1,209 @@ + + + + + Debug + AnyCPU + {6A1F04D0-8750-11E6-AADE-EB9EAC8D95CF} + Library + Properties + SQLitePlugin + SQLitePlugin + en-US + UAP + 10.0.15063.0 + 10.0.14393.0 + 14 + 512 + {A5A43C5B-DE2A-4C0C-9213-0A381AF9435A};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC} + ..\..\node_modules + win10-arm;win10-arm-aot;win10-x86;win10-x86-aot;win10-x64;win10-x64-aot + + + + ..\.. + + + AnyCPU + true + full + false + bin\Debug\ + DEBUG;TRACE;NETFX_CORE;WINDOWS_UWP + prompt + 4 + + + AnyCPU + pdbonly + true + bin\Release\ + TRACE;NETFX_CORE;WINDOWS_UWP + prompt + 4 + + + x86 + true + bin\x86\Debug\ + DEBUG;TRACE;NETFX_CORE;WINDOWS_UWP + ;2008 + full + x86 + false + prompt + + + x86 + bin\x86\Release\ + TRACE;NETFX_CORE;WINDOWS_UWP + true + ;2008 + pdbonly + x86 + false + prompt + + + ARM + true + bin\ARM\Debug\ + DEBUG;TRACE;NETFX_CORE;WINDOWS_UWP + ;2008 + full + ARM + false + prompt + + + ARM + bin\ARM\Release\ + TRACE;NETFX_CORE;WINDOWS_UWP + true + ;2008 + pdbonly + ARM + false + prompt + + + x64 + true + bin\x64\Debug\ + DEBUG;TRACE;NETFX_CORE;WINDOWS_UWP + ;2008 + full + x64 + false + prompt + + + x64 + bin\x64\Release\ + TRACE;NETFX_CORE;WINDOWS_UWP + true + ;2008 + pdbonly + x64 + false + prompt + + + + + + + + + + + + + + + + + + + + + + + + {c7673ad5-e3aa-468c-a5fd-fa38154e205c} + ReactNative + + + + + + + + 10.1.5 + + + 5.2.2 + + + $(NETCoreUWPVersion) + + + + 14.0 + + + true + bin\Development\ + DEBUG;TRACE;NETFX_CORE;WINDOWS_UWP + true + full + AnyCPU + false + prompt + MinimumRecommendedRules.ruleset + + + true + bin\x86\Development\ + DEBUG;TRACE;NETFX_CORE;WINDOWS_UWP + ;2008 + true + full + x86 + false + prompt + MinimumRecommendedRules.ruleset + + + true + bin\ARM\Development\ + DEBUG;TRACE;NETFX_CORE;WINDOWS_UWP + ;2008 + true + full + ARM + false + prompt + MinimumRecommendedRules.ruleset + + + true + bin\x64\Development\ + DEBUG;TRACE;NETFX_CORE;WINDOWS_UWP + ;2008 + true + full + x64 + false + prompt + MinimumRecommendedRules.ruleset + + + + \ No newline at end of file diff --git a/src/windows/SQLitePlugin/SQLitePluginModule.cs b/src/windows/SQLitePlugin/SQLitePluginModule.cs new file mode 100644 index 00000000..5f87e0be --- /dev/null +++ b/src/windows/SQLitePlugin/SQLitePluginModule.cs @@ -0,0 +1,635 @@ +/* +* SQLitePluginModule.cs +* +* A React Native module that wraps SQLite. +* +* Thread-safety. Except where otherwise noted, all of this class's code runs on a single +* ActionQueue which frees us from having to worry about thread-safety. We have a programming +* model similar to that of the UI thread. +* +* All ReactMethods must run on the same AwaitingQueue. This prevents the ReactMethods from +* interleaving when an `await` happens. This design enables us to think of each ReactMethod +* as being atomic relative to the other ReactMethods. +*/ + +using Newtonsoft.Json.Linq; +using ReactNative.Bridge; +using System; +using System.Collections.Generic; +using System.IO; +using System.Text; +using System.Threading; +using System.Threading.Tasks; + +namespace Org.PGSQLite.SQLitePlugin +{ + /// + /// A module that allows JS to utilize sqlite databases. + /// + public class SQLitePluginModule : NativeModuleBase + { + public enum WebSQLError + { + Unknown = 0, + Database = 1, + Version = 2, + TooLarge = 3, + Quota = 4, + Syntax = 5, + Constraint = 6, + Timeout = 7 + } + + private static readonly IntPtr NegativePointer = new IntPtr(-1); + + private static WebSQLError sqliteToWebSQLError(SQLite.Net.Interop.Result sqliteError) + { + switch (sqliteError) + { + case SQLite.Net.Interop.Result.Error: + return WebSQLError.Syntax; + case SQLite.Net.Interop.Result.Full: + return WebSQLError.Quota; + case SQLite.Net.Interop.Result.Constraint: + return WebSQLError.Constraint; + default: + return WebSQLError.Unknown; + } + } + + public class SQLiteError + { + public WebSQLError code { get; private set; } + public string message { get; private set; } + + public SQLiteError(WebSQLError aCode, string aMessage) + { + code = aCode; + message = aMessage; + } + } + + private class RNSQLiteException : Exception + { + public object JsonMessage { get; private set; } + + public RNSQLiteException() : base() + { + } + + public RNSQLiteException(object jsonMessage) : base() + { + JsonMessage = jsonMessage; + } + + public RNSQLiteException(string message) : base(message) + { + JsonMessage = message; + } + + public RNSQLiteException(string message, Exception inner) : base(message, inner) + { + JsonMessage = message; + } + } + + private static byte[] GetNullTerminatedUtf8(string s) + { + var utf8Length = Encoding.UTF8.GetByteCount(s); + var bytes = new byte[utf8Length + 1]; + Encoding.UTF8.GetBytes(s, 0, s.Length, bytes, 0); + return bytes; + } + + // Throws when the file already exists. + private static Windows.Foundation.IAsyncOperation CopyDbAsync(Windows.Storage.StorageFile srcDbFile, string destDbFileName) + { + // This implementation is closely related to ResolveDbFilePath. + return srcDbFile.CopyAsync(Windows.Storage.ApplicationData.Current.LocalFolder, destDbFileName, Windows.Storage.NameCollisionOption.FailIfExists); + } + + private static string ResolveDbFilePath(string dbFileName) + { + // This implementation is closely related to CopyDbAsync. + return Path.Combine(Windows.Storage.ApplicationData.Current.LocalFolder.Path, dbFileName); + } + + private static Windows.Foundation.IAsyncOperation ResolveAssetFile(string assetFilePath, string dbFileName) + { + if (assetFilePath == null || assetFilePath.Length == 0) + { + return null; + } + else if (assetFilePath == "1") + { + // Built path to pre-populated DB asset from app bundle www subdirectory + return Windows.Storage.StorageFile.GetFileFromApplicationUriAsync(new Uri( + "ms-appx:///www/" + dbFileName)); + } + else if (assetFilePath[0] == '~') + { + // Built path to pre-populated DB asset from app bundle subdirectory + return Windows.Storage.StorageFile.GetFileFromApplicationUriAsync(new Uri( + "ms-appx:///" + assetFilePath.Substring(1))); + } + else + { + // Built path to pre-populated DB asset from app sandbox directory + return Windows.Storage.StorageFile.GetFileFromApplicationUriAsync(new Uri( + "ms-appdata:///local/" + assetFilePath)); + } + } + + private class OpenDB + { + public SQLite.Net.Interop.IDbHandle Handle { get; private set; } + public string Path { get; private set; } + + public OpenDB(SQLite.Net.Interop.IDbHandle handle, string path) + { + Handle = handle; + Path = path; + } + } + + private readonly SQLite.Net.Platform.WinRT.SQLiteApiWinRT _sqliteAPI; + private readonly Dictionary _openDBs = new Dictionary(); + private readonly AwaitingQueue _awaitingQueue = new AwaitingQueue(); + private readonly CancellationTokenSource _cancellationTokenSource = new CancellationTokenSource(); + + /// + /// Instantiates the . + /// + internal SQLitePluginModule() + { + _sqliteAPI = new SQLite.Net.Platform.WinRT.SQLiteApiWinRT(tempFolderPath: null, useWinSqlite: true); + } + + public override void OnReactInstanceDispose() + { + _cancellationTokenSource.Cancel(); + // TODO: When React Native Windows introduces asynchronous disposal + // (`OnReactInstanceDisposeAsync`), we should start using that and + // we should run this work on the awaiting queue. Currently, there's + // a race condition where we might close the database while one of + // the ReactMethods is in the middle of an `await`. + // https://github.com/Microsoft/react-native-windows/issues/1873 + foreach (var dbInfoPair in _openDBs) + { + if (_sqliteAPI.Close(dbInfoPair.Value.Handle) != SQLite.Net.Interop.Result.OK) + { + System.Diagnostics.Debug.WriteLine("SQLitePluginModule: Error closing database: " + dbInfoPair.Value.Path); + } + } + _openDBs.Clear(); + } + + /// + /// The name of the native module. + /// + public override string Name + { + get + { + return "SQLite"; + } + } + + private async void QueueWithCancellation(Func work) + { + await _awaitingQueue.RunOrQueue(work, _cancellationTokenSource.Token); + } + + public class EchoStringValueOptions + { + public string Value { get; set; } + } + + [ReactMethod] + public void echoStringValue(EchoStringValueOptions options, ICallback success, ICallback error) + { + QueueWithCancellation(() => + { + success.Invoke(options.Value); + return Task.CompletedTask; + }); + } + + public class OpenOptions + { + // Path at which to store the database + public string Name { get; set; } + + // Optional. When creating the DB, uses this file as the initial state. + public string AssetFileName { get; set; } + + public bool ReadOnly { get; set; } + } + + [ReactMethod] + public void open(OpenOptions options, ICallback success, ICallback error) + { + QueueWithCancellation(async () => + { + var dbFileName = options.Name; + + if (dbFileName == null) + { + error.Invoke("You must specify database name"); + return; + } + + if (_openDBs.ContainsKey(dbFileName)) + { + success.Invoke("Database opened"); + return; + } + + var assetFileOp = ResolveAssetFile(options.AssetFileName, dbFileName); + var assetFile = assetFileOp == null ? null : await assetFileOp; + + // NoMutex means SQLite can be safely used by multiple threads provided that no + // single database connection is used simultaneously in two or more threads. + SQLite.Net.Interop.SQLiteOpenFlags sqlOpenFlags = SQLite.Net.Interop.SQLiteOpenFlags.NoMutex; + string absoluteDbPath; + if (options.ReadOnly && assetFileOp != null) + { + sqlOpenFlags |= SQLite.Net.Interop.SQLiteOpenFlags.ReadOnly; + absoluteDbPath = assetFile.Path; + } + else + { + sqlOpenFlags |= SQLite.Net.Interop.SQLiteOpenFlags.ReadWrite | SQLite.Net.Interop.SQLiteOpenFlags.Create; + absoluteDbPath = ResolveDbFilePath(dbFileName); + + // Option to create from resource (pre-populated) if db does not exist: + if (assetFileOp != null) + { + try + { + await CopyDbAsync(assetFile, dbFileName); + } + catch (Exception) + { + // CopyDbAsync throws when the file already exists. + } + } + } + + SQLite.Net.Interop.IDbHandle dbHandle; + if (_sqliteAPI.Open(GetNullTerminatedUtf8(absoluteDbPath), out dbHandle, (int)sqlOpenFlags, IntPtr.Zero) == SQLite.Net.Interop.Result.OK) + { + _openDBs[dbFileName] = new OpenDB(dbHandle, absoluteDbPath); + success.Invoke("Database opened"); + } + else + { + error.Invoke("Unable to open DB"); + } + }); + } + + public class CloseOptions + { + public string Path { get; set; } + } + + [ReactMethod] + public void close(CloseOptions options, ICallback success, ICallback error) + { + QueueWithCancellation(() => + { + var dbFileName = options.Path; + + if (dbFileName == null) + { + error.Invoke("You must specify database path"); + return Task.CompletedTask; + } + + if (!_openDBs.ContainsKey(dbFileName)) + { + error.Invoke("Specified db was not open"); + return Task.CompletedTask; + } + + var dbInfo = _openDBs[dbFileName]; + _openDBs.Remove(dbFileName); + + if (_sqliteAPI.Close(dbInfo.Handle) != SQLite.Net.Interop.Result.OK) + { + System.Diagnostics.Debug.WriteLine("SQLitePluginModule: Error closing database: " + dbInfo.Path); + } + + success.Invoke("DB closed"); + return Task.CompletedTask; + }); + } + + [ReactMethod] + public void attach(JObject options, ICallback success, ICallback error) + { + QueueWithCancellation(() => + { + error.Invoke("attach isn't supported on this platform"); + return Task.CompletedTask; + }); + } + + public class DeleteOptions + { + public string Path { get; set; } + } + + [ReactMethod] + public void delete(DeleteOptions options, ICallback success, ICallback error) + { + QueueWithCancellation(async () => + { + var dbFileName = options.Path; + + if (dbFileName == null) + { + error.Invoke("You must specify database path"); + return; + } + + if (_openDBs.ContainsKey(dbFileName)) + { + var dbInfo = _openDBs[dbFileName]; + _openDBs.Remove(dbFileName); + + if (_sqliteAPI.Close(dbInfo.Handle) != SQLite.Net.Interop.Result.OK) + { + System.Diagnostics.Debug.WriteLine("SQLitePluginModule: Error closing database: " + dbInfo.Path); + } + } + + var absoluteDbPath = ResolveDbFilePath(dbFileName); + try + { + var dbFile = await Windows.Storage.StorageFile.GetFileFromPathAsync(absoluteDbPath); + await dbFile.DeleteAsync(); + } + catch (FileNotFoundException) + { + error.Invoke("The database does not exist on that path"); + return; + } + + success.Invoke("DB deleted"); + }); + } + + public class DBArgs + { + public string DBName { get; set; } + } + + public class DBQuery + { + public int QID { get; set; } + public JArray Params { get; set; } // optional + public string SQL { get; set; } + } + + public class ExecuteSqlBatchOptions + { + public DBArgs DBArgs { get; set; } + public List Executes { get; set; } + } + + private void BindStatement(SQLite.Net.Interop.IDbStatement statement, int argIndex, JToken arg) + { + switch (arg.Type) + { + case JTokenType.Undefined: + case JTokenType.Null: + _sqliteAPI.BindNull(statement, argIndex); + break; + case JTokenType.Boolean: + _sqliteAPI.BindInt(statement, argIndex, arg.ToObject() ? 1 : 0); + break; + case JTokenType.Integer: + _sqliteAPI.BindInt64(statement, argIndex, arg.ToObject()); + break; + case JTokenType.Float: + _sqliteAPI.BindDouble(statement, argIndex, arg.ToObject()); + break; + case JTokenType.String: + _sqliteAPI.BindText16(statement, argIndex, arg.ToObject(), -1, NegativePointer); + break; + default: + _sqliteAPI.BindText16(statement, argIndex, arg.ToObject(), -1, NegativePointer); + break; + } + } + + private object ExtractColumn(SQLite.Net.Interop.IDbStatement statement, int columnIndex) + { + var columnType = _sqliteAPI.ColumnType(statement, columnIndex); + switch (columnType) + { + case SQLite.Net.Interop.ColType.Integer: + return _sqliteAPI.ColumnInt64(statement, columnIndex); + case SQLite.Net.Interop.ColType.Float: + return _sqliteAPI.ColumnDouble(statement, columnIndex); + case SQLite.Net.Interop.ColType.Blob: + return _sqliteAPI.ColumnBlob(statement, columnIndex); + case SQLite.Net.Interop.ColType.Text: + return _sqliteAPI.ColumnText16(statement, columnIndex); + case SQLite.Net.Interop.ColType.Null: + default: + return null; + } + } + + private Dictionary ExtractRow(SQLite.Net.Interop.IDbStatement statement) + { + var row = new Dictionary(); + var columnCount = _sqliteAPI.ColumnCount(statement); + for (var i = 0; i < columnCount; i++) + { + var columnName = _sqliteAPI.ColumnName16(statement, i); + var columnValue = ExtractColumn(statement, i); + if (columnValue != null) + { + row[columnName] = columnValue; + } + } + return row; + } + + public delegate void SQLiteErrorEvent(SQLiteError error); + + public event SQLiteErrorEvent OnSQLiteError; + + private bool _isExecutingQuery = false; + private Dictionary ExecuteQuery(OpenDB dbInfo, DBQuery query) + { + System.Diagnostics.Debug.Assert(!_isExecutingQuery, "SQLitePluginModule: Only 1 query should be executing at a time."); + + _isExecutingQuery = true; + try + { + if (query.SQL == null) + { + throw new RNSQLiteException("You must specify a sql query to execute"); + } + + try + { + var previousRowsAffected = _sqliteAPI.TotalChanges(dbInfo.Handle); + + var statement = _sqliteAPI.Prepare2(dbInfo.Handle, query.SQL); + if (query.Params != null) + { + var argIndex = 0; + foreach (var arg in query.Params.Children()) + { + // sqlite bind uses 1-based indexing for the arguments + BindStatement(statement, argIndex + 1, arg); + argIndex++; + } + } + + var resultRows = new List>(); + + long? insertId = null; + var rowsAffected = 0; + SQLiteError error = null; + var keepGoing = true; + while (keepGoing) + { + switch (_sqliteAPI.Step(statement)) + { + case SQLite.Net.Interop.Result.Row: + resultRows.Add(ExtractRow(statement)); + break; + + case SQLite.Net.Interop.Result.Done: + var nowRowsAffected = _sqliteAPI.TotalChanges(dbInfo.Handle); + rowsAffected = nowRowsAffected - previousRowsAffected; + var nowInsertId = _sqliteAPI.LastInsertRowid(dbInfo.Handle); + if (rowsAffected > 0 && nowInsertId != 0) + { + insertId = nowInsertId; + } + keepGoing = false; + break; + + default: + var webErrorCode = sqliteToWebSQLError(_sqliteAPI.ErrCode(dbInfo.Handle)); + var message = _sqliteAPI.Errmsg16(dbInfo.Handle); + error = new SQLiteError(webErrorCode, message); + keepGoing = false; + break; + } + } + + _sqliteAPI.Finalize(statement); + + if (error != null) + { + NotifyOnSQLiteException(error); + throw new RNSQLiteException(error); + } + + var resultSet = new Dictionary + { + { "rows", resultRows }, + { "rowsAffected", rowsAffected } + }; + if (insertId != null) + { + resultSet["insertId"] = insertId; + } + return resultSet; + } + catch (SQLite.Net.SQLiteException ex) + { + var error = new SQLiteError(sqliteToWebSQLError(ex.Result), ex.Message); + NotifyOnSQLiteException(error); + throw new RNSQLiteException(error); + } + } + finally + { + _isExecutingQuery = false; + } + } + + private void NotifyOnSQLiteException(SQLiteError error) + { + try + { + OnSQLiteError?.Invoke(error); + } + catch (Exception) + { + // no-op + } + } + + [ReactMethod] + public void executeSqlBatch(ExecuteSqlBatchOptions options, ICallback success, ICallback error) + { + QueueWithCancellation(() => + { + var dbFileName = options.DBArgs.DBName; + + if (dbFileName == null) + { + error.Invoke("You must specify database path"); + return Task.CompletedTask; + } + + OpenDB dbInfo; + if (!_openDBs.TryGetValue(dbFileName, out dbInfo)) + { + error.Invoke("No such database, you must open it first"); + return Task.CompletedTask; + } + + var results = new List>(); + foreach (var query in options.Executes) + { + try + { + var rawResult = ExecuteQuery(dbInfo, query); + results.Add(new Dictionary + { + { "qid", query.QID }, + { "type", "success" }, + { "result", rawResult } + }); + } + catch (RNSQLiteException ex) + { + results.Add(new Dictionary + { + { "qid", query.QID }, + { "type", "error" }, + { "error", ex.JsonMessage }, + { "result", ex.JsonMessage } + }); + } + } + + success.Invoke(results); + return Task.CompletedTask; + }); + } + + [ReactMethod] + public void backgroundExecuteSqlBatch(ExecuteSqlBatchOptions options, ICallback success, ICallback error) + { + // Currently, all ReactMethods are run on the same ActionQueue. This prevents + // queries from being able to run in parallel but it makes the code simpler. + // + // `executeSqlBatch` takes care of putting the work on the awaiting queue + // so we don't have to. + executeSqlBatch(options, success, error); + } + } +} diff --git a/src/windows/SQLitePlugin/SQLitePluginPackage.cs b/src/windows/SQLitePlugin/SQLitePluginPackage.cs new file mode 100644 index 00000000..3b2c617f --- /dev/null +++ b/src/windows/SQLitePlugin/SQLitePluginPackage.cs @@ -0,0 +1,53 @@ +using ReactNative.Bridge; +using ReactNative.Modules.Core; +using ReactNative.UIManager; +using System; +using System.Collections.Generic; + +namespace Org.PGSQLite.SQLitePlugin +{ + /// + /// Package defining core framework modules (e.g., ). + /// It should be used for modules that require special integration with + /// other framework parts (e.g., with the list of packages to load view + /// managers from). + /// + public class SQLitePluginPackage : IReactPackage + { + /// + /// Creates the list of native modules to register with the react + /// instance. + /// + /// The react application context. + /// The list of native modules. + public IReadOnlyList CreateNativeModules(ReactContext reactContext) + { + return new List + { + new SQLitePluginModule(), + }; + } + + /// + /// Creates the list of JavaScript modules to register with the + /// react instance. + /// + /// The list of JavaScript modules. + public IReadOnlyList CreateJavaScriptModulesConfig() + { + return new List(0); + } + + /// + /// Creates the list of view managers that should be registered with + /// the . + /// + /// The react application context. + /// The list of view managers. + public IReadOnlyList CreateViewManagers( + ReactContext reactContext) + { + return new List(0); + } + } +}