Dialect refactor improvements and clean up.

This commit is contained in:
go-jet 2019-08-06 10:29:04 +02:00
parent 23fd973699
commit 647ef21aaf
52 changed files with 1097 additions and 671 deletions

View file

@ -9,6 +9,7 @@ type cast interface {
jet.Cast
// Cast expression AS bool type
AS_BOOL() BoolExpression
// Cast expression AS smallint type
AS_SMALLINT() IntegerExpression
// Cast expression AS integer type
@ -16,7 +17,7 @@ type cast interface {
// Cast expression AS bigint type
AS_BIGINT() IntegerExpression
// Cast expression AS numeric type, using precision and optionally scale
AS_NUMERIC(precision int, scale ...int) FloatExpression
AS_NUMERIC(precisionAndScale ...int) FloatExpression
// Cast expression AS real type
AS_REAL() FloatExpression
@ -25,6 +26,8 @@ type cast interface {
// Cast expression AS text type
AS_TEXT() StringExpression
AS_BYTEA() StringExpression
// Cast expression AS time with time timezone type
AS_TIMEZ() TimezExpression
// Cast expression AS timestamp type
@ -64,16 +67,17 @@ func (b *castImpl) AS_BIGINT() IntegerExpression {
}
// Cast expression AS numeric type, using precision and optionally scale
func (b *castImpl) AS_NUMERIC(precision int, scale ...int) FloatExpression {
var castType string
func (b *castImpl) AS_NUMERIC(precisionAndScale ...int) FloatExpression {
var castArgs string
if len(scale) > 0 {
castType = fmt.Sprintf("numeric(%d, %d)", precision, scale[0])
} else {
castType = fmt.Sprintf("numeric(%d)", precision)
var argLen = len(precisionAndScale)
if argLen >= 2 {
castArgs = fmt.Sprintf("(%d, %d)", precisionAndScale[0], precisionAndScale[1])
} else if argLen == 1 {
castArgs = fmt.Sprintf("(%d)", precisionAndScale[0])
}
return jet.FloatExp(b.AS(castType))
return jet.FloatExp(b.AS("numeric" + castArgs))
}
// Cast expression AS real type
@ -91,6 +95,11 @@ func (b *castImpl) AS_TEXT() StringExpression {
return jet.StringExp(b.AS("text"))
}
// Cast expression AS text type
func (b *castImpl) AS_BYTEA() StringExpression {
return jet.StringExp(b.AS("bytea"))
}
// Cast expression AS date type
func (b *castImpl) AS_TIME() jet.TimeExpression {
return TimeExp(b.AS("time without time zone"))

View file

@ -2,62 +2,44 @@ package postgres
import "github.com/go-jet/jet/internal/jet"
type Column jet.Column
type IColumnList jet.IColumnList
var ColumnList = jet.ColumnList
type ColumnBool jet.ColumnBool
type BoolExpression jet.BoolExpression
var BoolColumn = jet.BoolColumn
type ColumnString jet.ColumnString
type StringExpression jet.StringExpression
var StringColumn = jet.StringColumn
type ColumnInteger jet.ColumnInteger
type IntegerExpression jet.IntegerExpression
var IntegerColumn = jet.IntegerColumn
type ColumnFloat jet.ColumnFloat
type FloatExpression jet.FloatExpression
var FloatColumn = jet.FloatColumn
var FloatExp = jet.FloatExp
type ColumnDate jet.ColumnDate
type DateExpression jet.DateExpression
var DateColumn = jet.DateColumn
type ColumnDateTime jet.ColumnTimestamp
type DateTimeExpression jet.TimestampExpression
var DateTimeColumn = jet.TimestampColumn
type TimeExpression jet.TimeExpression
type ColumnTime jet.ColumnTime
var TimeColumn = jet.TimeColumn
var TimeExp = jet.TimeExp
type TimezExpression jet.TimezExpression
type ColumnTimez jet.ColumnTimez
var TimezColumn = jet.TimezColumn
type ColumnTimestamp jet.ColumnTimestamp
type TimestampExpression jet.TimestampExpression
var TimestampColumn = jet.TimestampColumn
var TimestampExp = jet.TimestampExp
type TimestampzExpression jet.TimestampzExpression
type ColumnTimestampz jet.ColumnTimestampz
var TimestampzColumn = jet.TimestampzColumn
type SelectTable jet.SelectTable
// ---------------- statements -----------------//

View file

@ -1,24 +1,30 @@
package postgres
import (
"errors"
"github.com/go-jet/jet/internal/jet"
"strconv"
"strings"
)
var Dialect = NewDialect()
func NewDialect() jet.Dialect {
serializeOverrides := map[string]jet.SerializeOverride{}
serializeOverrides["REGEXP_LIKE"] = postgres_REGEXP_LIKE_function
dialectParams := jet.DialectParams{
Name: "PostgreSQL",
PackageName: "postgres",
CastOverride: castFunc,
SerializeOverrides: serializeOverrides,
AliasQuoteChar: '"',
IdentifierQuoteChar: '"',
ArgumentPlaceholder: func(ord int) string {
return "$" + strconv.Itoa(ord)
},
UpdateAssigment: postgresUpdateAssigment,
SetClause: postgresSetClause,
SupportsReturning: true,
}
@ -35,7 +41,7 @@ func castFunc(expression jet.Expression, castType string) jet.SerializeFunc {
}
}
func postgresUpdateAssigment(columns []jet.IColumn, values []jet.Clause, out *jet.SqlBuilder) (err error) {
func postgresSetClause(columns []jet.IColumn, values []jet.Clause, out *jet.SqlBuilder) (err error) {
if len(columns) > 1 {
out.WriteString("(")
}
@ -68,3 +74,37 @@ func postgresUpdateAssigment(columns []jet.IColumn, values []jet.Clause, out *je
return
}
func postgres_REGEXP_LIKE_function(expressions ...jet.Expression) jet.SerializeFunc {
return func(statement jet.StatementType, out *jet.SqlBuilder, options ...jet.SerializeOption) error {
if len(expressions) < 2 {
return errors.New("jet: invalid number of expressions for operator")
}
if err := jet.Serialize(expressions[0], statement, out, options...); err != nil {
return err
}
caseSensitive := false
if len(expressions) >= 3 {
if stringLiteral, ok := expressions[2].(jet.LiteralExpression); ok {
matchType := stringLiteral.Value().(string)
caseSensitive = !strings.Contains(matchType, "i")
}
}
if caseSensitive {
out.WriteString("~")
} else {
out.WriteString("~*")
}
if err := jet.Serialize(expressions[1], statement, out, options...); err != nil {
return err
}
return nil
}
}

15
postgres/dialect_test.go Normal file
View file

@ -0,0 +1,15 @@
package postgres
import "testing"
func TestString_REGEXP_LIKE_operator(t *testing.T) {
assertClauseSerialize(t, table3StrCol.REGEXP_LIKE(table2ColStr), "table3.col2 ~* table2.col_str")
assertClauseSerialize(t, table3StrCol.REGEXP_LIKE(String("JOHN"), "c"), "table3.col2 ~ $1", "JOHN")
assertClauseSerialize(t, table3StrCol.REGEXP_LIKE(String("JOHN"), "i"), "table3.col2 ~* $1", "JOHN")
}
func TestString_REGEXP_LIKE_function(t *testing.T) {
assertClauseSerialize(t, REGEXP_LIKE(table3StrCol, table2ColStr), "table3.col2 ~* table2.col_str")
assertClauseSerialize(t, REGEXP_LIKE(table3StrCol, String("JOHN"), "c"), "table3.col2 ~ $1", "JOHN")
assertClauseSerialize(t, REGEXP_LIKE(table3StrCol, String("JOHN"), "i"), "table3.col2 ~* $1", "JOHN")
}

View file

@ -3,3 +3,34 @@ package postgres
import "github.com/go-jet/jet/internal/jet"
type Expression jet.Expression
type BoolExpression jet.BoolExpression
type StringExpression jet.StringExpression
type IntegerExpression jet.IntegerExpression
type FloatExpression jet.FloatExpression
type TimeExpression jet.TimeExpression
type TimezExpression jet.TimezExpression
type DateExpression jet.DateExpression
type TimestampExpression jet.TimestampExpression
type TimestampzExpression jet.TimestampzExpression
var BoolExp = jet.BoolExp
var IntExp = jet.IntExp
var FloatExp = jet.FloatExp
var TimeExp = jet.TimeExp
var TimezExp = jet.TimezExp
var DateExp = jet.DateExp
var TimestampExp = jet.TimestampExp
var TimestampzExp = jet.TimestampzExp
var RAW = jet.RAW
var NewEnumValue = jet.NewEnumValue

View file

@ -38,6 +38,7 @@ var SUMi = jet.SUMi
//--------------------- String functions ------------------//
var REGEXP_LIKE = jet.REGEXP_LIKE
var BIT_LENGTH = jet.BIT_LENGTH
var CHAR_LENGTH = jet.CHAR_LENGTH
var OCTET_LENGTH = jet.OCTET_LENGTH
@ -47,11 +48,25 @@ var BTRIM = jet.BTRIM
var LTRIM = jet.LTRIM
var RTRIM = jet.RTRIM
var CHR = jet.CHR
var CONCAT = func(expressions ...Expression) StringExpression {
return jet.CONCAT(explicitCasts(expressions...)...)
}
func CONCAT_WS(expressions ...Expression) StringExpression {
return jet.CONCAT_WS(explicitCasts(expressions...)...)
}
var CONVERT = jet.CONVERT
var CONVERT_FROM = jet.CONVERT_FROM
var CONVERT_TO = jet.CONVERT_TO
var ENCODE = jet.ENCODE
var DECODE = jet.DECODE
func FORMAT(formatStr StringExpression, formatArgs ...Expression) StringExpression {
return jet.FORMAT(formatStr, explicitCasts(formatArgs...)...)
}
var INITCAP = jet.INITCAP
var LEFT = jet.LEFT
var RIGHT = jet.RIGHT
@ -91,3 +106,32 @@ var GREATEST = jet.GREATEST
var LEAST = jet.LEAST
var EXISTS = jet.EXISTS
var CASE = jet.CASE
func explicitCasts(expressions ...Expression) []jet.Expression {
ret := []jet.Expression{}
for _, exp := range expressions {
ret = append(ret, explicitCast(exp))
}
return ret
}
func explicitCast(expresion Expression) jet.Expression {
if _, ok := expresion.(jet.LiteralExpression); !ok {
return expresion
}
switch expresion.(type) {
case jet.BoolExpression:
return CAST(expresion).AS_BOOL()
case jet.IntegerExpression:
return CAST(expresion).AS_INTEGER()
case jet.FloatExpression:
return CAST(expresion).AS_NUMERIC()
case jet.StringExpression:
return CAST(expresion).AS_TEXT()
}
return expresion
}

View file

@ -1,24 +1,32 @@
package postgres
import "github.com/go-jet/jet/internal/jet"
import (
"github.com/go-jet/jet/internal/jet"
"time"
)
var Bool = jet.Bool
var Int = jet.Int
var Float = jet.Float
var String = jet.String
var Date = func(year, month, day int) DateExpression {
var Bytea = func(value string) StringExpression {
return CAST(jet.String(value)).AS_BYTEA()
}
var Date = func(year int, month time.Month, day int) DateExpression {
return CAST(jet.Date(year, month, day)).AS_DATE()
}
var Time = func(hour, minute, second, milliseconds int) TimeExpression {
return CAST(jet.Time(hour, minute, second, milliseconds)).AS_TIME()
var Time = func(hour, minute, second int, milliseconds ...int) TimeExpression {
return CAST(jet.Time(hour, minute, second, milliseconds...)).AS_TIME()
}
var Timez = func(hour, minute, second, milliseconds int, timezone int) TimezExpression {
return CAST(jet.Timez(hour, minute, second, milliseconds, timezone)).AS_TIMEZ()
}
var Timestamp = func(year, month, day, hour, minute, second, milliseconds int) TimestampExpression {
var Timestamp = func(year int, month time.Month, day, hour, minute, second, milliseconds int) TimestampExpression {
return CAST(jet.Timestamp(year, month, day, hour, minute, second, milliseconds)).AS_TIMESTAMP()
}

7
postgres/literal_test.go Normal file
View file

@ -0,0 +1,7 @@
package postgres
import "testing"
func TestDateLiteral(t *testing.T) {
assertClauseSerialize(t, Date(2019, 8, 6), "$1::DATE", "2019-08-06")
}

View file

@ -4,10 +4,18 @@ import "github.com/go-jet/jet/internal/jet"
// --------- Arithmetic operators -------------//
var MINUSi = jet.MINUSi
//var MINUSi = jet.MINUSi
var MINUSf = jet.MINUSf
//----------- Logical operators ---------------//
var NOT = jet.NOT
var BIT_NOT = jet.BIT_NOT
func MINUSi(intExp IntegerExpression) IntegerExpression {
if intLit, ok := intExp.(jet.LiteralExpression); ok {
intLit.SetConstant(true)
}
return intExp
}

View file

@ -1,9 +0,0 @@
package postgres
import "github.com/go-jet/jet/internal/jet"
type Column jet.Column
type IColumnList jet.IColumnList
var ColumnList = jet.ColumnList

View file

@ -1,5 +0,0 @@
package postgres
import "github.com/go-jet/jet/internal/jet"
var NewEnumValue = jet.NewEnumValue

View file

@ -1,5 +0,0 @@
package postgres
import "github.com/go-jet/jet/internal/jet"
var RAW = jet.RAW

View file

@ -3,6 +3,15 @@ package postgres
import "github.com/go-jet/jet/internal/jet"
type SelectStatement jet.SelectStatement
type SelectTable jet.SelectTable
type SelectLock jet.SelectLock
var (
UPDATE = jet.NewSelectLock("UPDATE")
NO_KEY_UPDATE = jet.NewSelectLock("NO KEY UPDATE")
SHARE = jet.NewSelectLock("SHARE")
KEY_SHARE = jet.NewSelectLock("KEY SHARE")
)
var SELECT = jet.SELECT
@ -31,12 +40,3 @@ func toJetSelects(selects ...SelectStatement) []jet.SelectStatement {
return ret
}
type SelectLock jet.SelectLock
var (
UPDATE = jet.NewSelectLock("UPDATE")
NO_KEY_UPDATE = jet.NewSelectLock("NO KEY UPDATE")
SHARE = jet.NewSelectLock("SHARE")
KEY_SHARE = jet.NewSelectLock("KEY SHARE")
)

View file

@ -1,53 +0,0 @@
package postgres
import (
"testing"
)
var timeVar = Time(10, 20, 0, 0)
func TestTimeExpressionEQ(t *testing.T) {
assertClauseSerialize(t, table1ColTime.EQ(table2ColTime), "(table1.col_time = table2.col_time)")
assertClauseSerialize(t, table1ColTime.EQ(timeVar), "(table1.col_time = $1::time without time zone)", "10:20:00.000")
}
func TestTimeExpressionNOT_EQ(t *testing.T) {
assertClauseSerialize(t, table1ColTime.NOT_EQ(table2ColTime), "(table1.col_time != table2.col_time)")
assertClauseSerialize(t, table1ColTime.NOT_EQ(timeVar), "(table1.col_time != $1::time without time zone)", "10:20:00.000")
}
func TestTimeExpressionIS_DISTINCT_FROM(t *testing.T) {
assertClauseSerialize(t, table1ColTime.IS_DISTINCT_FROM(table2ColTime), "(table1.col_time IS DISTINCT FROM table2.col_time)")
assertClauseSerialize(t, table1ColTime.IS_DISTINCT_FROM(timeVar), "(table1.col_time IS DISTINCT FROM $1::time without time zone)", "10:20:00.000")
}
func TestTimeExpressionIS_NOT_DISTINCT_FROM(t *testing.T) {
assertClauseSerialize(t, table1ColTime.IS_NOT_DISTINCT_FROM(table2ColTime), "(table1.col_time IS NOT DISTINCT FROM table2.col_time)")
assertClauseSerialize(t, table1ColTime.IS_NOT_DISTINCT_FROM(timeVar), "(table1.col_time IS NOT DISTINCT FROM $1::time without time zone)", "10:20:00.000")
}
func TestTimeExpressionLT(t *testing.T) {
assertClauseSerialize(t, table1ColTime.LT(table2ColTime), "(table1.col_time < table2.col_time)")
assertClauseSerialize(t, table1ColTime.LT(timeVar), "(table1.col_time < $1::time without time zone)", "10:20:00.000")
}
func TestTimeExpressionLT_EQ(t *testing.T) {
assertClauseSerialize(t, table1ColTime.LT_EQ(table2ColTime), "(table1.col_time <= table2.col_time)")
assertClauseSerialize(t, table1ColTime.LT_EQ(timeVar), "(table1.col_time <= $1::time without time zone)", "10:20:00.000")
}
func TestTimeExpressionGT(t *testing.T) {
assertClauseSerialize(t, table1ColTime.GT(table2ColTime), "(table1.col_time > table2.col_time)")
assertClauseSerialize(t, table1ColTime.GT(timeVar), "(table1.col_time > $1::time without time zone)", "10:20:00.000")
}
func TestTimeExpressionGT_EQ(t *testing.T) {
assertClauseSerialize(t, table1ColTime.GT_EQ(table2ColTime), "(table1.col_time >= table2.col_time)")
assertClauseSerialize(t, table1ColTime.GT_EQ(timeVar), "(table1.col_time >= $1::time without time zone)", "10:20:00.000")
}
func TestTimeExp(t *testing.T) {
assertClauseSerialize(t, TimeExp(table1ColFloat), "table1.col_float")
assertClauseSerialize(t, TimeExp(table1ColFloat).LT(Time(1, 1, 1, 1)),
"(table1.col_float < $1::time without time zone)", string("01:01:01.001"))
}

View file

@ -1,54 +0,0 @@
package postgres
import (
"testing"
)
var timestamp = Timestamp(2000, 1, 31, 10, 20, 0, 0)
func TestTimestampExpressionEQ(t *testing.T) {
assertClauseSerialize(t, table1ColTimestamp.EQ(table2ColTimestamp), "(table1.col_timestamp = table2.col_timestamp)")
assertClauseSerialize(t, table1ColTimestamp.EQ(timestamp),
"(table1.col_timestamp = $1::timestamp without time zone)", "2000-01-31 10:20:00.000")
}
func TestTimestampExpressionNOT_EQ(t *testing.T) {
assertClauseSerialize(t, table1ColTimestamp.NOT_EQ(table2ColTimestamp), "(table1.col_timestamp != table2.col_timestamp)")
assertClauseSerialize(t, table1ColTimestamp.NOT_EQ(timestamp), "(table1.col_timestamp != $1::timestamp without time zone)", "2000-01-31 10:20:00.000")
}
func TestTimestampExpressionIS_DISTINCT_FROM(t *testing.T) {
assertClauseSerialize(t, table1ColTimestamp.IS_DISTINCT_FROM(table2ColTimestamp), "(table1.col_timestamp IS DISTINCT FROM table2.col_timestamp)")
assertClauseSerialize(t, table1ColTimestamp.IS_DISTINCT_FROM(timestamp), "(table1.col_timestamp IS DISTINCT FROM $1::timestamp without time zone)", "2000-01-31 10:20:00.000")
}
func TestTimestampExpressionIS_NOT_DISTINCT_FROM(t *testing.T) {
assertClauseSerialize(t, table1ColTimestamp.IS_NOT_DISTINCT_FROM(table2ColTimestamp), "(table1.col_timestamp IS NOT DISTINCT FROM table2.col_timestamp)")
assertClauseSerialize(t, table1ColTimestamp.IS_NOT_DISTINCT_FROM(timestamp), "(table1.col_timestamp IS NOT DISTINCT FROM $1::timestamp without time zone)", "2000-01-31 10:20:00.000")
}
func TestTimestampExpressionLT(t *testing.T) {
assertClauseSerialize(t, table1ColTimestamp.LT(table2ColTimestamp), "(table1.col_timestamp < table2.col_timestamp)")
assertClauseSerialize(t, table1ColTimestamp.LT(timestamp), "(table1.col_timestamp < $1::timestamp without time zone)", "2000-01-31 10:20:00.000")
}
func TestTimestampExpressionLT_EQ(t *testing.T) {
assertClauseSerialize(t, table1ColTimestamp.LT_EQ(table2ColTimestamp), "(table1.col_timestamp <= table2.col_timestamp)")
assertClauseSerialize(t, table1ColTimestamp.LT_EQ(timestamp), "(table1.col_timestamp <= $1::timestamp without time zone)", "2000-01-31 10:20:00.000")
}
func TestTimestampExpressionGT(t *testing.T) {
assertClauseSerialize(t, table1ColTimestamp.GT(table2ColTimestamp), "(table1.col_timestamp > table2.col_timestamp)")
assertClauseSerialize(t, table1ColTimestamp.GT(timestamp), "(table1.col_timestamp > $1::timestamp without time zone)", "2000-01-31 10:20:00.000")
}
func TestTimestampExpressionGT_EQ(t *testing.T) {
assertClauseSerialize(t, table1ColTimestamp.GT_EQ(table2ColTimestamp), "(table1.col_timestamp >= table2.col_timestamp)")
assertClauseSerialize(t, table1ColTimestamp.GT_EQ(timestamp), "(table1.col_timestamp >= $1::timestamp without time zone)", "2000-01-31 10:20:00.000")
}
func TestTimestampExp(t *testing.T) {
assertClauseSerialize(t, TimestampExp(table1ColFloat), "table1.col_float")
assertClauseSerialize(t, TimestampExp(table1ColFloat).LT(timestamp),
"(table1.col_float < $1::timestamp without time zone)", "2000-01-31 10:20:00.000")
}