Files
Aurora.3/code/modules/client/preference_setup/preferences_sql.dm
Erki Meinberg 283f63d098 Preferences fixes, ahoy! (#3045)
Fixes #1670
Fixes #1780
Fixes #1781
Fixes #1493
2017-07-21 20:50:59 +03:00

259 lines
8.6 KiB
Plaintext

// Set this to 1 if you want to debug the SQL saves.
// I got tired as hell from writing these debug lines and deleting them later.
/*
* A proc for dynamically loading a character from the database.
* See the category items for procs on what pieces are given.
*
* Each query is cached as well, after generation. Only things recompiled are the arguments and the finalized query text (with arguments in it).
*/
/datum/category_group/player_setup_category/proc/handle_sql_loading(var/role_type)
var/static/list/query_cache
// We aren't loading this category. Bye.
if (role_type && !(sql_role & role_type))
#ifdef SQL_PREF_DEBUG
log_debug("SQL CHARACTER LOAD: Bad role_type, returned. Looking for: [role_type], had: [sql_role]. Conjunction: [sql_role & role_type].")
#endif
return
if (isnull(query_cache))
query_cache = list()
// This handles the generation of queries. One per category, total of 5 UNLESS one category wants to query more than one table.
// It uses like, 3+ levels of lists. But it works, and is only run once per category in the global sense. So technically, efficient!
// :D - Skull132
if (!query_cache.len || isnull(query_cache[type]))
query_cache[type] = list()
var/list/tables = list()
// First, consolidate the different lists into one.
for (var/datum/category_item/player_setup_item/PI in items)
var/list/pi_tables = PI.gather_load_query()
// Expected layout: list("table A name" = list("vars" = list("column name" = "var name"), "args" = list()),
// "table B name" = list("vars" = list(), "args" = list()))
for (var/table in pi_tables)
var/list/A = pi_tables[table]
if (isnull(tables[table]))
tables[table] = list("vars" = list(), "args" = list())
tables[table]["vars"] |= A["vars"]
tables[table]["args"] |= A["args"]
// Second, create the queries and save them.
for (var/table in tables)
var/query = "SELECT "
var/list/var_names = tables[table]["vars"]
var/count = var_names.len
// Process the variables and rows we want.
var/i = 1
for (var/name in var_names)
query += name
if (!isnull(var_names[name]))
var/new_name = var_names[name]
var_names.Remove(name)
var_names.Insert(i, new_name)
if (i != count)
query += ", "
else
query += " "
i++
query += "FROM [table] WHERE "
// Process the args.
var/list/arg_names = tables[table]["args"]
count = arg_names.len
for (i = 1, i <= count, i++)
query += "[arg_names[i]] = :[arg_names[i]]:"
if (i != count)
query += " AND "
else
query += ";"
#ifdef SQL_PREF_DEBUG
log_debug("SQL CHARACTER LOAD: Cached query [query] with variables [json_encode(var_names)]")
#endif
// Save it.
query_cache[type][query] = var_names
// Need to typecast due to reasons.
var/datum/category_collection/player_setup_collection/cc = collection
// Actually utilize the queries.
var/list/arg_list = gather_load_parameters()
#ifdef SQL_PREF_DEBUG
log_debug("SQL CHARACTER LOAD: Started loading with arguments: [json_encode(arg_list)]. Role type: [role_type]")
#endif
for (var/query_text in query_cache[type])
var/DBQuery/query = dbcon.NewQuery(query_text)
query.Execute(arg_list)
if (query.ErrorMsg())
error("SQL CHARACTER LOAD: SQL query error: [query.ErrorMsg()]")
log_debug("SQL CHARACTER LOAD: SQL query error: [query.ErrorMsg()]")
log_debug("SQL CHARACTER LOAD: query args: [json_encode(arg_list)]")
continue
#ifdef SQL_PREF_DEBUG
else
log_debug("SQL CHARACTER LOAD: Successfully executed query: [query_text]")
#endif
// Each query should only return exactly 1 row.
var/list/var_names = query_cache[type][query_text]
if (query.NextRow())
for (var/i = 1, i <= var_names.len, i++)
var/list/layers = splittext(var_names[i], "/")
try
if (layers.len == 1)
cc.preferences.vars[var_names[i]] = query.item[i]
else
cc.preferences.vars[layers[1]][layers[2]] = query.item[i]
catch(var/exception/e)
error("SQL CHARACTER LOAD: bad variable name: [e.name]")
log_debug("SQL CHARACTER LOAD: bad variable name: [e.name]")
log_debug("SQL CHARACTER LOAD: var name: [var_names[i]]")
/datum/category_group/player_setup_category/proc/gather_load_parameters()
var/list/arg_list = list()
for (var/datum/category_item/player_setup_item/PI in items)
arg_list |= PI.gather_load_parameters()
return arg_list
/*
* A proc for dynamically inserting new character and preference records into the database.
* See the category items for procs on what pieces are given.
*
* Each query is cached as well, after generation. Only things recompiled are the arguments and the finalized query text (with arguments in it).
*/
/datum/category_group/player_setup_category/proc/handle_sql_saving(var/role_type)
var/static/list/query_cache
// We aren't loading this category. Bye.
if (role_type && !(sql_role & role_type))
#ifdef SQL_PREF_DEBUG
log_debug("SQL CHARACTER SAVE: Bad role_type, returned. Looking for: [role_type], had: [sql_role]. Conjunction: [sql_role & role_type].")
#endif
return
if (isnull(query_cache))
query_cache = list()
// This handles the generation of queries. One per category, total of 5 UNLESS one category wants to query more than one table.
// It uses like, 3+ levels of lists. But it works, and is only run once per category in the global sense. So technically, efficient!
// :D - Skull132
if (!query_cache.len || isnull(query_cache[type]))
query_cache[type] = list()
var/list/tables = list()
// First, consolidate the different lists into one.
for (var/datum/category_item/player_setup_item/PI in items)
var/list/pi_tables = PI.gather_save_query()
// Expected layout: list("table A name" = list("var1" = 1, "var2" = 0, "var3" = 1),
// "table B name" = list("var4" = 0, "var5" = 0, "var6" = 0))
for (var/table in pi_tables)
var/list/A = pi_tables[table]
if (isnull(tables[table]))
tables[table] = list()
tables[table] |= A
// Second, create the queries and save them.
for (var/table in tables)
var/query = "INSERT INTO [table] ("
var/list/var_names = tables[table]
query += "[jointext(var_names, ", ")]) VALUES ("
// Process the args.
var/list/arg_names = list()
for (var/variable in var_names)
arg_names += ":[variable]:"
query += "[jointext(arg_names, ", ")]) ON DUPLICATE KEY UPDATE"
var/i = 1
for (var/variable in var_names)
if (isnull(var_names[variable]))
query += " [variable] = [arg_names[i]]"
if (i < var_names.len)
query += ","
i++
// Remove any potentially damaging commas from the end.
query = replacetext(query, ",", "", length(query) - 1)
#ifdef SQL_PREF_DEBUG
log_debug("SQL CHARACTER SAVE: Cached query [query].")
#endif
// Save it.
query_cache[type] += query
// Actually utilize the queries.
var/list/arg_list = gather_save_parameters()
#ifdef SQL_PREF_DEBUG
log_debug("SQL CHARACTER SAVE: Started saving with arguments: [json_encode(arg_list)]. Role type: [role_type]")
#endif
// Typecast the collection so we can access its preferences var.
var/datum/category_collection/player_setup_collection/cc = collection
for (var/query_text in query_cache[type])
var/DBQuery/query = dbcon.NewQuery(query_text)
query.Execute(arg_list)
if (query.ErrorMsg())
error("SQL CHARACTER SAVE: SQL query error: [query.ErrorMsg()]")
log_debug("SQL CHARACTER SAVE: SQL query error: [query.ErrorMsg()]")
log_debug("SQL CHARACTER SAVE: query args: [json_encode(arg_list)]")
continue
#ifdef SQL_PREF_DEBUG
else
log_debug("SQL CHARACTER SAVE: Successfully executed query: [query_text]")
#endif
if ((role_type & SQL_CHARACTER) && !cc.preferences.current_character)
// No current character, means we're doing insert queries.
// Quickly nab the new ID and substitute it within the args.
query = dbcon.NewQuery("SELECT LAST_INSERT_ID() AS new_id")
query.Execute()
if (query.NextRow())
arg_list["id"] = text2num(query.item[1])
arg_list["char_id"] = text2num(query.item[1])
cc.preferences.current_character = text2num(query.item[1])
#ifdef SQL_PREF_DEBUG
log_debug("SQL CHARACTER SAVE: Successfully set character ID to [cc.preferences.current_character]")
#endif
else
error("SQL CHARACTER SAVE: New ID was not recovered.")
log_debug("SQL CHARACTER SAVE: New ID was not recovered.")
if (query.ErrorMsg())
error("SQL CHARACTER SAVE: SQL query error from last_insert_id: [query.ErrorMsg()]")
log_debug("SQL CHARACTER SAVE: SQL query error from last_insert_id: [query.ErrorMsg()]")
/datum/category_group/player_setup_category/proc/gather_save_parameters()
var/list/arg_list = list()
for (var/datum/category_item/player_setup_item/PI in items)
arg_list |= PI.gather_save_parameters()
return arg_list