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