From 18ba121b88ae9351872d72b6b8ac31a051c1f170 Mon Sep 17 00:00:00 2001 From: UnknownShadow200 Date: Fri, 25 Mar 2016 19:06:34 +1100 Subject: [PATCH] More work on native sqlite backend. --- Database/Native/BulkNativeTransaction.cs | 8 +-- Database/Native/NativeCommand.cs | 66 ++++++++++++++++++++--- Database/Native/NativeConnection.cs | 69 ++++++++++++++++++++++++ Database/Native/Utils.cs | 59 ++++++++++++++------ MCGalaxy_.csproj | 1 + 5 files changed, 171 insertions(+), 32 deletions(-) create mode 100644 Database/Native/NativeConnection.cs diff --git a/Database/Native/BulkNativeTransaction.cs b/Database/Native/BulkNativeTransaction.cs index ead59ab13..b67349f0a 100644 --- a/Database/Native/BulkNativeTransaction.cs +++ b/Database/Native/BulkNativeTransaction.cs @@ -48,7 +48,7 @@ namespace MCGalaxy.SQL.Native { } public override IDbCommand CreateCommand(string query) { - IDbCommand cmd = new NativeSQLiteCommand(); + IDbCommand cmd = new NativeCommand(); cmd.CommandText = query; cmd.Connection = connection; cmd.Transaction = transaction; @@ -61,11 +61,5 @@ namespace MCGalaxy.SQL.Native { param.DbType = type; return param; } - - [DllImport("sqlite3.dll")] - static extern int sqlite3_open_v2(byte[] filename, out IntPtr db, int flags, IntPtr vfs); - - [DllImport("sqlite3.dll")] - static extern int sqlite3_close_v2(IntPtr db); } } \ No newline at end of file diff --git a/Database/Native/NativeCommand.cs b/Database/Native/NativeCommand.cs index 5b2144407..9e6adb050 100644 --- a/Database/Native/NativeCommand.cs +++ b/Database/Native/NativeCommand.cs @@ -16,22 +16,23 @@ permissions and limitations under the Licenses. */ using System; +using System.Collections.Generic; using System.Data; using System.Runtime.InteropServices; namespace MCGalaxy.SQL.Native { - sealed class NativeSQLiteCommand : IDbCommand { - IntPtr statement; - + sealed class NativeCommand : IDbCommand { + public IntPtr Statement; + NativeParamsList args = new NativeParamsList(); + public IDbConnection Connection { get; set; } public IDbTransaction Transaction { get; set; } public string CommandText { get; set; } public int CommandTimeout { get; set; } public CommandType CommandType { get; set; } - public IDataParameterCollection Parameters { get; private set; } + public IDataParameterCollection Parameters { get { return args; } } public UpdateRowSource UpdatedRowSource { get; set; } - public IDbDataParameter CreateParameter() { return null; } public void Cancel() { } public IDataReader ExecuteReader() { return ExecuteReader(CommandBehavior.Default); } @@ -39,21 +40,70 @@ namespace MCGalaxy.SQL.Native { public object ExecuteScalar() { return null; } public void Prepare() { + byte[] sql = NativeUtils.MakeUTF8(CommandText); + IntPtr db = ((NativeConnection)Connection).DB; + IntPtr tail; + int code = sqlite3_prepare_v2(db, sql, sql.Length, out Statement, out tail); + if (code > 0) throw new NativeException(code); } public int ExecuteNonQuery() { - int code = sqlite3_step(statement); + foreach (IDataParameter param in args) + BindParam(param); + + int code = sqlite3_step(Statement); if (code > 0) throw new NativeException(code); - code = sqlite3_reset(statement); + code = sqlite3_reset(Statement); if (code > 0) throw new NativeException(code); return 0; } public void Dispose() { - int code = sqlite3_finalize(statement); + int code = sqlite3_finalize(Statement); if (code > 0) throw new NativeException(code); } + void BindParam(IDataParameter param) { + NativeParameter nParam = (NativeParameter)param; + if (nParam.Index == -1) BindIndex(nParam); + + DbType type = param.DbType; + int code = 0; + switch (type) { + case DbType.AnsiStringFixedLength: + byte[] data = NativeUtils.MakeUTF8((string)nParam.Value); + code = sqlite3_bind_text(Statement, nParam.Index, data, data.Length - 1, IntPtr.Zero); + break; + case DbType.UInt16: + ushort value_u16 = (ushort)nParam.Value; + code = sqlite3_bind_int(Statement, nParam.Index, value_u16); + break; + case DbType.Byte: + byte value_u8 = (byte)nParam.Value; + code = sqlite3_bind_int(Statement, nParam.Index, value_u8); + break; + case DbType.Boolean: + bool value_bool = (bool)nParam.Value; + code = sqlite3_bind_int(Statement, nParam.Index, value_bool ? 1 : 0); + break; + } + if (code > 0) throw new NativeException(code); + } + + void BindIndex(NativeParameter nParam) { + byte[] name = NativeUtils.MakeUTF8(nParam.ParameterName); + nParam.Index = sqlite3_bind_parameter_index(Statement, name); + } + + [DllImport("sqlite3.dll")] + static extern int sqlite3_bind_int(IntPtr stmt, int index, int value); + + [DllImport("sqlite3.dll")] + static extern int sqlite3_bind_parameter_index(IntPtr stmt, byte[] name); + + [DllImport("sqlite3.dll")] + static extern int sqlite3_bind_text(IntPtr stmt, int index, byte[] text, int textLen, IntPtr reserved); + [DllImport("sqlite3.dll")] static extern int sqlite3_finalize(IntPtr stmt); diff --git a/Database/Native/NativeConnection.cs b/Database/Native/NativeConnection.cs new file mode 100644 index 000000000..fcd4b4f49 --- /dev/null +++ b/Database/Native/NativeConnection.cs @@ -0,0 +1,69 @@ +/* + Copyright 2015 MCGalaxy + + Dual-licensed under the Educational Community License, Version 2.0 and + the GNU General Public License, Version 3 (the "Licenses"); you may + not use this file except in compliance with the Licenses. You may + obtain a copy of the Licenses at + + http://www.opensource.org/licenses/ecl2.php + http://www.gnu.org/licenses/gpl-3.0.html + + Unless required by applicable law or agreed to in writing, + software distributed under the Licenses are distributed on an "AS IS" + BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express + or implied. See the Licenses for the specific language governing + permissions and limitations under the Licenses. + */ +using System; +using System.Data; +using System.Runtime.InteropServices; +using System.Text; + +namespace MCGalaxy.SQL.Native { + + sealed class NativeConnection : IDbConnection { + public IntPtr DB; + + public string ConnectionString { get; set; } + public int ConnectionTimeout { get { return 0; } } + public string Database { get { return ""; } } + public ConnectionState State { get { return ConnectionState.Open; } } + public IDbTransaction BeginTransaction() { return BeginTransaction(IsolationLevel.Unspecified); } + public void ChangeDatabase(string databaseName) { } + + public IDbTransaction BeginTransaction(IsolationLevel il) { + return null; + } + + public void Close() { + int code = sqlite3_close_v2(DB); + DB = IntPtr.Zero; + if (code > 0) throw new NativeException(code); + } + + public IDbCommand CreateCommand() { + IDbCommand cmd = new NativeCommand(); + cmd.Connection = this; + return cmd; + } + + public void Dispose() { + if (DB == IntPtr.Zero) return; + Close(); + } + + public void Open() { + string[] args = ConnectionString.Split(';'); + byte[] filename = NativeUtils.MakeUTF8(args[0]); + int code = sqlite3_open_v2(filename, out DB, 0, IntPtr.Zero); + if (code > 0) throw new NativeException(code); + } + + [DllImport("sqlite3.dll")] + static extern int sqlite3_open_v2(byte[] filename, out IntPtr db, int flags, IntPtr vfs); + + [DllImport("sqlite3.dll")] + static extern int sqlite3_close_v2(IntPtr db); + } +} \ No newline at end of file diff --git a/Database/Native/Utils.cs b/Database/Native/Utils.cs index 70bf5f251..68f95f950 100644 --- a/Database/Native/Utils.cs +++ b/Database/Native/Utils.cs @@ -16,32 +16,32 @@ permissions and limitations under the Licenses. */ using System; +using System.Collections.Generic; using System.Data; using System.Text; namespace MCGalaxy.SQL.Native { - - sealed class NativeConnection : IDbConnection { - public string ConnectionString { get; set; } - public int ConnectionTimeout { get { return 0; } } - public string Database { get { return ""; } } - public ConnectionState State { get { return ConnectionState.Open; } } - - public IDbTransaction BeginTransaction() { return BeginTransaction(IsolationLevel.Unspecified); } - public IDbTransaction BeginTransaction(IsolationLevel il) { return null; } - public void Close() { } - public void ChangeDatabase(string databaseName) { } - public IDbCommand CreateCommand() { return null; } - public void Open() { } - public void Dispose() { } - } sealed class NativeTransaction : IDbTransaction { public IDbConnection Connection { get; set; } public IsolationLevel IsolationLevel { get { return IsolationLevel.Unspecified; } } - public void Commit() { } - public void Rollback() { } + + public NativeTransaction(IDbConnection connection) { + Connection = connection; + DoCommand("BEGIN"); + } + + public void Commit() { DoCommand("COMMIT"); } + public void Rollback() { DoCommand("ROLLBACK"); } public void Dispose() { } + + void DoCommand(string query) { + using (IDbCommand cmd = Connection.CreateCommand()) { + cmd.CommandText = query; + cmd.Prepare(); + cmd.ExecuteNonQuery(); + } + } } sealed class NativeParameter : IDataParameter { @@ -52,6 +52,31 @@ namespace MCGalaxy.SQL.Native { public string SourceColumn { get; set; } public DataRowVersion SourceVersion { get; set; } public object Value { get; set; } + public int Index = -1; + } + + sealed class NativeParamsList : List, IDataParameterCollection { + + public object this[string parameterName] { + get { return this[IndexOf(parameterName)]; } + set { this[IndexOf(parameterName)] = (IDbDataParameter)value; } + } + + public bool Contains(string parameterName) { + return IndexOf(parameterName) >= 0; + } + + public int IndexOf(string parameterName) { + for (int i = 0; i < Count; i++) { + if (this[i].ParameterName == parameterName) return i; + } + return -1; + } + + public void RemoveAt(string parameterName) { + int index = IndexOf(parameterName); + if (index >= 0) RemoveAt(index); + } } static class NativeUtils { diff --git a/MCGalaxy_.csproj b/MCGalaxy_.csproj index d6c40642b..7cb865ae8 100644 --- a/MCGalaxy_.csproj +++ b/MCGalaxy_.csproj @@ -393,6 +393,7 @@ +