Skip to content

Commit c94216a

Browse files
committed
Add support for blob expressions.
1 parent 26e478d commit c94216a

37 files changed

+1296
-81
lines changed

generator/template/sql_builder_template.go

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -180,11 +180,15 @@ func getSqlBuilderColumnType(columnMetaData metadata.Column) string {
180180
return "Timez"
181181
case "interval":
182182
return "Interval"
183-
case "user-defined", "enum", "text", "character", "character varying", "bytea", "uuid",
183+
case "user-defined", "enum", "text", "character", "character varying", "uuid",
184184
"tsvector", "bit", "bit varying", "money", "json", "jsonb", "xml", "point", "line", "ARRAY",
185-
"char", "varchar", "nvarchar", "binary", "varbinary", "bpchar", "varbit",
186-
"tinyblob", "blob", "mediumblob", "longblob", "tinytext", "mediumtext", "longtext": // MySQL
185+
"char", "varchar", "nvarchar", "bpchar", "varbit",
186+
"tinytext", "mediumtext", "longtext": // MySQL
187187
return "String"
188+
case "bytea": // postgres
189+
return "Bytea"
190+
case "binary", "varbinary", "tinyblob", "mediumblob", "longblob", "blob": // mysql and sqlite
191+
return "Blob"
188192
case "real", "numeric", "decimal", "double precision", "float", "float4", "float8",
189193
"double": // MySQL
190194
return "Float"

internal/jet/blob_expression.go

Lines changed: 103 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,103 @@
1+
package jet
2+
3+
// BlobExpression interface
4+
type BlobExpression interface {
5+
Expression
6+
7+
isStringOrBlob()
8+
9+
EQ(rhs BlobExpression) BoolExpression
10+
NOT_EQ(rhs BlobExpression) BoolExpression
11+
IS_DISTINCT_FROM(rhs BlobExpression) BoolExpression
12+
IS_NOT_DISTINCT_FROM(rhs BlobExpression) BoolExpression
13+
14+
LT(rhs BlobExpression) BoolExpression
15+
LT_EQ(rhs BlobExpression) BoolExpression
16+
GT(rhs BlobExpression) BoolExpression
17+
GT_EQ(rhs BlobExpression) BoolExpression
18+
BETWEEN(min, max BlobExpression) BoolExpression
19+
NOT_BETWEEN(min, max BlobExpression) BoolExpression
20+
21+
CONCAT(rhs BlobExpression) BlobExpression
22+
23+
LIKE(pattern BlobExpression) BoolExpression
24+
NOT_LIKE(pattern BlobExpression) BoolExpression
25+
}
26+
27+
type blobInterfaceImpl struct {
28+
parent BlobExpression
29+
}
30+
31+
func (s *blobInterfaceImpl) isStringOrBlob() {}
32+
33+
func (s *blobInterfaceImpl) EQ(rhs BlobExpression) BoolExpression {
34+
return Eq(s.parent, rhs)
35+
}
36+
37+
func (s *blobInterfaceImpl) NOT_EQ(rhs BlobExpression) BoolExpression {
38+
return NotEq(s.parent, rhs)
39+
}
40+
41+
func (s *blobInterfaceImpl) IS_DISTINCT_FROM(rhs BlobExpression) BoolExpression {
42+
return IsDistinctFrom(s.parent, rhs)
43+
}
44+
45+
func (s *blobInterfaceImpl) IS_NOT_DISTINCT_FROM(rhs BlobExpression) BoolExpression {
46+
return IsNotDistinctFrom(s.parent, rhs)
47+
}
48+
49+
func (s *blobInterfaceImpl) GT(rhs BlobExpression) BoolExpression {
50+
return Gt(s.parent, rhs)
51+
}
52+
53+
func (s *blobInterfaceImpl) GT_EQ(rhs BlobExpression) BoolExpression {
54+
return GtEq(s.parent, rhs)
55+
}
56+
57+
func (s *blobInterfaceImpl) LT(rhs BlobExpression) BoolExpression {
58+
return Lt(s.parent, rhs)
59+
}
60+
61+
func (s *blobInterfaceImpl) LT_EQ(rhs BlobExpression) BoolExpression {
62+
return LtEq(s.parent, rhs)
63+
}
64+
65+
func (s *blobInterfaceImpl) BETWEEN(min, max BlobExpression) BoolExpression {
66+
return NewBetweenOperatorExpression(s.parent, min, max, false)
67+
}
68+
69+
func (s *blobInterfaceImpl) NOT_BETWEEN(min, max BlobExpression) BoolExpression {
70+
return NewBetweenOperatorExpression(s.parent, min, max, true)
71+
}
72+
73+
func (s *blobInterfaceImpl) CONCAT(rhs BlobExpression) BlobExpression {
74+
return BlobExp(newBinaryStringOperatorExpression(s.parent, rhs, StringConcatOperator))
75+
}
76+
77+
func (s *blobInterfaceImpl) LIKE(pattern BlobExpression) BoolExpression {
78+
return newBinaryBoolOperatorExpression(s.parent, pattern, "LIKE")
79+
}
80+
81+
func (s *blobInterfaceImpl) NOT_LIKE(pattern BlobExpression) BoolExpression {
82+
return newBinaryBoolOperatorExpression(s.parent, pattern, "NOT LIKE")
83+
}
84+
85+
//---------------------------------------------------//
86+
87+
type blobExpressionWrapper struct {
88+
blobInterfaceImpl
89+
Expression
90+
}
91+
92+
func newBlobExpressionWrap(expression Expression) BlobExpression {
93+
blobExpressionWrap := blobExpressionWrapper{Expression: expression}
94+
blobExpressionWrap.blobInterfaceImpl.parent = &blobExpressionWrap
95+
return &blobExpressionWrap
96+
}
97+
98+
// BlobExp is blob expression wrapper around arbitrary expression.
99+
// Allows go compiler to see any expression as blob expression.
100+
// Does not add sql cast to generated sql builder output.
101+
func BlobExp(expression Expression) BlobExpression {
102+
return newBlobExpressionWrap(expression)
103+
}

internal/jet/column_types.go

Lines changed: 42 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -122,7 +122,7 @@ func IntegerColumn(name string) ColumnInteger {
122122
//------------------------------------------------------//
123123

124124
// ColumnString is interface for SQL text, character, character varying
125-
// bytea, uuid columns and enums types.
125+
// uuid columns and enums types.
126126
type ColumnString interface {
127127
StringExpression
128128
Column
@@ -163,6 +163,47 @@ func StringColumn(name string) ColumnString {
163163

164164
//------------------------------------------------------//
165165

166+
// ColumnBlob is interface for binary data types (bytea, binary, blob, etc...)
167+
type ColumnBlob interface {
168+
BlobExpression
169+
Column
170+
171+
From(subQuery SelectTable) ColumnBlob
172+
SET(blob BlobExpression) ColumnAssigment
173+
}
174+
175+
type blobColumnImpl struct {
176+
blobInterfaceImpl
177+
178+
ColumnExpressionImpl
179+
}
180+
181+
func (i *blobColumnImpl) From(subQuery SelectTable) ColumnBlob {
182+
newBlobColumn := BlobColumn(i.name)
183+
newBlobColumn.setTableName(i.tableName)
184+
newBlobColumn.setSubQuery(subQuery)
185+
186+
return newBlobColumn
187+
}
188+
189+
func (i *blobColumnImpl) SET(blobExp BlobExpression) ColumnAssigment {
190+
return columnAssigmentImpl{
191+
column: i,
192+
expression: blobExp,
193+
}
194+
}
195+
196+
// BlobColumn creates named blob column.
197+
func BlobColumn(name string) ColumnBlob {
198+
blobColumn := &blobColumnImpl{}
199+
blobColumn.blobInterfaceImpl.parent = blobColumn
200+
blobColumn.ColumnExpressionImpl = NewColumnImpl(name, "", blobColumn)
201+
202+
return blobColumn
203+
}
204+
205+
//------------------------------------------------------//
206+
166207
// ColumnTime is interface for SQL time column.
167208
type ColumnTime interface {
168209
TimeExpression

internal/jet/dialect.go

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
package jet
22

3-
import "strings"
3+
import (
4+
"strings"
5+
)
46

57
// Dialect interface
68
type Dialect interface {
@@ -11,6 +13,7 @@ type Dialect interface {
1113
AliasQuoteChar() byte
1214
IdentifierQuoteChar() byte
1315
ArgumentPlaceholder() QueryPlaceholderFunc
16+
ArgumentToString(value any) (string, bool)
1417
IsReservedWord(name string) bool
1518
SerializeOrderBy() func(expression Expression, ascending, nullsFirst *bool) SerializerFunc
1619
ValuesDefaultColumnName(index int) string
@@ -34,6 +37,7 @@ type DialectParams struct {
3437
AliasQuoteChar byte
3538
IdentifierQuoteChar byte
3639
ArgumentPlaceholder QueryPlaceholderFunc
40+
ArgumentToString func(value any) (string, bool)
3741
ReservedWords []string
3842
SerializeOrderBy func(expression Expression, ascending, nullsFirst *bool) SerializerFunc
3943
ValuesDefaultColumnName func(index int) string
@@ -49,6 +53,7 @@ func NewDialect(params DialectParams) Dialect {
4953
aliasQuoteChar: params.AliasQuoteChar,
5054
identifierQuoteChar: params.IdentifierQuoteChar,
5155
argumentPlaceholder: params.ArgumentPlaceholder,
56+
argumentToString: params.ArgumentToString,
5257
reservedWords: arrayOfStringsToMapOfStrings(params.ReservedWords),
5358
serializeOrderBy: params.SerializeOrderBy,
5459
valuesDefaultColumnName: params.ValuesDefaultColumnName,
@@ -63,6 +68,7 @@ type dialectImpl struct {
6368
aliasQuoteChar byte
6469
identifierQuoteChar byte
6570
argumentPlaceholder QueryPlaceholderFunc
71+
argumentToString func(value any) (string, bool)
6672
reservedWords map[string]bool
6773
serializeOrderBy func(expression Expression, ascending, nullsFirst *bool) SerializerFunc
6874
valuesDefaultColumnName func(index int) string
@@ -102,6 +108,10 @@ func (d *dialectImpl) ArgumentPlaceholder() QueryPlaceholderFunc {
102108
return d.argumentPlaceholder
103109
}
104110

111+
func (d *dialectImpl) ArgumentToString(value any) (string, bool) {
112+
return d.argumentToString(value)
113+
}
114+
105115
func (d *dialectImpl) IsReservedWord(name string) bool {
106116
_, isReservedWord := d.reservedWords[strings.ToLower(name)]
107117
return isReservedWord

internal/jet/expression.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -159,7 +159,7 @@ func newExpressionListOperator(operator string, expressions ...Expression) *expr
159159
}
160160

161161
func newBoolExpressionListOperator(operator string, expressions ...BoolExpression) BoolExpression {
162-
return BoolExp(newExpressionListOperator(operator, BoolExpressionListToExpressionList(expressions)...))
162+
return BoolExp(newExpressionListOperator(operator, ToExpressionList(expressions)...))
163163
}
164164

165165
func (elo *expressionListOperator) serialize(statement StatementType, out *SQLBuilder, options ...SerializeOption) {

internal/jet/func_expression.go

Lines changed: 37 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -255,18 +255,30 @@ func leadLagImpl(name string, expr Expression, offsetAndDefault ...interface{})
255255

256256
//------------ String functions ------------------//
257257

258+
// HEX function takes an input and returns its equivalent hexadecimal representation
259+
func HEX(expression Expression) StringExpression {
260+
return StringExp(Func("HEX", expression))
261+
}
262+
263+
// UNHEX for a string argument str, UNHEX(str) interprets each pair of characters in the argument
264+
// as a hexadecimal number and converts it to the byte represented by the number.
265+
// The return value is a binary string.
266+
func UNHEX(expression StringExpression) BlobExpression {
267+
return BlobExp(Func("UNHEX", expression))
268+
}
269+
258270
// BIT_LENGTH returns number of bits in string expression
259-
func BIT_LENGTH(stringExpression StringExpression) IntegerExpression {
271+
func BIT_LENGTH(stringExpression StringOrBlobExpression) IntegerExpression {
260272
return newIntegerFunc("BIT_LENGTH", stringExpression)
261273
}
262274

263275
// CHAR_LENGTH returns number of characters in string expression
264-
func CHAR_LENGTH(stringExpression StringExpression) IntegerExpression {
276+
func CHAR_LENGTH(stringExpression StringOrBlobExpression) IntegerExpression {
265277
return newIntegerFunc("CHAR_LENGTH", stringExpression)
266278
}
267279

268280
// OCTET_LENGTH returns number of bytes in string expression
269-
func OCTET_LENGTH(stringExpression StringExpression) IntegerExpression {
281+
func OCTET_LENGTH(stringExpression StringOrBlobExpression) IntegerExpression {
270282
return newIntegerFunc("OCTET_LENGTH", stringExpression)
271283
}
272284

@@ -282,7 +294,7 @@ func UPPER(stringExpression StringExpression) StringExpression {
282294

283295
// BTRIM removes the longest string consisting only of characters
284296
// in characters (a space by default) from the start and end of string
285-
func BTRIM(stringExpression StringExpression, trimChars ...StringExpression) StringExpression {
297+
func BTRIM(stringExpression StringOrBlobExpression, trimChars ...StringOrBlobExpression) StringExpression {
286298
if len(trimChars) > 0 {
287299
return NewStringFunc("BTRIM", stringExpression, trimChars[0])
288300
}
@@ -291,7 +303,7 @@ func BTRIM(stringExpression StringExpression, trimChars ...StringExpression) Str
291303

292304
// LTRIM removes the longest string containing only characters
293305
// from characters (a space by default) from the start of string
294-
func LTRIM(str StringExpression, trimChars ...StringExpression) StringExpression {
306+
func LTRIM(str StringOrBlobExpression, trimChars ...StringOrBlobExpression) StringExpression {
295307
if len(trimChars) > 0 {
296308
return NewStringFunc("LTRIM", str, trimChars[0])
297309
}
@@ -300,7 +312,7 @@ func LTRIM(str StringExpression, trimChars ...StringExpression) StringExpression
300312

301313
// RTRIM removes the longest string containing only characters
302314
// from characters (a space by default) from the end of string
303-
func RTRIM(str StringExpression, trimChars ...StringExpression) StringExpression {
315+
func RTRIM(str StringOrBlobExpression, trimChars ...StringOrBlobExpression) StringExpression {
304316
if len(trimChars) > 0 {
305317
return NewStringFunc("RTRIM", str, trimChars[0])
306318
}
@@ -324,32 +336,32 @@ func CONCAT_WS(separator Expression, expressions ...Expression) StringExpression
324336

325337
// CONVERT converts string to dest_encoding. The original encoding is
326338
// specified by src_encoding. The string must be valid in this encoding.
327-
func CONVERT(str StringExpression, srcEncoding StringExpression, destEncoding StringExpression) StringExpression {
328-
return NewStringFunc("CONVERT", str, srcEncoding, destEncoding)
339+
func CONVERT(str BlobExpression, srcEncoding StringExpression, destEncoding StringExpression) BlobExpression {
340+
return BlobExp(Func("CONVERT", str, srcEncoding, destEncoding))
329341
}
330342

331343
// CONVERT_FROM converts string to the database encoding. The original
332344
// encoding is specified by src_encoding. The string must be valid in this encoding.
333-
func CONVERT_FROM(str StringExpression, srcEncoding StringExpression) StringExpression {
345+
func CONVERT_FROM(str BlobExpression, srcEncoding StringExpression) StringExpression {
334346
return NewStringFunc("CONVERT_FROM", str, srcEncoding)
335347
}
336348

337349
// CONVERT_TO converts string to dest_encoding.
338-
func CONVERT_TO(str StringExpression, toEncoding StringExpression) StringExpression {
339-
return NewStringFunc("CONVERT_TO", str, toEncoding)
350+
func CONVERT_TO(str StringExpression, toEncoding StringExpression) BlobExpression {
351+
return BlobExp(Func("CONVERT_TO", str, toEncoding))
340352
}
341353

342354
// ENCODE encodes binary data into a textual representation.
343355
// Supported formats are: base64, hex, escape. escape converts zero bytes and
344356
// high-bit-set bytes to octal sequences (\nnn) and doubles backslashes.
345-
func ENCODE(data StringExpression, format StringExpression) StringExpression {
346-
return NewStringFunc("ENCODE", data, format)
357+
func ENCODE(data BlobExpression, format StringExpression) StringExpression {
358+
return StringExp(Func("ENCODE", data, format))
347359
}
348360

349361
// DECODE decodes binary data from textual representation in string.
350362
// Options for format are same as in encode.
351-
func DECODE(data StringExpression, format StringExpression) StringExpression {
352-
return NewStringFunc("DECODE", data, format)
363+
func DECODE(data StringExpression, format StringExpression) BlobExpression {
364+
return BlobExp(Func("DECODE", data, format))
353365
}
354366

355367
// FORMAT formats a number to a format like "#,###,###.##", rounded to a specified number of decimal places, then it returns the result as a string.
@@ -379,11 +391,11 @@ func RIGHT(str StringExpression, n IntegerExpression) StringExpression {
379391
}
380392

381393
// LENGTH returns number of characters in string with a given encoding
382-
func LENGTH(str StringExpression, encoding ...StringExpression) StringExpression {
394+
func LENGTH(str StringOrBlobExpression, encoding ...StringExpression) IntegerExpression {
383395
if len(encoding) > 0 {
384-
return NewStringFunc("LENGTH", str, encoding[0])
396+
return IntExp(Func("LENGTH", str, encoding[0]))
385397
}
386-
return NewStringFunc("LENGTH", str)
398+
return IntExp(Func("LENGTH", str))
387399
}
388400

389401
// LPAD fills up the string to length length by prepending the characters
@@ -407,8 +419,13 @@ func RPAD(str StringExpression, length IntegerExpression, text ...StringExpressi
407419
return NewStringFunc("RPAD", str, length)
408420
}
409421

422+
// BIT_COUNT returns the number of bits set in the binary string (also known as “popcount”).
423+
func BIT_COUNT(bytes BlobExpression) IntegerExpression {
424+
return IntExp(Func("BIT_COUNT", bytes))
425+
}
426+
410427
// MD5 calculates the MD5 hash of string, returning the result in hexadecimal
411-
func MD5(stringExpression StringExpression) StringExpression {
428+
func MD5(stringExpression StringOrBlobExpression) StringExpression {
412429
return NewStringFunc("MD5", stringExpression)
413430
}
414431

@@ -434,7 +451,7 @@ func STRPOS(str, substring StringExpression) IntegerExpression {
434451
}
435452

436453
// SUBSTR extracts substring
437-
func SUBSTR(str StringExpression, from IntegerExpression, count ...IntegerExpression) StringExpression {
454+
func SUBSTR(str StringOrBlobExpression, from IntegerExpression, count ...IntegerExpression) StringExpression {
438455
if len(count) > 0 {
439456
return NewStringFunc("SUBSTR", str, from, count[0])
440457
}

internal/jet/literal_expression.go

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -468,6 +468,11 @@ func RawDate(raw string, namedArgs ...map[string]interface{}) DateExpression {
468468
return DateExp(Raw(raw, namedArgs...))
469469
}
470470

471+
// RawBlob is raw query helper that for blob expressions
472+
func RawBlob(raw string, namedArgs ...map[string]interface{}) BlobExpression {
473+
return BlobExp(Raw(raw, namedArgs...))
474+
}
475+
471476
// RawRange helper that for range expressions
472477
func RawRange[T Expression](raw string, namedArgs ...map[string]interface{}) Range[T] {
473478
return RangeExp[T](Raw(raw, namedArgs...))

0 commit comments

Comments
 (0)