{"id":13412205,"url":"https://gitlab.com/qosenergy/squalus","last_synced_at":"2025-03-14T18:30:37.854Z","repository":{"id":57488917,"uuid":"4966401","full_name":"qosenergy/squalus","owner":"qosenergy","description":"A package to make performing SQL queries in Go easier and less error-prone.","archived":false,"fork":false,"pushed_at":null,"size":null,"stargazers_count":12,"open_issues_count":6,"forks_count":5,"subscribers_count":null,"default_branch":"master","last_synced_at":"2024-07-31T20:50:02.186Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":null,"language":null,"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://gitlab.com/uploads/-/system/project/avatar/4966401/Squalus.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":"2017-12-22T10:10:57.584Z","updated_at":"2024-05-24T08:15:13.326Z","dependencies_parsed_at":"2022-08-29T15:10:47.249Z","dependency_job_id":null,"html_url":"https://gitlab.com/qosenergy/squalus","commit_stats":null,"previous_names":[],"tags_count":8,"template":null,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/gitlab.com/repositories/qosenergy%2Fsqualus","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/gitlab.com/repositories/qosenergy%2Fsqualus/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/gitlab.com/repositories/qosenergy%2Fsqualus/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/gitlab.com/repositories/qosenergy%2Fsqualus/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/gitlab.com/owners/qosenergy","download_url":"https://gitlab.com/qosenergy/squalus/-/archive/master/squalus-master.zip","host":{"name":"gitlab.com","url":"https://gitlab.com","kind":"gitlab","repositories_count":4518057,"owners_count":6822,"icon_url":"https://github.com/gitlab.png","version":null,"created_at":"2022-05-30T11:31:42.605Z","updated_at":"2024-07-18T11:24:13.055Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/gitlab.com","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/gitlab.com/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/gitlab.com/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/gitlab.com/owners"}},"keywords":[],"created_at":"2024-07-30T20:01:22.115Z","updated_at":"2025-03-14T18:30:37.397Z","avatar_url":"https://gitlab.com/uploads/-/system/project/avatar/4966401/Squalus.png","language":null,"funding_links":[],"categories":["Database","数据库  `go语言实现的数据库`","数据库","Generators","Data Integration Frameworks"],"sub_categories":["SQL Query Builders","Advanced Console UIs","SQL 查询语句构建库","SQL查询生成器"],"readme":"# Squalus — SQL made pleasant\n\nSqualus is a package that makes it much easier to perform SQL queries in Go by encapsulating cursor manipulation,\nerror handling and results fetching into a simple API. It is not an ORM, nor an SQL generator. SQL queries are only\nadapted to make them easier to manage, hiding some of the most annoying differences between SQL drivers and allowing\nto use named parameters even if the underlying engine does not support them. This project is intended to remain small\nand easy to use, staying away from feature bloating.\n\n## Supported Go version\n\nSqualus is currently tested with Go 1.20.1.\n\n## squalus.DB creation\n\nCreate an [`sql.DB`](https://golang.org/pkg/database/sql/#DB) as usual, then a `squalus.DB` from it.\n\n```go\ndb1, err := sql.Open(\"driver name\", \"some connection string\")\nif err != nil {\n\t// handle err\n}\ndb, err := squalus.NewDB(db1)\nif err != nil {\n\t// handle err\n}\ndefer db.Close()\n```\n\nSqualus automatically detects the driver type. Supported drivers are:\n* [Mysql](https://github.com/go-sql-driver/mysql) (go-sql-driver/mysql). Supported Mysql versions: 5.7 and 8.0.\n* [PostgreSQL](https://github.com/lib/pq) (lib/pq). Supported Postgresql versions: 10.7 and 11.2.\n* [SQLite3](https://github.com/mattn/go-sqlite3) (mattn/sqlite3). Supported SQLite version: 3.\n* [MS SQL Server](https://github.com/denisenkom/go-mssqldb) (denisenkom/go-mssqldb). Supported SQL Server versions:\n2017 CU12, 2019 CTP 2.2.\n\nAttempting to create a DB with another driver type results in an error.\n\n## Examples setting\n\nThe following examples use a table in which data about persons are stored. Here is the corresponding struct:\n\n```go\ntype Person struct {\n\tID        int       `db:\"id\"`     // notice the db tag\n\tName      string    `db:\"name\"`\n\tHeight    float64   `db:\"height\"` // in meters\n\tBirthDate time.Time `db:\"birth\"`\n}\n```\n\n## Query execution\n\nJust like `sql.DB`, `squalus.DB` provides an `Exec` method.\n\n```go\ndb.Exec(ctx, \"CREATE TABLE [persons]([id] INT, [name] VARCHAR(128), [height] FLOAT, [birth] DATETIME)\", nil)\nresult, err := db.Exec(\n\tctx,\n\t\"INSERT INTO [persons]([id], [name], [height], [birth]) VALUES({id}, {name}, {height}, {birth})\",\n\tmap[string]interface{}{\n\t\t\"id\":    1,\n\t\t\"name\": \"Alice Abbott\",\n\t\t\"height\": 1.65,\n\t\t\"birth\": time.Date(1985, 7, 12, 0, 0, 0, 0, time.UTC),\n\t},\n)\nif err != nil {\n\t// handle err\n}\n// result is the regular sql.Result\n```\n\nThis example shows that Squalus uses square brackets as database, table and field delimiters. They are automatically\nreplaced by whatever the underlying driver requires, and of course, they can be omitted when not needed. MySQL users\nwill appreciate finally being able to use backticks for long queries in their Go code.\n\nIt also shows how query parameters work. Only named parameters are supported, and they are passed through a\n`map[string]interface{}`, which can be `nil` if no parameters are provided.\n\nThe `ctx` parameter is a [context](https://golang.org/pkg/context/), `context.Background()` can be used if nothing else\nis available. Internally, Squalus uses the `Context` versions of the Go SQL methods.\n\n## Data acquisition\n\n`Query` is the only method that Squalus provides to read data. Its behaviour depends on the type of the `to` parameter.\n\nThe following examples assume that the table contains the rows below:\n\n| ID  | Name            | Height  | Birth      |\n| --- | --------------- | ------- | ---------- |\n| 1   | Alice Abbott    | 1.65    | 1985-07-11 |\n| 2   | Bob Burton      | 1.59    | 1977-03-01 |\n| 3   | Clarissa Cooper | 1.68    | 2003-09-30 |\n| 4   | Donald Dock     | 1.71    | 1954-12-04 |\n\n### Query to a single value\n\nTo read a single value, use a pointer to a basic type as the value of `to`.\n\n```go\nvar name string\nif err := db.Query(\n\tctx,\n\t`SELECT [name]\n\t FROM [persons]\n\t WHERE [id]={id}`,\n\tmap[string]interface{}{\"id\": 3},\n\t\u0026name,\n); err != nil {\n\t// handle err\n}\n// name contains \"Clarissa Cooper\"\n```\n\nIf no rows are found, Query returns `sql.ErrNoRows`.\n\nAs a special case, `time.Time` is treated like a basic type, so it behaves as expected.\n\n```go\nvar birthDate time.Time\nif err := db.Query(\n\tctx,\n\t`SELECT [birth]\n\t FROM [persons]\n\t WHERE [id]={id}`,\n\tmap[string]interface{}{\"id\": 3},\n\t\u0026birthDate,\n); err != nil {\n\t// handle err\n}\n// birthDate == time.Date(2003, 9, 30, 0, 0, 0, 0, time.UTC)\n```\n\n### Query to a struct\n\nYou can read one multicolumn row directly into a struct.\n\n```go\nvar person Person\nif err := db.Query(\n\tctx,\n\t`SELECT [name], [id], [birth], [height]\n\t FROM [persons]\n\t WHERE [id]={id}`,\n\tmap[string]interface{}{\"id\": 3},\n\t\u0026person,\n); err != nil {\n\t// handle err\n}\n// person contains the data for Clarissa Cooper\n```\n\nStruct composition is supported, with the same rules for naming fields as in Go. The one exception is that if a\n```db``` tag is given, it replaces the field name.\nThis makes it easier to work with joins and other scenarios in which several fields bear the same name.\nFor example, the example above also works with the following definition of Person, because the structs are embedded\n(anonymous):\n\n```go\ntype Height struct {\n\tHeight float64 `db:\"height\"`\n}\ntype NameBirthHeight struct {\n\tName      string    `db:\"name\"`\n\tBirthDate time.Time `db:\"birth\"`\n\tHeight\n}\ntype Person struct {\n\tID int `db:\"id\"`\n\tNameBirthHeight\n}\n```\n\nThis example illustrates the handling of named structs:\n\n```go\ntype Height struct {\n\tHeight float64 `db:\"height\"`\n}\ntype NameBirthHeight struct {\n\tName      string    `db:\"name\"`\n\tBirthDate time.Time `db:\"birth\"`\n\tH         Height    `db:\"hh\"`\n}\ntype PersonComposed struct {\n\tID  int `db:\"id\"`\n\tNBH NameBirthHeight\n}\n\nvar person1 PersonComposed\nif err := db.Query(\n\tctx,\n\t`SELECT [name] AS [NBH.name], [id], [birth] AS [NBH.birth], [height] AS [NBH.hh.height]\n\t FROM [persons]\n\t WHERE [id]={id}`,\n\tmap[string]interface{}{\"id\": 3},\n\t\u0026person1,\n); err != nil {\n\t// handle err\n}\n```\n\nAnother way of matching database column names to struct fields is the `FieldNameConverter` interface, which consists of\na method, `DBName(field string) string`. For example, the `Person` struct could be defined as follows:\n```go\ntype Person struct {\n\tID        int\n\tName      string\n\tHeight    float64\n\tBirthDate time.Time `db:\"birth\"`\n}\n\nfunc (Person) DBName(field string) string {\n\treturn strings.ToLower(field)\n}\n```\n\nThis illustrates that struct tags are still taken into account and take priority over the `DBName` method if it exists.\n\n### Query to a slice\n\nIf `to` is a pointer to a slice, Squalus fills the slice with all the data returned by the query. The rules for\nhandling basic types and structs are applied to the slice type.\n\n```go\nvar people []Person\nif err := db.Query(\n\tctx,\n\t`SELECT [name], [id], [birth], [height]\n\t FROM [persons]\n\t ORDER BY [id]`,\n\tnil,\n\t\u0026people,\n); err != nil {\n\t// handle err\n}\n// people contains all four persons\n```\n\nNotice how there is still exactly one place where an error may be returned, even though several rows were read from\ndatabase.\n\n### Query to a channel\n\nIf `to` is a channel, every row will be read and sent to that channel. Squalus closes the channel when there are no\nmore data.\n\n```go\nch := make(chan Person)\n\ngo func() {\n\tfor p := range ch {\n\t\tfmt.Println(p)\n\t}\n}()\n\nif err := db.Query(\n\tctx,\n\t`SELECT [name], [id], [birth], [height]\n\t FROM [persons]\n\t ORDER BY [id]`,\n\tnil,\n\tch,\n); err != nil {\n\t// handle err\n}\n// all people are printed to stdout\n```\n\n### Query using a callback\n\nIf `to` is a function, it is called once for each row. Columns and callback parameters are matched by rank only, not by\nname: each column, in the order of the `SELECT` clause, matches the corresponding function parameter. Struct parameters\nare scanned directly, without applying the mechanism described above to match columns to struct fields.\n\n```go\nif err := db.Query(\n\tctx,\n\t`SELECT [name], [id], [birth], [height]\n\t FROM [persons]\n\t ORDER BY [id]`,\n\tnil,\n\tfunc(name string, id int, birthDate time.Time, height float64) {\n\t\tfmt.Printf(\"%v has ID %v, birth date %v and height %v\\n\", name, id, birthDate, height)\n\t},\n); err != nil {\n\t// handle err\n}\n// all people are printed to stdout\n```\n\nIf the callback returns a value, it must be of type `error`. In that case, returning a non-`nil` error stops the query\nexecution and causes that error to be returned as the result of `Query`.\n\n```go\nif err := db.Query(\n\tctx,\n\t`SELECT [name], [id], [birth], [height] FROM [persons]`,\n\tnil,\n\tfunc(name string, id int, birthDate time.Time, height float64) error {\n\t\tif name == \"Donald Dock\" {\n\t\t\treturn errors.New(\"found an intruder\")\n\t\t}\n\t\treturn nil\n\t},\n); err != nil {\n\t// handle err\n}\n// Query returns an error with message \"found an intruder\".\n```\n\n### Structs that have a Scan method\n\nIf a struct has a `Scan` method with a pointer receiver, it is treated like a basic type, so it behaves as expected.\n\n```go\ntype NameResult struct {\n\tFirst string\n\tLast  string\n}\n\nfunc (nr *NameResult) Scan(src interface{}) error {\n\t// some drivers return a string here, some return a []byte\n\ts := \"\"\n\tswitch val := src.(type) {\n\tcase string:\n\t\ts = val\n\tcase []uint8:\n\t\ts = string(val)\n\tdefault:\n\t\treturn fmt.Errorf(\"could not acquire field value (type %T) as string or []byte\", src)\n\t}\n\tt := strings.Split(s, \" \")\n\tif len(t) != 2 {\n\t\treturn fmt.Errorf(\"format of %s is wrong: it should contain exactly one space\", s)\n\t}\n\tnr.First, nr.Last = t[0], t[1]\n\treturn nil\n}\n\nfunc getNames() {\n\tvar names []NameResult\n\tif err := db.Query(\n\t\tctx,\n\t\t`SELECT [name]\n\t\t FROM [persons]\n\t\t ORDER BY [id]`,\n\t\tnil,\n\t\t\u0026names,\n\t); err != nil {\n\t\t// handle err\n    }\n    // names contains the names of everybody\n}\n```\n\n### Writing IN clauses\n\nSqualus makes it easy to perform a `SELECT` with an `IN` clause: if the value of a parameter is a slice, it is expanded\nautomatically.\n\n```go\nvar people []Person\nif err := db.Query(\n\tctx,\n\t`SELECT [name], [id], [birth], [height]\n\t FROM [persons]\n\t WHERE [id] IN ({ids})\n\t ORDER BY [id]`,\n\tmap[string]interface{}{\"ids\": []int{1, 3, 4}},\n\t\u0026people,\n); err != nil {\n\t// handle err\n}\n// people contains Alice Abbott, Clarissa Cooper and Donald Dock\n```\n\nThis rule does not apply to byte slices (and uint8 slices, since Go does not distinguish internally between byte and\nuint8), in order to facilitate loading and storing data between []byte and blob.\n\n## Transactions\n\nTransactions are created as follows:\n\n```go\ntx, err := db.Begin(ctx, opts)\n```\n\nwhere `opts` is an [`*sql.TxOptions`](https://golang.org/pkg/database/sql/#TxOptions)\n(`nil` selects the default values). The return type is `squalus.Tx`, which has the following methods:\n* `Exec` and `Query` are identical to the corresponding methods in `DB`,\n* `Commit()` commits the transaction,\n* `Rollback()` aborts the transaction.\n\n## License\n\nSqualus is released under the MIT license, as found in the LICENSE file and below.\n\nCopyright (C) 2017 QOS Energy\n\nPermission is hereby granted, free of charge, to any person obtaining a copy of this software and associated\ndocumentation files (the \"Software\"), to deal in the Software without restriction, including without limitation the\nrights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit\npersons to whom the Software is furnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all copies or substantial portions of the\nSoftware.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE\nWARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR\nCOPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR\nOTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/gitlab.com%2Fqosenergy%2Fsqualus","html_url":"https://awesome.ecosyste.ms/projects/gitlab.com%2Fqosenergy%2Fsqualus","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/gitlab.com%2Fqosenergy%2Fsqualus/lists"}