Merge pull request #419 from go-jet/str-constr

Add PostgreSQL-specific character type constructors: Text, Char, and VarChar
This commit is contained in:
go-jet 2024-11-02 11:31:54 +01:00 committed by GitHub
commit f8f2f75a0d
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
13 changed files with 231 additions and 286 deletions

View file

@ -175,9 +175,9 @@ stmt := SELECT(
INNER_JOIN(FilmCategory, FilmCategory.FilmID.EQ(Film.FilmID)).
INNER_JOIN(Category, Category.CategoryID.EQ(FilmCategory.CategoryID)),
).WHERE(
Language.Name.EQ(String("English")).
AND(Category.Name.NOT_EQ(String("Action"))).
AND(Film.Length.GT(Int(180))),
Language.Name.EQ(Char(20)("English")).
AND(Category.Name.NOT_EQ(Text("Action"))).
AND(Film.Length.GT(Int32(180))),
).ORDER_BY(
Actor.ActorID.ASC(),
Film.FilmID.ASC(),
@ -228,7 +228,7 @@ FROM dvds.actor
INNER JOIN dvds.language ON (language.language_id = film.language_id)
INNER JOIN dvds.film_category ON (film_category.film_id = film.film_id)
INNER JOIN dvds.category ON (category.category_id = film_category.category_id)
WHERE ((language.name = $1) AND (category.name != $2)) AND (film.length > $3)
WHERE ((language.name = $1::char(20)) AND (category.name != $2::text)) AND (film.length > $3::integer)
ORDER BY actor.actor_id ASC, film.film_id ASC;
```
```sh
@ -277,7 +277,7 @@ FROM dvds.actor
INNER JOIN dvds.language ON (language.language_id = film.language_id)
INNER JOIN dvds.film_category ON (film_category.film_id = film.film_id)
INNER JOIN dvds.category ON (category.category_id = film_category.category_id)
WHERE ((language.name = 'English') AND (category.name != 'Action')) AND (film.length > 180)
WHERE ((language.name = 'English'::char(20)) AND (category.name != 'Action'::text)) AND (film.length > 180::integer)
ORDER BY actor.actor_id ASC, film.film_id ASC;
```
</details>
@ -545,18 +545,18 @@ The most expensive bugs are the one discovered on the production, and the least
With automatically generated type safe SQL, not only queries are written faster but bugs are found sooner.
Let's return to quick start example, and take closer look at a line:
```go
AND(Film.Length.GT(Int(180))),
AND(Film.Length.GT(Int32(180))),
```
Let's say someone changes column `length` to `duration` from `film` table. The next go build will fail at that line, and
the bug will be caught at compile time.
Let's say someone changes the type of `length` column to some non integer type. Build will also fail at the same line
Let's say someone changes the type of `length` column to some non-integer type. Build will also fail at the same line
because integer columns and expressions can be only compared to other integer columns and expressions.
Build will also fail if someone removes `length` column from `film` table. `Film` field will be omitted from SQL Builder and Model types,
next time `jet` generator is run.
Without Jet these bugs will have to be either caught by some test or by manual testing.
Without Jet these bugs will have to be either caught by tests or by manual testing.
## Dependencies
At the moment Jet dependence only of:

View file

@ -46,8 +46,8 @@ func main() {
INNER_JOIN(FilmCategory, FilmCategory.FilmID.EQ(Film.FilmID)).
INNER_JOIN(Category, Category.CategoryID.EQ(FilmCategory.CategoryID)),
).WHERE(
Language.Name.EQ(String("English")).
AND(Category.Name.NOT_EQ(String("Action"))).
Language.Name.EQ(Char(20)("English")).
AND(Category.Name.NOT_EQ(Text("Action"))).
AND(Film.Length.GT(Int(180))),
).ORDER_BY(
Actor.ActorID.ASC(),

8
go.mod
View file

@ -1,19 +1,21 @@
module github.com/go-jet/jet/v2
go 1.20
go 1.21
// used by jet generator
require (
github.com/go-sql-driver/mysql v1.8.1
github.com/google/go-cmp v0.6.0
github.com/google/uuid v1.6.0
github.com/jackc/pgconn v1.14.3
github.com/jackc/pgtype v1.14.3
github.com/jackc/pgtype v1.14.4
github.com/jackc/pgx/v4 v4.18.3
github.com/lib/pq v1.10.9
github.com/mattn/go-sqlite3 v1.14.24
)
// used in tests
require (
github.com/google/go-cmp v0.6.0
github.com/pkg/profile v1.7.0
github.com/shopspring/decimal v1.4.0
github.com/stretchr/testify v1.9.0

4
go.sum
View file

@ -71,8 +71,8 @@ github.com/jackc/pgtype v0.0.0-20190824184912-ab885b375b90/go.mod h1:KcahbBH1nCM
github.com/jackc/pgtype v0.0.0-20190828014616-a8802b16cc59/go.mod h1:MWlu30kVJrUS8lot6TQqcg7mtthZ9T0EoIBFiJcmcyw=
github.com/jackc/pgtype v1.8.1-0.20210724151600-32e20a603178/go.mod h1:C516IlIV9NKqfsMCXTdChteoXmwgUceqaLfjg2e3NlM=
github.com/jackc/pgtype v1.14.0/go.mod h1:LUMuVrfsFfdKGLw+AFFVv6KtHOFMwRgDDzBt76IqCA4=
github.com/jackc/pgtype v1.14.3 h1:h6W9cPuHsRWQFTWUZMAKMgG5jSwQI0Zurzdvlx3Plus=
github.com/jackc/pgtype v1.14.3/go.mod h1:aKeozOde08iifGosdJpz9MBZonJOUJxqNpPBcMJTlVA=
github.com/jackc/pgtype v1.14.4 h1:fKuNiCumbKTAIxQwXfB/nsrnkEI6bPJrrSiMKgbJ2j8=
github.com/jackc/pgtype v1.14.4/go.mod h1:aKeozOde08iifGosdJpz9MBZonJOUJxqNpPBcMJTlVA=
github.com/jackc/pgx/v4 v4.0.0-20190420224344-cc3461e65d96/go.mod h1:mdxmSJJuR08CZQyj1PVQBHy9XOp5p8/SHH6a0psbY9Y=
github.com/jackc/pgx/v4 v4.0.0-20190421002000-1b8f0016e912/go.mod h1:no/Y67Jkk/9WuGR0JG/JseM9irFbnEPbuWV2EELPNuM=
github.com/jackc/pgx/v4 v4.0.0-pre1.0.20190824185557-6972a5742186/go.mod h1:X+GQnOEnf1dqHGpw7JmHqHc1NxDoalibchSk9/RWuDc=

View file

@ -5,66 +5,40 @@ import (
"strconv"
)
type cast interface {
// AS casts expressions as castType type
AS(castType string) Expression
// AS_CHAR casts expression as char with optional length
AS_CHAR(length ...int) StringExpression
// AS_DATE casts expression AS date type
AS_DATE() DateExpression
// AS_FLOAT casts expressions as float type
AS_FLOAT() FloatExpression
// AS_DOUBLE casts expressions as double type
AS_DOUBLE() FloatExpression
// AS_DECIMAL casts expression AS numeric type
AS_DECIMAL() FloatExpression
// AS_TIME casts expression AS time type
AS_TIME() TimeExpression
// AS_DATETIME casts expression as datetime type
AS_DATETIME() DateTimeExpression
// AS_SIGNED casts expressions as signed integer type
AS_SIGNED() IntegerExpression
// AS_UNSIGNED casts expression as unsigned integer type
AS_UNSIGNED() IntegerExpression
// AS_BINARY casts expression as binary type
AS_BINARY() StringExpression
}
type castImpl struct {
type cast struct {
jet.Cast
}
// CAST function converts a expr (of any type) into latter specified datatype.
func CAST(expr Expression) cast {
castImpl := &castImpl{}
func CAST(expr Expression) *cast {
ret := &cast{}
ret.Cast = jet.NewCastImpl(expr)
castImpl.Cast = jet.NewCastImpl(expr)
return castImpl
return ret
}
// AS casts expressions to castType
func (c *castImpl) AS(castType string) Expression {
func (c *cast) AS(castType string) Expression {
return c.Cast.AS(castType)
}
// AS_DATETIME cast expression to DATETIME type
func (c *castImpl) AS_DATETIME() DateTimeExpression {
func (c *cast) AS_DATETIME() DateTimeExpression {
return DateTimeExp(c.AS("DATETIME"))
}
// AS_SIGNED casts expression to SIGNED type
func (c *castImpl) AS_SIGNED() IntegerExpression {
func (c *cast) AS_SIGNED() IntegerExpression {
return IntExp(c.AS("SIGNED"))
}
// AS_UNSIGNED casts expression to UNSIGNED type
func (c *castImpl) AS_UNSIGNED() IntegerExpression {
func (c *cast) AS_UNSIGNED() IntegerExpression {
return IntExp(c.AS("UNSIGNED"))
}
// AS_CHAR casts expression to CHAR type with optional length
func (c *castImpl) AS_CHAR(length ...int) StringExpression {
func (c *cast) AS_CHAR(length ...int) StringExpression {
if len(length) > 0 {
return StringExp(c.AS("CHAR(" + strconv.Itoa(length[0]) + ")"))
}
@ -73,29 +47,29 @@ func (c *castImpl) AS_CHAR(length ...int) StringExpression {
}
// AS_DATE casts expression AS DATE type
func (c *castImpl) AS_DATE() DateExpression {
func (c *cast) AS_DATE() DateExpression {
return DateExp(c.AS("DATE"))
}
func (c *castImpl) AS_FLOAT() FloatExpression {
func (c *cast) AS_FLOAT() FloatExpression {
return FloatExp(c.AS("FLOAT"))
}
func (c *castImpl) AS_DOUBLE() FloatExpression {
func (c *cast) AS_DOUBLE() FloatExpression {
return FloatExp(c.AS("DOUBLE"))
}
// AS_DECIMAL casts expression AS DECIMAL type
func (c *castImpl) AS_DECIMAL() FloatExpression {
func (c *cast) AS_DECIMAL() FloatExpression {
return FloatExp(c.AS("DECIMAL"))
}
// AS_TIME casts expression AS TIME type
func (c *castImpl) AS_TIME() TimeExpression {
func (c *cast) AS_TIME() TimeExpression {
return TimeExp(c.AS("TIME"))
}
// AS_BINARY casts expression as BINARY type
func (c *castImpl) AS_BINARY() StringExpression {
func (c *cast) AS_BINARY() StringExpression {
return StringExp(c.AS("BINARY"))
}

View file

@ -7,84 +7,45 @@ import (
"github.com/go-jet/jet/v2/internal/jet"
)
type cast interface {
AS(castType string) Expression
// Cast expression AS bool type
AS_BOOL() BoolExpression
// Cast expression AS smallint type
AS_SMALLINT() IntegerExpression
// Cast expression AS integer type
AS_INTEGER() IntegerExpression
// Cast expression AS bigint type
AS_BIGINT() IntegerExpression
// Cast expression AS numeric type, using precision and optionally scale
AS_NUMERIC(precisionAndScale ...int) FloatExpression
// Cast expression AS real type
AS_REAL() FloatExpression
// Cast expression AS double precision type
AS_DOUBLE() FloatExpression
// Cast expression AS char with optional length
AS_CHAR(length ...int) StringExpression
// Cast expression AS date type
AS_DATE() DateExpression
// Cast expression AS numeric type, using precision and optionally scale
AS_DECIMAL() FloatExpression
// Cast expression AS time type
AS_TIME() TimeExpression
// Cast expression AS text type
AS_TEXT() StringExpression
// Cast expression AS bytea type
AS_BYTEA() StringExpression
// Cast expression AS time with time timezone type
AS_TIMEZ() TimezExpression
// Cast expression AS timestamp type
AS_TIMESTAMP() TimestampExpression
// Cast expression AS timestamp with timezone type
AS_TIMESTAMPZ() TimestampzExpression
// Cast expression AS interval type
AS_INTERVAL() IntervalExpression
}
type castImpl struct {
type cast struct {
jet.Cast
}
// CAST function converts a expr (of any type) into latter specified datatype.
func CAST(expr Expression) cast {
castImpl := &castImpl{}
// CAST function converts an expr (of any type) into later specified datatype.
func CAST(expr Expression) *cast {
ret := &cast{}
ret.Cast = jet.NewCastImpl(expr)
castImpl.Cast = jet.NewCastImpl(expr)
return castImpl
return ret
}
// Cast expression as castType
func (b *castImpl) AS(castType string) Expression {
// AS casts expression as castType
func (b *cast) AS(castType string) Expression {
return b.Cast.AS(castType)
}
// Cast expression as bool type
func (b *castImpl) AS_BOOL() BoolExpression {
// AS_BOOL casts expression as bool type
func (b *cast) AS_BOOL() BoolExpression {
return BoolExp(b.AS("boolean"))
}
// Cast expression as smallint type
func (b *castImpl) AS_SMALLINT() IntegerExpression {
// AS_SMALLINT casts expression as smallint type
func (b *cast) AS_SMALLINT() IntegerExpression {
return IntExp(b.AS("smallint"))
}
// Cast expression AS integer type
func (b *castImpl) AS_INTEGER() IntegerExpression {
// AS_INTEGER casts expression AS integer type
func (b *cast) AS_INTEGER() IntegerExpression {
return IntExp(b.AS("integer"))
}
// Cast expression AS bigint type
func (b *castImpl) AS_BIGINT() IntegerExpression {
// AS_BIGINT casts expression AS bigint type
func (b *cast) AS_BIGINT() IntegerExpression {
return IntExp(b.AS("bigint"))
}
// Cast expression AS numeric type, using precision and optionally scale
func (b *castImpl) AS_NUMERIC(precisionAndScale ...int) FloatExpression {
// AS_NUMERIC casts expression as numeric type, using precision and optionally scale
func (b *cast) AS_NUMERIC(precisionAndScale ...int) FloatExpression {
var castArgs string
var argLen = len(precisionAndScale)
@ -97,22 +58,23 @@ func (b *castImpl) AS_NUMERIC(precisionAndScale ...int) FloatExpression {
return FloatExp(b.AS("numeric" + castArgs))
}
// Cast expression AS real type
func (b *castImpl) AS_REAL() FloatExpression {
// AS_REAL casts expression AS real type
func (b *cast) AS_REAL() FloatExpression {
return FloatExp(b.AS("real"))
}
// Cast expression AS double precision type
func (b *castImpl) AS_DOUBLE() FloatExpression {
// AS_DOUBLE casts expression AS double precision type
func (b *cast) AS_DOUBLE() FloatExpression {
return FloatExp(b.AS("double precision"))
}
// Cast expression AS text type
func (b *castImpl) AS_TEXT() StringExpression {
// AS_TEXT casts expression AS text type
func (b *cast) AS_TEXT() StringExpression {
return StringExp(b.AS("text"))
}
func (b *castImpl) AS_CHAR(length ...int) StringExpression {
// AS_CHAR casts expression AS a character type
func (b *cast) AS_CHAR(length ...int) StringExpression {
if len(length) > 0 {
return StringExp(b.AS("char(" + strconv.Itoa(length[0]) + ")"))
}
@ -120,42 +82,51 @@ func (b *castImpl) AS_CHAR(length ...int) StringExpression {
return StringExp(b.AS("char"))
}
// Cast expression AS date type
func (b *castImpl) AS_DATE() DateExpression {
// AS_VARCHAR casts expression AS a character varying type
func (b *cast) AS_VARCHAR(length ...int) StringExpression {
if len(length) > 0 {
return StringExp(b.AS("varchar(" + strconv.Itoa(length[0]) + ")"))
}
return StringExp(b.AS("varchar"))
}
// AS_DATE casts expression AS date type
func (b *cast) AS_DATE() DateExpression {
return DateExp(b.AS("date"))
}
// Cast expression AS date type
func (b *castImpl) AS_DECIMAL() FloatExpression {
// AS_DECIMAL casts expression AS date type
func (b *cast) AS_DECIMAL() FloatExpression {
return FloatExp(b.AS("decimal"))
}
// Cast expression AS text type
func (b *castImpl) AS_BYTEA() StringExpression {
// AS_BYTEA casts expression AS text type
func (b *cast) AS_BYTEA() StringExpression {
return StringExp(b.AS("bytea"))
}
// Cast expression AS date type
func (b *castImpl) AS_TIME() TimeExpression {
// AS_TIME casts expression AS date type
func (b *cast) AS_TIME() TimeExpression {
return TimeExp(b.AS("time without time zone"))
}
// Cast expression AS time with time timezone type
func (b *castImpl) AS_TIMEZ() TimezExpression {
// AS_TIMEZ casts expression AS time with time timezone type
func (b *cast) AS_TIMEZ() TimezExpression {
return TimezExp(b.AS("time with time zone"))
}
// Cast expression AS timestamp type
func (b *castImpl) AS_TIMESTAMP() TimestampExpression {
// AS_TIMESTAMP casts expression AS timestamp type
func (b *cast) AS_TIMESTAMP() TimestampExpression {
return TimestampExp(b.AS("timestamp without time zone"))
}
// Cast expression AS timestamp with timezone type
func (b *castImpl) AS_TIMESTAMPZ() TimestampzExpression {
// AS_TIMESTAMPZ casts expression AS timestamp with timezone type
func (b *cast) AS_TIMESTAMPZ() TimestampzExpression {
return TimestampzExp(b.AS("timestamp with time zone"))
}
// Cast expression AS interval type
func (b *castImpl) AS_INTERVAL() IntervalExpression {
// AS_INTERVAL casts expression AS interval type
func (b *cast) AS_INTERVAL() IntervalExpression {
return IntervalExp(b.AS("interval"))
}

View file

@ -34,47 +34,64 @@ func Int64(value int64) IntegerExpression {
return CAST(jet.Int(value)).AS_BIGINT()
}
// Uint8 is constructor for 8 bit unsigned integer expressions literals.
func Uint8(value uint8) IntegerExpression {
return CAST(jet.Uint8(value)).AS_SMALLINT()
}
// Uint16 is constructor for 16 bit unsigned integer expressions literals.
func Uint16(value uint16) IntegerExpression {
return CAST(jet.Uint16(value)).AS_INTEGER()
}
// Uint32 is constructor for 32 bit unsigned integer expressions literals.
func Uint32(value uint32) IntegerExpression {
return CAST(jet.Uint32(value)).AS_BIGINT()
}
// Uint64 is constructor for 64 bit unsigned integer expressions literals.
func Uint64(value uint64) IntegerExpression {
return CAST(jet.Uint64(value)).AS_BIGINT()
}
// Float creates new float literal expression
var Float = jet.Float
// Float32 is constructor for 32 bit float literals
func Float32(value float32) FloatExpression {
// Real is placeholder constructor for 32-bit float literals
func Real(value float32) FloatExpression {
return CAST(jet.Literal(value)).AS_REAL()
}
// Float64 is constructor for 64 bit float literals
func Float64(value float64) FloatExpression {
// Double is placeholder constructor for 64-bit float literals
func Double(value float64) FloatExpression {
return CAST(jet.Literal(value)).AS_DOUBLE()
}
// Decimal creates new float literal expression
var Decimal = jet.Decimal
// String creates new string literal expression
// String is a parameter constructor for the PostgreSQL text type. Using the `Text` constructor is
// generally preferable.
//
// WARNING: String always applies a `text` type cast, which can be problematic if a parameter is compared
// to a `character` column, as this may prevent index usage. In such cases, consider using the Char
// constructor instead. See also other PostgreSQL-specific constructors: Text, Char, and VarChar.
func String(value string) StringExpression {
return CAST(jet.String(value)).AS_TEXT()
}
// Text is a parameter constructor for the PostgreSQL text type. This constructor also adds an
// explicit placeholder type cast to text in the generated query, such as `$3::text`.
// Example usage:
//
// Text("English")
func Text(value string) StringExpression {
return CAST(jet.Literal(value)).AS_TEXT()
}
// Char is a parameter constructor for the PostgreSQL character type. This constructor also adds an
// explicit placeholder type cast to text in the generated query, such as `$3::char(30)`.
// Example usage:
//
// Char(20)("English")
func Char(length ...int) func(value string) StringExpression {
return func(value string) StringExpression {
return CAST(StringExp(jet.Literal(value))).AS_CHAR(length...)
}
}
// VarChar is a parameter constructor for the PostgreSQL character varying type. This constructor
// also adds an explicit placeholder type cast to text in the generated query, such as `$3::varchar(30)`.
// Example usage:
//
// VarChar(20)("English")
// VarChar()("English")
func VarChar(length ...int) func(value string) StringExpression {
return func(value string) StringExpression {
return CAST(StringExp(jet.Literal(value))).AS_VARCHAR(length...)
}
}
// Json creates new json literal expression
func Json(value interface{}) StringExpression {
switch value.(type) {

View file

@ -34,32 +34,21 @@ func TestInt64(t *testing.T) {
assertSerialize(t, Int64(val), `$1::bigint`, val)
}
func TestUint8(t *testing.T) {
val := uint8(math.MaxUint8)
assertSerialize(t, Uint8(val), `$1::smallint`, val)
}
func TestUint16(t *testing.T) {
val := uint16(math.MaxUint16)
assertSerialize(t, Uint16(val), `$1::integer`, val)
}
func TestUint32(t *testing.T) {
val := uint32(math.MaxUint32)
assertSerialize(t, Uint32(val), `$1::bigint`, val)
}
func TestUint64(t *testing.T) {
val := uint64(math.MaxUint64)
assertSerialize(t, Uint64(val), `$1::bigint`, val)
}
func TestFloat(t *testing.T) {
assertSerialize(t, Float(12.34), `$1`, float64(12.34))
assertSerialize(t, Real(12.34), `$1::real`, float32(12.34))
assertSerialize(t, Double(12.34), `$1::double precision`, float64(12.34))
}
func TestString(t *testing.T) {
assertSerialize(t, String("Some text"), `$1::text`, "Some text")
assertSerialize(t, Text("Some text"), `$1::text`, "Some text")
assertSerialize(t, Char(20)("John Doe"), `$1::char(20)`, "John Doe")
assertSerialize(t, Char()("John Doe"), `$1::char`, "John Doe")
assertSerialize(t, VarChar(20)("John Doe"), `$1::varchar(20)`, "John Doe")
assertSerialize(t, VarChar()("John Doe"), `$1::varchar`, "John Doe")
}
func TestBytea(t *testing.T) {

View file

@ -12,8 +12,8 @@ type values struct {
// Example usage:
//
// VALUES(
// WRAP(Int32(204), Float32(1.21)),
// WRAP(Int32(207), Float32(1.02)),
// WRAP(Int32(204), Real(1.21)),
// WRAP(Int32(207), Real(1.02)),
// )
func VALUES(rows ...RowExpression) values {
return values{Values: jet.Values(rows)}

View file

@ -4,52 +4,44 @@ import (
"github.com/go-jet/jet/v2/internal/jet"
)
type cast interface {
AS(castType string) Expression
AS_TEXT() StringExpression
AS_NUMERIC() FloatExpression
AS_INTEGER() IntegerExpression
AS_REAL() FloatExpression
AS_BLOB() StringExpression
}
type castImpl struct {
type cast struct {
jet.Cast
}
// CAST function converts a expr (of any type) into latter specified datatype.
func CAST(expr Expression) cast {
castImpl := &castImpl{}
castImpl.Cast = jet.NewCastImpl(expr)
return castImpl
func CAST(expr Expression) *cast {
ret := &cast{}
ret.Cast = jet.NewCastImpl(expr)
return ret
}
// AS casts expressions to castType
func (c *castImpl) AS(castType string) Expression {
func (c *cast) AS(castType string) Expression {
return c.Cast.AS(castType)
}
// AS_TEXT cast expression to TEXT type
func (c *castImpl) AS_TEXT() StringExpression {
func (c *cast) AS_TEXT() StringExpression {
return StringExp(c.AS("TEXT"))
}
// AS_NUMERIC cast expression to NUMERIC type
func (c *castImpl) AS_NUMERIC() FloatExpression {
func (c *cast) AS_NUMERIC() FloatExpression {
return FloatExp(c.AS("NUMERIC"))
}
// AS_INTEGER cast expression to INTEGER type
func (c *castImpl) AS_INTEGER() IntegerExpression {
func (c *cast) AS_INTEGER() IntegerExpression {
return IntExp(c.AS("INTEGER"))
}
// AS_REAL cast expression to REAL type
func (c *castImpl) AS_REAL() FloatExpression {
func (c *cast) AS_REAL() FloatExpression {
return FloatExp(c.AS("REAL"))
}
// AS_BLOB cast expression to BLOB type
func (c *castImpl) AS_BLOB() StringExpression {
func (c *cast) AS_BLOB() StringExpression {
return StringExp(c.AS("BLOB"))
}

View file

@ -462,15 +462,15 @@ func TestStringOperators(t *testing.T) {
query := AllTypes.SELECT(
AllTypes.Text.EQ(AllTypes.Char),
AllTypes.Text.EQ(String("Text")),
AllTypes.Text.EQ(Text("Text")),
AllTypes.Text.NOT_EQ(AllTypes.VarCharPtr),
AllTypes.Text.NOT_EQ(String("Text")),
AllTypes.Text.NOT_EQ(Text("Text")),
AllTypes.Text.GT(AllTypes.Text),
AllTypes.Text.GT(String("Text")),
AllTypes.Text.GT(Text("Text")),
AllTypes.Text.GT_EQ(AllTypes.TextPtr),
AllTypes.Text.GT_EQ(String("Text")),
AllTypes.Text.GT_EQ(Text("Text")),
AllTypes.Text.LT(AllTypes.Char),
AllTypes.Text.LT(String("Text")),
AllTypes.Text.LT(Text("Text")),
AllTypes.Text.LT_EQ(AllTypes.VarChar),
AllTypes.Text.LT_EQ(String("Text")),
AllTypes.Text.BETWEEN(String("min"), String("max")),
@ -490,13 +490,13 @@ func TestStringOperators(t *testing.T) {
OCTET_LENGTH(AllTypes.Text),
OCTET_LENGTH(String("length")),
LOWER(AllTypes.VarCharPtr),
LOWER(String("length")),
LOWER(Char(4)("length")),
UPPER(AllTypes.Char),
UPPER(String("upper")),
UPPER(VarChar()("upper")),
BTRIM(AllTypes.VarChar),
BTRIM(String("btrim")),
BTRIM(Char()("btrim")),
BTRIM(AllTypes.VarChar, String("AA")),
BTRIM(String("btrim"), String("AA")),
BTRIM(VarChar(11)("btrim"), String("AA")),
LTRIM(AllTypes.VarChar),
LTRIM(String("ltrim")),
LTRIM(AllTypes.VarChar, String("A")),
@ -533,7 +533,7 @@ func TestStringOperators(t *testing.T) {
TO_HEX(AllTypes.IntegerPtr),
)
dest := []struct{}{}
var dest []struct{}
err := query.Query(db, &dest)
require.NoError(t, err)
@ -630,9 +630,9 @@ func TestFloatOperators(t *testing.T) {
AllTypes.Numeric.BETWEEN(Float(1.34), AllTypes.Decimal).AS("between"),
AllTypes.Numeric.NOT_BETWEEN(AllTypes.Decimal.MUL(Float(3)), Float(100.12)).AS("not_between"),
TRUNC(AllTypes.Decimal.ADD(AllTypes.Decimal), Uint8(2)).AS("add1"),
TRUNC(AllTypes.Decimal.ADD(AllTypes.Decimal), Int8(2)).AS("add1"),
TRUNC(AllTypes.Decimal.ADD(Float(11.22)), Int8(2)).AS("add2"),
TRUNC(AllTypes.Decimal.SUB(AllTypes.DecimalPtr), Uint16(2)).AS("sub1"),
TRUNC(AllTypes.Decimal.SUB(AllTypes.DecimalPtr), Int32(2)).AS("sub1"),
TRUNC(AllTypes.Decimal.SUB(Float(11.22)), Int16(2)).AS("sub2"),
TRUNC(AllTypes.Decimal.MUL(AllTypes.DecimalPtr), Int16(2)).AS("mul1"),
TRUNC(AllTypes.Decimal.MUL(Float(11.22)), Int32(2)).AS("mul2"),
@ -736,13 +736,13 @@ func TestIntegerOperators(t *testing.T) {
AllTypes.Integer.BETWEEN(Int(11), Int(200)).AS("between"),
AllTypes.Integer.NOT_BETWEEN(Int(66), Int(77)).AS("not_between"),
AllTypes.BigInt.LT(AllTypes.BigIntPtr).AS("lt1"),
AllTypes.BigInt.LT(Uint8(65)).AS("lt2"),
AllTypes.BigInt.LT(Int16(65)).AS("lt2"),
AllTypes.BigInt.LT_EQ(AllTypes.BigIntPtr).AS("lte1"),
AllTypes.BigInt.LT_EQ(Uint16(65)).AS("lte2"),
AllTypes.BigInt.LT_EQ(Int32(65)).AS("lte2"),
AllTypes.BigInt.GT(AllTypes.BigIntPtr).AS("gt1"),
AllTypes.BigInt.GT(Uint32(65)).AS("gt2"),
AllTypes.BigInt.GT(Int64(65)).AS("gt2"),
AllTypes.BigInt.GT_EQ(AllTypes.BigIntPtr).AS("gte1"),
AllTypes.BigInt.GT_EQ(Uint64(65)).AS("gte2"),
AllTypes.BigInt.GT_EQ(Int64(65)).AS("gte2"),
AllTypes.BigInt.ADD(AllTypes.BigInt).AS("add1"),
AllTypes.BigInt.ADD(Int(11)).AS("add2"),
@ -1111,8 +1111,8 @@ func TestRowExpression(t *testing.T) {
nowAddHour := time.Now().Add(time.Hour)
stmt := SELECT(
ROW(Int32(1), Float32(11.22), String("john")).AS("row"),
WRAP(Int64(1), Float64(11.22), String("john")).AS("wrap"),
ROW(Int32(1), Real(11.22), Text("john")).AS("row"),
WRAP(Int64(1), Double(11.22), VarChar(10)("john")).AS("wrap"),
ROW(Bool(false), DateT(now)).EQ(ROW(Bool(true), DateT(now))),
WRAP(Bool(false), DateT(now)).NOT_EQ(WRAP(Bool(true), DateT(now))),
@ -1131,7 +1131,7 @@ func TestRowExpression(t *testing.T) {
testutils.AssertStatementSql(t, stmt, `
SELECT ROW($1::integer, $2::real, $3::text) AS "row",
($4::bigint, $5::double precision, $6::text) AS "wrap",
($4::bigint, $5::double precision, $6::varchar(10)) AS "wrap",
ROW($7::boolean, $8::date) = ROW($9::boolean, $10::date),
($11::boolean, $12::date) != ($13::boolean, $14::date),
ROW($15::time without time zone) IS DISTINCT FROM (row(NOW()::time)),

View file

@ -660,8 +660,8 @@ func TestSelectExecution1(t *testing.T) {
).
WHERE(
OR(
City.City.EQ(String("London")),
City.City.EQ(String("York")),
City.City.EQ(Text("London")),
City.City.EQ(Text("York")),
),
).
ORDER_BY(
@ -741,7 +741,7 @@ func TestSelectExecution2(t *testing.T) {
Customer.CustomerID.AS("my_customer.id"),
Customer.LastName.AS("my_customer.last_name"),
).
WHERE(City.City.EQ(String("London")).OR(City.City.EQ(String("York")))).
WHERE(City.City.EQ(VarChar()("London")).OR(City.City.EQ(VarChar()("York")))).
ORDER_BY(City.CityID, Address.AddressID, Customer.CustomerID)
testutils.AssertDebugStatementSql(t, stmt, `
@ -754,7 +754,7 @@ SELECT city.city_id AS "my_city.id",
FROM dvds.city
INNER JOIN dvds.address ON (address.city_id = city.city_id)
INNER JOIN dvds.customer ON (customer.address_id = address.address_id)
WHERE (city.city = 'London'::text) OR (city.city = 'York'::text)
WHERE (city.city = 'London'::varchar) OR (city.city = 'York'::varchar)
ORDER BY city.city_id, address.address_id, customer.customer_id;
`, "London", "York")
@ -798,7 +798,7 @@ func TestSelectExecution3(t *testing.T) {
Address.AddressID.AS("address_id"),
Address.Address.AS("address_line"),
).
WHERE(City.City.EQ(String("London")).OR(City.City.EQ(String("York")))).
WHERE(City.City.EQ(VarChar(20)("London")).OR(City.City.EQ(VarChar(20)("York")))).
ORDER_BY(City.CityID, Address.AddressID, Customer.CustomerID)
testutils.AssertDebugStatementSql(t, stmt, `
@ -811,7 +811,7 @@ SELECT city.city_id AS "city_id",
FROM dvds.city
INNER JOIN dvds.address ON (address.city_id = city.city_id)
INNER JOIN dvds.customer ON (customer.address_id = address.address_id)
WHERE (city.city = 'London'::text) OR (city.city = 'York'::text)
WHERE (city.city = 'London'::varchar(20)) OR (city.city = 'York'::varchar(20))
ORDER BY city.city_id, address.address_id, customer.customer_id;
`, "London", "York")
@ -1866,7 +1866,7 @@ SELECT customer.customer_id AS "customer.customer_id",
FROM dvds.payment
INNER JOIN dvds.customer ON (customer.customer_id = payment.customer_id)
GROUP BY customer.customer_id
HAVING SUM(payment.amount) > 125.6
HAVING SUM(payment.amount) > 125::real
ORDER BY customer.customer_id, SUM(payment.amount) ASC;
`
query := SELECT(
@ -1885,14 +1885,14 @@ ORDER BY customer.customer_id, SUM(payment.amount) ASC;
).GROUP_BY(
Customer.CustomerID,
).HAVING(
SUMf(Payment.Amount).GT(Float(125.6)),
SUMf(Payment.Amount).GT(Real(125)),
).ORDER_BY(
Customer.CustomerID, SUMf(Payment.Amount).ASC(),
)
//fmt.Println(query.DebugSql())
testutils.AssertDebugStatementSql(t, query, expectedSQL, float64(125.6))
testutils.AssertDebugStatementSql(t, query, expectedSQL, float32(125))
var dest []struct {
model.Customer
@ -2378,14 +2378,14 @@ func TestSelectUnion(t *testing.T) {
SELECT payment.payment_id AS "payment.payment_id",
payment.amount AS "payment.amount"
FROM dvds.payment
WHERE payment.amount <= 100
WHERE payment.amount <= 100::double precision
)
UNION ALL
(
SELECT payment.payment_id AS "payment.payment_id",
payment.amount AS "payment.amount"
FROM dvds.payment
WHERE payment.amount >= 200
WHERE payment.amount >= 200::double precision
)
ORDER BY "payment.payment_id" ASC, "payment.amount" DESC
LIMIT 10
@ -2394,10 +2394,10 @@ OFFSET 20;
query := UNION_ALL(
Payment.
SELECT(Payment.PaymentID.AS("payment.payment_id"), Payment.Amount).
WHERE(Payment.Amount.LT_EQ(Float(100))),
WHERE(Payment.Amount.LT_EQ(Double(100))),
Payment.
SELECT(Payment.PaymentID, Payment.Amount).
WHERE(Payment.Amount.GT_EQ(Float(200))),
WHERE(Payment.Amount.GT_EQ(Double(200))),
).ORDER_BY(
IntegerColumn("payment.payment_id").ASC(),
Payment.Amount.DESC(),
@ -2729,7 +2729,7 @@ FROM dvds.actor
INNER JOIN dvds.language ON (language.language_id = film.language_id)
INNER JOIN dvds.film_category ON (film_category.film_id = film.film_id)
INNER JOIN dvds.category ON (category.category_id = film_category.category_id)
WHERE ((language.name = 'English'::text) AND (category.name != 'Action'::text)) AND (film.length > 180)
WHERE ((language.name = 'English'::char(20)) AND (category.name != 'Action'::text)) AND (film.length > 180::integer)
ORDER BY actor.actor_id ASC, film.film_id ASC;
`
@ -2746,15 +2746,15 @@ ORDER BY actor.actor_id ASC, film.film_id ASC;
INNER_JOIN(FilmCategory, FilmCategory.FilmID.EQ(Film.FilmID)).
INNER_JOIN(Category, Category.CategoryID.EQ(FilmCategory.CategoryID)),
).WHERE(
Language.Name.EQ(String("English")). // note that every column has type.
AND(Category.Name.NOT_EQ(String("Action"))). // String column Language.Name and Category.Name can be compared only with string expression
AND(Film.Length.GT(Int(180))), // Film.Length is integer column and can be compared only with integer expression
Language.Name.EQ(Char(20)("English")). // note that every column has type.
AND(Category.Name.NOT_EQ(Text("Action"))). // String column Language.Name and Category.Name can be compared only with string expression
AND(Film.Length.GT(Int32(180))), // Film.Length is integer column and can be compared only with integer expression
).ORDER_BY(
Actor.ActorID.ASC(),
Film.FilmID.ASC(),
)
testutils.AssertDebugStatementSql(t, stmt, expectedSQL, "English", "Action", int64(180))
testutils.AssertDebugStatementSql(t, stmt, expectedSQL, "English", "Action", int32(180))
var dest []struct {
model.Actor
@ -3162,7 +3162,7 @@ func TestSelectLateral(t *testing.T) {
).FROM(
Language,
).WHERE(
Language.Name.NOT_IN(String("spanish")).
Language.Name.NOT_IN(Char(20)("Spanish"), Char(20)("Catalan")).
AND(Film.LanguageID.EQ(Language.LanguageID)),
),
).AS("films")
@ -3191,7 +3191,7 @@ FROM dvds.film
language.name AS "language.name",
language.last_update AS "language.last_update"
FROM dvds.language
WHERE (language.name NOT IN ('spanish'::text)) AND (film.language_id = language.language_id)
WHERE (language.name NOT IN ('Spanish'::char(20), 'Catalan'::char(20))) AND (film.language_id = language.language_id)
) AS films
WHERE film.film_id = 1
ORDER BY film.film_id
@ -3236,7 +3236,7 @@ FROM dvds.film,
language.name AS "language.name",
language.last_update AS "language.last_update"
FROM dvds.language
WHERE (language.name NOT IN ('spanish'::text)) AND (film.language_id = language.language_id)
WHERE (language.name NOT IN ('Spanish'::char(20), 'Catalan'::char(20))) AND (film.language_id = language.language_id)
) AS films
WHERE film.film_id = 1
ORDER BY film.film_id
@ -3787,9 +3787,9 @@ func TestSelectConditionalFunctions(t *testing.T) {
Film.SELECT(Film.FilmID).WHERE(Film.RentalDuration.GT(Int(100))),
).AS("exists"),
CASE(Film.Length.GT(Int(120))).
WHEN(Bool(true)).THEN(String("long film")).
ELSE(String("short film")).AS("case"),
COALESCE(Film.Description, String("none")).AS("coalesce"),
WHEN(Bool(true)).THEN(Text("long film")).
ELSE(Text("short film")).AS("case"),
COALESCE(Film.Description, Text("none")).AS("coalesce"),
NULLIF(Film.ReleaseYear, Int(200)).AS("null_if"),
GREATEST(Film.RentalDuration, Int(4), Int(5)).AS("greatest"),
LEAST(Film.RentalDuration, Int(7), Int(6)).AS("least"),

View file

@ -15,9 +15,9 @@ import (
func TestVALUES(t *testing.T) {
values := VALUES(
WRAP(Int32(1), Int32(2), Float32(4.666), Bool(false), String("txt")),
WRAP(Int32(11).ADD(Int32(2)), Int32(22), Float32(33.222), Bool(true), String("png")),
WRAP(Int32(11), Int32(22), Float32(33.222), Bool(true), NULL),
WRAP(Int32(1), Int32(2), Real(4.666), Bool(false), String("txt")),
WRAP(Int32(11).ADD(Int32(2)), Int32(22), Real(33.222), Bool(true), String("png")),
WRAP(Int32(11), Int32(22), Real(33.222), Bool(true), NULL),
).AS("values_table")
stmt := SELECT(
@ -86,11 +86,11 @@ func TestVALUES_Join(t *testing.T) {
lastUpdate := Timestamp(2007, time.February, 11, 12, 0, 0)
filmValues := VALUES(
WRAP(String("Chamber Italian"), Int64(117), Int32(2005), Float32(5.82), lastUpdate),
WRAP(String("Grosse Wonderful"), Int64(49), Int32(2004), Float32(6.242), lastUpdate.ADD(INTERVAL(1, HOUR))),
WRAP(String("Airport Pollock"), Int64(54), Int32(2001), Float32(7.22), NULL),
WRAP(String("Bright Encounters"), Int64(73), Int32(2002), Float32(8.25), NULL),
WRAP(String("Academy Dinosaur"), Int64(83), Int32(2010), Float32(9.22), lastUpdate.SUB(INTERVAL(2, MINUTE))),
WRAP(String("Chamber Italian"), Int64(117), Int32(2005), Real(5.82), lastUpdate),
WRAP(String("Grosse Wonderful"), Int64(49), Int32(2004), Real(6.242), lastUpdate.ADD(INTERVAL(1, HOUR))),
WRAP(String("Airport Pollock"), Int64(54), Int32(2001), Real(7.22), NULL),
WRAP(String("Bright Encounters"), Int64(73), Int32(2002), Real(8.25), NULL),
WRAP(String("Academy Dinosaur"), Int64(83), Int32(2010), Real(9.22), lastUpdate.SUB(INTERVAL(2, MINUTE))),
).AS("film_values",
title, IntegerColumn("length"), releaseYear, rentalRate, TimestampColumn("update_date"))
@ -216,10 +216,10 @@ func TestVALUES_CTE_Update(t *testing.T) {
stmt := WITH(
paymentsToUpdate.AS(
VALUES(
WRAP(Int32(20564), Float32(1.21)),
WRAP(Int32(20567), Float32(1.02)),
WRAP(Int32(20570), Float32(1.34)),
WRAP(Int32(20573), Float32(1.72)),
WRAP(Int32(20564), Real(1.21)),
WRAP(Int32(20567), Real(1.02)),
WRAP(Int32(20570), Real(1.34)),
WRAP(Int32(20573), Real(1.72)),
),
),
)(