mirror of
https://github.com/superseriousbusiness/gotosocial.git
synced 2025-01-15 11:00:14 +00:00
98263a7de6
* start fixing up tests * fix up tests + automate with drone * fiddle with linting * messing about with drone.yml * some more fiddling * hmmm * add cache * add vendor directory * verbose * ci updates * update some little things * update sig
347 lines
6.4 KiB
Go
347 lines
6.4 KiB
Go
package orm
|
|
|
|
import (
|
|
"bytes"
|
|
"fmt"
|
|
"strconv"
|
|
"strings"
|
|
|
|
"github.com/go-pg/pg/v10/types"
|
|
)
|
|
|
|
type SelectQuery struct {
|
|
q *Query
|
|
count string
|
|
}
|
|
|
|
var (
|
|
_ QueryAppender = (*SelectQuery)(nil)
|
|
_ QueryCommand = (*SelectQuery)(nil)
|
|
)
|
|
|
|
func NewSelectQuery(q *Query) *SelectQuery {
|
|
return &SelectQuery{
|
|
q: q,
|
|
}
|
|
}
|
|
|
|
func (q *SelectQuery) String() string {
|
|
b, err := q.AppendQuery(defaultFmter, nil)
|
|
if err != nil {
|
|
panic(err)
|
|
}
|
|
return string(b)
|
|
}
|
|
|
|
func (q *SelectQuery) Operation() QueryOp {
|
|
return SelectOp
|
|
}
|
|
|
|
func (q *SelectQuery) Clone() QueryCommand {
|
|
return &SelectQuery{
|
|
q: q.q.Clone(),
|
|
count: q.count,
|
|
}
|
|
}
|
|
|
|
func (q *SelectQuery) Query() *Query {
|
|
return q.q
|
|
}
|
|
|
|
func (q *SelectQuery) AppendTemplate(b []byte) ([]byte, error) {
|
|
return q.AppendQuery(dummyFormatter{}, b)
|
|
}
|
|
|
|
func (q *SelectQuery) AppendQuery(fmter QueryFormatter, b []byte) (_ []byte, err error) { //nolint:gocyclo
|
|
if q.q.stickyErr != nil {
|
|
return nil, q.q.stickyErr
|
|
}
|
|
|
|
cteCount := q.count != "" && (len(q.q.group) > 0 || q.isDistinct())
|
|
if cteCount {
|
|
b = append(b, `WITH "_count_wrapper" AS (`...)
|
|
}
|
|
|
|
if len(q.q.with) > 0 {
|
|
b, err = q.q.appendWith(fmter, b)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
}
|
|
|
|
if len(q.q.union) > 0 {
|
|
b = append(b, '(')
|
|
}
|
|
|
|
b = append(b, "SELECT "...)
|
|
|
|
if len(q.q.distinctOn) > 0 {
|
|
b = append(b, "DISTINCT ON ("...)
|
|
for i, app := range q.q.distinctOn {
|
|
if i > 0 {
|
|
b = append(b, ", "...)
|
|
}
|
|
b, err = app.AppendQuery(fmter, b)
|
|
}
|
|
b = append(b, ") "...)
|
|
} else if q.q.distinctOn != nil {
|
|
b = append(b, "DISTINCT "...)
|
|
}
|
|
|
|
if q.count != "" && !cteCount {
|
|
b = append(b, q.count...)
|
|
} else {
|
|
b, err = q.appendColumns(fmter, b)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
}
|
|
|
|
if q.q.hasTables() {
|
|
b = append(b, " FROM "...)
|
|
b, err = q.appendTables(fmter, b)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
}
|
|
|
|
err = q.q.forEachHasOneJoin(func(j *join) error {
|
|
b = append(b, ' ')
|
|
b, err = j.appendHasOneJoin(fmter, b, q.q)
|
|
return err
|
|
})
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
for _, j := range q.q.joins {
|
|
b, err = j.AppendQuery(fmter, b)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
}
|
|
|
|
if len(q.q.where) > 0 || q.q.isSoftDelete() {
|
|
b = append(b, " WHERE "...)
|
|
b, err = q.q.appendWhere(fmter, b)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
}
|
|
|
|
if len(q.q.group) > 0 {
|
|
b = append(b, " GROUP BY "...)
|
|
for i, f := range q.q.group {
|
|
if i > 0 {
|
|
b = append(b, ", "...)
|
|
}
|
|
b, err = f.AppendQuery(fmter, b)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
}
|
|
}
|
|
|
|
if len(q.q.having) > 0 {
|
|
b = append(b, " HAVING "...)
|
|
for i, f := range q.q.having {
|
|
if i > 0 {
|
|
b = append(b, " AND "...)
|
|
}
|
|
b = append(b, '(')
|
|
b, err = f.AppendQuery(fmter, b)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
b = append(b, ')')
|
|
}
|
|
}
|
|
|
|
if q.count == "" {
|
|
if len(q.q.order) > 0 {
|
|
b = append(b, " ORDER BY "...)
|
|
for i, f := range q.q.order {
|
|
if i > 0 {
|
|
b = append(b, ", "...)
|
|
}
|
|
b, err = f.AppendQuery(fmter, b)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
}
|
|
}
|
|
|
|
if q.q.limit != 0 {
|
|
b = append(b, " LIMIT "...)
|
|
b = strconv.AppendInt(b, int64(q.q.limit), 10)
|
|
}
|
|
|
|
if q.q.offset != 0 {
|
|
b = append(b, " OFFSET "...)
|
|
b = strconv.AppendInt(b, int64(q.q.offset), 10)
|
|
}
|
|
|
|
if q.q.selFor != nil {
|
|
b = append(b, " FOR "...)
|
|
b, err = q.q.selFor.AppendQuery(fmter, b)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
}
|
|
} else if cteCount {
|
|
b = append(b, `) SELECT `...)
|
|
b = append(b, q.count...)
|
|
b = append(b, ` FROM "_count_wrapper"`...)
|
|
}
|
|
|
|
if len(q.q.union) > 0 {
|
|
b = append(b, ")"...)
|
|
|
|
for _, u := range q.q.union {
|
|
b = append(b, u.expr...)
|
|
b = append(b, '(')
|
|
b, err = u.query.AppendQuery(fmter, b)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
b = append(b, ")"...)
|
|
}
|
|
}
|
|
|
|
return b, q.q.stickyErr
|
|
}
|
|
|
|
func (q SelectQuery) appendColumns(fmter QueryFormatter, b []byte) (_ []byte, err error) {
|
|
start := len(b)
|
|
|
|
switch {
|
|
case q.q.columns != nil:
|
|
b, err = q.q.appendColumns(fmter, b)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
case q.q.hasExplicitTableModel():
|
|
table := q.q.tableModel.Table()
|
|
if len(table.Fields) > 10 && isTemplateFormatter(fmter) {
|
|
b = append(b, table.Alias...)
|
|
b = append(b, '.')
|
|
b = types.AppendString(b, fmt.Sprintf("%d columns", len(table.Fields)), 2)
|
|
} else {
|
|
b = appendColumns(b, table.Alias, table.Fields)
|
|
}
|
|
default:
|
|
b = append(b, '*')
|
|
}
|
|
|
|
err = q.q.forEachHasOneJoin(func(j *join) error {
|
|
if len(b) != start {
|
|
b = append(b, ", "...)
|
|
start = len(b)
|
|
}
|
|
|
|
b = j.appendHasOneColumns(b)
|
|
return nil
|
|
})
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
b = bytes.TrimSuffix(b, []byte(", "))
|
|
|
|
return b, nil
|
|
}
|
|
|
|
func (q *SelectQuery) isDistinct() bool {
|
|
if q.q.distinctOn != nil {
|
|
return true
|
|
}
|
|
for _, column := range q.q.columns {
|
|
column, ok := column.(*SafeQueryAppender)
|
|
if ok {
|
|
if strings.Contains(column.query, "DISTINCT") ||
|
|
strings.Contains(column.query, "distinct") {
|
|
return true
|
|
}
|
|
}
|
|
}
|
|
return false
|
|
}
|
|
|
|
func (q *SelectQuery) appendTables(fmter QueryFormatter, b []byte) (_ []byte, err error) {
|
|
tables := q.q.tables
|
|
|
|
if q.q.modelHasTableName() {
|
|
table := q.q.tableModel.Table()
|
|
b = fmter.FormatQuery(b, string(table.SQLNameForSelects))
|
|
if table.Alias != "" {
|
|
b = append(b, " AS "...)
|
|
b = append(b, table.Alias...)
|
|
}
|
|
|
|
if len(tables) > 0 {
|
|
b = append(b, ", "...)
|
|
}
|
|
} else if len(tables) > 0 {
|
|
b, err = tables[0].AppendQuery(fmter, b)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
if q.q.modelHasTableAlias() {
|
|
b = append(b, " AS "...)
|
|
b = append(b, q.q.tableModel.Table().Alias...)
|
|
}
|
|
|
|
tables = tables[1:]
|
|
if len(tables) > 0 {
|
|
b = append(b, ", "...)
|
|
}
|
|
}
|
|
|
|
for i, f := range tables {
|
|
if i > 0 {
|
|
b = append(b, ", "...)
|
|
}
|
|
b, err = f.AppendQuery(fmter, b)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
}
|
|
|
|
return b, nil
|
|
}
|
|
|
|
//------------------------------------------------------------------------------
|
|
|
|
type joinQuery struct {
|
|
join *SafeQueryAppender
|
|
on []*condAppender
|
|
}
|
|
|
|
func (j *joinQuery) AppendOn(app *condAppender) {
|
|
j.on = append(j.on, app)
|
|
}
|
|
|
|
func (j *joinQuery) AppendQuery(fmter QueryFormatter, b []byte) (_ []byte, err error) {
|
|
b = append(b, ' ')
|
|
|
|
b, err = j.join.AppendQuery(fmter, b)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
if len(j.on) > 0 {
|
|
b = append(b, " ON "...)
|
|
for i, on := range j.on {
|
|
if i > 0 {
|
|
b = on.AppendSep(b)
|
|
}
|
|
b, err = on.AppendQuery(fmter, b)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
}
|
|
}
|
|
|
|
return b, nil
|
|
}
|