mirror of
https://github.com/stashapp/stash.git
synced 2025-12-17 20:34:37 +03:00
* Restructure data layer part 2 (#2599) * Refactor and separate image model * Refactor image query builder * Handle relationships in image query builder * Remove relationship management methods * Refactor gallery model/query builder * Add scenes to gallery model * Convert scene model * Refactor scene models * Remove unused methods * Add unit tests for gallery * Add image tests * Add scene tests * Convert unnecessary scene value pointers to values * Convert unnecessary pointer values to values * Refactor scene partial * Add scene partial tests * Refactor ImagePartial * Add image partial tests * Refactor gallery partial update * Add partial gallery update tests * Use zero/null package for null values * Add files and scan system * Add sqlite implementation for files/folders * Add unit tests for files/folders * Image refactors * Update image data layer * Refactor gallery model and creation * Refactor scene model * Refactor scenes * Don't set title from filename * Allow galleries to freely add/remove images * Add multiple scene file support to graphql and UI * Add multiple file support for images in graphql/UI * Add multiple file for galleries in graphql/UI * Remove use of some deprecated fields * Remove scene path usage * Remove gallery path usage * Remove path from image * Move funscript to video file * Refactor caption detection * Migrate existing data * Add post commit/rollback hook system * Lint. Comment out import/export tests * Add WithDatabase read only wrapper * Prepend tasks to list * Add 32 pre-migration * Add warnings in release and migration notes
696 lines
25 KiB
Go
696 lines
25 KiB
Go
package goqu
|
|
|
|
import (
|
|
"context"
|
|
"fmt"
|
|
|
|
"github.com/doug-martin/goqu/v9/exec"
|
|
"github.com/doug-martin/goqu/v9/exp"
|
|
"github.com/doug-martin/goqu/v9/internal/errors"
|
|
"github.com/doug-martin/goqu/v9/internal/sb"
|
|
)
|
|
|
|
// Dataset for creating and/or executing SELECT SQL statements.
|
|
type SelectDataset struct {
|
|
dialect SQLDialect
|
|
clauses exp.SelectClauses
|
|
isPrepared prepared
|
|
queryFactory exec.QueryFactory
|
|
err error
|
|
}
|
|
|
|
var ErrQueryFactoryNotFoundError = errors.New(
|
|
"unable to execute query did you use goqu.Database#From to create the dataset",
|
|
)
|
|
|
|
// used internally by database to create a database with a specific adapter
|
|
func newDataset(d string, queryFactory exec.QueryFactory) *SelectDataset {
|
|
return &SelectDataset{
|
|
clauses: exp.NewSelectClauses(),
|
|
dialect: GetDialect(d),
|
|
queryFactory: queryFactory,
|
|
}
|
|
}
|
|
|
|
func From(table ...interface{}) *SelectDataset {
|
|
return newDataset("default", nil).From(table...)
|
|
}
|
|
|
|
func Select(cols ...interface{}) *SelectDataset {
|
|
return newDataset("default", nil).Select(cols...)
|
|
}
|
|
|
|
// Sets the adapter used to serialize values and create the SQL statement
|
|
func (sd *SelectDataset) WithDialect(dl string) *SelectDataset {
|
|
ds := sd.copy(sd.GetClauses())
|
|
ds.dialect = GetDialect(dl)
|
|
return ds
|
|
}
|
|
|
|
// Set the parameter interpolation behavior. See examples
|
|
//
|
|
// prepared: If true the dataset WILL NOT interpolate the parameters.
|
|
func (sd *SelectDataset) Prepared(prepared bool) *SelectDataset {
|
|
ret := sd.copy(sd.clauses)
|
|
ret.isPrepared = preparedFromBool(prepared)
|
|
return ret
|
|
}
|
|
|
|
func (sd *SelectDataset) IsPrepared() bool {
|
|
return sd.isPrepared.Bool()
|
|
}
|
|
|
|
// Returns the current adapter on the dataset
|
|
func (sd *SelectDataset) Dialect() SQLDialect {
|
|
return sd.dialect
|
|
}
|
|
|
|
// Returns the current adapter on the dataset
|
|
func (sd *SelectDataset) SetDialect(dialect SQLDialect) *SelectDataset {
|
|
cd := sd.copy(sd.GetClauses())
|
|
cd.dialect = dialect
|
|
return cd
|
|
}
|
|
|
|
func (sd *SelectDataset) Expression() exp.Expression {
|
|
return sd
|
|
}
|
|
|
|
// Clones the dataset
|
|
func (sd *SelectDataset) Clone() exp.Expression {
|
|
return sd.copy(sd.clauses)
|
|
}
|
|
|
|
// Returns the current clauses on the dataset.
|
|
func (sd *SelectDataset) GetClauses() exp.SelectClauses {
|
|
return sd.clauses
|
|
}
|
|
|
|
// used interally to copy the dataset
|
|
func (sd *SelectDataset) copy(clauses exp.SelectClauses) *SelectDataset {
|
|
return &SelectDataset{
|
|
dialect: sd.dialect,
|
|
clauses: clauses,
|
|
isPrepared: sd.isPrepared,
|
|
queryFactory: sd.queryFactory,
|
|
err: sd.err,
|
|
}
|
|
}
|
|
|
|
// Creates a new UpdateDataset using the FROM of this dataset. This method will also copy over the `WITH`, `WHERE`,
|
|
// `ORDER , and `LIMIT`
|
|
func (sd *SelectDataset) Update() *UpdateDataset {
|
|
u := newUpdateDataset(sd.dialect.Dialect(), sd.queryFactory).
|
|
Prepared(sd.isPrepared.Bool())
|
|
if sd.clauses.HasSources() {
|
|
u = u.Table(sd.GetClauses().From().Columns()[0])
|
|
}
|
|
c := u.clauses
|
|
for _, ce := range sd.clauses.CommonTables() {
|
|
c = c.CommonTablesAppend(ce)
|
|
}
|
|
if sd.clauses.Where() != nil {
|
|
c = c.WhereAppend(sd.clauses.Where())
|
|
}
|
|
if sd.clauses.HasLimit() {
|
|
c = c.SetLimit(sd.clauses.Limit())
|
|
}
|
|
if sd.clauses.HasOrder() {
|
|
for _, oe := range sd.clauses.Order().Columns() {
|
|
c = c.OrderAppend(oe.(exp.OrderedExpression))
|
|
}
|
|
}
|
|
u.clauses = c
|
|
return u
|
|
}
|
|
|
|
// Creates a new InsertDataset using the FROM of this dataset. This method will also copy over the `WITH` clause to the
|
|
// insert.
|
|
func (sd *SelectDataset) Insert() *InsertDataset {
|
|
i := newInsertDataset(sd.dialect.Dialect(), sd.queryFactory).
|
|
Prepared(sd.isPrepared.Bool())
|
|
if sd.clauses.HasSources() {
|
|
i = i.Into(sd.GetClauses().From().Columns()[0])
|
|
}
|
|
c := i.clauses
|
|
for _, ce := range sd.clauses.CommonTables() {
|
|
c = c.CommonTablesAppend(ce)
|
|
}
|
|
i.clauses = c
|
|
return i
|
|
}
|
|
|
|
// Creates a new DeleteDataset using the FROM of this dataset. This method will also copy over the `WITH`, `WHERE`,
|
|
// `ORDER , and `LIMIT`
|
|
func (sd *SelectDataset) Delete() *DeleteDataset {
|
|
d := newDeleteDataset(sd.dialect.Dialect(), sd.queryFactory).
|
|
Prepared(sd.isPrepared.Bool())
|
|
if sd.clauses.HasSources() {
|
|
d = d.From(sd.clauses.From().Columns()[0])
|
|
}
|
|
c := d.clauses
|
|
for _, ce := range sd.clauses.CommonTables() {
|
|
c = c.CommonTablesAppend(ce)
|
|
}
|
|
if sd.clauses.Where() != nil {
|
|
c = c.WhereAppend(sd.clauses.Where())
|
|
}
|
|
if sd.clauses.HasLimit() {
|
|
c = c.SetLimit(sd.clauses.Limit())
|
|
}
|
|
if sd.clauses.HasOrder() {
|
|
for _, oe := range sd.clauses.Order().Columns() {
|
|
c = c.OrderAppend(oe.(exp.OrderedExpression))
|
|
}
|
|
}
|
|
d.clauses = c
|
|
return d
|
|
}
|
|
|
|
// Creates a new TruncateDataset using the FROM of this dataset.
|
|
func (sd *SelectDataset) Truncate() *TruncateDataset {
|
|
td := newTruncateDataset(sd.dialect.Dialect(), sd.queryFactory)
|
|
if sd.clauses.HasSources() {
|
|
td = td.Table(sd.clauses.From())
|
|
}
|
|
return td
|
|
}
|
|
|
|
// Creates a WITH clause for a common table expression (CTE).
|
|
//
|
|
// The name will be available to SELECT from in the associated query; and can optionally
|
|
// contain a list of column names "name(col1, col2, col3)".
|
|
//
|
|
// The name will refer to the results of the specified subquery.
|
|
func (sd *SelectDataset) With(name string, subquery exp.Expression) *SelectDataset {
|
|
return sd.copy(sd.clauses.CommonTablesAppend(exp.NewCommonTableExpression(false, name, subquery)))
|
|
}
|
|
|
|
// Creates a WITH RECURSIVE clause for a common table expression (CTE)
|
|
//
|
|
// The name will be available to SELECT from in the associated query; and must
|
|
// contain a list of column names "name(col1, col2, col3)" for a recursive clause.
|
|
//
|
|
// The name will refer to the results of the specified subquery. The subquery for
|
|
// a recursive query will always end with a UNION or UNION ALL with a clause that
|
|
// refers to the CTE by name.
|
|
func (sd *SelectDataset) WithRecursive(name string, subquery exp.Expression) *SelectDataset {
|
|
return sd.copy(sd.clauses.CommonTablesAppend(exp.NewCommonTableExpression(true, name, subquery)))
|
|
}
|
|
|
|
// Adds columns to the SELECT clause. See examples
|
|
// You can pass in the following.
|
|
// string: Will automatically be turned into an identifier
|
|
// Dataset: Will use the SQL generated from that Dataset. If the dataset is aliased it will use that alias as the
|
|
// column name.
|
|
// LiteralExpression: (See Literal) Will use the literal SQL
|
|
// SQLFunction: (See Func, MIN, MAX, COUNT....)
|
|
// Struct: If passing in an instance of a struct, we will parse the struct for the column names to select.
|
|
// See examples
|
|
func (sd *SelectDataset) Select(selects ...interface{}) *SelectDataset {
|
|
if len(selects) == 0 {
|
|
return sd.ClearSelect()
|
|
}
|
|
return sd.copy(sd.clauses.SetSelect(exp.NewColumnListExpression(selects...)))
|
|
}
|
|
|
|
// Adds columns to the SELECT DISTINCT clause. See examples
|
|
// You can pass in the following.
|
|
// string: Will automatically be turned into an identifier
|
|
// Dataset: Will use the SQL generated from that Dataset. If the dataset is aliased it will use that alias as the
|
|
// column name.
|
|
// LiteralExpression: (See Literal) Will use the literal SQL
|
|
// SQLFunction: (See Func, MIN, MAX, COUNT....)
|
|
// Struct: If passing in an instance of a struct, we will parse the struct for the column names to select.
|
|
// See examples
|
|
// Deprecated: Use Distinct() instead.
|
|
func (sd *SelectDataset) SelectDistinct(selects ...interface{}) *SelectDataset {
|
|
if len(selects) == 0 {
|
|
cleared := sd.ClearSelect()
|
|
return cleared.copy(cleared.clauses.SetDistinct(nil))
|
|
}
|
|
return sd.copy(sd.clauses.SetSelect(exp.NewColumnListExpression(selects...)).SetDistinct(exp.NewColumnListExpression()))
|
|
}
|
|
|
|
// Resets to SELECT *. If the SelectDistinct or Distinct was used the returned Dataset will have the the dataset set to SELECT *.
|
|
// See examples.
|
|
func (sd *SelectDataset) ClearSelect() *SelectDataset {
|
|
return sd.copy(sd.clauses.SetSelect(exp.NewColumnListExpression(exp.Star())).SetDistinct(nil))
|
|
}
|
|
|
|
// Adds columns to the SELECT clause. See examples
|
|
// You can pass in the following.
|
|
// string: Will automatically be turned into an identifier
|
|
// Dataset: Will use the SQL generated from that Dataset. If the dataset is aliased it will use that alias as the
|
|
// column name.
|
|
// LiteralExpression: (See Literal) Will use the literal SQL
|
|
// SQLFunction: (See Func, MIN, MAX, COUNT....)
|
|
func (sd *SelectDataset) SelectAppend(selects ...interface{}) *SelectDataset {
|
|
return sd.copy(sd.clauses.SelectAppend(exp.NewColumnListExpression(selects...)))
|
|
}
|
|
|
|
func (sd *SelectDataset) Distinct(on ...interface{}) *SelectDataset {
|
|
return sd.copy(sd.clauses.SetDistinct(exp.NewColumnListExpression(on...)))
|
|
}
|
|
|
|
// Adds a FROM clause. This return a new dataset with the original sources replaced. See examples.
|
|
// You can pass in the following.
|
|
// string: Will automatically be turned into an identifier
|
|
// Dataset: Will be added as a sub select. If the Dataset is not aliased it will automatically be aliased
|
|
// LiteralExpression: (See Literal) Will use the literal SQL
|
|
func (sd *SelectDataset) From(from ...interface{}) *SelectDataset {
|
|
var sources []interface{}
|
|
numSources := 0
|
|
for _, source := range from {
|
|
if ds, ok := source.(*SelectDataset); ok && !ds.clauses.HasAlias() {
|
|
numSources++
|
|
sources = append(sources, ds.As(fmt.Sprintf("t%d", numSources)))
|
|
} else {
|
|
sources = append(sources, source)
|
|
}
|
|
}
|
|
return sd.copy(sd.clauses.SetFrom(exp.NewColumnListExpression(sources...)))
|
|
}
|
|
|
|
// Returns a new Dataset with the current one as an source. If the current Dataset is not aliased (See Dataset#As) then
|
|
// it will automatically be aliased. See examples.
|
|
func (sd *SelectDataset) FromSelf() *SelectDataset {
|
|
return sd.copy(exp.NewSelectClauses()).From(sd)
|
|
}
|
|
|
|
// Alias to InnerJoin. See examples.
|
|
func (sd *SelectDataset) Join(table exp.Expression, condition exp.JoinCondition) *SelectDataset {
|
|
return sd.InnerJoin(table, condition)
|
|
}
|
|
|
|
// Adds an INNER JOIN clause. See examples.
|
|
func (sd *SelectDataset) InnerJoin(table exp.Expression, condition exp.JoinCondition) *SelectDataset {
|
|
return sd.joinTable(exp.NewConditionedJoinExpression(exp.InnerJoinType, table, condition))
|
|
}
|
|
|
|
// Adds a FULL OUTER JOIN clause. See examples.
|
|
func (sd *SelectDataset) FullOuterJoin(table exp.Expression, condition exp.JoinCondition) *SelectDataset {
|
|
return sd.joinTable(exp.NewConditionedJoinExpression(exp.FullOuterJoinType, table, condition))
|
|
}
|
|
|
|
// Adds a RIGHT OUTER JOIN clause. See examples.
|
|
func (sd *SelectDataset) RightOuterJoin(table exp.Expression, condition exp.JoinCondition) *SelectDataset {
|
|
return sd.joinTable(exp.NewConditionedJoinExpression(exp.RightOuterJoinType, table, condition))
|
|
}
|
|
|
|
// Adds a LEFT OUTER JOIN clause. See examples.
|
|
func (sd *SelectDataset) LeftOuterJoin(table exp.Expression, condition exp.JoinCondition) *SelectDataset {
|
|
return sd.joinTable(exp.NewConditionedJoinExpression(exp.LeftOuterJoinType, table, condition))
|
|
}
|
|
|
|
// Adds a FULL JOIN clause. See examples.
|
|
func (sd *SelectDataset) FullJoin(table exp.Expression, condition exp.JoinCondition) *SelectDataset {
|
|
return sd.joinTable(exp.NewConditionedJoinExpression(exp.FullJoinType, table, condition))
|
|
}
|
|
|
|
// Adds a RIGHT JOIN clause. See examples.
|
|
func (sd *SelectDataset) RightJoin(table exp.Expression, condition exp.JoinCondition) *SelectDataset {
|
|
return sd.joinTable(exp.NewConditionedJoinExpression(exp.RightJoinType, table, condition))
|
|
}
|
|
|
|
// Adds a LEFT JOIN clause. See examples.
|
|
func (sd *SelectDataset) LeftJoin(table exp.Expression, condition exp.JoinCondition) *SelectDataset {
|
|
return sd.joinTable(exp.NewConditionedJoinExpression(exp.LeftJoinType, table, condition))
|
|
}
|
|
|
|
// Adds a NATURAL JOIN clause. See examples.
|
|
func (sd *SelectDataset) NaturalJoin(table exp.Expression) *SelectDataset {
|
|
return sd.joinTable(exp.NewUnConditionedJoinExpression(exp.NaturalJoinType, table))
|
|
}
|
|
|
|
// Adds a NATURAL LEFT JOIN clause. See examples.
|
|
func (sd *SelectDataset) NaturalLeftJoin(table exp.Expression) *SelectDataset {
|
|
return sd.joinTable(exp.NewUnConditionedJoinExpression(exp.NaturalLeftJoinType, table))
|
|
}
|
|
|
|
// Adds a NATURAL RIGHT JOIN clause. See examples.
|
|
func (sd *SelectDataset) NaturalRightJoin(table exp.Expression) *SelectDataset {
|
|
return sd.joinTable(exp.NewUnConditionedJoinExpression(exp.NaturalRightJoinType, table))
|
|
}
|
|
|
|
// Adds a NATURAL FULL JOIN clause. See examples.
|
|
func (sd *SelectDataset) NaturalFullJoin(table exp.Expression) *SelectDataset {
|
|
return sd.joinTable(exp.NewUnConditionedJoinExpression(exp.NaturalFullJoinType, table))
|
|
}
|
|
|
|
// Adds a CROSS JOIN clause. See examples.
|
|
func (sd *SelectDataset) CrossJoin(table exp.Expression) *SelectDataset {
|
|
return sd.joinTable(exp.NewUnConditionedJoinExpression(exp.CrossJoinType, table))
|
|
}
|
|
|
|
// Joins this Datasets table with another
|
|
func (sd *SelectDataset) joinTable(join exp.JoinExpression) *SelectDataset {
|
|
return sd.copy(sd.clauses.JoinsAppend(join))
|
|
}
|
|
|
|
// Adds a WHERE clause. See examples.
|
|
func (sd *SelectDataset) Where(expressions ...exp.Expression) *SelectDataset {
|
|
return sd.copy(sd.clauses.WhereAppend(expressions...))
|
|
}
|
|
|
|
// Removes the WHERE clause. See examples.
|
|
func (sd *SelectDataset) ClearWhere() *SelectDataset {
|
|
return sd.copy(sd.clauses.ClearWhere())
|
|
}
|
|
|
|
// Adds a FOR UPDATE clause. See examples.
|
|
func (sd *SelectDataset) ForUpdate(waitOption exp.WaitOption, of ...exp.IdentifierExpression) *SelectDataset {
|
|
return sd.withLock(exp.ForUpdate, waitOption, of...)
|
|
}
|
|
|
|
// Adds a FOR NO KEY UPDATE clause. See examples.
|
|
func (sd *SelectDataset) ForNoKeyUpdate(waitOption exp.WaitOption, of ...exp.IdentifierExpression) *SelectDataset {
|
|
return sd.withLock(exp.ForNoKeyUpdate, waitOption, of...)
|
|
}
|
|
|
|
// Adds a FOR KEY SHARE clause. See examples.
|
|
func (sd *SelectDataset) ForKeyShare(waitOption exp.WaitOption, of ...exp.IdentifierExpression) *SelectDataset {
|
|
return sd.withLock(exp.ForKeyShare, waitOption, of...)
|
|
}
|
|
|
|
// Adds a FOR SHARE clause. See examples.
|
|
func (sd *SelectDataset) ForShare(waitOption exp.WaitOption, of ...exp.IdentifierExpression) *SelectDataset {
|
|
return sd.withLock(exp.ForShare, waitOption, of...)
|
|
}
|
|
|
|
func (sd *SelectDataset) withLock(strength exp.LockStrength, option exp.WaitOption, of ...exp.IdentifierExpression) *SelectDataset {
|
|
return sd.copy(sd.clauses.SetLock(exp.NewLock(strength, option, of...)))
|
|
}
|
|
|
|
// Adds a GROUP BY clause. See examples.
|
|
func (sd *SelectDataset) GroupBy(groupBy ...interface{}) *SelectDataset {
|
|
return sd.copy(sd.clauses.SetGroupBy(exp.NewColumnListExpression(groupBy...)))
|
|
}
|
|
|
|
// Adds more columns to the current GROUP BY clause. See examples.
|
|
func (sd *SelectDataset) GroupByAppend(groupBy ...interface{}) *SelectDataset {
|
|
return sd.copy(sd.clauses.GroupByAppend(exp.NewColumnListExpression(groupBy...)))
|
|
}
|
|
|
|
// Adds a HAVING clause. See examples.
|
|
func (sd *SelectDataset) Having(expressions ...exp.Expression) *SelectDataset {
|
|
return sd.copy(sd.clauses.HavingAppend(expressions...))
|
|
}
|
|
|
|
// Adds a ORDER clause. If the ORDER is currently set it replaces it. See examples.
|
|
func (sd *SelectDataset) Order(order ...exp.OrderedExpression) *SelectDataset {
|
|
return sd.copy(sd.clauses.SetOrder(order...))
|
|
}
|
|
|
|
// Adds a more columns to the current ORDER BY clause. If no order has be previously specified it is the same as
|
|
// calling Order. See examples.
|
|
func (sd *SelectDataset) OrderAppend(order ...exp.OrderedExpression) *SelectDataset {
|
|
return sd.copy(sd.clauses.OrderAppend(order...))
|
|
}
|
|
|
|
// Adds a more columns to the beginning of the current ORDER BY clause. If no order has be previously specified it is the same as
|
|
// calling Order. See examples.
|
|
func (sd *SelectDataset) OrderPrepend(order ...exp.OrderedExpression) *SelectDataset {
|
|
return sd.copy(sd.clauses.OrderPrepend(order...))
|
|
}
|
|
|
|
// Removes the ORDER BY clause. See examples.
|
|
func (sd *SelectDataset) ClearOrder() *SelectDataset {
|
|
return sd.copy(sd.clauses.ClearOrder())
|
|
}
|
|
|
|
// Adds a LIMIT clause. If the LIMIT is currently set it replaces it. See examples.
|
|
func (sd *SelectDataset) Limit(limit uint) *SelectDataset {
|
|
if limit > 0 {
|
|
return sd.copy(sd.clauses.SetLimit(limit))
|
|
}
|
|
return sd.copy(sd.clauses.ClearLimit())
|
|
}
|
|
|
|
// Adds a LIMIT ALL clause. If the LIMIT is currently set it replaces it. See examples.
|
|
func (sd *SelectDataset) LimitAll() *SelectDataset {
|
|
return sd.copy(sd.clauses.SetLimit(L("ALL")))
|
|
}
|
|
|
|
// Removes the LIMIT clause.
|
|
func (sd *SelectDataset) ClearLimit() *SelectDataset {
|
|
return sd.copy(sd.clauses.ClearLimit())
|
|
}
|
|
|
|
// Adds an OFFSET clause. If the OFFSET is currently set it replaces it. See examples.
|
|
func (sd *SelectDataset) Offset(offset uint) *SelectDataset {
|
|
return sd.copy(sd.clauses.SetOffset(offset))
|
|
}
|
|
|
|
// Removes the OFFSET clause from the Dataset
|
|
func (sd *SelectDataset) ClearOffset() *SelectDataset {
|
|
return sd.copy(sd.clauses.ClearOffset())
|
|
}
|
|
|
|
// Creates an UNION statement with another dataset.
|
|
// If this or the other dataset has a limit or offset it will use that dataset as a subselect in the FROM clause.
|
|
// See examples.
|
|
func (sd *SelectDataset) Union(other *SelectDataset) *SelectDataset {
|
|
return sd.withCompound(exp.UnionCompoundType, other.CompoundFromSelf())
|
|
}
|
|
|
|
// Creates an UNION ALL statement with another dataset.
|
|
// If this or the other dataset has a limit or offset it will use that dataset as a subselect in the FROM clause.
|
|
// See examples.
|
|
func (sd *SelectDataset) UnionAll(other *SelectDataset) *SelectDataset {
|
|
return sd.withCompound(exp.UnionAllCompoundType, other.CompoundFromSelf())
|
|
}
|
|
|
|
// Creates an INTERSECT statement with another dataset.
|
|
// If this or the other dataset has a limit or offset it will use that dataset as a subselect in the FROM clause.
|
|
// See examples.
|
|
func (sd *SelectDataset) Intersect(other *SelectDataset) *SelectDataset {
|
|
return sd.withCompound(exp.IntersectCompoundType, other.CompoundFromSelf())
|
|
}
|
|
|
|
// Creates an INTERSECT ALL statement with another dataset.
|
|
// If this or the other dataset has a limit or offset it will use that dataset as a subselect in the FROM clause.
|
|
// See examples.
|
|
func (sd *SelectDataset) IntersectAll(other *SelectDataset) *SelectDataset {
|
|
return sd.withCompound(exp.IntersectAllCompoundType, other.CompoundFromSelf())
|
|
}
|
|
|
|
func (sd *SelectDataset) withCompound(ct exp.CompoundType, other exp.AppendableExpression) *SelectDataset {
|
|
ce := exp.NewCompoundExpression(ct, other)
|
|
ret := sd.CompoundFromSelf()
|
|
ret.clauses = ret.clauses.CompoundsAppend(ce)
|
|
return ret
|
|
}
|
|
|
|
// Used internally to determine if the dataset needs to use iteself as a source.
|
|
// If the dataset has an order or limit it will select from itself
|
|
func (sd *SelectDataset) CompoundFromSelf() *SelectDataset {
|
|
if sd.clauses.HasOrder() || sd.clauses.HasLimit() {
|
|
return sd.FromSelf()
|
|
}
|
|
return sd.copy(sd.clauses)
|
|
}
|
|
|
|
// Sets the alias for this dataset. This is typically used when using a Dataset as a subselect. See examples.
|
|
func (sd *SelectDataset) As(alias string) *SelectDataset {
|
|
return sd.copy(sd.clauses.SetAlias(T(alias)))
|
|
}
|
|
|
|
// Returns the alias value as an identiier expression
|
|
func (sd *SelectDataset) GetAs() exp.IdentifierExpression {
|
|
return sd.clauses.Alias()
|
|
}
|
|
|
|
// Sets the WINDOW clauses
|
|
func (sd *SelectDataset) Window(ws ...exp.WindowExpression) *SelectDataset {
|
|
return sd.copy(sd.clauses.SetWindows(ws))
|
|
}
|
|
|
|
// Sets the WINDOW clauses
|
|
func (sd *SelectDataset) WindowAppend(ws ...exp.WindowExpression) *SelectDataset {
|
|
return sd.copy(sd.clauses.WindowsAppend(ws...))
|
|
}
|
|
|
|
// Sets the WINDOW clauses
|
|
func (sd *SelectDataset) ClearWindow() *SelectDataset {
|
|
return sd.copy(sd.clauses.ClearWindows())
|
|
}
|
|
|
|
// Get any error that has been set or nil if no error has been set.
|
|
func (sd *SelectDataset) Error() error {
|
|
return sd.err
|
|
}
|
|
|
|
// Set an error on the dataset if one has not already been set. This error will be returned by a future call to Error
|
|
// or as part of ToSQL. This can be used by end users to record errors while building up queries without having to
|
|
// track those separately.
|
|
func (sd *SelectDataset) SetError(err error) *SelectDataset {
|
|
if sd.err == nil {
|
|
sd.err = err
|
|
}
|
|
|
|
return sd
|
|
}
|
|
|
|
// Generates a SELECT sql statement, if Prepared has been called with true then the parameters will not be interpolated.
|
|
// See examples.
|
|
//
|
|
// Errors:
|
|
// * There is an error generating the SQL
|
|
func (sd *SelectDataset) ToSQL() (sql string, params []interface{}, err error) {
|
|
return sd.selectSQLBuilder().ToSQL()
|
|
}
|
|
|
|
// Generates the SELECT sql, and returns an Exec struct with the sql set to the SELECT statement
|
|
// db.From("test").Select("col").Executor()
|
|
//
|
|
// See Dataset#ToUpdateSQL for arguments
|
|
func (sd *SelectDataset) Executor() exec.QueryExecutor {
|
|
return sd.queryFactory.FromSQLBuilder(sd.selectSQLBuilder())
|
|
}
|
|
|
|
// Appends this Dataset's SELECT statement to the SQLBuilder
|
|
// This is used internally for sub-selects by the dialect
|
|
func (sd *SelectDataset) AppendSQL(b sb.SQLBuilder) {
|
|
if sd.err != nil {
|
|
b.SetError(sd.err)
|
|
return
|
|
}
|
|
sd.dialect.ToSelectSQL(b, sd.GetClauses())
|
|
}
|
|
|
|
func (sd *SelectDataset) ReturnsColumns() bool {
|
|
return true
|
|
}
|
|
|
|
// Generates the SELECT sql for this dataset and uses Exec#ScanStructs to scan the results into a slice of structs.
|
|
//
|
|
// ScanStructs will only select the columns that can be scanned in to the struct unless you have explicitly selected
|
|
// certain columns. See examples.
|
|
//
|
|
// i: A pointer to a slice of structs
|
|
func (sd *SelectDataset) ScanStructs(i interface{}) error {
|
|
return sd.ScanStructsContext(context.Background(), i)
|
|
}
|
|
|
|
// Generates the SELECT sql for this dataset and uses Exec#ScanStructsContext to scan the results into a slice of
|
|
// structs.
|
|
//
|
|
// ScanStructsContext will only select the columns that can be scanned in to the struct unless you have explicitly
|
|
// selected certain columns. See examples.
|
|
//
|
|
// i: A pointer to a slice of structs
|
|
func (sd *SelectDataset) ScanStructsContext(ctx context.Context, i interface{}) error {
|
|
if sd.queryFactory == nil {
|
|
return ErrQueryFactoryNotFoundError
|
|
}
|
|
ds := sd
|
|
if sd.GetClauses().IsDefaultSelect() {
|
|
ds = sd.Select(i)
|
|
}
|
|
return ds.Executor().ScanStructsContext(ctx, i)
|
|
}
|
|
|
|
// Generates the SELECT sql for this dataset and uses Exec#ScanStruct to scan the result into a slice of structs
|
|
//
|
|
// ScanStruct will only select the columns that can be scanned in to the struct unless you have explicitly selected
|
|
// certain columns. See examples.
|
|
//
|
|
// i: A pointer to a structs
|
|
func (sd *SelectDataset) ScanStruct(i interface{}) (bool, error) {
|
|
return sd.ScanStructContext(context.Background(), i)
|
|
}
|
|
|
|
// Generates the SELECT sql for this dataset and uses Exec#ScanStructContext to scan the result into a slice of structs
|
|
//
|
|
// ScanStructContext will only select the columns that can be scanned in to the struct unless you have explicitly
|
|
// selected certain columns. See examples.
|
|
//
|
|
// i: A pointer to a structs
|
|
func (sd *SelectDataset) ScanStructContext(ctx context.Context, i interface{}) (bool, error) {
|
|
if sd.queryFactory == nil {
|
|
return false, ErrQueryFactoryNotFoundError
|
|
}
|
|
ds := sd
|
|
if sd.GetClauses().IsDefaultSelect() {
|
|
ds = sd.Select(i)
|
|
}
|
|
return ds.Limit(1).Executor().ScanStructContext(ctx, i)
|
|
}
|
|
|
|
// Generates the SELECT sql for this dataset and uses Exec#ScanVals to scan the results into a slice of primitive values
|
|
//
|
|
// i: A pointer to a slice of primitive values
|
|
func (sd *SelectDataset) ScanVals(i interface{}) error {
|
|
return sd.ScanValsContext(context.Background(), i)
|
|
}
|
|
|
|
// Generates the SELECT sql for this dataset and uses Exec#ScanValsContext to scan the results into a slice of primitive
|
|
// values
|
|
//
|
|
// i: A pointer to a slice of primitive values
|
|
func (sd *SelectDataset) ScanValsContext(ctx context.Context, i interface{}) error {
|
|
if sd.queryFactory == nil {
|
|
return ErrQueryFactoryNotFoundError
|
|
}
|
|
return sd.Executor().ScanValsContext(ctx, i)
|
|
}
|
|
|
|
// Generates the SELECT sql for this dataset and uses Exec#ScanVal to scan the result into a primitive value
|
|
//
|
|
// i: A pointer to a primitive value
|
|
func (sd *SelectDataset) ScanVal(i interface{}) (bool, error) {
|
|
return sd.ScanValContext(context.Background(), i)
|
|
}
|
|
|
|
// Generates the SELECT sql for this dataset and uses Exec#ScanValContext to scan the result into a primitive value
|
|
//
|
|
// i: A pointer to a primitive value
|
|
func (sd *SelectDataset) ScanValContext(ctx context.Context, i interface{}) (bool, error) {
|
|
if sd.queryFactory == nil {
|
|
return false, ErrQueryFactoryNotFoundError
|
|
}
|
|
return sd.Limit(1).Executor().ScanValContext(ctx, i)
|
|
}
|
|
|
|
// Generates the SELECT COUNT(*) sql for this dataset and uses Exec#ScanVal to scan the result into an int64.
|
|
func (sd *SelectDataset) Count() (int64, error) {
|
|
return sd.CountContext(context.Background())
|
|
}
|
|
|
|
// Generates the SELECT COUNT(*) sql for this dataset and uses Exec#ScanValContext to scan the result into an int64.
|
|
func (sd *SelectDataset) CountContext(ctx context.Context) (int64, error) {
|
|
var count int64
|
|
_, err := sd.Select(COUNT(Star()).As("count")).ScanValContext(ctx, &count)
|
|
return count, err
|
|
}
|
|
|
|
// Generates the SELECT sql only selecting the passed in column and uses Exec#ScanVals to scan the result into a slice
|
|
// of primitive values.
|
|
//
|
|
// i: A slice of primitive values
|
|
//
|
|
// col: The column to select when generative the SQL
|
|
func (sd *SelectDataset) Pluck(i interface{}, col string) error {
|
|
return sd.PluckContext(context.Background(), i, col)
|
|
}
|
|
|
|
// Generates the SELECT sql only selecting the passed in column and uses Exec#ScanValsContext to scan the result into a
|
|
// slice of primitive values.
|
|
//
|
|
// i: A slice of primitive values
|
|
//
|
|
// col: The column to select when generative the SQL
|
|
func (sd *SelectDataset) PluckContext(ctx context.Context, i interface{}, col string) error {
|
|
return sd.Select(col).ScanValsContext(ctx, i)
|
|
}
|
|
|
|
func (sd *SelectDataset) selectSQLBuilder() sb.SQLBuilder {
|
|
buf := sb.NewSQLBuilder(sd.isPrepared.Bool())
|
|
if sd.err != nil {
|
|
return buf.SetError(sd.err)
|
|
}
|
|
sd.dialect.ToSelectSQL(buf, sd.GetClauses())
|
|
return buf
|
|
}
|