diff --git a/TShockAPI/DB/BanManager.cs b/TShockAPI/DB/BanManager.cs index 2dfb6322..c56e3d0d 100644 --- a/TShockAPI/DB/BanManager.cs +++ b/TShockAPI/DB/BanManager.cs @@ -78,7 +78,7 @@ namespace TShockAPI.DB } catch (DllNotFoundException) { - Console.WriteLine(GetString("Possible problem with your database - is Sqlite3.dll present?")); + System.Console.WriteLine(GetString("Possible problem with your database - is Sqlite3.dll present?")); throw new Exception(GetString("Could not find a database library (probably Sqlite3.dll)")); } @@ -355,9 +355,7 @@ namespace TShockAPI.DB return Bans[id]; } - string query = $"SELECT * FROM PlayerBans WHERE {"TicketNumber".EscapeSqlId(database)}=@0"; - - using var reader = database.QueryReader(query, id); + using var reader = database.QueryReader("SELECT * FROM PlayerBans WHERE TicketNumber=@0", id); if (reader.Read()) { @@ -382,11 +380,10 @@ namespace TShockAPI.DB /// public IEnumerable RetrieveBansByIdentifier(string identifier, bool currentOnly = true) { - string query = $"SELECT * FROM PlayerBans WHERE {"Identifier".EscapeSqlId(database)}=@0"; - + string query = "SELECT * FROM PlayerBans WHERE Identifier=@0"; if (currentOnly) { - query += $" AND {"Expiration".EscapeSqlId(database)} > {DateTime.UtcNow.Ticks}"; + query += $" AND Expiration > {DateTime.UtcNow.Ticks}"; } using var reader = database.QueryReader(query, identifier); @@ -415,11 +412,11 @@ namespace TShockAPI.DB //Generate a sequence of '@0, @1, @2, ... etc' var parameters = string.Join(", ", Enumerable.Range(0, identifiers.Length).Select(p => $"@{p}")); - string query = $"SELECT * FROM PlayerBans WHERE {"Identifier".EscapeSqlId(database)} IN ({parameters})"; + string query = $"SELECT * FROM PlayerBans WHERE Identifier IN ({parameters})"; if (currentOnly) { - query += $" AND {"Expiration".EscapeSqlId(database)} > {DateTime.UtcNow.Ticks}"; + query += $" AND Expiration > {DateTime.UtcNow.Ticks}"; } using var reader = database.QueryReader(query, identifiers); @@ -452,7 +449,7 @@ namespace TShockAPI.DB List banlist = new List(); try { - using var reader = database.QueryReader($"SELECT * FROM PlayerBans ORDER BY {SortToOrderByMap(sortMethod)}"); + using var reader = database.QueryReader($"SELECT * FROM PlayerBans ORDER BY {SortToOrderByMap[sortMethod]}"); while (reader.Read()) { @@ -493,12 +490,12 @@ namespace TShockAPI.DB return false; } - private string SortToOrderByMap(BanSortMethod sortMethod) => sortMethod switch + private readonly Dictionary SortToOrderByMap = new() { - BanSortMethod.AddedNewestToOldest => $"{"Date".EscapeSqlId(database)} DESC", - BanSortMethod.AddedOldestToNewest => $"{"Date".EscapeSqlId(database)} ASC", - BanSortMethod.ExpirationSoonestToLatest => $"{"Expiration".EscapeSqlId(database)} ASC", - BanSortMethod.ExpirationLatestToSoonest => $"{"Expiration".EscapeSqlId(database)} DESC" + { BanSortMethod.AddedNewestToOldest, "Date DESC" }, + { BanSortMethod.AddedOldestToNewest, "Date ASC" }, + { BanSortMethod.ExpirationSoonestToLatest, "Expiration ASC" }, + { BanSortMethod.ExpirationLatestToSoonest, "Expiration DESC" } }; } diff --git a/TShockAPI/DB/CharacterManager.cs b/TShockAPI/DB/CharacterManager.cs index 08d6d1ec..3b9890a3 100644 --- a/TShockAPI/DB/CharacterManager.cs +++ b/TShockAPI/DB/CharacterManager.cs @@ -82,7 +82,7 @@ namespace TShockAPI.DB try { - using var reader = database.QueryReader($"SELECT * FROM tsCharacter WHERE {"Account".EscapeSqlId(database)}=@0", acctid); + using var reader = database.QueryReader("SELECT * FROM tsCharacter WHERE Account=@0", acctid); if (reader.Read()) { playerData.exists = true; diff --git a/TShockAPI/DB/GroupManager.cs b/TShockAPI/DB/GroupManager.cs index f655b987..060b4606 100644 --- a/TShockAPI/DB/GroupManager.cs +++ b/TShockAPI/DB/GroupManager.cs @@ -253,7 +253,13 @@ namespace TShockAPI.DB /// /// The group. /// true if it does; otherwise, false. - public bool GroupExists(string group) => group is "superadmin" || groups.Any(g => g.Name.Equals(group)); + public bool GroupExists(string group) + { + if (group == "superadmin") + return true; + + return groups.Any(g => g.Name.Equals(group)); + } IEnumerator IEnumerable.GetEnumerator() { @@ -264,14 +270,21 @@ namespace TShockAPI.DB /// Gets the enumerator. /// /// The enumerator. - public IEnumerator GetEnumerator() => groups.GetEnumerator(); + public IEnumerator GetEnumerator() + { + return groups.GetEnumerator(); + } /// /// Gets the group matching the specified name. /// /// The name. /// The group. - public Group GetGroupByName(string name) => groups.FirstOrDefault(g => g.Name == name); + public Group GetGroupByName(string name) + { + var ret = groups.Where(g => g.Name == name); + return 1 == ret.Count() ? ret.ElementAt(0) : null; + } /// /// Adds group with name and permissions if it does not exist. @@ -305,7 +318,7 @@ namespace TShockAPI.DB { SqlType.Sqlite => "INSERT OR IGNORE INTO GroupList (GroupName, Parent, Commands, ChatColor) VALUES (@0, @1, @2, @3);", SqlType.Mysql => "INSERT IGNORE INTO GroupList SET GroupName=@0, Parent=@1, Commands=@2, ChatColor=@3", - SqlType.Postgres => "INSERT INTO GroupList (\"GroupName\", \"Parent\", \"Commands\", \"ChatColor\") VALUES (@0, @1, @2, @3) ON CONFLICT (\"GroupName\") DO NOTHING", + SqlType.Postgres => "INSERT INTO GroupList (GroupName, Parent, Commands, ChatColor) VALUES (@0, @1, @2, @3) ON CONFLICT (GroupName) DO NOTHING", _ => throw new NotSupportedException(GetString("Unsupported database type.")) }; @@ -356,12 +369,9 @@ namespace TShockAPI.DB } // Ensure any group validation is also persisted to the DB. - var newGroup = new Group(name, parent, chatcolor, permissions) - { - Prefix = prefix, - Suffix = suffix - }; - + var newGroup = new Group(name, parent, chatcolor, permissions); + newGroup.Prefix = prefix; + newGroup.Suffix = suffix; string query = "UPDATE GroupList SET Parent=@0, Commands=@1, ChatColor=@2, Suffix=@3, Prefix=@4 WHERE GroupName=@5"; if (database.Query(query, parentname, newGroup.Permissions, newGroup.ChatColor, suffix, prefix, name) != 1) throw new GroupManagerException(GetString($"Failed to update group \"{name}\".")); diff --git a/TShockAPI/DB/Queries/PostgresQueryCreator.cs b/TShockAPI/DB/Queries/PostgresQueryCreator.cs index 5637d689..c12e2ec8 100644 --- a/TShockAPI/DB/Queries/PostgresQueryCreator.cs +++ b/TShockAPI/DB/Queries/PostgresQueryCreator.cs @@ -68,7 +68,7 @@ public class PostgresQueryCreator : GenericQueryCreator dataType = DbTypeToString(c.Type, c.Length); } - return "\"{0}\" {1} {2} {3} {4}".SFormat(c.Name, + return "{0} {1} {2} {3} {4}".SFormat(c.Name, dataType, c.Primary ? "PRIMARY KEY" : "", c.NotNull && !c.AutoIncrement ? "NOT NULL" : "", // SERIAL implies NOT NULL @@ -76,12 +76,12 @@ public class PostgresQueryCreator : GenericQueryCreator }); string[] uniques = table.Columns - .Where(c => c.Unique).Select(c => $"\"{c.Name}\"") + .Where(c => c.Unique).Select(c => c.Name) .ToArray(); // No re-enumeration return $"CREATE TABLE {EscapeTableName(table.Name)} ({string.Join(", ", columns)} {(uniques.Any() ? ", UNIQUE({0})".SFormat(string.Join(", ", uniques)) : "")})"; } /// - public override string RenameTable(string from, string to) => /*lang=postgresql*/"ALTER TABLE {0} RENAME TO {1}".SFormat(from, to); + public override string RenameTable(string from, string to) => "ALTER TABLE {0} RENAME TO {1}".SFormat(from, to); } diff --git a/TShockAPI/DB/RegionManager.cs b/TShockAPI/DB/RegionManager.cs index 27acd7a6..d657980a 100644 --- a/TShockAPI/DB/RegionManager.cs +++ b/TShockAPI/DB/RegionManager.cs @@ -67,9 +67,9 @@ namespace TShockAPI.DB { try { - using var reader = database.QueryReader($"SELECT * FROM Regions WHERE {"WorldID".EscapeSqlId(database)}=@0", Main.worldID.ToString()); - Regions.Clear(); + using var reader = database.QueryReader("SELECT * FROM Regions WHERE WorldID=@0", Main.worldID.ToString()); + Regions.Clear(); while (reader.Read()) { int id = reader.Get("Id"); @@ -135,19 +135,11 @@ namespace TShockAPI.DB } try { - string query = database.GetSqlType() switch - { - SqlType.Postgres => "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, Groups, Owner, Z) VALUES (@0, @1, @2, @3, @4, @5, @6, @7, @8, @9, @10);", - - }; - - database.Query(query, tx, ty, width, height, regionname, worldid, "", 1, "", owner, z); - + 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);", + tx, ty, width, height, regionname, worldid, "", 1, "", owner, z); int id; - - using (QueryResult res = database.QueryReader( - $"SELECT {"Id".EscapeSqlId(database)} FROM Regions WHERE {"RegionName".EscapeSqlId(database)} = @0 AND {"WorldID".EscapeSqlId(database)} = @1", regionname, worldid)) + using (QueryResult res = database.QueryReader("SELECT Id FROM Regions WHERE RegionName = @0 AND WorldID = @1", regionname, worldid)) { if (res.Read()) { @@ -158,7 +150,6 @@ namespace TShockAPI.DB return false; } } - Region region = new Region(id, new Rectangle(tx, ty, width, height), regionname, owner, true, worldid, z); Regions.Add(region); Hooks.RegionHooks.OnRegionCreated(region); @@ -180,7 +171,7 @@ namespace TShockAPI.DB { try { - database.Query($"DELETE FROM Regions WHERE Id=@0 AND {"WorldID".EscapeSqlId(database)}=@1", id, Main.worldID.ToString()); + database.Query("DELETE FROM Regions WHERE Id=@0 AND WorldID=@1", id, Main.worldID.ToString()); var worldid = Main.worldID.ToString(); var region = Regions.FirstOrDefault(r => r.ID == id && r.WorldID == worldid); Regions.RemoveAll(r => r.ID == id && r.WorldID == worldid); @@ -203,7 +194,7 @@ namespace TShockAPI.DB { try { - database.Query($"DELETE FROM Regions WHERE {"RegionName".EscapeSqlId(database)}=@0 AND {"WorldID".EscapeSqlId(database)}=@1", name, Main.worldID.ToString()); + database.Query("DELETE FROM Regions WHERE RegionName=@0 AND WorldID=@1", name, Main.worldID.ToString()); var worldid = Main.worldID.ToString(); var region = Regions.FirstOrDefault(r => r.Name == name && r.WorldID == worldid); Regions.RemoveAll(r => r.Name == name && r.WorldID == worldid); @@ -253,7 +244,7 @@ namespace TShockAPI.DB { try { - database.Query($"UPDATE Regions SET {"Protected".EscapeSqlId(database)}=@0 WHERE {"RegionName".EscapeSqlId(database)}=@1 AND {"WorldID".EscapeSqlId(database)}=@2", state ? 1 : 0, name, + database.Query("UPDATE Regions SET Protected=@0 WHERE RegionName=@1 AND WorldID=@2", state ? 1 : 0, name, Main.worldID.ToString()); var region = GetRegionByName(name); if (region != null) diff --git a/TShockAPI/DB/UserManager.cs b/TShockAPI/DB/UserManager.cs index e5077ce1..e705e3fc 100644 --- a/TShockAPI/DB/UserManager.cs +++ b/TShockAPI/DB/UserManager.cs @@ -70,22 +70,15 @@ namespace TShockAPI.DB int ret; try { - string query = _database.GetSqlType() switch - { - SqlType.Postgres => "INSERT INTO Users (\"Username\", \"Password\", \"UUID\", \"Usergroup\", \"Registered\") VALUES (@0, @1, @2, @3, @4);", - _ => "INSERT INTO Users (Username, Password, UUID, Usergroup, Registered) VALUES (@0, @1, @2, @3, @4);" - }; - - ret = _database.Query(query, account.Name, account.Password, account.UUID, account.Group, DateTime.UtcNow.ToString("s")); + ret = _database.Query("INSERT INTO Users (Username, Password, UUID, UserGroup, Registered) VALUES (@0, @1, @2, @3, @4);", account.Name, + account.Password, account.UUID, account.Group, DateTime.UtcNow.ToString("s")); } - // Detect duplicate user using a regexp as Sqlite doesn't have well structured exceptions - catch (Exception e) when (Regex.IsMatch(e.Message, "Username.*not unique|UNIQUE constraint failed: Users\\.Username")) + catch (Exception ex) { - throw new UserAccountExistsException(account.Name); - } - catch (Exception e) - { - throw new UserAccountManagerException(GetString($"AddUser SQL returned an error ({e.Message})"), e); + // Detect duplicate user using a regexp as Sqlite doesn't have well structured exceptions + if (Regex.IsMatch(ex.Message, "Username.*not unique|UNIQUE constraint failed: Users\\.Username")) + throw new UserAccountExistsException(account.Name); + throw new UserAccountManagerException(GetString($"AddUser SQL returned an error ({ex.Message})"), ex); } if (1 > ret) @@ -106,7 +99,7 @@ namespace TShockAPI.DB TShock.Players.Where(p => p?.IsLoggedIn == true && p.Account.Name == account.Name).ForEach(p => p.Logout()); UserAccount tempuser = GetUserAccount(account); - int affected = _database.Query($"DELETE FROM Users WHERE {"Username".EscapeSqlId(_database)}=@0", account.Name); + int affected = _database.Query("DELETE FROM Users WHERE Username=@0", account.Name); if (affected < 1) throw new UserAccountNotExistException(account.Name); @@ -131,11 +124,10 @@ namespace TShockAPI.DB { account.CreateBCryptHash(password); - if (_database.Query($"UPDATE Users SET {"Password".EscapeSqlId(_database)} = @0 WHERE {"Username".EscapeSqlId(_database)} = @1;", - account.Password, account.Name) is 0) - { + if ( + _database.Query("UPDATE Users SET Password = @0 WHERE Username = @1;", account.Password, + account.Name) == 0) throw new UserAccountNotExistException(account.Name); - } } catch (Exception ex) { @@ -152,10 +144,10 @@ namespace TShockAPI.DB { try { - if (_database.Query(/*lang=sql*/$"UPDATE Users SET {"UUID".EscapeSqlId(_database)} = @0 WHERE {"Username".EscapeSqlId(_database)} = @1;", uuid, account.Name) is 0) - { + if ( + _database.Query("UPDATE Users SET UUID = @0 WHERE Username = @1;", uuid, + account.Name) == 0) throw new UserAccountNotExistException(account.Name); - } } catch (Exception ex) { @@ -177,7 +169,7 @@ namespace TShockAPI.DB if (AccountHooks.OnAccountGroupUpdate(account, ref grp)) throw new UserGroupUpdateLockedException(account.Name); - if (_database.Query($"UPDATE Users SET {"UserGroup".EscapeSqlId(_database)} = @0 WHERE {"Username".EscapeSqlId(_database)} = @1;", grp.Name, account.Name) == 0) + if (_database.Query("UPDATE Users SET UserGroup = @0 WHERE Username = @1;", grp.Name, account.Name) == 0) throw new UserAccountNotExistException(account.Name); try @@ -208,7 +200,7 @@ namespace TShockAPI.DB if (AccountHooks.OnAccountGroupUpdate(account, author, ref grp)) throw new UserGroupUpdateLockedException(account.Name); - if (_database.Query($"UPDATE Users SET {"UserGroup".EscapeSqlId(_database)} = @0 WHERE {"Username".EscapeSqlId(_database)} = @1;", grp.Name, account.Name) == 0) + if (_database.Query("UPDATE Users SET UserGroup = @0 WHERE Username = @1;", grp.Name, account.Name) == 0) throw new UserAccountNotExistException(account.Name); try @@ -231,12 +223,8 @@ namespace TShockAPI.DB { try { - if (_database.Query( - $"UPDATE Users SET {"LastAccessed".EscapeSqlId(_database)} = @0, {"KnownIPs".EscapeSqlId(_database)} = @1 WHERE {"Username".EscapeSqlId(_database)} = @2;", - DateTime.UtcNow.ToString("s"), account.KnownIps, account.Name) is 0 - ) { + if (_database.Query("UPDATE Users SET LastAccessed = @0, KnownIps = @1 WHERE Username = @2;", DateTime.UtcNow.ToString("s"), account.KnownIps, account.Name) == 0) throw new UserAccountNotExistException(account.Name); - } } catch (Exception ex) { @@ -251,7 +239,7 @@ namespace TShockAPI.DB { try { - using var reader = _database.QueryReader($"SELECT * FROM Users WHERE {"Username".EscapeSqlId(_database)}=@0", username); + using var reader = _database.QueryReader("SELECT * FROM Users WHERE Username=@0", username); if (reader.Read()) { return reader.Get("ID"); @@ -305,13 +293,13 @@ namespace TShockAPI.DB object arg; if (account.ID != 0) { - query = $"SELECT * FROM Users WHERE {"ID".EscapeSqlId(_database)}=@0"; + query = "SELECT * FROM Users WHERE ID=@0"; arg = account.ID; type = "id"; } else { - query = $"SELECT * FROM Users WHERE {"Username".EscapeSqlId(_database)}=@0"; + query = "SELECT * FROM Users WHERE Username=@0"; arg = account.Name; type = "name"; } @@ -370,9 +358,9 @@ namespace TShockAPI.DB try { List accounts = new List(); - string search = $"{(notAtStart ? "%" : "")}{username}%"; - using var reader = _database.QueryReader($"SELECT * FROM Users WHERE {"Username".EscapeSqlId(_database)} LIKE @0", search); - + string search = notAtStart ? string.Format("%{0}%", username) : string.Format("{0}%", username); + using var reader = _database.QueryReader("SELECT * FROM Users WHERE Username LIKE @0", + search); while (reader.Read()) { accounts.Add(LoadUserAccountFromResult(new UserAccount(), reader)); @@ -400,7 +388,7 @@ namespace TShockAPI.DB account.Name = result.Get("Username"); account.Registered = result.Get("Registered"); account.LastAccessed = result.Get("LastAccessed"); - account.KnownIps = result.Get("KnownIPs"); + account.KnownIps = result.Get("KnownIps"); return account; } } diff --git a/TShockAPI/DB/WarpsManager.cs b/TShockAPI/DB/WarpsManager.cs index 0bd49d0a..06723281 100644 --- a/TShockAPI/DB/WarpsManager.cs +++ b/TShockAPI/DB/WarpsManager.cs @@ -65,13 +65,8 @@ namespace TShockAPI.DB { try { - string query = database.GetSqlType() switch - { - SqlType.Postgres => "INSERT INTO Warps (\"X\", \"Y\", \"WarpName\", \"WorldID\") VALUES (@0, @1, @2, @3);", - _ => "INSERT INTO Warps (X, Y, WarpName, WorldID) VALUES (@0, @1, @2, @3);" - }; - - if (database.Query(query, x, y, name, Main.worldID.ToString()) > 0) + if (database.Query("INSERT INTO Warps (X, Y, WarpName, WorldID) VALUES (@0, @1, @2, @3);", + x, y, name, Main.worldID.ToString()) > 0) { Warps.Add(new Warp(new Point(x, y), name)); return true; @@ -81,7 +76,6 @@ namespace TShockAPI.DB { TShock.Log.Error(ex.ToString()); } - return false; } @@ -92,7 +86,7 @@ namespace TShockAPI.DB { Warps.Clear(); - using var reader = database.QueryReader($"SELECT * FROM Warps WHERE {"WorldID".EscapeSqlId(database)} = @0", + using var reader = database.QueryReader("SELECT * FROM Warps WHERE WorldID = @0", Main.worldID.ToString()); while (reader.Read()) { @@ -112,7 +106,7 @@ namespace TShockAPI.DB { try { - if (database.Query($"DELETE FROM Warps WHERE {"WarpName".EscapeSqlId(database)} = @0 AND {"WorldID".EscapeSqlId(database)} = @1", + if (database.Query("DELETE FROM Warps WHERE WarpName = @0 AND WorldID = @1", warpName, Main.worldID.ToString()) > 0) { Warps.RemoveAll(w => string.Equals(w.Name, warpName, StringComparison.OrdinalIgnoreCase)); @@ -147,7 +141,7 @@ namespace TShockAPI.DB { try { - if (database.Query($"UPDATE Warps SET X = @0, Y = @1 WHERE {"WarpName".EscapeSqlId(database)} = @2 AND WorldID = @3", + if (database.Query("UPDATE Warps SET X = @0, Y = @1 WHERE WarpName = @2 AND WorldID = @3", x, y, warpName, Main.worldID.ToString()) > 0) { Warps.Find(w => string.Equals(w.Name, warpName, StringComparison.OrdinalIgnoreCase)).Position = new Point(x, y); @@ -171,7 +165,7 @@ namespace TShockAPI.DB { try { - if (database.Query($"UPDATE Warps SET {"Private".EscapeSqlId(database)} = @0 WHERE {"WarpName".EscapeSqlId(database)} = @1 AND {"WorldID".EscapeSqlId(database)} = @2", + if (database.Query("UPDATE Warps SET Private = @0 WHERE WarpName = @1 AND WorldID = @2", state ? "1" : "0", warpName, Main.worldID.ToString()) > 0) { Warps.Find(w => string.Equals(w.Name, warpName, StringComparison.OrdinalIgnoreCase)).IsPrivate = state; diff --git a/TShockAPI/Extensions/DbExt.cs b/TShockAPI/Extensions/DbExt.cs index 55a681dd..ba2cfd1f 100644 --- a/TShockAPI/Extensions/DbExt.cs +++ b/TShockAPI/Extensions/DbExt.cs @@ -20,7 +20,6 @@ using System; using System.Collections.Generic; using System.Data; using System.Diagnostics.CodeAnalysis; -using System.Diagnostics.Contracts; using Microsoft.Data.Sqlite; using MySql.Data.MySqlClient; using Npgsql; @@ -43,14 +42,15 @@ namespace TShockAPI.DB [SuppressMessage("Microsoft.Security", "CA2100:Review SQL queries for security vulnerabilities")] public static int Query(this IDbConnection olddb, string query, params object[] args) { - using IDbConnection db = olddb.CloneEx(); + using var db = olddb.CloneEx(); db.Open(); - using IDbCommand com = db.CreateCommand(); + + using var com = db.CreateCommand(); com.CommandText = query; for (int i = 0; i < args.Length; i++) { - com.AddParameter("@" + i, args[i] ?? DBNull.Value); + com.AddParameter($"@{i}", args[i] ?? DBNull.Value); } return com.ExecuteNonQuery(); @@ -272,18 +272,6 @@ namespace TShockAPI.DB return (T)reader.GetValue(column); } - - /// - /// Escapes an identifier for use in a SQL query. - /// - /// The identifier to escape, typically a table or column name. - /// The escaped identifier. - [Pure] - public static string EscapeSqlId(this string id, IDbConnection db) => db.GetSqlType() switch - { - SqlType.Postgres => $"\"{id}\"", // The main PITA and culprit - _ => id // Default case for agnostic SQL - }; } public enum SqlType