{"id":13616101,"url":"https://github.com/blockloop/scan","last_synced_at":"2026-01-12T06:32:28.054Z","repository":{"id":28613495,"uuid":"112260492","full_name":"blockloop/scan","owner":"blockloop","description":"Tiny lib to scan SQL rows directly to structs, slices, and primitive types","archived":false,"fork":false,"pushed_at":"2024-06-05T11:32:46.000Z","size":4375,"stargazers_count":543,"open_issues_count":1,"forks_count":31,"subscribers_count":10,"default_branch":"master","last_synced_at":"2024-08-02T20:47:53.904Z","etag":null,"topics":["database","golang","scanning","sql","squirrel"],"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/blockloop.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","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":"2017-11-27T23:22:18.000Z","updated_at":"2024-07-31T17:59:37.000Z","dependencies_parsed_at":"2023-11-06T23:16:00.044Z","dependency_job_id":"d273535f-0efd-4a4c-b6c0-dc5224312927","html_url":"https://github.com/blockloop/scan","commit_stats":{"total_commits":130,"total_committers":11,"mean_commits":"11.818181818181818","dds":0.3384615384615385,"last_synced_commit":"f719cae30187368106aba048126ed3915116154e"},"previous_names":["blockloop/scnr"],"tags_count":15,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/blockloop%2Fscan","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/blockloop%2Fscan/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/blockloop%2Fscan/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/blockloop%2Fscan/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/blockloop","download_url":"https://codeload.github.com/blockloop/scan/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":223611717,"owners_count":17173488,"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","golang","scanning","sql","squirrel"],"created_at":"2024-08-01T20:01:23.658Z","updated_at":"2026-01-12T06:32:28.048Z","avatar_url":"https://github.com/blockloop.png","language":"Go","readme":"# Scan\n\n\u003e [!NOTE]\n\u003e For all intents and purposes, this project is considered 'complete.' I do not plan on making any changes in the near future and have not written any code here since approximately 2021. To the best of my knowledge, everything functions as intended. If you are seeking a more comprehensive solution, please refer to https://github.com/stytchauth/sqx. That being said, this project operates effectively in its current state and requires no new code. Feel free to fork it and continue development if desired.\n\u003e \n\n[![GoDoc](https://godoc.org/github.com/blockloop/scan?status.svg)](https://godoc.org/github.com/blockloop/scan)\n[![go test](https://github.com/blockloop/scan/workflows/go%20test/badge.svg)](https://github.com/blockloop/scan/actions)\n[![Coveralls github](https://img.shields.io/coveralls/github/blockloop/scan.svg)](https://coveralls.io/github/blockloop/scan)\n[![Report Card](https://goreportcard.com/badge/github.com/blockloop/scan)](https://goreportcard.com/report/github.com/blockloop/scan)\n\nScan standard lib database rows directly to structs or slices. \nFor the most comprehensive and up-to-date docs see the [godoc](https://godoc.org/github.com/blockloop/scan)\n\n```go\nimport \"github.com/blockloop/scan/v2\"\n```\n\n## Examples\n\n### Multiple Rows\n```go\ndb, err := sql.Open(\"sqlite3\", \"database.sqlite\")\nrows, err := db.Query(\"SELECT * FROM persons\")\n\nvar persons []Person\nerr := scan.Rows(\u0026persons, rows)\n\nfmt.Printf(\"%#v\", persons)\n// []Person{\n//    {ID: 1, Name: \"brett\"},\n//    {ID: 2, Name: \"fred\"},\n//    {ID: 3, Name: \"stacy\"},\n// }\n```\n### Multiple rows of primitive type\n\n```go\nrows, err := db.Query(\"SELECT name FROM persons\")\nvar names []string\nerr := scan.Rows(\u0026names, rows)\n\nfmt.Printf(\"%#v\", names)\n// []string{\n//    \"brett\",\n//    \"fred\",\n//    \"stacy\",\n// }\n```\n\n### Single row\n\n```go\nrows, err := db.Query(\"SELECT * FROM persons where name = 'brett' LIMIT 1\")\nvar person Person\nerr := scan.Row(\u0026person, rows)\n\nfmt.Printf(\"%#v\", person)\n// Person{ ID: 1, Name: \"brett\" }\n```\n\n### Scalar value\n\n```go\nrows, err := db.Query(\"SELECT age FROM persons where name = 'brett' LIMIT 1\")\nvar age int8\nerr := scan.Row(\u0026age, rows)\n\nfmt.Printf(\"%d\", age)\n// 100\n```\n\n### Nested Struct Fields (as of v2.0.0)\n```go\nrows, err := db.Query(`\n\tSELECT person.id,person.name,company.name FROM person\n\tJOIN company on company.id = person.company_id\n\tLIMIT 1\n`)\n\nvar person struct {\n\tID      int    `db:\"person.id\"`\n\tName    string `db:\"person.name\"`\n\tCompany struct {\n\t\tName string `db:\"company.name\"`\n\t}\n}\n\nerr = scan.RowStrict(\u0026person, rows)\n\nerr = json.NewEncoder(os.Stdout).Encode(\u0026person)\n// Output:\n// {\"ID\":1,\"Name\":\"brett\",\"Company\":{\"Name\":\"costco\"}}\n```\n\n### Custom Column Mapping\n\nBy default, column names are mapped [to](https://github.com/blockloop/scan/blob/4741cc8ac5746ca7e5893d3b54a3347a7735c168/columns.go#L35) and [from](https://github.com/blockloop/scan/blob/4741cc8ac5746ca7e5893d3b54a3347a7735c168/scanner.go#L33) database column names using basic title case conversion. You can override this behavior by setting `ColumnsMapper` and `ScannerMapper` to custom functions.\n\n### Strict Scanning\n\nBoth `Rows` and `Row` have strict alternatives to allow scanning to structs _strictly_ based on their `db` tag.\nTo avoid unwanted behavior you can use `RowsStrict` or `RowStrict` to scan without using field names.\nAny fields not tagged with the `db` tag will be ignored even if columns are found that match the field names.\n\n### Columns\n\n`Columns` scans a struct and returns a string slice of the assumed column names based on the `db` tag or the struct field name respectively. To avoid assumptions, use `ColumnsStrict` which will _only_ return the fields tagged with the `db` tag. Both `Columns` and `ColumnsStrict` are variadic. They both accept a string slice of column names to exclude from the list. It is recommended that you cache this slice.\n\n```go\npackage main\n\ntype User struct {\n        ID        int64\n        Name      string\n        Age       int\n        BirthDate string `db:\"bday\"`\n        Zipcode   string `db:\"-\"`\n        Store     struct {\n                ID int\n                // ...\n        }\n}\n\nvar nobody = new(User)\nvar userInsertCols = scan.Columns(nobody, \"ID\")\n// []string{ \"Name\", \"Age\", \"bday\" }\n\nvar userSelectCols = scan.Columns(nobody)\n// []string{ \"ID\", \"Name\", \"Age\", \"bday\" }\n```\n\n### Values\n\n`Values` scans a struct and returns the values associated with the provided columns. Values uses a `sync.Map` to cache fields of structs to greatly improve the performance of scanning types. The first time a struct is scanned it's **exported** fields locations are cached. When later retrieving values from the same struct it should be much faster. See [Benchmarks](#Benchmarks) below.\n\n```go\nuser := \u0026User{\n        ID: 1,\n        Name: \"Brett\",\n        Age: 100,\n}\n\nvals := scan.Values([]string{\"ID\", \"Name\"}, user)\n// []interface{}{ 1, \"Brett\" }\n```\n\nI find that the usefulness of both Values and Columns lies within using a library such as [sq][].\n\n```go\nsq.Insert(userCols...).\n        Into(\"users\").\n        Values(scan.Values(userCols, \u0026user)...)\n```\n\n## Configuration\n\nAutoClose: Automatically call `rows.Close()` after scan completes (default true)\n\n## Why\n\nWhile many other projects support similar features (i.e. [sqlx](https://github.com/jmoiron/sqlx)) scan allows you to use any database lib such as the stdlib or [squirrel][sq] to write fluent SQL statements and pass the resulting `rows` to `scan` for scanning.\n\n## Benchmarks \n\n```\n$ go test -bench=. -benchtime=10s ./...\ngoos: linux\ngoarch: amd64\npkg: github.com/blockloop/scan\ncpu: 11th Gen Intel(R) Core(TM) i7-1165G7 @ 2.80GHz\nBenchmarkColumnsLargeStruct-8           41527964               288.0 ns/op\nBenchmarkValuesLargeStruct-8             6816885              1807 ns/op\nBenchmarkScanRowOneField-8               5686971              2074 ns/op\nBenchmarkScanRowFiveFields-8             4962622              2381 ns/op\nBenchmarkScanTenRowsOneField-8           1537761              8598 ns/op\nBenchmarkScanTenRowsTenFields-8           322106             50431 ns/op\nPASS\nok      github.com/blockloop/scan       92.374s\n```\n\n\n[sq]: https://github.com/Masterminds/squirrel\t\"Squirrel\"\n","funding_links":[],"categories":["公用事业公司","Go","Utilities","工具库","工具库`可以提升效率的通用代码库和工具`","Utility"],"sub_categories":["实用程序/Miscellaneous","Utility/Miscellaneous","HTTP Clients","查询语","Fail injection"],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fblockloop%2Fscan","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fblockloop%2Fscan","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fblockloop%2Fscan/lists"}