This commit is contained in:
moemoequte 2025-09-30 23:33:39 +00:00 committed by GitHub
commit 3c228f3ad8
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
5 changed files with 881 additions and 128 deletions

View File

@ -140,6 +140,12 @@ public void SQL_CreateTables(Database hSQL, const char[] prefix, int driver)
"CREATE TABLE IF NOT EXISTS `%susers` (`auth` INT NOT NULL, `name` VARCHAR(32) COLLATE 'utf8mb4_general_ci', `ip` INT, `lastlogin` INT NOT NULL DEFAULT -1, `firstlogin` INT NOT NULL DEFAULT -1, `points` FLOAT NOT NULL DEFAULT 0, `playtime` FLOAT NOT NULL DEFAULT 0, PRIMARY KEY (`auth`), INDEX `points` (`points`), INDEX `lastlogin` (`lastlogin`)) ENGINE=INNODB;",
gS_SQLPrefix);
}
else if (driver == Driver_pgsql)
{
FormatEx(sQuery, sizeof(sQuery),
"CREATE TABLE IF NOT EXISTS %susers (auth INT NOT NULL PRIMARY KEY, name VARCHAR(32), ip INT, lastlogin INT NOT NULL DEFAULT -1, firstlogin INT NOT NULL DEFAULT -1, points REAL NOT NULL DEFAULT 0, playtime REAL NOT NULL DEFAULT 0);",
gS_SQLPrefix);
}
else
{
FormatEx(sQuery, sizeof(sQuery),
@ -149,9 +155,24 @@ public void SQL_CreateTables(Database hSQL, const char[] prefix, int driver)
AddQueryLog(trans, sQuery);
if (driver == Driver_mysql)
{
FormatEx(sQuery, sizeof(sQuery),
"CREATE TABLE IF NOT EXISTS `%smigrations` (`code` TINYINT NOT NULL, PRIMARY KEY (`code`));",
gS_SQLPrefix);
}
else if (driver == Driver_pgsql)
{
FormatEx(sQuery, sizeof(sQuery),
"CREATE TABLE IF NOT EXISTS %smigrations (code SMALLINT NOT NULL, PRIMARY KEY (code));",
gS_SQLPrefix);
}
else
{
FormatEx(sQuery, sizeof(sQuery),
"CREATE TABLE IF NOT EXISTS `%smigrations` (`code` TINYINT NOT NULL, PRIMARY KEY (`code`));",
gS_SQLPrefix);
}
AddQueryLog(trans, sQuery);
//
@ -164,6 +185,12 @@ public void SQL_CreateTables(Database hSQL, const char[] prefix, int driver)
"CREATE TABLE IF NOT EXISTS `%schat` (`auth` INT NOT NULL, `name` INT NOT NULL DEFAULT 0, `ccname` VARCHAR(128) COLLATE 'utf8mb4_unicode_ci', `message` INT NOT NULL DEFAULT 0, `ccmessage` VARCHAR(16) COLLATE 'utf8mb4_unicode_ci', `ccaccess` INT NOT NULL DEFAULT 0, PRIMARY KEY (`auth`), CONSTRAINT `%sch_auth` FOREIGN KEY (`auth`) REFERENCES `%susers` (`auth`) ON UPDATE CASCADE ON DELETE CASCADE) ENGINE=INNODB;",
gS_SQLPrefix, gS_SQLPrefix, gS_SQLPrefix);
}
else if (driver == Driver_pgsql)
{
FormatEx(sQuery, sizeof(sQuery),
"CREATE TABLE IF NOT EXISTS %schat (auth INT NOT NULL, name INT NOT NULL DEFAULT 0, ccname VARCHAR(128), message INT NOT NULL DEFAULT 0, ccmessage VARCHAR(16), ccaccess INT NOT NULL DEFAULT 0, PRIMARY KEY (auth), CONSTRAINT %sch_auth FOREIGN KEY (auth) REFERENCES %susers (auth) ON UPDATE CASCADE ON DELETE CASCADE);",
gS_SQLPrefix, gS_SQLPrefix, gS_SQLPrefix);
}
else
{
FormatEx(sQuery, sizeof(sQuery),
@ -177,18 +204,48 @@ public void SQL_CreateTables(Database hSQL, const char[] prefix, int driver)
//// shavit-rankings
//
if (driver == Driver_mysql)
{
FormatEx(sQuery, sizeof(sQuery),
"CREATE TABLE IF NOT EXISTS `%smaptiers` (`map` VARCHAR(255) NOT NULL, `tier` INT NOT NULL DEFAULT 1, PRIMARY KEY (`map`)) %s;",
gS_SQLPrefix, sOptionalINNODB);
}
else if (driver == Driver_pgsql)
{
FormatEx(sQuery, sizeof(sQuery),
"CREATE TABLE IF NOT EXISTS %smaptiers (map VARCHAR(255) NOT NULL, tier INT NOT NULL DEFAULT 1, PRIMARY KEY (map));",
gS_SQLPrefix);
}
else
{
FormatEx(sQuery, sizeof(sQuery),
"CREATE TABLE IF NOT EXISTS `%smaptiers` (`map` VARCHAR(255) NOT NULL, `tier` INT NOT NULL DEFAULT 1, PRIMARY KEY (`map`)) %s;",
gS_SQLPrefix, sOptionalINNODB);
}
AddQueryLog(trans, sQuery);
//
//// shavit-stats
//
if (driver == Driver_mysql)
{
FormatEx(sQuery, sizeof(sQuery),
"CREATE TABLE IF NOT EXISTS `%sstyleplaytime` (`auth` INT NOT NULL, `style` TINYINT NOT NULL, `playtime` FLOAT NOT NULL, PRIMARY KEY (`auth`, `style`));",
gS_SQLPrefix);
}
else if (driver == Driver_pgsql)
{
FormatEx(sQuery, sizeof(sQuery),
"CREATE TABLE IF NOT EXISTS %sstyleplaytime (auth INT NOT NULL, style SMALLINT NOT NULL, playtime REAL NOT NULL, PRIMARY KEY (auth, style));",
gS_SQLPrefix);
}
else
{
FormatEx(sQuery, sizeof(sQuery),
"CREATE TABLE IF NOT EXISTS `%sstyleplaytime` (`auth` INT NOT NULL, `style` TINYINT NOT NULL, `playtime` FLOAT NOT NULL, PRIMARY KEY (`auth`, `style`));",
gS_SQLPrefix);
}
AddQueryLog(trans, sQuery);
//
@ -201,6 +258,12 @@ public void SQL_CreateTables(Database hSQL, const char[] prefix, int driver)
"CREATE TABLE IF NOT EXISTS `%splayertimes` (`id` INT NOT NULL AUTO_INCREMENT, `style` TINYINT NOT NULL DEFAULT 0, `track` TINYINT NOT NULL DEFAULT 0, `time` FLOAT NOT NULL, `auth` INT NOT NULL, `map` VARCHAR(255) NOT NULL, `points` FLOAT NOT NULL DEFAULT 0, `jumps` INT, `date` INT, `strafes` INT, `sync` FLOAT, `perfs` FLOAT DEFAULT 0, `completions` SMALLINT DEFAULT 1, PRIMARY KEY (`id`), INDEX `map` (`map`, `style`, `track`, `time`), INDEX `auth` (`auth`, `date`, `points`), INDEX `time` (`time`), INDEX `map2` (`map`), CONSTRAINT `%spt_auth` FOREIGN KEY (`auth`) REFERENCES `%susers` (`auth`) ON UPDATE RESTRICT ON DELETE RESTRICT) ENGINE=INNODB;",
gS_SQLPrefix, gS_SQLPrefix, gS_SQLPrefix);
}
else if (driver == Driver_pgsql)
{
FormatEx(sQuery, sizeof(sQuery),
"CREATE TABLE IF NOT EXISTS %splayertimes (id SERIAL PRIMARY KEY, style SMALLINT NOT NULL DEFAULT 0, track SMALLINT NOT NULL DEFAULT 0, time REAL NOT NULL, auth INT NOT NULL, map VARCHAR(255) NOT NULL, points REAL NOT NULL DEFAULT 0, jumps INT, date INT, strafes INT, sync REAL, perfs REAL DEFAULT 0, completions SMALLINT DEFAULT 1, CONSTRAINT %spt_auth FOREIGN KEY (auth) REFERENCES %susers (auth) ON UPDATE RESTRICT ON DELETE RESTRICT);",
gS_SQLPrefix, gS_SQLPrefix, gS_SQLPrefix);
}
else
{
// id style track time auth map points exact_time_int
@ -212,14 +275,44 @@ public void SQL_CreateTables(Database hSQL, const char[] prefix, int driver)
AddQueryLog(trans, sQuery);
if (driver == Driver_mysql)
{
FormatEx(sQuery, sizeof(sQuery),
"CREATE TABLE IF NOT EXISTS `%sstagetimeswr` (`style` TINYINT NOT NULL, `track` TINYINT NOT NULL DEFAULT 0, `map` VARCHAR(255) NOT NULL, `stage` TINYINT NOT NULL, `auth` INT NOT NULL, `time` FLOAT NOT NULL, PRIMARY KEY (`style`, `track`, `map`, `stage`)) %s;",
gS_SQLPrefix, sOptionalINNODB);
}
else if (driver == Driver_pgsql)
{
FormatEx(sQuery, sizeof(sQuery),
"CREATE TABLE IF NOT EXISTS %sstagetimeswr (style SMALLINT NOT NULL, track SMALLINT NOT NULL DEFAULT 0, map VARCHAR(255) NOT NULL, stage SMALLINT NOT NULL, auth INT NOT NULL, time REAL NOT NULL, PRIMARY KEY (style, track, map, stage));",
gS_SQLPrefix);
}
else
{
FormatEx(sQuery, sizeof(sQuery),
"CREATE TABLE IF NOT EXISTS `%sstagetimeswr` (`style` TINYINT NOT NULL, `track` TINYINT NOT NULL DEFAULT 0, `map` VARCHAR(255) NOT NULL, `stage` TINYINT NOT NULL, `auth` INT NOT NULL, `time` FLOAT NOT NULL, PRIMARY KEY (`style`, `track`, `map`, `stage`)) %s;",
gS_SQLPrefix, sOptionalINNODB);
}
AddQueryLog(trans, sQuery);
if (driver == Driver_mysql)
{
FormatEx(sQuery, sizeof(sQuery),
"CREATE TABLE IF NOT EXISTS `%sstagetimespb` (`style` TINYINT NOT NULL, `track` TINYINT NOT NULL DEFAULT 0, `map` VARCHAR(255) NOT NULL, `stage` TINYINT NOT NULL, `auth` INT NOT NULL, `time` FLOAT NOT NULL, PRIMARY KEY (`style`, `track`, `auth`, `map`, `stage`)) %s;",
gS_SQLPrefix, sOptionalINNODB);
}
else if (driver == Driver_pgsql)
{
FormatEx(sQuery, sizeof(sQuery),
"CREATE TABLE IF NOT EXISTS %sstagetimespb (style SMALLINT NOT NULL, track SMALLINT NOT NULL DEFAULT 0, map VARCHAR(255) NOT NULL, stage SMALLINT NOT NULL, auth INT NOT NULL, time REAL NOT NULL, PRIMARY KEY (style, track, auth, map, stage));",
gS_SQLPrefix);
}
else
{
FormatEx(sQuery, sizeof(sQuery),
"CREATE TABLE IF NOT EXISTS `%sstagetimespb` (`style` TINYINT NOT NULL, `track` TINYINT NOT NULL DEFAULT 0, `map` VARCHAR(255) NOT NULL, `stage` TINYINT NOT NULL, `auth` INT NOT NULL, `time` FLOAT NOT NULL, PRIMARY KEY (`style`, `track`, `auth`, `map`, `stage`)) %s;",
gS_SQLPrefix, sOptionalINNODB);
}
AddQueryLog(trans, sQuery);
if (driver == Driver_sqlite)
@ -230,16 +323,44 @@ public void SQL_CreateTables(Database hSQL, const char[] prefix, int driver)
AddQueryLog(trans, sQuery);
}
if (driver == Driver_mysql)
{
FormatEx(sQuery, sizeof(sQuery),
"%s %swrs_min AS SELECT MIN(time) time, map, track, style FROM %splayertimes GROUP BY map, track, style;",
driver == Driver_sqlite ? "CREATE VIEW IF NOT EXISTS" : "CREATE OR REPLACE VIEW",
"CREATE OR REPLACE VIEW %swrs_min AS SELECT MIN(time) time, map, track, style FROM %splayertimes GROUP BY map, track, style;",
gS_SQLPrefix, gS_SQLPrefix);
}
else if (driver == Driver_pgsql)
{
FormatEx(sQuery, sizeof(sQuery),
"CREATE OR REPLACE VIEW %swrs_min AS SELECT MIN(time) time, map, track, style FROM %splayertimes GROUP BY map, track, style;",
gS_SQLPrefix, gS_SQLPrefix);
}
else
{
FormatEx(sQuery, sizeof(sQuery),
"CREATE VIEW IF NOT EXISTS %swrs_min AS SELECT MIN(time) time, map, track, style FROM %splayertimes GROUP BY map, track, style;",
gS_SQLPrefix, gS_SQLPrefix);
}
AddQueryLog(trans, sQuery);
if (driver == Driver_mysql)
{
FormatEx(sQuery, sizeof(sQuery),
"%s %swrs AS SELECT a.* FROM %splayertimes a JOIN %swrs_min b ON a.time = b.time AND a.map = b.map AND a.track = b.track AND a.style = b.style;",
driver == Driver_sqlite ? "CREATE VIEW IF NOT EXISTS" : "CREATE OR REPLACE VIEW",
"CREATE OR REPLACE VIEW %swrs AS SELECT a.* FROM %splayertimes a JOIN %swrs_min b ON a.time = b.time AND a.map = b.map AND a.track = b.track AND a.style = b.style;",
gS_SQLPrefix, gS_SQLPrefix, gS_SQLPrefix);
}
else if (driver == Driver_pgsql)
{
FormatEx(sQuery, sizeof(sQuery),
"CREATE OR REPLACE VIEW %swrs AS SELECT a.* FROM %splayertimes a JOIN %swrs_min b ON a.time = b.time AND a.map = b.map AND a.track = b.track AND a.style = b.style;",
gS_SQLPrefix, gS_SQLPrefix, gS_SQLPrefix);
}
else
{
FormatEx(sQuery, sizeof(sQuery),
"CREATE VIEW IF NOT EXISTS %swrs AS SELECT a.* FROM %splayertimes a JOIN %swrs_min b ON a.time = b.time AND a.map = b.map AND a.track = b.track AND a.style = b.style;",
gS_SQLPrefix, gS_SQLPrefix, gS_SQLPrefix);
}
AddQueryLog(trans, sQuery);
//
@ -253,6 +374,13 @@ public void SQL_CreateTables(Database hSQL, const char[] prefix, int driver)
gS_SQLPrefix, sOptionalINNODB);
AddQueryLog(trans, sQuery);
}
else if (driver == Driver_pgsql)
{
FormatEx(sQuery, sizeof(sQuery),
"CREATE TABLE IF NOT EXISTS %smapzones (id SERIAL PRIMARY KEY, map VARCHAR(255) NOT NULL, type INT, corner1_x REAL, corner1_y REAL, corner1_z REAL, corner2_x REAL, corner2_y REAL, corner2_z REAL, destination_x REAL NOT NULL DEFAULT 0, destination_y REAL NOT NULL DEFAULT 0, destination_z REAL NOT NULL DEFAULT 0, track INT NOT NULL DEFAULT 0, flags INT NOT NULL DEFAULT 0, data INT NOT NULL DEFAULT 0, form SMALLINT, target VARCHAR(63));",
gS_SQLPrefix);
AddQueryLog(trans, sQuery);
}
else
{
FormatEx(sQuery, sizeof(sQuery),
@ -262,11 +390,48 @@ public void SQL_CreateTables(Database hSQL, const char[] prefix, int driver)
strcopy(SQLiteMapzonesQuery, sizeof(SQLiteMapzonesQuery), sQuery);
}
if (driver == Driver_mysql)
{
FormatEx(sQuery, sizeof(sQuery),
"CREATE TABLE IF NOT EXISTS `%sstartpositions` (`auth` INTEGER NOT NULL, `track` TINYINT NOT NULL, `map` VARCHAR(255) NOT NULL, `pos_x` FLOAT, `pos_y` FLOAT, `pos_z` FLOAT, `ang_x` FLOAT, `ang_y` FLOAT, `ang_z` FLOAT, `angles_only` BOOL, PRIMARY KEY (`auth`, `track`, `map`)) %s;",
gS_SQLPrefix, sOptionalINNODB);
}
else if (driver == Driver_pgsql)
{
FormatEx(sQuery, sizeof(sQuery),
"CREATE TABLE IF NOT EXISTS %sstartpositions (auth INTEGER NOT NULL, track SMALLINT NOT NULL, map VARCHAR(255) NOT NULL, pos_x REAL, pos_y REAL, pos_z REAL, ang_x REAL, ang_y REAL, ang_z REAL, angles_only BOOLEAN, PRIMARY KEY (auth, track, map));",
gS_SQLPrefix);
}
else
{
FormatEx(sQuery, sizeof(sQuery),
"CREATE TABLE IF NOT EXISTS `%sstartpositions` (`auth` INTEGER NOT NULL, `track` TINYINT NOT NULL, `map` VARCHAR(255) NOT NULL, `pos_x` FLOAT, `pos_y` FLOAT, `pos_z` FLOAT, `ang_x` FLOAT, `ang_y` FLOAT, `ang_z` FLOAT, `angles_only` BOOL, PRIMARY KEY (`auth`, `track`, `map`)) %s;",
gS_SQLPrefix, sOptionalINNODB);
}
AddQueryLog(trans, sQuery);
// PostgreSQL indexes need to be created separately
if (driver == Driver_pgsql)
{
FormatEx(sQuery, sizeof(sQuery), "CREATE INDEX IF NOT EXISTS %susers_points_idx ON %susers (points);", gS_SQLPrefix, gS_SQLPrefix);
AddQueryLog(trans, sQuery);
FormatEx(sQuery, sizeof(sQuery), "CREATE INDEX IF NOT EXISTS %susers_lastlogin_idx ON %susers (lastlogin);", gS_SQLPrefix, gS_SQLPrefix);
AddQueryLog(trans, sQuery);
FormatEx(sQuery, sizeof(sQuery), "CREATE INDEX IF NOT EXISTS %splayertimes_map_idx ON %splayertimes (map, style, track, time);", gS_SQLPrefix, gS_SQLPrefix);
AddQueryLog(trans, sQuery);
FormatEx(sQuery, sizeof(sQuery), "CREATE INDEX IF NOT EXISTS %splayertimes_auth_idx ON %splayertimes (auth, date, points);", gS_SQLPrefix, gS_SQLPrefix);
AddQueryLog(trans, sQuery);
FormatEx(sQuery, sizeof(sQuery), "CREATE INDEX IF NOT EXISTS %splayertimes_time_idx ON %splayertimes (time);", gS_SQLPrefix, gS_SQLPrefix);
AddQueryLog(trans, sQuery);
FormatEx(sQuery, sizeof(sQuery), "CREATE INDEX IF NOT EXISTS %splayertimes_map2_idx ON %splayertimes (map);", gS_SQLPrefix, gS_SQLPrefix);
AddQueryLog(trans, sQuery);
}
hSQL.Execute(trans, Trans_CreateTables_Success, Trans_CreateTables_Error, 0, DBPrio_High);
}
@ -381,42 +546,108 @@ void ApplyMigration(int migration)
void ApplyMigration_LastLoginIndex()
{
char sQuery[128];
if (gI_Driver == Driver_mysql)
{
FormatEx(sQuery, 128, "ALTER TABLE `%susers` ADD INDEX `lastlogin` (`lastlogin`);", gS_SQLPrefix);
}
else if (gI_Driver == Driver_pgsql)
{
FormatEx(sQuery, 128, "CREATE INDEX lastlogin ON %susers (lastlogin);", gS_SQLPrefix);
}
else // SQLite
{
FormatEx(sQuery, 128, "CREATE INDEX lastlogin ON `%susers` (`lastlogin`);", gS_SQLPrefix);
}
QueryLog(gH_SQL, SQL_TableMigrationSingleQuery_Callback, sQuery, Migration_LastLoginIndex, DBPrio_High);
}
void ApplyMigration_RemoveCountry()
{
char sQuery[128];
if (gI_Driver == Driver_mysql)
{
FormatEx(sQuery, 128, "ALTER TABLE `%susers` DROP COLUMN `country`;", gS_SQLPrefix);
}
else if (gI_Driver == Driver_pgsql)
{
FormatEx(sQuery, 128, "ALTER TABLE %susers DROP COLUMN country;", gS_SQLPrefix);
}
else // SQLite
{
FormatEx(sQuery, 128, "ALTER TABLE `%susers` DROP COLUMN `country`;", gS_SQLPrefix);
}
QueryLog(gH_SQL, SQL_TableMigrationSingleQuery_Callback, sQuery, Migration_RemoveCountry, DBPrio_High);
}
void ApplyMigration_PlayertimesDateToInt()
{
char sQuery[128];
if (gI_Driver == Driver_mysql)
{
FormatEx(sQuery, 128, "ALTER TABLE `%splayertimes` CHANGE COLUMN `date` `date` INT;", gS_SQLPrefix);
}
else if (gI_Driver == Driver_pgsql)
{
FormatEx(sQuery, 128, "ALTER TABLE %splayertimes ALTER COLUMN date TYPE INT;", gS_SQLPrefix);
}
else // SQLite - doesn't support ALTER COLUMN type changes
{
FormatEx(sQuery, 128, "-- SQLite doesn't support ALTER COLUMN type changes");
}
QueryLog(gH_SQL, SQL_TableMigrationSingleQuery_Callback, sQuery, Migration_PlayertimesDateToInt, DBPrio_High);
}
void ApplyMigration_AddZonesFlagsAndData()
{
char sQuery[192];
if (gI_Driver == Driver_mysql)
{
FormatEx(sQuery, 192, "ALTER TABLE `%smapzones` ADD COLUMN `flags` INT NULL AFTER `track`, ADD COLUMN `data` INT NULL AFTER `flags`;", gS_SQLPrefix);
}
else if (gI_Driver == Driver_pgsql)
{
FormatEx(sQuery, 192, "ALTER TABLE %smapzones ADD COLUMN flags INT NULL, ADD COLUMN data INT NULL;", gS_SQLPrefix);
}
else // SQLite
{
FormatEx(sQuery, 192, "ALTER TABLE `%smapzones` ADD COLUMN `flags` INT NULL, ADD COLUMN `data` INT NULL;", gS_SQLPrefix);
}
QueryLog(gH_SQL, SQL_TableMigrationSingleQuery_Callback, sQuery, Migration_AddZonesFlagsAndData, DBPrio_High);
}
void ApplyMigration_AddPlayertimesCompletions()
{
char sQuery[192];
if (gI_Driver == Driver_mysql)
{
FormatEx(sQuery, 192, "ALTER TABLE `%splayertimes` ADD COLUMN `completions` SMALLINT DEFAULT 1 AFTER `perfs`;", gS_SQLPrefix);
}
else if (gI_Driver == Driver_pgsql)
{
FormatEx(sQuery, 192, "ALTER TABLE %splayertimes ADD COLUMN completions SMALLINT DEFAULT 1;", gS_SQLPrefix);
}
else // SQLite
{
FormatEx(sQuery, 192, "ALTER TABLE `%splayertimes` ADD COLUMN `completions` SMALLINT DEFAULT 1;", gS_SQLPrefix);
}
QueryLog(gH_SQL, SQL_TableMigrationSingleQuery_Callback, sQuery, Migration_AddPlayertimesCompletions, DBPrio_High);
}
void ApplyMigration_AddCustomChatAccess()
{
char sQuery[192];
FormatEx(sQuery, 192, "ALTER TABLE `%schat` ADD COLUMN `ccaccess` INT NOT NULL DEFAULT 0 %s;", gS_SQLPrefix, (gI_Driver == Driver_mysql) ? "AFTER `ccmessage`" : "");
if (gI_Driver == Driver_mysql)
{
FormatEx(sQuery, 192, "ALTER TABLE `%schat` ADD COLUMN `ccaccess` INT NOT NULL DEFAULT 0 AFTER `ccmessage`;", gS_SQLPrefix);
}
else if (gI_Driver == Driver_pgsql)
{
FormatEx(sQuery, 192, "ALTER TABLE %schat ADD COLUMN ccaccess INT NOT NULL DEFAULT 0;", gS_SQLPrefix);
}
else // SQLite
{
FormatEx(sQuery, 192, "ALTER TABLE `%schat` ADD COLUMN `ccaccess` INT NOT NULL DEFAULT 0;", gS_SQLPrefix);
}
QueryLog(gH_SQL, SQL_TableMigrationSingleQuery_Callback, sQuery, Migration_AddCustomChatAccess, DBPrio_High);
}
@ -434,7 +665,18 @@ void ApplyMigration_AddPlayertimesExactTimeInt()
void ApplyMigration_FixOldCompletionCounts()
{
char sQuery[192];
if (gI_Driver == Driver_mysql)
{
FormatEx(sQuery, 192, "UPDATE `%splayertimes` SET completions = completions - 1 WHERE completions > 1;", gS_SQLPrefix);
}
else if (gI_Driver == Driver_pgsql)
{
FormatEx(sQuery, 192, "UPDATE %splayertimes SET completions = completions - 1 WHERE completions > 1;", gS_SQLPrefix);
}
else // SQLite
{
FormatEx(sQuery, 192, "UPDATE `%splayertimes` SET completions = completions - 1 WHERE completions > 1;", gS_SQLPrefix);
}
QueryLog(gH_SQL, SQL_TableMigrationSingleQuery_Callback, sQuery, Migration_FixOldCompletionCounts, DBPrio_High);
}
@ -453,14 +695,36 @@ void ApplyMigration_AddPrebuiltToMapZonesTable()
void ApplyMigration_AddPlaytime()
{
char sQuery[192];
if (gI_Driver == Driver_mysql)
{
FormatEx(sQuery, 192, "ALTER TABLE `%susers` MODIFY COLUMN `playtime` FLOAT NOT NULL DEFAULT 0;", gS_SQLPrefix);
}
else if (gI_Driver == Driver_pgsql)
{
FormatEx(sQuery, 192, "ALTER TABLE %susers ALTER COLUMN playtime TYPE REAL USING playtime::REAL;", gS_SQLPrefix);
}
else // SQLite
{
FormatEx(sQuery, 192, "-- SQLite doesn't support ALTER COLUMN type changes");
}
QueryLog(gH_SQL, SQL_Migration_AddPlaytime2222222_Callback, sQuery, Migration_AddPlaytime, DBPrio_High);
}
public void SQL_Migration_AddPlaytime2222222_Callback(Database db, DBResultSet results, const char[] error, any data)
{
char sQuery[192];
FormatEx(sQuery, 192, "ALTER TABLE `%susers` ADD COLUMN `playtime` FLOAT NOT NULL DEFAULT 0 %s;", gS_SQLPrefix, (gI_Driver == Driver_mysql) ? "AFTER `points`" : "");
if (gI_Driver == Driver_mysql)
{
FormatEx(sQuery, 192, "ALTER TABLE `%susers` ADD COLUMN `playtime` FLOAT NOT NULL DEFAULT 0 AFTER `points`;", gS_SQLPrefix);
}
else if (gI_Driver == Driver_pgsql)
{
FormatEx(sQuery, 192, "ALTER TABLE %susers ADD COLUMN playtime REAL NOT NULL DEFAULT 0;", gS_SQLPrefix);
}
else // SQLite
{
FormatEx(sQuery, 192, "ALTER TABLE `%susers` ADD COLUMN `playtime` REAL NOT NULL DEFAULT 0;", gS_SQLPrefix);
}
QueryLog(gH_SQL, SQL_TableMigrationSingleQuery_Callback, sQuery, Migration_AddPlaytime, DBPrio_High);
}
@ -485,7 +749,18 @@ void ApplyMigration_AddPlayertimesPointsCalcedFrom()
void ApplyMigration_RemovePlayertimesPointsCalcedFrom()
{
char sQuery[192];
if (gI_Driver == Driver_mysql)
{
FormatEx(sQuery, 192, "ALTER TABLE `%splayertimes` DROP COLUMN `points_calced_from`;", gS_SQLPrefix);
}
else if (gI_Driver == Driver_pgsql)
{
FormatEx(sQuery, 192, "ALTER TABLE %splayertimes DROP COLUMN points_calced_from;", gS_SQLPrefix);
}
else // SQLite
{
FormatEx(sQuery, 192, "ALTER TABLE `%splayertimes` DROP COLUMN `points_calced_from`;", gS_SQLPrefix);
}
QueryLog(gH_SQL, SQL_TableMigrationSingleQuery_Callback, sQuery, Migration_RemovePlayertimesPointsCalcedFrom, DBPrio_High);
}
@ -517,14 +792,36 @@ void ApplyMigration_NormalizeMapzonePoints() // TODO: test with sqlite lol
void ApplyMigration_AddMapzonesForm()
{
char sQuery[192];
if (gI_Driver == Driver_mysql)
{
FormatEx(sQuery, sizeof(sQuery), "ALTER TABLE `%smapzones` ADD COLUMN `form` TINYINT;", gS_SQLPrefix);
}
else if (gI_Driver == Driver_pgsql)
{
FormatEx(sQuery, sizeof(sQuery), "ALTER TABLE %smapzones ADD COLUMN form SMALLINT;", gS_SQLPrefix);
}
else // SQLite
{
FormatEx(sQuery, sizeof(sQuery), "ALTER TABLE `%smapzones` ADD COLUMN `form` TINYINT;", gS_SQLPrefix);
}
QueryLog(gH_SQL, SQL_TableMigrationSingleQuery_Callback, sQuery, Migration_AddMapzonesForm, DBPrio_High);
}
void ApplyMigration_AddMapzonesTarget()
{
char sQuery[192];
if (gI_Driver == Driver_mysql)
{
FormatEx(sQuery, sizeof(sQuery), "ALTER TABLE `%smapzones` ADD COLUMN `target` VARCHAR(63);", gS_SQLPrefix);
}
else if (gI_Driver == Driver_pgsql)
{
FormatEx(sQuery, sizeof(sQuery), "ALTER TABLE %smapzones ADD COLUMN target VARCHAR(63);", gS_SQLPrefix);
}
else // SQLite
{
FormatEx(sQuery, sizeof(sQuery), "ALTER TABLE `%smapzones` ADD COLUMN `target` VARCHAR(63);", gS_SQLPrefix);
}
QueryLog(gH_SQL, SQL_TableMigrationSingleQuery_Callback, sQuery, Migration_AddMapzonesTarget, DBPrio_High);
}
@ -695,14 +992,36 @@ public void Trans_FixSQLiteMapzonesROWID_Error(Database db, any data, int numQue
void ApplyMigration_AddUsersFirstLogin()
{
char sQuery[256];
FormatEx(sQuery, sizeof(sQuery), "ALTER TABLE %susers ADD `firstlogin` INT NOT NULL DEFAULT -1 %s;", gS_SQLPrefix, (gI_Driver == Driver_mysql) ? "AFTER `lastlogin`" : "");
if (gI_Driver == Driver_mysql)
{
FormatEx(sQuery, sizeof(sQuery), "ALTER TABLE %susers ADD `firstlogin` INT NOT NULL DEFAULT -1 AFTER `lastlogin`;", gS_SQLPrefix);
}
else if (gI_Driver == Driver_pgsql)
{
FormatEx(sQuery, sizeof(sQuery), "ALTER TABLE %susers ADD firstlogin INT NOT NULL DEFAULT -1;", gS_SQLPrefix);
}
else // SQLite
{
FormatEx(sQuery, sizeof(sQuery), "ALTER TABLE %susers ADD `firstlogin` INT NOT NULL DEFAULT -1;", gS_SQLPrefix);
}
QueryLog(gH_SQL, ApplyMigration_AddUsersFirstLogin2222222_Callback, sQuery, Migration_AddUsersFirstLogin, DBPrio_High);
}
public void ApplyMigration_AddUsersFirstLogin2222222_Callback(Database db, DBResultSet results, const char[] error, any data)
{
char sQuery[256];
if (gI_Driver == Driver_mysql)
{
FormatEx(sQuery, sizeof(sQuery), "UPDATE `%susers` SET firstlogin = lastlogin WHERE lastlogin > 1188518400;", gS_SQLPrefix);
}
else if (gI_Driver == Driver_pgsql)
{
FormatEx(sQuery, sizeof(sQuery), "UPDATE %susers SET firstlogin = lastlogin WHERE lastlogin > 1188518400;", gS_SQLPrefix);
}
else // SQLite
{
FormatEx(sQuery, sizeof(sQuery), "UPDATE `%susers` SET firstlogin = lastlogin WHERE lastlogin > 1188518400;", gS_SQLPrefix);
}
QueryLog(gH_SQL, SQL_TableMigrationSingleQuery_Callback, sQuery, Migration_AddUsersFirstLogin, DBPrio_High);
}
@ -738,7 +1057,34 @@ public void ApplyMigration_MoreFirstLoginStuff()
);
AddQueryLog(trans, query);
}
else // sqlite & postgresql use the same syntax here
else if (gI_Driver == Driver_pgsql)
{
FormatEx(query, sizeof(query),
"UPDATE %susers SET firstlogin = pt.min_date::INT \
FROM ( \
SELECT auth, MIN(FLOOR(date - time))::INT as min_date \
FROM %splayertimes \
WHERE date > 1188518400 \
GROUP BY auth \
) as pt \
WHERE %susers.auth = pt.auth AND firstlogin <= 0;",
gS_SQLPrefix, gS_SQLPrefix, gS_SQLPrefix
);
AddQueryLog(trans, query);
FormatEx(query, sizeof(query),
"UPDATE %susers SET firstlogin = LEAST(firstlogin, pt.min_date::INT) \
FROM ( \
SELECT auth, MIN(FLOOR(date - time))::INT as min_date \
FROM %splayertimes \
WHERE date > 1188518400 \
GROUP BY auth \
) as pt \
WHERE %susers.auth = pt.auth AND firstlogin > 0;",
gS_SQLPrefix, gS_SQLPrefix, gS_SQLPrefix
);
AddQueryLog(trans, query);
}
else // sqlite
{
FormatEx(query, sizeof(query),
"UPDATE %susers SET firstlogin = pt.min_date \
@ -922,12 +1268,34 @@ void ApplyMigration_ConvertIPAddresses(bool index = true)
char sQuery[128];
if (index)
{
if (gI_Driver == Driver_mysql)
{
FormatEx(sQuery, 128, "ALTER TABLE `%susers` ADD INDEX `ip` (`ip`);", gS_SQLPrefix);
}
else if (gI_Driver == Driver_pgsql)
{
FormatEx(sQuery, 128, "CREATE INDEX ip ON %susers (ip);", gS_SQLPrefix);
}
else // SQLite
{
FormatEx(sQuery, 128, "CREATE INDEX ip ON `%susers` (`ip`);", gS_SQLPrefix);
}
QueryLog(gH_SQL, SQL_TableMigrationIndexing_Callback, sQuery, 0, DBPrio_High);
}
if (gI_Driver == Driver_mysql)
{
FormatEx(sQuery, 128, "SELECT DISTINCT ip FROM %susers WHERE ip LIKE '%%.%%';", gS_SQLPrefix);
}
else if (gI_Driver == Driver_pgsql)
{
FormatEx(sQuery, 128, "SELECT DISTINCT ip FROM %susers WHERE ip LIKE '%%.%%';", gS_SQLPrefix);
}
else // SQLite
{
FormatEx(sQuery, 128, "SELECT DISTINCT ip FROM %susers WHERE ip LIKE '%%.%%';", gS_SQLPrefix);
}
QueryLog(gH_SQL, SQL_TableMigrationIPAddresses_Callback, sQuery);
}
@ -973,10 +1341,32 @@ public void Trans_IPAddressMigrationSuccess(Database db, any data, int numQuerie
}
char sQuery[128];
if (gI_Driver == Driver_mysql)
{
FormatEx(sQuery, 128, "ALTER TABLE `%susers` DROP INDEX `ip`;", gS_SQLPrefix);
}
else if (gI_Driver == Driver_pgsql)
{
FormatEx(sQuery, 128, "DROP INDEX IF EXISTS ip;");
}
else // SQLite
{
FormatEx(sQuery, 128, "DROP INDEX IF EXISTS ip;");
}
QueryLog(gH_SQL, SQL_TableMigrationIndexing_Callback, sQuery, 0, DBPrio_High);
if (gI_Driver == Driver_mysql)
{
FormatEx(sQuery, 128, "ALTER TABLE `%susers` CHANGE COLUMN `ip` `ip` INT;", gS_SQLPrefix);
}
else if (gI_Driver == Driver_pgsql)
{
FormatEx(sQuery, 128, "ALTER TABLE %susers ALTER COLUMN ip TYPE INT USING ip::INT;", gS_SQLPrefix);
}
else // SQLite - doesn't support ALTER COLUMN type changes
{
FormatEx(sQuery, 128, "-- SQLite doesn't support ALTER COLUMN type changes");
}
QueryLog(gH_SQL, SQL_TableMigrationSingleQuery_Callback, sQuery, Migration_ConvertIPAddresses, DBPrio_High);
}

View File

@ -409,7 +409,18 @@ public void SQL_FillTierCache_Callback(Database db, DBResultSet results, const c
Call_Finish();
char sQuery[512];
if (gI_Driver == Driver_mysql)
{
FormatEx(sQuery, sizeof(sQuery), "REPLACE INTO %smaptiers (map, tier) VALUES ('%s', %d);", gS_MySQLPrefix, gS_Map, gI_Tier);
}
else if (gI_Driver == Driver_pgsql)
{
FormatEx(sQuery, sizeof(sQuery), "INSERT INTO %smaptiers (map, tier) VALUES ('%s', %d) ON CONFLICT (map) DO UPDATE SET tier = EXCLUDED.tier;", gS_MySQLPrefix, gS_Map, gI_Tier);
}
else // SQLite
{
FormatEx(sQuery, sizeof(sQuery), "INSERT OR REPLACE INTO %smaptiers (map, tier) VALUES ('%s', %d);", gS_MySQLPrefix, gS_Map, gI_Tier);
}
QueryLog(gH_SQL, SQL_SetMapTier_Callback, sQuery, 0, DBPrio_High);
}
}
@ -669,7 +680,18 @@ public Action Command_SetTier(int client, int args)
Shavit_LogMessage("%L - set tier of `%s` to %d", client, gS_Map, tier);
char sQuery[512];
if (gI_Driver == Driver_mysql)
{
FormatEx(sQuery, sizeof(sQuery), "REPLACE INTO %smaptiers (map, tier) VALUES ('%s', %d);", gS_MySQLPrefix, map, tier);
}
else if (gI_Driver == Driver_pgsql)
{
FormatEx(sQuery, sizeof(sQuery), "INSERT INTO %smaptiers (map, tier) VALUES ('%s', %d) ON CONFLICT (map) DO UPDATE SET tier = EXCLUDED.tier;", gS_MySQLPrefix, map, tier);
}
else // SQLite
{
FormatEx(sQuery, sizeof(sQuery), "INSERT OR REPLACE INTO %smaptiers (map, tier) VALUES ('%s', %d);", gS_MySQLPrefix, map, tier);
}
DataPack data = new DataPack();
data.WriteCell(client ? GetClientSerial(client) : 0);
@ -788,7 +810,7 @@ void FormatRecalculate(bool bUseCurrentMap, int track, int style, char[] sQuery,
gS_Map
);
}
else
else if (gI_Driver == Driver_mysql)
{
FormatEx(sQuery, sQueryLen,
"UPDATE %splayertimes AS PT " ...
@ -808,6 +830,27 @@ void FormatRecalculate(bool bUseCurrentMap, int track, int style, char[] sQuery,
gS_Map
);
}
else // PostgreSQL
{
FormatEx(sQuery, sQueryLen,
"UPDATE %splayertimes AS PT " ...
"SET " ...
" points = "...
" (%f + (WR.time / 15.0)) " ...
" * (WR.time / PT.time) " ...
" * %f " ...
"FROM %swrs WR " ...
"WHERE PT.track = WR.track AND PT.style = WR.style AND PT.map = WR.map " ...
" AND PT.track %c 0 AND PT.style = %d AND PT.map = '%s';",
gS_MySQLPrefix,
((gCV_PointsPerTier.FloatValue * fTier) * 1.5),
fMultiplier,
gS_MySQLPrefix,
(track > 0) ? '>' : '=',
style,
gS_Map
);
}
}
else if (gI_Driver == Driver_sqlite)
{
@ -835,7 +878,7 @@ void FormatRecalculate(bool bUseCurrentMap, int track, int style, char[] sQuery,
mapfilter
);
}
else
else if (gI_Driver == Driver_mysql)
{
char mapfilter[50+PLATFORM_MAX_PATH];
if (map[0]) FormatEx(mapfilter, sizeof(mapfilter), "AND PT.map = '%s'", map);
@ -862,6 +905,32 @@ void FormatRecalculate(bool bUseCurrentMap, int track, int style, char[] sQuery,
fMultiplier
);
}
else // PostgreSQL
{
char mapfilter[50+PLATFORM_MAX_PATH];
if (map[0]) FormatEx(mapfilter, sizeof(mapfilter), "AND PT.map = '%s'", map);
FormatEx(sQuery, sQueryLen,
"UPDATE %splayertimes AS PT " ...
"SET " ...
" points = "...
" (((%f * %s) * 1.5) + (WR.time / 15.0)) " ...
" * (WR.time / PT.time) " ...
" * %f " ...
"FROM %swrs AS WR, %smaptiers AS MT " ...
"WHERE PT.track %c 0 AND PT.track = WR.track AND PT.style = %d AND PT.style = WR.style %s AND PT.map = WR.map " ...
" AND PT.map = MT.map;",
gS_MySQLPrefix,
gCV_PointsPerTier.FloatValue,
(track > 0) ? "1" : "MT.tier",
fMultiplier,
gS_MySQLPrefix,
gS_MySQLPrefix,
(track > 0) ? '>' : '=',
style,
mapfilter
);
}
}
public Action Command_RecalcAll(int client, int args)
@ -1098,7 +1167,7 @@ void UpdateAllPoints(bool recalcall=false, char[] map="", int track=-1)
if (sLastLogin[0])
Format(sLastLogin, sizeof(sLastLogin), "u2.%s", sLastLogin);
// fuck you mysql
// MySQL-specific JOIN syntax
FormatEx(sQuery, sizeof(sQuery),
"UPDATE %susers AS u, (\n"
... " SELECT auth, SUM(t.points2) as pp FROM (\n"
@ -1125,8 +1194,34 @@ void UpdateAllPoints(bool recalcall=false, char[] map="", int track=-1)
sTrackWhere,
sLimit); // TODO: Remove/move sLimit?
}
else if (gB_SQLWindowFunctions && gI_Driver == Driver_pgsql)
{
// PostgreSQL-specific syntax (similar to standard SQL)
FormatEx(sQuery, sizeof(sQuery),
"UPDATE %susers AS u\n"
... "SET points = (\n"
... " SELECT SUM(points2) FROM (\n"
... " SELECT (points * POWER(%f, ROW_NUMBER() OVER (ORDER BY points DESC) - 1)) AS points2\n"
... " FROM %splayertimes\n"
... " WHERE auth = u.auth AND points > 0\n"
... " ORDER BY points DESC %s\n"
... " ) AS t\n"
... ") WHERE %s %s auth IN\n"
... " (SELECT DISTINCT auth FROM %splayertimes %s %s %s %s);",
gS_MySQLPrefix,
gCV_WeightingMultiplier.FloatValue,
gS_MySQLPrefix,
sLimit, // TODO: Remove/move sLimit?
sLastLogin, sLastLogin[0] ? "AND" : "",
gS_MySQLPrefix,
(sMapWhere[0] || sTrackWhere[0]) ? "WHERE" : "",
sMapWhere,
(sMapWhere[0] && sTrackWhere[0]) ? "AND" : "",
sTrackWhere);
}
else if (gB_SQLWindowFunctions)
{
// Standard SQL window functions (SQLite and other databases)
FormatEx(sQuery, sizeof(sQuery),
"UPDATE %susers AS u\n"
... "SET points = (\n"
@ -1194,8 +1289,21 @@ void UpdatePlayerRank(int client, bool first)
// if there's any issue with this query,
// add "ORDER BY points DESC " before "LIMIT 1"
char sQuery[512];
if (gI_Driver == Driver_mysql)
{
FormatEx(sQuery, 512, "SELECT u2.points, COUNT(*) FROM %susers u1 JOIN (SELECT points FROM %susers WHERE auth = %d) u2 WHERE u1.points >= u2.points;",
gS_MySQLPrefix, gS_MySQLPrefix, iSteamID);
}
else if (gI_Driver == Driver_pgsql)
{
FormatEx(sQuery, 512, "SELECT u2.points, COUNT(*) FROM %susers u1 CROSS JOIN (SELECT points FROM %susers WHERE auth = %d) u2 WHERE u1.points >= u2.points GROUP BY u2.points;",
gS_MySQLPrefix, gS_MySQLPrefix, iSteamID);
}
else // SQLite
{
FormatEx(sQuery, 512, "SELECT u2.points, COUNT(*) FROM %susers u1 JOIN (SELECT points FROM %susers WHERE auth = %d) u2 WHERE u1.points >= u2.points;",
gS_MySQLPrefix, gS_MySQLPrefix, iSteamID);
}
DataPack hPack = new DataPack();
hPack.WriteCell(GetClientSerial(client));
@ -1245,11 +1353,30 @@ public void SQL_UpdatePlayerRank_Callback(Database db, DBResultSet results, cons
void UpdateTop100()
{
char sQuery[512];
if (gI_Driver == Driver_mysql)
{
FormatEx(sQuery, sizeof(sQuery),
"SELECT * FROM (SELECT COUNT(*) as c, 0 as auth, '' as name, '' as p FROM %susers WHERE points > 0) a \
UNION ALL \
SELECT * FROM (SELECT -1 as c, auth, name, FORMAT(points, 2) FROM %susers WHERE points > 0 ORDER BY points DESC LIMIT 100) b;",
gS_MySQLPrefix, gS_MySQLPrefix);
}
else if (gI_Driver == Driver_pgsql)
{
FormatEx(sQuery, sizeof(sQuery),
"SELECT * FROM (SELECT COUNT(*) as c, 0 as auth, '' as name, '' as p FROM %susers WHERE points > 0) a \
UNION ALL \
SELECT * FROM (SELECT -1 as c, auth, name, ROUND(points::numeric, 2)::text FROM %susers WHERE points > 0 ORDER BY points DESC LIMIT 100) b;",
gS_MySQLPrefix, gS_MySQLPrefix);
}
else // SQLite
{
FormatEx(sQuery, sizeof(sQuery),
"SELECT * FROM (SELECT COUNT(*) as c, 0 as auth, '' as name, '' as p FROM %susers WHERE points > 0) a \
UNION ALL \
SELECT * FROM (SELECT -1 as c, auth, name, ROUND(points, 2) FROM %susers WHERE points > 0 ORDER BY points DESC LIMIT 100) b;",
gS_MySQLPrefix, gS_MySQLPrefix);
}
QueryLog(gH_SQL, SQL_UpdateTop100_Callback, sQuery, 0, DBPrio_High);
}
@ -1303,24 +1430,68 @@ public void SQL_UpdateTop100_Callback(Database db, DBResultSet results, const ch
}
bool DoWeHaveWindowFunctions(const char[] sVersion)
{
if (gI_Driver == Driver_sqlite)
{
char buf[100][2];
ExplodeString(sVersion, ".", buf, 2, 100);
int iMajor = StringToInt(buf[0]);
int iMinor = StringToInt(buf[1]);
if (gI_Driver == Driver_sqlite)
{
// 2018~
return iMajor > 3 || (iMajor == 3 && iMinor >= 25); // 2018~
}
else if (gI_Driver == Driver_pgsql)
{
// 2009~
// PostgreSQL version string format: "PostgreSQL 16.1 on x86_64-pc-linux-gnu..."
// Extract version number after "PostgreSQL "
char sVersionCopy[100];
strcopy(sVersionCopy, sizeof(sVersionCopy), sVersion);
int iStart = StrContains(sVersionCopy, "PostgreSQL ");
if (iStart != -1)
{
iStart += 11; // Length of "PostgreSQL "
char sVersionPart[32];
strcopy(sVersionPart, sizeof(sVersionPart), sVersionCopy[iStart]);
// Find first space to terminate version string
int iEnd = StrContains(sVersionPart, " ");
if (iEnd != -1)
{
sVersionPart[iEnd] = '\0';
}
char buf[32][2];
ExplodeString(sVersionPart, ".", buf, 2, 32);
int iMajor = StringToInt(buf[0]);
int iMinor = StringToInt(buf[1]);
// PostgreSQL has had window functions since 8.4 (2009)
// But modern PostgreSQL uses major.minor format where major >= 10
if (iMajor >= 10)
{
return true; // All PostgreSQL 10+ have window functions
}
else
{
return iMajor > 8 || (iMajor == 8 && iMinor >= 4);
}
}
// Fallback: assume old format and check anyway
char buf[100][2];
ExplodeString(sVersion, ".", buf, 2, 100);
int iMajor = StringToInt(buf[0]);
int iMinor = StringToInt(buf[1]);
return iMajor > 8 || (iMajor == 8 && iMinor >= 4);
}
else if (gI_Driver == Driver_mysql)
{
char buf[100][2];
ExplodeString(sVersion, ".", buf, 2, 100);
int iMajor = StringToInt(buf[0]);
int iMinor = StringToInt(buf[1]);
if (StrContains(sVersion, "MariaDB") != -1)
{
// 2016~
@ -1362,8 +1533,8 @@ public void SQL_Version_Callback(Database db, DBResultSet results, const char[]
}
else if (gI_Driver == Driver_pgsql)
{
LogError("Okay, really? Your postgres version is from 2014 or earlier... come on, brother...");
SetFailState("Update postgresql");
LogError("PostgreSQL version does not support window functions. Window functions require PostgreSQL 8.4 or later.");
SetFailState("Update PostgreSQL to version 8.4 or later");
}
else // mysql
{

View File

@ -49,6 +49,7 @@ bool gB_Rankings = false;
// database handle
Database gH_SQL = null;
char gS_MySQLPrefix[32];
int gI_Driver = Driver_unknown;
// cache
bool gB_CanOpenMenu[MAXPLAYERS+1];
@ -187,7 +188,7 @@ void FlushDisconnectPlaytime()
public void Shavit_OnDatabaseLoaded()
{
GetTimerSQLPrefix(gS_MySQLPrefix, 32);
gH_SQL = Shavit_GetDatabase();
gH_SQL = Shavit_GetDatabase(gI_Driver);
for (int i = 1; i <= MaxClients; i++)
{
@ -425,10 +426,19 @@ void SavePlaytime222(int client, float now, Transaction&trans, int style, int iS
return;
}
if (gI_Driver == Driver_mysql)
{
FormatEx(sQuery, sizeof(sQuery),
"UPDATE `%susers` SET playtime = playtime + %f WHERE auth = %d;",
gS_MySQLPrefix, diff, iSteamID);
}
else // PostgreSQL/SQLite
{
FormatEx(sQuery, sizeof(sQuery),
"UPDATE %susers SET playtime = playtime + %f WHERE auth = %d;",
gS_MySQLPrefix, diff, iSteamID);
}
}
else
{
float diff = gF_PlaytimeStyleSum[client][style];
@ -447,18 +457,36 @@ void SavePlaytime222(int client, float now, Transaction&trans, int style, int iS
}
if (gB_HavePlaytimeOnStyle[client][style])
{
if (gI_Driver == Driver_mysql)
{
FormatEx(sQuery, sizeof(sQuery),
"UPDATE `%sstyleplaytime` SET playtime = playtime + %f WHERE auth = %d AND style = %d;",
gS_MySQLPrefix, diff, iSteamID, style);
}
else // PostgreSQL/SQLite
{
FormatEx(sQuery, sizeof(sQuery),
"UPDATE %sstyleplaytime SET playtime = playtime + %f WHERE auth = %d AND style = %d;",
gS_MySQLPrefix, diff, iSteamID, style);
}
}
else
{
gB_HavePlaytimeOnStyle[client][style] = true;
if (gI_Driver == Driver_mysql)
{
FormatEx(sQuery, sizeof(sQuery),
"INSERT INTO `%sstyleplaytime` (`auth`, `style`, `playtime`) VALUES (%d, %d, %f);",
gS_MySQLPrefix, iSteamID, style, diff);
}
else // PostgreSQL/SQLite
{
FormatEx(sQuery, sizeof(sQuery),
"INSERT INTO %sstyleplaytime (auth, style, playtime) VALUES (%d, %d, %f);",
gS_MySQLPrefix, iSteamID, style, diff);
}
}
}
if (trans == null)
@ -554,11 +582,23 @@ public Action Command_Playtime(int client, int args)
}
char sQuery[512];
if (gI_Driver == Driver_mysql)
{
FormatEx(sQuery, sizeof(sQuery),
"(SELECT auth, name, playtime, -1 as ownrank FROM %susers WHERE playtime > 0 ORDER BY playtime DESC LIMIT 100) " ...
"UNION " ...
"(SELECT -1, '', u2.playtime, COUNT(*) as ownrank FROM %susers u1 JOIN (SELECT playtime FROM %susers WHERE auth = %d) u2 WHERE u1.playtime >= u2.playtime);",
gS_MySQLPrefix, gS_MySQLPrefix, gS_MySQLPrefix, GetSteamAccountID(client));
}
else // PostgreSQL/SQLite
{
FormatEx(sQuery, sizeof(sQuery),
"(SELECT auth, name, playtime, -1 as ownrank FROM %susers WHERE playtime > 0 ORDER BY playtime DESC LIMIT 100) " ...
"UNION " ...
"(SELECT -1, ''::TEXT, u2.playtime, COUNT(*)::INT as ownrank FROM %susers u1 JOIN (SELECT playtime FROM %susers WHERE auth = %d) u2 WHERE u1.playtime >= u2.playtime);",
gS_MySQLPrefix, gS_MySQLPrefix, gS_MySQLPrefix, GetSteamAccountID(client));
}
QueryLog(gH_SQL, SQL_TopPlaytime_Callback, sQuery, GetClientSerial(client), DBPrio_Normal);
return Plugin_Handled;
@ -819,6 +859,10 @@ Action OpenStatsMenu(int client, int steamid, int style = 0, int item = 0)
if (gB_Mapchooser && gCV_UseMapchooser.BoolValue)
{
char sQuery[2048];
if (gI_Driver == Driver_mysql)
{
// MySQL/SQLite: original query
FormatEx(sQuery, sizeof(sQuery),
// Note the `GROUP BY track>0` for now
"SELECT 0 as blah, map, track>0 FROM %splayertimes WHERE auth = %d AND style = %d GROUP BY map, track>0 " ...
@ -826,6 +870,28 @@ Action OpenStatsMenu(int client, int steamid, int style = 0, int item = 0)
"UNION SELECT 2 as blah, map, track FROM %swrs WHERE auth = %d AND style = %d GROUP BY map, track;",
gS_MySQLPrefix, steamid, style, gS_MySQLPrefix, gS_MySQLPrefix, steamid, style
);
}
else if (gI_Driver == Driver_pgsql)
{
// PostgreSQL: cast track to boolean to match track>0
FormatEx(sQuery, sizeof(sQuery),
"SELECT 0 as blah, map, track>0 FROM %splayertimes WHERE auth = %d AND style = %d GROUP BY map, track>0 " ...
"UNION SELECT 1 as blah, map, track>0 FROM %smapzones WHERE type = 0 GROUP BY map, track>0 " ...
"UNION SELECT 2 as blah, map, track>0 FROM %swrs WHERE auth = %d AND style = %d GROUP BY map, track>0;",
gS_MySQLPrefix, steamid, style, gS_MySQLPrefix, gS_MySQLPrefix, steamid, style
);
}
else // SQLite
{
// SQLite: same as MySQL
FormatEx(sQuery, sizeof(sQuery),
// Note the `GROUP BY track>0` for now
"SELECT 0 as blah, map, track>0 FROM %splayertimes WHERE auth = %d AND style = %d GROUP BY map, track>0 " ...
"UNION SELECT 1 as blah, map, track>0 FROM %smapzones WHERE type = 0 GROUP BY map, track>0 " ...
"UNION SELECT 2 as blah, map, track FROM %swrs WHERE auth = %d AND style = %d GROUP BY map, track;",
gS_MySQLPrefix, steamid, style, gS_MySQLPrefix, gS_MySQLPrefix, steamid, style
);
}
QueryLog(gH_SQL, OpenStatsMenu_Mapchooser_Callback, sQuery, data, DBPrio_Low);
@ -889,25 +955,43 @@ Action OpenStatsMenu_Main(int steamid, int style, DataPack data)
{
char sQuery[2048];
if (gI_Driver == Driver_mysql)
{
FormatEx(sQuery, sizeof(sQuery),
"SELECT 0, points, lastlogin, firstlogin, ip, playtime, name FROM %susers WHERE auth = %d\n" ...
"UNION ALL SELECT 1, SUM(playtime), 0, 0, 0, 0, '' FROM %sstyleplaytime WHERE auth = %d AND style = %d\n" ...
"UNION ALL SELECT 2, COUNT(*), 0, 0, 0, 0, '' FROM %susers u1\n" ...
" JOIN (SELECT points FROM %susers WHERE auth = %d) u2\n" ...
" JOIN (SELECT points FROM %susers WHERE auth = %d) u2 ON 1=1\n" ...
" WHERE u1.points >= u2.points",
gS_MySQLPrefix, steamid,
gS_MySQLPrefix, steamid, style,
gS_MySQLPrefix, gS_MySQLPrefix, steamid
);
}
else // PostgreSQL/SQLite
{
FormatEx(sQuery, sizeof(sQuery),
"SELECT 0, points::FLOAT, lastlogin, firstlogin, ip, playtime::FLOAT, name FROM %susers WHERE auth = %d\n" ...
"UNION ALL SELECT 1, SUM(playtime)::FLOAT, 0, 0, 0, 0.0, '' FROM %sstyleplaytime WHERE auth = %d AND style = %d\n" ...
"UNION ALL SELECT 2, COUNT(*)::FLOAT, 0, 0, 0, 0.0, '' FROM %susers u1\n" ...
" JOIN (SELECT points FROM %susers WHERE auth = %d) u2 ON 1=1\n" ...
" WHERE u1.points >= u2.points",
gS_MySQLPrefix, steamid,
gS_MySQLPrefix, steamid, style,
gS_MySQLPrefix, gS_MySQLPrefix, steamid
);
}
if (!gB_Mapchooser || !gCV_UseMapchooser.BoolValue)
{
if (gI_Driver == Driver_mysql)
{
Format(sQuery, sizeof(sQuery),
"%s\n" ...
"UNION ALL SELECT 3, COUNT(*), x.bonus, 0, 0, '' FROM\n"...
"UNION ALL SELECT 3, COUNT(*), x.bonus, 0, 0, 0, '' FROM\n"...
" (SELECT map, track>0 as bonus FROM %splayertimes WHERE auth = %d AND style = %d GROUP BY map, track>0) x GROUP BY x.bonus\n"...
"UNION ALL SELECT 4, COUNT(*), track>0, 0, 0, '' FROM %swrs WHERE auth = %d AND style = %d GROUP BY track>0\n"...
"UNION ALL SELECT 5, COUNT(*), x.bonus, 0, 0, '' FROM\n"...
"UNION ALL SELECT 4, COUNT(*), track>0, 0, 0, 0, '' FROM %swrs WHERE auth = %d AND style = %d GROUP BY track>0\n"...
"UNION ALL SELECT 5, COUNT(*), x.bonus, 0, 0, 0, '' FROM\n"...
" (SELECT map, track>0 as bonus FROM %smapzones WHERE type = 0 GROUP BY map, track>0) x GROUP BY x.bonus",
sQuery,
gS_MySQLPrefix, steamid, style,
@ -915,6 +999,22 @@ Action OpenStatsMenu_Main(int steamid, int style, DataPack data)
gS_MySQLPrefix
);
}
else // PostgreSQL/SQLite
{
Format(sQuery, sizeof(sQuery),
"%s\n" ...
"UNION ALL SELECT 3, COUNT(*)::FLOAT, CASE WHEN x.bonus THEN 1 ELSE 0 END, 0, 0, 0.0, '' FROM\n"...
" (SELECT map, track>0 as bonus FROM %splayertimes WHERE auth = %d AND style = %d GROUP BY map, track>0) x GROUP BY x.bonus\n"...
"UNION ALL SELECT 4, COUNT(*)::FLOAT, CASE WHEN track>0 THEN 1 ELSE 0 END, 0, 0, 0.0, '' FROM %swrs WHERE auth = %d AND style = %d GROUP BY track>0\n"...
"UNION ALL SELECT 5, COUNT(*)::FLOAT, CASE WHEN x.bonus THEN 1 ELSE 0 END, 0, 0, 0.0, '' FROM\n"...
" (SELECT map, track>0 as bonus FROM %smapzones WHERE type = 0 GROUP BY map, track>0) x GROUP BY x.bonus",
sQuery,
gS_MySQLPrefix, steamid, style,
gS_MySQLPrefix, steamid, style,
gS_MySQLPrefix
);
}
}
StrCat(sQuery, sizeof(sQuery), ";");

View File

@ -535,7 +535,20 @@ void UpdateClientCache(int client)
}
char sQuery[512];
FormatEx(sQuery, sizeof(sQuery), "SELECT %s, style, track, completions FROM %splayertimes WHERE map = '%s' AND auth = %d;", gI_Driver == Driver_mysql ? "REPLACE(FORMAT(time, 9), ',', '')" : "printf(\"%.9f\", time)", gS_MySQLPrefix, gS_Map, iSteamID);
char sTimeFormat[64];
if (gI_Driver == Driver_mysql)
{
strcopy(sTimeFormat, sizeof(sTimeFormat), "REPLACE(FORMAT(time, 9), ',', '')");
}
else if (gI_Driver == Driver_pgsql)
{
strcopy(sTimeFormat, sizeof(sTimeFormat), "time::text");
}
else // SQLite
{
strcopy(sTimeFormat, sizeof(sTimeFormat), "printf(\"%.9f\", time)");
}
FormatEx(sQuery, sizeof(sQuery), "SELECT %s, style, track, completions FROM %splayertimes WHERE map = '%s' AND auth = %d;", sTimeFormat, gS_MySQLPrefix, gS_Map, iSteamID);
QueryLog(gH_SQL, SQL_UpdateCache_Callback, sQuery, GetClientSerial(client), DBPrio_High);
}
@ -601,9 +614,24 @@ void UpdateWRCache(int client = -1)
char sQuery[512];
if (gI_Driver == Driver_mysql)
{
FormatEx(sQuery, sizeof(sQuery),
"SELECT style, track, auth, stage, time FROM `%sstagetimeswr` WHERE map = '%s';",
gS_MySQLPrefix, gS_Map);
}
else if (gI_Driver == Driver_pgsql)
{
FormatEx(sQuery, sizeof(sQuery),
"SELECT style, track, auth, stage, time FROM %sstagetimeswr WHERE map = '%s';",
gS_MySQLPrefix, gS_Map);
}
else // SQLite
{
FormatEx(sQuery, sizeof(sQuery),
"SELECT style, track, auth, stage, time FROM `%sstagetimeswr` WHERE map = '%s';",
gS_MySQLPrefix, gS_Map);
}
QueryLog(gH_SQL, SQL_UpdateWRStageTimes_Callback, sQuery);
}
@ -2750,10 +2778,27 @@ public void Shavit_OnFinish(int client, int style, float time, int jumps, int st
Transaction trans = new Transaction();
char query[512];
if (gI_Driver == Driver_mysql)
{
FormatEx(query, sizeof(query),
"DELETE FROM `%sstagetimeswr` WHERE style = %d AND track = %d AND map = '%s';",
gS_MySQLPrefix, style, track, gS_Map
);
}
else if (gI_Driver == Driver_pgsql)
{
FormatEx(query, sizeof(query),
"DELETE FROM %sstagetimeswr WHERE style = %d AND track = %d AND map = '%s';",
gS_MySQLPrefix, style, track, gS_Map
);
}
else // SQLite
{
FormatEx(query, sizeof(query),
"DELETE FROM `%sstagetimeswr` WHERE style = %d AND track = %d AND map = '%s';",
gS_MySQLPrefix, style, track, gS_Map
);
}
AddQueryLog(trans, query);
@ -2767,12 +2812,30 @@ public void Shavit_OnFinish(int client, int style, float time, int jumps, int st
continue;
}
FormatEx(query, sizeof(query),
char sInsertQuery[512];
if (gI_Driver == Driver_mysql)
{
FormatEx(sInsertQuery, sizeof(sInsertQuery),
"INSERT INTO `%sstagetimeswr` (`style`, `track`, `map`, `auth`, `time`, `stage`) VALUES (%d, %d, '%s', %d, %f, %d);",
gS_MySQLPrefix, style, track, gS_Map, iSteamID, fTime, i
);
}
else if (gI_Driver == Driver_pgsql)
{
FormatEx(sInsertQuery, sizeof(sInsertQuery),
"INSERT INTO %sstagetimeswr (style, track, map, auth, time, stage) VALUES (%d, %d, '%s', %d, %f, %d);",
gS_MySQLPrefix, style, track, gS_Map, iSteamID, fTime, i
);
}
else // SQLite
{
FormatEx(sInsertQuery, sizeof(sInsertQuery),
"INSERT INTO `%sstagetimeswr` (`style`, `track`, `map`, `auth`, `time`, `stage`) VALUES (%d, %d, '%s', %d, %f, %d);",
gS_MySQLPrefix, style, track, gS_Map, iSteamID, fTime, i
);
}
AddQueryLog(trans, query);
AddQueryLog(trans, sInsertQuery);
}
gH_SQL.Execute(trans, Trans_ReplaceStageTimes_Success, Trans_ReplaceStageTimes_Error, 0, DBPrio_High);
@ -2984,7 +3047,20 @@ public void Trans_ReplaceStageTimes_Error(Database db, any data, int numQueries,
void UpdateLeaderboards()
{
char sQuery[512];
FormatEx(sQuery, sizeof(sQuery), "SELECT p.style, p.track, %s, 0, p.id, p.auth, u.name FROM %splayertimes p LEFT JOIN %susers u ON p.auth = u.auth WHERE p.map = '%s' ORDER BY p.time ASC, p.date ASC;", gI_Driver == Driver_mysql ? "REPLACE(FORMAT(time, 9), ',', '')" : "printf(\"%.9f\", p.time)", gS_MySQLPrefix, gS_MySQLPrefix, gS_Map);
char sTimeFormat[64];
if (gI_Driver == Driver_mysql)
{
strcopy(sTimeFormat, sizeof(sTimeFormat), "REPLACE(FORMAT(time, 9), ',', '')");
}
else if (gI_Driver == Driver_pgsql)
{
strcopy(sTimeFormat, sizeof(sTimeFormat), "p.time::text");
}
else // SQLite
{
strcopy(sTimeFormat, sizeof(sTimeFormat), "printf(\"%.9f\", p.time)");
}
FormatEx(sQuery, sizeof(sQuery), "SELECT p.style, p.track, %s, 0, p.id, p.auth, u.name FROM %splayertimes p LEFT JOIN %susers u ON p.auth = u.auth WHERE p.map = '%s' ORDER BY p.time ASC, p.date ASC;", sTimeFormat, gS_MySQLPrefix, gS_MySQLPrefix, gS_Map);
QueryLog(gH_SQL, SQL_UpdateLeaderboards_Callback, sQuery);
}

View File

@ -1923,11 +1923,27 @@ void SetStart(int client, int track, bool anglesonly)
char query[1024];
if (gI_Driver == Driver_mysql)
{
FormatEx(query, sizeof(query),
"REPLACE INTO %sstartpositions (auth, track, map, pos_x, pos_y, pos_z, ang_x, ang_y, ang_z, angles_only) VALUES (%d, %d, '%s', %.03f, %.03f, %.03f, %.03f, %.03f, %.03f, %d);",
gS_MySQLPrefix, GetSteamAccountID(client), track, gS_Map,
gF_StartPos[client][track][0], gF_StartPos[client][track][1], gF_StartPos[client][track][2],
gF_StartAng[client][track][0], gF_StartAng[client][track][1], gF_StartAng[client][track][2], anglesonly);
}
else // PostgreSQL/SQLite
{
FormatEx(query, sizeof(query),
"INSERT INTO %sstartpositions (auth, track, map, pos_x, pos_y, pos_z, ang_x, ang_y, ang_z, angles_only) VALUES (%d, %d, '%s', %.03f, %.03f, %.03f, %.03f, %.03f, %.03f, %s) " ...
"ON CONFLICT (auth, track, map) DO UPDATE SET " ...
"pos_x = EXCLUDED.pos_x, pos_y = EXCLUDED.pos_y, pos_z = EXCLUDED.pos_z, " ...
"ang_x = EXCLUDED.ang_x, ang_y = EXCLUDED.ang_y, ang_z = EXCLUDED.ang_z, " ...
"angles_only = EXCLUDED.angles_only;",
gS_MySQLPrefix, GetSteamAccountID(client), track, gS_Map,
gF_StartPos[client][track][0], gF_StartPos[client][track][1], gF_StartPos[client][track][2],
gF_StartAng[client][track][0], gF_StartAng[client][track][1], gF_StartAng[client][track][2],
anglesonly ? "true" : "false");
}
QueryLog(gH_SQL, SQL_InsertStartPosition_Callback, query);
}