diff --git a/README.md b/README.md index 0c48172..c3c77ea 100644 --- a/README.md +++ b/README.md @@ -447,8 +447,8 @@ In those cases web server handler execution time is directly proportional to lat This is not such a big problem if handler calls database couple of times, but what if web server is using ORM to retrieve all data from database. ORM usually access the database once for every object needed. Now lets say latency is 30ms and there are 100 different objects required from the database. This handler will last 3s !!!. -With Jet, handler time lost on latency between server and database is constant(because we can write complex query and return result in one database call), -and handler execution will be proportional to number or rows returned from database. +With Jet, handler time lost on latency between server and database is constant. Because we can write complex query and +return result in one database call. Handler execution will be proportional to number or rows returned from database. ORM example replaced with jet will take just 30ms + 'result scan time' = 31ms (rough estimate). With Jet you can even join the whole database and store the whole structured result in in one query call. diff --git a/generator/postgresgen/templates.go b/generator/postgresgen/templates.go index 177e623..daa0e99 100644 --- a/generator/postgresgen/templates.go +++ b/generator/postgresgen/templates.go @@ -26,6 +26,8 @@ import ( "github.com/go-jet/jet" ) +var {{camelize .Name}} = new{{.GoStructName}}() + type {{.GoStructName}} struct { jet.Table @@ -37,7 +39,14 @@ type {{.GoStructName}} struct { AllColumns jet.ColumnList } -var {{camelize .Name}} = new{{.GoStructName}}() +// creates new {{.GoStructName}} with assigned alias +func (a *{{.GoStructName}}) AS(alias string) *{{.GoStructName}} { + aliasTable := new{{.GoStructName}}() + + aliasTable.Table.AS(alias) + + return aliasTable +} func new{{.GoStructName}}() *{{.GoStructName}} { var ( @@ -58,14 +67,6 @@ func new{{.GoStructName}}() *{{.GoStructName}} { } } -func (a *{{.GoStructName}}) AS(alias string) *{{.GoStructName}} { - aliasTable := new{{.GoStructName}}() - - aliasTable.Table.AS(alias) - - return aliasTable -} - ` var dataModelTemplate = `package model diff --git a/tests/init/init.go b/tests/init/init.go index 979bfd4..bf16bb1 100644 --- a/tests/init/init.go +++ b/tests/init/init.go @@ -3,7 +3,7 @@ package main import ( "database/sql" "fmt" - postgres_generator "github.com/go-jet/jet/generator/postgresgen" + "github.com/go-jet/jet/generator/postgresgen" "github.com/go-jet/jet/tests/dbconfig" "io/ioutil" ) @@ -33,7 +33,7 @@ func main() { _, err = db.Exec(string(testSampleSql)) - err = postgres_generator.Generate("./.gentestdata", postgres_generator.DBConnection{ + err = postgresgen.Generate("./.gentestdata", postgresgen.DBConnection{ Host: dbconfig.Host, Port: "5432", User: dbconfig.User, diff --git a/wiki/Generator.md b/wiki/Generator.md new file mode 100644 index 0000000..692800f --- /dev/null +++ b/wiki/Generator.md @@ -0,0 +1,271 @@ + +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: + +#### Generating from command line + +Install jetgen to GOPATH bin folder. This will allow generating jet files from the command line. + +```sh +go install github.com/go-jet/jet/cmd/jetgen +``` + +Make sure GOPATH bin folder is added to the PATH environment variable. + +Test jetgen can be found in the PATH. +```sh +jetgen -h +Usage of jetgen: + -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) +``` + +Now to generate sample database: +```sh +jetgen -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) +Cleaning up destination directory... +Generating table sql builder files... +Generating table model files... +Generating enum sql builder files... +Generating enum model files... +Done +``` +#### Generating from code + +``` +import "github.com/go-jet/jet/generator/postgresgen" + +... + +err = postgresgen.Generate("./gen", postgresgen.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 destination folder `./gen`, +- generate sql builder and model Go files for each schema tables and enums into destination folder `./gen`. + +Generated files folder structure will look like this: +```sh +|-- 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: Files used to write type safe SQL statements in Go (enum and table package) +* Model files: Files used to store result from database queries (model package) + +#### SQL builder files +Part of the sample sql builder file for table `film`. + +``` +package table + +import ( + "github.com/go-jet/jet" +) +var Film = newFilmTable() + +type FilmTable struct { + jet.Table + + //Columns + FilmID jet.ColumnInteger + Title jet.ColumnString + Description jet.ColumnString + ReleaseYear jet.ColumnInteger + LanguageID jet.ColumnInteger + RentalDuration jet.ColumnInteger + RentalRate jet.ColumnFloat + Length jet.ColumnInteger + ReplacementCost jet.ColumnFloat + Rating jet.ColumnString + LastUpdate jet.ColumnTimestamp + SpecialFeatures jet.ColumnString + Fulltext jet.ColumnString + + AllColumns jet.ColumnList +} + +// creates new FilmTable with assigned alias +func (a *FilmTable) AS(alias string) *FilmTable { + aliasTable := newFilmTable() + + aliasTable.Table.AS(alias) + + return aliasTable +} +``` +Table name and column names are camelized inside sql builder type. +`AllColumns` is used as shorthand notation for list of all columns `FilmID, Title, Description,...`. + +Mappings of database types to sql builder column types are following: + +| 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 | + +Sql builder file for enum type `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"), +} + +``` +#### Model files + +Sample model file for table `film`: +``` +package model + +import ( + "time" +) + +type Film struct { + FilmID int32 `sql:"primary_key"` + Title string + Description *string + ReleaseYear *int32 + LanguageID int16 + RentalDuration int16 + RentalRate float64 + Length *int16 + ReplacementCost float64 + Rating *MpaaRating + LastUpdate time.Time + SpecialFeatures *string + Fulltext string +} + +``` +For every column of table `film` there is appropriate field in model type. +Fields corresponding 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 TODO: +Fields are pointer types, if they relate to column that can be NULL. + +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 | + + +Part of the sample model file for enum `mpaa_rating`: + +``` +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" +) + +``` + +For a reference SQL table definition of table `film`: + +```sql +CREATE TABLE dvds.film ( + film_id integer DEFAULT nextval('dvds.film_film_id_seq'::regclass) NOT NULL, + title character varying(255) NOT NULL, + description text, + release_year dvds.year, + language_id smallint NOT NULL, + rental_duration smallint DEFAULT 3 NOT NULL, + rental_rate numeric(4,2) DEFAULT 4.99 NOT NULL, + length smallint, + replacement_cost numeric(5,2) DEFAULT 19.99 NOT NULL, + rating dvds.mpaa_rating DEFAULT 'G'::dvds.mpaa_rating, + last_update timestamp without time zone DEFAULT now() NOT NULL, + special_features text[], + fulltext tsvector NOT NULL +); +``` diff --git a/wiki/Installation.md b/wiki/Installation.md new file mode 100644 index 0000000..4a667fc --- /dev/null +++ b/wiki/Installation.md @@ -0,0 +1,13 @@ + +### 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