Better SQL prepared statements (#2474)

The system used to be of complexity O(n^2). Essentially two for loops running per every argument. Which ended up being surprisingly slow (there were instances where I saw the argument parser as using quite a lot of CPU time).

This replaces it with a more linear algorithm. It's somewhere near O(n) where n is the length of the unparsed query. Which is more stable and faaaster. This comes with two changes, however:

Parameters inside the query now have to be delimited from both sides with : (colons). The alternative to this would be to use something like $n or just assume that space marks the end of a marker. Only the former is workable, the latter would break a few queries already.
Arguments in the argument array no longer have to be prefixed by : (colons). So, while in the query you would write :thing:, you'd initialize the array of args as: list("thing" = somevar). It could be made to work without it, but eh, I think this is fine.
Argument validation is slightly weaker. What I mean by this is that with the old system, unused keys would result in an error. This is no longer a thing. Missing keys will still result in an error, however.
One more improvement: double delimiting removes an edge case where if key A partially covers key B, depending on the order, key A would mangle key B.
Updated and tested all queries that I could find. So this should be good.
This commit is contained in:
skull132
2017-05-29 21:17:41 +03:00
committed by GitHub
parent b88d6aaadb
commit a3ec0cf45d
36 changed files with 366 additions and 333 deletions
+12 -12
View File
@@ -15,8 +15,8 @@
var/bad_data = BAD_CKEY|BAD_IP|BAD_CID
var/DBQuery/original_ban = dbcon.NewQuery("SELECT ckey, ip, computerid FROM ss13_ban WHERE id = :ban_id")
original_ban.Execute(list(":ban_id" = ban_id))
var/DBQuery/original_ban = dbcon.NewQuery("SELECT ckey, ip, computerid FROM ss13_ban WHERE id = :ban_id:")
original_ban.Execute(list("ban_id" = ban_id))
if (original_ban.NextRow())
var/original_ckey = original_ban.item[1]
@@ -30,8 +30,8 @@
bad_data &= ~BAD_CID
if (bad_data)
var/DBQuery/mirrored_bans = dbcon.NewQuery("SELECT player_ckey, ban_mirror_ip, ban_mirror_computerid FROM ss13_ban_mirrors WHERE ban_id = :ban_id")
mirrored_bans.Execute(list(":ban_id" = ban_id))
var/DBQuery/mirrored_bans = dbcon.NewQuery("SELECT player_ckey, ban_mirror_ip, ban_mirror_computerid FROM ss13_ban_mirrors WHERE ban_id = :ban_id:")
mirrored_bans.Execute(list("ban_id" = ban_id))
while (mirrored_bans.NextRow())
var/mirrored_ckey = mirrored_bans.item[1]
@@ -66,8 +66,8 @@
log_misc("Ban database connection failure while attempting to check mirrors. Key passed for mirror checking: [ckey].")
return null
var/DBQuery/initial_query = dbcon.NewQuery("SELECT DISTINCT ban_id FROM ss13_ban_mirrors WHERE player_ckey = :ckey OR ban_mirror_ip = :address OR ban_mirror_computerid = :computerid")
initial_query.Execute(list(":ckey" = ckey, ":address" = address, ":computerid" = computer_id))
var/DBQuery/initial_query = dbcon.NewQuery("SELECT DISTINCT ban_id FROM ss13_ban_mirrors WHERE player_ckey = :ckey: OR ban_mirror_ip = :address: OR ban_mirror_computerid = :computerid:")
initial_query.Execute(list("ckey" = ckey, "address" = address, "computerid" = computer_id))
var/list/ban_ids = list()
@@ -78,8 +78,8 @@
if (!ban_ids.len)
return null
var/DBQuery/search_query = dbcon.NewQuery("SELECT id FROM ss13_ban WHERE id IN :vars AND (bantype = 'PERMABAN' OR (bantype = 'TEMPBAN' AND expiration_time > Now())) AND isnull(unbanned)")
search_query.Execute(list(":vars" = ban_ids))
var/DBQuery/search_query = dbcon.NewQuery("SELECT id FROM ss13_ban WHERE id IN :vars: AND (bantype = 'PERMABAN' OR (bantype = 'TEMPBAN' AND expiration_time > Now())) AND isnull(unbanned)")
search_query.Execute(list("vars" = ban_ids))
var/list/active_bans = list()
@@ -103,8 +103,8 @@
if (!dbcon.IsConnected())
return null
var/DBQuery/query = dbcon.NewQuery("SELECT ban_mirror_id, player_ckey, ban_mirror_ip, ban_mirror_computerid, date(ban_mirror_datetime) as datetime FROM ss13_ban_mirrors WHERE ban_id = :ban_id")
query.Execute(list(":ban_id" = ban_id))
var/DBQuery/query = dbcon.NewQuery("SELECT ban_mirror_id, player_ckey, ban_mirror_ip, ban_mirror_computerid, date(ban_mirror_datetime) as datetime FROM ss13_ban_mirrors WHERE ban_id = :ban_id:")
query.Execute(list("ban_id" = ban_id))
var/mirrors[] = list()
while (query.NextRow())
@@ -122,8 +122,8 @@
if (!ckey || !address || !computer_id || !ban_id)
return
var/DBQuery/new_mirror = dbcon.NewQuery("INSERT INTO ss13_ban_mirrors (ban_id, player_ckey, ban_mirror_ip, ban_mirror_computerid, ban_mirror_datetime) VALUES (:ban_id, :ckey, :address, :computerid, NOW())")
new_mirror.Execute(list(":ban_id" = ban_id, ":ckey" = ckey, ":address" = address, ":computerid" = computer_id))
var/DBQuery/new_mirror = dbcon.NewQuery("INSERT INTO ss13_ban_mirrors (ban_id, player_ckey, ban_mirror_ip, ban_mirror_computerid, ban_mirror_datetime) VALUES (:ban_id:, :ckey:, :address:, :computerid:, NOW())")
new_mirror.Execute(list("ban_id" = ban_id, "ckey" = ckey, "address" = address, "computerid" = computer_id))
log_misc("Mirrored ban #[ban_id] for player [ckey] from [address]-[computer_id].")
+2 -2
View File
@@ -515,8 +515,8 @@
output += "</tr>"
if (bantype in list("PERMABAN", "TEMPBAN"))
var/mirror_count = 0
var/DBQuery/get_mirrors = dbcon.NewQuery("SELECT ban_mirror_id FROM ss13_ban_mirrors WHERE ban_id = :ban_id")
get_mirrors.Execute(list(":ban_id" = text2num(banid)))
var/DBQuery/get_mirrors = dbcon.NewQuery("SELECT ban_mirror_id FROM ss13_ban_mirrors WHERE ban_id = :ban_id:")
get_mirrors.Execute(list("ban_id" = text2num(banid)))
while (get_mirrors.NextRow())
mirror_count++