diff --git a/TShockAPI/DB/BanManager.cs b/TShockAPI/DB/BanManager.cs index bd57bf8fb..76aa9d69c 100644 --- a/TShockAPI/DB/BanManager.cs +++ b/TShockAPI/DB/BanManager.cs @@ -1,4 +1,4 @@ -/* +/* TShock, a server mod for Terraria Copyright (C) 2011-2019 Pryaxis & TShock Contributors @@ -106,7 +106,7 @@ public void TryConvertBans() { SqlType.Mysql => database.QueryScalar("SELECT COUNT(table_name) FROM information_schema.tables WHERE table_schema = @0 and table_name = 'Bans'", TShock.Config.Settings.MySqlDbName), SqlType.Sqlite => database.QueryScalar("SELECT COUNT(name) FROM sqlite_master WHERE type='table' AND name = 'Bans'"), - SqlType.Postgres => database.QueryScalar("SELECT COUNT(table_name) FROM information_schema.tables WHERE table_name = 'Bans'"), + SqlType.Postgres => database.QueryScalar("SELECT COUNT(table_name) FROM information_schema.tables WHERE table_schema=current_schema() AND table_name ILIKE 'Bans'"), }; if (res != 0) @@ -298,7 +298,7 @@ public AddBanResult InsertBan(BanPreAddEventArgs args) { SqlType.Mysql => /*lang=mysql*/"; SELECT LAST_INSERT_ID();", SqlType.Sqlite => /*lang=sqlite*/"; SELECT last_insert_rowid();", - SqlType.Postgres => /*lang=postgresql*/"RETURNING \"Identifier\";", + SqlType.Postgres => /*lang=postgresql*/" RETURNING \"ticketnumber\";", _ => null }; diff --git a/TShockAPI/DB/Queries/GenericQueryBuilder.cs b/TShockAPI/DB/Queries/GenericQueryBuilder.cs index b62d3925d..d9e4d7d37 100644 --- a/TShockAPI/DB/Queries/GenericQueryBuilder.cs +++ b/TShockAPI/DB/Queries/GenericQueryBuilder.cs @@ -1,4 +1,4 @@ -/* +/* TShock, a server mod for Terraria Copyright (C) 2011-2025 Pryaxis & TShock Contributors @@ -39,6 +39,13 @@ public abstract class GenericQueryBuilder : IQueryBuilder /// protected abstract string EscapeTableName(string table); + /// + /// Escapes a column identifier for the current SQL dialect. + /// + /// The column name to escape. + /// The escaped column identifier. + protected virtual string EscapeColumnName(string column) => column; + /// public abstract string CreateTable(SqlTable table); @@ -55,7 +62,7 @@ public string AlterTable(SqlTable from, SqlTable to) var create = CreateTable(to); // combine all columns in the 'from' variable excluding ones that aren't in the 'to' variable. // exclude the ones that aren't in 'to' variable because if the column is deleted, why try to import the data? - var columns = string.Join(", ", from.Columns.Where(c => to.Columns.Any(c2 => c2.Name == c.Name)).Select(c => $"`{c.Name}`")); + var columns = string.Join(", ", from.Columns.Where(c => to.Columns.Any(c2 => c2.Name.Equals(c.Name, StringComparison.OrdinalIgnoreCase))).Select(c => EscapeColumnName(c.Name))); var insert = "INSERT INTO {0} ({1}) SELECT {1} FROM {2}".SFormat(escapedTable, columns, tmpTable); var drop = "DROP TABLE {0}".SFormat(tmpTable); return "{0}; {1}; {2}; {3};".SFormat(alter, create, insert, drop); @@ -131,6 +138,6 @@ public string InsertValues(string table, List values) /// /// protected static string BuildWhere(List wheres) => wheres.Count > 0 - ? string.Empty - : "WHERE {0}".SFormat(string.Join(", ", wheres.Select(v => $"{v.Name} = {v.Value}"))); + ? "WHERE {0}".SFormat(string.Join(" AND ", wheres.Select(v => $"{v.Name} = {v.Value}"))) + : string.Empty; } diff --git a/TShockAPI/DB/Queries/MysqlQueryBuilder.cs b/TShockAPI/DB/Queries/MysqlQueryBuilder.cs index 851859608..d2f397e70 100644 --- a/TShockAPI/DB/Queries/MysqlQueryBuilder.cs +++ b/TShockAPI/DB/Queries/MysqlQueryBuilder.cs @@ -1,4 +1,4 @@ -/* +/* TShock, a server mod for Terraria Copyright (C) 2011-2025 Pryaxis & TShock Contributors @@ -83,6 +83,9 @@ public override string DbTypeToString(MySqlDbType type, int? length) throw new NotImplementedException(Enum.GetName(typeof(MySqlDbType), type)); } + /// + protected override string EscapeColumnName(string column) => $"`{column}`"; + /// protected override string EscapeTableName(string table) => table.SFormat("`{0}`", table); } diff --git a/TShockAPI/DB/Queries/PostgresQueryBuilder.cs b/TShockAPI/DB/Queries/PostgresQueryBuilder.cs index dd92121e5..dec87ba7b 100644 --- a/TShockAPI/DB/Queries/PostgresQueryBuilder.cs +++ b/TShockAPI/DB/Queries/PostgresQueryBuilder.cs @@ -1,4 +1,4 @@ -/* +/* TShock, a server mod for Terraria Copyright (C) 2011-2025 Pryaxis & TShock Contributors @@ -46,6 +46,9 @@ public class PostgresQueryBuilder : GenericQueryBuilder _ => throw new NotImplementedException(Enum.GetName(typeof(MySqlDbType), type)) }; + /// + protected override string EscapeColumnName(string column) => $"\"{column.ToLowerInvariant().Replace("\"", "\"\"")}\""; + /// protected override string EscapeTableName(string table) => table.SFormat("\"{0}\"", table); @@ -68,7 +71,7 @@ public override string CreateTable(SqlTable table) dataType = DbTypeToString(c.Type, c.Length); } - return "{0} {1} {2} {3} {4}".SFormat(c.Name, + return "{0} {1} {2} {3} {4}".SFormat(EscapeColumnName(c.Name), dataType, c.Primary ? "PRIMARY KEY" : "", c.NotNull && !c.AutoIncrement ? "NOT NULL" : "", // SERIAL implies NOT NULL @@ -76,7 +79,7 @@ public override string CreateTable(SqlTable table) }); string[] uniques = table.Columns - .Where(c => c.Unique).Select(c => c.Name) + .Where(c => c.Unique).Select(c => EscapeColumnName(c.Name)) .ToArray(); // No re-enumeration return $"CREATE TABLE {EscapeTableName(table.Name)} ({string.Join(", ", columns)} {(uniques.Any() ? ", UNIQUE({0})".SFormat(string.Join(", ", uniques)) : "")})"; diff --git a/TShockAPI/DB/Queries/SqliteQueryBuilder.cs b/TShockAPI/DB/Queries/SqliteQueryBuilder.cs index 72de17d51..2ef47c743 100644 --- a/TShockAPI/DB/Queries/SqliteQueryBuilder.cs +++ b/TShockAPI/DB/Queries/SqliteQueryBuilder.cs @@ -1,4 +1,4 @@ -/* +/* TShock, a server mod for Terraria Copyright (C) 2011-2025 Pryaxis & TShock Contributors @@ -94,6 +94,9 @@ public override string DbTypeToString(MySqlDbType type, int? length) throw new NotImplementedException(Enum.GetName(typeof(MySqlDbType), type)); } + /// + protected override string EscapeColumnName(string column) => $"\"{column.Replace("\"", "\"\"")}\""; + /// /// Escapes the table name /// diff --git a/TShockAPI/DB/RegionManager.cs b/TShockAPI/DB/RegionManager.cs index d657980ac..80359d77d 100644 --- a/TShockAPI/DB/RegionManager.cs +++ b/TShockAPI/DB/RegionManager.cs @@ -39,6 +39,13 @@ public class RegionManager private IDbConnection database; + private string GroupsColumnName => database.GetSqlType() switch + { + SqlType.Mysql => "`Groups`", + SqlType.Postgres => "\"groups\"", + _ => "Groups" + }; + internal RegionManager(IDbConnection db) { database = db; @@ -136,7 +143,7 @@ public bool AddRegion(int tx, int ty, int width, int height, string regionname, try { database.Query( - "INSERT INTO Regions (X1, Y1, width, height, RegionName, WorldID, UserIds, Protected, `Groups`, Owner, Z) VALUES (@0, @1, @2, @3, @4, @5, @6, @7, @8, @9, @10);", + $"INSERT INTO Regions (X1, Y1, width, height, RegionName, WorldID, UserIds, Protected, {GroupsColumnName}, Owner, Z) VALUES (@0, @1, @2, @3, @4, @5, @6, @7, @8, @9, @10);", tx, ty, width, height, regionname, worldid, "", 1, "", owner, z); int id; using (QueryResult res = database.QueryReader("SELECT Id FROM Regions WHERE RegionName = @0 AND WorldID = @1", regionname, worldid)) @@ -583,7 +590,7 @@ public bool AllowGroup(string regionName, string groupName) { string mergedGroups = ""; using ( - var reader = database.QueryReader("SELECT `Groups` FROM Regions WHERE RegionName=@0 AND WorldID=@1", regionName, + var reader = database.QueryReader($"SELECT {GroupsColumnName} FROM Regions WHERE RegionName=@0 AND WorldID=@1", regionName, Main.worldID.ToString())) { if (reader.Read()) @@ -599,7 +606,7 @@ public bool AllowGroup(string regionName, string groupName) mergedGroups += ","; mergedGroups += groupName; - int q = database.Query("UPDATE Regions SET `Groups`=@0 WHERE RegionName=@1 AND WorldID=@2", mergedGroups, + int q = database.Query($"UPDATE Regions SET {GroupsColumnName}=@0 WHERE RegionName=@1 AND WorldID=@2", mergedGroups, regionName, Main.worldID.ToString()); Region r = GetRegionByName(regionName); @@ -628,7 +635,7 @@ public bool RemoveGroup(string regionName, string group) { r.RemoveGroup(group); string groups = string.Join(",", r.AllowedGroups); - int q = database.Query("UPDATE Regions SET `Groups`=@0 WHERE RegionName=@1 AND WorldID=@2", groups, + int q = database.Query($"UPDATE Regions SET {GroupsColumnName}=@0 WHERE RegionName=@1 AND WorldID=@2", groups, regionName, Main.worldID.ToString()); if (q > 0) return true;