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:
@@ -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]")
|
||||
|
||||
Reference in New Issue
Block a user