diff --git a/wiki/DELETE.md b/wiki/DELETE.md deleted file mode 100644 index 996cf09..0000000 --- a/wiki/DELETE.md +++ /dev/null @@ -1,58 +0,0 @@ -DELETE statement deletes rows that satisfy the WHERE clause from the specified table. More about delete statement -in PostgreSQL: https://www.postgresql.org/docs/11/sql-delete.html - -Following clauses are supported: -- WHERE(delete_condition) - Only rows for which delete condition returns true will be deleted. -- RETURNING(output_expression...) - An expressions to be computed and returned by the DELETE command after each row is deleted. -The expression can use any column names of the table. Write _TableName_.AllColumns to return all columns. - -### Example - -``` -// delete all links with name 'Gmail' and 'Outlook' -deleteStmt := Link. - DELETE(). - WHERE(Link.Name.IN(String("Gmail"), String("Outlook"))) -``` - -Debug sql of above statement: - -```sql -DELETE FROM test_sample.link -- test_sample is name of the schema -WHERE link.name IN ('Gmail', 'Outlook'); -``` - -### Execute statement - -To execute delete statement and get sql.Result: - -``` -res, err := deleteStmt.Exec(db) -``` - -To execute delete statement and return records deleted, -delete statement has to have RETURNING clause: - -``` -deleteStmt := Link. - DELETE(). - WHERE(Link.Name.IN(String("Gmail"), String("Outlook"))). - RETURNING(Link.AllColumns) - -dest := []model.Link{} - -err := deleteStmt.Query(db, &dest) - -``` - -Use `ExecContext` and `QueryContext` to provide context object to execution. - -##### SQL table used for the example: -```sql -CREATE TABLE IF NOT EXISTS link ( - id serial PRIMARY KEY, - url VARCHAR (255) NOT NULL, - name VARCHAR (255) NOT NULL, - description VARCHAR (255) -); -``` \ No newline at end of file diff --git a/wiki/Expressions.md b/wiki/Expressions.md deleted file mode 100644 index 8770a24..0000000 --- a/wiki/Expressions.md +++ /dev/null @@ -1,195 +0,0 @@ - -Jet sql builder supports following expression types: - - - Bool expressions - - Integer expressions - - Float expressions - - String expressions - - Date expressions - - Time expressions - - Timez expressions (Time with time zone) - - Timestamp expressions - - Timestampz expressions (Timestamp with time zone) - -_This list might be extended with feature Jet releases._ - -### Literal Types -For every expression type there is a method to create one expression literal type. -Literal type examples: - -``` -jet.Bool(true) -jet.Integer(11) -jet.Float(23.44) -jet.String("John Doe") -jet.Date(2010, 12, 3) -jet.Time(23, 6, 6, 1) -jet.Timez(23, 6, 6, 222, +200) -jet.Timestamp(2010, 10, 21, 15, 30, 12, 333) -jet.Timestampz(2010, 10, 21, 15, 30, 12, 444, 0) -``` - -There is also: -``` -jet.NULL -jet.STAR (alias for *) -``` - -### Column Types -Every sql builder table column belongs to one expression type. There are following column types: -``` -jet.ColumnBool -jet.ColumnInteger -jet.ColumnFloat -jet.ColumnString -jet.ColumnDate -jet.ColumnTime -jet.ColumnTimez -jet.ColumnTimestamp -jet.ColumnTimestampz -``` - -Columns and literals can form arbitrary expressions but have to follow valid SQL expression syntax. -For instance valid expressions are: - -``` -jet.Bool(true).AND(jet.Bool(false)).IS_FALSE() -(table.Film.Length.GT(jet.Int(100)).AND(table.Film.Length.LT(jet.Int(200))).IS_TRUE() -``` - -Some of the invalid expressions. These expressions will cause go build to break. - -``` -jet.Bool(true).ADD(jet.Int(11)) // can't add bool and integer -jet.Int(11).LIKE(jet.Float(22.2)) // integer expressions doesn't have LIKE method - ``` - - -## Comparision Operators - -Jet supports following comparison operators for all expression types: - -| Method | Example | Generated sql | -| ------------------------------ | ------------------------------------------------ |---------------------------- | -| EQ | jet.Int(1).EQ(table.Film.Length) | 1 = film.length | -| NOT_EQ | jet.Int(1).EQ(table.Film.Length) | 1 != film.length | -| IS_DISTINCT_FROM | jet.Int(1).IS_DISTINCT_FROM(table.Film.Length) | 1 IS DISTINCT FROM film.length | -| IS_NOT_DISTINCT_FROM | jet.Int(1).IS_NOT_DISTINCT_FROM(table.Film.Length) | 1 IS NOT DISTINCT FROM film.length | -| LT | jet.Int(1).LT(table.Film.Length) | 1 < film.length | -| LT_EQ | jet.Int(1).LT_EQ(table.Film.Length) | 1 <= film.length | -| GT | jet.Int(1).GT(table.Film.Length) | 1 > film.length | -| GT_EQ | jet.Int(1).GT_EQ(table.Film.Length) | 1 >= film.length | - -*Left-hand side and right-hand side of operators have to be of the same type* - - -## Arithmetic Operators - -Following arithmetic operators are supported for integer and float expressions. -If the first argument is float expression, second argument can be integer or float expression. -If the first argument is integer expression second argument can only be integer expression. - -| Method | Example | Generated sql | -| ------------------------------ | ------------------------------------------------ |---------------------------- | -| ADD | jet.Int(1).ADD(table.Film.Length) | 1 + film.length | -| SUB | jet.Float(1.11).SUB(Int(1)) | 1.11 + 1 | -| MUL | jet.Int(1).MUL(table.Film.Length) | 1 * film.length | -| DIV | jet.Float(1.11).DIV(jet.Float(3.33) | 1.11 / 3.33 | -| MOD | jet.Int(10).MOD(table.Film.Length) | 10 % film.length | -| POW | jet.Float(10.01).POW(table.Film.Length) | 10.01 ^ film.length | - - -## Bit Operators - -Following operators are only available on integer expressions: - -| Method | Example | Generated sql | -| ------------------------------ | ------------------------------------------------ |---------------------------- | -| BIT_AND | jet.Int(11).BIT_AND(table.Film.Length) | 11 & film.length | -| BIT_OR | jet.Int(11).BIT_OR(table.Film.Length) | 11 \| film.length | -| BIT_XOR | jet.Int(11).BIT_XOR(table.Film.Length) | 11 # film.length | -| BIT_NOT | BIT_NOT(table.Film.Length) | ~ film.length | -| BIT_SHIFT_LEFT | jet.Int(11).BIT_SHIFT_LEFT(table.Film.Length) | 11 >> film.length | -| BIT_SHIFT_RIGHT | jet.Int(11).BIT_SHIFT_RIGHT(table.Film.Length) | 11 >> film.length | - - -## Logical Operators - -Following operators are only available on boolean expressions: - -| Method | Example | Generated sql | -| ------------------------------ | ---------------------------------------------------------|---------------------------- | -| IS_TRUE | table.Staff.Active.IS_TRUE() | staff.active IS TRUE | -| IS_NOT_TRUE | (table.Staff.Active.AND(jet.Bool(true))).IS_NOT_TRUE() | (staff.active AND true) IS NOT TRUE | -| IS_FALSE | jet.Bool(false).IS_FALSE() | false IS FALSE | -| IS_NOT_FALSE | jet.Bool(true).IS_NOT_FALSE() | true IS NOT FALSE | -| IS_UNKNOWN | table.Staff.Active.IS_UNKNOWN() | staff.active IS UNKNOWN | -| IS_NOT_UNKNOWN | table.Staff.Active.IS_NOT_UNKNOWN() | staff.active IS NOT UNKNOWN | - - -## String Operators - -Following operators are only available on string expressions: - -| Method | Example | Generated sql | -| ------------------------------ | ---------------------------------------------------------|---------------------------- | -| CONCAT | table.Film.Name.CONCAT(table.Film.Description) | film.name \|\| film.description | -| LIKE | table.Film.Name.LIKE(String("%Wind%")) | film.name LIKE %Wind% | -| NOT_LIKE | table.Film.Name.NOT_LIKE(String("%Wind%")) | staff.active NOT LIKE %Wind% | -| SIMILAR_TO | table.Film.Name.SIMILAR_TO(String("%Wind%")) | staff.active SIMILAR TO %Wind% | -| NOT_SIMILAR_TO | table.Film.Name.NOT_SIMILAR_TO(String("%Wind%")) | staff.active NOT SIMILAR TO %Wind% | - - -## SQL Cast Operators - -Cast operators allow expressions to be casted to some other database type. -SQL builder expression type changes accordingly to database type. - -| Method | Example | Generated sql | -| ------------------------------ | ----------------------------------------------- | --------------------------------------------- | -| CAST(exp).AS_BOOL() | CAST(table.Film.Description).AS_BOOL() | film.description::boolean | -| CAST(exp).AS_SMALLINT() | CAST(table.Film.Description).AS_SMALLINT() | film.description::smallint | -| CAST(exp).AS_INTEGER() | CAST(table.Film.Description).AS_INTEGER() | film.description::integer | -| CAST(exp).AS_BIGINT() | CAST(table.Film.Description).AS_BIGINT() | film.description::bigint | -| CAST(exp).AS_NUMERIC() | CAST(table.Film.Description).AS_NUMERIC(10, 6) | film.description::numeric(10,6) | -| CAST(exp).AS_REAL() | CAST(table.Film.Description).AS_REAL() | film.description::real | -| CAST(exp).AS_DOUBLE() | CAST(table.Film.Description).AS_DOUBLE() | film.description::double | -| CAST(exp).AS_TEXT() | CAST(table.Film.Description).AS_TEXT() | film.description::text | -| CAST(exp).AS_DATE() | CAST(table.Film.Description).AS_DATE() | film.description::date | -| CAST(exp).AS_TIME() | CAST(table.Film.Description).AS_TIME() | film.description::time without time zone | -| CAST(exp).AS_TIMEZ() | CAST(table.Film.Description).AS_TIMEZ() | film.description::time with time zone | -| CAST(exp).AS_TIMESTAMP() | CAST(table.Film.Description).AS_TIMESTAMP() | film.description::timestamp without time zone | -| CAST(exp).AS_TIMESTAMPZ() | CAST(table.Film.Description).AS_TIMESTAMPZ() | film.description::timestamp with time zone | - -## SQL Builder Cast Wrapper - -For some expressions sql builder can't deduce expression type directly. For instance scalar sub-query: -``` - ( SELECT(MAXf(Film.RentalRate)). - FROM(Film) ).LT(Float(11.1)) -``` -This expression would not compile, because sub-query, although calculates one scalar float value, it is not a float expression. -To fix this sub-query can be cast to some float type, or just wrapped as float expression: -``` - FloatExp( SELECT(MAXf(Film.RentalRate)). - FROM(Film) ).LT(Float(11.1)) -``` -There are wrappers for all supported types: -``` - - BoolExp(exp) - - IntExp(exp) - - FloatExp(exp) - - StringExp(exp) - - DateExp(exp) - - TimeExp(exp) - - TimezExp(exp) - - TimestampExp(exp) - - TimestampzExp(exp) -``` -**Cast wrapper does NOT inject cast operator to generated SQL.** - -## RAW Operator -There is a RAW operator expression, that accepts raw sql as a string. It can be used for any unsupported functions, operators or expressions. -For example: - -```RAW("current_database()")``` _can be cast or wrapped, as needed._ diff --git a/wiki/Generator.md b/wiki/Generator.md deleted file mode 100644 index 7aae919..0000000 --- a/wiki/Generator.md +++ /dev/null @@ -1,107 +0,0 @@ - -Before we can write SQL queries in Go we have to generate necessary Go files. -To generate files we need running database instance containing already defined database schema. - -This files can be generated in two ways: - -#### 1) Generating from command line - -Install jet to GOPATH bin folder. This will allow generating jet files from the command line. - -```sh -go install github.com/go-jet/jet/cmd/jet -``` - -Make sure GOPATH bin folder is added to the PATH environment variable. - -Test jet generator can be found in the PATH. -```sh -jet -h -Usage of jet: - -host string - Database host path (Example: localhost) - -port string - Database port - -user string - Database user - -password string - The user’s password - -dbname string - name of the database - -schema string - Database schema name. (default "public") - -path string - Destination dir for generated files. - -sslmode string - Whether or not to use SSL(optional) (default "disable") - -params string - Additional connection string parameters(optional) -``` - -To generate jet SQL Builder and Data Model files from postgres database we need to call `jet` generator with postgres -connection parameters and root destination folder path for generated files. -Assuming we are running local postgres database, with user `jet`, database `jetdb` and schema `dvds` we will use this command: - -```sh -jet -host=localhost -port=5432 -user=jet -password=jet -dbname=jetdb -schema=dvds -path=./gen -``` -```sh -Connecting to postgres database: host=localhost port=5432 user=jet password=jet dbname=jetdb sslmode=disable -Retrieving schema information... - FOUND 15 table(s), 1 enum(s) -Destination directory: ./gen/jetdb/dvds -Cleaning up schema destination directory... -Generating table sql builder files... -Generating table model files... -Generating enum sql builder files... -Generating enum model files... -Done -``` -#### 2) Generating from code - -The same files can be generated manually from code. - -``` -import "github.com/go-jet/jet/generator/postgres" - -... - -err = postgres.Generate("./gen", postgres.DBConnection{ - Host: "localhost", - Port: "5432", - User: "jet", - Password: "jet", - DBName: "jetdb", - SchemaName: "dvds", - SslMode: "disable", -}) -``` - -In both ways, generator will: -- connect to postgres database and retrieve information about the _tables_ and _enums_ of `dvds` schema -- delete everything in schema destination folder - `./gen/jetdb/dvds`, -- and finally generate sql builder and model files for each schema tables and enums. - -Generated files folder structure will look like this: -``` -|-- gen # destination folder -| `-- jetdb # database name -| `-- dvds # schema name -| |-- enum # sql builder folder for enums -| | |-- mpaa_rating.go -| |-- table # sql builder folder for tables -| |-- actor.go -| |-- address.go -| |-- film.go - ... -| |-- model # Plain Old Data for every enum and table -| | |-- actor.go -| | |-- address.go -| | |-- film.go - ... - -``` - -Table and enums from database schema are used as a template to generate two types of Go files: -* SQL Builder files - used to write type safe SQL statements in Go (`enum` and `table` package) -* Model files - used to combine and store result from database queries (`model` package) diff --git a/wiki/INSERT.md b/wiki/INSERT.md deleted file mode 100644 index 9f335c5..0000000 --- a/wiki/INSERT.md +++ /dev/null @@ -1,137 +0,0 @@ - - -INSERT statement is used to insert a single record or multiple records -into a table. More about PostgreSQL INSERT statement can be found here: https://www.postgresql.org/docs/11/sql-insert.html - -Following clauses are supported: -- INSERT(columns...) - list of columns for insert -- VALUES(values...) - list of values -- MODEL(model) - list of values for columns will be extracted from model object -- MODELS([]model) - list of values for columns will be extracted from list of model objects -- QUERY(select) - select statement that supplies the rows to be inserted. -- RETURNING(output_expression...) - An expressions to be computed and returned by the INSERT statement after each row is inserted. -The expressions can use any column names of the table. Write _TableName_.AllColumns to return all columns. - - -_This list might be extended with feature Jet releases._ - - -## Example -### Insert row by row - -``` -insertStmt := Link.INSERT(Link.ID, Link.URL, Link.Name, Link.Description). - VALUES(100, "http://www.postgresqltutorial.com", "PostgreSQL Tutorial", DEFAULT). - VALUES(101, "http://www.google.com", "Google", DEFAULT). - VALUES(102, "http://www.yahoo.com", "Yahoo", nil) -``` -Debug SQL of above insert statement: - -```sql -INSERT INTO test_sample.link (id, url, name, description) VALUES - (100, 'http://www.postgresqltutorial.com', 'PostgreSQL Tutorial', DEFAULT), - (101, 'http://www.google.com', 'Google', DEFAULT), - (102, 'http://www.yahoo.com', 'Yahoo', NULL) -``` - - - -There is also shorthand notation for inserting model data: -``` -tutorial := model.Link{ - ID: 100, - URL: "http://www.postgresqltutorial.com", - Name: "PostgreSQL Tutorial", -} - -google := model.Link{ - ID: 101, - URL: "http://www.google.com", - Name: "Google", -} - -yahoo := model.Link{ - ID: 102, - URL: "http://www.yahoo.com", - Name: "Yahoo", -} - -insertStmt := Link.INSERT(Link.ID, Link.URL, Link.Name, Link.Description). - MODEL(turorial). - MODEL(google). - MODEL(yahoo) -``` -Or event shorter if model data is in the slice: -``` -insertStmt := Link.INSERT(Link.ID, Link.URL, Link.Name, Link.Description). - MODELS([]model.Link{turorial, google, yahoo}) -``` -`Link.ID, Link.URL, Link.Name, Link.Description` - is the same as Link.AllColumns -so above statement can be simplified to: - -``` -insertStmt := Link.INSERT(Link.AllColumns). - MODELS([]model.Link{turorial, google, yahoo}) -``` - -`Link.ID` is a primary key autoincrement column so it can be omitted in INSERT statement. -`Link.MutableColumns` - is shorthand notation for list of all columns minus primary key columns. - -``` -insertStmt := Link.INSERT(Link.MutableColumns). - MODELS([]model.Link{turorial, google, yahoo}) -``` - -Inserts using `VALUES`, `MODEL` and `MODELS` can appear as the part of the same insert statement. - -``` -insertStmt := Link.INSERT(Link.ID, Link.URL, Link.Name, Link.Description, Link.Description). - VALUES(101, "http://www.google.com", "Google", DEFAULT, DEFAULT). - MODEL(turorial). - MODELS([]model.Link{yahoo}) -``` - -### Insert using query -``` -// duplicate first 10 entries -insertStmt := Link. - INSERT(Link.URL, Link.Name). - QUERY( - SELECT(Link.URL, Link.Name). - FROM(Link). - WHERE(Link.ID.GT(Int(0)).AND(Link.ID.LT_EQ(10))), - ) -``` - -## Execute statement - -To execute insert statement and get sql.Result: - -``` -res, err := insertStmt.Exec(db) -``` - -To execute insert statement and return row records inserted, insert statement has to have RETURNING clause: -``` -insertStmt := Link.INSERT(Link.ID, Link.URL, Link.Name, Link.Description). - VALUES(100, "http://www.postgresqltutorial.com", "PostgreSQL Tutorial", DEFAULT). - VALUES(101, "http://www.google.com", "Google", DEFAULT). - RETURNING(Link.ID, Link.URL, Link.Name, Link.Description) // or RETURNING(Link.AllColumns) - -dest := []model.Link{} - -err := insertStmt.Query(db, &dest) -``` - -Use `ExecContext` and `QueryContext` to provide context object to execution. - -##### SQL table used for the example: -```sql -CREATE TABLE IF NOT EXISTS link ( - id serial PRIMARY KEY, - url VARCHAR (255) NOT NULL, - name VARCHAR (255) NOT NULL, - description VARCHAR (255) -); -``` - diff --git a/wiki/Installation.md b/wiki/Installation.md deleted file mode 100644 index 4a667fc..0000000 --- a/wiki/Installation.md +++ /dev/null @@ -1,13 +0,0 @@ - -### Prerequisites - -To install Jet package, you need to install Go and set your Go workspace first. - -[Go](https://golang.org/) **version 1.8+ is required** - -### Installation - -Use the bellow command to install jet -```sh -$ go get -u github.com/go-jet/jet -``` \ No newline at end of file diff --git a/wiki/LOCK.md b/wiki/LOCK.md deleted file mode 100644 index b86e849..0000000 --- a/wiki/LOCK.md +++ /dev/null @@ -1,45 +0,0 @@ - -LOCK statement obtains a table-level lock, waiting if necessary for any conflicting locks to be released. -More about LOCK statement in PostgreSQL: https://www.postgresql.org/docs/11/sql-lock.html - -Following clauses are supported: -- IN(mode) - mode specifies which locks this lock conflicts with. -Mode can be: - - jet.LOCK_ACCESS_SHARE - - jet.LOCK_ROW_SHARE - - jet.LOCK_ROW_EXCLUSIVE - - jet.LOCK_SHARE_UPDATE_EXCLUSIVE - - jet.LOCK_SHARE - - jet.LOCK_SHARE_ROW_EXCLUSIVE - - jet.LOCK_EXCLUSIVE - - jet.LOCK_ACCESS_EXCLUSIVE -- NOWAIT() - locked table should not wait for any conflicting locks to be released. If the specified lock(s) -cannot be acquired immediately without waiting, the transaction is aborted. - -## Example - - -``` -lockStmt := Address. - LOCK(). - IN(jet.LOCK_ACCESS_SHARE). - NOWAIT() -``` - -Debug SQL of above statement: - -```sql -LOCK TABLE dvds.address IN ACCESS SHARE MODE NOWAIT; -``` - -### Execute statement - -To execute update statement and get sql.Result: - -``` -res, err := lockStmt.Exec(db) -``` - -Use `ExecContext` to provide context object to execution. - - \ No newline at end of file diff --git a/wiki/Model-data.md b/wiki/Model-data.md deleted file mode 100644 index 3f2fe1b..0000000 --- a/wiki/Model-data.md +++ /dev/null @@ -1,141 +0,0 @@ -## Model - -Model files are simple Go struct types used to combine and store result of SQL queries. They are -autogenerated from database tables and enums. - -### Table model files - -Following rules are applied to generate model types from database tables: - -- for every table there is one Go file generated. File name is in snake case of the table name. -- every model file contains one struct type. Type name is a camel case of table name. Package name -is always `model`. -- for every column of table there is a field in model struct type. Field name is camel case of column name. -See below table for type mapping. -- fields are pointer types, if they relate to column that can be NULL. -- fields corresponds to primary key columns are tagged with `sql:"primary_key"`. -_This tag is used during query execution to group row results into desired arbitrary structure. -See more at [Execution](https://github.com/go-jet/jet/wiki/Execution)_ - - -##### Mappings of database types to Go types - -| Database type(postgres) | Go type | -| ----------------------------------------------- | -------------------------------------------------- | -| boolean | bool | -| smallint | int16 | -| integer | int32 | -| bigint | int64 | -| real | float32 | -| numeric, decimal, double precision | float64 | -| date, timestamp, time(with or without timezone) | time.Time | -| bytea | []byte | -| uuid | uuid.UUID | -| enum | enum name | -| text, character, character varying, | | -| and all remaining types | string | - -#### Example: - -Sql table `address`: -``` -CREATE TABLE dvds.address -( - address_id serial NOT NULL DEFAULT, - address character varying(50) NOT NULL, - address2 character varying(50), - district character varying(20) NOT NULL, - city_id smallint NOT NULL, - postal_code character varying(10), - phone character varying(20) NOT NULL, - last_update timestamp without time zone NOT NULL DEFAULT now(), - CONSTRAINT address_pkey PRIMARY KEY (address_id) -) -``` - -Autogenerated model file `address.go`: - -``` -package model - -import ( - "time" -) - -type Address struct { - AddressID int32 `sql:"primary_key"` - Address string - Address2 *string - District string - CityID int16 - PostalCode *string - Phone string - LastUpdate time.Time -} -``` - -### Enum model files - -Following rules are applied to generate model files from database enums: - -- for every enum there is one Go file generated. File name is a snake case of enum name. -- every file contains one renamed string type. Type name is a camel case of enum name. -Package name is always `model`. -Enum type has two helper methods to: - - initialize correctly from database query result - - easily convert enum to string. -- for every enum value there is one constant defined. -Name of the constant is in format `{CamelCase(enum_name)}_{CamelCase(enum_value_name)}`. - -#### Example - -Enum `mpaa_rating`: -``` -CREATE TYPE dvds.mpaa_rating AS ENUM - ('G', 'PG', 'PG-13', 'R', 'NC-17'); -``` - -Autogenerated model file `mpaa_rating.go` - -``` -package model - -import "errors" - -type MpaaRating string - -const ( - MpaaRating_G MpaaRating = "G" - MpaaRating_Pg MpaaRating = "PG" - MpaaRating_Pg13 MpaaRating = "PG-13" - MpaaRating_R MpaaRating = "R" - MpaaRating_Nc17 MpaaRating = "NC-17" -) - -func (e *MpaaRating) Scan(value interface{}) error { - if v, ok := value.(string); !ok { - return errors.New("jet: Invalid data for MpaaRating enum") - } else { - switch string(v) { - case "G": - *e = MpaaRating_G - case "PG": - *e = MpaaRating_Pg - case "PG-13": - *e = MpaaRating_Pg13 - case "R": - *e = MpaaRating_R - case "NC-17": - *e = MpaaRating_Nc17 - default: - return errors.New("Inavlid data " + string(v) + "for MpaaRating enum") - } - - return nil - } -} - -func (e MpaaRating) String() string { - return string(e) -} -``` diff --git a/wiki/SELECT.md b/wiki/SELECT.md deleted file mode 100644 index 5f94d5d..0000000 --- a/wiki/SELECT.md +++ /dev/null @@ -1,293 +0,0 @@ - -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 - -Following clauses are supported: -- SELECT(expressions...) - expressions to form output rows of the SELECT statement. -- DISTINCT() - remove all duplicate rows from result set -- FROM(tableSource...) - specifies one or more source tables for the SELECT. -- WHERE(condition) - only rows for which condition returns true will be selected. -- GROUP BY(groupingElement, ...) - will condense into a single row all selected rows that share the same values for the grouped expressions. -- HAVING(condition) - eliminates group rows that do not satisfy the condition -- ORDER BY(orderBy, ...) - causes the result rows to be sorted according to the specified expression(s) -- LIMIT(count) - specifies the maximum number of rows to return -- OFFSET(start) - specifies the number of rows to skip before starting to return rows -- FOR(lockMode) - how SELECT will lock rows as they are obtained from the table - Lock mode can be: - - jet.UPDATE() - - jet.NO_KEY_UPDATE() - - jet.SHARE() - - jet.KEY_SHARE() - - Lock mode can be extended with following clauses: - - NOWAIT() - - SKIP_LOCKED() - -- UNION(select) / UNION_ALL(select) - computes the set union of the rows returned by the involved SELECT statements -- INTERSECT(select) / INTERSECT_ALL(select) - computes the set intersection of the rows returned by the involved SELECT statements -- EXCEPT(select) / EXCEPT_ALL(select) - computes the set of rows that are in the result of the left SELECT statement but not in the result of the right one - -_This list might be extended with feature Jet releases._ - -### Examples per clause - -##### 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().SKIP_LOCKED()) - -SQL: -FOR NO KEY UPDATE SKIP LOCKED -``` - -##### 10. Set clauses (UNION, UNION_ALL, INTERSECT, INTERSECT_ALL, EXCEPT, EXCEPT_ALL) - -``` -Go: -SELECT(Payment.Amount).FROM(Payment) -UNION_ALL(SELECT(Payment.Amount).FROM(Payment)) - -Sql: -( - ( - SELECT payment.amount AS "payment.amount" - FROM dvds.payment - ) - UNION - ( - SELECT payment.amount AS "payment.amount" - FROM dvds.payment - ) -); -``` - -### Two forms of select statements in Jet - -#### 1. Classical select statement -Columns selected are before table source 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 -Table sources 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 source 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) -``` \ No newline at end of file diff --git a/wiki/SQL-Builder.md b/wiki/SQL-Builder.md deleted file mode 100644 index c7ce07c..0000000 --- a/wiki/SQL-Builder.md +++ /dev/null @@ -1,117 +0,0 @@ - -## SQL Builder - -SQL Builder files are Go files, containing types necessary to write type safe SQL queries in Go. They are -autogenerated from database tables and enums. File names is always snake case of the table or enum name. - -### Table SQL Builder files - -Following rules are applied to generate table SQL Builder files: - -- for every table there is one Go SQL Builder file generated. -- every file contains one type - struct with nested jet.Table. -- for every column of table there is a field column in SQL Builder table type. -Field name is camel case of column name. See below table for type mapping. -- `AllColumns` is used as shorthand notation for list of all columns. -- `MutableColumns` are all columns minus primary key columns _(Useful in INSERT or UPDATE statements)_. - -##### Mappings of database types to sql builder column types: - -| Database type(postgres) | Sql builder column type | -| ----------------------------------------------- | -------------------------------------------------- | -| boolean | ColumnBool | -| smallint, integer, bigint | ColumnInteger | -| real, numeric, decimal, double precision | ColumnFloat | -| date | ColumnDate | -| timestamp without time zone | ColumnTimestamp | -| timestamp with time zone | ColumnTimestampz | -| time without time zone | ColumnTime | -| time with time zone | ColumnTimez | -| enums, text, character, character varying | | -| bytea, uuid | | -| and all remaining types | ColumnString | - -#### Example - -Sql table `address`: -``` -CREATE TABLE dvds.address -( - address_id serial NOT NULL DEFAULT, - address character varying(50) NOT NULL, - address2 character varying(50), - district character varying(20) NOT NULL, - city_id smallint NOT NULL, - postal_code character varying(10), - phone character varying(20) NOT NULL, - last_update timestamp without time zone NOT NULL DEFAULT now(), - CONSTRAINT address_pkey PRIMARY KEY (address_id) -) -``` - -Part of the table sql builder file for table `address`. - -``` -package table - -import ( - "github.com/go-jet/jet" -) - -var Address = newAddressTable() - -type AddressTable struct { - jet.Table - - //Columns - AddressID jet.ColumnInteger - Address jet.ColumnString - Address2 jet.ColumnString - District jet.ColumnString - CityID jet.ColumnInteger - PostalCode jet.ColumnString - Phone jet.ColumnString - LastUpdate jet.ColumnTimestamp - - AllColumns jet.ColumnList - MutableColumns jet.ColumnList -} -``` - -### Enum SQL Builder files - -Following rules are applied to generate enum SQL Builder files: - -- for every enum there is one Go SQL Builder file generated. -- every file contains one type. Type name is a camel case of enum name. -- for every enum value there is a field in SQL Builder enum struct. Field name is camel case of enum value. Type is -jet.StringExpression, meaning it can be used by string expressions methods. - -#### Example - -Enum `mpaa_rating`: -``` -CREATE TYPE dvds.mpaa_rating AS ENUM - ('G', 'PG', 'PG-13', 'R', 'NC-17'); -``` - -Enum SQL Builder file for `mpaa_rating`: -``` -package enum - -import "github.com/go-jet/jet" - -var MpaaRating = &struct { - G jet.StringExpression - PG jet.StringExpression - PG13 jet.StringExpression - R jet.StringExpression - NC17 jet.StringExpression -}{ - G: jet.NewEnumValue("G"), - PG: jet.NewEnumValue("PG"), - PG13: jet.NewEnumValue("PG-13"), - R: jet.NewEnumValue("R"), - NC17: jet.NewEnumValue("NC-17"), -} -``` diff --git a/wiki/Scan-to-arbitrary-destination.md b/wiki/Scan-to-arbitrary-destination.md deleted file mode 100644 index 4bfc5cc..0000000 --- a/wiki/Scan-to-arbitrary-destination.md +++ /dev/null @@ -1,423 +0,0 @@ -## Scan to arbitrary destination - -Statements `Query` and `QueryContext` methods perform scan and grouping of row result to arbitrary `destination` structure. - -- `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. - - -### How scan works? - -The easiest way to understand how scan works is by an example. - -Lets say we want to retrieve list of cities, with list of customers for each city, and address for each customer. -For simplicity we will narrow the choice to 'London' and 'York'. - -Go SQL builder select statement: -``` -stmt := City. - INNER_JOIN(Address, Address.CityID.EQ(City.CityID)). - INNER_JOIN(Customer, Customer.AddressID.EQ(Address.AddressID)). - SELECT( - City.CityID, - City.City, - Address.AddressID, - Address.Address, - Customer.CustomerID, - Customer.LastName, - ). - WHERE(City.City.EQ(String("London")).OR(City.City.EQ(String("York")))). - ORDER_BY(City.CityID, Address.AddressID, Customer.CustomerID) -``` -_Note that we are using jet select statement format([TODO]())_ - -Debug sql of above statement: -``` -SELECT city.city_id AS "city.city_id", - city.city AS "city.city", - address.address_id AS "address.address_id", - address.address AS "address.address", - customer.customer_id AS "customer.customer_id", - customer.last_name AS "customer.last_name" -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') OR (city.city = 'York') -ORDER BY city.city_id, address.address_id, customer.customer_id; -``` - -Note that every column is aliased by default. Format is "`table_name`.`column_name`" - -Above statement will produce following result set: - -|_row_| city.city_id | city.city | address.address_id | address.address | customer.customer_id | customer.last_name | -|--- | ------------ | ------------- | ------------------- | -------------------- | -------------------- | ------------------ | -| _1_| 312 | "London" | 256 | "1497 Yuzhou Drive" | 252 | "Hoffman"| -| _2_| 312 | "London" | 517 | "548 Uruapan Street" | 512 | "Vines" | -| _3_| 589 | "York" | 502 | "1515 Korla Way" | 497 | "Sledge" | - -Lets execute statement and scan result set to destination `dest`: - ``` -var dest []struct { - model.City - - Customers []struct{ - model.Customer - - Address model.Address - } -} - -err := stmt.Query(db, &dest) - ``` - -Note that camel case of result set column names(aliases) is the same as `model type name`.`field name`. -For instance `city.city_id` -> `City.CityID`. This is being used to find appropriate column for each destination model field. -It is not an error if there is not a column for each destination model field. Table and column names does not have -to be in snake case. - -`Query` uses reflection to introspect destination type structure, and result set column names(aliases), to find appropriate destination field for result set column. -Every new destination struct object is cached by his and all the parents primary key. So grouping to work correctly at least table primary keys has to appear in query result set. If there is no primary key in a result set -row number is used as grouping condition(which is always unique). -For instance, after row 1 is processed, two objects are stored to cache: -``` -Key: Object: -(City(312)) -> (*struct { model.City; Customers []struct { model.Customer; Address model.Address } }) -(City(312)),(Customer(252),Address(256)) -> (*struct { model.Customer; Address model.Address }) -``` -After row 2 processing only one object is stored to cache, because city with city_id 312 is already in cache. -``` -Key: Object: -(City(312)) -> pulled from cache -(City(312)),(Customer(512),Address(517)) -> (*struct { model.Customer; Address model.Address }) -``` - -Lets print `dest` as a json, to visualize `Query` result: - - ``` - [ - { - "CityID": 312, - "City": "London", - "CountryID": 0, - "LastUpdate": "0001-01-01T00:00:00Z", - "Customers": [ - { - "CustomerID": 252, - "StoreID": 0, - "FirstName": "", - "LastName": "Hoffman", - "Email": null, - "AddressID": 0, - "Activebool": false, - "CreateDate": "0001-01-01T00:00:00Z", - "LastUpdate": null, - "Active": null, - "Address": { - "AddressID": 256, - "Address": "1497 Yuzhou Drive", - "Address2": null, - "District": "", - "CityID": 0, - "PostalCode": null, - "Phone": "", - "LastUpdate": "0001-01-01T00:00:00Z" - } - }, - { - "CustomerID": 512, - "StoreID": 0, - "FirstName": "", - "LastName": "Vines", - "Email": null, - "AddressID": 0, - "Activebool": false, - "CreateDate": "0001-01-01T00:00:00Z", - "LastUpdate": null, - "Active": null, - "Address": { - "AddressID": 517, - "Address": "548 Uruapan Street", - "Address2": null, - "District": "", - "CityID": 0, - "PostalCode": null, - "Phone": "", - "LastUpdate": "0001-01-01T00:00:00Z" - } - } - ] - }, - { - "CityID": 589, - "City": "York", - "CountryID": 0, - "LastUpdate": "0001-01-01T00:00:00Z", - "Customers": [ - { - "CustomerID": 497, - "StoreID": 0, - "FirstName": "", - "LastName": "Sledge", - "Email": null, - "AddressID": 0, - "Activebool": false, - "CreateDate": "0001-01-01T00:00:00Z", - "LastUpdate": null, - "Active": null, - "Address": { - "AddressID": 502, - "Address": "1515 Korla Way", - "Address2": null, - "District": "", - "CityID": 0, - "PostalCode": null, - "Phone": "", - "LastUpdate": "0001-01-01T00:00:00Z" - } - } - ] - } - ] - ``` - -All the fields missing source column in result set are initialized with empty value. -City of `London` has two customers, which is the product of object reuse in `ROW 2` processing. - -### Custom model files - -Destinations are not limited to just model files, any destination will work, as long as camel case of result set column -is equal to `model type name`.`field name`. -Custom model type can have field of any type listed in [Mappings of database types to Go types](), -plus any type that implements `sql.Scanner` interface. - -#### Named types - -Lets rewrite above example to use custom named model files: - -``` -type MyAddress struct { - ID int32 `sql:"primary_key"` - AddressLine string -} - -type MyCustomer struct { - ID int32 `sql:"primary_key"` - LastName *string - - Address MyAddress -} - -type MyCity struct { - ID int32 `sql:"primary_key"` - Name string - - Customers []MyCustomer -} - -dest2 := []MyCity{} - -stmt2 := City. - INNER_JOIN(Address, Address.CityID.EQ(City.CityID)). - INNER_JOIN(Customer, Customer.AddressID.EQ(Address.AddressID)). - SELECT( - City.CityID.AS("my_city.id"), //snake case - City.City.AS("myCity.Name"), //camel case - Address.AddressID.AS("My_Address.id"), //mixed case - Address.Address.AS("my address.address line"), //with spaces - 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")))). - ORDER_BY(City.CityID, Address.AddressID, Customer.CustomerID) - -err := stmt2.Query(db, &dest2) -``` - -Destination type names and field names are now changed. Every type has 'My' prefix, every primary key column is named `ID`, - `LastName` is now string pointer etc. -Because we are using custom types with changed identifier, every column has to be aliased. -For instance: `City.CityID.AS("my_city.id")`, ` City.City.AS("myCity.Name")` etc. -**Table names, column names and aliases doesn't have to be in a snake case. CamelCase, PascalCase or some other mixed space is also supported, -but it is strongly recommended to use snake case for database identifiers.** - -Json of new destination is also changed: - -``` -[ - { - "ID": 312, - "Name": "London", - "Customers": [ - { - "ID": 252, - "LastName": "Hoffman", - "Address": { - "ID": 256, - "AddressLine": "1497 Yuzhou Drive" - } - }, - { - "ID": 512, - "LastName": "Vines", - "Address": { - "ID": 517, - "AddressLine": "548 Uruapan Street" - } - } - ] - }, - { - "ID": 589, - "Name": "York", - "Customers": [ - { - "ID": 497, - "LastName": "Sledge", - "Address": { - "ID": 502, - "AddressLine": "1515 Korla Way" - } - } - ] - } -] -``` - -#### Anonymous custom types - -There is no need to create new named type for every custom model. -Destination type can be declared inline without naming any new type. - -``` -var dest []struct { - CityID int32 `sql:"primary_key"` - CityName string - - Customers []struct { - CustomerID int32 `sql:"primary_key"` - LastName string - - Address struct { - AddressID int32 `sql:"primary_key"` - AddressLine string - } - } -} - -stmt := City. - INNER_JOIN(Address, Address.CityID.EQ(City.CityID)). - INNER_JOIN(Customer, Customer.AddressID.EQ(Address.AddressID)). - SELECT( - City.CityID.AS("city_id"), - City.City.AS("city_name"), - Customer.CustomerID.AS("customer_id"), - Customer.LastName.AS("last_name"), - Address.AddressID.AS("address_id"), - Address.Address.AS("address_line"), - ). - WHERE(City.City.EQ(String("London")).OR(City.City.EQ(String("York")))). - ORDER_BY(City.CityID, Address.AddressID, Customer.CustomerID) - -err := stmt.Query(db, &dest) -``` -Aliasing is now simplified. Alias contains only (column/field) name. -On the other hand, we can not have 3 fields named `ID`, because aliases have to be unique. - -### Tagging model files - -Desired mapping can be set the other way around as well, by tagging destination fields and types. - -``` -var dest []struct { - CityID int32 `sql:"primary_key" alias:"city.city_id"` - CityName string `alias:"city.city"` - - Customers []struct { - // because the whole struct is refering to 'customer.*' (see below tag), - // we can just use 'alias:"customer_id"`' instead of 'alias:"customer.customer_id"`' - CustomerID int32 `sql:"primary_key" alias:"customer_id"` - LastName *string `alias:"last_name"` - - Address struct { - AddressID int32 `sql:"primary_key" alias:"AddressId"` // cammel case for alias will work as well - AddressLine string `alias:"address.address"` // full alias will work as well - } `alias:"address.*"` // struct is now refering to all address.* columns - - } `alias:"customer.*"` // struct is now refering to all customer.* columns -} - -stmt := City. - INNER_JOIN(Address, Address.CityID.EQ(City.CityID)). - INNER_JOIN(Customer, Customer.AddressID.EQ(Address.AddressID)). - SELECT( - City.CityID, - City.City, - Customer.CustomerID, - Customer.LastName, - Address.AddressID, - Address.Address, - ). - WHERE(City.City.EQ(String("London")).OR(City.City.EQ(String("York")))). - ORDER_BY(City.CityID, Address.AddressID, Customer.CustomerID) - -err := stmt.Query(db, &dest) -``` - -This kind of mapping is more complicated than in previous examples, and it should avoided and used -only when there is no alternative. Usually this is the case in two scenarios: - -1) Self join - -``` -var dest []struct{ - model.Employee - - Manager *model.Employee `alias:"Manager.*"` //or just `alias:"Manager" -} - -manager := Employee.AS("Manager") - -stmt := Employee. - LEFT_JOIN(manager, Employee.ReportsTo.EQ(manager.EmployeeId)). - SELECT( - Employee.EmployeeId, - Employee.FirstName, - manager.EmployeeId, - manager.FirstName, - ) -``` -_This example could also be written without tag alias, by just introducing of a new type `type Manager model.Employee`._ - -2) Slices of go base types (int32, float64, string, ...) - -``` -var dest struct { - model.Film - - InventoryIDs []int32 `alias:"inventory.inventory_id"` -} -``` - -### Combining autogenerated and custom model files - -It is allowed to combine autogenerated and custom model files. -For instance: - -``` -type MyCustomer struct { - ID int32 `sql:"primary_key"` - LastName string - - Address model.Address //model.Address is autogenerated model type -} - -type MyCity struct { - ID int32 `sql:"primary_key"` - Name string - - Customers []MyCustomer -} -``` - - diff --git a/wiki/Statements.md b/wiki/Statements.md deleted file mode 100644 index 093a46a..0000000 --- a/wiki/Statements.md +++ /dev/null @@ -1,51 +0,0 @@ - -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._ - -### Executing statements - -Statements can be executed with following methods: -- `Query(db execution.DB, destination interface{}) error` - executes statements over database connection db and maps -row result result set to destination. -- `QueryContext(db execution.DB, context context.Context, destination interface{}) error` - executes statement with a -context over database connection db and maps row result result set to destination. -- `Exec(db execution.DB) (sql.Result, error)` - executes statement over db connection and returns `sql.Result`. -- `ExecContext(db execution.DB, context context.Context) (sql.Result, error)` - executes statement with context over -db connection and returns `sql.Result`. - -Each execution method first creates parametrized sql query with list of arguments and then initiates query on database connection. -Exec and ExecContext are just a wrappers around database `Exec` and `ExecContext`. - -`Query` and `QueryContext` are bit more complex, the are wrappers around database `Query` and `QueryContext`, -but they also perform grouping of row result to arbitrary `destination` structure. - -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` - - -### Debugging statements - -Statements SQL can be debugged in two ways: - -- `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. diff --git a/wiki/UPDATE.md b/wiki/UPDATE.md deleted file mode 100644 index 172ccc9..0000000 --- a/wiki/UPDATE.md +++ /dev/null @@ -1,87 +0,0 @@ -UPDATE changes the values of the specified columns in all rows that satisfy the condition. -More about UPDATE statement in PostgreSQL: https://www.postgresql.org/docs/11/sql-update.html - -Following clauses are supported: -- UPDATE(columns...) - list of columns to update -- SET(values...) - list of values for columns -- MODEL(model) - list of values for columns will be extracted from model object -- WHERE(condition) - only rows for which condition returns true will be updated. -- RETURNING(output_expression...) - An expressions to be computed and returned by the UPDATE statement after each row is updated. -The expressions can use any column names of the table. Write _TableName_.AllColumns to return all columns. - -_This list might be extended with feature Jet releases._ - -## Example - -``` -// replace all Bing links with Yahoo -updateStmt := Link. - UPDATE(Link.Name, Link.URL). - SET("Yahoo", "http://yahoo.com"). - WHERE(Link.Name.EQ(String("Bing"))) -``` - -Debug sql of above statement: -```sql -UPDATE test_sample.link -- 'test_sample' is name of the schema -SET (name, url) = ('Yahoo', 'http://yahoo.com') -WHERE link.name = 'Bing'; -``` - -Short-hand notation to update values from model data: - -``` -yahoo := model.Link{ - URL: "http://www.yahoo.com", - Name: "Yahoo", -} - -updateStmt := Link. - UPDATE(Link.Name, Link.URL, Link.Description). - MODEL(yahoo). - WHERE(Link.Name.EQ(String("Bing"))) -``` - -`Link.Name, Link.URL, Link.Description` - can be replaced with Link.MutableColumns(all columns minus primary key column). -Primary key columns usually are not updated. - -``` -updateStmt := Link. - UPDATE(Link.MutableColumns). - MODEL(yahoo). - WHERE(Link.Name.EQ(String("Bing"))) -``` - -### Execute statement - -To execute update statement and get sql.Result: - -``` -res, err := updateStmt.Exec(db) -``` - -To execute update statement and return row records updated, statement has to have RETURNING clause: -``` -updateStmt := Link. - UPDATE(Link.MutableColumns). - MODEL(yahoo). - WHERE(Link.Name.EQ(String("Bing"))). - RETURNING(Link.AllColumns) - -dest := []model.Link{} - -err := updateStmt.Query(db, &dest) -``` - -Use `ExecContext` and `QueryContext` to provide context object to execution. - -##### SQL table used for the example: -```sql -CREATE TABLE IF NOT EXISTS link ( - id serial PRIMARY KEY, - url VARCHAR (255) NOT NULL, - name VARCHAR (255) NOT NULL, - description VARCHAR (255) -); -``` - diff --git a/wiki/_Sidebar.md b/wiki/_Sidebar.md deleted file mode 100644 index e504309..0000000 --- a/wiki/_Sidebar.md +++ /dev/null @@ -1,13 +0,0 @@ - -* [Installation](https://github.com/go-jet/jet/wiki/Installation) -* [Generator](https://github.com/go-jet/jet/wiki/Generator) -* [Model files](https://github.com/go-jet/jet/wiki/Model-data.md) -* [SQL Builder](https://github.com/go-jet/jet/wiki/SQL-Builder.md) - * [Expressions](https://github.com/go-jet/jet/wiki/Expressions) - * [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) -* [Scan to arbitrary destination](https://github.com/go-jet/jet/wiki/Scan-to-arbitrary-destination.md)