Encode json values implicitly in the sql queries according the golang json package spec.

This commit is contained in:
go-jet 2025-03-08 19:01:37 +01:00
parent 9616bb5cfe
commit 17646ca99c
54 changed files with 1446 additions and 744 deletions

View file

@ -18,10 +18,45 @@ func (a *alias) fromImpl(subQuery SelectTable) Projection {
// Generated columns have default aliasing.
tableName, columnName := extractTableAndColumnName(a.alias)
column := NewColumnImpl(columnName, tableName, nil)
column.subQuery = subQuery
newDummyColumn := newDummyColumnForExpression(a.expression, columnName)
newDummyColumn.setTableName(tableName)
newDummyColumn.setSubQuery(subQuery)
return &column
return newDummyColumn
}
// This function is used to create dummy columns when exporting sub-query columns using subQuery.AllColumns()
// In most case we don't care about type of the column, except when sub-query columns are used as SELECT_JSON projection.
// We need to know type to encode value for json unmarshal. At the moment only bool, time and blob columns are of interest,
// so we don't have to support every column type.
func newDummyColumnForExpression(exp Expression, name string) ColumnExpression {
switch exp.(type) {
case BoolExpression:
return BoolColumn(name)
case IntegerExpression:
return IntegerColumn(name)
case FloatExpression:
return FloatColumn(name)
case BlobExpression:
return BlobColumn(name)
case DateExpression:
return DateColumn(name)
case TimeExpression:
return TimeColumn(name)
case TimezExpression:
return TimezColumn(name)
case TimestampExpression:
return TimestampColumn(name)
case TimestampzExpression:
return TimestampzColumn(name)
case IntervalExpression:
return IntervalColumn(name)
case StringExpression:
return StringColumn(name)
}
return StringColumn(name)
}
func (a *alias) serializeForProjection(statement StatementType, out *SQLBuilder) {
@ -31,7 +66,14 @@ func (a *alias) serializeForProjection(statement StatementType, out *SQLBuilder)
out.WriteAlias(a.alias)
}
func (a *alias) serializeForJsonObj(statement StatementType, out *SQLBuilder) {
func (a *alias) serializeForJsonObjEntry(statement StatementType, out *SQLBuilder) {
out.WriteJsonObjKey(a.alias)
a.expression.serialize(statement, out)
a.expression.serializeForJsonValue(statement, out)
}
func (a *alias) serializeForRowToJsonProjection(statement StatementType, out *SQLBuilder) {
a.expression.serializeForJsonValue(statement, out)
out.WriteString("AS")
out.WriteAlias(a.alias)
}

View file

@ -28,71 +28,72 @@ type blobInterfaceImpl struct {
parent BlobExpression
}
func (s *blobInterfaceImpl) isStringOrBlob() {}
func (b *blobInterfaceImpl) isStringOrBlob() {}
func (s *blobInterfaceImpl) EQ(rhs BlobExpression) BoolExpression {
return Eq(s.parent, rhs)
func (b *blobInterfaceImpl) EQ(rhs BlobExpression) BoolExpression {
return Eq(b.parent, rhs)
}
func (s *blobInterfaceImpl) NOT_EQ(rhs BlobExpression) BoolExpression {
return NotEq(s.parent, rhs)
func (b *blobInterfaceImpl) NOT_EQ(rhs BlobExpression) BoolExpression {
return NotEq(b.parent, rhs)
}
func (s *blobInterfaceImpl) IS_DISTINCT_FROM(rhs BlobExpression) BoolExpression {
return IsDistinctFrom(s.parent, rhs)
func (b *blobInterfaceImpl) IS_DISTINCT_FROM(rhs BlobExpression) BoolExpression {
return IsDistinctFrom(b.parent, rhs)
}
func (s *blobInterfaceImpl) IS_NOT_DISTINCT_FROM(rhs BlobExpression) BoolExpression {
return IsNotDistinctFrom(s.parent, rhs)
func (b *blobInterfaceImpl) IS_NOT_DISTINCT_FROM(rhs BlobExpression) BoolExpression {
return IsNotDistinctFrom(b.parent, rhs)
}
func (s *blobInterfaceImpl) GT(rhs BlobExpression) BoolExpression {
return Gt(s.parent, rhs)
func (b *blobInterfaceImpl) GT(rhs BlobExpression) BoolExpression {
return Gt(b.parent, rhs)
}
func (s *blobInterfaceImpl) GT_EQ(rhs BlobExpression) BoolExpression {
return GtEq(s.parent, rhs)
func (b *blobInterfaceImpl) GT_EQ(rhs BlobExpression) BoolExpression {
return GtEq(b.parent, rhs)
}
func (s *blobInterfaceImpl) LT(rhs BlobExpression) BoolExpression {
return Lt(s.parent, rhs)
func (b *blobInterfaceImpl) LT(rhs BlobExpression) BoolExpression {
return Lt(b.parent, rhs)
}
func (s *blobInterfaceImpl) LT_EQ(rhs BlobExpression) BoolExpression {
return LtEq(s.parent, rhs)
func (b *blobInterfaceImpl) LT_EQ(rhs BlobExpression) BoolExpression {
return LtEq(b.parent, rhs)
}
func (s *blobInterfaceImpl) BETWEEN(min, max BlobExpression) BoolExpression {
return NewBetweenOperatorExpression(s.parent, min, max, false)
func (b *blobInterfaceImpl) BETWEEN(min, max BlobExpression) BoolExpression {
return NewBetweenOperatorExpression(b.parent, min, max, false)
}
func (s *blobInterfaceImpl) NOT_BETWEEN(min, max BlobExpression) BoolExpression {
return NewBetweenOperatorExpression(s.parent, min, max, true)
func (b *blobInterfaceImpl) NOT_BETWEEN(min, max BlobExpression) BoolExpression {
return NewBetweenOperatorExpression(b.parent, min, max, true)
}
func (s *blobInterfaceImpl) CONCAT(rhs BlobExpression) BlobExpression {
return BlobExp(newBinaryStringOperatorExpression(s.parent, rhs, StringConcatOperator))
func (b *blobInterfaceImpl) CONCAT(rhs BlobExpression) BlobExpression {
return BlobExp(newBinaryStringOperatorExpression(b.parent, rhs, StringConcatOperator))
}
func (s *blobInterfaceImpl) LIKE(pattern BlobExpression) BoolExpression {
return newBinaryBoolOperatorExpression(s.parent, pattern, "LIKE")
func (b *blobInterfaceImpl) LIKE(pattern BlobExpression) BoolExpression {
return newBinaryBoolOperatorExpression(b.parent, pattern, "LIKE")
}
func (s *blobInterfaceImpl) NOT_LIKE(pattern BlobExpression) BoolExpression {
return newBinaryBoolOperatorExpression(s.parent, pattern, "NOT LIKE")
func (b *blobInterfaceImpl) NOT_LIKE(pattern BlobExpression) BoolExpression {
return newBinaryBoolOperatorExpression(b.parent, pattern, "NOT LIKE")
}
//---------------------------------------------------//
type blobExpressionWrapper struct {
blobInterfaceImpl
Expression
blobInterfaceImpl
}
func newBlobExpressionWrap(expression Expression) BlobExpression {
blobExpressionWrap := blobExpressionWrapper{Expression: expression}
blobExpressionWrap.blobInterfaceImpl.parent = &blobExpressionWrap
return &blobExpressionWrap
blobExpressionWrap := &blobExpressionWrapper{Expression: expression}
blobExpressionWrap.blobInterfaceImpl.parent = blobExpressionWrap
expression.setParent(blobExpressionWrap)
return blobExpressionWrap
}
// BlobExp is blob expression wrapper around arbitrary expression.

View file

@ -102,9 +102,10 @@ type boolExpressionWrapper struct {
}
func newBoolExpressionWrap(expression Expression) BoolExpression {
boolExpressionWrap := boolExpressionWrapper{Expression: expression}
boolExpressionWrap.boolInterfaceImpl.parent = &boolExpressionWrap
return &boolExpressionWrap
boolExpressionWrap := &boolExpressionWrapper{Expression: expression}
boolExpressionWrap.boolInterfaceImpl.parent = boolExpressionWrap
expression.setParent(boolExpressionWrap)
return boolExpressionWrap
}
// BoolExp is bool expression wrapper around arbitrary expression.

View file

@ -41,6 +41,8 @@ type ClauseSelect struct {
DistinctOnColumns []ColumnExpression
ProjectionList []Projection
IsForRowToJson bool
// MySQL only
OptimizerHints optimizerHints
}
@ -70,7 +72,13 @@ func (s *ClauseSelect) Serialize(statementType StatementType, out *SQLBuilder, o
out.WriteByte(')')
}
out.WriteProjections(statementType, s.ProjectionList)
if s.IsForRowToJson {
out.IncreaseIdent()
out.WriteRowToJsonProjections(statementType, s.ProjectionList)
out.DecreaseIdent()
} else {
out.WriteProjections(statementType, s.ProjectionList)
}
}
// ClauseFrom struct

View file

@ -39,19 +39,19 @@ type ColumnExpressionImpl struct {
}
// NewColumnImpl creates new ColumnExpressionImpl
func NewColumnImpl(name string, tableName string, parent ColumnExpression) ColumnExpressionImpl {
bc := ColumnExpressionImpl{
func NewColumnImpl(name string, tableName string, parent ColumnExpression) *ColumnExpressionImpl {
newColumn := &ColumnExpressionImpl{
name: name,
tableName: tableName,
}
if parent != nil {
bc.ExpressionInterfaceImpl.Parent = parent
newColumn.ExpressionInterfaceImpl.Parent = parent
} else {
bc.ExpressionInterfaceImpl.Parent = &bc
newColumn.ExpressionInterfaceImpl.Parent = newColumn
}
return bc
return newColumn
}
// Name returns name of the column
@ -80,13 +80,6 @@ func (c *ColumnExpressionImpl) defaultAlias() string {
return c.name
}
func (c *ColumnExpressionImpl) fromImpl(subQuery SelectTable) Projection {
newColumn := NewColumnImpl(c.name, c.tableName, nil)
newColumn.setSubQuery(subQuery)
return &newColumn
}
func (c *ColumnExpressionImpl) serializeForOrderBy(statement StatementType, out *SQLBuilder) {
if statement == SetStatementType {
// set Statement (UNION, EXCEPT ...) can reference only select projections in order by clause
@ -97,24 +90,28 @@ func (c *ColumnExpressionImpl) serializeForOrderBy(statement StatementType, out
c.serialize(statement, out)
}
func (c ColumnExpressionImpl) serializeForProjection(statement StatementType, out *SQLBuilder) {
func (c *ColumnExpressionImpl) serializeForProjection(statement StatementType, out *SQLBuilder) {
c.serialize(statement, out)
out.WriteString("AS")
if statement.IsSelectJSON() {
out.WriteAlias(snaker.SnakeToCamel(c.name, false))
} else {
out.WriteAlias(c.defaultAlias())
}
out.WriteAlias(c.defaultAlias())
}
func (c ColumnExpressionImpl) serializeForJsonObj(statement StatementType, out *SQLBuilder) {
func (c *ColumnExpressionImpl) serializeForJsonObjEntry(statement StatementType, out *SQLBuilder) {
out.WriteJsonObjKey(snaker.SnakeToCamel(c.name, false))
c.serialize(statement, out)
c.Parent.serializeForJsonValue(statement, out)
}
func (c ColumnExpressionImpl) serialize(statement StatementType, out *SQLBuilder, options ...SerializeOption) {
func (c *ColumnExpressionImpl) serializeForRowToJsonProjection(statement StatementType, out *SQLBuilder) {
c.Parent.serializeForJsonValue(statement, out)
out.WriteString("AS")
out.WriteAlias(snaker.SnakeToCamel(c.name, false))
}
func (c *ColumnExpressionImpl) serialize(statement StatementType, out *SQLBuilder, options ...SerializeOption) {
if c.subQuery != nil {
out.WriteIdentifier(c.subQuery.Alias())

View file

@ -78,12 +78,18 @@ func (cl ColumnList) serializeForProjection(statement StatementType, out *SQLBui
SerializeProjectionList(statement, projections, out)
}
func (cl ColumnList) serializeForJsonObj(statement StatementType, out *SQLBuilder) {
func (cl ColumnList) serializeForJsonObjEntry(statement StatementType, out *SQLBuilder) {
projections := ColumnListToProjectionList(cl)
SerializeProjectionListJsonObj(statement, projections, out)
}
func (cl ColumnList) serializeForRowToJsonProjection(statement StatementType, out *SQLBuilder) {
projections := ColumnListToProjectionList(cl)
out.WriteRowToJsonProjections(statement, projections)
}
// dummy column interface implementation
// Name is placeholder for ColumnList to implement Column interface

View file

@ -4,11 +4,10 @@ import "testing"
func TestColumn(t *testing.T) {
column := NewColumnImpl("col", "", nil)
column.ExpressionInterfaceImpl.Parent = &column
assertClauseSerialize(t, column, "col")
column.setTableName("table1")
assertClauseSerialize(t, column, "table1.col")
assertProjectionSerialize(t, &column, `table1.col AS "table1.col"`)
assertProjectionSerialize(t, column, `table1.col AS "table1.col"`)
assertProjectionSerialize(t, column.AS("alias1"), `table1.col AS "alias1"`)
}

View file

@ -11,7 +11,11 @@ type ColumnBool interface {
type boolColumnImpl struct {
boolInterfaceImpl
ColumnExpressionImpl
*ColumnExpressionImpl
}
func (i *boolColumnImpl) fromImpl(subQuery SelectTable) Projection {
return i.From(subQuery)
}
func (i *boolColumnImpl) From(subQuery SelectTable) ColumnBool {
@ -51,7 +55,11 @@ type ColumnFloat interface {
type floatColumnImpl struct {
floatInterfaceImpl
ColumnExpressionImpl
*ColumnExpressionImpl
}
func (i *floatColumnImpl) fromImpl(subQuery SelectTable) Projection {
return i.From(subQuery)
}
func (i *floatColumnImpl) From(subQuery SelectTable) ColumnFloat {
@ -92,7 +100,11 @@ type ColumnInteger interface {
type integerColumnImpl struct {
integerInterfaceImpl
ColumnExpressionImpl
*ColumnExpressionImpl
}
func (i *integerColumnImpl) fromImpl(subQuery SelectTable) Projection {
return i.From(subQuery)
}
func (i *integerColumnImpl) From(subQuery SelectTable) ColumnInteger {
@ -134,7 +146,11 @@ type ColumnString interface {
type stringColumnImpl struct {
stringInterfaceImpl
ColumnExpressionImpl
*ColumnExpressionImpl
}
func (i *stringColumnImpl) fromImpl(subQuery SelectTable) Projection {
return i.From(subQuery)
}
func (i *stringColumnImpl) From(subQuery SelectTable) ColumnString {
@ -175,7 +191,11 @@ type ColumnBlob interface {
type blobColumnImpl struct {
blobInterfaceImpl
ColumnExpressionImpl
*ColumnExpressionImpl
}
func (i *blobColumnImpl) fromImpl(subQuery SelectTable) Projection {
return i.From(subQuery)
}
func (i *blobColumnImpl) From(subQuery SelectTable) ColumnBlob {
@ -215,7 +235,11 @@ type ColumnTime interface {
type timeColumnImpl struct {
timeInterfaceImpl
ColumnExpressionImpl
*ColumnExpressionImpl
}
func (i *timeColumnImpl) fromImpl(subQuery SelectTable) Projection {
return i.From(subQuery)
}
func (i *timeColumnImpl) From(subQuery SelectTable) ColumnTime {
@ -254,7 +278,11 @@ type ColumnTimez interface {
type timezColumnImpl struct {
timezInterfaceImpl
ColumnExpressionImpl
*ColumnExpressionImpl
}
func (i *timezColumnImpl) fromImpl(subQuery SelectTable) Projection {
return i.From(subQuery)
}
func (i *timezColumnImpl) From(subQuery SelectTable) ColumnTimez {
@ -294,7 +322,11 @@ type ColumnTimestamp interface {
type timestampColumnImpl struct {
timestampInterfaceImpl
ColumnExpressionImpl
*ColumnExpressionImpl
}
func (i *timestampColumnImpl) fromImpl(subQuery SelectTable) Projection {
return i.From(subQuery)
}
func (i *timestampColumnImpl) From(subQuery SelectTable) ColumnTimestamp {
@ -334,7 +366,11 @@ type ColumnTimestampz interface {
type timestampzColumnImpl struct {
timestampzInterfaceImpl
ColumnExpressionImpl
*ColumnExpressionImpl
}
func (i *timestampzColumnImpl) fromImpl(subQuery SelectTable) Projection {
return i.From(subQuery)
}
func (i *timestampzColumnImpl) From(subQuery SelectTable) ColumnTimestampz {
@ -374,7 +410,11 @@ type ColumnDate interface {
type dateColumnImpl struct {
dateInterfaceImpl
ColumnExpressionImpl
*ColumnExpressionImpl
}
func (i *dateColumnImpl) fromImpl(subQuery SelectTable) Projection {
return i.From(subQuery)
}
func (i *dateColumnImpl) From(subQuery SelectTable) ColumnDate {
@ -402,6 +442,51 @@ func DateColumn(name string) ColumnDate {
//------------------------------------------------------//
// ColumnInterval is interface of PostgreSQL interval columns.
type ColumnInterval interface {
IntervalExpression
Column
From(subQuery SelectTable) ColumnInterval
SET(intervalExp IntervalExpression) ColumnAssigment
}
//------------------------------------------------------//
type intervalColumnImpl struct {
*ColumnExpressionImpl
intervalInterfaceImpl
}
func (i *intervalColumnImpl) SET(intervalExp IntervalExpression) ColumnAssigment {
return columnAssigmentImpl{
column: i,
expression: intervalExp,
}
}
func (i *intervalColumnImpl) fromImpl(subQuery SelectTable) Projection {
return i.From(subQuery)
}
func (i *intervalColumnImpl) From(subQuery SelectTable) ColumnInterval {
newIntervalColumn := IntervalColumn(i.name)
newIntervalColumn.setTableName(i.tableName)
newIntervalColumn.setSubQuery(subQuery)
return newIntervalColumn
}
// IntervalColumn creates named interval column.
func IntervalColumn(name string) ColumnInterval {
intervalColumn := &intervalColumnImpl{}
intervalColumn.ColumnExpressionImpl = NewColumnImpl(name, "", intervalColumn)
intervalColumn.intervalInterfaceImpl.parent = intervalColumn
return intervalColumn
}
//------------------------------------------------------//
// ColumnRange is interface for range columns which can be int range, string range
// timestamp range or date range.
type ColumnRange[T Expression] interface {
@ -414,7 +499,11 @@ type ColumnRange[T Expression] interface {
type rangeColumnImpl[T Expression] struct {
rangeInterfaceImpl[T]
ColumnExpressionImpl
*ColumnExpressionImpl
}
func (i *rangeColumnImpl[T]) fromImpl(subQuery SelectTable) Projection {
return i.From(subQuery)
}
func (i *rangeColumnImpl[T]) From(subQuery SelectTable) ColumnRange[T] {

View file

@ -80,9 +80,10 @@ type dateExpressionWrapper struct {
}
func newDateExpressionWrap(expression Expression) DateExpression {
dateExpressionWrap := dateExpressionWrapper{Expression: expression}
dateExpressionWrap.dateInterfaceImpl.parent = &dateExpressionWrap
return &dateExpressionWrap
dateExpressionWrap := &dateExpressionWrapper{Expression: expression}
dateExpressionWrap.dateInterfaceImpl.parent = dateExpressionWrap
expression.setParent(dateExpressionWrap)
return dateExpressionWrap
}
// DateExp is date expression wrapper around arbitrary expression.

View file

@ -1,13 +0,0 @@
package jet
import (
"testing"
)
func TestDateArithmetic(t *testing.T) {
timestamp := Timestamp(2000, 1, 1, 0, 0, 0)
assertClauseDebugSerialize(t, table1ColDate.ADD(NewInterval(String("1 HOUR"))).EQ(timestamp),
"((table1.col_date + INTERVAL '1 HOUR') = '2000-01-01 00:00:00')")
assertClauseDebugSerialize(t, table1ColDate.SUB(NewInterval(String("1 HOUR"))).EQ(timestamp),
"((table1.col_date - INTERVAL '1 HOUR') = '2000-01-01 00:00:00')")
}

View file

@ -17,6 +17,7 @@ type Dialect interface {
IsReservedWord(name string) bool
SerializeOrderBy() func(expression Expression, ascending, nullsFirst *bool) SerializerFunc
ValuesDefaultColumnName(index int) string
JsonValueEncode(expr Expression) Expression
}
// SerializerFunc func
@ -41,6 +42,7 @@ type DialectParams struct {
ReservedWords []string
SerializeOrderBy func(expression Expression, ascending, nullsFirst *bool) SerializerFunc
ValuesDefaultColumnName func(index int) string
JsonValueEncode func(expr Expression) Expression
}
// NewDialect creates new dialect with params
@ -57,6 +59,7 @@ func NewDialect(params DialectParams) Dialect {
reservedWords: arrayOfStringsToMapOfStrings(params.ReservedWords),
serializeOrderBy: params.SerializeOrderBy,
valuesDefaultColumnName: params.ValuesDefaultColumnName,
jsonValueEncode: params.JsonValueEncode,
}
}
@ -72,6 +75,7 @@ type dialectImpl struct {
reservedWords map[string]bool
serializeOrderBy func(expression Expression, ascending, nullsFirst *bool) SerializerFunc
valuesDefaultColumnName func(index int) string
jsonValueEncode func(expr Expression) Expression
}
func (d *dialectImpl) Name() string {
@ -125,6 +129,10 @@ func (d *dialectImpl) ValuesDefaultColumnName(index int) string {
return d.valuesDefaultColumnName(index)
}
func (d *dialectImpl) JsonValueEncode(expr Expression) Expression {
return d.jsonValueEncode(expr)
}
func arrayOfStringsToMapOfStrings(arr []string) map[string]bool {
ret := map[string]bool{}
for _, elem := range arr {

View file

@ -10,6 +10,9 @@ type Expression interface {
GroupByClause
OrderByClause
serializeForJsonValue(statement StatementType, out *SQLBuilder)
setParent(parent Expression)
// IS_NULL tests expression whether it is a NULL value.
IS_NULL() BoolExpression
// IS_NOT_NULL tests expression whether it is a non-NULL value.
@ -34,6 +37,10 @@ type ExpressionInterfaceImpl struct {
Parent Expression
}
func (e *ExpressionInterfaceImpl) setParent(parent Expression) {
e.Parent = parent
}
func (e *ExpressionInterfaceImpl) fromImpl(subQuery SelectTable) Projection {
panic(fmt.Sprintf("jet: can't export unaliased expression subQuery: %s, expression: %s",
subQuery.Alias(), serializeToDefaultDebugString(e.Parent)))
@ -89,16 +96,21 @@ func (e *ExpressionInterfaceImpl) serializeForGroupBy(statement StatementType, o
}
func (e *ExpressionInterfaceImpl) serializeForProjection(statement StatementType, out *SQLBuilder) {
if statement.IsSelectJSON() {
panic("jet: expression need to be aliased when used as SELECT JSON projection.")
}
e.Parent.serialize(statement, out, NoWrap)
}
func (e *ExpressionInterfaceImpl) serializeForJsonObj(statement StatementType, out *SQLBuilder) {
func (e *ExpressionInterfaceImpl) serializeForJsonObjEntry(statement StatementType, out *SQLBuilder) {
panic("jet: expression need to be aliased when used as SELECT JSON projection.")
}
func (e *ExpressionInterfaceImpl) serializeForRowToJsonProjection(statement StatementType, out *SQLBuilder) {
panic("jet: expression need to be aliased when used as SELECT JSON projection.")
}
func (e *ExpressionInterfaceImpl) serializeForJsonValue(statement StatementType, out *SQLBuilder) {
out.Dialect.JsonValueEncode(e.Parent).serialize(statement, out)
}
func (e *ExpressionInterfaceImpl) serializeForOrderBy(statement StatementType, out *SQLBuilder) {
e.Parent.serialize(statement, out, NoWrap)
}

View file

@ -102,9 +102,10 @@ type floatExpressionWrapper struct {
}
func newFloatExpressionWrap(expression Expression) FloatExpression {
floatExpressionWrap := floatExpressionWrapper{Expression: expression}
floatExpressionWrap.floatInterfaceImpl.parent = &floatExpressionWrap
return &floatExpressionWrap
floatExpressionWrap := &floatExpressionWrapper{Expression: expression}
floatExpressionWrap.floatInterfaceImpl.parent = floatExpressionWrap
expression.setParent(floatExpressionWrap)
return floatExpressionWrap
}
// FloatExp is date expression wrapper around arbitrary expression.

View file

@ -141,11 +141,11 @@ type integerExpressionWrapper struct {
}
func newIntExpressionWrap(expression Expression) IntegerExpression {
intExpressionWrap := integerExpressionWrapper{Expression: expression}
intExpressionWrap := &integerExpressionWrapper{Expression: expression}
intExpressionWrap.integerInterfaceImpl.parent = intExpressionWrap
expression.setParent(intExpressionWrap)
intExpressionWrap.integerInterfaceImpl.parent = &intExpressionWrap
return &intExpressionWrap
return intExpressionWrap
}
// IntExp is int expression wrapper around arbitrary expression.

View file

@ -1,37 +0,0 @@
package jet
// Interval is internal common representation of sql interval
type Interval interface {
Serializer
IsInterval
}
// IsInterval interface
type IsInterval interface {
isInterval()
}
// IsIntervalImpl is implementation of IsInterval interface
type IsIntervalImpl struct{}
func (i *IsIntervalImpl) isInterval() {}
// NewInterval creates new interval from serializer
func NewInterval(s Serializer) *IntervalImpl {
newInterval := &IntervalImpl{
Value: s,
}
return newInterval
}
// IntervalImpl is implementation of Interval type
type IntervalImpl struct {
Value Serializer
IsIntervalImpl
}
func (i IntervalImpl) serialize(statement StatementType, out *SQLBuilder, options ...SerializeOption) {
out.WriteString("INTERVAL")
i.Value.serialize(statement, out, FallTrough(options)...)
}

View file

@ -0,0 +1,112 @@
package jet
// IntervalExpression interface
type IntervalExpression interface {
Expression
isInterval()
EQ(rhs IntervalExpression) BoolExpression
NOT_EQ(rhs IntervalExpression) BoolExpression
IS_DISTINCT_FROM(rhs IntervalExpression) BoolExpression
IS_NOT_DISTINCT_FROM(rhs IntervalExpression) BoolExpression
LT(rhs IntervalExpression) BoolExpression
LT_EQ(rhs IntervalExpression) BoolExpression
GT(rhs IntervalExpression) BoolExpression
GT_EQ(rhs IntervalExpression) BoolExpression
BETWEEN(min, max IntervalExpression) BoolExpression
NOT_BETWEEN(min, max IntervalExpression) BoolExpression
ADD(rhs IntervalExpression) IntervalExpression
SUB(rhs IntervalExpression) IntervalExpression
MUL(rhs NumericExpression) IntervalExpression
DIV(rhs NumericExpression) IntervalExpression
}
type intervalInterfaceImpl struct {
parent IntervalExpression
}
func (i *intervalInterfaceImpl) isInterval() {}
func (i *intervalInterfaceImpl) EQ(rhs IntervalExpression) BoolExpression {
return Eq(i.parent, rhs)
}
func (i *intervalInterfaceImpl) NOT_EQ(rhs IntervalExpression) BoolExpression {
return NotEq(i.parent, rhs)
}
func (i *intervalInterfaceImpl) IS_DISTINCT_FROM(rhs IntervalExpression) BoolExpression {
return IsDistinctFrom(i.parent, rhs)
}
func (i *intervalInterfaceImpl) IS_NOT_DISTINCT_FROM(rhs IntervalExpression) BoolExpression {
return IsNotDistinctFrom(i.parent, rhs)
}
func (i *intervalInterfaceImpl) LT(rhs IntervalExpression) BoolExpression {
return Lt(i.parent, rhs)
}
func (i *intervalInterfaceImpl) LT_EQ(rhs IntervalExpression) BoolExpression {
return LtEq(i.parent, rhs)
}
func (i *intervalInterfaceImpl) GT(rhs IntervalExpression) BoolExpression {
return Gt(i.parent, rhs)
}
func (i *intervalInterfaceImpl) GT_EQ(rhs IntervalExpression) BoolExpression {
return GtEq(i.parent, rhs)
}
func (i *intervalInterfaceImpl) BETWEEN(min, max IntervalExpression) BoolExpression {
return NewBetweenOperatorExpression(i.parent, min, max, false)
}
func (i *intervalInterfaceImpl) NOT_BETWEEN(min, max IntervalExpression) BoolExpression {
return NewBetweenOperatorExpression(i.parent, min, max, true)
}
func (i *intervalInterfaceImpl) ADD(rhs IntervalExpression) IntervalExpression {
return IntervalExp(Add(i.parent, rhs))
}
func (i *intervalInterfaceImpl) SUB(rhs IntervalExpression) IntervalExpression {
return IntervalExp(Sub(i.parent, rhs))
}
func (i *intervalInterfaceImpl) MUL(rhs NumericExpression) IntervalExpression {
return IntervalExp(Mul(i.parent, rhs))
}
func (i *intervalInterfaceImpl) DIV(rhs NumericExpression) IntervalExpression {
return IntervalExp(Div(i.parent, rhs))
}
type intervalWrapper struct {
intervalInterfaceImpl
Expression
}
func newIntervalExpressionWrap(expression Expression) IntervalExpression {
intervalWrap := &intervalWrapper{Expression: expression}
intervalWrap.intervalInterfaceImpl.parent = intervalWrap
expression.setParent(intervalWrap)
return intervalWrap
}
// IntervalExp is interval expression wrapper around arbitrary expression.
// Allows go compiler to see any expression as interval expression.
// Does not add sql cast to generated sql builder output.
func IntervalExp(expression Expression) IntervalExpression {
return newIntervalExpressionWrap(expression)
}
// Interval interface
type Interval interface {
Serializer
isInterval()
}

View file

@ -412,17 +412,6 @@ func Raw(raw string, namedArgs ...map[string]interface{}) Expression {
return rawExp
}
// RawWithParent is a Raw constructor used for construction dialect specific expression
func RawWithParent(raw string, parent ...Expression) Expression {
rawExp := &rawExpression{
Raw: raw,
noWrap: true,
}
rawExp.ExpressionInterfaceImpl.Parent = OptionalOrDefaultExpression(rawExp, parent...)
return rawExp
}
// RawBool helper that for raw string boolean expressions
func RawBool(raw string, namedArgs ...map[string]interface{}) BoolExpression {
return BoolExp(Raw(raw, namedArgs...))

View file

@ -3,7 +3,8 @@ package jet
// Projection is interface for all projection types. Types that can be part of, for instance SELECT clause.
type Projection interface {
serializeForProjection(statement StatementType, out *SQLBuilder)
serializeForJsonObj(statement StatementType, out *SQLBuilder)
serializeForJsonObjEntry(statement StatementType, out *SQLBuilder)
serializeForRowToJsonProjection(statement StatementType, out *SQLBuilder)
fromImpl(subQuery SelectTable) Projection
}
@ -29,7 +30,7 @@ func (pl ProjectionList) serializeForProjection(statement StatementType, out *SQ
SerializeProjectionList(statement, pl, out)
}
func (pl ProjectionList) serializeForJsonObj(statement StatementType, out *SQLBuilder) {
func (pl ProjectionList) serializeForJsonObjEntry(statement StatementType, out *SQLBuilder) {
SerializeProjectionListJsonObj(statement, pl, out)
}
@ -85,9 +86,17 @@ func (pl ProjectionList) Except(toExclude ...Column) ProjectionList {
return ret
}
// JsonProjectionList redefines []Projection so projections can be serialized as json object key/values
type JsonProjectionList []Projection
func (j JsonProjectionList) serialize(statement StatementType, out *SQLBuilder, options ...SerializeOption) {
SerializeProjectionListJsonObj(statement, j, out)
func (pl ProjectionList) serializeForRowToJsonProjection(statement StatementType, out *SQLBuilder) {
out.WriteRowToJsonProjections(statement, pl)
}
// JsonObjProjectionList redefines []Projection so projections can be serialized as json object key/values
type JsonObjProjectionList []Projection
func (j JsonObjProjectionList) serialize(statement StatementType, out *SQLBuilder, options ...SerializeOption) {
out.IncreaseIdent()
out.NewLine()
SerializeProjectionListJsonObj(statement, j, out)
out.DecreaseIdent()
out.NewLine()
}

View file

@ -118,9 +118,10 @@ type rangeExpressionWrapper[T Expression] struct {
}
func newRangeExpressionWrap[T Expression](expression Expression) Range[T] {
rangeExpressionWrap := rangeExpressionWrapper[T]{Expression: expression}
rangeExpressionWrap.rangeInterfaceImpl.parent = &rangeExpressionWrap
return &rangeExpressionWrap
rangeExpressionWrap := &rangeExpressionWrapper[T]{Expression: expression}
rangeExpressionWrap.rangeInterfaceImpl.parent = rangeExpressionWrap
expression.setParent(rangeExpressionWrap)
return rangeExpressionWrap
}
// RangeExp is range expression wrapper around arbitrary expression.

View file

@ -17,9 +17,9 @@ type RowExpression interface {
}
type rowInterfaceImpl struct {
parent Expression
dialect Dialect
elemCount int
parent Expression
dialect Dialect
expressions []Expression
}
func (n *rowInterfaceImpl) EQ(rhs RowExpression) BoolExpression {
@ -57,9 +57,8 @@ func (n *rowInterfaceImpl) LT_EQ(rhs RowExpression) BoolExpression {
func (n *rowInterfaceImpl) projections() ProjectionList {
var ret ProjectionList
for i := 0; i < n.elemCount; i++ {
rowColumn := NewColumnImpl(n.dialect.ValuesDefaultColumnName(i), "", nil)
ret = append(ret, &rowColumn)
for i, expression := range n.expressions {
ret = append(ret, newDummyColumnForExpression(expression, n.dialect.ValuesDefaultColumnName(i)))
}
return ret
@ -77,7 +76,7 @@ func newRowExpression(name string, dialect Dialect, expressions ...Expression) R
ret.Expression = NewFunc(name, expressions, ret)
ret.dialect = dialect
ret.elemCount = len(expressions)
ret.expressions = expressions
return ret
}

View file

@ -22,10 +22,6 @@ func (s SerializeOption) WithFallTrough(options []SerializeOption) []SerializeOp
// StatementType is type of the SQL statement
type StatementType string
func (s StatementType) IsSelectJSON() bool {
return s == SelectJsonObjStatementType || s == SelectJsonArrStatementType
}
// Statement types
const (
SelectStatementType StatementType = "SELECT"

View file

@ -61,6 +61,16 @@ func (s *SQLBuilder) WriteProjections(statement StatementType, projections []Pro
s.DecreaseIdent()
}
func (s *SQLBuilder) WriteRowToJsonProjections(statement StatementType, projections []Projection) {
for i, projection := range projections {
if i > 0 {
s.WriteString(",")
s.NewLine()
}
projection.serializeForRowToJsonProjection(statement, s)
}
}
// NewLine adds new line to output SQL
func (s *SQLBuilder) NewLine() {
s.write([]byte{'\n'})

View file

@ -257,12 +257,13 @@ type expressionStatementImpl struct {
}
func (s *expressionStatementImpl) serializeForProjection(statement StatementType, out *SQLBuilder) {
if statement.IsSelectJSON() {
panic("jet: SELECT JSON statements need to be aliased when used as a projection.")
}
s.serialize(statement, out)
}
func (e *expressionStatementImpl) serializeForRowToJsonProjection(statement StatementType, out *SQLBuilder) {
panic("jet: SELECT JSON statements need to be aliased when used as a projection.")
}
// NewStatementImpl creates new statementImpl
func NewStatementImpl(Dialect Dialect, statementType StatementType, parent SerializerStatement, clauses ...Clause) SerializerStatement {
return &statementImpl{

View file

@ -105,9 +105,10 @@ type stringExpressionWrapper struct {
}
func newStringExpressionWrap(expression Expression) StringExpression {
stringExpressionWrap := stringExpressionWrapper{Expression: expression}
stringExpressionWrap.stringInterfaceImpl.parent = &stringExpressionWrap
return &stringExpressionWrap
stringExpressionWrap := &stringExpressionWrapper{Expression: expression}
stringExpressionWrap.stringInterfaceImpl.parent = stringExpressionWrap
expression.setParent(stringExpressionWrap)
return stringExpressionWrap
}
// StringExp is string expression wrapper around arbitrary expression.

View file

@ -75,14 +75,15 @@ func (t *timeInterfaceImpl) SUB(rhs Interval) TimeExpression {
//---------------------------------------------------//
type timeExpressionWrapper struct {
timeInterfaceImpl
Expression
timeInterfaceImpl
}
func newTimeExpressionWrap(expression Expression) TimeExpression {
timeExpressionWrap := timeExpressionWrapper{Expression: expression}
timeExpressionWrap.timeInterfaceImpl.parent = &timeExpressionWrap
return &timeExpressionWrap
timeExpressionWrap := &timeExpressionWrapper{Expression: expression}
timeExpressionWrap.timeInterfaceImpl.parent = timeExpressionWrap
expression.setParent(timeExpressionWrap)
return timeExpressionWrap
}
// TimeExp is time expression wrapper around arbitrary expression.

View file

@ -52,11 +52,3 @@ func TestTimeExp(t *testing.T) {
assertClauseSerialize(t, TimeExp(table1ColFloat).LT(Time(1, 1, 1, 1*time.Millisecond)),
"(table1.col_float < $1)", string("01:01:01.001"))
}
func TestTimeArithmetic(t *testing.T) {
time := Time(10, 20, 3)
assertClauseDebugSerialize(t, table1ColTime.ADD(NewInterval(String("1 HOUR"))).EQ(time),
"((table1.col_time + INTERVAL '1 HOUR') = '10:20:03')")
assertClauseDebugSerialize(t, table1ColTime.SUB(NewInterval(String("1 HOUR"))).EQ(time),
"((table1.col_time - INTERVAL '1 HOUR') = '10:20:03')")
}

View file

@ -80,9 +80,10 @@ type timestampExpressionWrapper struct {
}
func newTimestampExpressionWrap(expression Expression) TimestampExpression {
timestampExpressionWrap := timestampExpressionWrapper{Expression: expression}
timestampExpressionWrap.timestampInterfaceImpl.parent = &timestampExpressionWrap
return &timestampExpressionWrap
timestampExpressionWrap := &timestampExpressionWrapper{Expression: expression}
timestampExpressionWrap.timestampInterfaceImpl.parent = timestampExpressionWrap
expression.setParent(timestampExpressionWrap)
return timestampExpressionWrap
}
// TimestampExp is timestamp expression wrapper around arbitrary expression.

View file

@ -53,11 +53,3 @@ func TestTimestampExp(t *testing.T) {
assertClauseSerialize(t, TimestampExp(table1ColFloat).LT(timestamp),
"(table1.col_float < $1)", "2000-01-31 10:20:00.003")
}
func TestTimestampArithmetic(t *testing.T) {
timestamp := Timestamp(2000, 1, 1, 0, 0, 0)
assertClauseDebugSerialize(t, table1ColTimestamp.ADD(NewInterval(String("1 HOUR"))).EQ(timestamp),
"((table1.col_timestamp + INTERVAL '1 HOUR') = '2000-01-01 00:00:00')")
assertClauseDebugSerialize(t, table1ColTimestamp.SUB(NewInterval(String("1 HOUR"))).EQ(timestamp),
"((table1.col_timestamp - INTERVAL '1 HOUR') = '2000-01-01 00:00:00')")
}

View file

@ -80,9 +80,10 @@ type timestampzExpressionWrapper struct {
}
func newTimestampzExpressionWrap(expression Expression) TimestampzExpression {
timestampzExpressionWrap := timestampzExpressionWrapper{Expression: expression}
timestampzExpressionWrap.timestampzInterfaceImpl.parent = &timestampzExpressionWrap
return &timestampzExpressionWrap
timestampzExpressionWrap := &timestampzExpressionWrapper{Expression: expression}
timestampzExpressionWrap.timestampzInterfaceImpl.parent = timestampzExpressionWrap
expression.setParent(timestampzExpressionWrap)
return timestampzExpressionWrap
}
// TimestampzExp is timestamp with time zone expression wrapper around arbitrary expression.

View file

@ -53,11 +53,3 @@ func TestTimestampzExp(t *testing.T) {
assertClauseSerialize(t, TimestampzExp(table1ColFloat).LT(timestampz),
"(table1.col_float < $1)", "2000-01-31 10:20:05.000023 +200")
}
func TestTimestampzArithmetic(t *testing.T) {
timestampz := Timestampz(2000, 1, 1, 0, 0, 0, 100, "UTC")
assertClauseDebugSerialize(t, table1ColTimestampz.ADD(NewInterval(String("1 HOUR"))).EQ(timestampz),
"((table1.col_timestampz + INTERVAL '1 HOUR') = '2000-01-01 00:00:00.0000001 UTC')")
assertClauseDebugSerialize(t, table1ColTimestampz.SUB(NewInterval(String("1 HOUR"))).EQ(timestampz),
"((table1.col_timestampz - INTERVAL '1 HOUR') = '2000-01-01 00:00:00.0000001 UTC')")
}

View file

@ -75,14 +75,15 @@ func (t *timezInterfaceImpl) SUB(rhs Interval) TimezExpression {
//---------------------------------------------------//
type timezExpressionWrapper struct {
timezInterfaceImpl
Expression
timezInterfaceImpl
}
func newTimezExpressionWrap(expression Expression) TimezExpression {
timezExpressionWrap := timezExpressionWrapper{Expression: expression}
timezExpressionWrap.timezInterfaceImpl.parent = &timezExpressionWrap
return &timezExpressionWrap
timezExpressionWrap := &timezExpressionWrapper{Expression: expression}
timezExpressionWrap.timezInterfaceImpl.parent = timezExpressionWrap
expression.setParent(timezExpressionWrap)
return timezExpressionWrap
}
// TimezExp is time with time zone expression wrapper around arbitrary expression.

View file

@ -51,11 +51,3 @@ func TestTimezExp(t *testing.T) {
assertClauseSerialize(t, TimezExp(table1ColFloat).LT(Timez(1, 1, 1, 1, "+4:00")),
"(table1.col_float < $1)", string("01:01:01.000000001 +4:00"))
}
func TestTimezArithmetic(t *testing.T) {
timez := Timez(0, 0, 0, 100, "UTC")
assertClauseDebugSerialize(t, table1ColTimez.ADD(NewInterval(String("1 HOUR"))).EQ(timez),
"((table1.col_timez + INTERVAL '1 HOUR') = '00:00:00.0000001 UTC')")
assertClauseDebugSerialize(t, table1ColTimez.SUB(NewInterval(String("1 HOUR"))).EQ(timez),
"((table1.col_timez - INTERVAL '1 HOUR') = '00:00:00.0000001 UTC')")
}

View file

@ -71,7 +71,7 @@ func SerializeProjectionListJsonObj(statement StatementType, projections []Proje
panic("jet: Projection is nil")
}
p.serializeForJsonObj(statement, out)
p.serializeForJsonObjEntry(statement, out)
}
}

View file

@ -122,7 +122,7 @@ func AssertJsonEqual(t require.TestingT, actual, expected interface{}, option ..
expectedJsonData, err := json.MarshalIndent(expected, "", "\t")
require.NoError(t, err)
require.Equal(t, actualJsonData, expectedJsonData)
require.Equal(t, string(actualJsonData), string(expectedJsonData))
}
// SaveJSONFile saves v as json at testRelativePath