//Structured Datum Query Language. Basically SQL meets BYOND objects. //Note: For use in BS12, need text_starts_with proc, and to modify the action on select to use BS12's object edit command(s). /client/proc/SDQL_query(query_text as message) set category = "Admin" if(!check_rights(R_DEBUG)) //Shouldn't happen... but just to be safe. message_admins("\red ERROR: Non-admin [usr.key] attempted to execute a SDQL query!") log_admin("Non-admin [usr.key] attempted to execute a SDQL query!") var/list/query_list = SDQL_tokenize(query_text) if(query_list.len < 2) if(query_list.len > 0) usr << "\red SDQL: Too few discrete tokens in query \"[query_text]\". Please check your syntax and try again." return if(!(lowertext(query_list[1]) in list("select", "delete", "update"))) usr << "\red SDQL: Unknown query type: \"[query_list[1]]\" in query \"[query_text]\". Please check your syntax and try again." return var/list/types = list() var/i for(i = 2; i <= query_list.len; i += 2) types += query_list[i] if(i + 1 >= query_list.len || query_list[i + 1] != ",") break i++ var/list/from = list() if(i <= query_list.len) if(lowertext(query_list[i]) in list("from", "in")) for(i++; i <= query_list.len; i += 2) from += query_list[i] if(i + 1 >= query_list.len || query_list[i + 1] != ",") break i++ if(from.len < 1) from += "world" var/list/set_vars = list() if(lowertext(query_list[1]) == "update") if(i <= query_list.len && lowertext(query_list[i]) == "set") for(i++; i <= query_list.len; i++) if(i + 2 <= query_list.len && query_list[i + 1] == "=") set_vars += query_list[i] set_vars[query_list[i]] = query_list[i + 2] else usr << "\red SDQL: Invalid set parameter in query \"[query_text]\". Please check your syntax and try again." return i += 3 if(i >= query_list.len || query_list[i] != ",") break if(set_vars.len < 1) usr << "\red SDQL: Invalid or missing set in query \"[query_text]\". Please check your syntax and try again." return var/list/where = list() if(i <= query_list.len && lowertext(query_list[i]) == "where") where = query_list.Copy(i + 1) var/list/from_objs = list() if("world" in from) from_objs += world else for(var/f in from) if(copytext(f, 1, 2) == "'" || copytext(f, 1, 2) == "\"") from_objs += locate(copytext(f, 2, length(f))) else if(copytext(f, 1, 2) != "/") from_objs += locate(f) else var/f2 = text2path(f) if(text_starts_with(f, "/mob")) for(var/mob/m in world) if(istype(m, f2)) from_objs += m else if(text_starts_with(f, "/turf/space")) for(var/turf/space/m in world) if(istype(m, f2)) from_objs += m else if(text_starts_with(f, "/turf/simulated")) for(var/turf/simulated/m in world) if(istype(m, f2)) from_objs += m else if(text_starts_with(f, "/turf/unsimulated")) for(var/turf/unsimulated/m in world) if(istype(m, f2)) from_objs += m else if(text_starts_with(f, "/turf")) for(var/turf/m in world) if(istype(m, f2)) from_objs += m else if(text_starts_with(f, "/area")) for(var/area/m in world) if(istype(m, f2)) from_objs += m else if(text_starts_with(f, "/obj/item")) for(var/obj/item/m in world) if(istype(m, f2)) from_objs += m else if(text_starts_with(f, "/obj/machinery")) for(var/obj/machinery/m in world) if(istype(m, f2)) from_objs += m else if(text_starts_with(f, "/obj")) for(var/obj/m in world) if(istype(m, f2)) from_objs += m else if(text_starts_with(f, "/atom")) for(var/atom/m in world) if(istype(m, f2)) from_objs += m /* else for(var/datum/m in world) if(istype(m, f2)) from_objs += m */ var/list/objs = list() for(var/from_obj in from_objs) if("*" in types) objs += from_obj:contents else for(var/f in types) if(copytext(f, 1, 2) == "'" || copytext(f, 1, 2) == "\"") objs += locate(copytext(f, 2, length(f))) in from_obj else if(copytext(f, 1, 2) != "/") objs += locate(f) in from_obj else var/f2 = text2path(f) if(text_starts_with(f, "/mob")) for(var/mob/m in from_obj) if(istype(m, f2)) objs += m else if(text_starts_with(f, "/turf/space")) for(var/turf/space/m in from_obj) if(istype(m, f2)) objs += m else if(text_starts_with(f, "/turf/simulated")) for(var/turf/simulated/m in from_obj) if(istype(m, f2)) objs += m else if(text_starts_with(f, "/turf/unsimulated")) for(var/turf/unsimulated/m in from_obj) if(istype(m, f2)) objs += m else if(text_starts_with(f, "/turf")) for(var/turf/m in from_obj) if(istype(m, f2)) objs += m else if(text_starts_with(f, "/area")) for(var/area/m in from_obj) if(istype(m, f2)) objs += m else if(text_starts_with(f, "/obj/item")) for(var/obj/item/m in from_obj) if(istype(m, f2)) objs += m else if(text_starts_with(f, "/obj/machinery")) for(var/obj/machinery/m in from_obj) if(istype(m, f2)) objs += m else if(text_starts_with(f, "/obj")) for(var/obj/m in from_obj) if(istype(m, f2)) objs += m else if(text_starts_with(f, "/atom")) for(var/atom/m in from_obj) if(istype(m, f2)) objs += m else for(var/datum/m in from_obj) if(istype(m, f2)) objs += m for(var/datum/t in objs) var/currently_false = 0 for(i = 1, i - 1 < where.len, i++) var/v = where[i++] var/compare_op = where[i++] if(!(compare_op in list("==", "=", "<>", "<", ">", "<=", ">=", "!="))) usr << "\red SDQL: Unknown comparison operator [compare_op] in where clause following [v] in query \"[query_text]\". Please check your syntax and try again." return var/j for(j = i, j <= where.len, j++) if(lowertext(where[j]) in list("and", "or", ";")) break if(!currently_false) var/value = SDQL_text2value(t, v) var/result = SDQL_evaluate(t, where.Copy(i, j)) switch(compare_op) if("=", "==") currently_false = !(value == result) if("!=", "<>") currently_false = !(value != result) if("<") currently_false = !(value < result) if(">") currently_false = !(value > result) if("<=") currently_false = !(value <= result) if(">=") currently_false = !(value >= result) if(j > where.len || lowertext(where[j]) == ";") break else if(lowertext(where[j]) == "or") if(currently_false) currently_false = 0 else break i = j if(currently_false) objs -= t usr << "\blue SQDL Query: [query_text]" message_admins("[usr] executed SDQL query: \"[query_text]\".") /* for(var/t in types) usr << "Type: [t]" for(var/t in from) usr << "From: [t]" for(var/t in set_vars) usr << "Set: [t] = [set_vars[t]]" if(where.len) var/where_str = "" for(var/t in where) where_str += "[t] " usr << "Where: [where_str]" usr << "From objects:" for(var/datum/t in from_objs) usr << t usr << "Objects:" for(var/datum/t in objs) usr << t */ switch(lowertext(query_list[1])) if("delete") for(var/datum/t in objs) del t if("update") for(var/datum/t in objs) objs[t] = list() for(var/v in set_vars) if(v in t.vars) objs[t][v] = SDQL_text2value(t, set_vars[v]) for(var/datum/t in objs) for(var/v in objs[t]) t.vars[v] = objs[t][v] if("select") var/text = "" for(var/datum/t in objs) if(istype(t, /atom)) var/atom/a = t if(a.x) text += "\ref[t]: [t] at ([a.x], [a.y], [a.z])
" else if(a.loc && a.loc.x) text += "\ref[t]: [t] in [a.loc] at ([a.loc.x], [a.loc.y], [a.loc.z])
" else text += "\ref[t]: [t]
" else text += "\ref[t]: [t]
" //text += "[t]
" usr << browse(text, "window=sdql_result") /client/Topic(href,href_list[],hsrc) if(href_list["SDQL_select"]) debug_variables(locate(href_list["SDQL_select"])) ..() /proc/SDQL_evaluate(datum/object, list/equation) if(equation.len == 0) return null else if(equation.len == 1) return SDQL_text2value(object, equation[1]) else if(equation[1] == "!") return !SDQL_evaluate(object, equation.Copy(2)) else if(equation[1] == "-") return -SDQL_evaluate(object, equation.Copy(2)) else usr << "\red SDQL: Sorry, equations not yet supported :(" return null /proc/SDQL_text2value(datum/object, text) if(text2num(text) != null) return text2num(text) else if(text == "null") return null else if(copytext(text, 1, 2) == "'" || copytext(text, 1, 2) == "\"" ) return copytext(text, 2, length(text)) else if(copytext(text, 1, 2) == "/") return text2path(text) else if(findtext(text, ".")) var/split = findtext(text, ".") var/v = copytext(text, 1, split) if((v in object.vars) && istype(object.vars[v], /datum)) return SDQL_text2value(object.vars[v], copytext(text, split + 1)) else return null else if(text in object.vars) return object.vars[text] else return null /proc/text_starts_with(text, start) if(copytext(text, 1, length(start) + 1) == start) return 1 else return 0 /proc/SDQL_tokenize(query_text) var/list/whitespace = list(" ", "\n", "\t") var/list/single = list("(", ")", ",", "+", "-") var/list/multi = list( "=" = list("", "="), "<" = list("", "=", ">"), ">" = list("", "="), "!" = list("", "=")) var/word = "" var/list/query_list = list() var/len = length(query_text) for(var/i = 1, i <= len, i++) var/char = copytext(query_text, i, i + 1) if(char in whitespace) if(word != "") query_list += word word = "" else if(char in single) if(word != "") query_list += word word = "" query_list += char else if(char in multi) if(word != "") query_list += word word = "" var/char2 = copytext(query_text, i + 1, i + 2) if(char2 in multi[char]) query_list += "[char][char2]" i++ else query_list += char else if(char == "'") if(word != "") usr << "\red SDQL: You have an error in your SDQL syntax, unexpected ' in query: \"[query_text]\" following \"[word]\". Please check your syntax, and try again." return null word = "'" for(i++, i <= len, i++) char = copytext(query_text, i, i + 1) if(char == "'") if(copytext(query_text, i + 1, i + 2) == "'") word += "'" i++ else break else word += char if(i > len) usr << "\red SDQL: You have an error in your SDQL syntax, unmatched ' in query: \"[query_text]\". Please check your syntax, and try again." return null query_list += "[word]'" word = "" else if(char == "\"") if(word != "") usr << "\red SDQL: You have an error in your SDQL syntax, unexpected \" in query: \"[query_text]\" following \"[word]\". Please check your syntax, and try again." return null word = "\"" for(i++, i <= len, i++) char = copytext(query_text, i, i + 1) if(char == "\"") if(copytext(query_text, i + 1, i + 2) == "'") word += "\"" i++ else break else word += char if(i > len) usr << "\red SDQL: You have an error in your SDQL syntax, unmatched \" in query: \"[query_text]\". Please check your syntax, and try again." return null query_list += "[word]\"" word = "" else word += char if(word != "") query_list += word return query_list