342 lines
9.5 KiB
Go
342 lines
9.5 KiB
Go
|
/* WIP Under Construction */
|
||
|
package main
|
||
|
|
||
|
//import "fmt"
|
||
|
import "strings"
|
||
|
import "os"
|
||
|
|
||
|
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
|
||
|
var parseOffset int
|
||
|
var left, right string
|
||
|
|
||
|
left, parseOffset = _get_identifier(segment, parseOffset)
|
||
|
outjoin.Operator, parseOffset = _get_operator(segment, parseOffset + 1)
|
||
|
right, parseOffset = _get_identifier(segment, parseOffset + 1)
|
||
|
|
||
|
|
||
|
left_column := strings.Split(left,".")
|
||
|
right_column := strings.Split(right,".")
|
||
|
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
|
||
|
}
|
||
|
|
||
|
// TO-DO: Add support for keywords like BETWEEN. We'll probably need an arbitrary expression parser like with the update setters.
|
||
|
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_set(setstr string) (setter []DB_Setter) {
|
||
|
if setstr == "" {
|
||
|
return setter
|
||
|
}
|
||
|
//fmt.Println("setstr",setstr)
|
||
|
|
||
|
// First pass, splitting the string by commas while ignoring the innards of functions
|
||
|
var setset []string
|
||
|
var buffer string
|
||
|
var last_item int
|
||
|
setstr += ","
|
||
|
for i := 0; i < len(setstr); i++ {
|
||
|
if setstr[i] == '(' {
|
||
|
i = _skip_function_call(setstr,i-1)
|
||
|
setset = append(setset,setstr[last_item:i+1])
|
||
|
buffer = ""
|
||
|
last_item = i + 2
|
||
|
} else if setstr[i] == ',' && buffer != "" {
|
||
|
setset = append(setset,buffer)
|
||
|
buffer = ""
|
||
|
last_item = i + 1
|
||
|
} else if (setstr[i] > 32) && setstr[i] != ',' && setstr[i] != ')' {
|
||
|
buffer += string(setstr[i])
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// Second pass. Break this setitem into manageable chunks
|
||
|
buffer = ""
|
||
|
for _, setitem := range setset {
|
||
|
var tmp_setter DB_Setter
|
||
|
halves := strings.Split(setitem,"=")
|
||
|
if len(halves) != 2 {
|
||
|
continue
|
||
|
}
|
||
|
tmp_setter.Column = strings.TrimSpace(halves[0])
|
||
|
|
||
|
halves[1] += ")"
|
||
|
var optype int // 0: None, 1: Number, 2: Column, 3: Function, 4: String, 5: Operator
|
||
|
//fmt.Println("halves[1]",halves[1])
|
||
|
for i := 0; i < len(halves[1]); i++ {
|
||
|
char := halves[1][i]
|
||
|
//fmt.Println("optype",optype)
|
||
|
switch(optype) {
|
||
|
case 0: // unknown
|
||
|
if ('0' <= char && char <= '9') {
|
||
|
optype = 1
|
||
|
buffer = string(char)
|
||
|
} else if ('a' <= char && char <= 'z') || ('A' <= char && char <= 'Z') {
|
||
|
optype = 2
|
||
|
buffer = string(char)
|
||
|
} else if char == '\'' {
|
||
|
optype = 4
|
||
|
} else if _is_op_byte(char) {
|
||
|
optype = 5
|
||
|
buffer = string(char)
|
||
|
} else if char == '?' {
|
||
|
//fmt.Println("Expr:","?")
|
||
|
tmp_setter.Expr = append(tmp_setter.Expr,DB_Token{"?","substitute"})
|
||
|
}
|
||
|
case 1: // number
|
||
|
if ('0' <= char && char <= '9') {
|
||
|
buffer += string(char)
|
||
|
} else {
|
||
|
optype = 0
|
||
|
i--
|
||
|
//fmt.Println("Expr:",buffer)
|
||
|
tmp_setter.Expr = append(tmp_setter.Expr,DB_Token{buffer,"number"})
|
||
|
}
|
||
|
case 2: // column
|
||
|
if ('a' <= char && char <= 'z') || ('A' <= char && char <= 'Z') {
|
||
|
buffer += string(char)
|
||
|
} else if char == '(' {
|
||
|
optype = 3
|
||
|
i--
|
||
|
} else {
|
||
|
optype = 0
|
||
|
i--
|
||
|
//fmt.Println("Expr:",buffer)
|
||
|
tmp_setter.Expr = append(tmp_setter.Expr,DB_Token{buffer,"column"})
|
||
|
}
|
||
|
case 3: // function
|
||
|
var pre_i int = i
|
||
|
//fmt.Println("buffer",buffer)
|
||
|
//fmt.Println("len(halves)",len(halves[1]))
|
||
|
//fmt.Println("pre_i",string(halves[1][pre_i]))
|
||
|
//fmt.Println("msg prior to pre_i",halves[1][0:pre_i])
|
||
|
i = _skip_function_call(halves[1],i-1)
|
||
|
//fmt.Println("i",i)
|
||
|
//fmt.Println("msg prior to i-1",halves[1][0:i-1])
|
||
|
//fmt.Println("string(i-1)",string(halves[1][i-1]))
|
||
|
//fmt.Println("string(i)",string(halves[1][i]))
|
||
|
buffer += halves[1][pre_i:i] + string(halves[1][i])
|
||
|
//fmt.Println("Expr:",buffer)
|
||
|
tmp_setter.Expr = append(tmp_setter.Expr,DB_Token{buffer,"function"})
|
||
|
optype = 0
|
||
|
case 4: // string
|
||
|
if char != '\'' {
|
||
|
buffer += string(char)
|
||
|
} else {
|
||
|
optype = 0
|
||
|
//fmt.Println("Expr:",buffer)
|
||
|
tmp_setter.Expr = append(tmp_setter.Expr,DB_Token{buffer,"string"})
|
||
|
}
|
||
|
case 5: // operator
|
||
|
if _is_op_byte(char) {
|
||
|
buffer += string(char)
|
||
|
} else {
|
||
|
optype = 0
|
||
|
i--
|
||
|
//fmt.Println("Expr:",buffer)
|
||
|
tmp_setter.Expr = append(tmp_setter.Expr,DB_Token{buffer,"operator"})
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
setter = append(setter,tmp_setter)
|
||
|
}
|
||
|
//fmt.Println("setter",setter)
|
||
|
return setter
|
||
|
}
|
||
|
|
||
|
func _is_op_byte(char byte) bool {
|
||
|
return char == '<' || char == '>' || char == '=' || char == '!' || char == '*' || char == '%' || char == '+' || char == '-' || char == '/'
|
||
|
}
|
||
|
|
||
|
func _process_fields(fieldstr string) (fields []DB_Field) {
|
||
|
if fieldstr == "" {
|
||
|
return fields
|
||
|
}
|
||
|
var buffer string
|
||
|
var last_item int
|
||
|
fieldstr += ","
|
||
|
for i := 0; i < len(fieldstr); i++ {
|
||
|
if fieldstr[i] == '(' {
|
||
|
i = _skip_function_call(fieldstr,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])
|
||
|
}
|
||
|
}
|
||
|
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) {
|
||
|
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] == ' ' || _is_op_byte(segment[i])) && i != startOffset {
|
||
|
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 !_is_op_byte(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 == '(' {
|
||
|
brace_count++
|
||
|
} else if char == ')' {
|
||
|
brace_count--
|
||
|
if brace_count == 0 {
|
||
|
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
|
||
|
}
|