Update README and the README build script

pull/32/head
Vinícius Garcia 2022-12-04 20:39:29 -03:00
parent e7896dc16e
commit d6aa694f82
5 changed files with 100 additions and 240 deletions

View File

@ -3,7 +3,7 @@ path=./...
GOBIN=$(shell go env GOPATH)/bin
TIME=1s
TIME=5s
test: setup go-mod-tidy
$(GOBIN)/richgo test $(path) $(args)
@ -18,7 +18,9 @@ bench: go-mod-tidy
@make --no-print-directory -C benchmarks TIME=$(TIME) | tee benchmark.tmp
@echo "Benchmark executed at: $$(date --iso)" | tee -a benchmark.tmp
@echo "Benchmark executed on commit: $$(git rev-parse HEAD)" | tee -a benchmark.tmp
go run scripts/build-readme-from-template.go readme.template.md benchmark.tmp
readme: benchmark.tmp README.template.md
go run scripts/build-readme-from-template.go README.template.md examples/crud/crud.go benchmark.tmp
lint: setup go-mod-tidy
@$(GOBIN)/staticcheck $(path) $(args)

View File

@ -3,7 +3,6 @@
[![Go Reference](https://pkg.go.dev/badge/github.com/vingarcia/ksql.svg)](https://pkg.go.dev/github.com/vingarcia/ksql)
![Go Report Card](https://goreportcard.com/badge/github.com/vingarcia/ksql)
# KSQL the Keep it Simple SQL library
KSQL was created to offer an actually simple and satisfactory
@ -150,23 +149,35 @@ package main
import (
"context"
"fmt"
"time"
"github.com/vingarcia/ksql"
"github.com/vingarcia/ksql/adapters/ksqlite3"
"github.com/vingarcia/ksql/nullable"
)
// User ...
type User struct {
ID int `ksql:"id"`
Name string `ksql:"name"`
Age int `ksql:"age"`
// This field will be saved as JSON in the database
// The following attributes are making use of the KSQL Modifiers,
// you can find more about them on our Wiki:
//
// - https://github.com/VinGarcia/ksql/wiki/Modifiers
//
// The `json` modifier will save the address as JSON in the database
Address Address `ksql:"address,json"`
// The timeNowUTC modifier will set this field to `time.Now().UTC()` before saving it:
UpdatedAt time.Time `ksql:"updated_at,timeNowUTC"`
// The timeNowUTC/skipUpdates modifier will set this field to `time.Now().UTC()` only
// when first creating it and ignore it during updates.
CreatedAt time.Time `ksql:"created_at,timeNowUTC/skipUpdates"`
}
// PartialUpdateUser ...
type PartialUpdateUser struct {
ID int `ksql:"id"`
Name *string `ksql:"name"`
@ -174,7 +185,6 @@ type PartialUpdateUser struct {
Address *Address `ksql:"address,json"`
}
// Address ...
type Address struct {
State string `json:"state"`
City string `json:"city"`
@ -211,7 +221,9 @@ func main() {
id INTEGER PRIMARY KEY,
age INTEGER,
name TEXT,
address BLOB
address BLOB,
created_at DATETIME,
updated_at DATETIME
)`)
if err != nil {
panic(err.Error())
@ -373,43 +385,43 @@ goos: linux
goarch: amd64
pkg: github.com/vingarcia/ksql/benchmarks
cpu: Intel(R) Core(TM) i7-10750H CPU @ 2.60GHz
BenchmarkInsert/ksql/sql-adapter/insert-one-12 9256 626985 ns/op
BenchmarkInsert/ksql/pgx-adapter/insert-one-12 11056 548748 ns/op
BenchmarkInsert/sql/insert-one-12 9565 623659 ns/op
BenchmarkInsert/sql/prep-stmt/insert-one-12 10000 541058 ns/op
BenchmarkInsert/sqlx/insert-one-12 9319 637775 ns/op
BenchmarkInsert/sqlx/prep-stmt/insert-one-12 10000 549806 ns/op
BenchmarkInsert/pgxpool/insert-one-12 10000 546349 ns/op
BenchmarkInsert/gorm/insert-one-12 8859 675650 ns/op
BenchmarkInsert/sqlc/insert-one-12 9889 634589 ns/op
BenchmarkInsert/sqlc/prep-stmt/insert-one-12 10000 552079 ns/op
BenchmarkInsert/sqlboiler/insert-one-12 9536 633515 ns/op
BenchmarkQuery/ksql/sql-adapter/single-row-12 41222 144799 ns/op
BenchmarkQuery/ksql/sql-adapter/multiple-rows-12 38523 156556 ns/op
BenchmarkQuery/ksql/pgx-adapter/single-row-12 85074 72465 ns/op
BenchmarkQuery/ksql/pgx-adapter/multiple-rows-12 70690 84502 ns/op
BenchmarkQuery/sql/single-row-12 41802 144467 ns/op
BenchmarkQuery/sql/multiple-rows-12 39248 147765 ns/op
BenchmarkQuery/sql/prep-stmt/single-row-12 80530 71376 ns/op
BenchmarkQuery/sql/prep-stmt/multiple-rows-12 76730 77769 ns/op
BenchmarkQuery/sqlx/single-row-12 41960 146817 ns/op
BenchmarkQuery/sqlx/multiple-rows-12 39349 152887 ns/op
BenchmarkQuery/sqlx/prep-stmt/single-row-12 81045 73004 ns/op
BenchmarkQuery/sqlx/prep-stmt/multiple-rows-12 75256 78604 ns/op
BenchmarkQuery/pgxpool/single-row-12 82630 72241 ns/op
BenchmarkQuery/pgxpool/multiple-rows-12 81619 74408 ns/op
BenchmarkQuery/gorm/single-row-12 76700 78651 ns/op
BenchmarkQuery/gorm/multiple-rows-12 62342 95746 ns/op
BenchmarkQuery/sqlc/single-row-12 41563 146143 ns/op
BenchmarkQuery/sqlc/multiple-rows-12 40240 149534 ns/op
BenchmarkQuery/sqlc/prep-stmt/single-row-12 83230 72397 ns/op
BenchmarkQuery/sqlc/prep-stmt/multiple-rows-12 79408 78645 ns/op
BenchmarkQuery/sqlboiler/single-row-12 65866 93841 ns/op
BenchmarkQuery/sqlboiler/multiple-rows-12 65091 94486 ns/op
BenchmarkInsert/ksql/sql-adapter/insert-one-12 9373 658434 ns/op
BenchmarkInsert/ksql/pgx-adapter/insert-one-12 10000 571623 ns/op
BenchmarkInsert/sql/insert-one-12 9423 627719 ns/op
BenchmarkInsert/sql/prep-stmt/insert-one-12 10000 559441 ns/op
BenchmarkInsert/sqlx/insert-one-12 9651 637823 ns/op
BenchmarkInsert/sqlx/prep-stmt/insert-one-12 10000 574260 ns/op
BenchmarkInsert/pgxpool/insert-one-12 10000 568112 ns/op
BenchmarkInsert/gorm/insert-one-12 8504 696791 ns/op
BenchmarkInsert/sqlc/insert-one-12 9504 662484 ns/op
BenchmarkInsert/sqlc/prep-stmt/insert-one-12 10000 568876 ns/op
BenchmarkInsert/sqlboiler/insert-one-12 9298 667913 ns/op
BenchmarkQuery/ksql/sql-adapter/single-row-12 39939 150412 ns/op
BenchmarkQuery/ksql/sql-adapter/multiple-rows-12 35901 156905 ns/op
BenchmarkQuery/ksql/pgx-adapter/single-row-12 83677 72461 ns/op
BenchmarkQuery/ksql/pgx-adapter/multiple-rows-12 71182 89788 ns/op
BenchmarkQuery/sql/single-row-12 40140 147991 ns/op
BenchmarkQuery/sql/multiple-rows-12 39210 154899 ns/op
BenchmarkQuery/sql/prep-stmt/single-row-12 82580 76769 ns/op
BenchmarkQuery/sql/prep-stmt/multiple-rows-12 76880 77115 ns/op
BenchmarkQuery/sqlx/single-row-12 42120 144501 ns/op
BenchmarkQuery/sqlx/multiple-rows-12 39396 155193 ns/op
BenchmarkQuery/sqlx/prep-stmt/single-row-12 84583 72094 ns/op
BenchmarkQuery/sqlx/prep-stmt/multiple-rows-12 75465 78078 ns/op
BenchmarkQuery/pgxpool/single-row-12 87724 72368 ns/op
BenchmarkQuery/pgxpool/multiple-rows-12 77012 77658 ns/op
BenchmarkQuery/gorm/single-row-12 74268 80303 ns/op
BenchmarkQuery/gorm/multiple-rows-12 63933 100220 ns/op
BenchmarkQuery/sqlc/single-row-12 39211 149178 ns/op
BenchmarkQuery/sqlc/multiple-rows-12 38748 153076 ns/op
BenchmarkQuery/sqlc/prep-stmt/single-row-12 83739 76111 ns/op
BenchmarkQuery/sqlc/prep-stmt/multiple-rows-12 75025 80939 ns/op
BenchmarkQuery/sqlboiler/single-row-12 63660 95534 ns/op
BenchmarkQuery/sqlboiler/multiple-rows-12 64256 98597 ns/op
PASS
ok github.com/vingarcia/ksql/benchmarks 226.109s
Benchmark executed at: 2022-11-13
Benchmark executed on commit: 5bfb5cd92affae29dab3499b07fcd36b70a20057
ok github.com/vingarcia/ksql/benchmarks 224.967s
Benchmark executed at: 2022-12-04
Benchmark executed on commit: e7896dc16ef8ede091e5d1568bd53096af65a1ef
```
## Running the KSQL tests (for contributors)

View File

@ -3,16 +3,20 @@
[![Go Reference](https://pkg.go.dev/badge/github.com/vingarcia/ksql.svg)](https://pkg.go.dev/github.com/vingarcia/ksql)
![Go Report Card](https://goreportcard.com/badge/github.com/vingarcia/ksql)
# KSQL the Keep it Simple SQL library
KSQL was created to offer an actually simple and satisfactory
tool for interacting with SQL Databases in Golang.
The core idea on KSQL is to offer an easy to use interface,
the actual communication with the database is decoupled so we can use
KSQL on top of `pgx`, `database/sql` and possibly other tools.
You can even create you own backend adapter for KSQL which is
The core goal of KSQL is not to offer new features that
are unavailable on other libraries (although we do have some),
but to offer a well-thought and well-planned API so that users
have an easier time, learning, debugging, and avoiding common pitfalls.
KSQL is also decoupled from its backend so that
the actual communication with the database is performed by
well-known and trusted technologies, namely: `pgx` and `database/sql`.
You can even create your own backend adapter for KSQL which is
useful in some situations.
In this README you will find examples for "Getting Started" with the library,
@ -140,185 +144,7 @@ which is also available [here](./examples/crud/crud.go)
if you want to compile it yourself.
```Go
package main
import (
"context"
"fmt"
"github.com/vingarcia/ksql"
"github.com/vingarcia/ksql/adapters/ksqlite3"
"github.com/vingarcia/ksql/nullable"
)
// User ...
type User struct {
ID int `ksql:"id"`
Name string `ksql:"name"`
Age int `ksql:"age"`
// This field will be saved as JSON in the database
Address Address `ksql:"address,json"`
}
// PartialUpdateUser ...
type PartialUpdateUser struct {
ID int `ksql:"id"`
Name *string `ksql:"name"`
Age *int `ksql:"age"`
Address *Address `ksql:"address,json"`
}
// Address ...
type Address struct {
State string `json:"state"`
City string `json:"city"`
}
// UsersTable informs KSQL the name of the table and that it can
// use the default value for the primary key column name: "id"
var UsersTable = ksql.NewTable("users")
func main() {
ctx := context.Background()
// The available adapters are:
// - kpgx.New(ctx, connURL, ksql.Config{})
// - kmysql.New(ctx, connURL, ksql.Config{})
// - ksqlserver.New(ctx, connURL, ksql.Config{})
// - ksqlite3.New(ctx, connURL, ksql.Config{})
//
// For more detailed examples see:
// - `./examples/all_adapters/all_adapters.go`
//
// In this example we'll use sqlite3:
db, err := ksqlite3.New(ctx, "/tmp/hello.sqlite", ksql.Config{
MaxOpenConns: 1,
})
if err != nil {
panic(err.Error())
}
defer db.Close()
// In the definition below, please note that BLOB is
// the only type we can use in sqlite for storing JSON.
_, err = db.Exec(ctx, `CREATE TABLE IF NOT EXISTS users (
id INTEGER PRIMARY KEY,
age INTEGER,
name TEXT,
address BLOB
)`)
if err != nil {
panic(err.Error())
}
var alison = User{
Name: "Alison",
Age: 22,
Address: Address{
State: "MG",
},
}
err = db.Insert(ctx, UsersTable, &alison)
if err != nil {
panic(err.Error())
}
fmt.Println("Alison ID:", alison.ID)
// Inserting inline:
err = db.Insert(ctx, UsersTable, &User{
Name: "Cristina",
Age: 27,
Address: Address{
State: "SP",
},
})
if err != nil {
panic(err.Error())
}
// Deleting Alison:
err = db.Delete(ctx, UsersTable, alison.ID)
if err != nil {
panic(err.Error())
}
// Retrieving Cristina, note that if you omit the SELECT part of the query
// KSQL will build it for you (efficiently) based on the fields from the struct:
var cris User
err = db.QueryOne(ctx, &cris, "FROM users WHERE name = ? ORDER BY id", "Cristina")
if err != nil {
panic(err.Error())
}
fmt.Printf("Cristina: %#v\n", cris)
// Updating all fields from Cristina:
cris.Name = "Cris"
err = db.Patch(ctx, UsersTable, cris)
// Changing the age of Cristina but not touching any other fields:
// Partial update technique 1:
err = db.Patch(ctx, UsersTable, struct {
ID int `ksql:"id"`
Age int `ksql:"age"`
}{ID: cris.ID, Age: 28})
if err != nil {
panic(err.Error())
}
// Partial update technique 2:
err = db.Patch(ctx, UsersTable, PartialUpdateUser{
ID: cris.ID,
Age: nullable.Int(28), // (just a pointer to an int, if null it won't be updated)
})
if err != nil {
panic(err.Error())
}
// Listing first 10 users from the database
// (each time you run this example a new Cristina is created)
//
// Note: Using this function it is recommended to set a LIMIT, since
// not doing so can load too many users on your computer's memory or
// cause an Out Of Memory Kill.
//
// If you need to query very big numbers of users we recommend using
// the `QueryChunks` function.
var users []User
err = db.Query(ctx, &users, "FROM users LIMIT 10")
if err != nil {
panic(err.Error())
}
fmt.Printf("Users: %#v\n", users)
// Making transactions:
err = db.Transaction(ctx, func(db ksql.Provider) error {
var cris2 User
err = db.QueryOne(ctx, &cris2, "FROM users WHERE id = ?", cris.ID)
if err != nil {
// This will cause an automatic rollback:
return err
}
err = db.Patch(ctx, UsersTable, PartialUpdateUser{
ID: cris2.ID,
Age: nullable.Int(29),
})
if err != nil {
// This will also cause an automatic rollback and then panic again
// so that we don't hide the panic inside the KSQL library
panic(err.Error())
}
// Commits the transaction
return nil
})
if err != nil {
panic(err.Error())
}
}
{{ .crudExample -}}
```
## Benchmark Comparison
@ -362,7 +188,7 @@ Without further ado, here are the results:
```bash
$ make bench TIME=5s
{{- .benchmark -}}
{{ .benchmark -}}
```
## Running the KSQL tests (for contributors)

View File

@ -3,23 +3,35 @@ package main
import (
"context"
"fmt"
"time"
"github.com/vingarcia/ksql"
"github.com/vingarcia/ksql/adapters/ksqlite3"
"github.com/vingarcia/ksql/nullable"
)
// User ...
type User struct {
ID int `ksql:"id"`
Name string `ksql:"name"`
Age int `ksql:"age"`
// This field will be saved as JSON in the database
// The following attributes are making use of the KSQL Modifiers,
// you can find more about them on our Wiki:
//
// - https://github.com/VinGarcia/ksql/wiki/Modifiers
//
// The `json` modifier will save the address as JSON in the database
Address Address `ksql:"address,json"`
// The timeNowUTC modifier will set this field to `time.Now().UTC()` before saving it:
UpdatedAt time.Time `ksql:"updated_at,timeNowUTC"`
// The timeNowUTC/skipUpdates modifier will set this field to `time.Now().UTC()` only
// when first creating it and ignore it during updates.
CreatedAt time.Time `ksql:"created_at,timeNowUTC/skipUpdates"`
}
// PartialUpdateUser ...
type PartialUpdateUser struct {
ID int `ksql:"id"`
Name *string `ksql:"name"`
@ -27,13 +39,12 @@ type PartialUpdateUser struct {
Address *Address `ksql:"address,json"`
}
// Address ...
type Address struct {
State string `json:"state"`
City string `json:"city"`
}
// UsersTable informs ksql the name of the table and that it can
// UsersTable informs KSQL the name of the table and that it can
// use the default value for the primary key column name: "id"
var UsersTable = ksql.NewTable("users")
@ -64,7 +75,9 @@ func main() {
id INTEGER PRIMARY KEY,
age INTEGER,
name TEXT,
address BLOB
address BLOB,
created_at DATETIME,
updated_at DATETIME
)`)
if err != nil {
panic(err.Error())
@ -102,7 +115,7 @@ func main() {
}
// Retrieving Cristina, note that if you omit the SELECT part of the query
// ksql will build it for you (efficiently) based on the fields from the struct:
// KSQL will build it for you (efficiently) based on the fields from the struct:
var cris User
err = db.QueryOne(ctx, &cris, "FROM users WHERE name = ? ORDER BY id", "Cristina")
if err != nil {

View File

@ -7,14 +7,15 @@ import (
)
func main() {
if len(os.Args) < 3 {
if len(os.Args) < 4 {
log.Fatalf(
"USAGE: go run scripts/build-readme-from-template.go TEMPLATE_FILEPATH BENCHMARK_FILEPATH",
"USAGE: go run scripts/build-readme-from-template.go PATH_TO_TEMPLATE PATH_TO_CRUD_EXAMPLE PATH_TO_BENCHMARK",
)
}
templateFilepath := os.Args[1]
benchmarkFilepath := os.Args[2]
crudExampleFilepath := os.Args[2]
benchmarkFilepath := os.Args[3]
data, err := os.ReadFile(templateFilepath)
if err != nil {
@ -26,6 +27,11 @@ func main() {
log.Fatalf("unable to parse README template '%s': %s", templateFilepath, err)
}
crudExample, err := os.ReadFile(crudExampleFilepath)
if err != nil {
log.Fatalf("unable to read benchmark results '%s': %s", benchmarkFilepath, err)
}
benchmark, err := os.ReadFile(benchmarkFilepath)
if err != nil {
log.Fatalf("unable to read benchmark results '%s': %s", benchmarkFilepath, err)
@ -37,7 +43,8 @@ func main() {
}
err = t.Execute(f, map[string]interface{}{
"benchmark": string(benchmark),
"crudExample": string(crudExample),
"benchmark": string(benchmark),
})
if err != nil {
log.Fatalf("error executing template file: %s", err)