Add support for blob expressions.
This commit is contained in:
parent
26e478dc7e
commit
c94216ab0e
37 changed files with 1296 additions and 81 deletions
103
internal/jet/blob_expression.go
Normal file
103
internal/jet/blob_expression.go
Normal file
|
|
@ -0,0 +1,103 @@
|
|||
package jet
|
||||
|
||||
// BlobExpression interface
|
||||
type BlobExpression interface {
|
||||
Expression
|
||||
|
||||
isStringOrBlob()
|
||||
|
||||
EQ(rhs BlobExpression) BoolExpression
|
||||
NOT_EQ(rhs BlobExpression) BoolExpression
|
||||
IS_DISTINCT_FROM(rhs BlobExpression) BoolExpression
|
||||
IS_NOT_DISTINCT_FROM(rhs BlobExpression) BoolExpression
|
||||
|
||||
LT(rhs BlobExpression) BoolExpression
|
||||
LT_EQ(rhs BlobExpression) BoolExpression
|
||||
GT(rhs BlobExpression) BoolExpression
|
||||
GT_EQ(rhs BlobExpression) BoolExpression
|
||||
BETWEEN(min, max BlobExpression) BoolExpression
|
||||
NOT_BETWEEN(min, max BlobExpression) BoolExpression
|
||||
|
||||
CONCAT(rhs BlobExpression) BlobExpression
|
||||
|
||||
LIKE(pattern BlobExpression) BoolExpression
|
||||
NOT_LIKE(pattern BlobExpression) BoolExpression
|
||||
}
|
||||
|
||||
type blobInterfaceImpl struct {
|
||||
parent BlobExpression
|
||||
}
|
||||
|
||||
func (s *blobInterfaceImpl) isStringOrBlob() {}
|
||||
|
||||
func (s *blobInterfaceImpl) EQ(rhs BlobExpression) BoolExpression {
|
||||
return Eq(s.parent, rhs)
|
||||
}
|
||||
|
||||
func (s *blobInterfaceImpl) NOT_EQ(rhs BlobExpression) BoolExpression {
|
||||
return NotEq(s.parent, rhs)
|
||||
}
|
||||
|
||||
func (s *blobInterfaceImpl) IS_DISTINCT_FROM(rhs BlobExpression) BoolExpression {
|
||||
return IsDistinctFrom(s.parent, rhs)
|
||||
}
|
||||
|
||||
func (s *blobInterfaceImpl) IS_NOT_DISTINCT_FROM(rhs BlobExpression) BoolExpression {
|
||||
return IsNotDistinctFrom(s.parent, rhs)
|
||||
}
|
||||
|
||||
func (s *blobInterfaceImpl) GT(rhs BlobExpression) BoolExpression {
|
||||
return Gt(s.parent, rhs)
|
||||
}
|
||||
|
||||
func (s *blobInterfaceImpl) GT_EQ(rhs BlobExpression) BoolExpression {
|
||||
return GtEq(s.parent, rhs)
|
||||
}
|
||||
|
||||
func (s *blobInterfaceImpl) LT(rhs BlobExpression) BoolExpression {
|
||||
return Lt(s.parent, rhs)
|
||||
}
|
||||
|
||||
func (s *blobInterfaceImpl) LT_EQ(rhs BlobExpression) BoolExpression {
|
||||
return LtEq(s.parent, rhs)
|
||||
}
|
||||
|
||||
func (s *blobInterfaceImpl) BETWEEN(min, max BlobExpression) BoolExpression {
|
||||
return NewBetweenOperatorExpression(s.parent, min, max, false)
|
||||
}
|
||||
|
||||
func (s *blobInterfaceImpl) NOT_BETWEEN(min, max BlobExpression) BoolExpression {
|
||||
return NewBetweenOperatorExpression(s.parent, min, max, true)
|
||||
}
|
||||
|
||||
func (s *blobInterfaceImpl) CONCAT(rhs BlobExpression) BlobExpression {
|
||||
return BlobExp(newBinaryStringOperatorExpression(s.parent, rhs, StringConcatOperator))
|
||||
}
|
||||
|
||||
func (s *blobInterfaceImpl) LIKE(pattern BlobExpression) BoolExpression {
|
||||
return newBinaryBoolOperatorExpression(s.parent, pattern, "LIKE")
|
||||
}
|
||||
|
||||
func (s *blobInterfaceImpl) NOT_LIKE(pattern BlobExpression) BoolExpression {
|
||||
return newBinaryBoolOperatorExpression(s.parent, pattern, "NOT LIKE")
|
||||
}
|
||||
|
||||
//---------------------------------------------------//
|
||||
|
||||
type blobExpressionWrapper struct {
|
||||
blobInterfaceImpl
|
||||
Expression
|
||||
}
|
||||
|
||||
func newBlobExpressionWrap(expression Expression) BlobExpression {
|
||||
blobExpressionWrap := blobExpressionWrapper{Expression: expression}
|
||||
blobExpressionWrap.blobInterfaceImpl.parent = &blobExpressionWrap
|
||||
return &blobExpressionWrap
|
||||
}
|
||||
|
||||
// BlobExp is blob expression wrapper around arbitrary expression.
|
||||
// Allows go compiler to see any expression as blob expression.
|
||||
// Does not add sql cast to generated sql builder output.
|
||||
func BlobExp(expression Expression) BlobExpression {
|
||||
return newBlobExpressionWrap(expression)
|
||||
}
|
||||
|
|
@ -122,7 +122,7 @@ func IntegerColumn(name string) ColumnInteger {
|
|||
//------------------------------------------------------//
|
||||
|
||||
// ColumnString is interface for SQL text, character, character varying
|
||||
// bytea, uuid columns and enums types.
|
||||
// uuid columns and enums types.
|
||||
type ColumnString interface {
|
||||
StringExpression
|
||||
Column
|
||||
|
|
@ -163,6 +163,47 @@ func StringColumn(name string) ColumnString {
|
|||
|
||||
//------------------------------------------------------//
|
||||
|
||||
// ColumnBlob is interface for binary data types (bytea, binary, blob, etc...)
|
||||
type ColumnBlob interface {
|
||||
BlobExpression
|
||||
Column
|
||||
|
||||
From(subQuery SelectTable) ColumnBlob
|
||||
SET(blob BlobExpression) ColumnAssigment
|
||||
}
|
||||
|
||||
type blobColumnImpl struct {
|
||||
blobInterfaceImpl
|
||||
|
||||
ColumnExpressionImpl
|
||||
}
|
||||
|
||||
func (i *blobColumnImpl) From(subQuery SelectTable) ColumnBlob {
|
||||
newBlobColumn := BlobColumn(i.name)
|
||||
newBlobColumn.setTableName(i.tableName)
|
||||
newBlobColumn.setSubQuery(subQuery)
|
||||
|
||||
return newBlobColumn
|
||||
}
|
||||
|
||||
func (i *blobColumnImpl) SET(blobExp BlobExpression) ColumnAssigment {
|
||||
return columnAssigmentImpl{
|
||||
column: i,
|
||||
expression: blobExp,
|
||||
}
|
||||
}
|
||||
|
||||
// BlobColumn creates named blob column.
|
||||
func BlobColumn(name string) ColumnBlob {
|
||||
blobColumn := &blobColumnImpl{}
|
||||
blobColumn.blobInterfaceImpl.parent = blobColumn
|
||||
blobColumn.ColumnExpressionImpl = NewColumnImpl(name, "", blobColumn)
|
||||
|
||||
return blobColumn
|
||||
}
|
||||
|
||||
//------------------------------------------------------//
|
||||
|
||||
// ColumnTime is interface for SQL time column.
|
||||
type ColumnTime interface {
|
||||
TimeExpression
|
||||
|
|
|
|||
|
|
@ -1,6 +1,8 @@
|
|||
package jet
|
||||
|
||||
import "strings"
|
||||
import (
|
||||
"strings"
|
||||
)
|
||||
|
||||
// Dialect interface
|
||||
type Dialect interface {
|
||||
|
|
@ -11,6 +13,7 @@ type Dialect interface {
|
|||
AliasQuoteChar() byte
|
||||
IdentifierQuoteChar() byte
|
||||
ArgumentPlaceholder() QueryPlaceholderFunc
|
||||
ArgumentToString(value any) (string, bool)
|
||||
IsReservedWord(name string) bool
|
||||
SerializeOrderBy() func(expression Expression, ascending, nullsFirst *bool) SerializerFunc
|
||||
ValuesDefaultColumnName(index int) string
|
||||
|
|
@ -34,6 +37,7 @@ type DialectParams struct {
|
|||
AliasQuoteChar byte
|
||||
IdentifierQuoteChar byte
|
||||
ArgumentPlaceholder QueryPlaceholderFunc
|
||||
ArgumentToString func(value any) (string, bool)
|
||||
ReservedWords []string
|
||||
SerializeOrderBy func(expression Expression, ascending, nullsFirst *bool) SerializerFunc
|
||||
ValuesDefaultColumnName func(index int) string
|
||||
|
|
@ -49,6 +53,7 @@ func NewDialect(params DialectParams) Dialect {
|
|||
aliasQuoteChar: params.AliasQuoteChar,
|
||||
identifierQuoteChar: params.IdentifierQuoteChar,
|
||||
argumentPlaceholder: params.ArgumentPlaceholder,
|
||||
argumentToString: params.ArgumentToString,
|
||||
reservedWords: arrayOfStringsToMapOfStrings(params.ReservedWords),
|
||||
serializeOrderBy: params.SerializeOrderBy,
|
||||
valuesDefaultColumnName: params.ValuesDefaultColumnName,
|
||||
|
|
@ -63,6 +68,7 @@ type dialectImpl struct {
|
|||
aliasQuoteChar byte
|
||||
identifierQuoteChar byte
|
||||
argumentPlaceholder QueryPlaceholderFunc
|
||||
argumentToString func(value any) (string, bool)
|
||||
reservedWords map[string]bool
|
||||
serializeOrderBy func(expression Expression, ascending, nullsFirst *bool) SerializerFunc
|
||||
valuesDefaultColumnName func(index int) string
|
||||
|
|
@ -102,6 +108,10 @@ func (d *dialectImpl) ArgumentPlaceholder() QueryPlaceholderFunc {
|
|||
return d.argumentPlaceholder
|
||||
}
|
||||
|
||||
func (d *dialectImpl) ArgumentToString(value any) (string, bool) {
|
||||
return d.argumentToString(value)
|
||||
}
|
||||
|
||||
func (d *dialectImpl) IsReservedWord(name string) bool {
|
||||
_, isReservedWord := d.reservedWords[strings.ToLower(name)]
|
||||
return isReservedWord
|
||||
|
|
|
|||
|
|
@ -159,7 +159,7 @@ func newExpressionListOperator(operator string, expressions ...Expression) *expr
|
|||
}
|
||||
|
||||
func newBoolExpressionListOperator(operator string, expressions ...BoolExpression) BoolExpression {
|
||||
return BoolExp(newExpressionListOperator(operator, BoolExpressionListToExpressionList(expressions)...))
|
||||
return BoolExp(newExpressionListOperator(operator, ToExpressionList(expressions)...))
|
||||
}
|
||||
|
||||
func (elo *expressionListOperator) serialize(statement StatementType, out *SQLBuilder, options ...SerializeOption) {
|
||||
|
|
|
|||
|
|
@ -255,18 +255,30 @@ func leadLagImpl(name string, expr Expression, offsetAndDefault ...interface{})
|
|||
|
||||
//------------ String functions ------------------//
|
||||
|
||||
// HEX function takes an input and returns its equivalent hexadecimal representation
|
||||
func HEX(expression Expression) StringExpression {
|
||||
return StringExp(Func("HEX", expression))
|
||||
}
|
||||
|
||||
// UNHEX for a string argument str, UNHEX(str) interprets each pair of characters in the argument
|
||||
// as a hexadecimal number and converts it to the byte represented by the number.
|
||||
// The return value is a binary string.
|
||||
func UNHEX(expression StringExpression) BlobExpression {
|
||||
return BlobExp(Func("UNHEX", expression))
|
||||
}
|
||||
|
||||
// BIT_LENGTH returns number of bits in string expression
|
||||
func BIT_LENGTH(stringExpression StringExpression) IntegerExpression {
|
||||
func BIT_LENGTH(stringExpression StringOrBlobExpression) IntegerExpression {
|
||||
return newIntegerFunc("BIT_LENGTH", stringExpression)
|
||||
}
|
||||
|
||||
// CHAR_LENGTH returns number of characters in string expression
|
||||
func CHAR_LENGTH(stringExpression StringExpression) IntegerExpression {
|
||||
func CHAR_LENGTH(stringExpression StringOrBlobExpression) IntegerExpression {
|
||||
return newIntegerFunc("CHAR_LENGTH", stringExpression)
|
||||
}
|
||||
|
||||
// OCTET_LENGTH returns number of bytes in string expression
|
||||
func OCTET_LENGTH(stringExpression StringExpression) IntegerExpression {
|
||||
func OCTET_LENGTH(stringExpression StringOrBlobExpression) IntegerExpression {
|
||||
return newIntegerFunc("OCTET_LENGTH", stringExpression)
|
||||
}
|
||||
|
||||
|
|
@ -282,7 +294,7 @@ func UPPER(stringExpression StringExpression) StringExpression {
|
|||
|
||||
// BTRIM removes the longest string consisting only of characters
|
||||
// in characters (a space by default) from the start and end of string
|
||||
func BTRIM(stringExpression StringExpression, trimChars ...StringExpression) StringExpression {
|
||||
func BTRIM(stringExpression StringOrBlobExpression, trimChars ...StringOrBlobExpression) StringExpression {
|
||||
if len(trimChars) > 0 {
|
||||
return NewStringFunc("BTRIM", stringExpression, trimChars[0])
|
||||
}
|
||||
|
|
@ -291,7 +303,7 @@ func BTRIM(stringExpression StringExpression, trimChars ...StringExpression) Str
|
|||
|
||||
// LTRIM removes the longest string containing only characters
|
||||
// from characters (a space by default) from the start of string
|
||||
func LTRIM(str StringExpression, trimChars ...StringExpression) StringExpression {
|
||||
func LTRIM(str StringOrBlobExpression, trimChars ...StringOrBlobExpression) StringExpression {
|
||||
if len(trimChars) > 0 {
|
||||
return NewStringFunc("LTRIM", str, trimChars[0])
|
||||
}
|
||||
|
|
@ -300,7 +312,7 @@ func LTRIM(str StringExpression, trimChars ...StringExpression) StringExpression
|
|||
|
||||
// RTRIM removes the longest string containing only characters
|
||||
// from characters (a space by default) from the end of string
|
||||
func RTRIM(str StringExpression, trimChars ...StringExpression) StringExpression {
|
||||
func RTRIM(str StringOrBlobExpression, trimChars ...StringOrBlobExpression) StringExpression {
|
||||
if len(trimChars) > 0 {
|
||||
return NewStringFunc("RTRIM", str, trimChars[0])
|
||||
}
|
||||
|
|
@ -324,32 +336,32 @@ func CONCAT_WS(separator Expression, expressions ...Expression) StringExpression
|
|||
|
||||
// CONVERT converts string to dest_encoding. The original encoding is
|
||||
// specified by src_encoding. The string must be valid in this encoding.
|
||||
func CONVERT(str StringExpression, srcEncoding StringExpression, destEncoding StringExpression) StringExpression {
|
||||
return NewStringFunc("CONVERT", str, srcEncoding, destEncoding)
|
||||
func CONVERT(str BlobExpression, srcEncoding StringExpression, destEncoding StringExpression) BlobExpression {
|
||||
return BlobExp(Func("CONVERT", str, srcEncoding, destEncoding))
|
||||
}
|
||||
|
||||
// CONVERT_FROM converts string to the database encoding. The original
|
||||
// encoding is specified by src_encoding. The string must be valid in this encoding.
|
||||
func CONVERT_FROM(str StringExpression, srcEncoding StringExpression) StringExpression {
|
||||
func CONVERT_FROM(str BlobExpression, srcEncoding StringExpression) StringExpression {
|
||||
return NewStringFunc("CONVERT_FROM", str, srcEncoding)
|
||||
}
|
||||
|
||||
// CONVERT_TO converts string to dest_encoding.
|
||||
func CONVERT_TO(str StringExpression, toEncoding StringExpression) StringExpression {
|
||||
return NewStringFunc("CONVERT_TO", str, toEncoding)
|
||||
func CONVERT_TO(str StringExpression, toEncoding StringExpression) BlobExpression {
|
||||
return BlobExp(Func("CONVERT_TO", str, toEncoding))
|
||||
}
|
||||
|
||||
// ENCODE encodes binary data into a textual representation.
|
||||
// Supported formats are: base64, hex, escape. escape converts zero bytes and
|
||||
// high-bit-set bytes to octal sequences (\nnn) and doubles backslashes.
|
||||
func ENCODE(data StringExpression, format StringExpression) StringExpression {
|
||||
return NewStringFunc("ENCODE", data, format)
|
||||
func ENCODE(data BlobExpression, format StringExpression) StringExpression {
|
||||
return StringExp(Func("ENCODE", data, format))
|
||||
}
|
||||
|
||||
// DECODE decodes binary data from textual representation in string.
|
||||
// Options for format are same as in encode.
|
||||
func DECODE(data StringExpression, format StringExpression) StringExpression {
|
||||
return NewStringFunc("DECODE", data, format)
|
||||
func DECODE(data StringExpression, format StringExpression) BlobExpression {
|
||||
return BlobExp(Func("DECODE", data, format))
|
||||
}
|
||||
|
||||
// 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 {
|
|||
}
|
||||
|
||||
// LENGTH returns number of characters in string with a given encoding
|
||||
func LENGTH(str StringExpression, encoding ...StringExpression) StringExpression {
|
||||
func LENGTH(str StringOrBlobExpression, encoding ...StringExpression) IntegerExpression {
|
||||
if len(encoding) > 0 {
|
||||
return NewStringFunc("LENGTH", str, encoding[0])
|
||||
return IntExp(Func("LENGTH", str, encoding[0]))
|
||||
}
|
||||
return NewStringFunc("LENGTH", str)
|
||||
return IntExp(Func("LENGTH", str))
|
||||
}
|
||||
|
||||
// LPAD fills up the string to length length by prepending the characters
|
||||
|
|
@ -407,8 +419,13 @@ func RPAD(str StringExpression, length IntegerExpression, text ...StringExpressi
|
|||
return NewStringFunc("RPAD", str, length)
|
||||
}
|
||||
|
||||
// BIT_COUNT returns the number of bits set in the binary string (also known as “popcount”).
|
||||
func BIT_COUNT(bytes BlobExpression) IntegerExpression {
|
||||
return IntExp(Func("BIT_COUNT", bytes))
|
||||
}
|
||||
|
||||
// MD5 calculates the MD5 hash of string, returning the result in hexadecimal
|
||||
func MD5(stringExpression StringExpression) StringExpression {
|
||||
func MD5(stringExpression StringOrBlobExpression) StringExpression {
|
||||
return NewStringFunc("MD5", stringExpression)
|
||||
}
|
||||
|
||||
|
|
@ -434,7 +451,7 @@ func STRPOS(str, substring StringExpression) IntegerExpression {
|
|||
}
|
||||
|
||||
// SUBSTR extracts substring
|
||||
func SUBSTR(str StringExpression, from IntegerExpression, count ...IntegerExpression) StringExpression {
|
||||
func SUBSTR(str StringOrBlobExpression, from IntegerExpression, count ...IntegerExpression) StringExpression {
|
||||
if len(count) > 0 {
|
||||
return NewStringFunc("SUBSTR", str, from, count[0])
|
||||
}
|
||||
|
|
|
|||
|
|
@ -468,6 +468,11 @@ func RawDate(raw string, namedArgs ...map[string]interface{}) DateExpression {
|
|||
return DateExp(Raw(raw, namedArgs...))
|
||||
}
|
||||
|
||||
// RawBlob is raw query helper that for blob expressions
|
||||
func RawBlob(raw string, namedArgs ...map[string]interface{}) BlobExpression {
|
||||
return BlobExp(Raw(raw, namedArgs...))
|
||||
}
|
||||
|
||||
// RawRange helper that for range expressions
|
||||
func RawRange[T Expression](raw string, namedArgs ...map[string]interface{}) Range[T] {
|
||||
return RangeExp[T](Raw(raw, namedArgs...))
|
||||
|
|
|
|||
|
|
@ -127,7 +127,7 @@ func (s *SQLBuilder) finalize() (string, []interface{}) {
|
|||
}
|
||||
|
||||
func (s *SQLBuilder) insertConstantArgument(arg interface{}) {
|
||||
s.WriteString(argToString(arg))
|
||||
s.WriteString(s.argToString(arg))
|
||||
}
|
||||
|
||||
func (s *SQLBuilder) insertParametrizedArgument(arg interface{}) {
|
||||
|
|
@ -200,7 +200,7 @@ func (s *SQLBuilder) insertRawQuery(raw string, namedArg map[string]interface{})
|
|||
}
|
||||
|
||||
if s.Debug {
|
||||
placeholder = argToString(namedArgumentPos.Value)
|
||||
placeholder = s.argToString(namedArgumentPos.Value)
|
||||
}
|
||||
|
||||
raw = strings.Replace(raw, namedArgumentPos.Name, placeholder, toReplace)
|
||||
|
|
@ -209,11 +209,17 @@ func (s *SQLBuilder) insertRawQuery(raw string, namedArg map[string]interface{})
|
|||
s.WriteString(raw)
|
||||
}
|
||||
|
||||
func argToString(value interface{}) string {
|
||||
func (s *SQLBuilder) argToString(value interface{}) string {
|
||||
if is.Nil(value) {
|
||||
return "NULL"
|
||||
}
|
||||
|
||||
strVal, ok := s.Dialect.ArgumentToString(value)
|
||||
|
||||
if ok {
|
||||
return strVal
|
||||
}
|
||||
|
||||
switch bindVal := value.(type) {
|
||||
case bool:
|
||||
if bindVal {
|
||||
|
|
@ -250,7 +256,7 @@ func argToString(value interface{}) string {
|
|||
return err.Error()
|
||||
}
|
||||
|
||||
return argToString(val)
|
||||
return s.argToString(val)
|
||||
}
|
||||
|
||||
panic(fmt.Sprintf("jet: %s type can not be used as SQL query parameter", reflect.TypeOf(value).String()))
|
||||
|
|
|
|||
|
|
@ -3,6 +3,7 @@ package jet
|
|||
// StringExpression interface
|
||||
type StringExpression interface {
|
||||
Expression
|
||||
isStringOrBlob()
|
||||
|
||||
EQ(rhs StringExpression) BoolExpression
|
||||
NOT_EQ(rhs StringExpression) BoolExpression
|
||||
|
|
@ -29,6 +30,8 @@ type stringInterfaceImpl struct {
|
|||
parent StringExpression
|
||||
}
|
||||
|
||||
func (s *stringInterfaceImpl) isStringOrBlob() {}
|
||||
|
||||
func (s *stringInterfaceImpl) EQ(rhs StringExpression) BoolExpression {
|
||||
return Eq(s.parent, rhs)
|
||||
}
|
||||
|
|
|
|||
8
internal/jet/string_or_blob_expression.go
Normal file
8
internal/jet/string_or_blob_expression.go
Normal file
|
|
@ -0,0 +1,8 @@
|
|||
package jet
|
||||
|
||||
// StringOrBlobExpression is common interface for all string and blob expressions
|
||||
type StringOrBlobExpression interface {
|
||||
Expression
|
||||
|
||||
isStringOrBlob()
|
||||
}
|
||||
|
|
@ -132,8 +132,8 @@ func ExpressionListToSerializerList(expressions []Expression) []Serializer {
|
|||
return ret
|
||||
}
|
||||
|
||||
// BoolExpressionListToExpressionList converts list of bool expressions to list of expressions
|
||||
func BoolExpressionListToExpressionList(expressions []BoolExpression) []Expression {
|
||||
// ToExpressionList converts list of any expressions to list of expressions
|
||||
func ToExpressionList[T Expression](expressions []T) []Expression {
|
||||
var ret []Expression
|
||||
|
||||
for _, expression := range expressions {
|
||||
|
|
|
|||
|
|
@ -1,6 +1,8 @@
|
|||
package is
|
||||
|
||||
import "reflect"
|
||||
import (
|
||||
"reflect"
|
||||
)
|
||||
|
||||
// Nil check if v is nil
|
||||
func Nil(v interface{}) bool {
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue