Ports TG's BSQL library (#26455)

* Ports TG's BSQL

* write perms

* my mistake

* Missing migrations, fixes mistakes, removes unneeded logs

* Missing migrations, fixes mistakes, removes unneeded logs

* haha

* Final missing migration, actually fix runtime

* Fucked up this doesn't throw a warning

* sql fixes; polls
This commit is contained in:
ShiftyRail
2020-05-19 18:35:10 +02:00
committed by GitHub
parent 06b63e6654
commit bbd746ae42
61 changed files with 1638 additions and 883 deletions

View File

@@ -1,5 +1,6 @@
language: generic
dist: bionic
os: linux
dist: xenial
sudo: false
env:
@@ -53,27 +54,36 @@ matrix:
env:
DM_UNIT_TESTS="1"
addons:
mariadb: '10.2'
apt:
sources:
- ubuntu-toolchain-r-test
packages:
- libstdc++6:i386
- libc6-i386
- libgcc1:i386
- gcc-multilib
- g++-7
- g++-7-multilib
- rustc
- cargo
- moreutils
- libmariadb-client-lgpl-dev:i386
- libmariadbd-dev
cache:
directories:
- $HOME/BYOND-${BYOND_MAJOR}.${BYOND_MINOR}
- $TRAVIS_BUILD_DIR/libvg/target
- $HOME/.cargo
- $HOME/libmariadb
install:
- tools/travis/install-byond.sh
- tools/travis/install_libmariadb.sh
- source $HOME/BYOND-${BYOND_MAJOR}.${BYOND_MINOR}/byond/bin/byondsetup
- cd libvg
# --jobs 1 to prevent threading problems with the BYOND crate.
- cargo test --jobs 1 --verbose
- cd -
- tools/travis/build.py
- tools/travis/build_bsql.sh
- cp tools/travis/config/config.txt config/
- tools/travis/run_tests.py 2>&1 | tee /dev/stderr | sponge | awk '/UNIT TEST FAIL/ { exit 1 }'

BIN
BSQL.dll Normal file

Binary file not shown.

126
__DEFINES/BSQL.dm Normal file
View File

@@ -0,0 +1,126 @@
//BSQL - DMAPI
#define BSQL_VERSION "v1.3.0.0"
//types of connections
#define BSQL_CONNECTION_TYPE_MARIADB "MySql"
#define BSQL_CONNECTION_TYPE_SQLSERVER "SqlServer"
#define BSQL_DEFAULT_TIMEOUT 5
#define BSQL_DEFAULT_THREAD_LIMIT 50
//Call this before rebooting or shutting down your world to clean up gracefully. This invalidates all active connection and operation datums
/world/proc/BSQL_Shutdown()
return
/*
Called whenever a library call is made with verbose information, override and do with as you please
message: English debug message
*/
/world/proc/BSQL_Debug(msg)
return
/*
Create a new database connection, does not perform the actual connect
connection_type: The BSQL connection_type to use
asyncTimeout: The timeout to use for normal operations, 0 for infinite, defaults to BSQL_DEFAULT_TIMEOUT
blockingTimeout: The timeout to use for blocking operations, must be less than or equal to asyncTimeout, 0 for infinite, defaults to asyncTimeout
threadLimit: The limit of additional threads BSQL will run simultaneously, defaults to BSQL_DEFAULT_THREAD_LIMIT
*/
/datum/BSQL_Connection/New(connection_type, asyncTimeout, blockingTimeout, threadLimit)
return ..()
/*
Starts an operation to connect to a database. Should only have 1 successful call
ipaddress: The ip/hostname of the target server
port: The port of the target server
username: The username to login to the target server
password: The password for the target server
database: Optional database to connect to. Must be used when trying to do database operations, `USE x` is not sufficient
Returns: A /datum/BSQL_Operation representing the connection or null if an error occurred
*/
/datum/BSQL_Connection/proc/BeginConnect(ipaddress, port, username, password, database)
return
/*
Properly quotes a string for use by the database. The connection must be open for this proc to succeed
str: The string to quote
Returns: The string quoted on success, null on error
*/
/datum/BSQL_Connection/proc/Quote(str)
return
/*
Starts an operation for a query
query: The text of the query. Only one query allowed per invocation, no semicolons
Returns: A /datum/BSQL_Operation/Query representing the running query and subsequent result set or null if an error occurred
Note for MariaDB: The underlying connection is pooled. In order to use connection state based properties (i.e. LAST_INSERT_ID()) you can guarantee multiple queries will use the same connection by running BSQL_DEL_CALL(query) on the finished /datum/BSQL_Operation/Query and then creating the next one with another call to BeginQuery() with no sleeps in between
*/
/datum/BSQL_Connection/proc/BeginQuery(query)
return
/*
Checks if the operation is complete. This, in some cases must be called multiple times with false return before a result is present regardless of timespan. For best performance check it once per tick
Returns: TRUE if the operation is complete, FALSE if it's not, null on error
*/
/datum/BSQL_Operation/proc/IsComplete()
return
/*
Blocks the entire game until the given operation completes. IsComplete should not be checked after calling this to avoid potential side effects.
Returns: TRUE on success, FALSE if the operation wait time exceeded the connection's blockingTimeout setting
*/
/datum/BSQL_Operation/proc/WaitForCompletion()
return
/*
Get the error message associated with an operation. Should not be used while IsComplete() returns FALSE
Returns: The error message, if any. null otherwise
*/
/datum/BSQL_Operation/proc/GetError()
return
/*
Get the error code associated with an operation. Should not be used while IsComplete() returns FALSE
Returns: The error code, if any. null otherwise
*/
/datum/BSQL_Operation/proc/GetErrorCode()
return
/*
Gets an associated list of column name -> value representation of the most recent row in the query. Only valid if IsComplete() returns TRUE. If this returns null and no errors are present there are no more results in the query. Important to note that once IsComplete() returns TRUE it must not be called again without checking this or the row values may be lost
Returns: An associated list of column name -> value for the row. Values will always be either strings or null
*/
/datum/BSQL_Operation/Query/proc/CurrentRow()
return
/*
Code configuration options below
Define this to avoid modifying this file but the following defines must be declared somewhere else before BSQL/includes.dm is included
*/
#ifndef BSQL_EXTERNAL_CONFIGURATION
//Modify this if you disagree with byond's GC schemes. Ensure this is called for all connections and operations when they are deleted or they will leak native resources until /world/proc/BSQL_Shutdown() is called
#define BSQL_DEL_PROC(path) ##path/Del()
//The equivalent of calling del() in your codebase
// Must not be changed to "qdel()".
#define BSQL_DEL_CALL(obj) del(##obj)
//Returns TRUE if an object is delete
#define BSQL_IS_DELETED(obj) (obj == null)
//Modify this to add protections to the connection and query datums
#define BSQL_PROTECT_DATUM(path)
//Modify this to change up error handling for the library
#define BSQL_ERROR(message) CRASH("BSQL: [##message]")
#endif
/*
Copyright 2018 Jordan Brown
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/

View File

@@ -1,7 +1,26 @@
var/list/protected_global_vars = list(
"sqlfdbklogin",
"sqlfdbkpass",
"sqlfdbkdb",
"sqladdress",
"sqlport",
"sqllogin",
"sqlpass",
"sqlfdbkdb",
"forbidden_varedit_object_types",
"unviewable_varedit_object_types",
"protected_global_vars", // Hhaha!
)
/proc/writeglobal(var/which, var/what)
if (which in protected_global_vars)
return "Cannot write variable."
global.vars[which] = what
/proc/readglobal(var/which)
if (which in protected_global_vars)
return "Cannot read variable."
return global.vars[which]
#define DNA_SE_LENGTH 58
@@ -209,11 +228,6 @@ var/forum_authenticated_group = "10"
var/fileaccess_timer = 0
var/custom_event_msg = null
//Database connections
//A connection is established on world creation. Ideally, the connection dies when the server restarts (After feedback logging.).
var/DBConnection/dbcon //Feedback database (New database)
var/DBConnection/dbcon_old //Tgstation database (Old database) - See the files in the SQL folder for information what goes where.
#define MIDNIGHT_ROLLOVER 864000 //number of deciseconds in a day
//Recall time limit: 2 hours
@@ -477,4 +491,4 @@ var/list/variables_not_to_be_copied = list(
var/global/list/ties = list(/obj/item/clothing/accessory/tie/blue,/obj/item/clothing/accessory/tie/red,/obj/item/clothing/accessory/tie/horrible)
//Observers
var/global_poltergeist_cooldown = 300 //30s by default, badmins can var-edit this to reduce the poltergeist cooldown globally
var/global_poltergeist_cooldown = 300 //30s by default, badmins can var-edit this to reduce the poltergeist cooldown globally

View File

@@ -1304,6 +1304,11 @@ var/default_colour_matrix = list(1,0,0,0,\
#define astar_debug(text)
#endif
#define BSQL_DEBUG_CONNECTION 0
#if BSQL_DEBUG_CONNECTION == 1
#warn "BSQL_DEBUG_CONNECTION MUST BE SET TO 0 BEFORE COMMITING."
#endif
//#define JUSTFUCKMYSHITUP 1
#ifdef JUSTFUCKMYSHITUP
#define writepanic(a) if(ticker && ticker.current_state >= 3 && world.cpu > 100) write_panic(a)

View File

@@ -2,6 +2,8 @@
// All in one file so it's easier to see what everything is relative to.
#define SS_INIT_TICKER_SPAWN 999
#define SS_INIT_DBCORE 900
#define SS_INIT_SSdbcore 800
#define SS_INIT_RUST 26
#define SS_INIT_SUPPLY_SHUTTLE 25
#define SS_INIT_SUN 24
@@ -48,6 +50,7 @@
#define SS_PRIORITY_UNSPECIFIED 30
#define SS_PRIORITY_LIGHTING 20
#define SS_PRIORITY_AMBIENCE 19
#define SS_PRIORITY_DBCORE 18
#define SS_PRIORITY_SUN 3
#define SS_PRIORITY_GARBAGE 2
#define SS_PRIORITY_INACTIVITY 1
@@ -74,8 +77,9 @@
#define SS_DISPLAY_POWER -20
#define SS_DISPLAY_TICKER -10
#define SS_DISPLAY_UNSPECIFIED 0
#define SS_DISPLAY_SUN 10
#define SS_DISPLAY_WEATHER 5
#define SS_DISPLAY_SUN 10
#define SS_DISPLAY_DBCORE 15
#define SS_TRASH "trash"
#define SS_CLEANABLE "cleanable_decals"

View File

@@ -7,3 +7,6 @@
var/global/internal_tick_usage = 0.2 * world.tick_lag // default value. extools-updated value, see extools maptick
#define TICK_CHECK ( world.tick_usage > CURRENT_TICKLIMIT ? stoplag() : 0 )
#define CHECK_TICK if (world.tick_usage > CURRENT_TICKLIMIT) stoplag()
// Do X until it's done, while looking for lag.
#define UNTIL(X) while(!(X)) stoplag()

View File

@@ -54,8 +54,17 @@
if(C.prefs.toggles & CHAT_DEBUGLOGS)
to_chat(C, "DEBUG: [text]")
/proc/log_sql(text)
if (!config || (config && config.log_sql))
diary << html_decode("\[[time_stamp()]]SQL: [text]")
/proc/log_query_debug(text)
if (!config || (config && config.log_sql_queries))
diary << html_decode("\[[time_stamp()]]SQL QUERY: [text]")
/proc/log_world(text)
log_game(text)
to_chat(world, "<span class='notice'>[text]</span>")
/proc/log_adminghost(text)
if (config.log_adminghost)

View File

@@ -31,11 +31,9 @@ forLineInText(text)
/proc/sanitizeSQL(var/t as text)
//var/sanitized_text = replacetext(t, "'", "\\'")
//sanitized_text = replacetext(sanitized_text, "\"", "\\\"")
var/sqltext = dbcon.Quote(t)
//testing("sanitizeSQL(): BEFORE copytext(): [sqltext]")
sqltext = copytext(sqltext, 2, length(sqltext))//Quote() adds quotes around input, we already do that
//testing("sanitizeSQL(): AFTER copytext(): [sqltext]")
var/sqltext = SSdbcore.Quote(t)
//to_chat(world, "sanitizeSQL(): BEFORE Quote(): [t]")
//to_chat(world, "sanitizeSQL(): AFTER Quote(): [sqltext]")
return sqltext
/*
@@ -588,4 +586,4 @@ proc/sql_sanitize_text(var/text)
speech = nya_lowercase.Replace(speech, "ny")
speech = nya_uppercase.Replace(speech, "NY")
speech = nya_Ny.Replace(speech, "Ny")
return speech
return speech

View File

@@ -6,6 +6,8 @@
#define TimeOfGame (get_game_time())
#define TimeOfTick (world.tick_usage*0.01*world.tick_lag)
//#define REALTIMEOFDAY (world.timeofday + (MIDNIGHT_ROLLOVER * MIDNIGHT_ROLLOVER_CHECK))
/proc/get_game_time()
var/global/time_offset = 0
var/global/last_time = 0
@@ -119,4 +121,17 @@ var/global/obj/effect/statclick/time/time_statclick
/proc/timeStatEntry()
if(!time_statclick)
time_statclick = new /obj/effect/statclick/time("loading...")
stat("Station Time:", time_statclick.update("[worldtime2text()]"))
stat("Station Time:", time_statclick.update("[worldtime2text()]"))
var/midnight_rollovers = 0
var/rollovercheck_last_timeofday = 0
/proc/update_midnight_rollover()
if (world.timeofday < rollovercheck_last_timeofday) //TIME IS GOING BACKWARDS!
midnight_rollovers++
rollovercheck_last_timeofday = world.timeofday
return midnight_rollovers
#define MIDNIGHT_ROLLOVER_CHECK (rollovercheck_last_timeofday != world.timeofday ? update_midnight_rollover() : midnight_rollovers)
#define MIDNIGHT_ROLLOVER 864000 //number of deciseconds in a day
#define REALTIMEOFDAY (world.timeofday + (MIDNIGHT_ROLLOVER * MIDNIGHT_ROLLOVER_CHECK))

View File

@@ -14,6 +14,8 @@
var/log_admin = 0 // log admin actions
var/log_admin_only = FALSE
var/log_debug = 1 // log debug output
var/log_sql = 0 // log SQL events
var/log_sql_queries = 0 // debug info SQL queries
var/log_game = 0 // log game events
var/log_vote = 0 // log voting
var/log_whisper = 0 // log client whisper
@@ -26,7 +28,7 @@
var/log_rc = 0 // log requests consoles
var/log_hrefs = 0 // logs all links clicked in-game. Could be used for debugging and tracking down exploits
var/log_runtimes = 0 // Logs all runtimes.
var/sql_enabled = 1 // for sql switching
var/sql_enabled = 0 // for sql switching
var/allow_admin_ooccolor = 0 // Allows admins with relevant permissions to have their own ooc colour
var/allow_vote_restart = 0 // allow votes to restart
var/allow_vote_mode = 0 // allow votes to change mode
@@ -71,6 +73,12 @@
var/jobs_have_minimal_access = 0 //determines whether jobs use minimal access or expanded access.
var/copy_logs = null
// BSQL things
var/bsql_debug = 0
var/async_query_timeout = 10
var/blocking_query_timeout = 5
var/bsql_thread_limit = 50
var/cult_ghostwriter = 1 //Allows ghosts to write in blood in cult rounds...
var/cult_ghostwriter_req_cultists = 10 //...so long as this many cultists are active.
@@ -286,6 +294,12 @@
if ("log_debug")
config.log_debug = text2num(value)
if ("log_sql")
config.log_sql = 1
if ("log_sql_queries")
config.log_sql_queries = 1
if ("log_game")
config.log_game = 1
@@ -546,6 +560,17 @@
config.assistantratio = text2num(value)
if("copy_logs")
copy_logs = value
// BSQL
if("bsql_debug")
bsql_debug = value
if("async_query_timeout")
async_query_timeout = value
if("blocking_query_timeout")
blocking_query_timeout = value
if("bsql_thread_limit")
bsql_thread_limit = value
if("media_base_url")
media_base_url = value
if("media_secret_key")

View File

@@ -0,0 +1,449 @@
// Original code from /tg/station at https://github.com/tgstation/tgstation
var/datum/subsystem/dbcore/SSdbcore
#define DB_MAJOR_VERSION 0
#define DB_MINOR_VERSION 1
/datum/subsystem/dbcore
name = "feedback Database"
wait = 1 MINUTES
flags = SS_NO_INIT
priority = SS_PRIORITY_DBCORE
display_order = SS_DISPLAY_DBCORE
var/const/FAILED_DB_CONNECTION_CUTOFF = 5
var/failed_connection_timeout = 0
var/schema_mismatch = 0
var/db_minor = 0
var/db_major = 0
var/failed_connections = 0
var/last_error
var/list/active_queries = list()
var/datum/BSQL_Connection/connection
var/datum/BSQL_Operation/connectOperation
/datum/subsystem/dbcore/New()
NEW_SS_GLOBAL(SSdbcore)
/datum/subsystem/dbcore/proc/get_db_ids()
var/list/ids = list()
ids["user"] = sqlfdbklogin
ids["pass"] = sqlfdbkpass
ids["db"] = sqlfdbkdb
ids["address"] = sqladdress
ids["port"] = sqlport
return ids
/datum/subsystem/dbcore/Initialize()
//We send warnings to the admins during subsystem init, as the clients will be New'd and messages
//will queue properly with goonchat
if(!Connect())
world.log << "Your server failed to establish a connection with the [name]."
else
world.log << "[name] connection established."
migration_controller_sqlite = new ("players2.sqlite", "players2_empty.sqlite")
switch(schema_mismatch)
if(1)
message_admins("Database schema ([db_major].[db_minor]) doesn't match the latest schema version ([DB_MAJOR_VERSION].[DB_MINOR_VERSION]), this may lead to undefined behaviour or errors")
if(2)
message_admins("Could not get schema version from database")
. = ..()
// All connections established, migrations can start.
migration_controller_mysql = new
/datum/subsystem/dbcore/fire()
for(var/I in active_queries)
var/datum/DBQuery/Q = I
if(world.time - Q.last_activity_time > (5 MINUTES))
message_admins("Found undeleted query, please check the server logs and notify coders.")
log_sql("Undeleted query: \"[Q.sql]\" LA: [Q.last_activity] LAT: [Q.last_activity_time]")
qdel(Q)
if(MC_TICK_CHECK)
return
/datum/subsystem/dbcore/Recover()
connection = SSdbcore.connection
connectOperation = SSdbcore.connectOperation
/datum/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
if(SSdbcore.Connect())
ShutdownQuery()
if(IsConnected())
Disconnect()
world.BSQL_Shutdown()
/datum/subsystem/dbcore/proc/Connect()
if(IsConnected())
return TRUE
if(failed_connection_timeout <= world.time) //it's been more than 5 seconds since we failed to connect, reset the counter
failed_connections = 0
if(failed_connections > FAILED_DB_CONNECTION_CUTOFF) //If it failed to establish a connection more than 5 times in a row, don't bother attempting to connect for 5 seconds.
failed_connection_timeout = world.time + 5 SECONDS
return FALSE
if(!config.sql_enabled)
return FALSE
var/list/ids = get_db_ids()
var/user = ids["user"]
var/pass = ids["pass"]
var/db = ids["db"]
var/address = ids["address"]
var/port = ids["port"]
connection = new /datum/BSQL_Connection(BSQL_CONNECTION_TYPE_MARIADB, config.async_query_timeout, config.blocking_query_timeout, config.bsql_thread_limit)
var/error
if(!connection || connection.gcDestroyed)
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 (!.)
last_error = error
log_sql("Connect() failed | [error]")
++failed_connections
qdel(connection)
qdel(connectOperation)
connection = null
connectOperation = null
/datum/subsystem/dbcore/proc/CheckSchemaVersion()
if(config.sql_enabled)
if(Connect())
log_world("Database connection established.")
/*
var/datum/DBQuery/query_db_version = NewQuery("SELECT major, minor FROM 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])
db_minor = text2num(query_db_version.item[2])
if(db_major != DB_MAJOR_VERSION || db_minor != DB_MINOR_VERSION)
schema_mismatch = 1 // flag admin message about mismatch
log_sql("Database schema ([db_major].[db_minor]) doesn't match the latest schema version ([DB_MAJOR_VERSION].[DB_MINOR_VERSION]), this may lead to undefined behaviour or errors")
else
schema_mismatch = 2 //flag admin message about no schema version
log_sql("Could not get schema version from database")
qdel(query_db_version)
*/
else
log_sql("Your server failed to establish a connection with the [name].")
else
log_sql("Database is not enabled in configuration.")
/datum/subsystem/dbcore/proc/SetRoundID()
return
/datum/subsystem/dbcore/proc/SetRoundStart()
return
/datum/subsystem/dbcore/proc/SetRoundEnd()
return
/datum/subsystem/dbcore/proc/ShutdownQuery()
return
/*
/datum/subsystem/dbcore/proc/SetRoundID()
if(!Connect())
return
var/datum/DBQuery/query_round_initialize = SSdbcore.NewQuery("INSERT INTO [format_table_name("round")] (initialize_datetime, server_ip, server_port) VALUES (Now(), INET_ATON(IF('[world.internet_address]' LIKE '', '0', '[world.internet_address]')), '[world.port]')")
query_round_initialize.Execute(async = FALSE)
qdel(query_round_initialize)
var/datum/DBQuery/query_round_last_id = SSdbcore.NewQuery("SELECT LAST_INSERT_ID()")
query_round_last_id.Execute(async = FALSE)
if(query_round_last_id.NextRow(async = FALSE))
GLOB.round_id = query_round_last_id.item[1]
qdel(query_round_last_id)
/datum/subsystem/dbcore/proc/SetRoundStart()
if(!Connect())
return
var/datum/DBQuery/query_round_start = SSdbcore.NewQuery("UPDATE [format_table_name("round")] SET start_datetime = Now() WHERE id = [GLOB.round_id]")
query_round_start.Execute()
qdel(query_round_start)
/datum/subsystem/dbcore/proc/SetRoundEnd()
if(!Connect())
return
var/sql_station_name = sanitizeSQL(station_name())
//var/datum/DBQuery/query_round_end = SSdbcore.NewQuery("UPDATE [format_table_name("round")] SET end_datetime = Now(), game_mode_result = '[sanitizeSQL(SSticker.mode_result)]', station_name = '[sql_station_name]' WHERE id = [GLOB.round_id]")
query_round_end.Execute()
qdel(query_round_end)
/datum/subsystem/dbcore/proc/ShutdownQuery()
var/datum/DBQuery/query_round_shutdown = SSdbcore.NewQuery("UPDATE [format_table_name("round")] SET shutdown_datetime = Now(), end_state = '[sanitizeSQL(SSticker.end_state)]' WHERE id = [GLOB.round_id]")
query_round_shutdown.Execute()
qdel(query_round_shutdown)
*/
/datum/subsystem/dbcore/proc/Disconnect()
failed_connections = 0
qdel(connectOperation)
qdel(connection)
connectOperation = null
connection = null
/datum/subsystem/dbcore/proc/IsConnected()
if(!config.sql_enabled)
return FALSE
if (!initialized)
return FALSE
//block until any connect operations finish
var/datum/BSQL_Connection/_connection = connection
var/datum/BSQL_Operation/op = connectOperation
while ( (!_connection || _connection.gcDestroyed) || !op.IsComplete() ) // Waiting we have a real connection and that it's complete
stoplag()
return connection && !(connection.gcDestroyed && !op.GetError()) // Connect
/datum/subsystem/dbcore/proc/Quote(str)
if(connection)
return connection.Quote(str)
/datum/subsystem/dbcore/proc/ErrorMsg()
if(!config.sql_enabled)
return "Database disabled by configuration"
return last_error
/datum/subsystem/dbcore/proc/ReportError(error)
last_error = error
//
/datum/subsystem/dbcore/proc/NewQuery(sql_query)
return new /datum/DBQuery(sql_query, connection)
/datum/subsystem/dbcore/proc/QuerySelect(list/querys, warn = FALSE, qdel = FALSE)
if (!islist(querys))
if (!istype(querys, /datum/DBQuery))
CRASH("Invalid query passed to QuerySelect: [querys]")
querys = list(querys)
for (var/thing in querys)
var/datum/DBQuery/query = thing
if (warn)
call(query, /datum/DBQuery.proc/warn_execute)()
else
call(query, /datum/DBQuery.proc/Execute)()
for (var/thing in querys)
var/datum/DBQuery/query = thing
UNTIL(!query.in_progress)
if (qdel)
qdel(query)
/*
Takes a list of rows (each row being an associated list of column => value) and inserts them via a single mass query.
Rows missing columns present in other rows will resolve to SQL NULL
You are expected to do your own escaping of the data, and expected to provide your own quotes for strings.
The duplicate_key arg can be true to automatically generate this part of the query
or set to a string that is appended to the end of the query
Ignore_errors instructes mysql to continue inserting rows if some of them have errors.
the erroneous row(s) aren't inserted and there isn't really any way to know why or why errored
Delayed insert mode was removed in mysql 7 and only works with MyISAM type tables,
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/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()
var/list/sorted_rows = list()
for (var/list/row in rows)
var/list/sorted_row = list()
sorted_row.len = columns.len
for (var/column in row)
var/idx = columns[column]
if (!idx)
idx = columns.len + 1
columns[column] = idx
sorted_row.len = columns.len
sorted_row[idx] = row[column]
sorted_rows[++sorted_rows.len] = sorted_row
if (duplicate_key == TRUE)
var/list/column_list = list()
for (var/column in columns)
column_list += "[column] = VALUES([column])"
duplicate_key = "ON DUPLICATE KEY UPDATE [column_list.Join(", ")]\n"
else if (duplicate_key == FALSE)
duplicate_key = null
if (ignore_errors)
ignore_errors = " IGNORE"
else
ignore_errors = null
if (delayed)
delayed = " DELAYED"
else
delayed = null
var/list/sqlrowlist = list()
var/len = columns.len
for (var/list/row in sorted_rows)
if (length(row) != len)
row.len = len
for (var/value in row)
if (value == null)
value = "NULL"
sqlrowlist += "([row.Join(", ")])"
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(async)
else
. = Query.Execute(async)
qdel(Query)
/datum/DBQuery
var/sql // The sql query being executed.
var/list/item //list of data values populated by NextRow()
var/last_activity
var/last_activity_time
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)
log_query_debug("new query with SQL : {[sql_query]} \n on [SSdbcore.name]")
SSdbcore.active_queries[src] = TRUE
Activity("Created")
item = list()
src.connection = connection
sql = sql_query
/datum/DBQuery/Destroy()
log_query_debug("query with [sql] being qdeleted. Will die any second now.")
Close()
SSdbcore.active_queries -= src
return ..()
/datum/DBQuery/Del()
log_query_debug("query with [sql] died.")
return ..()
/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(async = TRUE)
. = Execute(async)
if(!.)
to_chat(usr, "<span class='danger'>A SQL error occurred during this operation, check the server logs.</span>")
/datum/DBQuery/proc/Execute(async = TRUE, log_error = TRUE)
Activity("Execute")
if(in_progress)
CRASH("Attempted to start a new query while waiting on the old one")
if(!connection || connection.gcDestroyed)
last_error = "No connection!"
return FALSE
var/start_time
var/timed_out
if(!async)
start_time = REALTIMEOFDAY
Close()
timed_out = run_query(async)
if(query.GetErrorCode() == 2006) //2006 is the return code for "MySQL server has gone away" time-out error, meaning the connection has been lost to the server (if it's still alive)
log_sql("Executing query encountered returned a lost database connection (2006).")
SSdbcore.Disconnect()
if(SSdbcore.Connect()) //connection was restablished, reattempt the query
log_sql("Connection restablished")
timed_out = run_query(async)
else
log_sql("Executing query failed to restablish database connection.")
skip_next_is_complete = TRUE
var/error = (!query || query.gcDestroyed) ? "Query object deleted!" : query.GetError()
last_error = error
. = !error
if(!. && log_error)
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/run_query(async)
query = connection.BeginQuery(sql)
if(!async)
. = !query.WaitForCompletion()
else
in_progress = TRUE
UNTIL(query.IsComplete())
in_progress = FALSE
/datum/DBQuery/proc/slow_query_check()
message_admins("HEY! A database query timed out. Tell coders or Pomf what happened, please.")
/datum/DBQuery/proc/NextRow(async = TRUE)
Activity("NextRow")
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
last_error = query.GetError()
var/list/results = query.CurrentRow()
. = results != null
item.Cut()
//populate item array
for(var/I in results)
item += results[I]
/datum/DBQuery/proc/ErrorMsg()
return last_error
/datum/DBQuery/proc/Close()
item.Cut()
qdel(query)
query = null
/world/BSQL_Debug(message)
if(!config.bsql_debug)
return
//strip sensitive stuff
if(findtext(message, ": OpenConnection("))
message = "OpenConnection CENSORED"
log_sql("BSQL_DEBUG: [message]")

View File

@@ -73,31 +73,25 @@ var/datum/blackbox/blackbox = new
var/watch = start_watch()
log_startup_progress("Storing Black Box data...")
round_end_data_gathering() //round_end time logging and some other data processing
establish_db_connection()
if(!dbcon.IsConnected())
if(!SSdbcore.Connect())
return
var/round_id
var/nqueries = 0
var/DBQuery/query = dbcon.NewQuery("SELECT MAX(round_id) AS round_id FROM erro_feedback")
query.Execute()
var/datum/DBQuery/query = SSdbcore.NewQuery("SELECT MAX(round_id) AS round_id FROM erro_feedback")
if(!query.Execute())
log_sql("Error: [query.ErrorMsg()]")
qdel(query)
return
nqueries++
while(query.NextRow())
round_id = query.item[1]
qdel(query)
if(!isnum(round_id))
round_id = text2num(round_id)
round_id++
/*
for(var/datum/feedback_variable/FV in feedback)
var/sql = "INSERT INTO erro_feedback VALUES (null, Now(), [round_id], \"[FV.get_variable()]\", [FV.get_value()], \"[FV.get_details()]\")"
var/DBQuery/query_insert = dbcon.NewQuery(sql)
query_insert.Execute()
nqueries++
sleep(1) // Let other shit do things
*/
// MySQL and MariaDB support compound inserts and this insert is slow as fuck.
var/sql = "INSERT INTO erro_feedback VALUES "
var/ninserts=0
@@ -106,10 +100,13 @@ var/datum/blackbox/blackbox = new
sql += ","
ninserts++
sql += "(null, Now(), [round_id], \"[FV.get_variable()]\", [FV.get_value()], \"[FV.get_details()]\")"
var/DBQuery/query_insert = dbcon.NewQuery(sql)
query_insert.Execute()
var/datum/DBQuery/query_insert = SSdbcore.NewQuery(sql)
if(!query_insert.Execute())
log_sql("Error: [query_insert.ErrorMsg()]")
qdel(query_insert)
return
nqueries++
qdel(query_insert)
log_startup_progress(" Wrote Black Box data with [nqueries] queries in [stop_watch(watch)]s.")

View File

@@ -5,30 +5,29 @@ proc/sql_poll_players()
for(var/mob/M in player_list)
if(M.client)
playercount += 1
establish_db_connection()
if(!dbcon.IsConnected())
if(!SSdbcore.Connect())
log_game("SQL ERROR during player polling. Failed to connect.")
else
var/sqltime = time2text(world.realtime, "YYYY-MM-DD hh:mm:ss")
var/DBQuery/query = dbcon_old.NewQuery("INSERT INTO population (playercount, time) VALUES ([playercount], '[sqltime]')")
var/datum/DBQuery/query = SSdbcore.NewQuery("INSERT INTO population (playercount, time) VALUES ([playercount], '[sqltime]')")
if(!query.Execute())
var/err = query.ErrorMsg()
log_game("SQL ERROR during player polling. Error : \[[err]\]\n")
qdel(query)
proc/sql_poll_admins()
if(!sqllogging)
return
var/admincount = admins.len
establish_db_connection()
if(!dbcon.IsConnected())
if(!SSdbcore.Connect())
log_game("SQL ERROR during admin polling. Failed to connect.")
else
var/sqltime = time2text(world.realtime, "YYYY-MM-DD hh:mm:ss")
var/DBQuery/query = dbcon_old.NewQuery("INSERT INTO population (admincount, time) VALUES ([admincount], '[sqltime]')")
var/datum/DBQuery/query = SSdbcore.NewQuery("INSERT INTO population (admincount, time) VALUES ([admincount], '[sqltime]')")
if(!query.Execute())
var/err = query.ErrorMsg()
log_game("SQL ERROR during admin polling. Error : \[[err]\]\n")
qdel(query)
proc/sql_report_round_start()
// TODO
@@ -64,15 +63,14 @@ proc/sql_report_death(var/mob/living/carbon/human/H)
var/sqltime = time2text(world.realtime, "YYYY-MM-DD hh:mm:ss")
var/coord = "[H.x], [H.y], [H.z]"
// to_chat(world, "INSERT INTO death (name, byondkey, job, special, pod, tod, laname, lakey, gender, bruteloss, fireloss, brainloss, oxyloss) VALUES ('[sqlname]', '[sqlkey]', '[sqljob]', '[sqlspecial]', '[sqlpod]', '[sqltime]', '[laname]', '[lakey]', '[H.gender]', [H.bruteloss], [H.getFireLoss()], [H.brainloss], [H.getOxyLoss()])")
establish_db_connection()
if(!dbcon.IsConnected())
if(!SSdbcore.Connect())
log_game("SQL ERROR during death reporting. Failed to connect.")
else
var/DBQuery/query = dbcon.NewQuery("INSERT INTO death (name, byondkey, job, special, pod, tod, laname, lakey, gender, bruteloss, fireloss, brainloss, oxyloss, coord) VALUES ('[sqlname]', '[sqlkey]', '[sqljob]', '[sqlspecial]', '[sqlpod]', '[sqltime]', '[laname]', '[lakey]', '[H.gender]', [H.getBruteLoss()], [H.getFireLoss()], [H.brainloss], [H.getOxyLoss()], '[coord]')")
var/datum/DBQuery/query = SSdbcore.NewQuery("INSERT INTO death (name, byondkey, job, special, pod, tod, laname, lakey, gender, bruteloss, fireloss, brainloss, oxyloss, coord) VALUES ('[sqlname]', '[sqlkey]', '[sqljob]', '[sqlspecial]', '[sqlpod]', '[sqltime]', '[laname]', '[lakey]', '[H.gender]', [H.getBruteLoss()], [H.getFireLoss()], [H.brainloss], [H.getOxyLoss()], '[coord]')")
if(!query.Execute())
var/err = query.ErrorMsg()
log_game("SQL ERROR during death reporting. Error : \[[err]\]\n")
qdel(query)
proc/sql_report_cyborg_death(var/mob/living/silicon/robot/H)
if(!sqllogging)
@@ -98,16 +96,14 @@ proc/sql_report_cyborg_death(var/mob/living/silicon/robot/H)
lakey = sanitizeSQL(H.lastattacker:key)
var/sqltime = time2text(world.realtime, "YYYY-MM-DD hh:mm:ss")
var/coord = "[H.x], [H.y], [H.z]"
// to_chat(world, "INSERT INTO death (name, byondkey, job, special, pod, tod, laname, lakey, gender, bruteloss, fireloss, brainloss, oxyloss) VALUES ('[sqlname]', '[sqlkey]', '[sqljob]', '[sqlspecial]', '[sqlpod]', '[sqltime]', '[laname]', '[lakey]', '[H.gender]', [H.bruteloss], [H.getFireLoss()], [H.brainloss], [H.getOxyLoss()])")
establish_db_connection()
if(!dbcon.IsConnected())
if(!SSdbcore.Connect())
log_game("SQL ERROR during death reporting. Failed to connect.")
else
var/DBQuery/query = dbcon.NewQuery("INSERT INTO death (name, byondkey, job, special, pod, tod, laname, lakey, gender, bruteloss, fireloss, brainloss, oxyloss, coord) VALUES ('[sqlname]', '[sqlkey]', '[sqljob]', '[sqlspecial]', '[sqlpod]', '[sqltime]', '[laname]', '[lakey]', '[H.gender]', [H.getBruteLoss()], [H.getFireLoss()], [H.brainloss], [H.getOxyLoss()], '[coord]')")
var/datum/DBQuery/query = SSdbcore.NewQuery("INSERT INTO death (name, byondkey, job, special, pod, tod, laname, lakey, gender, bruteloss, fireloss, brainloss, oxyloss, coord) VALUES ('[sqlname]', '[sqlkey]', '[sqljob]', '[sqlspecial]', '[sqlpod]', '[sqltime]', '[laname]', '[lakey]', '[H.gender]', [H.getBruteLoss()], [H.getFireLoss()], [H.brainloss], [H.getOxyLoss()], '[coord]')")
if(!query.Execute())
var/err = query.ErrorMsg()
log_game("SQL ERROR during death reporting. Error : \[[err]\]\n")
qdel(query)
proc/statistic_cycle()
if(!sqllogging)
@@ -130,14 +126,15 @@ proc/sql_commit_feedback()
if(!content)
log_game("Round ended without any feedback being generated. No feedback was sent to the database.")
return
establish_db_connection()
if(!dbcon.IsConnected())
if(!SSdbcore.Connect())
log_game("SQL ERROR during feedback reporting. Failed to connect.")
else
var/DBQuery/max_query = dbcon.NewQuery("SELECT MAX(roundid) AS max_round_id FROM erro_feedback")
max_query.Execute()
var/datum/DBQuery/max_query = SSdbcore.NewQuery("SELECT MAX(roundid) AS max_round_id FROM erro_feedback")
if(!max_query.Execute())
log_sql("Error: [max_query.ErrorMsg()]")
qdel(max_query)
var/newroundid
@@ -156,7 +153,8 @@ proc/sql_commit_feedback()
var/variable = item.get_variable()
var/value = item.get_value()
var/DBQuery/query = dbcon.NewQuery("INSERT INTO erro_feedback (id, roundid, time, variable, value) VALUES (null, [newroundid], Now(), '[variable]', '[value]')")
var/datum/DBQuery/query = SSdbcore.NewQuery("INSERT INTO erro_feedback (id, roundid, time, variable, value) VALUES (null, [newroundid], Now(), '[variable]', '[value]')")
if(!query.Execute())
var/err = query.ErrorMsg()
log_game("SQL ERROR during death reporting. Error : \[[err]\]\n")
qdel(query)

7
code/libs/BSQL/LICENSE Normal file
View File

@@ -0,0 +1,7 @@
Copyright 2018 Jordan Brown
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

View File

@@ -0,0 +1,68 @@
/datum/BSQL_Connection
var/id
var/connection_type
BSQL_PROTECT_DATUM(/datum/BSQL_Connection)
/datum/BSQL_Connection/New(connection_type, asyncTimeout, blockingTimeout, threadLimit)
if(asyncTimeout == null)
asyncTimeout = BSQL_DEFAULT_TIMEOUT
if(blockingTimeout == null)
blockingTimeout = asyncTimeout
if(threadLimit == null)
threadLimit = BSQL_DEFAULT_THREAD_LIMIT
src.connection_type = connection_type
world._BSQL_InitCheck(src)
var/error = world._BSQL_Internal_Call("CreateConnection", connection_type, "[asyncTimeout]", "[blockingTimeout]", "[threadLimit]")
if(error)
BSQL_ERROR(error)
return
id = world._BSQL_Internal_Call("GetConnection")
if(!id)
BSQL_ERROR("BSQL library failed to provide connect operation for connection id [id]([connection_type])!")
BSQL_DEL_PROC(/datum/BSQL_Connection)
var/error
if(id)
error = world._BSQL_Internal_Call("ReleaseConnection", id)
. = ..()
if(error)
BSQL_ERROR(error)
/datum/BSQL_Connection/BeginConnect(ipaddress, port, username, password, database)
var/error = world._BSQL_Internal_Call("OpenConnection", id, ipaddress, "[port]", username, password, database)
if(error)
BSQL_ERROR(error)
return
var/op_id = world._BSQL_Internal_Call("GetOperation")
if(!op_id)
BSQL_ERROR("Library failed to provide connect operation for connection id [id]([connection_type])!")
return
return new /datum/BSQL_Operation(src, op_id)
/datum/BSQL_Connection/BeginQuery(query)
var/error = world._BSQL_Internal_Call("NewQuery", id, query)
if(error)
BSQL_ERROR(error)
return
var/op_id = world._BSQL_Internal_Call("GetOperation")
if(!op_id)
BSQL_ERROR("Library failed to provide query operation for connection id [id]([connection_type])!")
return
return new /datum/BSQL_Operation/Query(src, op_id)
/datum/BSQL_Connection/Quote(str)
if(!str)
return null;
. = world._BSQL_Internal_Call("QuoteString", id, "[str]")
if(!.)
BSQL_ERROR("Library failed to provide quote for [str]!")

View File

@@ -0,0 +1,43 @@
/world/proc/_BSQL_Internal_Call(func, ...)
var/list/call_args = args.Copy(2)
BSQL_Debug("[.....]: [args[1]]([call_args.Join(", ")])")
. = call(_BSQL_Library_Path(), func)(arglist(call_args))
BSQL_Debug("Result: [. == null ? "NULL" : "\"[.]\""]")
/world/proc/_BSQL_Library_Path()
return system_type == MS_WINDOWS ? "BSQL.dll" : "libBSQL.so"
/world/proc/_BSQL_InitCheck(datum/BSQL_Connection/caller)
var/static/library_initialized = FALSE
if(_BSQL_Initialized())
return
var/libPath = _BSQL_Library_Path()
if(!fexists(libPath))
BSQL_DEL_CALL(caller)
BSQL_ERROR("Could not find [libPath]!")
return
var/version = _BSQL_Internal_Call("Version")
if(version != BSQL_VERSION)
BSQL_DEL_CALL(caller)
BSQL_ERROR("BSQL DMAPI version mismatch! Expected [BSQL_VERSION], got [version == null ? "NULL" : version]!")
return
var/result = _BSQL_Internal_Call("Initialize")
if(result)
BSQL_DEL_CALL(caller)
BSQL_ERROR(result)
return
_BSQL_Initialized(TRUE)
/world/proc/_BSQL_Initialized(new_val)
var/static/bsql_library_initialized = FALSE
if(new_val != null)
bsql_library_initialized = new_val
return bsql_library_initialized
/world/BSQL_Shutdown()
if(!_BSQL_Initialized())
return
_BSQL_Internal_Call("Shutdown")
_BSQL_Initialized(FALSE)

View File

@@ -0,0 +1,47 @@
/datum/BSQL_Operation
var/datum/BSQL_Connection/connection
var/id
BSQL_PROTECT_DATUM(/datum/BSQL_Operation)
/datum/BSQL_Operation/New(datum/BSQL_Connection/connection, id)
src.connection = connection
src.id = id
BSQL_DEL_PROC(/datum/BSQL_Operation)
var/error
if(!BSQL_IS_DELETED(connection))
error = world._BSQL_Internal_Call("ReleaseOperation", connection.id, id)
. = ..()
if(error)
BSQL_ERROR(error)
/datum/BSQL_Operation/IsComplete()
if(BSQL_IS_DELETED(connection))
return TRUE
var/result = world._BSQL_Internal_Call("OpComplete", connection.id, id)
if(!result)
BSQL_ERROR("Error fetching operation [id] for connection [connection.id]!")
return
return result == "DONE"
/datum/BSQL_Operation/GetError()
if(BSQL_IS_DELETED(connection))
return "Connection deleted!"
return world._BSQL_Internal_Call("GetError", connection.id, id)
/datum/BSQL_Operation/GetErrorCode()
if(BSQL_IS_DELETED(connection))
return -2
return text2num(world._BSQL_Internal_Call("GetErrorCode", connection.id, id))
/datum/BSQL_Operation/WaitForCompletion()
if(BSQL_IS_DELETED(connection))
return
var/error = world._BSQL_Internal_Call("BlockOnOperation", connection.id, id)
if(error)
if(error == "Operation timed out!") //match this with the implementation
return FALSE
BSQL_ERROR("Error waiting for operation [id] for connection [connection.id]! [error]")
return
return TRUE

View File

@@ -0,0 +1,35 @@
/datum/BSQL_Operation/Query
var/last_result_json
var/list/last_result
BSQL_PROTECT_DATUM(/datum/BSQL_Operation/Query)
/datum/BSQL_Operation/Query/CurrentRow()
return last_result
/datum/BSQL_Operation/Query/IsComplete()
//whole different ballgame here
if(BSQL_IS_DELETED(connection))
return TRUE
var/result = world._BSQL_Internal_Call("ReadyRow", connection.id, id)
switch(result)
if("DONE")
//load the data
LoadQueryResult()
return TRUE
if("NOTDONE")
return FALSE
else
BSQL_ERROR(result)
/datum/BSQL_Operation/Query/WaitForCompletion()
. = ..()
if(.)
LoadQueryResult()
/datum/BSQL_Operation/Query/proc/LoadQueryResult()
last_result_json = world._BSQL_Internal_Call("GetRow", connection.id, id)
if(last_result_json)
last_result = json_decode(last_result_json)
else
last_result = null

View File

@@ -0,0 +1,4 @@
#include "core\connection.dm"
#include "core\library.dm"
#include "core\operation.dm"
#include "core\query.dm"

View File

@@ -1,38 +0,0 @@
// Contains all of the various defines and constants required by the library.
//cursors
#define Default_Cursor 0
#define Client_Cursor 1
#define Server_Cursor 2
//conversions
#define TEXT_CONV 1
#define RSC_FILE_CONV 2
#define NUMBER_CONV 3
//column flag values:
#define IS_NUMERIC 1
#define IS_BINARY 2
#define IS_NOT_NULL 4
#define IS_PRIMARY_KEY 8
#define IS_UNSIGNED 16
//types
#define TINYINT 1
#define SMALLINT 2
#define MEDIUMINT 3
#define INTEGER 4
#define BIGINT 5
#define DECIMAL 6
#define FLOAT 7
#define DOUBLE 8
#define DATE 9
#define DATETIME 10
#define TIMESTAMP 11
#define TIME 12
#define STRING 13
#define BLOB 14
// TODO: Investigate more recent type additions and see if I can handle them. - Nadrew

View File

@@ -1,179 +0,0 @@
//var/const
//DB_SERVER = "localhost" // This is the location of your MySQL server (localhost is USUALLY fine)
//DB_PORT = 3306 // This is the port your MySQL server is running on (3306 is the default)
DBConnection
var/_db_con // This variable contains a reference to the actual database connection.
var/dbi // This variable is a string containing the DBI MySQL requires.
var/user // This variable contains the username data.
var/password // This variable contains the password data.
var/default_cursor // This contains the default database cursor data.
//server = DB_SERVER // "localhost"
var/server = "localhost"
//port = DB_PORT // 3306
var/port = 3306
DBConnection/New(dbi_handler,username,password_handler,cursor_handler)
src.dbi = dbi_handler
src.user = username
src.password = password_handler
src.default_cursor = cursor_handler
_db_con = _dm_db_new_con()
DBConnection/proc/Connect(dbi_handler=src.dbi,user_handler=src.user,password_handler=src.password,cursor_handler)
//if(!src) return 0
if(!sqllogging || !src)
return 0
cursor_handler = src.default_cursor
if(!cursor_handler)
cursor_handler = Default_Cursor
return _dm_db_connect(_db_con,dbi_handler,user_handler,password_handler,cursor_handler,null)
DBConnection/proc/Disconnect() return _dm_db_close(_db_con)
//IsConnected() return _dm_db_is_connected(_db_con)
DBConnection/proc/IsConnected() return !sqllogging ? 0 : _dm_db_is_connected(_db_con)
DBConnection/proc/Quote(str) return _dm_db_quote(_db_con,str)
DBConnection/proc/ErrorMsg() return _dm_db_error_msg(_db_con)
DBConnection/proc/SelectDB(database_name,dbi)
if(IsConnected())
Disconnect()
//return Connect("[dbi?"[dbi]":"dbi:mysql:[database_name]:[DB_SERVER]:[DB_PORT]"]",user,password)
return Connect("[dbi?"[dbi]":"dbi:mysql:[database_name]:[sqladdress]:[sqlport]"]",user,password)
DBConnection/proc/NewQuery(sql_query,cursor_handler=src.default_cursor) return new/DBQuery(sql_query,src,cursor_handler)
DBQuery
var/closed = 0 // N3X: Explicitly closed?
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[0] // list of data values populated by NextRow()
var/DBConnection/db_connection
var/_db_query
DBQuery/New(sql_query,DBConnection/connection_handler,cursor_handler)
if(sql_query)
src.sql = sql_query
if(connection_handler)
src.db_connection = connection_handler
if(cursor_handler)
src.default_cursor = cursor_handler
_db_query = _dm_db_new_query()
return ..()
// DO NOT CHANGE TO DESTROY() - N3X
DBQuery/Del()
Close()
DBQuery/proc/Connect(DBConnection/connection_handler)
src.db_connection = connection_handler
DBQuery/proc/Execute(sql_query=src.sql,cursor_handler=default_cursor)
Close()
return _dm_db_execute(_db_query,sql_query,db_connection._db_con,cursor_handler,null)
DBQuery/proc/NextRow()
return _dm_db_next_row(_db_query,item,conversions)
DBQuery/proc/RowsAffected()
return _dm_db_rows_affected(_db_query)
DBQuery/proc/RowCount()
return _dm_db_row_count(_db_query)
DBQuery/proc/ErrorMsg()
return _dm_db_error_msg(_db_query)
DBQuery/proc/Columns()
if(!columns)
columns = _dm_db_columns(_db_query,/DBColumn)
return columns
DBQuery/proc/GetRowData()
var/list/columns = Columns()
var/list/results
if(columns.len)
results = list()
for(var/C in columns)
results+=C
var/DBColumn/cur_col = columns[C]
results[C] = src.item[(cur_col.position+1)]
return results
DBQuery/proc/Close()
if(closed)
return // Don't do this twice.
closed=1
item.len = 0
columns = null
conversions = null
return _dm_db_close(_db_query)
DBQuery/proc/Quote(str)
return db_connection.Quote(str)
/* SetConversion(column,conversion)
// This doesn't seem to be doing anything internally...
if(istext(column))
column = columns.Find(column)
if(!conversions)
conversions = new/list(column)
else if(conversions.len < column)
conversions.len = column
conversions[column] = conversion*/
DBColumn
var/name
var/table
var/position // 1-based index into item data
var/sql_type
var/flags
var/length
var/max_length
DBColumn/New(name_handler,table_handler,position_handler,type_handler,flag_handler,length_handler,max_length_handler)
src.name = name_handler
src.table = table_handler
src.position = position_handler
src.sql_type = type_handler
src.flags = flag_handler
src.length = length_handler
src.max_length = max_length_handler
return ..()
DBColumn/proc/SqlTypeName(type_handler=src.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"

View File

@@ -1,45 +0,0 @@
/*
Dantom.DB
Created by BYOND for BYOND, 2002.
Release History:
v0.6 Nov 22, 2013 (Nadrew)
v0.5 Mar 23, 2008 (Nadrew)
v0.4 Mar 19, 2008 (Nadrew)
v0.3 Feb 08, 2006 (Nadrew)
v0.2 Jan 31, 2003 (Dan)
v0.1 Nov 30, 2002 (Dan)
Updates:
v0.6 - Updated the documentation.
Commented out DBQuery.SetConversion(), my tests show it doing absolutely nothing to the resulting data.
Moved the defines into their own file to reduce the clutter in core.dm
Cleaned up the commenting of the code and the code itself a bit more.
v0.5 - Added DBConnection.SelectDB() see db.html for details.
Changed all global constants to quicker #defines.
Added global variables DB_SERVER and DB_PORT to help SelectDB() out.
Moved this information and the core code into seperate files.
v0.4 - Cleaned up the code even more.
Rewrote the argument names for the procs to be less cryptic (you know, Dancode-y).
Added a few comments here and there.
Sped up various procs by "modernizing" some of the code inside of them.
Wrote some actual documentation for the library, since as of 413.00 it should get a bit more usage.
v0.3 - Fixed long-standing bug with the connection process, adding a workaround to a strange BYOND bug.
Updated all of the command arguments to not match local variables, as it was causing tons of issues.
The arguments aren't named very well, but you can tell what they do.
Added GetRowData() function to the DBQuery datum, this function will allow you to obtain a list of
table data referenced by name and not by index number.
v0.2 - Cleaned up the code significantly.
See db.html for documentation.
See core.dm for guts.
See constants.dm for #defines and constants.
*/

View File

@@ -1,23 +0,0 @@
// DM Environment file for Dantom.DB.dme.
// All manual changes should be made outside the BEGIN_ and END_ blocks.
// New source code should be placed in .dm files: choose File/New --> Code File.
// BEGIN_INTERNALS
/*
FILE: db.html
*/
// END_INTERNALS
// BEGIN_FILE_DIR
#define FILE_DIR .
// END_FILE_DIR
// BEGIN_PREFERENCES
#define DEBUG
// END_PREFERENCES

View File

@@ -1,43 +0,0 @@
<html>
<head>
<title>Dantom.DB Documentation</title>
</head>
<body>
<base target="contentframe">
<h2>Dantom.DB</h2>
<hr>
<table width=100% height=100%><tr><td width=25% valign=top>
<ul>
<li><a href=dbinfo.html#DBConnection>DBConnection</a>
<ul>
<li><a href=dbinfo.html#DBConnection.Connect()>Connect()</a></li>
<li><a href=dbinfo.html#DBConnection.Disconnect()>Disconnect()</a></li>
<li><a href=dbinfo.html#DBConnection.IsConnected()>IsConnected()</a></li>
<li><a href=dbinfo.html#DBConnection.Quote()>Quote()</a></li>
<li><a href=dbinfo.html#DBConnection.ErrorMsg()>ErrorMsg()</a></li>
<li><a href=dbinfo.html#DBConnection.SelectDB()>SelectDB()</a></li>
<li><a href=dbinfo.html#DBConnection.NewQuery()>NewQuery()</a></li>
</ul>
</li>
<li><a href=dbinfo.html#DBQuery>DBQuery</a>

View File

@@ -1,142 +0,0 @@
<b>Note:</b> I wrote this document to the best of my knowledge, I still don't know everything there is to know about this library or MySQL. - Nadrew<p>
<h2><a name="DBConnection">DBConnection</a></h2>
The DBConnection datum is what handles your connection to the MySQL database.
<br><b>A note on the DBI string's format, it's "dbi:mysql:[database_name]:[server]:[port]"</b>
<p style="color:red;"><b>If libMySQL (.dll/.so) does not exist on the system DBConnection.ErrorMsg() will return 'The selected module could not be loaded.'</b></p>
<hr>
<font size=+1><b><a name="DBConnection.Connect()">Connect()</a></b></font><br>
Creates a new connection to your MySQL server.<br>
<b>Format:</b> Connect(dbi,username,password,cursor)<br>
<b>Arguments:</b>
<dd><b>dbi</b> The database interface string to send to MySQL.
<dd><b>username</b> The MySQL username to send.
<dd><b>password</b> The MySQL password associated with the username.
<dd><b>cursor</b> The MySQL cursor to connect with, best not to set this.<p>
<b>Example:</b><br>
<pre>
var/DBConnection/dbcon = new()
dbcon.Connect("dbi:mysql:database_name:localhost:3306","username","password")
if(!dbcon.IsConnected()) usr << dbcon.ErrorMsg()
</pre>
<hr>
<font size=+1><b><a name="DBConnection.Disconnect()">Disconnect()</a></b></font><br>
Disconnects the current MySQL database connection.
<hr>
<font size=+1><b><a name="DBConnection.IsConnected()">IsConnected()</a></b></font><br>
Returns true if the DBConnection object has a connection, false otherwise.
<hr>
<font size=+1><b><a name="DBConnection.Quote()">Quote()</a></b></font><br>
This will escape the string passed using the MySQL escaping function, this will prevent against things like data injection attacks<br>
<b>Format:</b> Quote(string)<br>
<b>Arguments:</b>
<dd><b>string</b> The string you want to escape.<hr>
<font size=+1><b><a name="DBConnection.ErrorMsg()">ErrorMsg()</a></b></font><br>
This will return the error message (if any) given by the MySQL server. For an example of the usage, check the example under <a href="#DBConnection.Connect()">Connect()</a>.<hr>
<font size=+1><b><a name="DBConnection.SelectDB()">SelectDB()</a></b></font><br>
This allows you to quickly change the database your DBConnection object is acting on.<br>
<b>Format:</b> SelectDB(database,dbi)<br>
<b>Arguments:</b>
<dd><b>database</b> The database you wish to switch to.
<dd><b>dbi</b> (Optional) The dbi string you want to use to make the switch.<hr>
<font size=+1><b><a name="DBConnection.NewQuery()">NewQuery()</a></b></font><br>
This is a nice handy function that'll handle the creation of new query objects for you and return the resulting object.<br>
<b>Format:</b> NewQuery(query,cursor)<br>
<b>Arguments:</b>
<dd><b>query</b> The SQL query you want to execute.
<dd><b>cursor</b> As usual, best to leave this one alone ;)<br>
<b>Example:</b><br>
<pre>
var/DBQuery/my_query = dbcon.NewQuery("SELECT * FROM `my_table`")
if(my_query.RowCount()) usr << "Got some data."
else usr << "No data there!"
</pre><hr>
<h2><a name="DBQuery">DBQuery</a></h2>
The DBQuery datum is what handles executing SQL queries and contains all of the various data given by that query.
<hr>
<font size=+1><b><a name="DBQuery.Connect()">Connect()</a></b></font><br>
This proc is a way of quickly changing the DBConnection object the query object is connected to.<br>
<b>Format:</b> Connect(DBConnection)<br>
<b>Arguments:</b>
<dd><b>DBConnection</b> The new /DBConnection object to associate with this query.<hr>
<font size=+1><b><a name="DBQuery.Execute()">Execute()</a></b></font><br>
Executes a SQL query and returns true if the query succeded or false otherwise. If no query is passed this will execute the last query sent to the object (which is set if a query is passed to NewQuery())<br>
<b>Format:</b> Execute(query,cursor)<br>
<b>Arguments:</b>
<dd><b>query</b> The SQL query to execute.
<dd><b>cursor</b> Seriously, if you don't know what this is, no touching!<br>
<b>Example:</b><br>
<pre>
var/DBQuery/query = dbcon.NewQuery("SELECT * FROM `my_table`")
if(!query.Execute()) usr << query.ErrorMsg()
else usr << "The query worked!"
</pre><hr>

View File

@@ -5,8 +5,7 @@
if(!check_rights(R_BAN))
return
establish_db_connection()
if(!dbcon.IsConnected())
if(!SSdbcore.Connect())
return
var/serverip = "[world.internet_address]:[world.port]"
@@ -56,11 +55,16 @@
else if(banckey)
ckey = ckey(banckey)
var/DBQuery/query = dbcon.NewQuery("SELECT id FROM erro_player WHERE ckey = '[ckey]'")
query.Execute()
var/datum/DBQuery/query = SSdbcore.NewQuery("SELECT id FROM erro_player WHERE ckey = '[ckey]'")
if(!query.Execute())
message_admins("Error: [query.ErrorMsg()]")
log_sql("Error: [query.ErrorMsg()]")
qdel(query)
return
var/validckey = 0
if(query.NextRow())
validckey = 1
qdel(query)
if(!validckey)
if(!banned_mob || (banned_mob && !IsGuestKey(banned_mob.key)))
message_admins("<span class='red'>[key_name_admin(usr)] attempted to ban [ckey], but [ckey] has not been seen yet. Please only ban actual players.</span>",1)
@@ -92,8 +96,13 @@
reason = sql_sanitize_text(reason)
var/sql = "INSERT INTO erro_ban (`id`,`bantime`,`serverip`,`bantype`,`reason`,`job`,`duration`,`rounds`,`expiration_time`,`ckey`,`computerid`,`ip`,`a_ckey`,`a_computerid`,`a_ip`,`who`,`adminwho`,`edits`,`unbanned`,`unbanned_datetime`,`unbanned_ckey`,`unbanned_computerid`,`unbanned_ip`) VALUES (null, Now(), '[serverip]', '[bantype_str]', '[reason]', '[job]', [(duration)?"[duration]":"0"], [(rounds)?"[rounds]":"0"], Now() + INTERVAL [(duration>0) ? duration : 0] MINUTE, '[ckey]', '[computerid]', '[ip]', '[a_ckey]', '[a_computerid]', '[a_ip]', '[who]', '[adminwho]', '', null, null, null, null, null)"
var/DBQuery/query_insert = dbcon.NewQuery(sql)
query_insert.Execute()
var/datum/DBQuery/query_insert = SSdbcore.NewQuery(sql)
if(!query_insert.Execute())
message_admins("Error: [query_insert.ErrorMsg()]")
log_sql("Error: [query_insert.ErrorMsg()]")
qdel(query_insert)
return
qdel(query_insert)
to_chat(usr, "<span class='notice'>Ban saved to database.</span>")
message_admins("[key_name_admin(usr)] has added a [bantype_str] for [ckey] [(job)?"([job])":""] [(duration > 0)?"([duration] minutes)":""] with the reason: \"[reason]\" to the ban database.",1)
@@ -151,19 +160,22 @@ datum/admins/proc/DB_ban_unban(var/ckey, var/bantype, var/job = "")
if(job)
sql += " AND job = '[job]'"
establish_db_connection()
if(!dbcon.IsConnected())
if(!SSdbcore.Connect())
return
var/ban_id
var/ban_number = 0 //failsafe
var/DBQuery/query = dbcon.NewQuery(sql)
query.Execute()
var/datum/DBQuery/query = SSdbcore.NewQuery(sql)
if(!query.Execute())
message_admins("Error: [query.ErrorMsg()]")
log_sql("Error: [query.ErrorMsg()]")
qdel(query)
return
while(query.NextRow())
ban_id = query.item[1]
ban_number++;
qdel(query)
if(ban_number == 0)
to_chat(usr, "<span class='warning'>Database update failed due to no bans fitting the search criteria. If this is not a legacy ban you should contact the database admin.</span>")
return
@@ -190,8 +202,12 @@ datum/admins/proc/DB_ban_edit(var/banid = null, var/param = null)
to_chat(usr, "Cancelled")
return
var/DBQuery/query = dbcon.NewQuery("SELECT ckey, duration, reason FROM erro_ban WHERE id = [banid]")
query.Execute()
var/datum/DBQuery/query = SSdbcore.NewQuery("SELECT ckey, duration, reason FROM erro_ban WHERE id = [banid]")
if(!query.Execute())
message_admins("Error: [query.ErrorMsg()]")
log_sql("Error: [query.ErrorMsg()]")
qdel(query)
return
var/eckey = usr.ckey //Editing admin ckey
var/pckey //(banned) Player ckey
@@ -204,8 +220,9 @@ datum/admins/proc/DB_ban_edit(var/banid = null, var/param = null)
reason = query.item[3]
else
to_chat(usr, "Invalid ban id. Contact the database admin")
qdel(query)
return
qdel(query)
reason = sql_sanitize_text(reason)
var/value
@@ -218,9 +235,14 @@ datum/admins/proc/DB_ban_edit(var/banid = null, var/param = null)
to_chat(usr, "Cancelled")
return
var/DBQuery/update_query = dbcon.NewQuery("UPDATE erro_ban SET reason = '[value]', edits = CONCAT(edits,'- [eckey] changed ban reason from <cite><b>\\\"[reason]\\\"</b></cite> to <cite><b>\\\"[value]\\\"</b></cite><BR>') WHERE id = [banid]")
update_query.Execute()
var/datum/DBQuery/update_query = SSdbcore.NewQuery("UPDATE erro_ban SET reason = '[value]', edits = CONCAT(edits,'- [eckey] changed ban reason from <cite><b>\\\"[reason]\\\"</b></cite> to <cite><b>\\\"[value]\\\"</b></cite><BR>') WHERE id = [banid]")
if(!update_query.Execute())
message_admins("Error: [update_query.ErrorMsg()]")
log_sql("Error: [update_query.ErrorMsg()]")
qdel(update_query)
return
message_admins("[key_name_admin(usr)] has edited a ban for [pckey]'s reason from [reason] to [value]",1)
qdel(update_query)
if("duration")
if(!value)
value = input("Insert the new duration (in minutes) for [pckey]'s ban", "New Duration", "[duration]", null) as null|num
@@ -228,9 +250,14 @@ datum/admins/proc/DB_ban_edit(var/banid = null, var/param = null)
to_chat(usr, "Cancelled")
return
var/DBQuery/update_query = dbcon.NewQuery("UPDATE erro_ban SET duration = [value], edits = CONCAT(edits,'- [eckey] changed ban duration from [duration] to [value]<br>'), expiration_time = DATE_ADD(bantime, INTERVAL [value] MINUTE) WHERE id = [banid]")
var/datum/DBQuery/update_query = SSdbcore.NewQuery("UPDATE erro_ban SET duration = [value], edits = CONCAT(edits,'- [eckey] changed ban duration from [duration] to [value]<br>'), expiration_time = DATE_ADD(bantime, INTERVAL [value] MINUTE) WHERE id = [banid]")
message_admins("[key_name_admin(usr)] has edited a ban for [pckey]'s duration from [duration] to [value]",1)
update_query.Execute()
if(!update_query.Execute())
message_admins("Error: [update_query.ErrorMsg()]")
log_sql("Error: [update_query.ErrorMsg()]")
qdel(update_query)
return
qdel(update_query)
if("unban")
if(alert("Unban [pckey]?", "Unban?", "Yes", "No") == "Yes")
DB_ban_unban_by_id(banid)
@@ -250,19 +277,22 @@ datum/admins/proc/DB_ban_unban_by_id(var/id)
var/sql = "SELECT ckey FROM erro_ban WHERE id = [id]"
establish_db_connection()
if(!dbcon.IsConnected())
if(!SSdbcore.Connect())
return
var/ban_number = 0 //failsafe
var/pckey
var/DBQuery/query = dbcon.NewQuery(sql)
query.Execute()
var/datum/DBQuery/query = SSdbcore.NewQuery(sql)
if(!query.Execute())
message_admins("Error: [query.ErrorMsg()]")
log_sql("Error: [query.ErrorMsg()]")
qdel(query)
return
while(query.NextRow())
pckey = query.item[1]
ban_number++;
qdel(query)
if(ban_number == 0)
to_chat(usr, "<span class='warning'>Database update failed due to a ban id not being present in the database.</span>")
return
@@ -281,8 +311,13 @@ datum/admins/proc/DB_ban_unban_by_id(var/id)
var/sql_update = "UPDATE erro_ban SET unbanned = 1, unbanned_datetime = Now(), unbanned_ckey = '[unban_ckey]', unbanned_computerid = '[unban_computerid]', unbanned_ip = '[unban_ip]' WHERE id = [id]"
message_admins("[key_name_admin(usr)] has lifted [pckey]'s ban.",1)
var/DBQuery/query_update = dbcon.NewQuery(sql_update)
query_update.Execute()
var/datum/DBQuery/query_update = SSdbcore.NewQuery(sql_update)
if(!query_update.Execute())
message_admins("Error: [query_update.ErrorMsg()]")
log_sql("Error: [query_update.ErrorMsg()]")
qdel(query_update)
return
qdel(query_update)
INVOKE_EVENT(on_unban,list(
"id"=id,
@@ -308,8 +343,7 @@ datum/admins/proc/DB_ban_unban_by_id(var/id)
if(!check_rights(R_BAN))
return
establish_db_connection()
if(!dbcon.IsConnected())
if(!SSdbcore.Connect())
to_chat(usr, "<span class='warning'>Failed to establish database connection</span>")
return
@@ -383,8 +417,12 @@ datum/admins/proc/DB_ban_unban_by_id(var/id)
if(playerckey)
playersearch = "AND ckey = '[playerckey]' "
var/DBQuery/select_query = dbcon.NewQuery("SELECT id, bantime, bantype, reason, job, duration, expiration_time, ckey, a_ckey, unbanned, unbanned_ckey, unbanned_datetime, edits FROM erro_ban WHERE 1 [playersearch] [adminsearch] ORDER BY bantime DESC")
select_query.Execute()
var/datum/DBQuery/select_query = SSdbcore.NewQuery("SELECT id, bantime, bantype, reason, job, duration, expiration_time, ckey, a_ckey, unbanned, unbanned_ckey, unbanned_datetime, edits FROM erro_ban WHERE 1 [playersearch] [adminsearch] ORDER BY bantime DESC")
if(!select_query.Execute())
qdel(select_query)
message_admins("Error: [select_query.ErrorMsg()]")
log_sql("Error: [select_query.ErrorMsg()]")
return
while(select_query.NextRow())
var/banid = select_query.item[1]
@@ -470,5 +508,6 @@ datum/admins/proc/DB_ban_unban_by_id(var/id)
</tr>"}
output += "</table></div>"
qdel(select_query)
usr << browse(output,"window=lookupbans;size=900x500")

View File

@@ -51,7 +51,7 @@
var/ckeytext = ckey(key)
if(!establish_db_connection())
if(!SSdbcore.Connect())
world.log << "Ban database connection failure. Key [ckeytext] not checked"
diary << "Ban database connection failure. Key [ckeytext] not checked"
return
@@ -68,9 +68,13 @@
failedcid = 0
cidquery = " OR computerid = '[computer_id]' "
var/DBQuery/query = dbcon.NewQuery("SELECT ckey, ip, computerid, a_ckey, reason, expiration_time, duration, bantime, bantype FROM erro_ban WHERE (ckey = '[ckeytext]' [ipquery] [cidquery]) AND (bantype = 'PERMABAN' OR (bantype = 'TEMPBAN' AND expiration_time > Now())) AND isnull(unbanned)")
var/datum/DBQuery/query = SSdbcore.NewQuery("SELECT ckey, ip, computerid, a_ckey, reason, expiration_time, duration, bantime, bantype FROM erro_ban WHERE (ckey = '[ckeytext]' [ipquery] [cidquery]) AND (bantype = 'PERMABAN' OR (bantype = 'TEMPBAN' AND expiration_time > Now())) AND isnull(unbanned)")
query.Execute()
if(!query.Execute())
message_admins("Error: [query.ErrorMsg()]")
log_sql("Error: [query.ErrorMsg()]")
qdel(query)
return
while(query.NextRow())
var/pckey = query.item[1]
//var/pip = query.item[2]
@@ -92,9 +96,10 @@
else
desc = "\nReason: You, or another user of this computer or connection ([pckey]) is banned from playing here. The ban reason is:\n[reason]\nThis ban was applied by [ackey] on [bantime] \nBan type: [bantype] \nExpires: [expires] \nAppeal: <span class='warning'>No ban appeals link set</span>"
log_access("Failed Login: [key] [computer_id] [address] - Banned [desc]")
qdel(query)
return list("reason"="[bantype]", "desc"="[desc]")
//return "[bantype][desc]"
qdel(query)
if (failedcid)
message_admins("[key] has logged in with a blank computer id in the ban check.")
if (failedip)
@@ -113,4 +118,4 @@
what.Remove("message")
what["desc"] = "[desc]"
what["reason"] = "PERMABAN"
return . //default pager ban stuff
return . //default pager ban stuff

View File

@@ -131,17 +131,19 @@ var/list/admin_ranks = list() //list of all ranks with associated rights
world.SetConfig("APP/admin", ckey, "role=admin")
else
//The current admin system uses SQL
establish_db_connection()
if(!dbcon.IsConnected())
if(!SSdbcore.Connect())
world.log << "Failed to connect to database in load_admins(). Reverting to legacy system."
diary << "Failed to connect to database in load_admins(). Reverting to legacy system."
config.admin_legacy_system = 1
load_admins()
return
var/DBQuery/query = dbcon.NewQuery("SELECT ckey, rank, level, flags FROM erro_admin")
query.Execute()
var/datum/DBQuery/query = SSdbcore.NewQuery("SELECT ckey, rank, level, flags FROM erro_admin")
if(!query.Execute())
message_admins("Error: [query.ErrorMsg()]")
log_sql("Error: [query.ErrorMsg()]")
qdel(query)
return
while(query.NextRow())
var/ckey = query.item[1]
var/rank = query.item[2]
@@ -158,7 +160,7 @@ var/list/admin_ranks = list() //list of all ranks with associated rights
if(D.rights & (R_DEBUG|R_SERVER)) // Grant profile/reboot access
world.SetConfig("APP/admin", ckey, "role=admin")
qdel(query)
if(!admin_datums)
world.log << "The database query in load_admins() resulted in no admins being added to the list. Reverting to legacy system."
diary << "The database query in load_admins() resulted in no admins being added to the list. Reverting to legacy system."

View File

@@ -927,7 +927,7 @@ var/list/admin_verbs_mod = list(
if(D.rights & (R_DEBUG|R_SERVER)) // Grant profile/reboot access
world.SetConfig("APP/admin", ckey, "role=admin")
else
if(!dbcon.IsConnected())
if(!SSdbcore.IsConnected())
message_admins("Warning, mysql database is not connected.")
to_chat(src, "Warning, mysql database is not connected.")
return
@@ -936,8 +936,11 @@ var/list/admin_verbs_mod = list(
verbs -= /client/proc/readmin
return
var/sql_ckey = sanitizeSQL(ckey(ckey))
var/DBQuery/query = dbcon.NewQuery("SELECT ckey, rank, level, flags FROM erro_admin WHERE ckey = '[sql_ckey]'")
query.Execute()
var/datum/DBQuery/query = SSdbcore.NewQuery("SELECT ckey, rank, level, flags FROM erro_admin WHERE ckey = '[sql_ckey]'")
if(!query.Execute())
log_sql("Error: [query.ErrorMsg()]")
qdel(query)
return
while(query.NextRow())
var/dckey = query.item[1]
var/rank = query.item[2]
@@ -955,6 +958,7 @@ var/list/admin_verbs_mod = list(
log_admin("[src] re-adminned themselves.")
feedback_add_details("admin_verb","RAS")
verbs -= /client/proc/readmin
qdel(query)
return
/client/proc/achievement()

View File

@@ -54,7 +54,7 @@ DEBUG
appearance_keylist=list()
log_admin("appearance_keylist was empty")
else
if(!establish_db_connection())
if(!SSdbcore.Connect())
world.log << "Database connection failed. Reverting to the legacy ban system."
diary << "Database connection failed. Reverting to the legacy ban system."
config.ban_legacy_system = 1
@@ -62,13 +62,16 @@ DEBUG
return
//appearance bans
var/DBQuery/query = dbcon.NewQuery("SELECT ckey FROM erro_ban WHERE bantype = 'APPEARANCE_PERMABAN' AND isnull(unbanned)")
query.Execute()
var/datum/DBQuery/query = SSdbcore.NewQuery("SELECT ckey FROM erro_ban WHERE bantype = 'APPEARANCE_PERMABAN' AND isnull(unbanned)")
if(!query.Execute())
log_sql("Error: [query.ErrorMsg()]")
qdel(query)
return
while(query.NextRow())
var/ckey = query.item[1]
appearance_keylist.Add("[ckey]")
qdel(query)
/proc/appearance_savebanfile()
var/savefile/S=new("data/appearance_full.ban")
@@ -96,18 +99,3 @@ DEBUG
appearance_savebanfile()
return 1
return 0
/*
proc/DB_ban_isappearancebanned(var/playerckey)
establish_db_connection()
if(!dbcon.IsConnected())
return
var/sqlplayerckey = sql_sanitize_text(ckey(playerckey))
var/DBQuery/query = dbcon.NewQuery("SELECT id FROM erro_ban WHERE CKEY = '[sqlplayerckey]' AND ((bantype = 'APPEARANCE_PERMABAN') OR (bantype = 'APPEARANCE_TEMPBAN' AND expiration_time > Now())) AND unbanned != 1")
query.Execute()
while(query.NextRow())
return 1
return 0
*/

View File

@@ -62,7 +62,7 @@ DEBUG
jobban_keylist=list()
log_admin("jobban_keylist was empty")
else
if(!establish_db_connection())
if(!SSdbcore.Connect())
world.log << "Database connection failed. Reverting to the legacy ban system."
diary << "Database connection failed. Reverting to the legacy ban system."
config.ban_legacy_system = 1
@@ -70,24 +70,31 @@ DEBUG
return
//Job permabans
var/DBQuery/query = dbcon.NewQuery("SELECT ckey, job FROM erro_ban WHERE bantype = 'JOB_PERMABAN' AND isnull(unbanned)")
query.Execute()
var/datum/DBQuery/query = SSdbcore.NewQuery("SELECT ckey, job FROM erro_ban WHERE bantype = 'JOB_PERMABAN' AND isnull(unbanned)")
if(!query.Execute())
message_admins("Error: [query.ErrorMsg()]")
log_sql("Error: [query.ErrorMsg()]")
qdel(query)
return
while(query.NextRow())
var/ckey = query.item[1]
var/job = query.item[2]
jobban_keylist.Add("[ckey] - [job]")
qdel(query)
//Job tempbans
var/DBQuery/query1 = dbcon.NewQuery("SELECT ckey, job FROM erro_ban WHERE bantype = 'JOB_TEMPBAN' AND isnull(unbanned) AND expiration_time > Now()")
query1.Execute()
var/datum/DBQuery/query1 = SSdbcore.NewQuery("SELECT ckey, job FROM erro_ban WHERE bantype = 'JOB_TEMPBAN' AND isnull(unbanned) AND expiration_time > Now()")
if(!query1.Execute())
log_sql("Error: [query1.ErrorMsg()]")
qdel(query1)
return
while(query1.NextRow())
var/ckey = query1.item[1]
var/job = query1.item[2]
jobban_keylist.Add("[ckey] - [job]")
qdel(query1)
/proc/jobban_savebanfile()
var/savefile/S = new("data/job_full.ban")

View File

@@ -11,16 +11,20 @@ var/oocban_keylist[0]
return oocban_keylist.Add("[M.ckey]")
/proc/oocban_loadbanfile()
if(!establish_db_connection())
if(!SSdbcore.Connect())
world.log << "Database connection failed. Skipping ooc ban loading"
diary << "Database connection failed. Skipping ooc ban loading"
return
//OOC permabans
var/DBQuery/query = dbcon.NewQuery("SELECT ckey FROM erro_ban WHERE (bantype = 'OOC_PERMABAN' OR (bantype = 'OOC_TEMPBAN' AND expiration_time > Now())) AND isnull(unbanned)")
query.Execute()
var/datum/DBQuery/query = SSdbcore.NewQuery("SELECT ckey FROM erro_ban WHERE (bantype = 'OOC_PERMABAN' OR (bantype = 'OOC_TEMPBAN' AND expiration_time > Now())) AND isnull(unbanned)")
if(!query.Execute())
message_admins("Error: [query.ErrorMsg()]")
log_sql("Error: [query.ErrorMsg()]")
qdel(query)
return
while(query.NextRow())
var/ckey = query.item[1]
oocban_keylist.Add("[ckey]")
oocban_keylist.Add("[ckey]")
qdel(query)

View File

@@ -3,7 +3,7 @@
set category = "Special Verbs"
if(!check_rights(R_POLLING))
return
if(!dbcon.IsConnected())
if(!SSdbcore.Connect())
src << "<span class='danger'>Failed to establish database connection.</span>"
return
var/polltype = input("Choose poll type.","Poll Type") in list("Single Option","Text Reply","Rating","Multiple Choice")
@@ -21,28 +21,33 @@
var/starttime = SQLtime()
var/endtime = input("Set end time for poll as format YYYY-MM-DD HH:MM:SS. All times in server time. HH:MM:SS is optional and 24-hour. Must be later than starting time for obvious reasons.", "Set end time", SQLtime()) as text
if(!endtime)
to_chat(usr, "<span class='warning'>endtime is null!</span>")
return
endtime = sanitizeSQL(endtime)
var/DBQuery/query_validate_time = dbcon.NewQuery("SELECT STR_TO_DATE('[endtime]','%Y-%c-%d %T')")
var/datum/DBQuery/query_validate_time = SSdbcore.NewQuery("SELECT STR_TO_DATE('[endtime]','%Y-%c-%d %T')")
if(!query_validate_time.Execute())
var/err = query_validate_time.ErrorMsg()
log_game("SQL ERROR validating endtime. Error : \[[err]\]\n")
log_sql("SQL ERROR validating endtime. Error : \[[err]\]\n")
qdel(query_validate_time)
return
if(query_validate_time.NextRow())
endtime = query_validate_time.item[1]
if(!endtime)
to_chat(src, "Datetime entered is invalid.")
return
var/DBQuery/query_time_later = dbcon.NewQuery("SELECT DATE('[endtime]') < NOW()")
qdel(query_validate_time)
var/datum/DBQuery/query_time_later = SSdbcore.NewQuery("SELECT DATE('[endtime]') < NOW()")
if(!query_time_later.Execute())
var/err = query_time_later.ErrorMsg()
log_game("SQL ERROR comparing endtime to NOW(). Error : \[[err]\]\n")
log_sql("SQL ERROR comparing endtime to NOW(). Error : \[[err]\]\n")
qdel(query_time_later)
return
if(query_time_later.NextRow())
var/checklate = text2num(query_time_later.item[1])
if(checklate)
src << "Datetime entered is not later than current server time."
return
qdel(query_time_later)
var/adminonly
switch(alert("Admin only poll?",,"Yes","No","Cancel"))
if("Yes")
@@ -55,21 +60,25 @@
var/question = input("Write your question","Question") as message
if(!question)
return
question = replacetext(question, "'", "\'")
question = sanitizeSQL(question)
var/DBQuery/query_polladd_question = dbcon.NewQuery("INSERT INTO erro_poll_question (polltype, starttime, endtime, question, adminonly, multiplechoiceoptions, createdby_ckey, createdby_ip) VALUES ('[polltype]', '[starttime]', '[endtime]', '[question]', '[adminonly]', '[choice_amount]', '[sql_ckey]', '[address]')")
var/datum/DBQuery/query_polladd_question = SSdbcore.NewQuery("INSERT INTO erro_poll_question (polltype, starttime, endtime, question, adminonly, multiplechoiceoptions, createdby_ckey, createdby_ip) VALUES ('[polltype]', '[starttime]', '[endtime]', '[question]', '[adminonly]', '[choice_amount]', '[sql_ckey]', '[address]')")
if(!query_polladd_question.Execute())
var/err = query_polladd_question.ErrorMsg()
log_game("SQL ERROR adding new poll question to table. Error : \[[err]\]\n")
qdel(query_polladd_question)
log_sql("SQL ERROR adding new poll question to table. Error : \[[err]\]\n")
return
qdel(query_polladd_question)
var/pollid = 0
var/DBQuery/query_get_id = dbcon.NewQuery("SELECT id FROM erro_poll_question WHERE question = '[question]' AND starttime = '[starttime]' AND endtime = '[endtime]' AND createdby_ckey = '[sql_ckey]' AND createdby_ip = '[address]'")
var/datum/DBQuery/query_get_id = SSdbcore.NewQuery("SELECT id FROM erro_poll_question WHERE question = '[question]' AND starttime = '[starttime]' AND endtime = '[endtime]' AND createdby_ckey = '[sql_ckey]' AND createdby_ip = '[address]'")
if(!query_get_id.Execute())
var/err = query_get_id.ErrorMsg()
log_game("SQL ERROR obtaining id from poll_question table. Error : \[[err]\]\n")
qdel(query_get_id)
log_sql("SQL ERROR obtaining id from poll_question table. Error : \[[err]\]\n")
return
if(query_get_id.NextRow())
pollid = query_get_id.item[1]
qdel(query_get_id)
log_admin("[key_name(src)] created the poll with id [pollid].")
message_admins("<span class='notice'>[key_name_admin(src)] created the poll with id [pollid].</span>")
@@ -113,13 +122,15 @@
descmax = input("Optional: Set description for maximum rating","Maximum rating description") as message
if(descmax)
descmax = sanitizeSQL(descmax)
var/DBQuery/query_polladd_option = dbcon.NewQuery("INSERT INTO erro_poll_option (pollid, text, percentagecalc, minval, maxval, descmin, descmid, descmax) VALUES ('[pollid]', '[option]', '[percentagecalc]', '[minval]', '[maxval]', '[descmin]', '[descmid]', '[descmax]')")
var/datum/DBQuery/query_polladd_option = SSdbcore.NewQuery("INSERT INTO erro_poll_option (pollid, text, percentagecalc, minval, maxval, descmin, descmid, descmax) VALUES ('[pollid]', '[option]', '[percentagecalc]', '[minval]', '[maxval]', '[descmin]', '[descmid]', '[descmax]')")
if(!query_polladd_option.Execute())
var/err = query_polladd_option.ErrorMsg()
log_game("SQL ERROR adding new poll option to table. Error : \[[err]\]\n")
log_sql("SQL ERROR adding new poll option to table. Error : \[[err]\]\n")
qdel(query_polladd_option)
return
qdel(query_polladd_option)
switch(alert(" ",,"Add option","Finish"))
if("Add option")
add_option = 1
if("Finish")
add_option = 0
add_option = 0

View File

@@ -131,19 +131,38 @@ you will have to do something like if(client.rights & R_ADMIN) yourself.
if(recurse==5)
return "\[BROKEN\]";
recurse++
var/DBQuery/query = dbcon.NewQuery("DELETE FROM admin_sessions WHERE expires < Now()")
query.Execute()
query = dbcon.NewQuery("SELECT sessID FROM admin_sessions WHERE ckey = '[owner.ckey]' AND expires > Now()")
query.Execute()
var/datum/DBQuery/query = SSdbcore.NewQuery("DELETE FROM admin_sessions WHERE expires < Now()")
if(!query.Execute())
message_admins("Error: [query.ErrorMsg()]")
log_sql("Error: [query.ErrorMsg()]")
qdel(query)
return
var/datum/DBQuery/sel_query = SSdbcore.NewQuery("SELECT sessID FROM admin_sessions WHERE ckey = '[owner.ckey]' AND expires > Now()")
if(!sel_query.Execute())
message_admins("Error: [sel_query.ErrorMsg()]")
log_sql("Error: [sel_query.ErrorMsg()]")
qdel(sel_query)
return
qdel(sel_query)
sessKey=0
while(query.NextRow())
sessKey = query.item[1]
query=dbcon.NewQuery("UPDATE admin_sessions SET expires=DATE_ADD(NOW(), INTERVAL 24 HOUR), IP='[owner.address]' WHERE ckey = '[owner.ckey]")
query.Execute()
var/datum/DBQuery/up_query=SSdbcore.NewQuery("UPDATE admin_sessions SET expires=DATE_ADD(NOW(), INTERVAL 24 HOUR), IP='[owner.address]' WHERE ckey = '[owner.ckey]")
if(!up_query.Execute())
message_admins("Error: [up_query.ErrorMsg()]")
log_sql("Error: [up_query.ErrorMsg()]")
qdel(up_query)
return
qdel(up_query)
return sessKey
qdel(query)
query=dbcon.NewQuery("INSERT INTO admin_sessions (sessID,ckey,expires, IP) VALUES (UUID(), '[owner.ckey]', DATE_ADD(NOW(), INTERVAL 24 HOUR), '[owner.address]')")
query.Execute()
var/datum/DBQuery/insert_query=SSdbcore.NewQuery("INSERT INTO admin_sessions (sessID,ckey,expires, IP) VALUES (UUID(), '[owner.ckey]', DATE_ADD(NOW(), INTERVAL 24 HOUR), '[owner.address]')")
if(!insert_query.Execute())
message_admins("Error: [insert_query.ErrorMsg()]")
log_sql("Error: [insert_query.ErrorMsg()]")
qdel(insert_query)
return
qdel(insert_query)
return checkSessionKey(recurse)

View File

@@ -65,9 +65,7 @@
to_chat(usr, "<span class='warning'>You do not have permission to do this!</span>")
return
establish_db_connection()
if(!dbcon.IsConnected())
if(!SSdbcore.Connect())
to_chat(usr, "<span class='warning'>Failed to establish database connection</span>")
return
@@ -82,27 +80,51 @@
if(!istext(adm_ckey) || !istext(new_rank))
return
var/DBQuery/select_query = dbcon.NewQuery("SELECT id FROM erro_admin WHERE ckey = '[adm_ckey]'")
select_query.Execute()
var/datum/DBQuery/select_query = SSdbcore.NewQuery("SELECT id FROM erro_admin WHERE ckey = '[adm_ckey]'")
if(!select_query.Execute())
message_admins("Error: [select_query.ErrorMsg()]")
log_sql("Error: [select_query.ErrorMsg()]")
qdel(select_query)
return
var/new_admin = 1
var/admin_id
while(select_query.NextRow())
new_admin = 0
admin_id = text2num(select_query.item[1])
qdel(select_query)
if(new_admin)
var/DBQuery/insert_query = dbcon.NewQuery("INSERT INTO `erro_admin` (`id`, `ckey`, `rank`, `level`, `flags`) VALUES (null, '[adm_ckey]', '[new_rank]', -1, 0)")
insert_query.Execute()
var/DBQuery/log_query = dbcon.NewQuery("INSERT INTO `test`.`erro_admin_log` (`id` ,`datetime` ,`adminckey` ,`adminip` ,`log` ) VALUES (NULL , NOW( ) , '[usr.ckey]', '[usr.client.address]', 'Added new admin [adm_ckey] to rank [new_rank]');")
log_query.Execute()
var/datum/DBQuery/insert_query = SSdbcore.NewQuery("INSERT INTO `erro_admin` (`id`, `ckey`, `rank`, `level`, `flags`) VALUES (null, '[adm_ckey]', '[new_rank]', -1, 0)")
if(!insert_query.Execute())
message_admins("Error: [insert_query.ErrorMsg()]")
log_sql("Error: [insert_query.ErrorMsg()]")
qdel(insert_query)
return
qdel(insert_query)
var/datum/DBQuery/log_query = SSdbcore.NewQuery("INSERT INTO [sqlfdbkdb].`erro_admin_log` (`id` ,`datetime` ,`adminckey` ,`adminip` ,`log` ) VALUES (NULL , NOW( ) , '[usr.ckey]', '[usr.client.address]', 'Added new admin [adm_ckey] to rank [new_rank]');") // FIXME: [sqlfdbkdb] is the default name of the feedback DB.
if(!log_query.Execute())
message_admins("Error: [log_query.ErrorMsg()]")
log_sql("Error: [log_query.ErrorMsg()]")
qdel(log_query)
return
qdel(log_query)
to_chat(usr, "<span class='notice'>New admin added.</span>")
else
if(!isnull(admin_id) && isnum(admin_id))
var/DBQuery/insert_query = dbcon.NewQuery("UPDATE `erro_admin` SET rank = '[new_rank]' WHERE id = [admin_id]")
insert_query.Execute()
var/DBQuery/log_query = dbcon.NewQuery("INSERT INTO `test`.`erro_admin_log` (`id` ,`datetime` ,`adminckey` ,`adminip` ,`log` ) VALUES (NULL , NOW( ) , '[usr.ckey]', '[usr.client.address]', 'Edited the rank of [adm_ckey] to [new_rank]');")
log_query.Execute()
var/datum/DBQuery/insert_query = SSdbcore.NewQuery("UPDATE `erro_admin` SET rank = '[new_rank]' WHERE id = [admin_id]")
if(!insert_query.Execute())
message_admins("Error: [insert_query.ErrorMsg()]")
log_sql("Error: [insert_query.ErrorMsg()]")
qdel(insert_query)
return
qdel(insert_query)
var/datum/DBQuery/log_query = SSdbcore.NewQuery("INSERT INTO [sqlfdbkdb].`erro_admin_log` (`id` ,`datetime` ,`adminckey` ,`adminip` ,`log` ) VALUES (NULL , NOW( ) , '[usr.ckey]', '[usr.client.address]', 'Edited the rank of [adm_ckey] to [new_rank]');") // FIXME: [sqlfdbkdb] is the default name of the feedback DB.
if(!log_query.Execute())
message_admins("Error: [log_query.ErrorMsg()]")
log_sql("Error: [log_query.ErrorMsg()]")
qdel(log_query)
return
qdel(log_query)
to_chat(usr, "<span class='notice'>Admin rank changed.</span>")
/datum/admins/proc/log_admin_permission_modification(var/adm_ckey, var/new_permission)
@@ -116,8 +138,7 @@
to_chat(usr, "<span class='warning'>You do not have permission to do this!</span>")
return
establish_db_connection()
if(!dbcon.IsConnected())
if(!SSdbcore.Connect())
to_chat(usr, "<span class='warning'>Failed to establish database connection</span>")
return
@@ -135,27 +156,51 @@
if(!istext(adm_ckey) || !isnum(new_permission))
return
var/DBQuery/select_query = dbcon.NewQuery("SELECT id, flags FROM erro_admin WHERE ckey = '[adm_ckey]'")
select_query.Execute()
var/datum/DBQuery/select_query = SSdbcore.NewQuery("SELECT id, flags FROM erro_admin WHERE ckey = '[adm_ckey]'")
if(!select_query.Execute())
message_admins("Error: [select_query.ErrorMsg()]")
log_sql("Error: [select_query.ErrorMsg()]")
qdel(select_query)
return
var/admin_id
var/admin_rights
while(select_query.NextRow())
admin_id = text2num(select_query.item[1])
admin_rights = text2num(select_query.item[2])
qdel(select_query)
if(!admin_id)
return
if(admin_rights & new_permission) //This admin already has this permission, so we are removing it.
var/DBQuery/insert_query = dbcon.NewQuery("UPDATE `erro_admin` SET flags = [admin_rights & ~new_permission] WHERE id = [admin_id]")
insert_query.Execute()
var/DBQuery/log_query = dbcon.NewQuery("INSERT INTO `test`.`erro_admin_log` (`id` ,`datetime` ,`adminckey` ,`adminip` ,`log` ) VALUES (NULL , NOW( ) , '[usr.ckey]', '[usr.client.address]', 'Removed permission [rights2text(new_permission)] (flag = [new_permission]) to admin [adm_ckey]');")
log_query.Execute()
var/datum/DBQuery/insert_query = SSdbcore.NewQuery("UPDATE `erro_admin` SET flags = [admin_rights & ~new_permission] WHERE id = [admin_id]")
if(!insert_query.Execute())
message_admins("Error: [insert_query.ErrorMsg()]")
log_sql("Error: [insert_query.ErrorMsg()]")
qdel(insert_query)
return
qdel(insert_query)
var/datum/DBQuery/log_query = SSdbcore.NewQuery("INSERT INTO [sqlfdbkdb].`erro_admin_log` (`id` ,`datetime` ,`adminckey` ,`adminip` ,`log` ) VALUES (NULL , NOW( ) , '[usr.ckey]', '[usr.client.address]', 'Removed permission [rights2text(new_permission)] (flag = [new_permission]) to admin [adm_ckey]');")
if(!log_query.Execute())
message_admins("Error: [log_query.ErrorMsg()]")
log_sql("Error: [log_query.ErrorMsg()]")
qdel(log_query)
return
qdel(log_query)
to_chat(usr, "<span class='notice'>Permission removed.</span>")
else //This admin doesn't have this permission, so we are adding it.
var/DBQuery/insert_query = dbcon.NewQuery("UPDATE `erro_admin` SET flags = '[admin_rights | new_permission]' WHERE id = [admin_id]")
insert_query.Execute()
var/DBQuery/log_query = dbcon.NewQuery("INSERT INTO `test`.`erro_admin_log` (`id` ,`datetime` ,`adminckey` ,`adminip` ,`log` ) VALUES (NULL , NOW( ) , '[usr.ckey]', '[usr.client.address]', 'Added permission [rights2text(new_permission)] (flag = [new_permission]) to admin [adm_ckey]')")
log_query.Execute()
var/datum/DBQuery/insert_query = SSdbcore.NewQuery("UPDATE `erro_admin` SET flags = '[admin_rights | new_permission]' WHERE id = [admin_id]")
if(!insert_query.Execute())
message_admins("Error: [insert_query.ErrorMsg()]")
log_sql("Error: [insert_query.ErrorMsg()]")
qdel(insert_query)
return
qdel(insert_query)
var/datum/DBQuery/log_query = SSdbcore.NewQuery("INSERT INTO [sqlfdbkdb].`erro_admin_log` (`id` ,`datetime` ,`adminckey` ,`adminip` ,`log` ) VALUES (NULL , NOW( ) , '[usr.ckey]', '[usr.client.address]', 'Added permission [rights2text(new_permission)] (flag = [new_permission]) to admin [adm_ckey]')")
if(!log_query.Execute())
message_admins("Error: [log_query.ErrorMsg()]")
log_sql("Error: [log_query.ErrorMsg()]")
qdel(log_query)
return
qdel(log_query)
to_chat(usr, "<span class='notice'>Permission added.</span>")

View File

@@ -32,8 +32,7 @@ var/inactive_keys = "None<br>"
if(checked_for_inactives)
return
establish_db_connection()
if(!dbcon.IsConnected())
if(!SSdbcore.Connect())
return
//grab all ckeys associated with custom items
@@ -58,22 +57,31 @@ var/inactive_keys = "None<br>"
//run a query to get all ckeys inactive for over 2 months
var/list/inactive_ckeys = list()
if(ckeys_with_customitems.len)
var/DBQuery/query_inactive = dbcon.NewQuery("SELECT ckey, lastseen FROM erro_player WHERE datediff(Now(), lastseen) > 60")
query_inactive.Execute()
var/datum/DBQuery/query_inactive = SSdbcore.NewQuery("SELECT ckey, lastseen FROM erro_player WHERE datediff(Now(), lastseen) > 60")
if(!query_inactive.Execute())
log_sql("Error: [query_inactive.ErrorMsg()]")
qdel(query_inactive)
return
while(query_inactive.NextRow())
var/cur_ckey = query_inactive.item[1]
//if the ckey has a custom item attached, output it
if(ckeys_with_customitems.Find(cur_ckey))
ckeys_with_customitems.Remove(cur_ckey)
inactive_ckeys[cur_ckey] = "last seen on [query_inactive.item[2]]"
qdel(query_inactive)
//if there are ckeys left over, check whether they have a database entry at all
if(ckeys_with_customitems.len)
for(var/cur_ckey in ckeys_with_customitems)
var/DBQuery/query_inactive = dbcon.NewQuery("SELECT ckey FROM erro_player WHERE ckey = '[cur_ckey]'")
var/datum/DBQuery/query_inactive = SSdbcore.NewQuery("SELECT ckey FROM erro_player WHERE ckey = '[cur_ckey]'")
query_inactive.Execute()
if(!query_inactive.RowCount())
if(!query_inactive.Execute())
log_sql("Error: [query_inactive.ErrorMsg()]")
qdel(query_inactive)
return
if(!query_inactive.item.len)
inactive_ckeys += cur_ckey
qdel(query_inactive)
if(inactive_ckeys.len)
inactive_keys = ""

View File

@@ -70,6 +70,13 @@ But you can call procs that are of type /mob/living/carbon/human/proc/ for that
if(!procname)
return
// Do not make this a global reference. Global references can be cleared out.
if (istype(target, /datum/subsystem/dbcore/))
to_chat(usr, "<span class='red'>Never use atom proc call to inject SQL.</span>")
message_admins("[key_name(usr)] used atom proc call on the db controller.")
log_admin("[key_name(usr)] used atom proc call on the db controller.")
return FALSE
if(target && !hascall(target, procname))
to_chat(usr, "<span class='red'>Error: callproc(): target has no such call [procname].</span>")
return

View File

@@ -2,8 +2,14 @@ var/list/forbidden_varedit_object_types = list(
/datum/admins, //Admins editing their own admin-power object? Yup, sounds like a good idea.
/datum/blackbox, //Prevents people messing with feedback gathering
/datum/feedback_variable, //Prevents people messing with feedback gathering
/datum/subsystem/dbcore/, // No messing with the database.
)
var/list/unviewable_varedit_object_types = list(
/datum/BSQL_Connection/,
/datum/BSQL_Operation/,
)
//Interface for editing a variable. It returns its new value. If edited_datum, it automatically changes the edited datum's value
//If called with just [user] argument, it allows you to create a value such as a string, a number, an empty list, a nearby object, etc...
//If called with [edited_datum] and [edited_variable], you gain the ability to get the variable's initial value.
@@ -23,6 +29,9 @@ var/list/forbidden_varedit_object_types = list(
if(!C.can_edit_var(edited_variable, edited_datum?.type))
return
if (is_type_in_list(edited_datum, unviewable_varedit_object_types) || BSQL_DEBUG_CONNECTION)
return
//Special case for "appearance", because appearance values can't be stored anywhere.
//It's impossible for this proc to return an appearance value, so just set it directly here
if((isimage(edited_datum) || isatom(edited_datum)) && edited_variable == "appearance")

View File

@@ -252,10 +252,9 @@
if(IsGuestKey(key))
return
establish_db_connection()
if(!dbcon.IsConnected())
if(!SSdbcore.Connect())
return
var/list/http[] = world.Export("http://www.byond.com/members/[src.key]?format=text") // Retrieve information from BYOND
var/Joined = 2550-01-01
if(http && http.len && ("CONTENT" in http))
@@ -267,32 +266,43 @@
var/sql_ckey = sanitizeSQL(ckey)
var/age
testing("sql_ckey = [sql_ckey]")
var/DBQuery/query = dbcon.NewQuery("SELECT id, datediff(Now(),firstseen) as age, datediff(Now(),accountjoined) as age2 FROM erro_player WHERE ckey = '[sql_ckey]'")
query.Execute()
var/datum/DBQuery/query = SSdbcore.NewQuery("SELECT id, datediff(Now(),firstseen) as age, datediff(Now(),accountjoined) as age2 FROM erro_player WHERE ckey = '[sql_ckey]'")
if(!query.Execute())
message_admins("Error: [query.ErrorMsg()]")
log_sql("Error: [query.ErrorMsg()]")
qdel(query)
return
var/sql_id = 0
while(query.NextRow())
sql_id = query.item[1]
player_age = text2num(query.item[2])
age = text2num(query.item[3])
break
qdel(query)
var/sql_address = sanitizeSQL(address)
var/DBQuery/query_ip = dbcon.NewQuery("SELECT distinct ckey FROM erro_connection_log WHERE ip = '[sql_address]'")
query_ip.Execute()
var/datum/DBQuery/query_ip = SSdbcore.NewQuery("SELECT distinct ckey FROM erro_connection_log WHERE ip = '[sql_address]'")
if(!query_ip.Execute())
log_sql("Error: [query_ip.ErrorMsg()]")
qdel(query_ip)
return
related_accounts_ip = ""
while(query_ip.NextRow())
related_accounts_ip += "[query_ip.item[1]], "
qdel(query_ip)
var/sql_computerid = sanitizeSQL(computer_id)
var/DBQuery/query_cid = dbcon.NewQuery("SELECT distinct ckey FROM erro_connection_log WHERE computerid = '[sql_computerid]'")
query_cid.Execute()
var/datum/DBQuery/query_cid = SSdbcore.NewQuery("SELECT distinct ckey FROM erro_connection_log WHERE computerid = '[sql_computerid]'")
if(!query_cid.Execute())
log_sql("Error: [query_cid.ErrorMsg()]")
qdel(query_cid)
return
related_accounts_cid = ""
while(query_cid.NextRow())
related_accounts_cid += "[query_cid.item[1]], "
qdel(query_cid)
//Just the standard check to see if it's actually a number
if(sql_id)
@@ -310,27 +320,29 @@
if(sql_id)
//Player already identified previously, we need to just update the 'lastseen', 'ip' and 'computer_id' variables
var/DBQuery/query_update
var/datum/DBQuery/query_update
if(isnum(age))
query_update = dbcon.NewQuery("UPDATE erro_player SET lastseen = Now(), ip = '[sql_address]', computerid = '[sql_computerid]', lastadminrank = '[sql_admin_rank]' WHERE id = [sql_id]")
query_update = SSdbcore.NewQuery("UPDATE erro_player SET lastseen = Now(), ip = '[sql_address]', computerid = '[sql_computerid]', lastadminrank = '[sql_admin_rank]' WHERE id = [sql_id]")
else
query_update = dbcon.NewQuery("UPDATE erro_player SET lastseen = Now(), ip = '[sql_address]', computerid = '[sql_computerid]', lastadminrank = '[sql_admin_rank]', accountjoined = '[Joined]' WHERE id = [sql_id]")
query_update = SSdbcore.NewQuery("UPDATE erro_player SET lastseen = Now(), ip = '[sql_address]', computerid = '[sql_computerid]', lastadminrank = '[sql_admin_rank]', accountjoined = '[Joined]' WHERE id = [sql_id]")
query_update.Execute()
if(query_update.ErrorMsg())
WARNING("FINGERPRINT: [query_update.ErrorMsg()]")
qdel(query_update)
else
//New player!! Need to insert all the stuff
var/DBQuery/query_insert = dbcon.NewQuery("INSERT INTO erro_player (id, ckey, firstseen, lastseen, ip, computerid, lastadminrank, accountjoined) VALUES (null, '[sql_ckey]', Now(), Now(), '[sql_address]', '[sql_computerid]', '[sql_admin_rank]', '[Joined]')")
var/datum/DBQuery/query_insert = SSdbcore.NewQuery("INSERT INTO erro_player (id, ckey, firstseen, lastseen, ip, computerid, lastadminrank, accountjoined) VALUES (null, '[sql_ckey]', Now(), Now(), '[sql_address]', '[sql_computerid]', '[sql_admin_rank]', '[Joined]')")
query_insert.Execute()
if(query_insert.ErrorMsg())
WARNING("FINGERPRINT: [query_insert.ErrorMsg()]")
qdel(query_insert)
if(!isnum(age))
var/DBQuery/query_age = dbcon.NewQuery("SELECT datediff(Now(),accountjoined) as age2 FROM erro_player WHERE ckey = '[sql_ckey]'")
query_age.Execute()
var/datum/DBQuery/query_age = SSdbcore.NewQuery("SELECT datediff(Now(),accountjoined) as age2 FROM erro_player WHERE ckey = '[sql_ckey]'")
if(!query_age.Execute())
WARNING("FINGERPRINT: [query_age.ErrorMsg()]")
while(query_age.NextRow())
age = text2num(query_age.item[1])
qdel(query_age)
if(!isnum(player_age))
player_age = 0
if(age < 14)
@@ -342,11 +354,12 @@
// logging player access
var/server_address_port = "[world.internet_address]:[world.port]"
var/sql_server_address_port = sanitizeSQL(server_address_port)
var/DBQuery/query_connection_log = dbcon.NewQuery("INSERT INTO `erro_connection_log`(`id`,`datetime`,`serverip`,`ckey`,`ip`,`computerid`) VALUES(null,Now(),'[sql_server_address_port]','[sql_ckey]','[sql_address]','[sql_computerid]');")
var/datum/DBQuery/query_connection_log = SSdbcore.NewQuery("INSERT INTO `erro_connection_log`(`id`,`datetime`,`serverip`,`ckey`,`ip`,`computerid`) VALUES(null,Now(),'[sql_server_address_port]','[sql_ckey]','[sql_address]','[sql_computerid]');")
query_connection_log.Execute()
if(query_connection_log.ErrorMsg())
WARNING("FINGERPRINT: [query_connection_log.ErrorMsg()]")
qdel(query_connection_log)
#undef TOPIC_SPAM_DELAY
#undef UPLOAD_LIMIT

View File

@@ -1,7 +1,7 @@
/proc/EquipCustomItems(mob/living/carbon/human/M)
// testing("\[CustomItem\] Checking for custom items for [M.ckey] ([M.real_name])...")
if(!establish_db_connection())
if(!SSdbcore.Connect())
return
// SCHEMA
@@ -19,8 +19,12 @@
*/
// Grab the info we want.
var/DBQuery/query = dbcon.NewQuery("SELECT cuiPath, cuiPropAdjust, cuiJobMask FROM CustomUserItems WHERE cuiCKey='[M.ckey]' AND (cuiRealName='[M.real_name]' OR cuiRealName='*')")
query.Execute()
var/datum/DBQuery/query = SSdbcore.NewQuery("SELECT cuiPath, cuiPropAdjust, cuiJobMask FROM CustomUserItems WHERE cuiCKey='[M.ckey]' AND (cuiRealName='[M.real_name]' OR cuiRealName='*')")
if(!query.Execute())
message_admins("Error: [query.ErrorMsg()]")
log_sql("Error: [query.ErrorMsg()]")
qdel(query)
return
while(query.NextRow())
var/path = text2path(query.item[1])
@@ -81,7 +85,7 @@
Item.forceMove(get_turf(M.loc))
HackProperties(Item,propadjust)
qdel(query)
// This is hacky, but since it's difficult as fuck to make a proper parser in BYOND without killing the server, here it is. - N3X
/proc/HackProperties(var/mob/living/carbon/human/M,var/obj/item/I,var/script)
@@ -114,4 +118,4 @@
if(typeChunks.len==2)
I.vars[varname]=new /icon(typeChunks[2])
if(typeChunks.len==3)
I.vars[varname]=new /icon(typeChunks[2],typeChunks[3])
I.vars[varname]=new /icon(typeChunks[2],typeChunks[3])

View File

@@ -48,10 +48,12 @@
//sql += " [query.toSQL()]"
// Pagination
// to_chat(world, sql)
var/DBQuery/_query = dbcon_old.NewQuery(sql)
var/datum/DBQuery/_query = SSdbcore.NewQuery(sql)
_query.Execute()
if(_query.ErrorMsg())
world.log << _query.ErrorMsg()
qdel(_query)
return
var/list/results = list()
while(_query.NextRow())
@@ -64,6 +66,7 @@
"ckey" =_query.item[5]
))
results += CB
qdel(_query)
return results
/obj/machinery/computer/library/proc/get_num_results()
@@ -71,8 +74,11 @@
//if(query)
//sql += query.toSQL()
var/DBQuery/_query = dbcon_old.NewQuery(sql)
_query.Execute()
var/datum/DBQuery/_query = SSdbcore.NewQuery(sql)
if(!_query.Execute())
message_admins("Error: [_query.ErrorMsg()]")
log_sql("Error: [_query.ErrorMsg()]")
return
while(_query.NextRow())
return text2num(_query.item[1])
return 0

View File

@@ -93,7 +93,7 @@
<A href='?src=\ref[src];switchscreen=0'>(Return to main menu)</A><BR>"}
if(4)
dat += "<h3>External Archive</h3>"
if(!dbcon_old.IsConnected())
if(!SSdbcore.IsConnected())
dat += "<font color=red><b>ERROR</b>: Unable to contact External Archive. Please contact your system administrator for assistance.</font>"
else
num_results = src.get_num_results()
@@ -267,14 +267,16 @@
var/datum/cachedbook/target = getBookByID(href_list["del"]) // Sanitized in getBookByID
var/ans = alert(usr, "Are you sure you wish to delete \"[target.title]\", by [target.author]? This cannot be undone.", "Library System", "Yes", "No")
if(ans=="Yes")
var/DBQuery/query = dbcon_old.NewQuery("DELETE FROM library WHERE id=[target.id]")
var/datum/DBQuery/query = SSdbcore.NewQuery("DELETE FROM library WHERE id=[target.id]")
var/response = query.Execute()
if(!response)
to_chat(usr, query.ErrorMsg())
qdel(query)
return
log_admin("LIBRARY: [usr.name]/[usr.key] has deleted \"[target.title]\", by [target.author] ([target.ckey])!")
message_admins("[key_name_admin(usr)] has deleted \"[target.title]\", by [target.author] ([target.ckey])!")
src.updateUsrDialog()
qdel(query)
return
if(href_list["delbyckey"])
@@ -284,17 +286,19 @@
var/tckey = ckey(href_list["delbyckey"])
var/ans = alert(usr,"Are you sure you wish to delete all books by [tckey]? This cannot be undone.", "Library System", "Yes", "No")
if(ans=="Yes")
var/DBQuery/query = dbcon_old.NewQuery("DELETE FROM library WHERE ckey='[sanitizeSQL(tckey)]'")
var/response = query.Execute()
var/datum/DBQuery/query = SSdbcore.NewQuery("DELETE FROM library WHERE ckey='[sanitizeSQL(tckey)]'")
var/datum/DBQuery/response = query.Execute()
if(!response)
to_chat(usr, query.ErrorMsg())
qdel(query)
return
var/affected=query.RowsAffected()
if(affected==0)
if(response.item.len==0)
to_chat(usr, "<span class='danger'>Unable to find any matching rows.</span>")
qdel(query)
return
log_admin("LIBRARY: [usr.name]/[usr.key] has deleted [affected] books written by [tckey]!")
message_admins("[key_name_admin(usr)] has deleted [affected] books written by [tckey]!")
log_admin("LIBRARY: [usr.name]/[usr.key] has deleted [response.item.len] books written by [tckey]!")
message_admins("[key_name_admin(usr)] has deleted [response.item.len] books written by [tckey]!")
qdel(query)
src.updateUsrDialog()
return
@@ -390,15 +394,14 @@
if(scanner.cache)
var/choice = input("Are you certain you wish to upload this title to the Archive?") in list("Confirm", "Abort")
if(choice == "Confirm")
establish_old_db_connection()
if(!dbcon_old.IsConnected())
if(!SSdbcore.Connect())
alert("Connection to Archive has been severed. Aborting.")
else
var/sqltitle = sanitizeSQL(scanner.cache.name)
var/sqlauthor = sanitizeSQL(scanner.cache.author)
var/sqlcontent = sanitizeSQL(scanner.cache.dat)
var/sqlcategory = sanitizeSQL(upload_category)
var/DBQuery/query = dbcon_old.NewQuery("INSERT INTO library (author, title, content, category, ckey) VALUES ('[sqlauthor]', '[sqltitle]', '[sqlcontent]', '[sqlcategory]', '[ckey(usr.key)]')")
var/datum/DBQuery/query = SSdbcore.NewQuery("INSERT INTO library (author, title, content, category, ckey) VALUES ('[sqlauthor]', '[sqltitle]', '[sqlcontent]', '[sqlcategory]', '[ckey(usr.key)]')")
var/response = query.Execute()
if(!response)
to_chat(usr, query.ErrorMsg())
@@ -413,7 +416,7 @@
if(!href_list["id"])
return
if(!dbcon_old.IsConnected())
if(!SSdbcore.IsConnected())
alert("Connection to Archive has been severed. Aborting.")
return
@@ -438,7 +441,7 @@
return
var/bookid = href_list["manual"]
if(!dbcon_old.IsConnected())
if(!SSdbcore.IsConnected())
alert("Connection to Archive has been severed. Aborting.")
return

View File

@@ -20,8 +20,7 @@
<A href='?src=\ref[src];setauthor=1'>Filter by Author: [query.author]</A><br />
<A href='?src=\ref[src];search=1'>\[Start Search\]</A><br />"}
if(1)
establish_old_db_connection()
if(!dbcon_old.IsConnected())
if(!SSdbcore.Connect())
dat += "<font color=red><b>ERROR</b>: Unable to contact External Archive. Please contact your system administrator for assistance.</font><br />"
else if(num_results == 0)
dat += "<em>No results found.</em>"

View File

@@ -91,8 +91,11 @@
var/sqlid = text2num(id)
if(!sqlid)
return
var/DBQuery/query = dbcon_old.NewQuery("DELETE FROM library WHERE id=[sqlid]")
query.Execute()
var/datum/DBQuery/query = SSdbcore.NewQuery("DELETE FROM library WHERE id=[sqlid]")
if(!query.Execute())
message_admins("Error: [query.ErrorMsg()]")
log_sql("Error: [query.ErrorMsg()]")
qdel(query)
/datum/library_catalog/proc/getBookByID(var/id as text)
if("[id]" in cached_books)
@@ -101,8 +104,13 @@
var/sqlid = text2num(id)
if(!sqlid)
return
var/DBQuery/query = dbcon_old.NewQuery("SELECT id, author, title, category, ckey FROM library WHERE id=[sqlid]")
query.Execute()
var/datum/DBQuery/query = SSdbcore.NewQuery("SELECT id, author, title, category, ckey FROM library WHERE id=[sqlid]")
if(!query.Execute())
message_admins("Error: [query.ErrorMsg()]")
log_sql("Error: [query.ErrorMsg()]")
qdel(query)
return
qdel(query)
var/list/results=list()
while(query.NextRow())

View File

@@ -0,0 +1,19 @@
/datum/migration/mysql/ss13/_010
id = 10
name = "Admin-only polls"
/datum/migration/mysql/ss13/_010/up()
if(!hasColumn("erro_poll_question","hidden"))
return execute("ALTER TABLE erro_poll_question ADD COLUMN `hidden` BOOLEAN NULL;");
else
warning("hidden column exists. Skipping addition.")
return TRUE
/datum/migration/mysql/ss13/_010/down()
if(hasColumn("erro_poll_question","hidden"))
return execute("ALTER TABLE erro_poll_question DROP COLUMN `hidden` BOOLEAN NULL;");
else
warning("hidden column does not exist. Skipping drop.")
return TRUE

View File

@@ -0,0 +1,30 @@
/datum/migration/mysql/ss13/_011
id = 11
name = "Library in feedback DB"
/datum/migration/mysql/ss13/_011/up()
if(!hasTable("library"))
return execute({"CREATE TABLE IF NOT EXISTS `library` (
`id` INT(11) NOT NULL AUTO_INCREMENT ,
`author` TEXT NOT NULL ,
`title` TEXT NOT NULL ,
`content` TEXT NOT NULL ,
`category` TEXT NOT NULL ,
`ckey` VARCHAR(32) NULL ,
PRIMARY KEY (`id`) )
ENGINE = MyISAM
AUTO_INCREMENT = 184
DEFAULT CHARACTER SET = latin1;
"});
else
warning("library table exists. Skipping addition.")
return TRUE
/datum/migration/mysql/ss13/_011/down()
if(hasTable("library"))
return execute("DROP TABLE library");
else
warning("library table doesn't exist. Skipping drop.")
return TRUE

View File

@@ -0,0 +1,29 @@
/datum/migration/mysql/ss13/_012
id = 12
name = "Customitems in feedback DB"
/datum/migration/mysql/ss13/_012/up()
if(!hasTable("customitems"))
return execute({"CREATE TABLE IF NOT EXISTS `customitems` (
cuiCKey VARCHAR(36) NOT NULL,
cuiRealName VARCHAR(60) NOT NULL,
cuiPath VARCHAR(255) NOT NULL,
cuiDescription TEXT NOT NULL,
cuiReason TEXT NOT NULL,
cuiPropAdjust TEXT NOT NULL,
cuiJobMask TEXT NOT NULL,
PRIMARY KEY(cuiCkey,cuiRealName,cuiPath)
)
"});
else
warning("customitems table exists. Skipping addition.")
return TRUE
/datum/migration/mysql/ss13/_012/down()
if(hasTable("customitems"))
return execute("DROP TABLE customitems");
else
warning("customitems table doesn't exist. Skipping drop.")
return TRUE

View File

@@ -0,0 +1,19 @@
/datum/migration/mysql/ss13/_013
id = 13
name = "Ckey creator in polls"
/datum/migration/mysql/ss13/_013/up()
if(!hasColumn("erro_poll_question","createdby_ckey"))
return execute("ALTER TABLE erro_poll_question ADD COLUMN `createdby_ckey` VARCHAR(32) NULL;");
else
warning("createdby_ckey column exists. Skipping addition.")
return TRUE
/datum/migration/mysql/ss13/_013/down()
if(hasColumn("erro_poll_question","createdby_ckey"))
return execute("ALTER TABLE erro_poll_question DROP COLUMN `createdby_ckey` VARCHAR(32) NULL;");
else
warning("createdby_ckey column does not exist. Skipping drop.")
return TRUE

View File

@@ -0,0 +1,19 @@
/datum/migration/mysql/ss13/_014
id = 14
name = "IP creator in polls"
/datum/migration/mysql/ss13/_014/up()
if(!hasColumn("erro_poll_question","createdby_ip"))
return execute("ALTER TABLE erro_poll_question ADD COLUMN `createdby_ip` VARCHAR(32) NULL;");
else
warning("createdby_ip column exists. Skipping addition.")
return TRUE
/datum/migration/mysql/ss13/_013/down()
if(hasColumn("erro_poll_question","createdby_ip"))
return execute("ALTER TABLE erro_poll_question DROP COLUMN `createdby_ip` VARCHAR(32) NULL;");
else
warning("createdby_ip column does not exist. Skipping drop.")
return TRUE

View File

@@ -97,7 +97,7 @@
return TRUE
/datum/migration_controller/proc/query(var/sql)
var/DBQuery/query = execute(sql)
var/datum/DBQuery/query = execute(sql)
var/list/rows=list()
while(query.NextRow())

View File

@@ -2,19 +2,19 @@ var/global/datum/migration_controller/mysql/migration_controller_mysql = null
/datum/migration_controller/mysql
id="mysql"
var/DBConnection/db
var/datum/subsystem/dbcore/db
/datum/migration_controller/mysql/setup()
if(!dbcon || !istype(dbcon) || !dbcon.IsConnected())
warning("Something wrong with dbcon.")
if(!SSdbcore || !istype(SSdbcore) || !SSdbcore.IsConnected())
warning("Something wrong with SSdbcore.")
return FALSE
var/DBQuery/Q = dbcon.NewQuery()
var/datum/DBQuery/Q = SSdbcore.NewQuery()
if(!Q)
warning("Something wrong with dbcon.NewQuery()")
warning("Something wrong with SSdbcore.NewQuery()")
return FALSE
Q.Close()
qdel(Q)
//testing("MySQL is okay")
db = dbcon
db = SSdbcore
return TRUE
/datum/migration_controller/mysql/createMigrationTable()
@@ -27,25 +27,28 @@ CREATE TABLE IF NOT EXISTS [TABLE_NAME] (
execute(tableSQL)
/datum/migration_controller/mysql/query(var/sql)
var/DBQuery/query = execute(sql)
var/datum/DBQuery/query = execute(sql)
var/list/rows=list()
while(query.NextRow())
rows[++rows.len] = query.item.Copy()
qdel(query)
return rows
/datum/migration_controller/mysql/hasResult(var/sql)
var/DBQuery/query = execute(sql)
var/datum/DBQuery/query = execute(sql)
if (query.NextRow())
qdel(query)
return TRUE
qdel(query)
return FALSE
/datum/migration_controller/mysql/execute(var/sql)
var/DBQuery/query = db.NewQuery(sql)
var/datum/DBQuery/query = db.NewQuery(sql)
query.Execute()
return query
. = query
/datum/migration_controller/mysql/hasTable(var/tableName)
return hasResult("SHOW TABLES LIKE '[tableName]")
return hasResult("SHOW TABLES LIKE '[tableName]'")

View File

@@ -1,5 +1,5 @@
/datum/migration/mysql
var/DBConnection/db
var/datum/subsystem/dbcore/db
dbms="mysql"
/datum/migration/mysql/New(var/datum/migration_controller/mysql/mc)
@@ -8,31 +8,38 @@
db=mc.db
/datum/migration/mysql/query(var/sql)
var/DBQuery/query = db.NewQuery(sql)
var/datum/DBQuery/query = db.NewQuery(sql)
if(!query.Execute())
world.log << "Error in [package]#[id]: [query.ErrorMsg()]"
qdel(query)
return FALSE
var/list/rows=list()
while(query.NextRow())
rows += list(query.item)
qdel(query)
return rows
/datum/migration/mysql/hasResult(var/sql)
var/DBQuery/query = db.NewQuery(sql)
var/datum/DBQuery/query = db.NewQuery(sql)
if(!query.Execute())
world.log << "Error in [package]#[id]: [query.ErrorMsg()]"
qdel(query)
return FALSE
if (query.NextRow())
qdel(query)
return TRUE
qdel(query)
return FALSE
/datum/migration/mysql/execute(var/sql)
var/DBQuery/query = db.NewQuery(sql)
var/datum/DBQuery/query = db.NewQuery(sql)
if(!query.Execute())
world.log << "Error in [package]#[id]: [query.ErrorMsg()]"
qdel(query)
return FALSE
qdel(query)
return TRUE
/datum/migration/mysql/hasTable(var/tableName)

View File

@@ -40,19 +40,20 @@
output += "<p><a href='byond://?src=\ref[src];observe=1'>Observe</A></p>"
if(!IsGuestKey(src.key))
establish_db_connection()
if(dbcon.IsConnected())
if(SSdbcore.Connect())
var/isadmin = 0
if(src.client && src.client.holder)
isadmin = 1
var/DBQuery/query = dbcon.NewQuery("SELECT id FROM erro_poll_question WHERE [(isadmin ? "" : "adminonly = false AND")] hidden IS NULL AND Now() BETWEEN starttime AND endtime AND id NOT IN (SELECT pollid FROM erro_poll_vote WHERE ckey = \"[ckey]\") AND id NOT IN (SELECT pollid FROM erro_poll_textreply WHERE ckey = \"[ckey]\")")
query.Execute()
var/datum/DBQuery/query = SSdbcore.NewQuery("SELECT id FROM erro_poll_question WHERE [(isadmin ? "" : "adminonly = false AND")] hidden IS NULL AND Now() BETWEEN starttime AND endtime AND id NOT IN (SELECT pollid FROM erro_poll_vote WHERE ckey = \"[ckey]\") AND id NOT IN (SELECT pollid FROM erro_poll_textreply WHERE ckey = \"[ckey]\")")
if(!query.Execute())
log_sql("Error fetching poll question: [query.ErrorMsg()]")
qdel(query)
return
var/newpoll = 0
while(query.NextRow())
newpoll = 1
break
qdel(query)
if(newpoll)
output += "<p><b><a href='byond://?src=\ref[src];showpoll=1'>Show Player Polls</A> (NEW!)</b></p>"
else

View File

@@ -3,14 +3,17 @@
var/optiontext
/mob/new_player/proc/handle_player_polling()
establish_db_connection()
if(dbcon.IsConnected())
if(SSdbcore.Connect())
var/isadmin = 0
if(src.client && src.client.holder)
isadmin = 1
var/DBQuery/select_query = dbcon.NewQuery("SELECT id, question FROM erro_poll_question WHERE [(isadmin ? "" : "adminonly = false AND")] hidden IS NULL AND Now() BETWEEN starttime AND endtime")
select_query.Execute()
var/datum/DBQuery/select_query = SSdbcore.NewQuery("SELECT id, question FROM erro_poll_question WHERE [(isadmin ? "" : "adminonly = false AND")] hidden IS NULL AND Now() BETWEEN starttime AND endtime")
if(!select_query.Execute())
message_admins("Error: [select_query.ErrorMsg()]")
log_sql("Error: [select_query.ErrorMsg()]")
qdel(select_query)
return
var/output = {"<div align='center'><B>Player polls</B>
@@ -30,7 +33,7 @@
i++
output += "</table>"
qdel(select_query)
src << browse(output,"window=playerpolllist;size=500x300")
@@ -38,11 +41,14 @@
/mob/new_player/proc/poll_player(var/pollid = -1)
if(pollid == -1)
return
establish_db_connection()
if(dbcon.IsConnected())
if(SSdbcore.Connect())
var/DBQuery/select_query = dbcon.NewQuery("SELECT starttime, endtime, question, polltype, multiplechoiceoptions FROM erro_poll_question WHERE id = [pollid]")
select_query.Execute()
var/datum/DBQuery/select_query = SSdbcore.NewQuery("SELECT starttime, endtime, question, polltype, multiplechoiceoptions FROM erro_poll_question WHERE id = [pollid]")
if(!select_query.Execute())
message_admins("Error: [select_query.ErrorMsg()]")
log_sql("Error: [select_query.ErrorMsg()]")
qdel(select_query)
return
var/pollstarttime = ""
var/pollendtime = ""
@@ -58,6 +64,7 @@
polltype = select_query.item[4]
found = 1
break
qdel(select_query)
if(!found)
to_chat(usr, "<span class='warning'>Poll question details not found.</span>")
@@ -66,8 +73,12 @@
switch(polltype)
//Polls that have enumerated options
if("OPTION")
var/DBQuery/voted_query = dbcon.NewQuery("SELECT optionid FROM erro_poll_vote WHERE pollid = [pollid] AND ckey = '[usr.ckey]'")
voted_query.Execute()
var/datum/DBQuery/voted_query = SSdbcore.NewQuery("SELECT optionid FROM erro_poll_vote WHERE pollid = [pollid] AND ckey = '[usr.ckey]'")
if(!voted_query.Execute())
message_admins("Error: [voted_query.ErrorMsg()]")
log_sql("Error: [voted_query.ErrorMsg()]")
qdel(voted_query)
return
var/voted = 0
var/votedoptionid = 0
@@ -75,11 +86,15 @@
votedoptionid = text2num(voted_query.item[1])
voted = 1
break
qdel(voted_query)
var/list/datum/polloption/options = list()
var/DBQuery/options_query = dbcon.NewQuery("SELECT id, text FROM erro_poll_option WHERE pollid = [pollid]")
options_query.Execute()
var/datum/DBQuery/options_query = SSdbcore.NewQuery("SELECT id, text FROM erro_poll_option WHERE pollid = [pollid]")
if(!options_query.Execute())
message_admins("Error: [options_query.ErrorMsg()]")
log_sql("Error: [options_query.ErrorMsg()]")
return
while(options_query.NextRow())
var/datum/polloption/PO = new()
PO.optionid = text2num(options_query.item[1])
@@ -122,8 +137,12 @@
//Polls with a text input
if("TEXT")
var/DBQuery/voted_query = dbcon.NewQuery("SELECT replytext FROM erro_poll_textreply WHERE pollid = [pollid] AND ckey = '[usr.ckey]'")
voted_query.Execute()
var/datum/DBQuery/voted_query = SSdbcore.NewQuery("SELECT replytext FROM erro_poll_textreply WHERE pollid = [pollid] AND ckey = '[usr.ckey]'")
if(!voted_query.Execute())
message_admins("Error: [voted_query.ErrorMsg()]")
log_sql("Error: [voted_query.ErrorMsg()]")
qdel(voted_query)
return
var/voted = 0
var/vote_text = ""
@@ -131,7 +150,7 @@
vote_text = voted_query.item[1]
voted = 1
break
qdel(voted_query)
var/output = "<div align='center'><B>Player poll</B>"
@@ -164,8 +183,12 @@
//Polls with a text input
if("NUMVAL")
var/DBQuery/voted_query = dbcon.NewQuery("SELECT o.text, v.rating FROM erro_poll_option o, erro_poll_vote v WHERE o.pollid = [pollid] AND v.ckey = '[usr.ckey]' AND o.id = v.optionid")
voted_query.Execute()
var/datum/DBQuery/voted_query = SSdbcore.NewQuery("SELECT o.text, v.rating FROM erro_poll_option o, erro_poll_vote v WHERE o.pollid = [pollid] AND v.ckey = '[usr.ckey]' AND o.id = v.optionid")
if(!voted_query.Execute())
message_admins("Error: [voted_query.ErrorMsg()]")
log_sql("Error: [voted_query.ErrorMsg()]")
qdel(voted_query)
return
var/output = "<div align='center'><B>Player poll</B>"
@@ -181,6 +204,7 @@
var/rating = voted_query.item[2]
output += "<br><b>[optiontext] - [rating]</b>"
qdel(voted_query)
if(!voted) //Only make this a form if we have not voted yet
@@ -192,8 +216,12 @@
var/minid = 999999
var/maxid = 0
var/DBQuery/option_query = dbcon.NewQuery("SELECT id, text, minval, maxval, descmin, descmid, descmax FROM erro_poll_option WHERE pollid = [pollid]")
option_query.Execute()
var/datum/DBQuery/option_query = SSdbcore.NewQuery("SELECT id, text, minval, maxval, descmin, descmid, descmax FROM erro_poll_option WHERE pollid = [pollid]")
if(!option_query.Execute())
message_admins("Error: [option_query.ErrorMsg()]")
log_sql("Error: [option_query.ErrorMsg()]")
qdel(option_query)
return
while(option_query.NextRow())
var/optionid = text2num(option_query.item[1])
var/optiontext = option_query.item[2]
@@ -234,24 +262,33 @@
<input type='hidden' name='maxid' value='[maxid]'>
<p><input type='submit' value='Submit'>
</form>"}
qdel(option_query)
src << browse(output,"window=playerpoll;size=500x500")
if("MULTICHOICE")
var/DBQuery/voted_query = dbcon.NewQuery("SELECT optionid FROM erro_poll_vote WHERE pollid = [pollid] AND ckey = '[usr.ckey]'")
voted_query.Execute()
var/datum/DBQuery/voted_query = SSdbcore.NewQuery("SELECT optionid FROM erro_poll_vote WHERE pollid = [pollid] AND ckey = '[usr.ckey]'")
if(!voted_query.Execute())
message_admins("Error: [voted_query.ErrorMsg()]")
log_sql("Error: [voted_query.ErrorMsg()]")
qdel(voted_query)
return
var/list/votedfor = list()
var/voted = 0
while(voted_query.NextRow())
votedfor.Add(text2num(voted_query.item[1]))
voted = 1
qdel(voted_query)
var/list/datum/polloption/options = list()
var/maxoptionid = 0
var/minoptionid = 0
var/DBQuery/options_query = dbcon.NewQuery("SELECT id, text FROM erro_poll_option WHERE pollid = [pollid]")
options_query.Execute()
var/datum/DBQuery/options_query = SSdbcore.NewQuery("SELECT id, text FROM erro_poll_option WHERE pollid = [pollid]")
if(!options_query.Execute())
message_admins("Error: [options_query.ErrorMsg()]")
log_sql("Error: [options_query.ErrorMsg()]")
qdel(options_query)
return
while(options_query.NextRow())
var/datum/polloption/PO = new()
PO.optionid = text2num(options_query.item[1])
@@ -309,11 +346,14 @@
if(!isnum(pollid) || !isnum(optionid))
return
establish_db_connection()
if(dbcon.IsConnected())
if(SSdbcore.Connect())
var/DBQuery/select_query = dbcon.NewQuery("SELECT starttime, endtime, question, polltype, multiplechoiceoptions FROM erro_poll_question WHERE id = [pollid] AND Now() BETWEEN starttime AND endtime")
select_query.Execute()
var/datum/DBQuery/select_query = SSdbcore.NewQuery("SELECT starttime, endtime, question, polltype, multiplechoiceoptions FROM erro_poll_question WHERE id = [pollid] AND Now() BETWEEN starttime AND endtime")
if(!select_query.Execute())
message_admins("Error: [select_query.ErrorMsg()]")
log_sql("Error: [select_query.ErrorMsg()]")
qdel(select_query)
return
var/validpoll = 0
var/multiplechoiceoptions = 0
@@ -325,19 +365,25 @@
if(select_query.item[5])
multiplechoiceoptions = text2num(select_query.item[5])
break
qdel(select_query)
if(!validpoll)
to_chat(usr, "<span class='warning'>Poll is not valid.</span>")
return
var/DBQuery/select_query2 = dbcon.NewQuery("SELECT id FROM erro_poll_option WHERE id = [optionid] AND pollid = [pollid]")
select_query2.Execute()
var/datum/DBQuery/select_query2 = SSdbcore.NewQuery("SELECT id FROM erro_poll_option WHERE id = [optionid] AND pollid = [pollid]")
if(!select_query2.Execute())
message_admins("Error: [select_query.ErrorMsg()]")
log_sql("Error: [select_query.ErrorMsg()]")
qdel(select_query2)
return
var/validoption = 0
while(select_query2.NextRow())
validoption = 1
break
qdel(select_query2)
if(!validoption)
to_chat(usr, "<span class='warning'>Poll option is not valid.</span>")
@@ -345,8 +391,11 @@
var/alreadyvoted = 0
var/DBQuery/voted_query = dbcon.NewQuery("SELECT id FROM erro_poll_vote WHERE pollid = [pollid] AND ckey = '[usr.ckey]'")
voted_query.Execute()
var/datum/DBQuery/voted_query = SSdbcore.NewQuery("SELECT id FROM erro_poll_vote WHERE pollid = [pollid] AND ckey = '[usr.ckey]'")
if(!voted_query.Execute())
message_admins("Error: [voted_query.ErrorMsg()]")
log_sql("Error: [voted_query.ErrorMsg()]")
return
while(voted_query.NextRow())
alreadyvoted += 1
@@ -366,8 +415,13 @@
adminrank = usr.client.holder.rank
var/DBQuery/insert_query = dbcon.NewQuery("INSERT INTO erro_poll_vote (id ,datetime ,pollid ,optionid ,ckey ,ip ,adminrank) VALUES (null, Now(), [pollid], [optionid], '[usr.ckey]', '[usr.client.address]', '[adminrank]')")
insert_query.Execute()
var/datum/DBQuery/insert_query = SSdbcore.NewQuery("INSERT INTO erro_poll_vote (id ,datetime ,pollid ,optionid ,ckey ,ip ,adminrank) VALUES (null, Now(), [pollid], [optionid], '[usr.ckey]', '[usr.client.address]', '[adminrank]')")
if(!insert_query.Execute())
message_admins("Error: [insert_query.ErrorMsg()]")
log_sql("Error: [insert_query.ErrorMsg()]")
qdel(insert_query)
return
qdel(insert_query)
to_chat(usr, "<span class='notice'>Vote successful.</span>")
usr << browse(null,"window=playerpoll")
@@ -379,19 +433,24 @@
if(!isnum(pollid) || !istext(replytext))
return
establish_db_connection()
if(dbcon.IsConnected())
if(SSdbcore.Connect())
var/DBQuery/select_query = dbcon.NewQuery("SELECT starttime, endtime, question, polltype FROM erro_poll_question WHERE id = [pollid] AND Now() BETWEEN starttime AND endtime")
select_query.Execute()
var/datum/DBQuery/select_query = SSdbcore.NewQuery("SELECT starttime, endtime, question, polltype FROM erro_poll_question WHERE id = [pollid] AND Now() BETWEEN starttime AND endtime")
if(!select_query.Execute())
message_admins("Error: [select_query.ErrorMsg()]")
log_sql("Error: [select_query.ErrorMsg()]")
qdel(select_query)
return
var/validpoll = 0
while(select_query.NextRow())
if(select_query.item[4] != "TEXT")
qdel(select_query)
return
validpoll = 1
break
qdel(select_query)
if(!validpoll)
to_chat(usr, "<span class='warning'>Poll is not valid.</span>")
@@ -399,12 +458,17 @@
var/alreadyvoted = 0
var/DBQuery/voted_query = dbcon.NewQuery("SELECT id FROM erro_poll_textreply WHERE pollid = [pollid] AND ckey = '[usr.ckey]'")
voted_query.Execute()
var/datum/DBQuery/voted_query = SSdbcore.NewQuery("SELECT id FROM erro_poll_textreply WHERE pollid = [pollid] AND ckey = '[usr.ckey]'")
if(!voted_query.Execute())
message_admins("Error: [voted_query.ErrorMsg()]")
log_sql("Error: [voted_query.ErrorMsg()]")
qdel(voted_query)
return
while(voted_query.NextRow())
alreadyvoted = 1
break
qdel(voted_query)
if(alreadyvoted)
to_chat(usr, "<span class='warning'>You already sent your feedback for this poll.</span>")
@@ -417,6 +481,7 @@
replytext = replacetext(replytext, "%BR%", "")
replytext = replacetext(replytext, "\n", "%BR%")
replytext = replacetext(replytext, "'", "\'")
var/text_pass = reject_bad_text(replytext,8000)
replytext = replacetext(replytext, "%BR%", "<BR>")
@@ -424,8 +489,13 @@
to_chat(usr, "The text you entered was blank, contained illegal characters or was too long. Please correct the text and submit again.")
return
var/DBQuery/insert_query = dbcon.NewQuery("INSERT INTO erro_poll_textreply (id ,datetime ,pollid ,ckey ,ip ,replytext ,adminrank) VALUES (null, Now(), [pollid], '[usr.ckey]', '[usr.client.address]', '[replytext]', '[adminrank]')")
insert_query.Execute()
var/datum/DBQuery/insert_query = SSdbcore.NewQuery("INSERT INTO erro_poll_textreply (id ,datetime ,pollid ,ckey ,ip ,replytext ,adminrank) VALUES (null, Now(), [pollid], '[usr.ckey]', '[usr.client.address]', '[replytext]', '[adminrank]')")
if(!insert_query.Execute())
message_admins("Error: [insert_query.ErrorMsg()]")
log_sql("Error: [insert_query.ErrorMsg()]")
qdel(insert_query)
return
qdel(insert_query)
to_chat(usr, "<span class='notice'>Feedback logging successful.</span>")
usr << browse(null,"window=playerpoll")
@@ -437,32 +507,43 @@
if(!isnum(pollid) || !isnum(optionid))
return
establish_db_connection()
if(dbcon.IsConnected())
SSdbcore.Connect()
if(SSdbcore.IsConnected())
var/DBQuery/select_query = dbcon.NewQuery("SELECT starttime, endtime, question, polltype FROM erro_poll_question WHERE id = [pollid] AND Now() BETWEEN starttime AND endtime")
select_query.Execute()
var/datum/DBQuery/select_query = SSdbcore.NewQuery("SELECT starttime, endtime, question, polltype FROM erro_poll_question WHERE id = [pollid] AND Now() BETWEEN starttime AND endtime")
if(!select_query.Execute())
message_admins("Error: [select_query.ErrorMsg()]")
log_sql("Error: [select_query.ErrorMsg()]")
qdel(select_query)
return
var/validpoll = 0
while(select_query.NextRow())
if(select_query.item[4] != "NUMVAL")
qdel(select_query)
return
validpoll = 1
break
qdel(select_query)
if(!validpoll)
to_chat(usr, "<span class='warning'>Poll is not valid.</span>")
return
var/DBQuery/select_query2 = dbcon.NewQuery("SELECT id FROM erro_poll_option WHERE id = [optionid] AND pollid = [pollid]")
select_query2.Execute()
var/datum/DBQuery/select_query2 = SSdbcore.NewQuery("SELECT id FROM erro_poll_option WHERE id = [optionid] AND pollid = [pollid]")
if(!select_query2.Execute())
message_admins("Error: [select_query.ErrorMsg()]")
log_sql("Error: [select_query.ErrorMsg()]")
qdel(select_query2)
return
var/validoption = 0
while(select_query2.NextRow())
validoption = 1
break
qdel(select_query2)
if(!validoption)
to_chat(usr, "<span class='warning'>Poll is not valid.</span>")
@@ -470,12 +551,18 @@
var/alreadyvoted = 0
var/DBQuery/voted_query = dbcon.NewQuery("SELECT id FROM erro_poll_vote WHERE optionid = [optionid] AND ckey = '[usr.ckey]'")
voted_query.Execute()
var/datum/DBQuery/voted_query = SSdbcore.NewQuery("SELECT id FROM erro_poll_vote WHERE optionid = [optionid] AND ckey = '[usr.ckey]'")
if(!voted_query.Execute())
message_admins("Error: [voted_query.ErrorMsg()]")
log_sql("Error: [voted_query.ErrorMsg()]")
qdel(voted_query)
return
while(voted_query.NextRow())
alreadyvoted = 1
break
qdel(voted_query)
if(alreadyvoted)
to_chat(usr, "<span class='warning'>You already voted in this poll.</span>")
@@ -486,8 +573,13 @@
adminrank = usr.client.holder.rank
var/DBQuery/insert_query = dbcon.NewQuery("INSERT INTO erro_poll_vote (id ,datetime ,pollid ,optionid ,ckey ,ip ,adminrank, rating) VALUES (null, Now(), [pollid], [optionid], '[usr.ckey]', '[usr.client.address]', '[adminrank]', [(isnull(rating)) ? "null" : rating])")
insert_query.Execute()
var/datum/DBQuery/insert_query = SSdbcore.NewQuery("INSERT INTO erro_poll_vote (id ,datetime ,pollid ,optionid ,ckey ,ip ,adminrank, rating) VALUES (null, Now(), [pollid], [optionid], '[usr.ckey]', '[usr.client.address]', '[adminrank]', [(isnull(rating)) ? "null" : rating])")
if(!insert_query.Execute())
message_admins("Error: [insert_query.ErrorMsg()]")
log_sql("Error: [insert_query.ErrorMsg()]")
qdel(insert_query)
return
qdel(insert_query)
to_chat(usr, "<span class='notice'>Vote successful.</span>")
usr << browse(null,"window=playerpoll")
usr << browse(null,"window=playerpoll")

View File

@@ -74,6 +74,9 @@ var/savefile/panicfile
make_datum_references_lists() //initialises global lists for referencing frequently used datums (so that we only ever do it once)
load_configuration()
SSdbcore.Initialize() // Get a database running, first thing
load_mode()
load_motd()
load_admins()
@@ -116,18 +119,6 @@ var/savefile/panicfile
data_core = new /obj/effect/datacore()
paiController = new /datum/paiController()
if(!setup_database_connection())
world.log << "Your server failed to establish a connection with the feedback database."
else
world.log << "Feedback database connection established."
migration_controller_mysql = new
migration_controller_sqlite = new ("players2.sqlite", "players2_empty.sqlite")
if(!setup_old_database_connection())
world.log << "Your server failed to establish a connection with the tgstation database."
else
world.log << "Tgstation database connection established."
plmaster = new /obj/effect/overlay()
plmaster.icon = 'icons/effects/tile_effects.dmi'
plmaster.icon_state = "plasma"
@@ -417,89 +408,3 @@ var/savefile/panicfile
/* does this help? I do not know */
if (src.status != s)
src.status = s
#define FAILED_DB_CONNECTION_CUTOFF 5
var/failed_db_connections = 0
var/failed_old_db_connections = 0
proc/setup_database_connection()
if(failed_db_connections > FAILED_DB_CONNECTION_CUTOFF) //If it failed to establish a connection more than 5 times in a row, don't bother attempting to conenct anymore.
return 0
if(!dbcon)
dbcon = new()
var/user = sqlfdbklogin
var/pass = sqlfdbkpass
var/db = sqlfdbkdb
var/address = sqladdress
var/port = sqlport
dbcon.Connect("dbi:mysql:[db]:[address]:[port]","[user]","[pass]")
. = dbcon.IsConnected()
if ( . )
failed_db_connections = 0 //If this connection succeeded, reset the failed connections counter.
else
world.log << "Database Error: [dbcon.ErrorMsg()]"
failed_db_connections++ //If it failed, increase the failed connections counter.
return .
//This proc ensures that the connection to the feedback database (global variable dbcon) is established
proc/establish_db_connection()
if(failed_db_connections > FAILED_DB_CONNECTION_CUTOFF)
return 0
var/DBQuery/q
if(dbcon)
q = dbcon.NewQuery("show global variables like 'wait_timeout'")
q.Execute()
if(q && q.ErrorMsg())
dbcon.Disconnect()
if(!dbcon || !dbcon.IsConnected())
return setup_database_connection()
else
return 1
//These two procs are for the old database, while it's being phased out. See the tgstation.sql file in the SQL folder for more information.
proc/setup_old_database_connection()
if(failed_old_db_connections > FAILED_DB_CONNECTION_CUTOFF) //If it failed to establish a connection more than 5 times in a row, don't bother attempting to conenct anymore.
return 0
if(!dbcon_old)
dbcon_old = new()
var/user = sqllogin
var/pass = sqlpass
var/db = sqldb
var/address = sqladdress
var/port = sqlport
dbcon_old.Connect("dbi:mysql:[db]:[address]:[port]","[user]","[pass]")
. = dbcon_old.IsConnected()
if ( . )
failed_old_db_connections = 0 //If this connection succeeded, reset the failed connections counter.
else
failed_old_db_connections++ //If it failed, increase the failed connections counter.
world.log << dbcon_old.ErrorMsg()
return .
//This proc ensures that the connection to the feedback database (global variable dbcon) is established
proc/establish_old_db_connection()
if(failed_old_db_connections > FAILED_DB_CONNECTION_CUTOFF)
return 0
if(!dbcon_old || !dbcon_old.IsConnected())
return setup_old_database_connection()
else
return 1
#undef FAILED_DB_CONNECTION_CUTOFF

View File

@@ -76,8 +76,14 @@ LOG_RC
## log world.log and runtime errors to a file
# LOG_RUNTIMES
## log sql status messages.
# LOG_SQL
## log all sql queries.
# LOG_SQL_QUERIES
## sql switching
# SQL_ENABLED
# SQL_ENABLED 1
## disconnect players who did nothing during 10 minutes
# KICK_INACTIVE
@@ -338,3 +344,18 @@ ENABLE_WAGES
# HTTP URL to send Discord messages to.
# DISCORD_URL
# DISCORD_PASSWORD
## Time in seconds for asynchronous queries to timeout
## Set to 0 for infinite
ASYNC_QUERY_TIMEOUT 10
## Time in seconds for blocking queries to execute before slow query timeout
## Set to 0 for infinite
## Must be less than or equal to ASYNC_QUERY_TIMEOUT
BLOCKING_QUERY_TIMEOUT 5
## The maximum number of additional threads BSQL is allowed to run at once
BSQL_THREAD_LIMIT 50
## Uncomment to enable verbose BSQL communication logs
#BSQL_DEBUG

BIN
libmariadb.dll Normal file

Binary file not shown.

22
tools/travis/build_bsql.sh Executable file
View File

@@ -0,0 +1,22 @@
#!/usr/bin/env bash
set -euo pipefail
# Variable.
BSQL_VERSION=v1.4.0.0
mkdir -p BSQL
cd BSQL
git init
git remote add origin https://github.com/tgstation/BSQL
git fetch --depth 1 origin $BSQL_VERSION
git checkout FETCH_HEAD
mkdir -p artifacts
cd artifacts
export CXX=g++-7
# The -D will be unnecessary past BSQL v1.4.0.0
cmake .. -DMARIA_LIBRARY=/usr/lib/i386-linux-gnu/libmariadb.so
make
mkdir -p ~/.byond/bin
ln -s $PWD/src/BSQL/libBSQL.so ../../libBSQL.so

View File

@@ -0,0 +1,15 @@
#!/bin/bash
set -euo pipefail
# get libmariadb, cache it so limmex doesn't get angery
if [ -f $HOME/libmariadb ]; then
#travis likes to interpret the cache command as it being a file for some reason
rm $HOME/libmariadb
fi
mkdir -p $HOME/libmariadb
if [ ! -f $HOME/libmariadb/libmariadb.so ]; then
wget http://www.byond.com/download/db/mariadb_client-2.0.0-linux.tgz
tar -xvf mariadb_client-2.0.0-linux.tgz
mv mariadb_client-2.0.0-linux/libmariadb.so $HOME/libmariadb/libmariadb.so
rm -rf mariadb_client-2.0.0-linux.tgz mariadb_client-2.0.0-linux
fi

View File

@@ -21,6 +21,7 @@
#include "__DEFINES\atom_locking_and_control.dm"
#include "__DEFINES\atoms.dm"
#include "__DEFINES\bots.dm"
#include "__DEFINES\BSQL.dm"
#include "__DEFINES\cameranet.dm"
#include "__DEFINES\carbon_defines.dm"
#include "__DEFINES\clothing.dm"
@@ -203,6 +204,7 @@
#include "code\controllers\subsystem\ambient_sound.dm"
#include "code\controllers\subsystem\bots.dm"
#include "code\controllers\subsystem\component.dm"
#include "code\controllers\subsystem\dbcore.dm"
#include "code\controllers\subsystem\disease.dm"
#include "code\controllers\subsystem\emergency_shuttle.dm"
#include "code\controllers\subsystem\event.dm"
@@ -1204,9 +1206,7 @@
#include "code\game\verbs\who.dm"
#include "code\js\byjax.dm"
#include "code\js\menus.dm"
#include "code\libs\db\constants.dm"
#include "code\libs\db\core.dm"
#include "code\libs\db\db.dm"
#include "code\libs\BSQL\includes.dm"
#include "code\libs\Get Flat Icon\Get Flat Icon.dm"
#include "code\libs\IconProcs\IconProcs.dm"
#include "code\libs\s_html\hexadecimal.dm"
@@ -1586,6 +1586,11 @@
#include "code\modules\migrations\SS13\007-secret-fingerprints.dm"
#include "code\modules\migrations\SS13\008-add-connection-log.dm"
#include "code\modules\migrations\SS13\009-add-accountjoined.dm"
#include "code\modules\migrations\SS13\010-add-hidden-poll-option.dm"
#include "code\modules\migrations\SS13\011-port-library-to-feedback-db.dm"
#include "code\modules\migrations\SS13\012-add-customitems-table.dm"
#include "code\modules\migrations\SS13\013-add-createdby-ckey-to-polls.dm"
#include "code\modules\migrations\SS13\014-add-createdby-ip-to-polls.dm"
#include "code\modules\migrations\SS13\_base.dm"
#include "code\modules\migrations\SS13_Prefs\001-create.dm"
#include "code\modules\migrations\SS13_Prefs\002-add-progress-bars.dm"