133 lines
2.4 KiB
Go
133 lines
2.4 KiB
Go
package exql
|
|
|
|
import (
|
|
"errors"
|
|
"reflect"
|
|
"strings"
|
|
|
|
"git.hexq.cn/tiglog/mydb/internal/cache"
|
|
)
|
|
|
|
var errUnknownTemplateType = errors.New("Unknown template type")
|
|
|
|
// represents different kinds of SQL statements.
|
|
type Statement struct {
|
|
Type
|
|
Table Fragment
|
|
Database Fragment
|
|
Columns Fragment
|
|
Values Fragment
|
|
Distinct bool
|
|
ColumnValues Fragment
|
|
OrderBy Fragment
|
|
GroupBy Fragment
|
|
Joins Fragment
|
|
Where Fragment
|
|
Returning Fragment
|
|
|
|
Limit
|
|
Offset
|
|
|
|
SQL string
|
|
|
|
amendFn func(string) string
|
|
}
|
|
|
|
func (layout *Template) doCompile(c Fragment) (string, error) {
|
|
if c != nil && !reflect.ValueOf(c).IsNil() {
|
|
return c.Compile(layout)
|
|
}
|
|
return "", nil
|
|
}
|
|
|
|
// Hash returns a unique identifier for the struct.
|
|
func (s *Statement) Hash() uint64 {
|
|
if s == nil {
|
|
return cache.NewHash(FragmentType_Statement, nil)
|
|
}
|
|
return cache.NewHash(
|
|
FragmentType_Statement,
|
|
s.Type,
|
|
s.Table,
|
|
s.Database,
|
|
s.Columns,
|
|
s.Values,
|
|
s.Distinct,
|
|
s.ColumnValues,
|
|
s.OrderBy,
|
|
s.GroupBy,
|
|
s.Joins,
|
|
s.Where,
|
|
s.Returning,
|
|
s.Limit,
|
|
s.Offset,
|
|
s.SQL,
|
|
)
|
|
}
|
|
|
|
func (s *Statement) SetAmendment(amendFn func(string) string) {
|
|
s.amendFn = amendFn
|
|
}
|
|
|
|
func (s *Statement) Amend(in string) string {
|
|
if s.amendFn == nil {
|
|
return in
|
|
}
|
|
return s.amendFn(in)
|
|
}
|
|
|
|
func (s *Statement) template(layout *Template) (string, error) {
|
|
switch s.Type {
|
|
case Truncate:
|
|
return layout.TruncateLayout, nil
|
|
case DropTable:
|
|
return layout.DropTableLayout, nil
|
|
case DropDatabase:
|
|
return layout.DropDatabaseLayout, nil
|
|
case Count:
|
|
return layout.CountLayout, nil
|
|
case Select:
|
|
return layout.SelectLayout, nil
|
|
case Delete:
|
|
return layout.DeleteLayout, nil
|
|
case Update:
|
|
return layout.UpdateLayout, nil
|
|
case Insert:
|
|
return layout.InsertLayout, nil
|
|
default:
|
|
return "", errUnknownTemplateType
|
|
}
|
|
}
|
|
|
|
// Compile transforms the Statement into an equivalent SQL query.
|
|
func (s *Statement) Compile(layout *Template) (compiled string, err error) {
|
|
if s.Type == SQL {
|
|
// No need to hit the cache.
|
|
return s.SQL, nil
|
|
}
|
|
|
|
if z, ok := layout.Read(s); ok {
|
|
return s.Amend(z), nil
|
|
}
|
|
|
|
tpl, err := s.template(layout)
|
|
if err != nil {
|
|
return "", err
|
|
}
|
|
|
|
compiled = layout.MustCompile(tpl, s)
|
|
|
|
compiled = strings.TrimSpace(compiled)
|
|
layout.Write(s, compiled)
|
|
|
|
return s.Amend(compiled), nil
|
|
}
|
|
|
|
// RawSQL represents a raw SQL statement.
|
|
func RawSQL(s string) *Statement {
|
|
return &Statement{
|
|
Type: SQL,
|
|
SQL: s,
|
|
}
|
|
}
|