{"id":13412268,"url":"https://github.com/VinGarcia/ksql","last_synced_at":"2025-03-14T18:31:08.458Z","repository":{"id":37315424,"uuid":"294882257","full_name":"VinGarcia/ksql","owner":"VinGarcia","description":"A Simple and Powerful Golang SQL Library","archived":false,"fork":false,"pushed_at":"2024-10-24T02:39:24.000Z","size":826,"stargazers_count":312,"open_issues_count":1,"forks_count":22,"subscribers_count":12,"default_branch":"master","last_synced_at":"2024-10-24T19:02:54.185Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":"","language":"Go","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/VinGarcia.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2020-09-12T06:08:45.000Z","updated_at":"2024-10-24T02:39:29.000Z","dependencies_parsed_at":"2024-10-24T05:45:33.422Z","dependency_job_id":"513f6c6a-f4f7-4d25-968b-79d35526955f","html_url":"https://github.com/VinGarcia/ksql","commit_stats":{"total_commits":500,"total_committers":8,"mean_commits":62.5,"dds":0.03200000000000003,"last_synced_commit":"e67ef737e7e08206a703d7454431ef31e5e513c6"},"previous_names":["vingarcia/kisssql"],"tags_count":123,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/VinGarcia%2Fksql","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/VinGarcia%2Fksql/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/VinGarcia%2Fksql/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/VinGarcia%2Fksql/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/VinGarcia","download_url":"https://codeload.github.com/VinGarcia/ksql/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":221495289,"owners_count":16832453,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2024-07-30T20:01:22.822Z","updated_at":"2024-10-26T04:30:21.001Z","avatar_url":"https://github.com/VinGarcia.png","language":"Go","funding_links":[],"categories":["Database Drivers","Generators","数据库驱动程序","Data Integration Frameworks"],"sub_categories":["Relational Database Drivers","关系数据库驱动程序"],"readme":"[![CI](https://github.com/VinGarcia/ksql/actions/workflows/ci.yml/badge.svg)](https://github.com/VinGarcia/ksql/actions/workflows/ci.yml)\n[![codecov](https://codecov.io/gh/VinGarcia/ksql/branch/master/graph/badge.svg?token=5CNJ867C66)](https://codecov.io/gh/VinGarcia/ksql)\n[![Go Reference](https://pkg.go.dev/badge/github.com/vingarcia/ksql.svg)](https://pkg.go.dev/github.com/vingarcia/ksql)\n![Go Report Card](https://goreportcard.com/badge/github.com/vingarcia/ksql)\n\n# KSQL the Keep it Simple SQL library\n\nKSQL was created to offer an actually simple and satisfactory\ntool for interacting with SQL Databases in Golang.\n\nThe core goal of KSQL is not to offer new features that\nare unavailable on other libraries (although we do have some),\nbut to offer a well-thought and well-planned API so that users\nhave an easier time, learning, debugging, and avoiding common pitfalls.\n\nKSQL is also decoupled from its backend so that\nthe actual communication with the database is performed by\nwell-known and trusted technologies, namely: `pgx` and `database/sql`.\nYou can even create your own backend adapter for KSQL which is\nuseful in some situations.\n\nIn this README you will find examples for \"Getting Started\" with the library,\nfor more advanced use-cases [please read our Wiki](https://github.com/VinGarcia/ksql/wiki).\n\n## Outstanding Features\n\n- Every operation returns errors a single time, so its easier to handle them\n- Helper functions for everyday operations, namely: Insert, Patch and Delete\n- Generic and powerful functions for Querying and Scanning data into structs\n- Works on top of existing battle-tested libraries such as `database/sql` and `pgx`\n- Supports `sql.Scanner` and `sql.Valuer` and also all `pgx` special types (when using `kpgx`)\n- And many other features designed to make your life easier\n\n## Let's start with some Code:\n\nThis short example below is a TLDR version to illustrate how easy it is to use KSQL.\n\nYou will find more complete examples in the sections below.\n\n\u003e This example is available on ./examples/overview/main.go if you want to run it\n\n```golang\npackage main\n\nimport (\n\t\"context\"\n\t\"errors\"\n\t\"fmt\"\n\t\"log\"\n\n\t\"github.com/vingarcia/ksql\"\n\t\"github.com/vingarcia/ksql/adapters/kpgx\"\n)\n\nvar UsersTable = ksql.NewTable(\"users\", \"user_id\")\n\ntype User struct {\n\tID    int    `ksql:\"user_id\"`\n\tName  string `ksql:\"name\"`\n\tType  string `ksql:\"type\"`\n\tPosts []Post\n}\n\n// Post have a many to one relationship with User\nvar PostsTable = ksql.NewTable(\"posts\", \"post_id\")\n\ntype Post struct {\n\tID     int    `ksql:\"post_id\"`\n\tUserID int    `ksql:\"user_id\"`\n\tTitle  string `ksql:\"title\"`\n\tText   string `ksql:\"text\"`\n}\n\n// Address have a one to one relationship with User\nvar AddressesTable = ksql.NewTable(\"addresses\", \"id\")\n\ntype Address struct {\n\tID       int    `ksql:\"id\"`\n\tUserID   int    `ksql:\"user_id\"`\n\tFullAddr string `ksql:\"full_addr\"`\n}\n\nfunc main() {\n\tctx := context.Background()\n\tdbURL, closeDB := startExampleDB(ctx)\n\tdefer closeDB()\n\n\tdb, err := kpgx.New(ctx, dbURL, ksql.Config{})\n\tif err != nil {\n\t\tlog.Fatalf(\"unable connect to database: %s\", err)\n\t}\n\tdefer db.Close()\n\n\t// For querying only some attributes you can\n\t// create a custom struct like this:\n\tvar count []struct {\n\t\tCount int    `ksql:\"count\"`\n\t\tType  string `ksql:\"type\"`\n\t}\n\terr = db.Query(ctx, \u0026count, \"SELECT type, count(*) as count FROM users GROUP BY type\")\n\tif err != nil {\n\t\tlog.Fatalf(\"unable to query users: %s\", err)\n\t}\n\n\tfmt.Println(\"number of users by type:\", count)\n\n\t// For loading entities from the database KSQL can build\n\t// the SELECT part of the query for you if you omit it like this:\n\tvar adminUsers []User\n\terr = db.Query(ctx, \u0026adminUsers, \"FROM users WHERE type = $1\", \"admin\")\n\tif err != nil {\n\t\tlog.Fatalf(\"unable to query admin users: %s\", err)\n\t}\n\n\tfmt.Println(\"admin users:\", adminUsers)\n\n\t// A nice way of loading the posts of a user might be like this:\n\tvar user User\n\terr = errors.Join(\n\t\tdb.QueryOne(ctx, \u0026user, \"FROM users WHERE user_id = $1\", 42),\n\t\tdb.Query(ctx, \u0026user.Posts, \"FROM posts WHERE user_id = $1\", user.ID),\n\t)\n\tif err != nil {\n\t\tlog.Fatalf(\"unable to query users: %s\", err)\n\t}\n\n\tfmt.Println(\"user with posts:\", user)\n\n\t// You can retrieve data from joined tables like this\n\t// (notice you can either use the name of the table or the alias you choose for it in the query):\n\tvar rows []struct {\n\t\tOneUser    User    `tablename:\"users\"`\n\t\tOneAddress Address `tablename:\"addr\"`\n\t}\n\terr = db.Query(ctx, \u0026rows,\n\t\t`FROM users\n\t\tJOIN addresses addr\n\t\t  ON users.user_id = addr.user_id`,\n\t)\n\tif err != nil {\n\t\tlog.Fatalf(\"unable to query users: %s\", err)\n\t}\n\n\tfmt.Println(\"rows of joined tables:\", rows)\n}\n```\n\n\u003e Note: In the example above we are using the `$1`, `$2` and `$3` as placeholders on the query\n\u003e because this example is meant to run on top of Postgres.\n\u003e\n\u003e If you are running on top of MySQL or SQLite use `?` instead, and if you are running\n\u003e on top of SQLServer use `@p1`, `@p2` and `@p3` instead.\n\n## Supported Adapters:\n\nWe support a few different adapters,\none of them is illustrated above (`kpgx`),\nthe other ones have the exact same signature\nbut work on different databases or driver versions,\nthey are:\n\n- `kpgx.New(ctx, os.Getenv(\"DATABASE_URL\"), ksql.Config{})` for Postgres, it works on top of `pgxpool`\n  and [pgx](https://github.com/jackc/pgx) version 4, download it with:\n\n  ```bash\n  go get github.com/vingarcia/ksql/adapters/kpgx\n  ```\n- `kpgx5.New(ctx, os.Getenv(\"DATABASE_URL\"), ksql.Config{})` for Postgres, it works on top of `pgxpool`\n  and [pgx](https://github.com/jackc/pgx) version 5, download it with:\n\n  ```bash\n  go get github.com/vingarcia/ksql/adapters/kpgx5\n  ```\n- `kmysql.New(ctx, os.Getenv(\"DATABASE_URL\"), ksql.Config{})` for MySQL, it works on top of `database/sql`,\n  download it with:\n\n  ```bash\n  go get github.com/vingarcia/ksql/adapters/kmysql\n  ```\n- `ksqlserver.New(ctx, os.Getenv(\"DATABASE_URL\"), ksql.Config{})` for SQLServer, it works on top of `database/sql`,\n  download it with:\n\n  ```bash\n  go get github.com/vingarcia/ksql/adapters/ksqlserver\n  ```\n- `ksqlite3.New(ctx, os.Getenv(\"DATBAASE_PATH\"), ksql.Config{})` for SQLite3, it works on top of `database/sql`\n  and [mattn/go-sqlite3](https://github.com/mattn/go-sqlite3) which relies on CGO, download it with:\n\n  ```bash\n  go get github.com/vingarcia/ksql/adapters/ksqlite3\n  ```\n- `ksqlite.New(ctx, os.Getenv(\"DATABASE_PATH\"), ksql.Config{})` for SQLite, it works on top of `database/sql`\n  and [modernc.org/sqlite](https://modernc.org/sqlite) which does not require CGO, download it with:\n\n  ```bash\n  go get github.com/vingarcia/ksql/adapters/modernc-ksqlite\n  ```\n\nFor more detailed examples see:\n- `./examples/all_adapters/all_adapters.go`\n\n## The KSQL Interface\n\nThe current interface contains the methods the users are expected to use,\nand it is also used for making it easy to mock the whole library if needed.\n\nThis interface is declared in the project as `ksql.Provider` and is displayed below.\n\nWe plan on keeping it very simple with a small number\nof well-thought functions that cover all use cases,\nso don't expect many additions:\n\n```go\n// Provider describes the KSQL public behavior\n//\n// The Insert, Patch, Delete and QueryOne functions return `ksql.ErrRecordNotFound`\n// if no record was found or no rows were changed during the operation.\ntype Provider interface {\n\tInsert(ctx context.Context, table Table, record interface{}) error\n\tPatch(ctx context.Context, table Table, record interface{}) error\n\tDelete(ctx context.Context, table Table, idOrRecord interface{}) error\n\n\tQuery(ctx context.Context, records interface{}, query string, params ...interface{}) error\n\tQueryOne(ctx context.Context, record interface{}, query string, params ...interface{}) error\n\tQueryChunks(ctx context.Context, parser ChunkParser) error\n\n\tExec(ctx context.Context, query string, params ...interface{}) (Result, error)\n\tTransaction(ctx context.Context, fn func(Provider) error) error\n}\n```\n\n## Using KSQL\n\nIn the example below we'll cover all the most common use cases such as:\n\n1. Inserting records\n2. Updating records\n3. Deleting records\n4. Querying one or many records\n5. Making transactions\n\nMore advanced use cases are illustrated on their own pages on [our Wiki](https://github.com/VinGarcia/ksql/wiki):\n\n- [Querying in Chunks for Big Queries](https://github.com/VinGarcia/ksql/wiki/Querying-in-Chunks-for-Big-Queries)\n- [Avoiding Code Duplication with the Select Builder](https://github.com/VinGarcia/ksql/wiki/Avoiding-Code-Duplication-with-the-Select-Builder)\n- [Reusing Existing Structs on Queries with JOINs](https://github.com/VinGarcia/ksql/wiki/Reusing-Existing-Structs-on-Queries-with-JOINs)\n- [Testing Tools and `ksql.Mock`](https://github.com/VinGarcia/ksql/wiki/Testing-Tools-and-ksql.Mock)\n\nFor the more common use cases please read the example below,\nwhich is also available [here](./examples/crud/crud.go)\nif you want to compile it yourself.\n\n```Go\npackage main\n\nimport (\n\t\"context\"\n\t\"fmt\"\n\t\"time\"\n\n\t\"github.com/vingarcia/ksql\"\n\t\"github.com/vingarcia/ksql/adapters/ksqlite3\"\n\t\"github.com/vingarcia/ksql/nullable\"\n)\n\ntype User struct {\n\tID   int    `ksql:\"id\"`\n\tName string `ksql:\"name\"`\n\tAge  int    `ksql:\"age\"`\n\n\t// The following attributes are making use of the KSQL Modifiers,\n\t// you can find more about them on our Wiki:\n\t//\n\t// - https://github.com/VinGarcia/ksql/wiki/Modifiers\n\t//\n\n\t// The `json` modifier will save the address as JSON in the database\n\tAddress Address `ksql:\"address,json\"`\n\n\t// The timeNowUTC modifier will set this field to `time.Now().UTC()` before saving it:\n\tUpdatedAt time.Time `ksql:\"updated_at,timeNowUTC\"`\n\n\t// The timeNowUTC/skipUpdates modifier will set this field to `time.Now().UTC()` only\n\t// when first creating it and ignore it during updates.\n\tCreatedAt time.Time `ksql:\"created_at,timeNowUTC/skipUpdates\"`\n}\n\ntype PartialUpdateUser struct {\n\tID      int      `ksql:\"id\"`\n\tName    *string  `ksql:\"name\"`\n\tAge     *int     `ksql:\"age\"`\n\tAddress *Address `ksql:\"address,json\"`\n}\n\ntype Address struct {\n\tState string `json:\"state\"`\n\tCity  string `json:\"city\"`\n}\n\n// UsersTable informs KSQL the name of the table and that it can\n// use the default value for the primary key column name: \"id\"\nvar UsersTable = ksql.NewTable(\"users\")\n\nfunc main() {\n\tctx := context.Background()\n\n\t// In this example we'll use sqlite3:\n\tdb, err := ksqlite3.New(ctx, \"/tmp/hello.sqlite\", ksql.Config{\n\t\tMaxOpenConns: 1,\n\t})\n\tif err != nil {\n\t\tpanic(err.Error())\n\t}\n\tdefer db.Close()\n\n\t// In the definition below, please note that BLOB is\n\t// the only type we can use in sqlite for storing JSON.\n\t_, err = db.Exec(ctx, `CREATE TABLE IF NOT EXISTS users (\n\t  id INTEGER PRIMARY KEY,\n\t\tage INTEGER,\n\t\tname TEXT,\n\t\taddress BLOB,\n\t\tcreated_at DATETIME,\n\t\tupdated_at DATETIME\n\t)`)\n\tif err != nil {\n\t\tpanic(err.Error())\n\t}\n\n\tvar alison = User{\n\t\tName: \"Alison\",\n\t\tAge:  22,\n\t\tAddress: Address{\n\t\t\tState: \"MG\",\n\t\t},\n\t}\n\terr = db.Insert(ctx, UsersTable, \u0026alison)\n\tif err != nil {\n\t\tpanic(err.Error())\n\t}\n\tfmt.Println(\"Alison ID:\", alison.ID)\n\n\t// Inserting inline:\n\terr = db.Insert(ctx, UsersTable, \u0026User{\n\t\tName: \"Cristina\",\n\t\tAge:  27,\n\t\tAddress: Address{\n\t\t\tState: \"SP\",\n\t\t},\n\t})\n\tif err != nil {\n\t\tpanic(err.Error())\n\t}\n\n\t// Deleting Alison:\n\terr = db.Delete(ctx, UsersTable, alison.ID)\n\tif err != nil {\n\t\tpanic(err.Error())\n\t}\n\n\t// Retrieving Cristina, note that if you omit the SELECT part of the query\n\t// KSQL will build it for you (efficiently) based on the fields from the struct:\n\tvar cris User\n\terr = db.QueryOne(ctx, \u0026cris, \"FROM users WHERE name = ? ORDER BY id\", \"Cristina\")\n\tif err != nil {\n\t\tpanic(err.Error())\n\t}\n\tfmt.Printf(\"Cristina: %#v\\n\", cris)\n\n\t// Updating all fields from Cristina:\n\tcris.Name = \"Cris\"\n\terr = db.Patch(ctx, UsersTable, cris)\n\n\t// Changing the age of Cristina but not touching any other fields:\n\n\t// Partial update technique 1:\n\terr = db.Patch(ctx, UsersTable, struct {\n\t\tID  int `ksql:\"id\"`\n\t\tAge int `ksql:\"age\"`\n\t}{ID: cris.ID, Age: 28})\n\tif err != nil {\n\t\tpanic(err.Error())\n\t}\n\n\t// Partial update technique 2:\n\terr = db.Patch(ctx, UsersTable, PartialUpdateUser{\n\t\tID:  cris.ID,\n\t\tAge: nullable.Int(28), // (just a pointer to an int, if null it won't be updated)\n\t})\n\tif err != nil {\n\t\tpanic(err.Error())\n\t}\n\n\t// Listing first 10 users from the database\n\t// (each time you run this example a new Cristina is created)\n\t//\n\t// Note: Using this function it is recommended to set a LIMIT, since\n\t// not doing so can load too many users on your computer's memory or\n\t// cause an Out Of Memory Kill.\n\t//\n\t// If you need to query very big numbers of users we recommend using\n\t// the `QueryChunks` function.\n\tvar users []User\n\terr = db.Query(ctx, \u0026users, \"FROM users LIMIT 10\")\n\tif err != nil {\n\t\tpanic(err.Error())\n\t}\n\n\tfmt.Printf(\"Users: %#v\\n\", users)\n\n\t// Making transactions:\n\terr = db.Transaction(ctx, func(db ksql.Provider) error {\n\t\tvar cris2 User\n\t\terr = db.QueryOne(ctx, \u0026cris2, \"FROM users WHERE id = ?\", cris.ID)\n\t\tif err != nil {\n\t\t\t// This will cause an automatic rollback:\n\t\t\treturn err\n\t\t}\n\n\t\terr = db.Patch(ctx, UsersTable, PartialUpdateUser{\n\t\t\tID:  cris2.ID,\n\t\t\tAge: nullable.Int(29),\n\t\t})\n\t\tif err != nil {\n\t\t\t// This will also cause an automatic rollback and then panic again\n\t\t\t// so that we don't hide the panic inside the KSQL library\n\t\t\tpanic(err.Error())\n\t\t}\n\n\t\t// Commits the transaction\n\t\treturn nil\n\t})\n\tif err != nil {\n\t\tpanic(err.Error())\n\t}\n}\n```\n\n## Benchmark Comparison\n\nThe results of the benchmark are good for KSQL, but not flawless.\n\nThe next section summarizes the results so its more comprehensible,\nbut if you prefer to read the raw benchmark data just scroll down to the\n[Benchmark Results](https://github.com/VinGarcia/ksql#benchmark-results) section.\n\n### Summary\n\nFor transparency purposes this summary will focus\nat the benchmark showing the _worst_ results for KSQL\nwhich is querying multiple lines, this is the summary:\n\nComparing KSQL running on top of `database/sql` with `sqlx`, `sqlx` is\n5% faster than KSQL, which is in practical terms an insignificant difference.\nAnd if KSQL is running on top of `pgx` then KSQL becomes 42% faster\nbecause `pgx` is significantly faster than `sqlx`.\nFinally if you are using `sqlx` with prepared statements everytime\nthen `sqlx` is 7.5% faster than KSQL on top of `pgx`.\n\nSo between KSQL vs `sqlx` the performance difference is very small, and\nif you are using Postgres odds are KSQL will be much faster.\n\nComparing KSQL running on top of `pgx` with `pgx` itself, KSQL\nis 13.66% slower (on average), which is not insignificant but isn't much either.\n\nComparing KSQL running on top `pgx` with `gorm`, KSQL is\n11.87% faster than `gorm` or inversely `gorm` is 13.4% slower.\n\n\u003e It is worth noting that KSQL is only caching of prepared statements\n\u003e when using postgres, because this is performed by `pgx`, and this\n\u003e means that when using MySQL, SQLServer or SQLite, if you plan\n\u003e on also using prepared statements other libaries such as `sqlx` will\n\u003e be significantly faster than KSQL.\n\u003e\n\u003e We are working on adding support for cached prepared statements for\n\u003e these other databases in the future.\n\n### Benchmark Results\n\nTo understand the benchmark below you must know\nthat all tests are performed using Postgres 12.1 and\nthat we are comparing the following tools:\n\n- KSQL using the adapter that wraps `database/sql`\n- KSQL using the adapter that wraps `pgx`\n- `database/sql`\n- `sqlx`\n- `pgx` (with `pgxpool`)\n- `gorm`\n- `sqlc`\n- `sqlboiler`\n\nFor each of these tools, we are running 3 different queries:\n\nThe `insert-one` query looks like this:\n\n`INSERT INTO users (name, age) VALUES ($1, $2) RETURNING id`\n\nThe `single-row` query looks like this:\n\n`SELECT id, name, age FROM users OFFSET $1 LIMIT 1`\n\nThe `multiple-rows` query looks like this:\n\n`SELECT id, name, age FROM users OFFSET $1 LIMIT 10`\n\nKeep in mind that some of the tools tested (like GORM) actually build\nthe queries internally so the actual code used for the benchmark\nmight differ a little bit from the example ones above.\n\nWithout further ado, here are the results:\n\n```bash\n$ make bench TIME=5s\nsqlc generate\ngo test -bench=. -benchtime=5s\ngoos: linux\ngoarch: amd64\npkg: github.com/vingarcia/ksql/benchmarks\ncpu: Intel(R) Core(TM) i7-10750H CPU @ 2.60GHz\nBenchmarkInsert/ksql/sql-adapter/insert-one-12         \t    9711\t    618727 ns/op\nBenchmarkInsert/ksql/pgx-adapter/insert-one-12         \t   10000\t    555967 ns/op\nBenchmarkInsert/sql/insert-one-12                      \t    9450\t    624334 ns/op\nBenchmarkInsert/sql/prep-stmt/insert-one-12            \t   10000\t    555119 ns/op\nBenchmarkInsert/sqlx/insert-one-12                     \t    9552\t    632986 ns/op\nBenchmarkInsert/sqlx/prep-stmt/insert-one-12           \t   10000\t    560244 ns/op\nBenchmarkInsert/pgxpool/insert-one-12                  \t   10000\t    553535 ns/op\nBenchmarkInsert/gorm/insert-one-12                     \t    9231\t    668423 ns/op\nBenchmarkInsert/sqlc/insert-one-12                     \t    9589\t    632277 ns/op\nBenchmarkInsert/sqlc/prep-stmt/insert-one-12           \t   10803\t    560301 ns/op\nBenchmarkInsert/sqlboiler/insert-one-12                \t    9790\t    631464 ns/op\nBenchmarkQuery/ksql/sql-adapter/single-row-12          \t   44436\t    131191 ns/op\nBenchmarkQuery/ksql/sql-adapter/multiple-rows-12       \t   42087\t    143795 ns/op\nBenchmarkQuery/ksql/pgx-adapter/single-row-12          \t   86192\t     65447 ns/op\nBenchmarkQuery/ksql/pgx-adapter/multiple-rows-12       \t   74106\t     79004 ns/op\nBenchmarkQuery/sql/single-row-12                       \t   44719\t    134491 ns/op\nBenchmarkQuery/sql/multiple-rows-12                    \t   43218\t    138309 ns/op\nBenchmarkQuery/sql/prep-stmt/single-row-12             \t   89328\t     64162 ns/op\nBenchmarkQuery/sql/prep-stmt/multiple-rows-12          \t   84282\t     71454 ns/op\nBenchmarkQuery/sqlx/single-row-12                      \t   44118\t    132928 ns/op\nBenchmarkQuery/sqlx/multiple-rows-12                   \t   43824\t    137235 ns/op\nBenchmarkQuery/sqlx/prep-stmt/single-row-12            \t   87570\t     66610 ns/op\nBenchmarkQuery/sqlx/prep-stmt/multiple-rows-12         \t   82202\t     72660 ns/op\nBenchmarkQuery/pgxpool/single-row-12                   \t   94034\t     63373 ns/op\nBenchmarkQuery/pgxpool/multiple-rows-12                \t   86275\t     70275 ns/op\nBenchmarkQuery/gorm/single-row-12                      \t   83052\t     71539 ns/op\nBenchmarkQuery/gorm/multiple-rows-12                   \t   62636\t     89652 ns/op\nBenchmarkQuery/sqlc/single-row-12                      \t   44329\t    132659 ns/op\nBenchmarkQuery/sqlc/multiple-rows-12                   \t   44440\t    139026 ns/op\nBenchmarkQuery/sqlc/prep-stmt/single-row-12            \t   91486\t     66679 ns/op\nBenchmarkQuery/sqlc/prep-stmt/multiple-rows-12         \t   78583\t     72583 ns/op\nBenchmarkQuery/sqlboiler/single-row-12                 \t   70030\t     87089 ns/op\nBenchmarkQuery/sqlboiler/multiple-rows-12              \t   69961\t     84376 ns/op\nPASS\nok  \tgithub.com/vingarcia/ksql/benchmarks\t221.596s\nBenchmark executed at: 2023-10-22\nBenchmark executed on commit: 35b6882317e82de7773fb3908332e8ac3d127010\n```\n\n## Running the KSQL tests (for contributors)\n\nThe tests use `docker-test` for setting up all the supported databases,\nwhich means that:\n\n- You need to have `docker` installed\n- You must be able to run docker without `sudo`, i.e.\n  if you are not root you should add yourself to the docker group, e.g.:\n\n  ```bash\n  $ sudo usermod \u003cyour_username\u003e -aG docker\n  ```\n  And then restart your login session (or just reboot)\n- Finally run `make pre-download-all-images` only once so your tests don't\n  timeout downloading the database images.\n\nAfter that, you can just run the tests by using:\n\n```bash\nmake test\n```\n\n## TODO List\n\n- Add an `Upsert` helper method\n- Try to implement an automatic prepared statements cache like pgx does.\n- Update `ksqltest.FillStructWith` to work with `ksql:\"..,json\"` tagged attributes\n- Improve error messages (ongoing)\n- Finish the `kbuilder` package\n\n## Optimization Opportunities\n\n- Test if using a pointer on the field info is faster or not\n- Consider passing the cached structInfo as an argument for all the functions that use it,\n  so that we don't need to get it more than once in the same call.\n- Use a cache to store often-used queries (like pgx)\n- Preload the insert method for all dialects inside `ksql.NewTable()`\n- Use prepared statements for the helper functions, `Update`, `Insert` and `Delete`.\n\n## Features for a possible V2\n\n- Change the `.Transaction(db ksql.Provider)` to a `.Transaction(ctx context.Context)`\n- Make the `.Query()` method to return a `type Query interface { One(); All(); Chunks(); }`\n- Have an `Update()` method that updates without ignoring NULLs as `Patch()` does\n  - Have a new Modifier `skipNullUpdates` so that the Update function will do the job of the `Patch`\n  - Remove the `Patch` function.\n- Rename `NewTable()` to just `Table()` so it feels right to declare it inline when convenient\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2FVinGarcia%2Fksql","html_url":"https://awesome.ecosyste.ms/projects/github.com%2FVinGarcia%2Fksql","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2FVinGarcia%2Fksql/lists"}