Update wiki pages.

This commit is contained in:
go-jet 2019-06-29 16:58:41 +02:00
parent fafe0866ea
commit 67e6fca0ce
13 changed files with 350 additions and 51 deletions

View file

@ -3,7 +3,7 @@
[![CircleCI](https://circleci.com/gh/go-jet/jet/tree/develop.svg?style=svg&circle-token=97f255c6a4a3ab6590ea2e9195eb3ebf9f97b4a7)](https://circleci.com/gh/go-jet/jet/tree/develop)
Jet is Go SQL Builder for PostgreSQL(support for MySql and OracleSql will be added later).
Jet enables writing type safe SQL queries in Go, and ability to easily convert database query result to desired arbitrary structure.
Jet enables writing type safe SQL queries in Go, and has ability to convert database query result to desired arbitrary structure.
## Contents
- [Getting Started](#getting-started)

View file

@ -69,21 +69,21 @@ func (d *deleteStatementImpl) Sql() (query string, args []interface{}, err error
}
func (d *deleteStatementImpl) DebugSql() (query string, err error) {
return DebugSql(d)
return debugSql(d)
}
func (d *deleteStatementImpl) Query(db execution.DB, destination interface{}) error {
return Query(d, db, destination)
return query(d, db, destination)
}
func (d *deleteStatementImpl) QueryContext(db execution.DB, context context.Context, destination interface{}) error {
return QueryContext(d, db, context, destination)
return queryContext(d, db, context, destination)
}
func (d *deleteStatementImpl) Exec(db execution.DB) (res sql.Result, err error) {
return Exec(d, db)
return exec(d, db)
}
func (d *deleteStatementImpl) ExecContext(db execution.DB, context context.Context) (res sql.Result, err error) {
return ExecContext(d, db, context)
return execContext(d, db, context)
}

View file

@ -56,7 +56,7 @@ func (i *insertStatementImpl) QUERY(selectStatement SelectStatement) InsertState
}
func (i *insertStatementImpl) DebugSql() (query string, err error) {
return DebugSql(i)
return debugSql(i)
}
func (i *insertStatementImpl) Sql() (sql string, args []interface{}, err error) {
@ -147,17 +147,17 @@ func (i *insertStatementImpl) Sql() (sql string, args []interface{}, err error)
}
func (i *insertStatementImpl) Query(db execution.DB, destination interface{}) error {
return Query(i, db, destination)
return query(i, db, destination)
}
func (i *insertStatementImpl) QueryContext(db execution.DB, context context.Context, destination interface{}) error {
return QueryContext(i, db, context, destination)
return queryContext(i, db, context, destination)
}
func (i *insertStatementImpl) Exec(db execution.DB) (res sql.Result, err error) {
return Exec(i, db)
return exec(i, db)
}
func (i *insertStatementImpl) ExecContext(db execution.DB, context context.Context) (res sql.Result, err error) {
return ExecContext(i, db, context)
return execContext(i, db, context)
}

View file

@ -50,7 +50,7 @@ func (l *lockStatementImpl) NOWAIT() LockStatement {
}
func (l *lockStatementImpl) DebugSql() (query string, err error) {
return DebugSql(l)
return debugSql(l)
}
func (l *lockStatementImpl) Sql() (query string, args []interface{}, err error) {
@ -94,17 +94,17 @@ func (l *lockStatementImpl) Sql() (query string, args []interface{}, err error)
}
func (l *lockStatementImpl) Query(db execution.DB, destination interface{}) error {
return Query(l, db, destination)
return query(l, db, destination)
}
func (l *lockStatementImpl) QueryContext(db execution.DB, context context.Context, destination interface{}) error {
return QueryContext(l, db, context, destination)
return queryContext(l, db, context, destination)
}
func (l *lockStatementImpl) Exec(db execution.DB) (sql.Result, error) {
return Exec(l, db)
return exec(l, db)
}
func (l *lockStatementImpl) ExecContext(db execution.DB, context context.Context) (res sql.Result, err error) {
return ExecContext(l, db, context)
return execContext(l, db, context)
}

View file

@ -192,7 +192,7 @@ func (s *selectStatementImpl) Sql() (query string, args []interface{}, err error
}
func (s *selectStatementImpl) DebugSql() (query string, err error) {
return DebugSql(s)
return debugSql(s)
}
func (s *selectStatementImpl) projections() []projection {
@ -290,17 +290,17 @@ func (s *selectLockImpl) serialize(statement statementType, out *queryData, opti
}
func (s *selectStatementImpl) Query(db execution.DB, destination interface{}) error {
return Query(s, db, destination)
return query(s, db, destination)
}
func (s *selectStatementImpl) QueryContext(db execution.DB, context context.Context, destination interface{}) error {
return QueryContext(s, db, context, destination)
return queryContext(s, db, context, destination)
}
func (s *selectStatementImpl) Exec(db execution.DB) (res sql.Result, err error) {
return Exec(s, db)
return exec(s, db)
}
func (s *selectStatementImpl) ExecContext(db execution.DB, context context.Context) (res sql.Result, err error) {
return ExecContext(s, db, context)
return execContext(s, db, context)
}

View file

@ -200,21 +200,21 @@ func (s *setStatementImpl) Sql() (query string, args []interface{}, err error) {
}
func (s *setStatementImpl) DebugSql() (query string, err error) {
return DebugSql(s)
return debugSql(s)
}
func (s *setStatementImpl) Query(db execution.DB, destination interface{}) error {
return Query(s, db, destination)
return query(s, db, destination)
}
func (s *setStatementImpl) QueryContext(db execution.DB, context context.Context, destination interface{}) error {
return QueryContext(s, db, context, destination)
return queryContext(s, db, context, destination)
}
func (s *setStatementImpl) Exec(db execution.DB) (res sql.Result, err error) {
return Exec(s, db)
return exec(s, db)
}
func (s *setStatementImpl) ExecContext(db execution.DB, context context.Context) (res sql.Result, err error) {
return ExecContext(s, db, context)
return execContext(s, db, context)
}

View file

@ -9,19 +9,28 @@ import (
)
type Statement interface {
// String returns generated SQL as string.
// Sql returns parametrized sql query with list of arguments.
// err is returned if statement is not composed correctly
Sql() (query string, args []interface{}, err error)
// DebugSql returns debug query where every parametrized placeholder is replaced with its argument.
// Do not use it in production. Use it only for debug purposes.
// err is returned if statement is not composed correctly
DebugSql() (query string, err error)
// Query executes statement over database connection db and stores row result in destination.
// Destination can be arbitrary structure
Query(db execution.DB, destination interface{}) error
// QueryContext executes statement with a context over database connection db and stores row result in destination.
// Destination can be of arbitrary structure
QueryContext(db execution.DB, context context.Context, destination interface{}) error
//Exec executes statement over db connection without returning any rows.
Exec(db execution.DB) (sql.Result, error)
//Exec executes statement with context over db connection without returning any rows.
ExecContext(db execution.DB, context context.Context) (sql.Result, error)
}
func DebugSql(statement Statement) (string, error) {
func debugSql(statement Statement) (string, error) {
sqlQuery, args, err := statement.Sql()
if err != nil {
@ -38,7 +47,7 @@ func DebugSql(statement Statement) (string, error) {
return debugSqlQuery, nil
}
func Query(statement Statement, db execution.DB, destination interface{}) error {
func query(statement Statement, db execution.DB, destination interface{}) error {
query, args, err := statement.Sql()
if err != nil {
@ -48,7 +57,7 @@ func Query(statement Statement, db execution.DB, destination interface{}) error
return execution.Query(db, context.Background(), query, args, destination)
}
func QueryContext(statement Statement, db execution.DB, context context.Context, destination interface{}) error {
func queryContext(statement Statement, db execution.DB, context context.Context, destination interface{}) error {
query, args, err := statement.Sql()
if err != nil {
@ -58,7 +67,7 @@ func QueryContext(statement Statement, db execution.DB, context context.Context,
return execution.Query(db, context, query, args, destination)
}
func Exec(statement Statement, db execution.DB) (res sql.Result, err error) {
func exec(statement Statement, db execution.DB) (res sql.Result, err error) {
query, args, err := statement.Sql()
if err != nil {
@ -68,7 +77,7 @@ func Exec(statement Statement, db execution.DB) (res sql.Result, err error) {
return db.Exec(query, args...)
}
func ExecContext(statement Statement, db execution.DB, context context.Context) (res sql.Result, err error) {
func execContext(statement Statement, db execution.DB, context context.Context) (res sql.Result, err error) {
query, args, err := statement.Sql()
if err != nil {

View file

@ -67,8 +67,12 @@ ORDER BY payment.payment_id ASC
LIMIT 30;
`
query := SELECT(Payment.AllColumns, Customer.AllColumns).
FROM(Payment.INNER_JOIN(Customer, Payment.CustomerID.EQ(Customer.CustomerID))).
query := SELECT(
Payment.AllColumns,
Customer.AllColumns,
).
FROM(Payment.
INNER_JOIN(Customer, Payment.CustomerID.EQ(Customer.CustomerID))).
ORDER_BY(Payment.PaymentID.ASC()).
LIMIT(30)
@ -599,7 +603,9 @@ SELECT actor.actor_id AS "actor.actor_id",
film_actor.actor_id AS "film_actor.actor_id",
film_actor.film_id AS "film_actor.film_id",
film_actor.last_update AS "film_actor.last_update",
"rFilms"."film.title" AS "film.title"
"rFilms"."film.film_id" AS "film.film_id",
"rFilms"."film.title" AS "film.title",
"rFilms"."film.rating" AS "film.rating"
FROM dvds.actor
INNER JOIN dvds.film_actor ON (actor.actor_id = film_actor.film_id)
INNER JOIN (
@ -621,7 +627,6 @@ FROM dvds.actor
AsTable("rFilms")
rFilmId := Film.FilmID.From(rRatingFilms)
rTitle := Film.Title.From(rRatingFilms)
query := Actor.
INNER_JOIN(FilmActor, Actor.ActorID.EQ(FilmActor.FilmID)).
@ -629,10 +634,10 @@ FROM dvds.actor
SELECT(
Actor.AllColumns,
FilmActor.AllColumns,
rTitle.AS("film.title"),
rRatingFilms.AllColumns(),
)
fmt.Println(query.Sql())
fmt.Println(query.DebugSql())
assertStatementSql(t, query, expectedQuery)

View file

@ -136,21 +136,21 @@ func (u *updateStatementImpl) Sql() (sql string, args []interface{}, err error)
}
func (u *updateStatementImpl) DebugSql() (query string, err error) {
return DebugSql(u)
return debugSql(u)
}
func (u *updateStatementImpl) Query(db execution.DB, destination interface{}) error {
return Query(u, db, destination)
return query(u, db, destination)
}
func (u *updateStatementImpl) QueryContext(db execution.DB, context context.Context, destination interface{}) error {
return QueryContext(u, db, context, destination)
return queryContext(u, db, context, destination)
}
func (u *updateStatementImpl) Exec(db execution.DB) (res sql.Result, err error) {
return Exec(u, db)
return exec(u, db)
}
func (u *updateStatementImpl) ExecContext(db execution.DB, context context.Context) (res sql.Result, err error) {
return ExecContext(u, db, context)
return execContext(u, db, context)
}

View file

@ -137,7 +137,7 @@ Following operators are only available on string expressions:
| NOT_SIMILAR_TO | table.Film.Name.NOT_SIMILAR_TO(String("%Wind%")) | staff.active NOT SIMILAR TO %Wind% |
## Cast operators
## SQL Cast operators
Cast operators allow expressions to be casted to some other database type.
SQL builder expression type changes accordingly to database type.
@ -158,4 +158,6 @@ SQL builder expression type changes accordingly to database type.
| TO_TIMESTAMP | table.Film.Description.TO_TIMESTAMP() | film.description::timestamp |
| TO_TIMESTAMPZ | table.Film.Description.TO_TIMESTAMPZ() | film.description::timestampz |
## SQL builder cast
TODO:

View file

@ -0,0 +1,248 @@
SELECT statement is used to retrieve records from one or more tables in PostgreSQL.
More about SELECT statement in postgres can be found at: https://www.postgresql.org/docs/11/sql-select.html
## SELECT statement clauses
Following clauses are supported - SELECT, DISTINCT, FROM, WHERE, GROUP BY, HAVING, ORDER BY, LIMIT, OFFSET and FOR.
_This list might be extended with feature Jet releases._
##### 1. SELECT clause
Sample SELECT clause written in Go:
```
jet.SELECT(
jet.Int(1).ADD(jet.Int(12)).SUB(jet.Int(21)), // arbitrary expression
table.Film.Name, // column
table.Customer.FirstName.CONCAT(table.Customer.LastName).AS("FullName") // alias
)
```
If jet and autogenerated table package, is dot "." imported, above statement will look more like native SQL:
```
SELECT(
Int(1).ADD(Int(12)).SUB(Int(21)), // arbitrary expression
Film.Name, // column
Customer.FirstName.CONCAT(Customer.LastName).AS("FullName") // alias
)
```
*For the most of the wiki dot import is used.*
Above SQL clause written in go will produce following raw SQL:
```
SELECT 1 + 12 - 21,
film.name AS "film.name", --
customer.first_name || customer.last_name AS "FullName"
```
`film.name AS "film.name"` - column names are aliased by default. Alias is used during execution to map row result to
appropriate `model` structure.
##### 2. DISTINCT clause
```
SELECT(Film.Name).
.DISTINCT().
```
Raw SQL:
```
SELECT DISTINCT film.name
```
##### 3. FROM clause
```
Go:
1).FROM(Film)
2).FROM(
Film.
INNER_JOIN(Language, Langauge.LanguageID.EQ(Film.FilmID))
)
SQL:
1)FROM dvds.film
2)FROM Film
INNER JOIN Language ON (Language.LanguageID = Film.FilmID)
```
##### 4. WHERE clause
```
Go:
.WHERE(Film.Length.GT(Int(150)))
SQL:
WHERE film.length > 150
```
##### 5. GROUP BY clause
```
Go:
.GROUP_BY(Film.Length)
SQL:
GROUP BY film.length
```
##### 6. HAVING clause
```
Go:
.HAVING(Film.Length.GT(Int(150))
SQL:
HAVING film.length > 150
```
##### 7. ORDER BY clause
```
Go:
.ORDER_BY(Film.Length)
SQL:
ORDER BY film.length
```
##### 8. LIMIT clause
```
Go:
.LIMIT(11)
SQL:
LIMIT 11
```
##### 9. OFFSET clause
```
Go:
.OFFSET(11)
SQL:
OFFSET 11
```
##### 9. FOR clause
```
Go:
.FOR(jet.NO_KEY_UPDATE)
SQL:
FOR NO KEY UPDATE
```
### Two forms of select statements in Jet
#### 1. Classical select statement
Columns selected are before tables selected
```
SELECT(
Payment.AllColumns,
Customer.AllColumns,
).
FROM(Payment.
INNER_JOIN(Customer, Payment.CustomerID.EQ(Customer.CustomerID))).
ORDER_BY(Payment.PaymentID.ASC()).
LIMIT(30)
```
#### 2. Jet select statement
Joined tables(or just one) are before columns selected. There is no FROM.
```
Payment.
INNER_JOIN(Customer, Payment.CustomerID.EQ(Customer.CustomerID))
SELECT(
Payment.AllColumns,
Customer.AllColumns,
).
ORDER_BY(Payment.PaymentID.ASC()).
LIMIT(30)
```
Second form is added, because sometimes feels more natural to first think about the tables
of interest, and than about the columns.
**Both forms produces exactly the same raw SQL.**
## Sub-queries
How to write SELECT statement with sub-queries?
Sub-queries are composed first:
```
// select film_id and title from film table that have 'R' rating.
rRatingFilms := Film.
SELECT(
Film.FilmID,
Film.Title,
).
WHERE(Film.Rating.EQ(enum.MpaaRating.R)).
AsTable("rFilms")
```
`AsTable("rFilms")` - allows SELECT statements to be seen as table.
To use sub-query columns in SELECT statement expressions we have to export column from sub-query,
using `From` method.
```
rFilmId := Film.FilmID.From(rRatingFilms) //used for join condition
```
Now we can write:
```
query := Actor.
INNER_JOIN(FilmActor, Actor.ActorID.EQ(FilmActor.FilmID)).
INNER_JOIN(rRatingFilms, FilmActor.FilmID.EQ(rFilmId)).
SELECT(
Actor.AllColumns,
FilmActor.AllColumns,
rRatingFilms.AllColumns(),
)
```
`rRatingFilms.AllColumns(),` - sub-query columns needed for projection can be exported with AllColumns() method.
No need to export each one of them with `From`.
Debug SQL of above example:
```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_actor.actor_id AS "film_actor.actor_id",
film_actor.film_id AS "film_actor.film_id",
film_actor.last_update AS "film_actor.last_update",
"rFilms"."film.film_id" AS "film.film_id",
"rFilms"."film.title" AS "film.title",
"rFilms"."film.rating" AS "film.rating"
FROM dvds.actor
INNER JOIN dvds.film_actor ON (actor.actor_id = film_actor.film_id)
INNER JOIN (
SELECT film.film_id AS "film.film_id",
film.title AS "film.title",
film.rating AS "film.rating"
FROM dvds.film
WHERE film.rating = 'R'
) AS "rFilms" ON (film_actor.film_id = "rFilms"."film.film_id");
```
#### What If sub-query projection is not a column?
For instance:
```
customersPayments := Payment.
SELECT(
Payment.CustomerID,
SUMf(Payment.Amount).AS("amount_sum"),
).
GROUP_BY(Payment.CustomerID).
AsTable("customer_payment_sum")
customerId := Payment.CustomerID.From(customersPayments)
```
To export `"amount_sum"` from `customersPayments` sub-query we have to create column first with appropriate type and a name.
Because SUMf produces float expression we will create FloatColumn with name of the column alias `"amount_sum"`
```
amountSum := FloatColumn("amount_sum").From(customersPayments)
```

35
wiki/Statements.md Normal file
View file

@ -0,0 +1,35 @@
Following statements are supported:
* [SELECT](https://github.com/go-jet/jet/wiki/SELECT)
* [INSERT](https://github.com/go-jet/jet/wiki/INSERT)
* [UPDATE](https://github.com/go-jet/jet/wiki/UPDATE)
* [DELETE](https://github.com/go-jet/jet/wiki/DELETE)
* [LOCK](https://github.com/go-jet/jet/wiki/LOCK)
_This list might be extended with feature Jet releases._
There is a common set of action that can be performed for each statement type:
- `Sql() (query string, args []interface{}, err error)` - retrieves parametrized sql query with list of arguments
- `DebugSql() (query string, err error)` - retrieves debug query where every parametrized placeholder is replaced with its argument.
- `Query(db execution.DB, destination interface{}) error` - executes statements over database connection db and stores row result in destination.
- `QueryContext(db execution.DB, context context.Context, destination interface{}) error` - executes statement with a context over database connection db and stores row result in destination.
- `Exec(db execution.DB) (sql.Result, error)` - executes statement over db connection without returning any rows.
- `ExecContext(db execution.DB, context context.Context) (sql.Result, error)` - executes statement with context over db connection without returning any rows.
Database connection can be of any type that implements following interface:
```go
type DB interface {
Exec(query string, args ...interface{}) (sql.Result, error)
ExecContext(ctx context.Context, query string, args ...interface{}) (sql.Result, error)
Query(query string, args ...interface{}) (*sql.Rows, error)
QueryContext(ctx context.Context, query string, args ...interface{}) (*sql.Rows, error)
}
```
These include but are not limited to:
- `sql.DB`
- `sql.Tx`
- `sql.Conn`

View file

@ -3,9 +3,9 @@
* [Generator](https://github.com/go-jet/jet/wiki/Generator)
* [Writing SQL in Go]()
* [Expressions](https://github.com/go-jet/jet/wiki/Expressions)
* [Statements]()
* [SELECT]()
* [INSERT]()
* [UPDATE]()
* [DELETE]()
* [LOCK]()
* [Statements](https://github.com/go-jet/jet/wiki/Statements)
* [SELECT](https://github.com/go-jet/jet/wiki/SELECT)
* [INSERT](https://github.com/go-jet/jet/wiki/INSERT)
* [UPDATE](https://github.com/go-jet/jet/wiki/UPDATE)
* [DELETE](https://github.com/go-jet/jet/wiki/DELETE)
* [LOCK](https://github.com/go-jet/jet/wiki/LOCK)