github.com/mitranim/sqlb@v0.7.2/sqlb_ord.go (about) 1 package sqlb 2 3 import ( 4 "encoding/json" 5 ) 6 7 /* 8 Short for "orderings". Sequence of arbitrary expressions used for an SQL 9 "order by" clause. Nil elements are treated as non-existent. If there are no 10 non-nil elements, the resulting expression is empty. Otherwise, the resulting 11 expression is "order by" followed by comma-separated sub-expressions. You can 12 construct `Ords` manually, or parse client inputs via `OrdsParser`. See the 13 examples. 14 */ 15 type Ords []Expr 16 17 /* 18 Allows types that embed `Ords` to behave like a slice in JSON encoding, avoiding 19 some edge issues. For example, this allows an empty `ParserOrds` to be encoded 20 as JSON `null` rather than a struct, allowing types that include it as a field 21 to be used for encoding JSON, not just decoding it. However, this doesn't make 22 ords encoding/decoding actually reversible. Decoding "consults" a struct type 23 to convert JSON field names to DB column names. Ideally, JSON marshaling would 24 perform the same process in reverse, which is not currently implemented. 25 */ 26 func (self Ords) MarshalJSON() ([]byte, error) { 27 return json.Marshal([]Expr(self)) 28 } 29 30 /* 31 Returns an `OrdsParser` that can decode arbitrary JSON or a string slice into 32 the given `*Ords` pointer. Initializes the parser to the provided type, using 33 `typ` only as a type carrier. 34 */ 35 func (self *Ords) OrdsParser(typ any) (out OrdsParser) { 36 out.OrType(typ) 37 out.Ords = self 38 return 39 } 40 41 // Implement the `Expr` interface, making this a sub-expression. 42 func (self Ords) AppendExpr(text []byte, args []any) ([]byte, []any) { 43 bui := Bui{text, args} 44 var found bool 45 46 for _, val := range self { 47 if val == nil { 48 continue 49 } 50 51 if !found { 52 found = true 53 bui.Str(`order by`) 54 } else { 55 bui.Str(`,`) 56 } 57 58 bui.Expr(val) 59 } 60 61 return bui.Get() 62 } 63 64 // Implement the `AppenderTo` interface, sometimes allowing more efficient text 65 // encoding. 66 func (self Ords) AppendTo(text []byte) []byte { return exprAppend(&self, text) } 67 68 // Implement the `fmt.Stringer` interface for debug purposes. 69 func (self Ords) String() string { return exprString(&self) } 70 71 /* 72 Returns an expression for the Postgres window function `row_number`: 73 74 Ords{}.RowNumber() 75 -> `0` 76 77 Ords{OrdAsc(`col`)}.RowNumber() 78 -> `row_number() over (order by "col" asc)` 79 80 As shown above, empty `Ords` generates `0`. The Postgres query planner 81 should optimize away any ordering by this constant column. 82 */ 83 func (self Ords) RowNumberOver() RowNumberOver { 84 if self.IsEmpty() { 85 return RowNumberOver{} 86 } 87 return RowNumberOver{self} 88 } 89 90 // Returns true if there are no non-nil items. 91 func (self Ords) IsEmpty() bool { return self.Len() == 0 } 92 93 // Returns the amount of non-nil items. 94 func (self Ords) Len() (count int) { 95 for _, val := range self { 96 if val != nil { 97 count++ 98 } 99 } 100 return 101 } 102 103 /* 104 Empties the receiver. If the receiver was non-nil, its length is reduced to 0 105 while keeping any capacity, and it remains non-nil. 106 */ 107 func (self *Ords) Zero() { 108 if self != nil && *self != nil { 109 *self = (*self)[:0] 110 } 111 } 112 113 // Convenience method for appending. 114 func (self *Ords) Add(vals ...Expr) { 115 for _, val := range vals { 116 if val != nil { 117 *self = append(*self, val) 118 } 119 } 120 } 121 122 // If empty, sets the given vals. Otherwise it's a nop. 123 func (self *Ords) Or(vals ...Expr) { 124 if self.IsEmpty() { 125 self.Zero() 126 self.Add(vals...) 127 } 128 } 129 130 // Resizes to ensure that space capacity is `<= size`. 131 func (self *Ords) Grow(size int) { 132 *self = growExprs(*self, size) 133 } 134 135 // Sometimes handy for types that embed `Ords`. 136 func (self *Ords) OrdsPtr() *Ords { return self } 137 138 /* 139 Structured representation of an arbitrary SQL ordering expression. This is not 140 the entire "order by" clause (see `Ords`), but rather just one element in that 141 clause. This is the general-case representation, but because most ordering 142 expressions use only column names and direction, a more specialized 143 representation is preferred: `Ord`. This is provided just-in-case. 144 */ 145 type Ordering struct { 146 Expr Expr 147 Dir Dir 148 Nulls Nulls 149 Using Expr 150 } 151 152 // Implement the `Expr` interface, making this a sub-expression. 153 func (self Ordering) AppendExpr(text []byte, args []any) ([]byte, []any) { 154 if self.Expr == nil { 155 return text, args 156 } 157 158 text, args = self.Expr.AppendExpr(text, args) 159 text = self.Dir.AppendTo(text) 160 text = self.Nulls.AppendTo(text) 161 162 if self.Using != nil { 163 text = appendMaybeSpaced(text, `using `) 164 text, args = self.Using.AppendExpr(text, args) 165 } 166 167 return text, args 168 } 169 170 // Implement the `AppenderTo` interface, sometimes allowing more efficient text 171 // encoding. 172 func (self Ordering) AppendTo(text []byte) []byte { return exprAppend(&self, text) } 173 174 // Implement the `fmt.Stringer` interface for debug purposes. 175 func (self Ordering) String() string { return exprString(&self) } 176 177 /* 178 Structured representation of an arbitrary SQL ordering expression. This is not 179 the entire "order by" clause (see `Ords`), but rather just one element in that 180 clause. Also see `Ords`, `OrdsParser`, and the various provided examples. 181 */ 182 type Ord struct { 183 Path Path 184 Dir Dir 185 Nulls Nulls 186 } 187 188 // Implement the `Expr` interface, making this a sub-expression. 189 func (self Ord) AppendExpr(text []byte, args []any) ([]byte, []any) { 190 return self.AppendTo(text), args 191 } 192 193 // Implement the `AppenderTo` interface, sometimes allowing more efficient text 194 // encoding. 195 func (self Ord) AppendTo(text []byte) []byte { 196 if len(self.Path) > 0 { 197 text = self.Path.AppendTo(text) 198 text = self.Dir.AppendTo(text) 199 text = self.Nulls.AppendTo(text) 200 } 201 return text 202 } 203 204 // Implement the `fmt.Stringer` interface for debug purposes. 205 func (self Ord) String() string { return AppenderString(&self) } 206 207 // True if the path is empty. 208 func (self Ord) IsEmpty() bool { return len(self.Path) == 0 } 209 210 // Same as `Ord{Path: path, Dir: DirAsc}` but more syntactically convenient 211 // and uses less memory. 212 type OrdAsc []string 213 214 // Implement the `Expr` interface, making this a sub-expression. 215 func (self OrdAsc) AppendExpr(text []byte, args []any) ([]byte, []any) { 216 return Ord{Path: Path(self), Dir: DirAsc}.AppendExpr(text, args) 217 } 218 219 // Implement the `AppenderTo` interface, sometimes allowing more efficient text 220 // encoding. 221 func (self OrdAsc) AppendTo(text []byte) []byte { return exprAppend(&self, text) } 222 223 // Implement the `fmt.Stringer` interface for debug purposes. 224 func (self OrdAsc) String() string { return exprString(&self) } 225 226 // Same as `Ord{Path: path, Dir: DirDesc}` but more syntactically 227 // convenient and uses less memory. 228 type OrdDesc []string 229 230 // Implement the `Expr` interface, making this a sub-expression. 231 func (self OrdDesc) AppendExpr(text []byte, args []any) ([]byte, []any) { 232 return Ord{Path: Path(self), Dir: DirDesc}.AppendExpr(text, args) 233 } 234 235 // Implement the `AppenderTo` interface, sometimes allowing more efficient text 236 // encoding. 237 func (self OrdDesc) AppendTo(text []byte) []byte { return exprAppend(&self, text) } 238 239 // Implement the `fmt.Stringer` interface for debug purposes. 240 func (self OrdDesc) String() string { return exprString(&self) } 241 242 // Same as `Ord{Path: path, Nulls: NullsFirst}` but more syntactically 243 // convenient and uses less memory. 244 type OrdNullsFirst []string 245 246 // Implement the `Expr` interface, making this a sub-expression. 247 func (self OrdNullsFirst) AppendExpr(text []byte, args []any) ([]byte, []any) { 248 return Ord{Path: Path(self), Nulls: NullsFirst}.AppendExpr(text, args) 249 } 250 251 // Implement the `AppenderTo` interface, sometimes allowing more efficient text 252 // encoding. 253 func (self OrdNullsFirst) AppendTo(text []byte) []byte { return exprAppend(&self, text) } 254 255 // Implement the `fmt.Stringer` interface for debug purposes. 256 func (self OrdNullsFirst) String() string { return exprString(&self) } 257 258 // Same as `Ord{Path: path, Nulls: NullsLast}` but more syntactically 259 // convenient and uses less memory. 260 type OrdNullsLast []string 261 262 // Implement the `Expr` interface, making this a sub-expression. 263 func (self OrdNullsLast) AppendExpr(text []byte, args []any) ([]byte, []any) { 264 return Ord{Path: Path(self), Nulls: NullsLast}.AppendExpr(text, args) 265 } 266 267 // Implement the `AppenderTo` interface, sometimes allowing more efficient text 268 // encoding. 269 func (self OrdNullsLast) AppendTo(text []byte) []byte { return exprAppend(&self, text) } 270 271 // Implement the `fmt.Stringer` interface for debug purposes. 272 func (self OrdNullsLast) String() string { return exprString(&self) } 273 274 // Same as `Ord{Path: path, Dir: DirAsc, Nulls: NullsFirst}` but more 275 // syntactically convenient and uses less memory. 276 type OrdAscNullsFirst []string 277 278 // Implement the `Expr` interface, making this a sub-expression. 279 func (self OrdAscNullsFirst) AppendExpr(text []byte, args []any) ([]byte, []any) { 280 return Ord{Path: Path(self), Dir: DirAsc, Nulls: NullsFirst}.AppendExpr(text, args) 281 } 282 283 // Implement the `AppenderTo` interface, sometimes allowing more efficient text 284 // encoding. 285 func (self OrdAscNullsFirst) AppendTo(text []byte) []byte { return exprAppend(&self, text) } 286 287 // Implement the `fmt.Stringer` interface for debug purposes. 288 func (self OrdAscNullsFirst) String() string { return exprString(&self) } 289 290 // Same as `Ord{Path: path, Dir: DirAsc, Nulls: NullsLast}` but more 291 // syntactically convenient and uses less memory. 292 type OrdAscNullsLast []string 293 294 // Implement the `Expr` interface, making this a sub-expression. 295 func (self OrdAscNullsLast) AppendExpr(text []byte, args []any) ([]byte, []any) { 296 return Ord{Path: Path(self), Dir: DirAsc, Nulls: NullsLast}.AppendExpr(text, args) 297 } 298 299 // Implement the `AppenderTo` interface, sometimes allowing more efficient text 300 // encoding. 301 func (self OrdAscNullsLast) AppendTo(text []byte) []byte { return exprAppend(&self, text) } 302 303 // Implement the `fmt.Stringer` interface for debug purposes. 304 func (self OrdAscNullsLast) String() string { return exprString(&self) } 305 306 // Same as `Ord{Path: path, Dir: DirDesc, Nulls: NullsFirst}` but more 307 // syntactically convenient and uses less memory. 308 type OrdDescNullsFirst []string 309 310 // Implement the `Expr` interface, making this a sub-expression. 311 func (self OrdDescNullsFirst) AppendExpr(text []byte, args []any) ([]byte, []any) { 312 return Ord{Path: Path(self), Dir: DirDesc, Nulls: NullsFirst}.AppendExpr(text, args) 313 } 314 315 // Implement the `AppenderTo` interface, sometimes allowing more efficient text 316 // encoding. 317 func (self OrdDescNullsFirst) AppendTo(text []byte) []byte { return exprAppend(&self, text) } 318 319 // Implement the `fmt.Stringer` interface for debug purposes. 320 func (self OrdDescNullsFirst) String() string { return exprString(&self) } 321 322 // Same as `Ord{Path: path, Dir: DirDesc, Nulls: NullsLast}` but more 323 // syntactically convenient and uses less memory. 324 type OrdDescNullsLast []string 325 326 // Implement the `Expr` interface, making this a sub-expression. 327 func (self OrdDescNullsLast) AppendExpr(text []byte, args []any) ([]byte, []any) { 328 return Ord{Path: Path(self), Dir: DirDesc, Nulls: NullsLast}.AppendExpr(text, args) 329 } 330 331 // Implement the `AppenderTo` interface, sometimes allowing more efficient text 332 // encoding. 333 func (self OrdDescNullsLast) AppendTo(text []byte) []byte { return exprAppend(&self, text) } 334 335 // Implement the `fmt.Stringer` interface for debug purposes. 336 func (self OrdDescNullsLast) String() string { return exprString(&self) }