github.com/mitranim/sqlb@v0.7.2/sqlb_bui.go (about)

     1  package sqlb
     2  
     3  /*
     4  Prealloc tool. Makes a `Bui` with the specified capacity of the text and args
     5  buffers.
     6  */
     7  func MakeBui(textCap, argsCap int) Bui {
     8  	return Bui{
     9  		make([]byte, 0, textCap),
    10  		make([]any, 0, argsCap),
    11  	}
    12  }
    13  
    14  /*
    15  Short for "builder". Tiny shortcut for building SQL expressions. Significantly
    16  simplifies the code and avoids various common mistakes. Used internally by most
    17  `Expr` implementations in this package. Careful use of `Bui` incurs very litte
    18  overhead compared to writing the corresponding code inline. The design may
    19  allow future Go versions to optimize it away completely.
    20  */
    21  type Bui struct {
    22  	Text []byte
    23  	Args []any
    24  }
    25  
    26  // Returns text and args as-is. Useful shortcut for passing them to
    27  // `AppendExpr`.
    28  func (self Bui) Get() ([]byte, []any) {
    29  	return self.Text, self.Args
    30  }
    31  
    32  /*
    33  Replaces text and args with the inputs. The following idiom is equivalent to
    34  `bui.Expr` but more efficient if the expression type is concrete, avoiding an
    35  interface-induced allocation:
    36  
    37  	bui.Set(SomeExpr{}.AppendExpr(bui.Get()))
    38  */
    39  func (self *Bui) Set(text []byte, args []any) {
    40  	self.Text = text
    41  	self.Args = args
    42  }
    43  
    44  // Shortcut for `self.String(), self.Args`. Go database drivers tend to require
    45  // `string, []any` as inputs for queries and statements.
    46  func (self Bui) Reify() (string, []any) {
    47  	return self.String(), self.Args
    48  }
    49  
    50  // Returns inner text as a string, performing a free cast.
    51  func (self Bui) String() string {
    52  	return bytesToMutableString(self.Text)
    53  }
    54  
    55  // Increases the capacity (not length) of the text and args buffers by the
    56  // specified amounts. If there's already enough capacity, avoids allocation.
    57  func (self *Bui) Grow(textLen, argsLen int) {
    58  	self.Text = growBytes(self.Text, textLen)
    59  	self.Args = growInterfaces(self.Args, argsLen)
    60  }
    61  
    62  // Adds a space if the preceding text doesn't already end with a terminator.
    63  func (self *Bui) Space() {
    64  	self.Text = maybeAppendSpace(self.Text)
    65  }
    66  
    67  // Appends the provided string, delimiting it from the previous text with a
    68  // space if necessary.
    69  func (self *Bui) Str(val string) {
    70  	self.Text = appendMaybeSpaced(self.Text, val)
    71  }
    72  
    73  /*
    74  Appends an expression, delimited from the preceding text by a space, if
    75  necessary. Nil input is a nop: nothing will be appended.
    76  
    77  Should be used only if you already have an `Expr` value. If you have a concrete
    78  value that implements the interface, call `bui.Set(val.AppendExpr(bui.Get())`
    79  instead, to avoid a heap allocation and a minor slowdown.
    80  */
    81  func (self *Bui) Expr(val Expr) {
    82  	if val != nil {
    83  		self.Space()
    84  		self.Set(val.AppendExpr(self.Get()))
    85  	}
    86  }
    87  
    88  /*
    89  Appends a sub-expression wrapped in parens. Nil input is a nop: nothing will be
    90  appended.
    91  
    92  Performance note: if you have a concrete value rather than an `Expr`, calling
    93  this method will allocate, so you may want to avoid it. If you already have an
    94  `Expr`, calling this is fine.
    95  */
    96  func (self *Bui) SubExpr(val Expr) {
    97  	if val != nil {
    98  		self.Str(`(`)
    99  		self.Expr(val)
   100  		self.Str(`)`)
   101  	}
   102  }
   103  
   104  // Appends each expr by calling `(*Bui).Expr`. They will be space-separated as
   105  // necessary.
   106  func (self *Bui) Exprs(vals ...Expr) {
   107  	for _, val := range vals {
   108  		self.Expr(val)
   109  	}
   110  }
   111  
   112  // Same as `(*Bui).Exprs` but catches panics. Since many functions in this
   113  // package use panics, this should be used for final reification by apps that
   114  // insist on errors-as-values.
   115  func (self *Bui) CatchExprs(vals ...Expr) (err error) {
   116  	defer rec(&err)
   117  	self.Exprs(vals...)
   118  	return
   119  }
   120  
   121  /*
   122  Appends an ordinal parameter such as "$1", space-separated from previous text if
   123  necessary. Requires caution: does not verify the existence of the corresponding
   124  argument.
   125  */
   126  func (self *Bui) OrphanParam(val OrdinalParam) {
   127  	self.Space()
   128  	self.Text = val.AppendTo(self.Text)
   129  }
   130  
   131  /*
   132  Appends an arg to the inner slice of args, returning the corresponding ordinal
   133  parameter that should be appended to the text. Requires caution: does not
   134  append the corresponding ordinal parameter.
   135  */
   136  func (self *Bui) OrphanArg(val any) OrdinalParam {
   137  	self.Args = append(self.Args, val)
   138  	return OrdinalParam(len(self.Args))
   139  }
   140  
   141  /*
   142  Appends an argument to `.Args` and a corresponding ordinal parameter to
   143  `.Text`.
   144  */
   145  func (self *Bui) Arg(val any) { self.OrphanParam(self.OrphanArg(val)) }
   146  
   147  /*
   148  Appends an arbitrary value. If the value implements `Expr`, this calls
   149  `(*Bui).Expr`, which may append to the text and args in arbitrary ways.
   150  Otherwise, appends an argument to the inner slice of args, and the
   151  corresponding ordinal parameter such as "$1"/"$2"/.../"$N" to the text.
   152  */
   153  func (self *Bui) Any(val any) {
   154  	impl, _ := val.(Expr)
   155  	if impl != nil {
   156  		self.Expr(impl)
   157  		return
   158  	}
   159  
   160  	/**
   161  	TODO consider the following:
   162  
   163  		if val == nil {
   164  			self.Str(`null`)
   165  		} else {
   166  			self.Arg(val)
   167  		}
   168  
   169  	Makes some assumptions, and might be the wrong place for such a special case.
   170  	*/
   171  
   172  	self.Arg(val)
   173  }
   174  
   175  /*
   176  Appends an arbitrary value or sub-expression. Like `(*Bui).Any`, but if the
   177  value implements `Expr`, this uses `(*Bui).SubExpr` in order to parenthesize
   178  the sub-expression.
   179  */
   180  func (self *Bui) SubAny(val any) {
   181  	impl, _ := val.(Expr)
   182  	if impl != nil {
   183  		self.SubExpr(impl)
   184  		return
   185  	}
   186  	self.Arg(val)
   187  }