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 }