Added BypassGet() to the topic and user caches. Moved 13 more queries to the query generator. The query generator now supports simple insert queries. Removed the pointless console logging for reply and topic deletions. We have modlogs for that. The MySQL files now have build tags to make it easy for you to disable them for other database engines. Those will be coming after the query generator is done. Moved a bunch of inline queries into more appropriate locations. Rewrote _process_where and DB_Where to better handle joins. Atom's continuing it's pointless crusade against trailing tabs.
358 lines
12 KiB
Go
358 lines
12 KiB
Go
/* WIP Under Construction */
|
|
package main
|
|
|
|
import "fmt"
|
|
import "strings"
|
|
import "log"
|
|
import "os"
|
|
|
|
var db_registry []DB_Adapter
|
|
var blank_order []DB_Order
|
|
|
|
type DB_Column struct
|
|
{
|
|
Table string
|
|
Left string // Could be a function or a column, so I'm naming this Left
|
|
Alias string // aka AS Blah, if it's present
|
|
Type string // function or column
|
|
}
|
|
|
|
type DB_Field struct
|
|
{
|
|
Name string
|
|
Type string
|
|
}
|
|
|
|
type DB_Where struct
|
|
{
|
|
LeftTable string
|
|
LeftColumn string
|
|
RightTable string
|
|
RightColumn string
|
|
Operator string
|
|
LeftType string
|
|
RightType string
|
|
}
|
|
|
|
type DB_Joiner struct
|
|
{
|
|
LeftTable string
|
|
LeftColumn string
|
|
RightTable string
|
|
RightColumn string
|
|
}
|
|
|
|
type DB_Order struct
|
|
{
|
|
Column string
|
|
Order string
|
|
}
|
|
|
|
type DB_Adapter interface {
|
|
get_name() string
|
|
simple_insert(string,string,string,string) error
|
|
//simple_replace(string,string,[]string,string) error
|
|
simple_update() error
|
|
simple_select(string,string,string,string,string/*,int,int*/) error
|
|
simple_left_join(string,string,string,string,string,string,string/*,int,int*/) error
|
|
write() error
|
|
// TO-DO: Add a simple query builder
|
|
}
|
|
|
|
func main() {
|
|
log.Println("Running the query generator")
|
|
for _, adapter := range db_registry {
|
|
log.Println("Building the queries for the " + adapter.get_name() + " adapter")
|
|
write_statements(adapter)
|
|
adapter.write()
|
|
}
|
|
}
|
|
|
|
func write_statements(adapter DB_Adapter) error {
|
|
// url_prefix and url_name will be removed from this query in a later commit
|
|
adapter.simple_select("get_user","users","name, group, is_super_admin, avatar, message, url_prefix, url_name, level","uid = ?","")
|
|
|
|
adapter.simple_select("get_full_user","users","name, group, is_super_admin, session, email, avatar, message, url_prefix, url_name, level, score, last_ip","uid = ?","")
|
|
|
|
adapter.simple_select("get_topic","topics","title, content, createdBy, createdAt, is_closed, sticky, parentID, ipaddress, postCount, likeCount","tid = ?","")
|
|
|
|
adapter.simple_select("get_reply","replies","content, createdBy, createdAt, lastEdit, lastEditBy, ipaddress, likeCount","rid = ?","")
|
|
|
|
adapter.simple_select("login","users","uid, name, password, salt","name = ?","")
|
|
|
|
adapter.simple_select("get_password","users","password,salt","uid = ?","")
|
|
|
|
adapter.simple_select("username_exists","users","name","name = ?","")
|
|
|
|
|
|
adapter.simple_select("get_settings","settings","name, content, type","","")
|
|
|
|
adapter.simple_select("get_setting","settings","content, type","name = ?","")
|
|
|
|
adapter.simple_select("get_full_setting","settings","name, type, constraints","name = ?","")
|
|
|
|
adapter.simple_select("is_plugin_active","plugins","active","uname = ?","")
|
|
|
|
adapter.simple_select("get_users","users","uid, name, group, active, is_super_admin, avatar","","")
|
|
|
|
adapter.simple_select("is_theme_default","themes","default","uname = ?","")
|
|
|
|
adapter.simple_select("get_modlogs","moderation_logs","action, elementID, elementType, ipaddress, actorID, doneAt","","")
|
|
|
|
adapter.simple_select("get_reply_tid","replies","tid","rid = ?","")
|
|
|
|
adapter.simple_select("get_topic_fid","topics","parentID","tid = ?","")
|
|
|
|
adapter.simple_select("get_user_reply_uid","users_replies","uid","rid = ?","")
|
|
|
|
adapter.simple_select("has_liked_topic","likes","targetItem","sentBy = ? and targetItem = ? and targetType = 'topics'","")
|
|
|
|
/*"select targetItem from likes where sentBy = ? and targetItem = ? and targetType = 'replies'"*/
|
|
|
|
|
|
|
|
adapter.simple_left_join("get_topic_list","topics","users","topics.tid, topics.title, topics.content, topics.createdBy, topics.is_closed, topics.sticky, topics.createdAt, topics.parentID, users.name, users.avatar","topics.createdBy = users.uid","","topics.sticky DESC, topics.lastReplyAt DESC, topics.createdBy DESC")
|
|
|
|
adapter.simple_left_join("get_topic_user","topics","users","topics.title, topics.content, topics.createdBy, topics.createdAt, topics.is_closed, topics.sticky, topics.parentID, topics.ipaddress, topics.postCount, topics.likeCount, users.name, users.avatar, users.group, users.url_prefix, users.url_name, users.level","topics.createdBy = users.uid","tid = ?","")
|
|
|
|
adapter.simple_left_join("get_topic_by_reply","replies","topics","topics.tid, topics.title, topics.content, topics.createdBy, topics.createdAt, topics.is_closed, topics.sticky, topics.parentID, topics.ipaddress, topics.postCount, topics.likeCount","replies.tid = topics.tid","rid = ?","")
|
|
|
|
adapter.simple_left_join("get_topic_replies","replies","users","replies.rid, replies.content, replies.createdBy, replies.createdAt, replies.lastEdit, replies.lastEditBy, users.avatar, users.name, users.group, users.url_prefix, users.url_name, users.level, replies.ipaddress","replies.createdBy = users.uid","tid = ?","")
|
|
|
|
adapter.simple_left_join("get_forum_topics","topics","users","topics.tid, topics.title, topics.content, topics.createdBy, topics.is_closed, topics.sticky, topics.createdAt, topics.lastReplyAt, topics.parentID, users.name, users.avatar","topics.createdBy = users.uid","topics.parentID = ?","topics.sticky DESC, topics.lastReplyAt DESC, topics.createdBy desc")
|
|
|
|
adapter.simple_left_join("get_profile_replies","users_replies","users","users_replies.rid, users_replies.content, users_replies.createdBy, users_replies.createdAt, users_replies.lastEdit, users_replies.lastEditBy, users.avatar, users.name, users.group","users_replies.createdBy = users.uid","users_replies.uid = ?","")
|
|
|
|
adapter.simple_insert("create_topic","topics","parentID,title,content,parsed_content,createdAt,lastReplyAt,ipaddress,words,createdBy","?,?,?,?,NOW(),NOW(),?,?,?")
|
|
|
|
return nil
|
|
}
|
|
|
|
func _process_columns(colstr string) (columns []DB_Column) {
|
|
if colstr == "" {
|
|
return columns
|
|
}
|
|
colstr = strings.Replace(colstr," as "," AS ",-1)
|
|
for _, segment := range strings.Split(colstr,",") {
|
|
var outcol DB_Column
|
|
dothalves := strings.Split(strings.TrimSpace(segment),".")
|
|
|
|
var halves []string
|
|
if len(dothalves) == 2 {
|
|
outcol.Table = dothalves[0]
|
|
halves = strings.Split(dothalves[1]," AS ")
|
|
} else {
|
|
halves = strings.Split(dothalves[0]," AS ")
|
|
}
|
|
|
|
halves[0] = strings.TrimSpace(halves[0])
|
|
if len(halves) == 2 {
|
|
outcol.Alias = strings.TrimSpace(halves[1])
|
|
}
|
|
if halves[0][len(halves[0]) - 1] == ')' {
|
|
outcol.Type = "function"
|
|
} else {
|
|
outcol.Type = "column"
|
|
}
|
|
|
|
outcol.Left = halves[0]
|
|
columns = append(columns,outcol)
|
|
}
|
|
return columns
|
|
}
|
|
|
|
func _process_orderby(orderstr string) (order []DB_Order) {
|
|
if orderstr == "" {
|
|
return order
|
|
}
|
|
for _, segment := range strings.Split(orderstr,",") {
|
|
var outorder DB_Order
|
|
halves := strings.Split(strings.TrimSpace(segment)," ")
|
|
if len(halves) != 2 {
|
|
continue
|
|
}
|
|
outorder.Column = halves[0]
|
|
outorder.Order = strings.ToLower(halves[1])
|
|
order = append(order,outorder)
|
|
}
|
|
return order
|
|
}
|
|
|
|
func _process_joiner(joinstr string) (joiner []DB_Joiner) {
|
|
if joinstr == "" {
|
|
return joiner
|
|
}
|
|
joinstr = strings.Replace(joinstr," on "," ON ",-1)
|
|
joinstr = strings.Replace(joinstr," and "," AND ",-1)
|
|
for _, segment := range strings.Split(joinstr," AND ") {
|
|
var outjoin DB_Joiner
|
|
halves := strings.Split(segment,"=")
|
|
if len(halves) != 2 {
|
|
continue
|
|
}
|
|
|
|
left_column := strings.Split(halves[0],".")
|
|
right_column := strings.Split(halves[1],".")
|
|
outjoin.LeftTable = strings.TrimSpace(left_column[0])
|
|
outjoin.RightTable = strings.TrimSpace(right_column[0])
|
|
outjoin.LeftColumn = strings.TrimSpace(left_column[1])
|
|
outjoin.RightColumn = strings.TrimSpace(right_column[1])
|
|
|
|
joiner = append(joiner,outjoin)
|
|
}
|
|
return joiner
|
|
}
|
|
|
|
func _process_where(wherestr string) (where []DB_Where) {
|
|
if wherestr == "" {
|
|
return where
|
|
}
|
|
wherestr = strings.Replace(wherestr," and "," AND ",-1)
|
|
for _, segment := range strings.Split(wherestr," AND ") {
|
|
// TO-DO: Subparse the contents of a function and spit out a DB_Function struct
|
|
var outwhere DB_Where
|
|
var parseOffset int
|
|
var left, right string
|
|
|
|
|
|
left, parseOffset = _get_identifier(segment, parseOffset)
|
|
outwhere.Operator, parseOffset = _get_operator(segment, parseOffset + 1)
|
|
right, parseOffset = _get_identifier(segment, parseOffset + 1)
|
|
outwhere.LeftType = _get_identifier_type(left)
|
|
outwhere.RightType = _get_identifier_type(right)
|
|
|
|
left_operand := strings.Split(left,".")
|
|
right_operand := strings.Split(right,".")
|
|
|
|
if len(left_operand) == 2 {
|
|
outwhere.LeftTable = strings.TrimSpace(left_operand[0])
|
|
outwhere.LeftColumn = strings.TrimSpace(left_operand[1])
|
|
} else {
|
|
outwhere.LeftColumn = strings.TrimSpace(left_operand[0])
|
|
}
|
|
|
|
if len(right_operand) == 2 {
|
|
outwhere.RightTable = strings.TrimSpace(right_operand[0])
|
|
outwhere.RightColumn = strings.TrimSpace(right_operand[1])
|
|
} else {
|
|
outwhere.RightColumn = strings.TrimSpace(right_operand[0])
|
|
}
|
|
|
|
where = append(where,outwhere)
|
|
}
|
|
return where
|
|
}
|
|
|
|
func _process_fields(fieldstr string) (fields []DB_Field) {
|
|
fmt.Println("_Entering _process_fields")
|
|
if fieldstr == "" {
|
|
return fields
|
|
}
|
|
var buffer string
|
|
var last_item int
|
|
fieldstr += ","
|
|
for i := 0; i < len(fieldstr); i++ {
|
|
if fieldstr[i] == '(' {
|
|
var pre_i int
|
|
pre_i = i
|
|
i = _skip_function_call(fieldstr,i-1)
|
|
fmt.Println("msg prior to i",fieldstr[0:i])
|
|
fmt.Println("len(fieldstr)",len(fieldstr))
|
|
fmt.Println("pre_i",pre_i)
|
|
fmt.Println("last_item",last_item)
|
|
fmt.Println("pre_i",string(fieldstr[pre_i]))
|
|
fmt.Println("last_item",string(fieldstr[last_item]))
|
|
fmt.Println("fieldstr[pre_i:i+1]",fieldstr[pre_i:i+1])
|
|
fmt.Println("fieldstr[last_item:i+1]",fieldstr[last_item:i+1])
|
|
fields = append(fields,DB_Field{Name:fieldstr[last_item:i+1],Type:_get_identifier_type(fieldstr[last_item:i+1])})
|
|
buffer = ""
|
|
last_item = i + 2
|
|
} else if fieldstr[i] == ',' && buffer != "" {
|
|
fields = append(fields,DB_Field{Name:buffer,Type:_get_identifier_type(buffer)})
|
|
buffer = ""
|
|
last_item = i + 1
|
|
} else if (fieldstr[i] > 32) && fieldstr[i] != ',' && fieldstr[i] != ')' {
|
|
buffer += string(fieldstr[i])
|
|
}
|
|
}
|
|
fmt.Println("fields",fields)
|
|
return fields
|
|
}
|
|
|
|
func _get_identifier_type(identifier string) string {
|
|
if ('a' <= identifier[0] && identifier[0] <= 'z') || ('A' <= identifier[0] && identifier[0] <= 'Z') {
|
|
if identifier[len(identifier) - 1] == ')' {
|
|
return "function"
|
|
}
|
|
return "column"
|
|
}
|
|
if identifier[0] == '\'' || identifier[0] == '"' {
|
|
return "string"
|
|
}
|
|
return "literal"
|
|
}
|
|
|
|
func _get_identifier(segment string, startOffset int) (out string, i int) {
|
|
//fmt.Println("entering _get_identifier")
|
|
segment = strings.TrimSpace(segment)
|
|
segment += " " // Avoid overflow bugs with slicing
|
|
for i = startOffset; i < len(segment); i++ {
|
|
if segment[i] == '(' {
|
|
i = _skip_function_call(segment,i)
|
|
return strings.TrimSpace(segment[startOffset:i]), (i - 1)
|
|
}
|
|
if segment[i] == ' ' && i != startOffset {
|
|
//fmt.Println("segment[startOffset:i]",segment[startOffset:i])
|
|
//fmt.Println("startOffset",startOffset)
|
|
//fmt.Println("segment[startOffset]",string(segment[startOffset]))
|
|
//fmt.Println("i",i)
|
|
return strings.TrimSpace(segment[startOffset:i]), (i - 1)
|
|
}
|
|
}
|
|
return strings.TrimSpace(segment[startOffset:]), (i - 1)
|
|
}
|
|
|
|
func _get_operator(segment string, startOffset int) (out string, i int) {
|
|
segment = strings.TrimSpace(segment)
|
|
segment += " " // Avoid overflow bugs with slicing
|
|
for i = startOffset; i < len(segment); i++ {
|
|
if segment[i] == ' ' && i != startOffset {
|
|
return strings.TrimSpace(segment[startOffset:i]), (i - 1)
|
|
}
|
|
}
|
|
return strings.TrimSpace(segment[startOffset:]), (i - 1)
|
|
}
|
|
|
|
func _skip_function_call(data string, index int) int {
|
|
var brace_count int
|
|
for ;index < len(data); index++{
|
|
char := data[index]
|
|
if char == '(' {
|
|
fmt.Println("Enter brace")
|
|
brace_count++
|
|
} else if char == ')' {
|
|
brace_count--
|
|
fmt.Println("Exit brace")
|
|
if brace_count == 0 {
|
|
fmt.Println("Exit function segment")
|
|
return index
|
|
}
|
|
}
|
|
}
|
|
return index
|
|
}
|
|
|
|
func write_file(name string, content string) (err error) {
|
|
f, err := os.Create(name)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
_, err = f.WriteString(content)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
f.Sync()
|
|
f.Close()
|
|
return
|
|
}
|