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