2021-01-18 01:23:20 +00:00
|
|
|
package sqlite
|
|
|
|
|
2021-02-01 20:57:56 +00:00
|
|
|
import (
|
2021-03-16 00:13:14 +00:00
|
|
|
"fmt"
|
2021-02-01 20:57:56 +00:00
|
|
|
"regexp"
|
2021-06-03 10:52:19 +00:00
|
|
|
"strings"
|
2021-02-01 20:57:56 +00:00
|
|
|
|
|
|
|
"github.com/stashapp/stash/pkg/models"
|
|
|
|
)
|
2021-01-18 01:23:20 +00:00
|
|
|
|
|
|
|
type queryBuilder struct {
|
|
|
|
repository *repository
|
|
|
|
|
|
|
|
body string
|
|
|
|
|
2021-03-02 00:27:36 +00:00
|
|
|
joins joins
|
2021-01-18 01:23:20 +00:00
|
|
|
whereClauses []string
|
|
|
|
havingClauses []string
|
|
|
|
args []interface{}
|
2021-06-03 10:52:19 +00:00
|
|
|
withClauses []string
|
2021-01-18 01:23:20 +00:00
|
|
|
|
|
|
|
sortAndPagination string
|
2021-02-01 20:57:56 +00:00
|
|
|
|
|
|
|
err error
|
2021-01-18 01:23:20 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
func (qb queryBuilder) executeFind() ([]int, int, error) {
|
2021-02-01 20:57:56 +00:00
|
|
|
if qb.err != nil {
|
|
|
|
return nil, 0, qb.err
|
|
|
|
}
|
|
|
|
|
2021-03-02 00:27:36 +00:00
|
|
|
body := qb.body
|
|
|
|
body += qb.joins.toSQL()
|
|
|
|
|
2021-06-03 10:52:19 +00:00
|
|
|
return qb.repository.executeFindQuery(body, qb.args, qb.sortAndPagination, qb.whereClauses, qb.havingClauses, qb.withClauses)
|
2021-01-18 01:23:20 +00:00
|
|
|
}
|
|
|
|
|
2021-04-15 00:46:31 +00:00
|
|
|
func (qb queryBuilder) executeCount() (int, error) {
|
|
|
|
if qb.err != nil {
|
|
|
|
return 0, qb.err
|
|
|
|
}
|
|
|
|
|
|
|
|
body := qb.body
|
|
|
|
body += qb.joins.toSQL()
|
|
|
|
|
2021-06-03 10:52:19 +00:00
|
|
|
withClause := ""
|
|
|
|
if len(qb.withClauses) > 0 {
|
|
|
|
withClause = "WITH " + strings.Join(qb.withClauses, ", ") + " "
|
|
|
|
}
|
|
|
|
|
2021-04-15 00:46:31 +00:00
|
|
|
body = qb.repository.buildQueryBody(body, qb.whereClauses, qb.havingClauses)
|
2021-06-03 10:52:19 +00:00
|
|
|
countQuery := withClause + qb.repository.buildCountQuery(body)
|
2021-04-15 00:46:31 +00:00
|
|
|
return qb.repository.runCountQuery(countQuery, qb.args)
|
|
|
|
}
|
|
|
|
|
2021-01-18 01:23:20 +00:00
|
|
|
func (qb *queryBuilder) addWhere(clauses ...string) {
|
|
|
|
for _, clause := range clauses {
|
|
|
|
if len(clause) > 0 {
|
|
|
|
qb.whereClauses = append(qb.whereClauses, clause)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func (qb *queryBuilder) addHaving(clauses ...string) {
|
|
|
|
for _, clause := range clauses {
|
|
|
|
if len(clause) > 0 {
|
|
|
|
qb.havingClauses = append(qb.havingClauses, clause)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-06-03 10:52:19 +00:00
|
|
|
func (qb *queryBuilder) addWith(clauses ...string) {
|
|
|
|
for _, clause := range clauses {
|
|
|
|
if len(clause) > 0 {
|
|
|
|
qb.withClauses = append(qb.withClauses, clause)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-01-18 01:23:20 +00:00
|
|
|
func (qb *queryBuilder) addArg(args ...interface{}) {
|
|
|
|
qb.args = append(qb.args, args...)
|
|
|
|
}
|
|
|
|
|
2021-03-02 00:27:36 +00:00
|
|
|
func (qb *queryBuilder) join(table, as, onClause string) {
|
|
|
|
newJoin := join{
|
|
|
|
table: table,
|
|
|
|
as: as,
|
|
|
|
onClause: onClause,
|
|
|
|
}
|
|
|
|
|
|
|
|
qb.joins.add(newJoin)
|
|
|
|
}
|
|
|
|
|
|
|
|
func (qb *queryBuilder) addJoins(joins ...join) {
|
|
|
|
qb.joins.add(joins...)
|
|
|
|
}
|
|
|
|
|
|
|
|
func (qb *queryBuilder) addFilter(f *filterBuilder) {
|
|
|
|
err := f.getError()
|
|
|
|
if err != nil {
|
|
|
|
qb.err = err
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
2021-06-03 10:52:19 +00:00
|
|
|
clause, args := f.generateWithClauses()
|
|
|
|
if len(clause) > 0 {
|
|
|
|
qb.addWith(clause)
|
|
|
|
}
|
|
|
|
|
|
|
|
if len(args) > 0 {
|
|
|
|
// WITH clause always comes first and thus precedes alk args
|
|
|
|
qb.args = append(args, qb.args...)
|
|
|
|
}
|
|
|
|
|
|
|
|
clause, args = f.generateWhereClauses()
|
2021-03-02 00:27:36 +00:00
|
|
|
if len(clause) > 0 {
|
|
|
|
qb.addWhere(clause)
|
|
|
|
}
|
|
|
|
|
|
|
|
if len(args) > 0 {
|
|
|
|
qb.addArg(args...)
|
|
|
|
}
|
|
|
|
|
|
|
|
clause, args = f.generateHavingClauses()
|
|
|
|
if len(clause) > 0 {
|
|
|
|
qb.addHaving(clause)
|
|
|
|
}
|
|
|
|
|
|
|
|
if len(args) > 0 {
|
|
|
|
qb.addArg(args...)
|
|
|
|
}
|
|
|
|
|
|
|
|
qb.addJoins(f.getAllJoins()...)
|
|
|
|
}
|
|
|
|
|
2021-01-18 01:23:20 +00:00
|
|
|
func (qb *queryBuilder) handleIntCriterionInput(c *models.IntCriterionInput, column string) {
|
|
|
|
if c != nil {
|
|
|
|
clause, count := getIntCriterionWhereClause(column, *c)
|
|
|
|
qb.addWhere(clause)
|
|
|
|
if count == 1 {
|
|
|
|
qb.addArg(c.Value)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func (qb *queryBuilder) handleStringCriterionInput(c *models.StringCriterionInput, column string) {
|
|
|
|
if c != nil {
|
|
|
|
if modifier := c.Modifier; c.Modifier.IsValid() {
|
|
|
|
switch modifier {
|
|
|
|
case models.CriterionModifierIncludes:
|
|
|
|
clause, thisArgs := getSearchBinding([]string{column}, c.Value, false)
|
|
|
|
qb.addWhere(clause)
|
|
|
|
qb.addArg(thisArgs...)
|
|
|
|
case models.CriterionModifierExcludes:
|
|
|
|
clause, thisArgs := getSearchBinding([]string{column}, c.Value, true)
|
|
|
|
qb.addWhere(clause)
|
|
|
|
qb.addArg(thisArgs...)
|
|
|
|
case models.CriterionModifierEquals:
|
|
|
|
qb.addWhere(column + " LIKE ?")
|
|
|
|
qb.addArg(c.Value)
|
|
|
|
case models.CriterionModifierNotEquals:
|
|
|
|
qb.addWhere(column + " NOT LIKE ?")
|
|
|
|
qb.addArg(c.Value)
|
2021-02-01 20:57:56 +00:00
|
|
|
case models.CriterionModifierMatchesRegex:
|
|
|
|
if _, err := regexp.Compile(c.Value); err != nil {
|
|
|
|
qb.err = err
|
|
|
|
return
|
|
|
|
}
|
2021-03-16 00:13:14 +00:00
|
|
|
qb.addWhere(fmt.Sprintf("(%s IS NOT NULL AND %[1]s regexp ?)", column))
|
2021-02-01 20:57:56 +00:00
|
|
|
qb.addArg(c.Value)
|
|
|
|
case models.CriterionModifierNotMatchesRegex:
|
|
|
|
if _, err := regexp.Compile(c.Value); err != nil {
|
|
|
|
qb.err = err
|
|
|
|
return
|
|
|
|
}
|
2021-03-16 00:13:14 +00:00
|
|
|
qb.addWhere(fmt.Sprintf("(%s IS NULL OR %[1]s NOT regexp ?)", column))
|
2021-02-01 20:57:56 +00:00
|
|
|
qb.addArg(c.Value)
|
2021-03-01 00:48:25 +00:00
|
|
|
case models.CriterionModifierIsNull:
|
|
|
|
qb.addWhere("(" + column + " IS NULL OR TRIM(" + column + ") = '')")
|
|
|
|
case models.CriterionModifierNotNull:
|
|
|
|
qb.addWhere("(" + column + " IS NOT NULL AND TRIM(" + column + ") != '')")
|
2021-01-18 01:23:20 +00:00
|
|
|
default:
|
|
|
|
clause, count := getSimpleCriterionClause(modifier, "?")
|
|
|
|
qb.addWhere(column + " " + clause)
|
|
|
|
if count == 1 {
|
|
|
|
qb.addArg(c.Value)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2021-04-09 08:46:00 +00:00
|
|
|
|
|
|
|
func (qb *queryBuilder) handleCountCriterion(countFilter *models.IntCriterionInput, primaryTable, joinTable, primaryFK string) {
|
|
|
|
if countFilter != nil {
|
|
|
|
clause, count := getCountCriterionClause(primaryTable, joinTable, primaryFK, *countFilter)
|
|
|
|
|
|
|
|
if count == 1 {
|
|
|
|
qb.addArg(countFilter.Value)
|
|
|
|
}
|
|
|
|
|
|
|
|
qb.addWhere(clause)
|
|
|
|
}
|
|
|
|
}
|