diff --git a/Commands/Moderation/CmdRenameLvl.cs b/Commands/Moderation/CmdRenameLvl.cs index 4e0614666..ce198093b 100644 --- a/Commands/Moderation/CmdRenameLvl.cs +++ b/Commands/Moderation/CmdRenameLvl.cs @@ -103,7 +103,7 @@ namespace MCGalaxy.Commands "`Messages{0}` TO `Messages{1}`, " + "`Zone{0}` TO `Zone{1}`", foundLevel.name.ToLower(), newName.ToLower())); else { - using (DatabaseTransactionHelper helper = SQLiteTransactionHelper.Create()) { // ensures that it's either all work, or none work. + using (BulkDatabaseTransaction helper = BulkSQLiteTransaction.Create()) { // ensures that it's either all work, or none work. helper.Execute(String.Format("ALTER TABLE Block{0} RENAME TO Block{1}", foundLevel.name.ToLower(), newName.ToLower())); helper.Execute(String.Format("ALTER TABLE Portals{0} RENAME TO Portals{1}", foundLevel.name.ToLower(), newName.ToLower())); helper.Execute(String.Format("ALTER TABLE Messages{0} RENAME TO Messages{1}", foundLevel.name.ToLower(), newName.ToLower())); diff --git a/Database/DatabaseTransactionHelper.cs b/Database/BulkDatabaseTransaction.cs similarity index 85% rename from Database/DatabaseTransactionHelper.cs rename to Database/BulkDatabaseTransaction.cs index 25eb38f65..9e185635b 100644 --- a/Database/DatabaseTransactionHelper.cs +++ b/Database/BulkDatabaseTransaction.cs @@ -25,20 +25,20 @@ using MySql.Data.MySqlClient; namespace MCGalaxy.SQL { - public abstract class DatabaseTransactionHelper : IDisposable { + public abstract class BulkDatabaseTransaction : IDisposable { protected IDbConnection connection; protected IDbTransaction transaction; - public static DatabaseTransactionHelper Create() { - if (Server.useMySQL) return MySQLTransactionHelper.Create(MySQL.connString); - else return SQLiteTransactionHelper.Create(SQLite.connString); + public static BulkDatabaseTransaction Create() { + if (Server.useMySQL) return BulkMySQLTransaction.Create(MySQL.connString); + else return BulkSQLiteTransaction.Create(SQLite.connString); } public abstract bool Execute(string query); public abstract IDbCommand CreateCommand(string query); - public abstract DbParameter CreateParam(string paramName, DbType type); + public abstract IDataParameter CreateParam(string paramName, DbType type); public void Commit() { try { diff --git a/Database/MySQLTransactionHelper.cs b/Database/BulkMySQLTransaction.cs similarity index 86% rename from Database/MySQLTransactionHelper.cs rename to Database/BulkMySQLTransaction.cs index 2f0456bfe..04a01f242 100644 --- a/Database/MySQLTransactionHelper.cs +++ b/Database/BulkMySQLTransaction.cs @@ -25,9 +25,9 @@ using MySql.Data.MySqlClient; namespace MCGalaxy.SQL { - public sealed class MySQLTransactionHelper : DatabaseTransactionHelper { + public sealed class BulkMySQLTransaction : BulkDatabaseTransaction { - public MySQLTransactionHelper(string connString) { + public BulkMySQLTransaction(string connString) { Init(connString); } @@ -39,9 +39,9 @@ namespace MCGalaxy.SQL { transaction = connection.BeginTransaction(); } - public static DatabaseTransactionHelper Create(string connString) { + public static BulkDatabaseTransaction Create(string connString) { try { - return new MySQLTransactionHelper(connString); + return new BulkMySQLTransaction(connString); } catch (Exception ex) { Server.ErrorLog(ex); return null; @@ -66,7 +66,7 @@ namespace MCGalaxy.SQL { return new MySqlCommand(query, (MySqlConnection)connection, (MySqlTransaction)transaction); } - public override DbParameter CreateParam(string paramName, DbType type) { + public override IDataParameter CreateParam(string paramName, DbType type) { MySqlParameter arg = new MySqlParameter(paramName, null); arg.DbType = type; return arg; diff --git a/Database/SQLiteTransactionHelper.cs b/Database/BulkSQLiteTransaction.cs similarity index 85% rename from Database/SQLiteTransactionHelper.cs rename to Database/BulkSQLiteTransaction.cs index 6fdd73903..19693d977 100644 --- a/Database/SQLiteTransactionHelper.cs +++ b/Database/BulkSQLiteTransaction.cs @@ -22,9 +22,9 @@ using System.Data.SQLite; namespace MCGalaxy.SQL { - public sealed class SQLiteTransactionHelper : DatabaseTransactionHelper { + public sealed class BulkSQLiteTransaction : BulkDatabaseTransaction { - private SQLiteTransactionHelper(string connString) { + private BulkSQLiteTransaction(string connString) { Init(connString); } @@ -35,9 +35,9 @@ namespace MCGalaxy.SQL { transaction = connection.BeginTransaction(); } - public static DatabaseTransactionHelper Create(string connString) { + public static BulkDatabaseTransaction Create(string connString) { try { - return new SQLiteTransactionHelper(connString); + return new BulkSQLiteTransaction(connString); } catch (Exception ex) { Server.ErrorLog(ex); return null; @@ -62,7 +62,7 @@ namespace MCGalaxy.SQL { return new SQLiteCommand(query, (SQLiteConnection)connection, (SQLiteTransaction)transaction); } - public override DbParameter CreateParam(string paramName, DbType type) { + public override IDataParameter CreateParam(string paramName, DbType type) { return new SQLiteParameter(paramName, type); } } diff --git a/Database/Database.cs b/Database/Database.cs index 20886804f..b1ba3facb 100644 --- a/Database/Database.cs +++ b/Database/Database.cs @@ -301,7 +301,7 @@ namespace MCGalaxy string[] cmds = script.Split(';'); StringBuilder sb = new StringBuilder(); - using (DatabaseTransactionHelper helper = DatabaseTransactionHelper.Create()) + using (BulkDatabaseTransaction helper = BulkDatabaseTransaction.Create()) { foreach (string cmd in cmds) diff --git a/Database/Native/BulkNativeTransaction.cs b/Database/Native/BulkNativeTransaction.cs new file mode 100644 index 000000000..ead59ab13 --- /dev/null +++ b/Database/Native/BulkNativeTransaction.cs @@ -0,0 +1,71 @@ +/* + 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; + +namespace MCGalaxy.SQL.Native { + + public partial class BulkNativeTransaction : BulkDatabaseTransaction { + + private BulkNativeTransaction(string connString) { + Init(connString); + } + + void Init(string connString) { + connection = new NativeConnection(); + connection.ConnectionString = SQLite.connString; + connection.Open(); + transaction = connection.BeginTransaction(); + } + + public static BulkDatabaseTransaction Create(string connString) { + try { + return new BulkNativeTransaction(connString); + } catch (Exception ex) { + Server.ErrorLog(ex); + return null; + } + } + + public override bool Execute(string query) { + throw new NotImplementedException(); + } + + public override IDbCommand CreateCommand(string query) { + IDbCommand cmd = new NativeSQLiteCommand(); + cmd.CommandText = query; + cmd.Connection = connection; + cmd.Transaction = transaction; + return cmd; + } + + public override IDataParameter CreateParam(string paramName, DbType type) { + IDataParameter param = new NativeParameter(); + param.ParameterName = paramName; + 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 new file mode 100644 index 000000000..5b2144407 --- /dev/null +++ b/Database/Native/NativeCommand.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; + +namespace MCGalaxy.SQL.Native { + + sealed class NativeSQLiteCommand : IDbCommand { + IntPtr statement; + + 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 UpdateRowSource UpdatedRowSource { get; set; } + + public IDbDataParameter CreateParameter() { return null; } + public void Cancel() { } + public IDataReader ExecuteReader() { return ExecuteReader(CommandBehavior.Default); } + public IDataReader ExecuteReader(CommandBehavior behavior) { return null; } + public object ExecuteScalar() { return null; } + + public void Prepare() { + } + + public int ExecuteNonQuery() { + int code = sqlite3_step(statement); + if (code > 0) throw new NativeException(code); + code = sqlite3_reset(statement); + if (code > 0) throw new NativeException(code); + return 0; + } + + public void Dispose() { + int code = sqlite3_finalize(statement); + if (code > 0) throw new NativeException(code); + } + + [DllImport("sqlite3.dll")] + static extern int sqlite3_finalize(IntPtr stmt); + + [DllImport("sqlite3.dll")] + static extern int sqlite3_prepare_v2(IntPtr db, byte[] sql, int nBytes, out IntPtr stmt, out IntPtr sqlTail); + + [DllImport("sqlite3.dll")] + static extern int sqlite3_reset(IntPtr stmt); + + [DllImport("sqlite3.dll")] + static extern int sqlite3_step(IntPtr stmt); + } +} \ No newline at end of file diff --git a/Database/Native/NativeException.cs b/Database/Native/NativeException.cs new file mode 100644 index 000000000..436c6466e --- /dev/null +++ b/Database/Native/NativeException.cs @@ -0,0 +1,72 @@ +/* + 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; + +namespace MCGalaxy.SQL.Native { + + sealed class NativeException : Exception { + public readonly int ErrorCode; + + public NativeException(int errorCode) { + ErrorCode = errorCode; + } + + public override string ToString() { + byte primaryCode = (byte)ErrorCode; + string desc = errors[primaryCode] ?? "SQL error"; + return desc + " (" + ErrorCode + ")"; + } + + static string[] errors = new string[256]; + static NativeException() { + errors[0] = "Successful result"; + errors[1] = "SQL error or missing database"; + errors[2] = "Internal logic error in SQLite"; + errors[3] = "Access permission denied"; + errors[4] = "Callback routine requested an abort"; + errors[5] = "The database file is locked"; + errors[6] = "A table in the database is locked"; + errors[7] = "A malloc() failed"; + errors[8] = "Attempt to write a readonly database"; + errors[9] = "Operation terminated by sqlite3_interrupt()"; + errors[10] = "Some kind of disk I/O error occurred"; + errors[11] = "The database disk image is malformed"; + errors[12] = "Unknown opcode in sqlite3_file_control()"; + errors[13] = "Insertion failed because database is full"; + errors[14] = "Unable to open the database file"; + errors[15] = "Database lock protocol error"; + errors[16] = "Database is empty"; + errors[17] = "The database schema changed"; + errors[18] = "String or BLOB exceeds size limit"; + errors[19] = "Abort due to constraint violation"; + errors[20] = "Data type mismatch"; + errors[21] = "Library used incorrectly"; + errors[22] = "Uses OS features not supported on host"; + errors[23] = "Authorization denied"; + errors[24] = "Auxiliary database format error"; + errors[25] = "2nd parameter to sqlite3_bind out of range"; + errors[26] = "File opened that is not a database file"; + errors[27] = "Notifications from sqlite3_log()"; + errors[28] = "Warnings from sqlite3_log()"; + errors[100] = "sqlite3_step() has another row ready"; + errors[101] = "sqlite3_step() has finished executing"; + } + } +} \ No newline at end of file diff --git a/Database/Native/Utils.cs b/Database/Native/Utils.cs new file mode 100644 index 000000000..70bf5f251 --- /dev/null +++ b/Database/Native/Utils.cs @@ -0,0 +1,67 @@ +/* + 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.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 void Dispose() { } + } + + sealed class NativeParameter : IDataParameter { + public DbType DbType { get; set; } + public ParameterDirection Direction { get; set; } + public bool IsNullable { get { return false; } } + public string ParameterName { get; set; } + public string SourceColumn { get; set; } + public DataRowVersion SourceVersion { get; set; } + public object Value { get; set; } + } + + static class NativeUtils { + static Encoding encoding = Encoding.UTF8; + + public static byte[] MakeUTF8(string input) { + int count = encoding.GetByteCount(input) + 1; // null terminator + byte[] chars = new byte[count]; + encoding.GetBytes(input, 0, input.Length, chars, 0); + return chars; + } + } +} \ No newline at end of file diff --git a/Levels/Level.cs b/Levels/Level.cs index 56841935f..197df855c 100644 --- a/Levels/Level.cs +++ b/Levels/Level.cs @@ -366,7 +366,7 @@ namespace MCGalaxy List tempCache = blockCache; string date = new String('-', 19); //yyyy-mm-dd hh:mm:ss - using (DatabaseTransactionHelper transaction = DatabaseTransactionHelper.Create()) { + using (BulkDatabaseTransaction transaction = BulkDatabaseTransaction.Create()) { fixed (char* ptr = date) { ptr[4] = '-'; ptr[7] = '-'; ptr[10] = ' '; ptr[13] = ':'; ptr[16] = ':'; DoSaveChanges(tempCache, ptr, date, transaction); @@ -378,19 +378,19 @@ namespace MCGalaxy } unsafe bool DoSaveChanges(List tempCache, char* ptr, string date, - DatabaseTransactionHelper transaction) { + BulkDatabaseTransaction transaction) { string template = "INSERT INTO `Block" + name + "` (Username, TimePerformed, X, Y, Z, type, deleted) VALUES (@Name, @Time, @X, @Y, @Z, @Tile, @Del)"; ushort x, y, z; IDbCommand cmd = transaction.CreateCommand(template); - DbParameter nameP = transaction.CreateParam("@Name", DbType.AnsiStringFixedLength); cmd.Parameters.Add(nameP); - DbParameter timeP = transaction.CreateParam("@Time", DbType.AnsiStringFixedLength); cmd.Parameters.Add(timeP); - DbParameter xP = transaction.CreateParam("@X", DbType.UInt16); cmd.Parameters.Add(xP); - DbParameter yP = transaction.CreateParam("@Y", DbType.UInt16); cmd.Parameters.Add(yP); - DbParameter zP = transaction.CreateParam("@Z", DbType.UInt16); cmd.Parameters.Add(zP); - DbParameter tileP = transaction.CreateParam("@Tile", DbType.Byte); cmd.Parameters.Add(tileP); - DbParameter delP = transaction.CreateParam("@Del", DbType.Boolean); cmd.Parameters.Add(delP); + IDataParameter nameP = transaction.CreateParam("@Name", DbType.AnsiStringFixedLength); cmd.Parameters.Add(nameP); + IDataParameter timeP = transaction.CreateParam("@Time", DbType.AnsiStringFixedLength); cmd.Parameters.Add(timeP); + IDataParameter xP = transaction.CreateParam("@X", DbType.UInt16); cmd.Parameters.Add(xP); + IDataParameter yP = transaction.CreateParam("@Y", DbType.UInt16); cmd.Parameters.Add(yP); + IDataParameter zP = transaction.CreateParam("@Z", DbType.UInt16); cmd.Parameters.Add(zP); + IDataParameter tileP = transaction.CreateParam("@Tile", DbType.Byte); cmd.Parameters.Add(tileP); + IDataParameter delP = transaction.CreateParam("@Del", DbType.Boolean); cmd.Parameters.Add(delP); for (int i = 0; i < tempCache.Count; i++) { BlockPos bP = tempCache[i]; @@ -405,7 +405,7 @@ namespace MCGalaxy tileP.Value = (bP.flags & 2) != 0 ? Block.custom_block : bP.rawType; delP.Value = (bP.flags & 1) != 0; - if (!DatabaseTransactionHelper.Execute(template, cmd)) { + if (!BulkDatabaseTransaction.Execute(template, cmd)) { cmd.Dispose(); transaction.Rollback(); return false; } diff --git a/MCGalaxy_.csproj b/MCGalaxy_.csproj index 3cdae749f..d6c40642b 100644 --- a/MCGalaxy_.csproj +++ b/MCGalaxy_.csproj @@ -393,6 +393,10 @@ + + + + @@ -536,7 +540,7 @@ - + @@ -548,13 +552,13 @@ EditText.cs - + - + @@ -734,6 +738,7 @@ +