{"id":19610554,"url":"https://github.com/kaboc/sqlp","last_synced_at":"2025-02-26T17:22:20.732Z","repository":{"id":57501846,"uuid":"138506607","full_name":"kaboc/sqlp","owner":"kaboc","description":"[WIP] Extension of Go's database/sql for easier and simpler use","archived":false,"fork":false,"pushed_at":"2020-09-04T02:09:46.000Z","size":40,"stargazers_count":3,"open_issues_count":2,"forks_count":0,"subscribers_count":1,"default_branch":"master","last_synced_at":"2025-01-09T10:02:19.650Z","etag":null,"topics":["database","go","go-","sql"],"latest_commit_sha":null,"homepage":"https://pkg.go.dev/github.com/kaboc/sqlp","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/kaboc.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}},"created_at":"2018-06-24T18:40:37.000Z","updated_at":"2022-04-07T15:03:57.000Z","dependencies_parsed_at":"2022-09-19T08:50:16.203Z","dependency_job_id":null,"html_url":"https://github.com/kaboc/sqlp","commit_stats":null,"previous_names":[],"tags_count":4,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/kaboc%2Fsqlp","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/kaboc%2Fsqlp/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/kaboc%2Fsqlp/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/kaboc%2Fsqlp/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/kaboc","download_url":"https://codeload.github.com/kaboc/sqlp/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":240898863,"owners_count":19875268,"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":["database","go","go-","sql"],"created_at":"2024-11-11T10:30:03.775Z","updated_at":"2025-02-26T17:22:20.703Z","avatar_url":"https://github.com/kaboc.png","language":"Go","readme":"# sqlp\n\n[![Build Status](https://travis-ci.org/kaboc/sqlp.svg?branch=master)](https://travis-ci.org/kaboc/sqlp)\n\nsqlp is a Go package extending `database/sql` to make it a little easier to use by adding some features that may come in handy for you.\nThe key features are:\n\n* Bulk inserting data in structs into a table\n* Reading rows into structs, maps or slices\n* Easier binding of values to unnamed/named placeholders\n\n### Table of Contents\n\n* [Installation](#installation)\n* [Requirements](#requirements)\n* [Usage](#usage)\n    * [Example Table](#example-table)\n    * [Getting Started](#getting-started)\n    * [Insert](#insert)\n        * [Note](#note)\n    * [Scan](#scan)\n        * [Into struct](#into-struct)\n        * [Into map](#into-map)\n        * [Into slice](#into-slice)\n    * [Select](#select)\n        * [Into slice of structs](#into-slice-of-structs)\n        * [Into slice of maps](#into-slice-of-maps)\n        * [Into slice of slices](#into-slice-of-slices)\n* [Placeholders](#placeholders)\n    * [Unnamed Placeholder](#unnamed-placeholder)\n        * [For different types of placeholder](#for-different-types-of-placeholder)\n    * [Named Placeholder](#named-placeholder)\n* [License](#license)\n\n## Requirements\n\n* Go 1.15 or newer\n\n## Installation\n\n```\ngo get github.com/kaboc/sqlp\n```\n\n## Usage\n\nBasic usage is mostly the same as that of `database/sql`, so only major differences are described in this section.\n\n### Example Table\n\nMySQL\n\n```sql\nCREATE TABLE user (\n  id int(10) unsigned NOT NULL AUTO_INCREMENT,\n  name varchar(32) NOT NULL,\n  age tinyint(3) unsigned NOT NULL,\n  recorded_at datetime DEFAULT NULL,\n  PRIMARY KEY (id)\n) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;\n```\n\nPostgreSQL\n\n```sql\ncreate table \"user\" (\n  id serial not null primary key,\n  name varchar(32) not null,\n  age smallint not null,\n  recorded_at timestamp\n);\n```\n\n### Getting Started\n\nImport `sqlp` and other necessary packages including a database driver like `go-sql-driver/mysql`.\n\n```go\nimport (\n    _ \"github.com/go-sql-driver/mysql\"\n    \"github.com/kaboc/sqlp\"\n)\n```\n\n```go\ndb, err := sqlp.Open(\"mysql\", \"user:pw@tcp(host:3306)/dbname\")\n```\n\nUse `sqlp.Init()` instead if there is a connection already opened by `database/sql`'s `Open()`.\n\n```go\nsqlDB, err := sql.Open(\"mysql\", \"user:pw@tcp(host:3306)/dbname\")\ndb := sqlp.Init(sqlDB)\n```\n\n### Insert\n\nYou can bulk insert multiple rows easily using a slice of structs containing sets of data to be inserted.\n\n```go\ntype tUser struct {\n    Name       string\n    Age        int\n    RecordedAt sqlp.NullTime `col:\"recorded_at\"`\n}\n\nnow := sqlp.NullTime{Time: time.Now(), Valid: true}\ndata := []tUser{\n    {Name: \"User1\", Age: 22, RecordedAt: now},\n    {Name: \"User2\", Age: 27, RecordedAt: sqlp.NullTime{}},\n    {Name: \"User3\", Age: 31, RecordedAt: now},\n}\n\nres, err := db.Insert(\"user\", data)\nif err != nil {\n    log.Fatal(err)\n}\n\ncnt, _ := res.RowsAffected()\nfmt.Printf(\"%d rows were affected\", cnt) // 3 rows were affected\n```\n\nStruct fields need to be capitalized so that sqlp can access them.\n\nA tag is necessary only when the field name is not the same as the column name.\nIn the above example, `col:\"age\"` can be omitted since column names are case insensitive in MySQL by default and `Age` and `age` are not distinguished.\n\nValues are processed via [placeholders](#placeholders) internally and escaped to be safe. There is no need to worry about SQL injection.\n\n#### Note\n\n* If the table name is a reserved keyword, it has to be enclosed with back quotes, double quotes, etc. depending on the DBMS. Below is an example for PostgreSQL.\n\n    ```go\n    res, err := db.Insert(`\"user\"`, data)\n    ```\n\n### Scan\n\n#### Into struct\n\n```go\ntype tUser struct {\n    Name       string\n    Age        int\n    RecordedAt sqlp.NullTime\n}\n\nrows, err := db.Query(`SELECT name, age, recorded_at FROM user`)\nif err != nil {\n    log.Fatal(err)\n}\ndefer rows.Close()\n\nfor rows.Next() {\n    var u tUser\n    err = rows.ScanToStruct(\u0026u)\n    if err != nil {\n        log.Fatal(err)\n    }\n    fmt.Printf(\"%s: %d yo [%s, %t]\\n\", u.Name, u.Age, u.RecordedAt.Time, u.RecordedAt.Valid)\n}\n\n// User1: 22 yo [2018-06-24 01:23:45 +0000 UTC, true]\n// User2: 27 yo [0001-01-01 00:00:00 +0000 UTC, false]\n// User3: 31 yo [2018-06-24 01:23:45 +0000 UTC, true]\n```\n\nColumns are mapped to corresponding struct fields.\n\nHere, unlike the previous [Insert](#insert) example, the `RecordedAt` field does not have the `` `col:\"recorded_at` `` tag.\nThis is because `RecordedAt` is regarded as identical to `recorded_at` by case-insensitive comparison after underscores are removed.\n\n#### Into map\n\n```go\nfor rows.Next() {\n    u, err := rows.ScanToMap()\n    if err != nil {\n        log.Fatal(err)\n    }\n    fmt.Printf(\"%s: %s yo [%s]\\n\", u[\"name\"], u[\"age\"], u[\"recorded_at\"])\n}\n\n// User1: 22 yo [2018-06-24T01:23:45+00:00]\n// User2: 27 yo []\n// User3: 31 yo [2018-06-24T01:23:45+00:00]\n```\n\n#### Into slice\n\n```go\nfor rows.Next() {\n    u, err := rows.ScanToSlice()\n    if err != nil {\n        log.Fatal(err)\n    }\n    fmt.Printf(\"%s: %s yo, [%s]\\n\", u[0], u[1], u[2])\n}\n\n// User1: 22 yo, [2018-06-24T01:23:45+00:00]\n// User2: 27 yo, []\n// User3: 31 yo, [2018-06-24T01:23:45+00:00]\n```\n\n### Select\n\n#### Into slice of structs\n\n```go\ntype tUser struct {\n    Name       string\n    Age        int\n    RecordedAt sqlp.NullTime\n}\n\nvar u []tUser\nerr := db.SelectToStruct(\u0026u, `SELECT name, age, recorded_at FROM user`)\nfmt.Println(u)\n```\n\nThis saves you the bother of making a query and then scanning each row.\nIt is convenient, but be careful not to use up huge amounts of memory by fetching too many rows at a time.\n\n#### Into slice of maps\n\n```go\nu, err := db.SelectToMap(`SELECT name, age, recorded_at FROM user`)\nfmt.Println(u)\n```\n\n#### Into slice of slices\n\n```go\nu, err := db.SelectToSlice(`SELECT name, age, recorded_at FROM user`)\nfmt.Println(u)\n```\n\n## Placeholders\n\nsqlp provides both named and unnamed placeholders.\n\n### Unnamed Placeholder\n\nThis is quite similar to `database/sql`'s placeholder, with only several differences:\n\n* Only `?` is used regardless of the type of DBMS or the database driver. `$1` or other types are not available.\n* `WHERE name IN (?, ?)` can be replaced with `WHERE name IN ?[2]`.\n* Binding values are passed as literals, variables, slices, or combinations of these.\n\nExample:\n\n```go\nq := `SELECT name, age, recorded_at FROM user\n      WHERE name LIKE ? AND age IN ?[2]`\n```\n\nThis is internally converted to the next statement:\n\n```sql\nSELECT name, age, recorded_at FROM user\nWHERE name LIKE ? AND age IN (?,?)\n```\n\nThe following three ways of binding values are all acceptable.\n\n```go\nu, err := db.SelectToMap(q, \"User%\", 22, 31)\n```\n\n```go\nb1 := \"User%\"\nb2 := []interface{}{22, 31}\nu, err := db.SelectToMap(q, b1, b2)\n```\n\n```go\nb := []interface{}{\"User%\", 22, 31}\nu, err := db.SelectToMap(q, b)\n//u, err := db.SelectToMap(q, b...) // This works fine too.\n```\n\n#### For different types of placeholder\n\nIf the DBMS or the database driver that you use is not compatible with the `?` type of placeholder, you will need to instruct sqlp to use another one.\n\nFor example, PostgreSQL uses `$1` instead of `?`.\n\n```sql\nSELECT name, age, recorded_at FROM user\nWHERE name LIKE $1 AND age IN ($2,$3)\n```\n\nThis type of placeholder is defined as the constant `placeholder.Dollar`.\nIf you specify it by `placeholder.SetType()`, sqlp converts `?` to `$1` internally so you can use `?` in your query.\n\n```go\nplaceholder.SetType(placeholder.Dollar)\nq := \"SELECT * FROM user WHERE name LIKE ? AND age IN ?[2]\"\nu, err := db.SelectToMap(q, \"User%\", 22, 31)\n```\n\nAnother way is to pass your custom conversion function to `placeholder.SetConvertFunc()`.\nYou should be able to make do with this even if definition of your required type is missing in sqlp.\n\n```go\nplaceholder.SetConvertFunc(func(query *string) {\n    cnt := strings.Count(*query, \"?\")\n    for i := 1; i \u003c= cnt; i++ {\n        *query = strings.Replace(*query, \"?\", \"$\"+strconv.Itoa(i), 1)\n    }\n})\n```\n\n### Named Placeholder\n\nThis is radically different from `database/sql`'s named placeholder.\nHere is an example similar to the previous one.\n\n```go\nq := `SELECT name, age, recorded_at FROM user\n      WHERE name LIKE :like AND age IN :age[2]`\n```\n\nA placeholder name must start with a colon followed by either of or a combination of alphabets, numbers, or underscores.\n\n`:like` and `:age[2]` are the named placeholders.\nThey are internally converted to unnamed ones as below:\n\n```sql\nSELECT name, age, recorded_at FROM user\nWHERE name LIKE ? AND age IN (?,?)\n```\nValues are passed only in the form of a single map.\n`:XXXX[N]` requires a slice of interface{} with N numbers of elements.\n\n```go\nb := map[string]interface{}{\n    \"like\": \"User%\",\n    \"age\":  []interface{}{22, 31},\n}\nu, err := db.SelectToMap(q, b)\n```\n\nThe same applies here as for `placeholder.SetType()` or `placeholder.SetConvertFunc()`.\n\n## License\n\n[MIT](./LICENSE)\n","funding_links":[],"categories":[],"sub_categories":[],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fkaboc%2Fsqlp","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fkaboc%2Fsqlp","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fkaboc%2Fsqlp/lists"}