diff --git a/TShockAPI/DB/Queries/PostgresQueryCreator.cs b/TShockAPI/DB/Queries/PostgresQueryCreator.cs
new file mode 100644
index 00000000..779e38b0
--- /dev/null
+++ b/TShockAPI/DB/Queries/PostgresQueryCreator.cs
@@ -0,0 +1,89 @@
+/*
+TShock, a server mod for Terraria
+Copyright (C) 2011-2019 Pryaxis & TShock Contributors
+
+This program is free software: you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation, either version 3 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program. If not, see .
+*/
+
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using MySql.Data.MySqlClient;
+
+namespace TShockAPI.DB.Queries;
+
+///
+/// Query Creator for PostgreSQL
+///
+public class PostgresQueryCreator : GenericQueryCreator
+{
+ ///
+ public override string DbTypeToString(MySqlDbType type, int? length) => type switch
+ {
+ MySqlDbType.VarChar when length is not null => "VARCHAR({0})".SFormat(length),
+ MySqlDbType.String when length is not null => "CHAR({0})".SFormat(length),
+ MySqlDbType.Text => "TEXT",
+ MySqlDbType.TinyText => "TEXT",
+ MySqlDbType.MediumText => "TEXT",
+ MySqlDbType.LongText => "TEXT",
+ MySqlDbType.Float => "REAL",
+ MySqlDbType.Double => "DOUBLE PRECISION",
+ MySqlDbType.Int32 => "INT",
+ MySqlDbType.Int64 => "BIGINT",
+ MySqlDbType.DateTime => "TIMESTAMP",
+
+ _ => throw new NotImplementedException(Enum.GetName(typeof(MySqlDbType), type))
+ };
+
+ ///
+ protected override string EscapeTableName(string table) => table.SFormat("\"{0}\"", table);
+
+ ///
+ public override string CreateTable(SqlTable table)
+ {
+ ValidateSqlColumnType(table.Columns);
+
+ IEnumerable columns = table.Columns.Select(c =>
+ {
+ // Handle PostgreSQL-specific auto-increment using SERIAL/BIGSERIAL
+ string dataType;
+
+ if (c.AutoIncrement)
+ {
+ dataType = c.Type is MySqlDbType.Int32 ? "SERIAL" : "BIGSERIAL";
+ }
+ else
+ {
+ dataType = DbTypeToString(c.Type, c.Length);
+ }
+
+ return "\"{0}\" {1} {2} {3} {4}".SFormat(c.Name,
+ dataType,
+ c.Primary ? "PRIMARY KEY" : "",
+ c.NotNull && !c.AutoIncrement ? "NOT NULL" : "", // SERIAL implies NOT NULL
+ c.DefaultCurrentTimestamp ? "DEFAULT CURRENT_TIMESTAMP" : "");
+ });
+
+ string[] uniques = table.Columns
+ .Where(c => c.Unique).Select(c => $"\"{c.Name}\"")
+ .ToArray(); // No re-enumeration
+
+ return "CREATE TABLE {0} ({1} {2})".SFormat(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);
+}
diff --git a/TShockAPI/SqlLog.cs b/TShockAPI/SqlLog.cs
index 3e79aaab..2e5a55e3 100644
--- a/TShockAPI/SqlLog.cs
+++ b/TShockAPI/SqlLog.cs
@@ -24,6 +24,7 @@ using System.Globalization;
using System.Linq;
using MySql.Data.MySqlClient;
using TShockAPI.DB;
+using TShockAPI.DB.Queries;
namespace TShockAPI
{
diff --git a/TShockAPI/TShockAPI.csproj b/TShockAPI/TShockAPI.csproj
index a01bb022..a152bd03 100644
--- a/TShockAPI/TShockAPI.csproj
+++ b/TShockAPI/TShockAPI.csproj
@@ -36,6 +36,7 @@
+