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
+26 -26
View File
@@ -235,34 +235,34 @@
return
var/list/sql_args[] = list(
":char_id" = char_id,
":uid" = UID,
":datetime" = datetime,
":notes" = notes,
":charges" = json_encode(charges),
":evidence" = json_encode(evidence),
":arbiters" = json_encode(arbiters),
":brig_sentence" = brig_sentence,
":fine" = fine,
":felony" = felony,
":created_by" = created_by,
":game_id" = game_id
"char_id" = char_id,
"uid" = UID,
"datetime" = datetime,
"notes" = notes,
"charges" = json_encode(charges),
"evidence" = json_encode(evidence),
"arbiters" = json_encode(arbiters),
"brig_sentence" = brig_sentence,
"fine" = fine,
"felony" = felony,
"created_by" = created_by,
"game_id" = game_id
)
//Insert a new entry into the db. Upate if a entry with the same chard_id and UID already exists
var/DBQuery/infraction_insert_query = dbcon.NewQuery({"INSERT INTO ss13_character_incidents
(char_id, UID, datetime, notes, charges, evidence, arbiters, brig_sentence, fine, felony, created_by, game_id)
VALUES
(:char_id, :uid, :datetime, :notes, :charges, :evidence, :arbiters, :brig_sentence, :fine, :felony, :created_by, :game_id)
(:char_id:, :uid:, :datetime:, :notes:, :charges:, :evidence:, :arbiters:, :brig_sentence:, :fine:, :felony:, :created_by:, :game_id:)
ON DUPLICATE KEY UPDATE
notes = :notes,
charges = :charges,
evidence = :evidence,
arbiters = :arbiters,
brig_sentence = :brig_sentence,
fine = :fine,
felony = :felony,
created_by = :created_by,
game_id = :game_id
notes = :notes:,
charges = :charges:,
evidence = :evidence:,
arbiters = :arbiters:,
brig_sentence = :brig_sentence:,
fine = :fine:,
felony = :felony:,
created_by = :created_by:,
game_id = :game_id:
"})
infraction_insert_query.Execute(sql_args)
@@ -282,14 +282,14 @@
log_debug("Infraction: Not deleted from the db - db_id 0")
var/list/sql_args[] = list(
":id" = db_id,
":deleted_by" = deleted_by
"id" = db_id,
"deleted_by" = deleted_by
)
//Insert a new entry into the db. Upate if a entry with the same chard_id and UID already exists
var/DBQuery/infraction_delete_query = dbcon.NewQuery({"UPDATE ss13_character_incidents
SET
deleted_by=:deleted_by,
deleted_by=:deleted_by:,
deleted_at=NOW()
WHERE
id = :id"})
id = :id:"})
infraction_delete_query.Execute(sql_args)