ports BSQL to go along with tools

There, now you can shut up about it not being ported you meme
This commit is contained in:
Poojawa
2018-09-05 23:45:53 -05:00
parent cfc2e061a5
commit 235bbae157
15 changed files with 516 additions and 185 deletions
@@ -28,4 +28,24 @@
/datum/config_entry/number/query_debug_log_timeout
config_entry_value = 70
min_val = 1
protection = CONFIG_ENTRY_LOCKED | CONFIG_ENTRY_HIDDEN
protection = CONFIG_ENTRY_LOCKED
deprecated_by = /datum/config_entry/number/blocking_query_timeout
/datum/config_entry/number/query_debug_log_timeout/DeprecationUpdate(value)
return value
/datum/config_entry/number/async_query_timeout
config_entry_value = 10
min_val = 0
protection = CONFIG_ENTRY_LOCKED
/datum/config_entry/number/blocking_query_timeout
config_entry_value = 5
min_val = 0
protection = CONFIG_ENTRY_LOCKED
/datum/config_entry/number/bsql_thread_limit
config_entry_value = 50
min_val = 1
/datum/config_entry/flag/bsql_debug
+120 -169
View File
@@ -5,32 +5,16 @@ SUBSYSTEM_DEF(dbcore)
init_order = INIT_ORDER_DBCORE
var/const/FAILED_DB_CONNECTION_CUTOFF = 5
var/const/Default_Cursor = 0
var/const/Client_Cursor = 1
var/const/Server_Cursor = 2
//conversions
var/const/TEXT_CONV = 1
var/const/RSC_FILE_CONV = 2
var/const/NUMBER_CONV = 3
//column flag values:
var/const/IS_NUMERIC = 1
var/const/IS_BINARY = 2
var/const/IS_NOT_NULL = 4
var/const/IS_PRIMARY_KEY = 8
var/const/IS_UNSIGNED = 16
var/schema_mismatch = 0
var/db_minor = 0
var/db_major = 0
// TODO: Investigate more recent type additions and see if I can handle them. - Nadrew
var/_db_con// This variable contains a reference to the actual database connection.
var/failed_connections = 0
var/last_error
var/list/active_queries = list()
/datum/controller/subsystem/dbcore/PreInit()
if(!_db_con)
_db_con = _dm_db_new_con()
var/datum/BSQL_Connection/connection
var/datum/BSQL_Operation/connectOperation
/datum/controller/subsystem/dbcore/Initialize()
//We send warnings to the admins during subsystem init, as the clients will be New'd and messages
@@ -54,7 +38,8 @@ SUBSYSTEM_DEF(dbcore)
return
/datum/controller/subsystem/dbcore/Recover()
_db_con = SSdbcore._db_con
connection = SSdbcore.connection
connectOperation = SSdbcore.connectOperation
/datum/controller/subsystem/dbcore/Shutdown()
//This is as close as we can get to the true round end before Disconnect() without changing where it's called, defeating the reason this is a subsystem
@@ -64,13 +49,14 @@ SUBSYSTEM_DEF(dbcore)
qdel(query_round_shutdown)
if(IsConnected())
Disconnect()
world.BSQL_Shutdown()
//nu
/datum/controller/subsystem/dbcore/can_vv_get(var_name)
return var_name != NAMEOF(src, _db_con) && var_name != NAMEOF(src, active_queries) && ..()
return var_name != NAMEOF(src, connection) && var_name != NAMEOF(src, active_queries) && var_name != NAMEOF(src, connectOperation) && ..()
/datum/controller/subsystem/dbcore/vv_edit_var(var_name, var_value)
if(var_name == "_db_con")
if(var_name == NAMEOF(src, connection) || var_name == NAMEOF(src, connectOperation))
return FALSE
return ..()
@@ -90,17 +76,31 @@ SUBSYSTEM_DEF(dbcore)
var/address = CONFIG_GET(string/address)
var/port = CONFIG_GET(number/port)
_dm_db_connect(_db_con, "dbi:mysql:[db]:[address]:[port]", user, pass, Default_Cursor, null)
. = IsConnected()
connection = new /datum/BSQL_Connection(BSQL_CONNECTION_TYPE_MARIADB, CONFIG_GET(number/async_query_timeout), CONFIG_GET(number/blocking_query_timeout), CONFIG_GET(number/bsql_thread_limit))
var/error
if(QDELETED(connection))
connection = null
error = last_error
else
SSdbcore.last_error = null
connectOperation = connection.BeginConnect(address, port, user, pass, db)
if(SSdbcore.last_error)
CRASH(SSdbcore.last_error)
UNTIL(connectOperation.IsComplete())
error = connectOperation.GetError()
. = !error
if (!.)
log_sql("Connect() failed | [ErrorMsg()]")
last_error = error
log_sql("Connect() failed | [error]")
++failed_connections
QDEL_NULL(connection)
QDEL_NULL(connectOperation)
/datum/controller/subsystem/dbcore/proc/CheckSchemaVersion()
if(CONFIG_GET(flag/sql_enabled))
if(SSdbcore.Connect())
if(Connect())
log_world("Database connection established.")
var/datum/DBQuery/query_db_version = SSdbcore.NewQuery("SELECT major, minor FROM [format_table_name("schema_revision")] ORDER BY date DESC LIMIT 1")
var/datum/DBQuery/query_db_version = NewQuery("SELECT major, minor FROM [format_table_name("schema_revision")] ORDER BY date DESC LIMIT 1")
query_db_version.Execute()
if(query_db_version.NextRow())
db_major = text2num(query_db_version.item[1])
@@ -146,27 +146,36 @@ SUBSYSTEM_DEF(dbcore)
/datum/controller/subsystem/dbcore/proc/Disconnect()
failed_connections = 0
return _dm_db_close(_db_con)
QDEL_NULL(connectOperation)
QDEL_NULL(connection)
/datum/controller/subsystem/dbcore/proc/IsConnected()
if(!CONFIG_GET(flag/sql_enabled))
return FALSE
return _dm_db_is_connected(_db_con)
//block until any connect operations finish
var/datum/BSQL_Connection/_connection = connection
var/datum/BSQL_Operation/op = connectOperation
UNTIL(QDELETED(_connection) || op.IsComplete())
return !QDELETED(connection) && !op.GetError()
/datum/controller/subsystem/dbcore/proc/Quote(str)
return _dm_db_quote(_db_con, str)
if(connection)
return connection.Quote(str)
/datum/controller/subsystem/dbcore/proc/ErrorMsg()
if(!CONFIG_GET(flag/sql_enabled))
return "Database disabled by configuration"
return _dm_db_error_msg(_db_con)
return last_error
/datum/controller/subsystem/dbcore/proc/NewQuery(sql_query, cursor_handler = Default_Cursor)
/datum/controller/subsystem/dbcore/proc/ReportError(error)
last_error = error
/datum/controller/subsystem/dbcore/proc/NewQuery(sql_query)
if(IsAdminAdvancedProcCall())
log_admin_private("ERROR: Advanced admin proc call led to sql query: [sql_query]. Query has been blocked")
message_admins("ERROR: Advanced admin proc call led to sql query. Query has been blocked")
return FALSE
return new /datum/DBQuery(sql_query, src, cursor_handler)
return new /datum/DBQuery(sql_query, connection)
/*
Takes a list of rows (each row being an associated list of column => value) and inserts them via a single mass query.
@@ -180,7 +189,7 @@ Delayed insert mode was removed in mysql 7 and only works with MyISAM type table
It was included because it is still supported in mariadb.
It does not work with duplicate_key and the mysql server ignores it in those cases
*/
/datum/controller/subsystem/dbcore/proc/MassInsert(table, list/rows, duplicate_key = FALSE, ignore_errors = FALSE, delayed = FALSE, warn = FALSE)
/datum/controller/subsystem/dbcore/proc/MassInsert(table, list/rows, duplicate_key = FALSE, ignore_errors = FALSE, delayed = FALSE, warn = FALSE, async = TRUE)
if (!table || !rows || !istype(rows))
return
var/list/columns = list()
@@ -230,34 +239,30 @@ Delayed insert mode was removed in mysql 7 and only works with MyISAM type table
sqlrowlist = " [sqlrowlist.Join(",\n ")]"
var/datum/DBQuery/Query = NewQuery("INSERT[delayed][ignore_errors] INTO [table]\n([columns.Join(", ")])\nVALUES\n[sqlrowlist]\n[duplicate_key]")
if (warn)
. = Query.warn_execute()
. = Query.warn_execute(async)
else
. = Query.Execute()
. = Query.Execute(async)
qdel(Query)
/datum/DBQuery
var/sql // The sql query being executed.
var/default_cursor
var/list/columns //list of DB Columns populated by Columns()
var/list/conversions
var/list/item //list of data values populated by NextRow()
var/last_activity
var/last_activity_time
var/datum/controller/subsystem/dbcore/db_connection
var/_db_query
/datum/DBQuery/New(sql_query, datum/controller/subsystem/dbcore/connection_handler, cursor_handler)
var/last_error
var/skip_next_is_complete
var/in_progress
var/datum/BSQL_Connection/connection
var/datum/BSQL_Operation/Query/query
/datum/DBQuery/New(sql_query, datum/BSQL_Connection/connection)
SSdbcore.active_queries[src] = TRUE
Activity("Created")
if(sql_query)
sql = sql_query
if(connection_handler)
db_connection = connection_handler
if(cursor_handler)
default_cursor = cursor_handler
item = list()
_db_query = _dm_db_new_query()
src.connection = connection
sql = sql_query
/datum/DBQuery/Destroy()
Close()
@@ -268,147 +273,93 @@ Delayed insert mode was removed in mysql 7 and only works with MyISAM type table
//fuck off kevinz
return FALSE
/datum/DBQuery/proc/SetQuery(new_sql)
if(in_progress)
CRASH("Attempted to set new sql while waiting on active query")
Close()
sql = new_sql
/datum/DBQuery/proc/Activity(activity)
last_activity = activity
last_activity_time = world.time
/datum/DBQuery/proc/warn_execute()
. = Execute()
/datum/DBQuery/proc/warn_execute(async = FALSE)
. = Execute(async)
if(!.)
to_chat(usr, "<span class='danger'>A SQL error occurred during this operation, check the server logs.</span>")
/datum/DBQuery/proc/SetQuery(new_sql)
Activity("SetQuery")
Close()
sql = new_sql
/datum/DBQuery/proc/Execute(sql_query = sql, cursor_handler = default_cursor, log_error = TRUE)
/datum/DBQuery/proc/Execute(async = FALSE, log_error = TRUE)
Activity("Execute")
if(in_progress)
CRASH("Attempted to start a new query while waiting on the old one")
if(QDELETED(connection))
last_error = "No connection!"
return FALSE
var/start_time
var/timeout = CONFIG_GET(number/query_debug_log_timeout)
if(timeout)
var/timed_out
if(!async)
start_time = REALTIMEOFDAY
Close()
. = _dm_db_execute(_db_query, sql_query, db_connection._db_con, cursor_handler, null)
query = connection.BeginQuery(sql)
if(!async)
timed_out = !query.WaitForCompletion()
else
in_progress = TRUE
UNTIL(query.IsComplete())
in_progress = FALSE
skip_next_is_complete = TRUE
var/error = QDELETED(query) ? "Query object deleted!" : query.GetError()
last_error = error
. = !error
if(!. && log_error)
log_sql("[ErrorMsg()] | Query used: [sql]")
if(timeout)
if((REALTIMEOFDAY - start_time) > timeout)
log_query_debug("Query execution started at [start_time]")
log_query_debug("Query execution ended at [REALTIMEOFDAY]")
log_query_debug("Possible slow query timeout detected.")
log_query_debug("Query used: [sql]")
slow_query_check()
log_sql("[error] | Query used: [sql]")
if(!async && timed_out)
log_query_debug("Query execution started at [start_time]")
log_query_debug("Query execution ended at [REALTIMEOFDAY]")
log_query_debug("Slow query timeout detected.")
log_query_debug("Query used: [sql]")
slow_query_check()
/datum/DBQuery/proc/slow_query_check()
message_admins("HEY! A database query may have timed out. Did the server just hang? <a href='?_src_=holder;[HrefToken()];slowquery=yes'>\[YES\]</a>|<a href='?_src_=holder;[HrefToken()];slowquery=no'>\[NO\]</a>")
message_admins("HEY! A database query timed out. Did the server just hang? <a href='?_src_=holder;[HrefToken()];slowquery=yes'>\[YES\]</a>|<a href='?_src_=holder;[HrefToken()];slowquery=no'>\[NO\]</a>")
/datum/DBQuery/proc/NextRow()
/datum/DBQuery/proc/NextRow(async)
Activity("NextRow")
return _dm_db_next_row(_db_query,item,conversions)
UNTIL(!in_progress)
if(!skip_next_is_complete)
if(!async)
query.WaitForCompletion()
else
in_progress = TRUE
UNTIL(query.IsComplete())
in_progress = FALSE
else
skip_next_is_complete = FALSE
/datum/DBQuery/proc/RowsAffected()
return _dm_db_rows_affected(_db_query)
last_error = query.GetError()
var/list/results = query.CurrentRow()
. = results != null
/datum/DBQuery/proc/RowCount()
return _dm_db_row_count(_db_query)
item.Cut()
//populate item array
for(var/I in results)
item += results[I]
/datum/DBQuery/proc/ErrorMsg()
return _dm_db_error_msg(_db_query)
/datum/DBQuery/proc/Columns()
if(!columns)
columns = _dm_db_columns(_db_query, /datum/DBColumn)
return columns
/datum/DBQuery/proc/GetRowData()
var/list/columns = Columns()
var/list/results
if(columns.len)
results = list()
for(var/C in columns)
results+=C
var/datum/DBColumn/cur_col = columns[C]
results[C] = src.item[(cur_col.position+1)]
return results
return last_error
/datum/DBQuery/proc/Close()
item.Cut()
columns = null
conversions = null
return _dm_db_close(_db_query)
QDEL_NULL(query)
/datum/DBQuery/proc/Quote(str)
return db_connection.Quote(str)
/world/BSQL_Debug(message)
if(!CONFIG_GET(flag/bsql_debug))
return
/datum/DBQuery/proc/SetConversion(column,conversion)
if(istext(column))
column = columns.Find(column)
if(!conversions)
conversions = new /list(column)
else if(conversions.len < column)
conversions.len = column
conversions[column] = conversion
/datum/DBColumn
var/name
var/table
var/position //1-based index into item data
var/sql_type
var/flags
var/length
var/max_length
//types
var/const/TINYINT = 1
var/const/SMALLINT = 2
var/const/MEDIUMINT = 3
var/const/INTEGER = 4
var/const/BIGINT = 5
var/const/DECIMAL = 6
var/const/FLOAT = 7
var/const/DOUBLE = 8
var/const/DATE = 9
var/const/DATETIME = 10
var/const/TIMESTAMP = 11
var/const/TIME = 12
var/const/STRING = 13
var/const/BLOB = 14
/datum/DBColumn/New(name_handler, table_handler, position_handler, type_handler, flag_handler, length_handler, max_length_handler)
name = name_handler
table = table_handler
position = position_handler
sql_type = type_handler
flags = flag_handler
length = length_handler
max_length = max_length_handler
/datum/DBColumn/proc/SqlTypeName(type_handler = sql_type)
switch(type_handler)
if(TINYINT)
return "TINYINT"
if(SMALLINT)
return "SMALLINT"
if(MEDIUMINT)
return "MEDIUMINT"
if(INTEGER)
return "INTEGER"
if(BIGINT)
return "BIGINT"
if(FLOAT)
return "FLOAT"
if(DOUBLE)
return "DOUBLE"
if(DATE)
return "DATE"
if(DATETIME)
return "DATETIME"
if(TIMESTAMP)
return "TIMESTAMP"
if(TIME)
return "TIME"
if(STRING)
return "STRING"
if(BLOB)
return "BLOB"
//strip sensitive stuff
if(findtext(message, ": CreateConnection("))
message = "CreateConnection CENSORED"
log_sql("BSQL_DEBUG: [message]")