github.com/paweljw/pop@v4.13.1+incompatible/query.go (about)

     1  package pop
     2  
     3  import (
     4  	"fmt"
     5  	"strings"
     6  
     7  	"github.com/gobuffalo/pop/logging"
     8  )
     9  
    10  // Query is the main value that is used to build up a query
    11  // to be executed against the `Connection`.
    12  type Query struct {
    13  	RawSQL                  *clause
    14  	limitResults            int
    15  	addColumns              []string
    16  	eager                   bool
    17  	eagerFields             []string
    18  	whereClauses            clauses
    19  	orderClauses            clauses
    20  	fromClauses             fromClauses
    21  	belongsToThroughClauses belongsToThroughClauses
    22  	joinClauses             joinClauses
    23  	groupClauses            groupClauses
    24  	havingClauses           havingClauses
    25  	Paginator               *Paginator
    26  	Connection              *Connection
    27  }
    28  
    29  // Clone will fill targetQ query with the connection used in q, if
    30  // targetQ is not empty, Clone will override all the fields.
    31  func (q *Query) Clone(targetQ *Query) {
    32  	rawSQL := *q.RawSQL
    33  	targetQ.RawSQL = &rawSQL
    34  
    35  	targetQ.limitResults = q.limitResults
    36  	targetQ.whereClauses = q.whereClauses
    37  	targetQ.orderClauses = q.orderClauses
    38  	targetQ.fromClauses = q.fromClauses
    39  	targetQ.belongsToThroughClauses = q.belongsToThroughClauses
    40  	targetQ.joinClauses = q.joinClauses
    41  	targetQ.groupClauses = q.groupClauses
    42  	targetQ.havingClauses = q.havingClauses
    43  	targetQ.addColumns = q.addColumns
    44  
    45  	if q.Paginator != nil {
    46  		paginator := *q.Paginator
    47  		targetQ.Paginator = &paginator
    48  	}
    49  
    50  	if q.Connection != nil {
    51  		connection := *q.Connection
    52  		targetQ.Connection = &connection
    53  	}
    54  }
    55  
    56  // RawQuery will override the query building feature of Pop and will use
    57  // whatever query you want to execute against the `Connection`. You can continue
    58  // to use the `?` argument syntax.
    59  //
    60  //	c.RawQuery("select * from foo where id = ?", 1)
    61  func (c *Connection) RawQuery(stmt string, args ...interface{}) *Query {
    62  	return Q(c).RawQuery(stmt, args...)
    63  }
    64  
    65  // RawQuery will override the query building feature of Pop and will use
    66  // whatever query you want to execute against the `Connection`. You can continue
    67  // to use the `?` argument syntax.
    68  //
    69  //	q.RawQuery("select * from foo where id = ?", 1)
    70  func (q *Query) RawQuery(stmt string, args ...interface{}) *Query {
    71  	q.RawSQL = &clause{stmt, args}
    72  	return q
    73  }
    74  
    75  // Eager will enable associations loading of the model.
    76  // by defaults loads all the associations on the model,
    77  // but can take a variadic list of associations to load.
    78  //
    79  // 	c.Eager().Find(model, 1) // will load all associations for model.
    80  // 	c.Eager("Books").Find(model, 1) // will load only Book association for model.
    81  //
    82  // Eager also enable nested models creation:
    83  //
    84  //	model := Parent{Child: Child{}, Parent: &Parent{}}
    85  //	c.Eager().Create(&model) // will create all associations for model.
    86  //	c.Eager("Child").Create(&model) // will only create the Child association for model.
    87  func (c *Connection) Eager(fields ...string) *Connection {
    88  	con := c.copy()
    89  	con.eager = true
    90  	con.eagerFields = append(c.eagerFields, fields...)
    91  	return con
    92  }
    93  
    94  // Eager will enable load associations of the model.
    95  // by defaults loads all the associations on the model,
    96  // but can take a variadic list of associations to load.
    97  //
    98  // 	q.Eager().Find(model, 1) // will load all associations for model.
    99  // 	q.Eager("Books").Find(model, 1) // will load only Book association for model.
   100  func (q *Query) Eager(fields ...string) *Query {
   101  	q.eager = true
   102  	q.eagerFields = append(q.eagerFields, fields...)
   103  	return q
   104  }
   105  
   106  // disableEager disables eager mode for current query and Connection.
   107  func (q *Query) disableEager() {
   108  	q.Connection.eager, q.eager = false, false
   109  	q.Connection.eagerFields, q.eagerFields = []string{}, []string{}
   110  }
   111  
   112  // Where will append a where clause to the query. You may use `?` in place of
   113  // arguments.
   114  //
   115  // 	c.Where("id = ?", 1)
   116  // 	q.Where("id in (?)", 1, 2, 3)
   117  func (c *Connection) Where(stmt string, args ...interface{}) *Query {
   118  	q := Q(c)
   119  	return q.Where(stmt, args...)
   120  }
   121  
   122  // Where will append a where clause to the query. You may use `?` in place of
   123  // arguments.
   124  //
   125  // 	q.Where("id = ?", 1)
   126  // 	q.Where("id in (?)", 1, 2, 3)
   127  func (q *Query) Where(stmt string, args ...interface{}) *Query {
   128  	if q.RawSQL.Fragment != "" {
   129  		log(logging.Warn, "Query is setup to use raw SQL")
   130  		return q
   131  	}
   132  	if inRegex.MatchString(stmt) {
   133  		var inq []string
   134  		for i := 0; i < len(args); i++ {
   135  			inq = append(inq, "?")
   136  		}
   137  		qs := fmt.Sprintf("(%s)", strings.Join(inq, ","))
   138  		stmt = strings.Replace(stmt, "(?)", qs, 1)
   139  	}
   140  	q.whereClauses = append(q.whereClauses, clause{stmt, args})
   141  	return q
   142  }
   143  
   144  // Order will append an order clause to the query.
   145  //
   146  // 	c.Order("name desc")
   147  func (c *Connection) Order(stmt string) *Query {
   148  	return Q(c).Order(stmt)
   149  }
   150  
   151  // Order will append an order clause to the query.
   152  //
   153  // 	q.Order("name desc")
   154  func (q *Query) Order(stmt string) *Query {
   155  	if q.RawSQL.Fragment != "" {
   156  		log(logging.Warn, "Query is setup to use raw SQL")
   157  		return q
   158  	}
   159  	q.orderClauses = append(q.orderClauses, clause{stmt, []interface{}{}})
   160  	return q
   161  }
   162  
   163  // Limit will create a query and add a limit clause to it.
   164  //
   165  // 	c.Limit(10)
   166  func (c *Connection) Limit(limit int) *Query {
   167  	return Q(c).Limit(limit)
   168  }
   169  
   170  // Limit will add a limit clause to the query.
   171  //
   172  // 	q.Limit(10)
   173  func (q *Query) Limit(limit int) *Query {
   174  	q.limitResults = limit
   175  	return q
   176  }
   177  
   178  // Q will create a new "empty" query from the current connection.
   179  func Q(c *Connection) *Query {
   180  	return &Query{
   181  		RawSQL:      &clause{},
   182  		Connection:  c,
   183  		eager:       c.eager,
   184  		eagerFields: c.eagerFields,
   185  	}
   186  }
   187  
   188  // ToSQL will generate SQL and the appropriate arguments for that SQL
   189  // from the `Model` passed in.
   190  func (q Query) ToSQL(model *Model, addColumns ...string) (string, []interface{}) {
   191  	sb := q.toSQLBuilder(model, addColumns...)
   192  	return sb.String(), sb.Args()
   193  }
   194  
   195  // ToSQLBuilder returns a new `SQLBuilder` that can be used to generate SQL,
   196  // get arguments, and more.
   197  func (q Query) toSQLBuilder(model *Model, addColumns ...string) *sqlBuilder {
   198  	if len(q.addColumns) != 0 {
   199  		addColumns = q.addColumns
   200  	}
   201  	return newSQLBuilder(q, model, addColumns...)
   202  }