github.com/royge/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 }