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) }