{"id":28051547,"url":"https://github.com/xo/dbtpl","last_synced_at":"2025-05-12T01:55:20.663Z","repository":{"id":41186813,"uuid":"51139948","full_name":"xo/dbtpl","owner":"xo","description":"Command line tool to generate idiomatic Go code for SQL databases supporting PostgreSQL, MySQL, SQLite, Oracle, and Microsoft SQL Server","archived":false,"fork":false,"pushed_at":"2025-05-11T23:11:36.000Z","size":11463,"stargazers_count":3814,"open_issues_count":51,"forks_count":322,"subscribers_count":69,"default_branch":"master","last_synced_at":"2025-05-12T01:55:13.546Z","etag":null,"topics":["code-generator","golang","microsoft-sql-server","mysql","oracle","orm","postgresql","sql","sqlite"],"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/xo.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,"zenodo":null}},"created_at":"2016-02-05T10:22:20.000Z","updated_at":"2025-05-11T23:11:40.000Z","dependencies_parsed_at":"2025-05-06T01:47:44.305Z","dependency_job_id":null,"html_url":"https://github.com/xo/dbtpl","commit_stats":{"total_commits":406,"total_committers":32,"mean_commits":12.6875,"dds":0.5738916256157636,"last_synced_commit":"26e7f0841b2339cb2f398efc2737775da3c6049e"},"previous_names":["knq/xo","xo/dbtpl"],"tags_count":3,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/xo%2Fdbtpl","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/xo%2Fdbtpl/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/xo%2Fdbtpl/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/xo%2Fdbtpl/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/xo","download_url":"https://codeload.github.com/xo/dbtpl/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":253660837,"owners_count":21943823,"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":["code-generator","golang","microsoft-sql-server","mysql","oracle","orm","postgresql","sql","sqlite"],"created_at":"2025-05-12T01:55:20.019Z","updated_at":"2025-05-12T01:55:20.625Z","avatar_url":"https://github.com/xo.png","language":"Go","readme":"# dbtpl\n\n`dbtpl` is a command-line tool to inspect and generate templated code based on\na database schema or a custom database query.\n\nIn addition to being able to generate standardized \"model\" code for a database,\n`dbtpl` is also capable of creating schema creation scripts for a database,\ngenerating JSON/YAML definitions, and [Graphviz](https://graphviz.org) diagrams\nfor schemas.\n\n\u003cp align=\"center\"\u003e\n  \u003ca href=\"#installing\" title=\"Installing\"\u003eInstalling\u003c/a\u003e |\n  \u003ca href=\"#building\" title=\"Building\"\u003eBuilding\u003c/a\u003e |\n  \u003ca href=\"#using\" title=\"Using\"\u003eUsing\u003c/a\u003e |\n  \u003ca href=\"https://github.com/xo/dbtpl/releases\" title=\"Releases\"\u003eReleases\u003c/a\u003e\n\u003c/p\u003e\n\n[![Releases][release-status]][Releases]\n[![Discord Discussion][discord-status]][discord]\n\n[releases]: https://github.com/xo/dbtpl/releases \"Releases\"\n[release-status]: https://img.shields.io/github/v/release/xo/dbtpl?display_name=tag\u0026sort=semver \"Latest Release\"\n[discord]: https://discord.gg/WDWAgXwJqN \"Discord Discussion\"\n[discord-status]: https://img.shields.io/discord/829150509658013727.svg?label=Discord\u0026logo=Discord\u0026colorB=7289da\u0026style=flat-square \"Discord Discussion\"\n\n#### Supported languages\n\nAt the moment, `dbtpl` only supports [Go](https://golang.org). Support for\nother languages is possible, but not currently planned.\n\n#### How it works\n\nIn schema mode, `dbtpl` connects to your database and generates code using Go\ntemplates. `dbtpl` works by using database metadata and SQL introspection\nqueries to discover the types and relationships contained within a schema, and\napplying a standard set of base (or customized) Go [templates](templates)\nagainst the discovered relationships.\n\nCurrently, `dbtpl` can generate types for tables, enums, stored procedures, and\ncustom SQL queries for PostgreSQL, MySQL, Oracle, Microsoft SQL Server, and\nSQLite3 databases.\n\n\u003e **Note:** While the code generated by dbtpl is production quality, it is not\n\u003e the goal, nor the intention for dbtpl to be a \"silver bullet,\" nor to\n\u003e completely eliminate the manual authoring of SQL / Go code.\n\nIn query mode, `dbtpl` parses your query to generate code from Go templates. It\nfinds related tables in your database to ensure type safety.\n\n## Database Feature Support\n\nThe following is a matrix of the feature support for each database:\n\n|              |     PostgreSQL     |       MySQL        |       Oracle       | Microsoft SQL Server |       SQLite       |\n| ------------ | :----------------: | :----------------: | :----------------: | :------------------: | :----------------: |\n| Models       | :white_check_mark: | :white_check_mark: | :white_check_mark: |  :white_check_mark:  | :white_check_mark: |\n| Primary Keys | :white_check_mark: | :white_check_mark: | :white_check_mark: |  :white_check_mark:  | :white_check_mark: |\n| Foreign Keys | :white_check_mark: | :white_check_mark: | :white_check_mark: |  :white_check_mark:  | :white_check_mark: |\n| Indexes      | :white_check_mark: | :white_check_mark: | :white_check_mark: |  :white_check_mark:  | :white_check_mark: |\n| Stored Procs | :white_check_mark: | :white_check_mark: | :white_check_mark: |  :white_check_mark:  | :white_check_mark: |\n| Functions    | :white_check_mark: | :white_check_mark: | :white_check_mark: |  :white_check_mark:  | :white_check_mark: |\n| ENUM types   | :white_check_mark: | :white_check_mark: |                    |                      |                    |\n| Custom types | :white_check_mark: |                    |                    |                      |                    |\n\n## Installing\n\n`dbtpl` can be installed [via Release][], [via Homebrew][], [via AUR][], [via\nScoop][] or [via Go][]:\n\n[via Release]: #installing-via-release\n[via Homebrew]: #installing-via-homebrew-macos-and-linux\n[via AUR]: #installing-via-aur-arch-linux\n[via Scoop]: #installing-via-scoop-windows\n[via Go]: #installing-via-go\n\n### Installing via Release\n\n1. [Download a release for your platform][releases]\n2. Extract the `dbtpl` or `dbtpl.exe` file from the `.tar.bz2` or `.zip` file\n3. Move the extracted executable to somewhere on your `$PATH` (Linux/macOS) or\n   `%PATH%` (Windows)\n\n### Installing via Homebrew (macOS and Linux)\n\nInstall `dbtpl` from the [`xo/xo` tap][xo-tap] in the usual way with the [`brew`\ncommand][homebrew]:\n\n```sh\n# install\n$ brew install xo/xo/dbtpl\n```\n\n### Installing via AUR (Arch Linux)\n\nInstall `dbtpl` from the [Arch Linux AUR][aur] in the usual way with the [`yay`\ncommand][yay]:\n\n```sh\n# install\n$ yay -S dbtpl\n```\n\nAlternately, build and [install using `makepkg`][arch-makepkg]:\n\n```sh\n# clone package repo and make/install package\n$ git clone https://aur.archlinux.org/dbtpl.git \u0026\u0026 cd dbtpl\n$ makepkg -si\n==\u003e Making package: dbtpl 0.4.4-1 (Sat 11 Nov 2023 02:28:28 PM WIB)\n==\u003e Checking runtime dependencies...\n==\u003e Checking buildtime dependencies...\n==\u003e Retrieving sources...\n...\n```\n\n### Installing via Scoop (Windows)\n\nInstall `dbtpl` using [Scoop](https://scoop.sh):\n\n```powershell\n# Optional: Needed to run a remote script the first time\n\u003e Set-ExecutionPolicy RemoteSigned -Scope CurrentUser\n\n# install scoop if not already installed\n\u003e irm get.scoop.sh | iex\n\n# install dbtpl with scoop\n\u003e scoop install dbtpl\n```\n\n### Installing via Go\n\nInstall `dbtpl` in the usual Go fashion:\n\n```sh\n# install latest dbtpl version\n$ go install github.com/xo/dbtpl@latest\n```\n\n## Quickstart\n\nThe following is a quick overview of using `dbtpl` on the command-line:\n\n```sh\n# Make an output directory for generated code.\n$ mkdir -p models\n\n# Generate code from your Postgres schema. (Default output folder is models)\n$ dbtpl schema postgres://user:pass@host/dbname\n\n# Generate code from a Microsoft SQL schema using a custom template directory (see notes below)\n$ mkdir -p mssqlmodels\n$ dbtpl schema mssql://user:pass@host/dbname -o mssqlmodels --src custom/templates\n\n# Generate code from a custom SQL query for Postgres\n$ dbtpl query postgres://user:pass@host/dbname -M -B -2 -T AuthorResult \u003c\u003c ENDSQL\nSELECT\n  a.name::varchar AS name,\n  b.type::integer AS my_type\nFROM authors a\n  INNER JOIN authortypes b ON a.id = b.author_id\nWHERE\n  a.id = %%authorID int%%\nLIMIT %%limit int%%\nENDSQL\n\n# Build generated code - verify it compiles\n$ go build ./models/\n$ go build ./mssqlmodels/\n```\n\n## Command Line Options\n\nThe following are `dbtpl`'s command-line commands, arguments, and options:\n\n```sh\n$ dbtpl --help-long\nusage: dbtpl [\u003cflags\u003e] \u003ccommand\u003e [\u003cargs\u003e ...]\n\nFlags:\n      --help     Show context-sensitive help (also try --help-long and\n                 --help-man).\n  -v, --verbose  enable verbose output\n      --version  display version and exit\n\nCommands:\n  help [\u003ccommand\u003e...]\n    Show help.\n\n\n  query [\u003cflags\u003e] \u003cDSN\u003e\n    Generate code for a database custom query from a template.\n\n    -s, --schema=\u003cname\u003e            database schema name\n    -t, --template=go              template type (createdb, dot, go, json, yaml;\n                                   default: go)\n    -f, --suffix=\u003cext\u003e             file extension suffix for generated files\n                                   (otherwise set by template type)\n    -o, --out=models               out path (default: models)\n    -a, --append                   enable append mode\n    -S, --single=\u003cfile\u003e            enable single file output\n    -D, --debug                    debug generated code (writes generated code\n                                   to disk without post processing)\n    -Q, --query=\"\"                 custom database query (uses stdin if not\n                                   provided)\n    -T, --type=\u003cname\u003e              type name\n        --type-comment=\"\"          type comment\n    -F, --func=\u003cname\u003e              func name\n        --func-comment=\"\"          func comment\n    -M, --trim                     enable trimming whitespace\n    -B, --strip                    enable stripping type casts\n    -1, --one                      enable returning single (only one) result\n    -l, --flat                     enable returning unstructured values\n    -X, --exec                     enable exec (no introspection performed)\n    -I, --interpolate              enable interpolation of embedded params\n    -L, --delimiter=%%             delimiter used for embedded params (default:\n                                   %%)\n    -Z, --fields=\u003cfield\u003e           override field names for results\n    -U, --allow-nulls              allow result fields with NULL values\n    -d, --src=\u003cpath\u003e               template source directory\n    -2, --go-not-first             disable package comment (ie, not first\n                                   generated file)\n        --go-int32=int             int32 type (default: int)\n        --go-uint32=uint           uint32 type (default: uint)\n        --go-pkg=\u003cname\u003e            package name\n        --go-tag=\"\" ...            build tags\n        --go-import=\"\" ...         package imports\n        --go-uuid=\u003cpkg\u003e            uuid type package\n        --go-custom=\u003cname\u003e         package name for custom types\n        --go-conflict=Val          name conflict suffix (default: Val)\n        --go-initialism=\u003cval\u003e ...  add initialism (i.e ID, API, URI)\n        --go-esc=none ...          escape fields (none, schema, table, column,\n                                   all; default: none)\n    -g, --go-field-tag=\u003ctag\u003e       field tag\n        --go-context=only          context mode (disable, both, only; default:\n                                   only)\n        --go-inject=\"\"             insert code into generated file headers\n        --go-inject-file=\u003cfile\u003e    insert code into generated file headers from\n                                   a file\n        --go-legacy                enables legacy v1 template funcs\n        --go-enum-table-prefix     enables table name prefix to enums\n        --json-indent=\"  \"         indent spacing\n        --json-ugly                disable indentation\n\n  schema [\u003cflags\u003e] \u003cDSN\u003e\n    Generate code for a database schema from a template.\n\n    -s, --schema=\u003cname\u003e            database schema name\n    -t, --template=go              template type (createdb, dot, go, json, yaml;\n                                   default: go)\n    -f, --suffix=\u003cext\u003e             file extension suffix for generated files\n                                   (otherwise set by template type)\n    -o, --out=models               out path (default: models)\n    -a, --append                   enable append mode\n    -S, --single=\u003cfile\u003e            enable single file output\n    -D, --debug                    debug generated code (writes generated code\n                                   to disk without post processing)\n    -k, --fk-mode=smart            foreign key resolution mode (smart, parent,\n                                   field, key; default: smart)\n    -i, --include=\u003cglob\u003e ...       include types (\u003ctype\u003e)\n    -e, --exclude=\u003cglob\u003e ...       exclude types/fields (\u003ctype\u003e[.\u003cfield\u003e])\n    -j, --use-index-names          use index names as defined in schema for\n                                   generated code\n    -d, --src=\u003cpath\u003e               template source directory\n        --createdb-fmt=\u003cpath\u003e      fmt command (default:\n                                   /home/ken/.npm-global/bin/sql-formatter)\n        --createdb-fmt-opts=\u003copts\u003e ...\n                                   fmt options (default: -u, -l={{ . }}, -i=2,\n                                   --lines-between-queries=2)\n        --createdb-constraint      enable constraint name in output (postgres,\n                                   mysql, sqlite3)\n        --createdb-escape=none     escape mode (none, types, all; default: none)\n        --createdb-engine=\"\"       mysql table engine (default: InnoDB)\n        --createdb-trim-comment    trim leading comment from views and procs\n                                   (--no-createdb-trim-comment)\n        --dot-defaults=\"\" ...      default statements (default: node\n                                   [shape=none, margin=0])\n        --dot-bold                 bold header row\n        --dot-color=\"\"             header color (default: lightblue)\n        --dot-row=\"\"               row value template (default: {{ .Name }}: {{\n                                   .Type.Type }})\n        --dot-direction            enable edge directions\n    -2, --go-not-first             disable package comment (ie, not first\n                                   generated file)\n        --go-int32=int             int32 type (default: int)\n        --go-uint32=uint           uint32 type (default: uint)\n        --go-pkg=\u003cname\u003e            package name\n        --go-tag=\"\" ...            build tags\n        --go-import=\"\" ...         package imports\n        --go-uuid=\u003cpkg\u003e            uuid type package\n        --go-custom=\u003cname\u003e         package name for custom types\n        --go-conflict=Val          name conflict suffix (default: Val)\n        --go-initialism=\u003cval\u003e ...  add initialism (i.e ID, API, URI)\n        --go-esc=none ...          escape fields (none, schema, table, column,\n                                   all; default: none)\n    -g, --go-field-tag=\u003ctag\u003e       field tag\n        --go-context=only          context mode (disable, both, only; default:\n                                   only)\n        --go-inject=\"\"             insert code into generated file headers\n        --go-inject-file=\u003cfile\u003e    insert code into generated file headers from\n                                   a file\n        --go-legacy                enables legacy v1 template funcs\n        --go-enum-table-prefix     enables table name prefix to enums\n        --json-indent=\"  \"         indent spacing\n        --json-ugly                disable indentation\n        --postgres-oids            enable postgres OIDs\n\n  dump [\u003cflags\u003e] \u003cout\u003e\n    Dump internal templates to path.\n\n    -t, --template=go   template type (createdb, dot, go, json, yaml; default:\n                        go)\n    -f, --suffix=\u003cext\u003e  file extension suffix for generated files (otherwise set\n                        by template type)\n```\n\n## About Base Templates\n\n`dbtpl` provides a set of generic \"base\" [templates](templates) for each of the\nsupported databases, but it is understood these templates are not suitable for\nevery organization or every schema out there. As such, you can author your own\ncustom templates, or modify the base templates available in the `dbtpl` source\ntree, and use those with `dbtpl` by a passing a directory path via the `--src`\nflag.\n\nFor non-trivial schemas, custom templates are the most practical, common, and\nbest way to use `dbtpl` (see below quickstart and related example).\n\n### Custom Template Quickstart\n\nThe following is a quick overview of copying the base templates contained in\nthe `dbtpl` project's [`templates/`](templates) directory, editing to suit, and\nusing with `dbtpl`:\n\n```sh\n# Create a working directory\n$ mkdir -p my-tpl\n\n# Dump an embedded template to disk\n$ dbtpl dump -t createdb my-tpl\n\n# edit base template files\n$ vi my-tpl/*.go.tpl\n\n# see command line options for the template\n$ dbtpl schema --src my-tpl --help\n\n# generate a schema using the custom template\n$ dbtpl schema --src my-tpl -o models postgres://user:pass@host/db\n```\n\nSee the Custom Template example below for more information on adapting the base\ntemplates in the `dbtpl` source tree for use within your own project.\n\n### Storing Project Templates\n\nIdeally, custom templates for your project/schema should be stored alongside\nyour project. and generated as part of an automated build pipeline using `go\ngenerate`:\n\n```sh\n# Add to custom dbtpl command to go generate:\n$ tee -a gen.go \u003c\u003c END\npackage mypackage\n\n//go:generate dbtpl postgres://user:pass@host/db -o models --src templates\nEND\n\n# Run go generate\n$ go generate\n\n# Add custom templates and gen.go to project\n$ git add templates gen.go \u0026\u0026 git commit -m 'Adding custom dbtpl templates for models'\n```\n\n\u003e **Note**: via the `--template`/`-t` parameter of `dbtpl dump` you can generate\n\u003e other templates with `dbtpl`. The default template is the `go` template.\n\n### Template Language/Syntax\n\n`dbtpl` templates are standard Go text templates. Please see the [documentation\nfor Go's standard `text/template` package](https://pkg.go.dev/text/template)\nfor information concerning the syntax, logic, and variable use within Go\ntemplates.\n\n### Template Context and File Layout\n\nThe contexts (ie, the `.` identifier in templates) made available to custom\ntemplates can be found in [templates/types.go](templates/types.go)\n(see below table for more information on which file uses which type).\n\nEach language, has its own set of templates for `$TYPE` and are\navailable in the [templates/](templates).\n\n| Template File        | Description                                                                            |\n| -------------------- | -------------------------------------------------------------------------------------- |\n| `*.go`               | Template logic                                                                         |\n| `hdr.dbtpl.*.tpl`    | File header template. Executed with content for each generated file.                   |\n| `db.dbtpl.*.tpl`     | Package level template with base types and interface data. Generated once per package. |\n| `query.dbtpl.*.tpl`  | Template for custom query execution.                                                   |\n| `schema.dbtpl.*.tpl` | Template for custom query's generated type.                                            |\n\n_\\*_ - is the template type, for example `go`, `json`, `yaml`, etc.\n\n## Examples\n\n### Example: End-to-End\n\nPlease see the [booktest example](_examples/booktest) for a full end-to-end\nexample for each supported database, showcasing how to use a database schema\nwith `dbtpl`, and the resulting code generated by `dbtpl`.\n\nAdditionally, please see the [`northwind`](_examples/northwind) and\n[`django`](_examples/django) for a demonstration of running `dbtpl` against larger\nschema and against databases from other frameworks. Please note that these\nexamples are works in progress, and may not work properly in all scenarios.\n\n### Example: Ignoring Fields\n\nSometimes you may wish to have the database manage the values of columns\ninstead of having them managed by code generated by `dbtpl`. As such, when you\nneed `dbtpl` to ignore fields for a database schema, you can use the `-e` or\n`--exclude` flag. For example, a common use case is to define a table with\n`created_at` and/or `modified_at` timestamps fields, where the database is\nresponsible for setting column values on `INSERT` and `UPDATE`, respectively.\n\nConsider the following PostgreSQL schema where a `users` table has a\n`created_at` and `modified_at` field, where `created_at` has a default value of\n`now()` and where `modified_at` is updated by a trigger on `UPDATE`:\n\n```postgresql\nCREATE TABLE users (\n  id          SERIAL PRIMARY KEY,\n  name        text NOT NULL DEFAULT '' UNIQUE,\n  created_at  timestamptz   default now(),\n  modified_at timestamptz   default now()\n);\n\nCREATEOR REPLACE FUNCTION update_modified_column() RETURNS TRIGGER AS $$\nBEGIN\n    NEW.modified_at= now();\nRETURN NEW;\nEND;\n$$language 'plpgsql';\n\nCREATE TRIGGER update_users_modtime BEFORE UPDATE ON users\n  FOR EACH ROW EXECUTE PROCEDURE update_modified_column();\n```\n\nWe can ensure that these columns are managed by PostgreSQL and not by the\napplication logic but by `dbtpl` by passing the `--exclude` or `-e` flag:\n\n```sh\n# Ignore special fields\n$ dbtpl schema postgres://user:pass@host/db -e users.created_at -e users.modified_at\n# or, To ignore these fields in all tables\n$ dbtpl schema postgres://user:pass@host/db -e *.created_at -e *.modified_at\n```\n\n### Example: Custom Template -- adding a `GetMostRecent` lookup for all tables (Go)\n\nOften, a schema has a common layout/pattern, such as every table having a\n`created_at` and `modified_at` field (as in the PostgreSQL schema in the\nprevious example). It is then a common use-case to have a `GetMostRecent`\nlookup for each table type, retrieving the most recently modified rows for each\ntable (up to some limit, N).\n\nTo accomplish this with `dbtpl`, we will need to create our own set of custom\ntemplates, and then add a `GetMostRecent` lookup to the `.type.go.tpl`\ntemplate.\n\nFirst, we dump the base `dbtpl` Go template:\n\n```sh\n$ mkdir -p my-tpl\n\n$ dbtpl dump my-tpl\n```\n\nWe can now modify the templates to suit our specific schema, adding lookups,\nhelpers, or anything else necessary for our schema.\n\nTo add a `GetMostRecent` lookup, we edit our copy of the `typedef.dbtpl.go.tpl`\ntemplate:\n\n```sh\n$ vi templates/gotpl/schema/typedef.dbtpl.go.tpl\n```\n\nAnd add the following templated `GetMostRecent` func at the end of the file:\n\n```go\n// GetMostRecent{{ $type.Name }} returns n most recent rows from '{{ $table }}',\n// ordered by \"created_at\" in descending order.\nfunc GetMostRecent{{ $type.Name }}(ctx context.Context, db DB, n int) ([]*{{ $type.Name }}, error) {\n    const sqlstr = `SELECT ` +\n        `{{ $type.Fields \"created_at\" \"modified_at\" }}` +\n        `FROM {{ $table }} ` +\n        `ORDER BY created_at DESC LIMIT $1`\n\n    rows, err := db.QueryContext(ctx, sqlstr, n)\n    if err != nil {\n        return nil, logerror(err)\n    }\n    defer rows.Close()\n\n    // load results\n    var res []*{{ $type.Name }}\n    for rows.Next() {\n        {{ $short }} := {{ $type.Name }}{\n        {{- if $type.PrimaryKey }}\n            _exists: true,\n        {{ end -}}\n        }\n        // scan\n        if err := rows.Scan({{ fieldnames $type.Fields (print \"\u0026\" $short) }}); err != nil {\n            return nil, logerror(err)\n        }\n        res = append(res, \u0026{{ $short }})\n    }\n    return res, nil\n}\n```\n\nWe can then use the templates in conjunction with `dbtpl` to generate our \"model\"\ncode:\n\n```sh\n$ dbtpl schema postgres://user:pass@localhost/dbname --src templates/\n```\n\nThere will now be a `GetMostRecentUsers` func defined in `models/user.dbtpl.go`,\nwhich can be used as follows:\n\n```go\ndb, err := dburl.Open(\"postgres://user:pass@localhost/dbname\")\nif err != nil { /* ... */ }\n\n// retrieve 15 most recent items\nmostRecentUsers, err := models.GetMostRecentUsers(context.Background(), db, 15)\nif err != nil { /* ... */ }\nfor _, user := range users {\n    log.Printf(\"got user: %+v\", user)\n}\n```\n\n## Using SQL Drivers\n\nPlease note that the base `dbtpl` templates do not import any SQL drivers. It is\nleft for the user of `dbtpl`'s generated code to import the actual drivers. For\nreference, these are the expected drivers to use with the code generated by\n`dbtpl`:\n\n| Database (driver)            | Package                                                                    |\n| ---------------------------- | -------------------------------------------------------------------------- |\n| PostgreSQL (postgres)        | [github.com/lib/pq](https://github.com/lib/pq)                             |\n| SQLite3 (sqlite3)            | [github.com/mattn/go-sqlite3](https://github.com/mattn/go-sqlite3)         |\n| MySQL (mysql)                | [github.com/go-sql-driver/mysql](https://github.com/go-sql-driver/mysql)   |\n| Microsoft SQL Server (mssql) | [github.com/microsoft/go-mssqldb](https://github.com/microsoft/go-mssqldb) |\n| Oracle (ora)                 | [github.com/sijms/go-ora/v2](https://github.com/sijms/go-ora/v2)           |\n\nAdditionally, please see below for usage notes on specific SQL database\ndrivers.\n\n### MySQL (mysql)\n\nIf your schema or custom query contains table or column names that need to be\nescaped using any of the `--escape-*` options, you must pass the `sql_mode=ansi`\noption to the MySQL driver:\n\n```sh\n$ dbtpl --escape-all 'mysql://user:pass@host/?parseTime=true\u0026sql_mode=ansi' -o models\n```\n\nAnd when opening a database connection:\n\n```go\ndb, err := dburl.Open(\"mysql://user:pass@host/?parseTime=true\u0026sql_mode=ansi\")\n```\n\nAdditionally, when working with date/time column types in MySQL, one should\npass the `parseTime=true` option to the MySQL driver:\n\n```sh\n$ dbtpl schema 'mysql://user:pass@host/dbname?parseTime=true' -o models\n```\n\nAnd when opening a database connection:\n\n```go\ndb, err := dburl.Open(\"mysql://user:pass@host/dbname?parseTime=true\")\n```\n\n### SQLite3 (sqlite3)\n\nWhile not required, one should specify the `loc=auto` option when using `dbtpl`\nwith a SQLite3 database:\n\n```sh\n$ dbtpl schema 'file:mydatabase.sqlite3?loc=auto' -o models\n```\n\nAnd when opening a database connection:\n\n```go\ndb, err := dburl.Open(\"file:mydatabase.sqlite3?loc=auto\")\n```\n\n## About Primary Keys\n\nFor row inserts `dbtpl` determines whether the primary key is\nautomatically generated by the DB or must be provided by the application for the\ntable row being inserted. For example a table that has a primary key that is\nalso a foreign key to another table, or a table that has multiple primary keys\nin a many-to-many link table, it is desired that the application provide the\nprimary key(s) for the insert rather than the DB.\n\n`dbtpl` will query the schema to determine if the database provides an automatic\nprimary key and if the table does not provide one then it will require that the\napplication provide the primary key for the object passed to the Insert method.\nBelow is information on how the logic works for each database type to determine\nif the DB automatically provides the PK.\n\n### PostgreSQL Auto PK Logic\n\n- Checks for a sequence that is owned by the table in question.\n\n### MySQL Auto PK Logic\n\n- Checks for an autoincrement row in the information_schema for the table in\n  question.\n\n### SQLite Auto PK Logic\n\n- Checks the SQL that is used to generate the table contains\n  the _AUTOINCREMENT_ keyword.\n- Checks that the table was created with the primary key type of _INTEGER_.\n\nIf either of the above conditions are satisfied then the PK is determined to be\nautomatically provided by the DB. For the case of integer PK's when you want to\noverride that the PK be manually provided then you can define the key type as\n_INT_ instead of _INTEGER_, for example as in the following many-to-many link\ntable:\n\n```sql\n  CREATE TABLE site_contacts (\n  contact_id\tINT NOT NULL,\n  site_id\tINT NOT NULL,\n  PRIMARY KEY(contact_id,siteid),\n  FOREIGN KEY(contact_id) REFERENCES contacts (contact_id),\n  FOREIGN KEY(site_id) REFERENCES sites (site_id)\n)\n```\n\n### SQL Server Auto PK Logic\n\n- Checks for an identity associated with one of the columns for the table in\n  question.\n\n### Oracle Auto PK Logic\n\n`ALWAYS GENERATED` types will be parsed as Auto PK types for Oracle.\n\n## About dbtpl: Design, Origin, Philosophy, and History\n\n`dbtpl` can likely get you 99% \"of the way there\" on medium or large database\nschemas and 100% of the way there for small or trivial database schemas. In\nshort, `dbtpl` is a great launching point for developing standardized packages\nfor standard database abstractions/relationships, and `dbtpl`'s most common\nuse-case is indeed in a code generation pipeline, ala `stringer`.\n\n### Design\n\n`dbtpl` is **NOT** designed to be an ORM or to generate an ORM. Instead, `dbtpl` is\ndesigned to vastly reduce the overhead/redundancy of (re-)writing types and\nfuncs for common database queries/relationships -- it is not meant to be\na \"silver bullet\".\n\n### History\n\n`dbtpl` was originally developed while migrating a large application written in\nPHP to Go. The schema in use in the original app, while well-designed, had\nbecome inconsistent over multiple iterations/generations, mainly due to\ndifferent naming styles adopted by various developers/database admins over the\npreceding years. Additionally, some components had been written in different\nlanguages (Ruby, Java) and had also accumulated significant drift from the\noriginal application and accompanying schema. Simultaneously, a large amount of\ngrowth meant that the PHP/Ruby code could no longer efficiently serve the\ntraffic volumes.\n\nIn late 2014/early 2015, a decision was made to unify and strip out certain\nbackend services and to fully isolate the API from the original application,\nallowing the various components to instead speak to a common API layer instead\nof directly to the database, and to build that service layer in Go.\n\nHowever, unraveling the old PHP/Ruby/Java code became a large headache, as the\ncode, the database, and the API, all had significant drift -- thus, underlying\nfunction names, fields, and API methods no longer coincided with the actual\ndatabase schema, and were named differently in each language. As such, after a\nround of standardizing names, dropping cruft, and adding a few relationship\nchanges to the schema, the various codebases were fixed to match the schema\nchanges. After that was determined to be a success, the next target was to\nrewrite the backend services in Go.\n\nIn order to keep a similar and consistent workflow for the developers, the\nprevious code generator (written in PHP and Twig templates) was modified to\ngenerate Go code. Additionally, at this time, but tangential to the story, the\nAPI definitions were ported from JSON to Protobuf to make use of its code\ngeneration abilities as well.\n\n`dbtpl` is the open source version of that code generation tool, and is the\nfruits of those development efforts. It is hoped that others will be able to\nuse and expand `dbtpl` to support other databases -- SQL or otherwise -- and\nthat `dbtpl` can become a common tool in any Go developer's toolbox.\n\nIn May of 2025, the project was renamed from `xo` to `dbtpl` to more readily\nconvey the tool's purpose.\n\n### Goals\n\nPart of `dbtpl`'s goals is to avoid writing an ORM, or an ORM-like in Go, and\nto instead generate static, type-safe, fast, and idiomatic Go code across\nlanguages and databases.\n\nAdditionally, the `dbtpl` developers are of the opinion that relational\ndatabases should have proper, well-designed relationships and all the related\ndefinitions should reside within the database schema itself: ie, a\n\"self-documenting\" schema. `dbtpl` is an end to that pursuit.\n\n## Related Projects\n\n- [dburl](https://github.com/xo/dburl) - a Go package providing a standard, URL\n  style mechanism for parsing and opening database connection URLs\n- [usql](https://github.com/xo/usql) - a universal command-line interface for\n  SQL databases\n\n### Other Projects\n\nThe following projects work with similar concepts as `dbtpl`:\n\n#### Go Generators\n\n- [ModelQ](https://github.com/mijia/modelq)\n- [sqlgen](https://github.com/drone/sqlgen)\n- [squirrel](https://github.com/Masterminds/squirrel)\n- [scaneo](https://github.com/variadico/scaneo)\n- [acorn](https://github.com/willowtreeapps/acorn) and\n  [rootx](https://github.com/willowtreeapps/rootx) ([read overview here](http://willowtreeapps.com/blog/go-generate-your-database-code/))\n\n#### Go ORM-likes\n\n- [sqlc](https://github.com/relops/sqlc)\n\n[homebrew]: https://brew.sh/\n[xo-tap]: https://github.com/xo/homebrew-xo\n[aur]: https://aur.archlinux.org/packages/xo-cli\n[arch-makepkg]: https://wiki.archlinux.org/title/makepkg\n[yay]: https://github.com/Jguer/yay\n","funding_links":[],"categories":["Go","Database"],"sub_categories":["SQL Query Builders"],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fxo%2Fdbtpl","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fxo%2Fdbtpl","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fxo%2Fdbtpl/lists"}