208 lines
5.1 KiB
Go
208 lines
5.1 KiB
Go
package database
|
|
|
|
import (
|
|
"fmt"
|
|
"reflect"
|
|
"strings"
|
|
)
|
|
|
|
type JoinType string
|
|
type QueryKind string
|
|
|
|
const (
|
|
Join JoinType = "JOIN"
|
|
NaturalJoin JoinType = "NATURAL JOIN"
|
|
InnerJoin JoinType = "INNER JOIN"
|
|
LeftJoin JoinType = "LEFT JOIN"
|
|
RightJoin JoinType = "RIGHT JOIN"
|
|
|
|
SelectQuery QueryKind = "SELECT"
|
|
DeleteQuery QueryKind = "DELETE"
|
|
)
|
|
|
|
type JoinCondition struct {
|
|
JoinType JoinType
|
|
Table string
|
|
On string
|
|
}
|
|
|
|
type queryBuilder[Obj SQLTable] struct {
|
|
object Obj
|
|
table *sqlTable
|
|
selectedRows []string
|
|
distinct bool
|
|
highPriority bool
|
|
joins []*JoinCondition
|
|
where WhereGroups
|
|
havingStr string
|
|
havingValues []any
|
|
groupBy []string
|
|
orderBy []string
|
|
limit int
|
|
offset int
|
|
}
|
|
|
|
func NewQueryBuilder[Val SQLTable](obj Val) *queryBuilder[Val] {
|
|
return &queryBuilder[Val]{object: obj, table: getSQLTableFromInterface(obj)}
|
|
}
|
|
|
|
func QueryBuilderFromInterface[Val SQLTable](obj Val) *queryBuilder[Val] {
|
|
builder := queryBuilder[Val]{object: obj, table: getSQLTableFromInterface(obj)}
|
|
|
|
reflectElem := reflect.ValueOf(obj).Elem()
|
|
reflectType := reflectElem.Type()
|
|
for i := 0; i < reflectElem.NumField(); i++ {
|
|
field := reflectElem.Field(i)
|
|
if len(reflectType.Field(i).Tag.Get("db")) < 2 || !field.IsValid() || field.IsZero() {
|
|
continue
|
|
}
|
|
|
|
builder.Where(&QueryCondition{Row: reflectType.Field(i).Name, Operator: Equal, Value: field.Interface()})
|
|
}
|
|
|
|
return &builder
|
|
}
|
|
|
|
func (builder *queryBuilder[TVal]) Select(rows ...string) *queryBuilder[TVal] {
|
|
if len(rows) > 0 {
|
|
builder.selectedRows = append(builder.selectedRows, rows...)
|
|
}
|
|
return builder
|
|
}
|
|
|
|
func (builder *queryBuilder[TVal]) Distinct() *queryBuilder[TVal] {
|
|
builder.distinct = true
|
|
return builder
|
|
}
|
|
|
|
func (builder *queryBuilder[TVal]) HighPriority() *queryBuilder[TVal] {
|
|
builder.highPriority = true
|
|
return builder
|
|
}
|
|
|
|
func (builder *queryBuilder[TVal]) Join(joinType JoinType, table SQLTable, on string) *queryBuilder[TVal] {
|
|
builder.joins = append(builder.joins, &JoinCondition{JoinType: joinType, Table: getSQLTableName(table), On: on})
|
|
return builder
|
|
}
|
|
|
|
func (builder *queryBuilder[TVal]) Where(conditions ...*QueryCondition) *queryBuilder[TVal] {
|
|
if len(conditions) > 0 {
|
|
builder.where = append(builder.where, &WhereGroup{conditions, AND, nil})
|
|
}
|
|
return builder
|
|
}
|
|
|
|
func (builder *queryBuilder[TVal]) WhereGroup(groups ...*WhereGroup) *queryBuilder[TVal] {
|
|
if len(groups) > 0 {
|
|
builder.where = append(builder.where, groups...)
|
|
}
|
|
return builder
|
|
}
|
|
|
|
func (builder *queryBuilder[TVal]) Having(str string, values ...any) *queryBuilder[TVal] {
|
|
builder.havingStr = str
|
|
builder.havingValues = values
|
|
return builder
|
|
}
|
|
|
|
func (builder *queryBuilder[TVal]) GroupBy(columns ...string) *queryBuilder[TVal] {
|
|
if len(columns) > 0 {
|
|
builder.groupBy = append(builder.groupBy, columns...)
|
|
}
|
|
return builder
|
|
}
|
|
|
|
func (builder *queryBuilder[TVal]) OrderBy(columns ...string) *queryBuilder[TVal] {
|
|
if len(columns) > 0 {
|
|
builder.orderBy = append(builder.orderBy, columns...)
|
|
}
|
|
return builder
|
|
}
|
|
|
|
func (builder *queryBuilder[TVal]) Limit(limit int) *queryBuilder[TVal] {
|
|
builder.limit = limit
|
|
return builder
|
|
}
|
|
|
|
func (builder *queryBuilder[TVal]) Offset(offset int) *queryBuilder[TVal] {
|
|
builder.offset = offset
|
|
return builder
|
|
}
|
|
|
|
func (builder *queryBuilder[TVal]) BuildSelect() (string, []any) {
|
|
return builder.build(SelectQuery)
|
|
}
|
|
|
|
func (builder *queryBuilder[TVal]) BuildDelete() (string, []any) {
|
|
return builder.build(DeleteQuery)
|
|
}
|
|
|
|
func (builder *queryBuilder[TVal]) build(queryKind QueryKind) (string, []any) {
|
|
var query strings.Builder
|
|
|
|
query.WriteString(fmt.Sprintf("%s ", queryKind))
|
|
if queryKind == SelectQuery {
|
|
if builder.distinct {
|
|
query.WriteString("DISTINCT ")
|
|
}
|
|
if builder.highPriority {
|
|
query.WriteString("HIGH_PRIORITY ")
|
|
}
|
|
if len(builder.selectedRows) > 0 {
|
|
query.WriteString(strings.Join(builder.selectedRows, ", "))
|
|
} else {
|
|
query.WriteString("*")
|
|
}
|
|
}
|
|
|
|
// FROM clause
|
|
query.WriteString(" FROM ")
|
|
query.WriteString(builder.table.Name)
|
|
|
|
// JOIN clauses
|
|
for _, join := range builder.joins {
|
|
if join.On == "" {
|
|
query.WriteString(fmt.Sprintf(" %s %s", join.JoinType, join.Table))
|
|
} else {
|
|
query.WriteString(fmt.Sprintf(" %s %s ON %s", join.JoinType, join.Table, join.On))
|
|
}
|
|
}
|
|
|
|
// WHERE clause
|
|
whereStatement, queryValues := builder.where.Build(true)
|
|
if len(queryValues) > 0 {
|
|
query.WriteString(whereStatement)
|
|
}
|
|
|
|
// GROUP BY clause
|
|
if len(builder.groupBy) > 0 {
|
|
query.WriteString(" GROUP BY ")
|
|
query.WriteString(strings.Join(builder.groupBy, ", "))
|
|
}
|
|
|
|
// ORDER BY clause
|
|
if len(builder.orderBy) > 0 {
|
|
query.WriteString(" ORDER BY ")
|
|
query.WriteString(strings.Join(builder.orderBy, ", "))
|
|
}
|
|
|
|
// HAVING clause
|
|
if builder.havingStr != "" {
|
|
query.WriteString(" HAVING ")
|
|
query.WriteString(builder.havingStr)
|
|
queryValues = append(queryValues, builder.havingValues...)
|
|
}
|
|
|
|
// LIMIT clause
|
|
if builder.limit > 0 {
|
|
query.WriteString(fmt.Sprintf(" LIMIT %d", builder.limit))
|
|
}
|
|
|
|
// OFFSET clause
|
|
if builder.offset > 0 {
|
|
query.WriteString(fmt.Sprintf(" OFFSET %d", builder.offset))
|
|
}
|
|
|
|
return query.String(), queryValues
|
|
}
|