Skip to content

Commit

Permalink
Merge pull request #419 from go-jet/str-constr
Browse files Browse the repository at this point in the history
Add PostgreSQL-specific character type constructors: Text, Char, and VarChar
  • Loading branch information
go-jet authored Nov 2, 2024
2 parents aaf705d + 2183af4 commit f8f2f75
Show file tree
Hide file tree
Showing 13 changed files with 231 additions and 286 deletions.
70 changes: 35 additions & 35 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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(),
Expand All @@ -200,35 +200,35 @@ args - query parameters

```sql
SELECT actor.actor_id AS "actor.actor_id",
actor.first_name AS "actor.first_name",
actor.last_name AS "actor.last_name",
actor.last_update AS "actor.last_update",
film.film_id AS "film.film_id",
film.title AS "film.title",
film.description AS "film.description",
film.release_year AS "film.release_year",
film.language_id AS "film.language_id",
film.rental_duration AS "film.rental_duration",
film.rental_rate AS "film.rental_rate",
film.length AS "film.length",
film.replacement_cost AS "film.replacement_cost",
film.rating AS "film.rating",
film.last_update AS "film.last_update",
film.special_features AS "film.special_features",
film.fulltext AS "film.fulltext",
language.language_id AS "language.language_id",
language.name AS "language.name",
language.last_update AS "language.last_update",
category.category_id AS "category.category_id",
category.name AS "category.name",
category.last_update AS "category.last_update"
actor.first_name AS "actor.first_name",
actor.last_name AS "actor.last_name",
actor.last_update AS "actor.last_update",
film.film_id AS "film.film_id",
film.title AS "film.title",
film.description AS "film.description",
film.release_year AS "film.release_year",
film.language_id AS "film.language_id",
film.rental_duration AS "film.rental_duration",
film.rental_rate AS "film.rental_rate",
film.length AS "film.length",
film.replacement_cost AS "film.replacement_cost",
film.rating AS "film.rating",
film.last_update AS "film.last_update",
film.special_features AS "film.special_features",
film.fulltext AS "film.fulltext",
language.language_id AS "language.language_id",
language.name AS "language.name",
language.last_update AS "language.last_update",
category.category_id AS "category.category_id",
category.name AS "category.name",
category.last_update AS "category.last_update"
FROM dvds.actor
INNER JOIN dvds.film_actor ON (actor.actor_id = film_actor.actor_id)
INNER JOIN dvds.film ON (film.film_id = film_actor.film_id)
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)
INNER JOIN dvds.film_actor ON (actor.actor_id = film_actor.actor_id)
INNER JOIN dvds.film ON (film.film_id = film_actor.film_id)
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::char(20)) AND (category.name != $2::text)) AND (film.length > $3::integer)
ORDER BY actor.actor_id ASC, film.film_id ASC;
```
```sh
Expand Down Expand Up @@ -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>
Expand Down Expand Up @@ -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:
Expand Down
4 changes: 2 additions & 2 deletions examples/quick-start/quick-start.go
Original file line number Diff line number Diff line change
Expand Up @@ -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(),
Expand Down
8 changes: 5 additions & 3 deletions go.mod
Original file line number Diff line number Diff line change
@@ -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
Expand Down
4 changes: 2 additions & 2 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -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=
Expand Down
58 changes: 16 additions & 42 deletions mysql/cast.go
Original file line number Diff line number Diff line change
Expand Up @@ -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{}

castImpl.Cast = jet.NewCastImpl(expr)
func CAST(expr Expression) *cast {
ret := &cast{}
ret.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]) + ")"))
}
Expand All @@ -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"))
}
Loading

0 comments on commit f8f2f75

Please sign in to comment.