├── .gitattributes ├── .github └── workflows │ ├── announce.yml │ ├── release.yml │ └── test.yml ├── .gitignore ├── LICENSE ├── README.md ├── _examples ├── a_bit_of_everything │ ├── .gitignore │ ├── gen.sh │ ├── main.go │ ├── mysql.go │ ├── mysql │ │ ├── abitofeverything.dbtpl.go │ │ ├── aenum.dbtpl.go │ │ ├── aenumnullable.dbtpl.go │ │ ├── aforeignkey.dbtpl.go │ │ ├── aforeignkeycomposite.dbtpl.go │ │ ├── aindex.dbtpl.go │ │ ├── aindexcomposite.dbtpl.go │ │ ├── amanualtable.dbtpl.go │ │ ├── aprimary.dbtpl.go │ │ ├── aprimarycomposite.dbtpl.go │ │ ├── aprimarymulti.dbtpl.go │ │ ├── asequence.dbtpl.go │ │ ├── asequencemulti.dbtpl.go │ │ ├── auniqueindex.dbtpl.go │ │ ├── auniqueindexcomposite.dbtpl.go │ │ ├── aviewofeverything.dbtpl.go │ │ ├── aviewofeverythingsome.dbtpl.go │ │ ├── dbtpl.dbtpl.dot │ │ ├── dbtpl.dbtpl.go │ │ ├── dbtpl.dbtpl.json │ │ ├── dbtpl.dbtpl.sql │ │ ├── dbtpl.dbtpl.yaml │ │ ├── sf_afunc0in.dbtpl.go │ │ ├── sf_afunc1in.dbtpl.go │ │ ├── sf_afunc2in.dbtpl.go │ │ ├── sp_a0in0out.dbtpl.go │ │ ├── sp_a0in1out.dbtpl.go │ │ ├── sp_a1in0out.dbtpl.go │ │ ├── sp_a1in1out.dbtpl.go │ │ └── sp_a2in2out.dbtpl.go │ ├── oracle.go │ ├── oracle │ │ ├── abitofeverything.dbtpl.go │ │ ├── aforeignkey.dbtpl.go │ │ ├── aforeignkeycomposite.dbtpl.go │ │ ├── aindex.dbtpl.go │ │ ├── aindexcomposite.dbtpl.go │ │ ├── amanualtable.dbtpl.go │ │ ├── aprimary.dbtpl.go │ │ ├── aprimarycomposite.dbtpl.go │ │ ├── aprimarymulti.dbtpl.go │ │ ├── asequence.dbtpl.go │ │ ├── asequencemulti.dbtpl.go │ │ ├── auniqueindex.dbtpl.go │ │ ├── auniqueindexcomposite.dbtpl.go │ │ ├── aviewofeverything.dbtpl.go │ │ ├── aviewofeverythingsome.dbtpl.go │ │ ├── dbtpl.dbtpl.dot │ │ ├── dbtpl.dbtpl.go │ │ ├── dbtpl.dbtpl.json │ │ ├── dbtpl.dbtpl.sql │ │ ├── dbtpl.dbtpl.yaml │ │ ├── sf_afunc0in.dbtpl.go │ │ ├── sf_afunc1in.dbtpl.go │ │ ├── sf_afunc2in.dbtpl.go │ │ ├── sp_a0in0out.dbtpl.go │ │ ├── sp_a0in1out.dbtpl.go │ │ ├── sp_a1in0out.dbtpl.go │ │ ├── sp_a1in1out.dbtpl.go │ │ └── sp_a2in2out.dbtpl.go │ ├── postgres.go │ ├── postgres │ │ ├── abitofeverything.dbtpl.go │ │ ├── aenum.dbtpl.go │ │ ├── aforeignkey.dbtpl.go │ │ ├── aforeignkeycomposite.dbtpl.go │ │ ├── aindex.dbtpl.go │ │ ├── aindexcomposite.dbtpl.go │ │ ├── amanualtable.dbtpl.go │ │ ├── aprimary.dbtpl.go │ │ ├── aprimarycomposite.dbtpl.go │ │ ├── aprimarymulti.dbtpl.go │ │ ├── asamefkname1.dbtpl.go │ │ ├── asamefkname2.dbtpl.go │ │ ├── asequence.dbtpl.go │ │ ├── asequencemulti.dbtpl.go │ │ ├── auniqueindex.dbtpl.go │ │ ├── auniqueindexcomposite.dbtpl.go │ │ ├── aviewofeverything.dbtpl.go │ │ ├── aviewofeverythingsome.dbtpl.go │ │ ├── dbtpl.dbtpl.dot │ │ ├── dbtpl.dbtpl.go │ │ ├── dbtpl.dbtpl.json │ │ ├── dbtpl.dbtpl.sql │ │ ├── dbtpl.dbtpl.yaml │ │ ├── notes.txt │ │ ├── sf_a0in1out.dbtpl.go │ │ ├── sf_a1in1out.dbtpl.go │ │ ├── sf_a2in2out.dbtpl.go │ │ ├── sf_afunc0in.dbtpl.go │ │ ├── sf_afunc1in.dbtpl.go │ │ ├── sf_afunc2in.dbtpl.go │ │ ├── sp_a0in0out.dbtpl.go │ │ ├── sp_a1in0out.dbtpl.go │ │ └── sp_aoverloaded.dbtpl.go │ ├── sql │ │ ├── mysql_schema.sql │ │ ├── oracle_schema.sql │ │ ├── postgres_schema.sql │ │ ├── sqlite3_schema.sql │ │ └── sqlserver_schema.sql │ ├── sqlite3.go │ ├── sqlite3 │ │ ├── abitofeverything.dbtpl.go │ │ ├── aforeignkey.dbtpl.go │ │ ├── aforeignkeycomposite.dbtpl.go │ │ ├── aindex.dbtpl.go │ │ ├── aindexcomposite.dbtpl.go │ │ ├── amanualtable.dbtpl.go │ │ ├── aprimary.dbtpl.go │ │ ├── aprimarycomposite.dbtpl.go │ │ ├── aprimarymulti.dbtpl.go │ │ ├── asequence.dbtpl.go │ │ ├── asequencemulti.dbtpl.go │ │ ├── auniqueindex.dbtpl.go │ │ ├── auniqueindexcomposite.dbtpl.go │ │ ├── aviewofeverything.dbtpl.go │ │ ├── aviewofeverythingsome.dbtpl.go │ │ ├── dbtpl.dbtpl.dot │ │ ├── dbtpl.dbtpl.go │ │ ├── dbtpl.dbtpl.json │ │ ├── dbtpl.dbtpl.sql │ │ └── dbtpl.dbtpl.yaml │ ├── sqlserver.go │ └── sqlserver │ │ ├── abitofeverything.dbtpl.go │ │ ├── aforeignkey.dbtpl.go │ │ ├── aforeignkeycomposite.dbtpl.go │ │ ├── aindex.dbtpl.go │ │ ├── aindexcomposite.dbtpl.go │ │ ├── amanualtable.dbtpl.go │ │ ├── aprimary.dbtpl.go │ │ ├── aprimarycomposite.dbtpl.go │ │ ├── aprimarymulti.dbtpl.go │ │ ├── asequence.dbtpl.go │ │ ├── asequencemulti.dbtpl.go │ │ ├── auniqueindex.dbtpl.go │ │ ├── auniqueindexcomposite.dbtpl.go │ │ ├── aviewofeverything.dbtpl.go │ │ ├── aviewofeverythingsome.dbtpl.go │ │ ├── dbtpl.dbtpl.dot │ │ ├── dbtpl.dbtpl.go │ │ ├── dbtpl.dbtpl.json │ │ ├── dbtpl.dbtpl.sql │ │ ├── dbtpl.dbtpl.yaml │ │ ├── sf_afunc0in.dbtpl.go │ │ ├── sf_afunc1in.dbtpl.go │ │ ├── sf_afunc2in.dbtpl.go │ │ ├── sp_a0in0out.dbtpl.go │ │ ├── sp_a0in1out.dbtpl.go │ │ ├── sp_a1in0out.dbtpl.go │ │ ├── sp_a1in1out.dbtpl.go │ │ └── sp_a2in2out.dbtpl.go ├── booktest │ ├── .gitignore │ ├── README.md │ ├── gen.sh │ ├── main.go │ ├── mysql.go │ ├── mysql │ │ ├── author.dbtpl.go │ │ ├── authorbookresult.dbtpl.go │ │ ├── book.dbtpl.go │ │ ├── booktype.dbtpl.go │ │ ├── dbtpl.dbtpl.dot │ │ ├── dbtpl.dbtpl.go │ │ ├── dbtpl.dbtpl.json │ │ ├── dbtpl.dbtpl.sql │ │ ├── dbtpl.dbtpl.yaml │ │ └── sf_sayhello.dbtpl.go │ ├── oracle.go │ ├── oracle │ │ ├── author.dbtpl.go │ │ ├── authorbookresult.dbtpl.go │ │ ├── book.dbtpl.go │ │ ├── dbtpl.dbtpl.dot │ │ ├── dbtpl.dbtpl.go │ │ ├── dbtpl.dbtpl.json │ │ ├── dbtpl.dbtpl.sql │ │ ├── dbtpl.dbtpl.yaml │ │ └── sf_sayhello.dbtpl.go │ ├── postgres.go │ ├── postgres │ │ ├── author.dbtpl.go │ │ ├── authorbookresult.dbtpl.go │ │ ├── book.dbtpl.go │ │ ├── booktype.dbtpl.go │ │ ├── dbtpl.dbtpl.dot │ │ ├── dbtpl.dbtpl.go │ │ ├── dbtpl.dbtpl.json │ │ ├── dbtpl.dbtpl.sql │ │ ├── dbtpl.dbtpl.yaml │ │ └── sf_sayhello.dbtpl.go │ ├── sql │ │ ├── mysql_query.sql │ │ ├── mysql_schema.sql │ │ ├── oracle_query.sql │ │ ├── oracle_schema.sql │ │ ├── postgres_query.sql │ │ ├── postgres_schema.sql │ │ ├── sqlite3_query.sql │ │ ├── sqlite3_schema.sql │ │ ├── sqlserver_query.sql │ │ └── sqlserver_schema.sql │ ├── sqlite3.go │ ├── sqlite3 │ │ ├── author.dbtpl.go │ │ ├── authorbookresult.dbtpl.go │ │ ├── book.dbtpl.go │ │ ├── dbtpl.dbtpl.dot │ │ ├── dbtpl.dbtpl.go │ │ ├── dbtpl.dbtpl.json │ │ ├── dbtpl.dbtpl.sql │ │ └── dbtpl.dbtpl.yaml │ ├── sqlserver.go │ └── sqlserver │ │ ├── author.dbtpl.go │ │ ├── authorbookresult.dbtpl.go │ │ ├── book.dbtpl.go │ │ ├── dbtpl.dbtpl.dot │ │ ├── dbtpl.dbtpl.go │ │ ├── dbtpl.dbtpl.json │ │ ├── dbtpl.dbtpl.sql │ │ ├── dbtpl.dbtpl.yaml │ │ └── sp_sayhello.dbtpl.go ├── createdb.sh ├── django │ ├── .gitignore │ ├── Pipfile │ ├── Pipfile.lock │ ├── README.md │ ├── gen.sh │ ├── init.sh │ ├── main.go │ ├── models.py │ ├── mysql.go │ ├── mysql │ │ ├── authgroup.dbtpl.go │ │ ├── authgrouppermission.dbtpl.go │ │ ├── author.dbtpl.go │ │ ├── authpermission.dbtpl.go │ │ ├── authuser.dbtpl.go │ │ ├── authusergroup.dbtpl.go │ │ ├── authuseruserpermission.dbtpl.go │ │ ├── book.dbtpl.go │ │ ├── bookstag.dbtpl.go │ │ ├── dbtpl.dbtpl.dot │ │ ├── dbtpl.dbtpl.go │ │ ├── dbtpl.dbtpl.json │ │ ├── dbtpl.dbtpl.sql │ │ ├── dbtpl.dbtpl.yaml │ │ ├── djangoadminlog.dbtpl.go │ │ ├── djangocontenttype.dbtpl.go │ │ ├── djangomigration.dbtpl.go │ │ ├── djangosession.dbtpl.go │ │ └── tag.dbtpl.go │ ├── oracle.go │ ├── oracle │ │ ├── authgroup.dbtpl.go │ │ ├── authgrouppermission.dbtpl.go │ │ ├── author.dbtpl.go │ │ ├── authpermission.dbtpl.go │ │ ├── authuser.dbtpl.go │ │ ├── authusergroup.dbtpl.go │ │ ├── authuseruserpermission.dbtpl.go │ │ ├── book.dbtpl.go │ │ ├── bookstag.dbtpl.go │ │ ├── dbtpl.dbtpl.dot │ │ ├── dbtpl.dbtpl.go │ │ ├── dbtpl.dbtpl.json │ │ ├── dbtpl.dbtpl.sql │ │ ├── dbtpl.dbtpl.yaml │ │ ├── djangoadminlog.dbtpl.go │ │ ├── djangocontenttype.dbtpl.go │ │ ├── djangomigration.dbtpl.go │ │ ├── djangosession.dbtpl.go │ │ └── tag.dbtpl.go │ ├── postgres.go │ ├── postgres │ │ ├── authgroup.dbtpl.go │ │ ├── authgrouppermission.dbtpl.go │ │ ├── author.dbtpl.go │ │ ├── authpermission.dbtpl.go │ │ ├── authuser.dbtpl.go │ │ ├── authusergroup.dbtpl.go │ │ ├── authuseruserpermission.dbtpl.go │ │ ├── book.dbtpl.go │ │ ├── bookstag.dbtpl.go │ │ ├── dbtpl.dbtpl.dot │ │ ├── dbtpl.dbtpl.go │ │ ├── dbtpl.dbtpl.json │ │ ├── dbtpl.dbtpl.sql │ │ ├── dbtpl.dbtpl.yaml │ │ ├── djangoadminlog.dbtpl.go │ │ ├── djangocontenttype.dbtpl.go │ │ ├── djangomigration.dbtpl.go │ │ ├── djangosession.dbtpl.go │ │ └── tag.dbtpl.go │ ├── sql │ │ ├── oracle_post.sql │ │ ├── postgres_post.sql │ │ └── sqlserver_post.sql │ ├── sqlite3.go │ ├── sqlite3 │ │ ├── authgroup.dbtpl.go │ │ ├── authgrouppermission.dbtpl.go │ │ ├── author.dbtpl.go │ │ ├── authpermission.dbtpl.go │ │ ├── authuser.dbtpl.go │ │ ├── authusergroup.dbtpl.go │ │ ├── authuseruserpermission.dbtpl.go │ │ ├── book.dbtpl.go │ │ ├── bookstag.dbtpl.go │ │ ├── dbtpl.dbtpl.dot │ │ ├── dbtpl.dbtpl.go │ │ ├── dbtpl.dbtpl.json │ │ ├── dbtpl.dbtpl.sql │ │ ├── dbtpl.dbtpl.yaml │ │ ├── djangoadminlog.dbtpl.go │ │ ├── djangocontenttype.dbtpl.go │ │ ├── djangomigration.dbtpl.go │ │ ├── djangosession.dbtpl.go │ │ └── tag.dbtpl.go │ ├── sqlserver.go │ └── sqlserver │ │ ├── authgroup.dbtpl.go │ │ ├── authgrouppermission.dbtpl.go │ │ ├── author.dbtpl.go │ │ ├── authpermission.dbtpl.go │ │ ├── authuser.dbtpl.go │ │ ├── authusergroup.dbtpl.go │ │ ├── authuseruserpermission.dbtpl.go │ │ ├── book.dbtpl.go │ │ ├── bookstag.dbtpl.go │ │ ├── dbtpl.dbtpl.dot │ │ ├── dbtpl.dbtpl.go │ │ ├── dbtpl.dbtpl.json │ │ ├── dbtpl.dbtpl.sql │ │ ├── dbtpl.dbtpl.yaml │ │ ├── djangoadminlog.dbtpl.go │ │ ├── djangocontenttype.dbtpl.go │ │ ├── djangomigration.dbtpl.go │ │ ├── djangosession.dbtpl.go │ │ └── tag.dbtpl.go ├── init │ ├── mysql.sql │ ├── oracle.sql │ ├── postgres.sql │ └── sqlserver.sql └── northwind │ ├── .gitignore │ ├── README.md │ ├── gen.sh │ ├── main.go │ ├── mysql.go │ ├── mysql │ ├── category.dbtpl.go │ ├── customer.dbtpl.go │ ├── customercustomerdemo.dbtpl.go │ ├── customerdemographic.dbtpl.go │ ├── dbtpl.dbtpl.dot │ ├── dbtpl.dbtpl.go │ ├── dbtpl.dbtpl.json │ ├── dbtpl.dbtpl.sql │ ├── dbtpl.dbtpl.yaml │ ├── employee.dbtpl.go │ ├── employeeterritory.dbtpl.go │ ├── order.dbtpl.go │ ├── orderdetail.dbtpl.go │ ├── product.dbtpl.go │ ├── region.dbtpl.go │ ├── shipper.dbtpl.go │ ├── supplier.dbtpl.go │ ├── territory.dbtpl.go │ └── usstate.dbtpl.go │ ├── oracle.go │ ├── oracle │ ├── category.dbtpl.go │ ├── customer.dbtpl.go │ ├── customercustomerdemo.dbtpl.go │ ├── customerdemographic.dbtpl.go │ ├── dbtpl.dbtpl.dot │ ├── dbtpl.dbtpl.go │ ├── dbtpl.dbtpl.json │ ├── dbtpl.dbtpl.sql │ ├── dbtpl.dbtpl.yaml │ ├── employee.dbtpl.go │ ├── employeeterritory.dbtpl.go │ ├── order.dbtpl.go │ ├── orderdetail.dbtpl.go │ ├── product.dbtpl.go │ ├── region.dbtpl.go │ ├── shipper.dbtpl.go │ ├── supplier.dbtpl.go │ ├── territory.dbtpl.go │ └── usstate.dbtpl.go │ ├── postgres.go │ ├── postgres │ ├── category.dbtpl.go │ ├── customer.dbtpl.go │ ├── customercustomerdemo.dbtpl.go │ ├── customerdemographic.dbtpl.go │ ├── dbtpl.dbtpl.dot │ ├── dbtpl.dbtpl.go │ ├── dbtpl.dbtpl.json │ ├── dbtpl.dbtpl.sql │ ├── dbtpl.dbtpl.yaml │ ├── employee.dbtpl.go │ ├── employeeterritory.dbtpl.go │ ├── order.dbtpl.go │ ├── orderdetail.dbtpl.go │ ├── product.dbtpl.go │ ├── region.dbtpl.go │ ├── shipper.dbtpl.go │ ├── supplier.dbtpl.go │ ├── territory.dbtpl.go │ └── usstate.dbtpl.go │ ├── sql │ ├── mysql_schema.sql │ ├── oracle_schema.sql │ ├── postgres_data.sql │ ├── postgres_schema.sql │ ├── sqlite3_schema.sql │ └── sqlserver_schema.sql │ ├── sqlite3.go │ ├── sqlite3 │ ├── category.dbtpl.go │ ├── customer.dbtpl.go │ ├── customercustomerdemo.dbtpl.go │ ├── customerdemographic.dbtpl.go │ ├── dbtpl.dbtpl.dot │ ├── dbtpl.dbtpl.go │ ├── dbtpl.dbtpl.json │ ├── dbtpl.dbtpl.sql │ ├── dbtpl.dbtpl.yaml │ ├── employee.dbtpl.go │ ├── employeeterritory.dbtpl.go │ ├── order.dbtpl.go │ ├── orderdetail.dbtpl.go │ ├── product.dbtpl.go │ ├── region.dbtpl.go │ ├── shipper.dbtpl.go │ ├── supplier.dbtpl.go │ ├── territory.dbtpl.go │ └── usstate.dbtpl.go │ ├── sqlserver.go │ └── sqlserver │ ├── category.dbtpl.go │ ├── customer.dbtpl.go │ ├── customercustomerdemo.dbtpl.go │ ├── customerdemographic.dbtpl.go │ ├── dbtpl.dbtpl.dot │ ├── dbtpl.dbtpl.go │ ├── dbtpl.dbtpl.json │ ├── dbtpl.dbtpl.sql │ ├── dbtpl.dbtpl.yaml │ ├── employee.dbtpl.go │ ├── employeeterritory.dbtpl.go │ ├── order.dbtpl.go │ ├── orderdetail.dbtpl.go │ ├── product.dbtpl.go │ ├── region.dbtpl.go │ ├── shipper.dbtpl.go │ ├── supplier.dbtpl.go │ ├── territory.dbtpl.go │ └── usstate.dbtpl.go ├── build.sh ├── cmd ├── cmd.go ├── query.go └── schema.go ├── gen.sh ├── go.mod ├── go.sum ├── internal ├── gen.sh ├── github_com-Masterminds-sprig-v3.go ├── github_com-goccy-go-yaml.go ├── github_com-kenshaw-glob.go ├── github_com-kenshaw-inflector.go ├── github_com-kenshaw-snaker.go ├── github_com-xo-dbtpl-loader.go ├── github_com-xo-dbtpl-types.go ├── github_com-yookoala-realpath.go ├── golang_org-x-tools-imports.go ├── internal.go ├── mvdan_cc-gofumpt-format.go └── os-exec.go ├── loader ├── loader.go ├── mysql.go ├── mysql_test.go ├── oracle.go ├── postgres.go ├── sqlite3.go └── sqlserver.go ├── main.go ├── models ├── column.dbtpl.go ├── enum.dbtpl.go ├── enumvalue.dbtpl.go ├── foreignkey.dbtpl.go ├── index.dbtpl.go ├── indexcolumn.dbtpl.go ├── models.dbtpl.go ├── mysqlenumvalue.dbtpl.go ├── postgrescolorder.dbtpl.go ├── proc.dbtpl.go ├── procparam.dbtpl.go ├── sequence.dbtpl.go └── table.dbtpl.go ├── templates ├── createdb │ ├── createdb.go │ └── dbtpl.dbtpl.sql.tpl ├── dot │ ├── dbtpl.dbtpl.dot.tpl │ └── dot.go ├── go │ ├── db.dbtpl.go.tpl │ ├── go.go │ ├── hdr.dbtpl.go.tpl │ ├── query.dbtpl.go.tpl │ └── schema.dbtpl.go.tpl ├── json │ ├── dbtpl.dbtpl.json.tpl │ └── json.go ├── templates.go └── yaml │ ├── dbtpl.dbtpl.yaml.tpl │ └── yaml.go └── types └── types.go /.gitattributes: -------------------------------------------------------------------------------- 1 | _examples/**/*.xo.* linguist-generated 2 | **/*.go.tpl linguist-language=go 3 | **/*.yaml.tpl linguist-language=yaml 4 | **/*.json.tpl linguist-language=json 5 | **/*.dot.tpl linguist-language=dot 6 | -------------------------------------------------------------------------------- /.github/workflows/test.yml: -------------------------------------------------------------------------------- 1 | on: [push, pull_request] 2 | name: Test 3 | jobs: 4 | test: 5 | name: Build 6 | runs-on: ubuntu-latest 7 | steps: 8 | - name: Install Go 9 | uses: actions/setup-go@v5 10 | with: 11 | go-version: stable 12 | - name: Checkout code 13 | uses: actions/checkout@v3 14 | - name: Build 15 | run: go build ./... 16 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /dbtpl 2 | /dbtpl.exe 3 | 4 | /build/ 5 | /x/ 6 | /tmp/ 7 | /test/ 8 | 9 | *.txt 10 | *.db 11 | *.dot 12 | *.json 13 | *.yaml 14 | *.yml 15 | *.sqlite3 16 | *.sqlite3-journal 17 | *.svg 18 | *.dbtpl.sql 19 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2015-2023 Kenneth Shaw 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /_examples/a_bit_of_everything/.gitignore: -------------------------------------------------------------------------------- 1 | /a_bit_of_everything 2 | /a_bit_of_everything.exe 3 | -------------------------------------------------------------------------------- /_examples/a_bit_of_everything/gen.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | SRC=$(realpath $(cd -P "$(dirname "${BASH_SOURCE[0]}")" && pwd)) 4 | 5 | TEST=$(basename $SRC) 6 | 7 | declare -A DSNS 8 | DSNS+=( 9 | [mysql]=my://$TEST:$TEST@localhost/$TEST 10 | [oracle]=or://$TEST:$TEST@localhost/free 11 | [postgres]=pg://$TEST:$TEST@localhost/$TEST 12 | [sqlite3]=sq:$TEST.db 13 | [sqlserver]=ms://$TEST:$TEST@localhost/$TEST 14 | ) 15 | 16 | APPLY=0 17 | BUILD=0 18 | DATABASES="mysql oracle postgres sqlite3 sqlserver" 19 | ARGS=() 20 | 21 | OPTIND=1 22 | while getopts "abd:vD" opt; do 23 | case "$opt" in 24 | a) APPLY=1 ;; 25 | b) BUILD=1 ;; 26 | d) DATABASES=$OPTARG ;; 27 | v) ARGS+=(-v) ;; 28 | D) ARGS+=(-D) ;; 29 | esac 30 | done 31 | shift $(($OPTIND-1)) 32 | 33 | if [ "$BUILD" = "1" ]; then 34 | pushd $SRC/../../ &> /dev/null 35 | (set -ex; 36 | go build 37 | ) 38 | popd &> /dev/null 39 | fi 40 | 41 | DBTPLBIN=$(which dbtpl) 42 | if [ -e $SRC/../../dbtpl ]; then 43 | DBTPLBIN=$SRC/../../dbtpl 44 | fi 45 | DBTPLBIN=$(realpath $DBTPLBIN) 46 | 47 | pushd $SRC &> /dev/null 48 | 49 | for TYPE in $DATABASES; do 50 | DB=${DSNS[$TYPE]} 51 | if [ -z "$DB" ]; then 52 | echo "$TYPE has no defined DSN" 53 | exit 1 54 | fi 55 | mkdir -p $TYPE 56 | rm -f $TYPE/*.dbtpl.* 57 | echo "------------------------------------------------------" 58 | echo "$TYPE: $DB" 59 | if [ "$APPLY" = "1" ]; then 60 | if [[ "$TYPE" = "sqlite3" && -f $TEST.db ]]; then 61 | (set -ex; 62 | rm $TEST.db 63 | ) 64 | fi 65 | (set -ex; 66 | $SRC/../createdb.sh -d $TYPE -n $TEST 67 | usql -f sql/${TYPE}_schema.sql $DB 68 | ) 69 | if [ -f sql/${TYPE}_data.sql ]; then 70 | (set -ex; 71 | usql -f sql/${TYPE}_data.sql $DB 72 | ) 73 | fi 74 | fi 75 | (set -ex; 76 | $DBTPLBIN schema $DB -o $TYPE ${ARGS[@]} 77 | $DBTPLBIN schema $DB -o $TYPE -t createdb ${ARGS[@]} --createdb-fmt="" 78 | $DBTPLBIN schema $DB -o $TYPE -t json ${ARGS[@]} 79 | $DBTPLBIN schema $DB -o $TYPE -t yaml ${ARGS[@]} 80 | $DBTPLBIN schema $DB -o $TYPE -t dot ${ARGS[@]} 81 | go build ./$TYPE 82 | go build 83 | ./$TEST -dsn $DB ${ARGS[@]} 84 | ) 85 | done 86 | 87 | popd &> /dev/null 88 | -------------------------------------------------------------------------------- /_examples/a_bit_of_everything/main.go: -------------------------------------------------------------------------------- 1 | // Command a_bit_of_everything demonstrates using generated models for the a_bit_of_everything 2 | // sample database. 3 | // 4 | //go:debug x509negativeserial=1 5 | package main 6 | 7 | import ( 8 | "context" 9 | "database/sql" 10 | "flag" 11 | "fmt" 12 | "os" 13 | "os/user" 14 | 15 | // drivers 16 | _ "github.com/go-sql-driver/mysql" 17 | _ "github.com/lib/pq" 18 | _ "github.com/mattn/go-sqlite3" 19 | _ "github.com/microsoft/go-mssqldb" 20 | _ "github.com/sijms/go-ora/v2" 21 | 22 | // models 23 | "github.com/xo/dbtpl/_examples/a_bit_of_everything/mysql" 24 | "github.com/xo/dbtpl/_examples/a_bit_of_everything/oracle" 25 | "github.com/xo/dbtpl/_examples/a_bit_of_everything/postgres" 26 | "github.com/xo/dbtpl/_examples/a_bit_of_everything/sqlite3" 27 | "github.com/xo/dbtpl/_examples/a_bit_of_everything/sqlserver" 28 | 29 | "github.com/xo/dburl" 30 | "github.com/xo/dburl/passfile" 31 | ) 32 | 33 | func main() { 34 | verbose := flag.Bool("v", false, "verbose") 35 | dsn := flag.String("dsn", "", "dsn") 36 | flag.Parse() 37 | if err := run(context.Background(), *verbose, *dsn); err != nil { 38 | fmt.Fprintln(os.Stderr, "error:", err) 39 | os.Exit(1) 40 | } 41 | } 42 | 43 | func run(ctx context.Context, verbose bool, dsn string) error { 44 | if verbose { 45 | logger := func(s string, v ...any) { 46 | fmt.Printf("-------------------------------------\nQUERY: %s\n VAL: %v\n\n", s, v) 47 | } 48 | mysql.SetLogger(logger) 49 | oracle.SetLogger(logger) 50 | postgres.SetLogger(logger) 51 | sqlite3.SetLogger(logger) 52 | sqlserver.SetLogger(logger) 53 | } 54 | v, err := user.Current() 55 | if err != nil { 56 | return err 57 | } 58 | // parse url 59 | u, err := parse(dsn) 60 | if err != nil { 61 | return err 62 | } 63 | // open database 64 | db, err := passfile.OpenURL(u, v.HomeDir, "dbtplpass") 65 | if err != nil { 66 | return err 67 | } 68 | var f func(context.Context, *sql.DB) error 69 | switch u.Driver { 70 | case "mysql": 71 | f = runMysql 72 | case "oracle": 73 | f = runOracle 74 | case "postgres": 75 | f = runPostgres 76 | case "sqlite3": 77 | f = runSqlite3 78 | case "sqlserver": 79 | f = runSqlserver 80 | } 81 | return f(ctx, db) 82 | } 83 | 84 | func parse(dsn string) (*dburl.URL, error) { 85 | v, err := dburl.Parse(dsn) 86 | if err != nil { 87 | return nil, err 88 | } 89 | switch v.Driver { 90 | case "mysql": 91 | q := v.Query() 92 | q.Set("parseTime", "true") 93 | v.RawQuery = q.Encode() 94 | return dburl.Parse(v.String()) 95 | case "sqlite3": 96 | q := v.Query() 97 | q.Set("_loc", "auto") 98 | v.RawQuery = q.Encode() 99 | return dburl.Parse(v.String()) 100 | } 101 | return v, nil 102 | } 103 | -------------------------------------------------------------------------------- /_examples/a_bit_of_everything/mysql.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "context" 5 | "database/sql" 6 | "fmt" 7 | 8 | models "github.com/xo/dbtpl/_examples/a_bit_of_everything/mysql" 9 | ) 10 | 11 | func runMysql(ctx context.Context, db *sql.DB) error { 12 | // run procs 13 | if err := models.A0In0Out(ctx, db); err != nil { 14 | return fmt.Errorf("0 in 0 out: %v", err) 15 | } 16 | // 1 in 0 out 17 | if err := models.A1In0Out(ctx, db, 10); err != nil { 18 | return fmt.Errorf("1 in 0 out: %v", err) 19 | } 20 | var res1 int 21 | var err error 22 | // 0 in 1 out 23 | /* 24 | if res1, err = models.A0In1Out(ctx, db); err != nil { 25 | return fmt.Errorf("0 in 1 out: %v", err) 26 | } 27 | fmt.Printf("a_0_in_1_out(): %d\n", res1) 28 | // 1 in 1 out 29 | if res1, err = models.A1In1Out(ctx, db, 10); err != nil { 30 | return fmt.Errorf("1 in 0 out: %v", err) 31 | } 32 | fmt.Printf("a_1_in_1_out(%d): %d\n", 10, res1) 33 | // 2 in 2 out 34 | if res1, res2, err = models.A2In2Out(ctx, db, 10, 20); err != nil { 35 | return fmt.Errorf("2 in 2 out: %v", err) 36 | } 37 | fmt.Printf("a_2_in_2_out(%d, %d): %d, %d\n", 10, 20, res1, res2) 38 | */ 39 | // run funcs 40 | // 0 in 41 | if res1, err = models.AFunc0In(ctx, db); err != nil { 42 | return fmt.Errorf("a func 0 in: %v", err) 43 | } 44 | fmt.Printf("a_func_0_in(): %d\n", res1) 45 | // 1 in 46 | if res1, err = models.AFunc1In(ctx, db, 10); err != nil { 47 | return fmt.Errorf("a func 1 in: %v", err) 48 | } 49 | fmt.Printf("a_func_1_in(%d): %d\n", 10, res1) 50 | // 1 in 51 | if res1, err = models.AFunc2In(ctx, db, 10, 20); err != nil { 52 | return fmt.Errorf("a func 2 in: %v", err) 53 | } 54 | fmt.Printf("a_func_2_in(%d, %d): %d\n", 10, 20, res1) 55 | return nil 56 | } 57 | -------------------------------------------------------------------------------- /_examples/a_bit_of_everything/mysql/aenum.dbtpl.go: -------------------------------------------------------------------------------- 1 | package mysql 2 | 3 | // Code generated by dbtpl. DO NOT EDIT. 4 | 5 | import ( 6 | "database/sql/driver" 7 | "fmt" 8 | ) 9 | 10 | // AEnum is the 'a_enum' enum type from schema 'a_bit_of_everything'. 11 | type AEnum uint16 12 | 13 | // AEnum values. 14 | const ( 15 | // AEnumOne is the 'ONE' a_enum. 16 | AEnumOne AEnum = 1 17 | // AEnumTwo is the 'TWO' a_enum. 18 | AEnumTwo AEnum = 2 19 | ) 20 | 21 | // String satisfies the [fmt.Stringer] interface. 22 | func (ae AEnum) String() string { 23 | switch ae { 24 | case AEnumOne: 25 | return "ONE" 26 | case AEnumTwo: 27 | return "TWO" 28 | } 29 | return fmt.Sprintf("AEnum(%d)", ae) 30 | } 31 | 32 | // MarshalText marshals [AEnum] into text. 33 | func (ae AEnum) MarshalText() ([]byte, error) { 34 | return []byte(ae.String()), nil 35 | } 36 | 37 | // UnmarshalText unmarshals [AEnum] from text. 38 | func (ae *AEnum) UnmarshalText(buf []byte) error { 39 | switch str := string(buf); str { 40 | case "ONE": 41 | *ae = AEnumOne 42 | case "TWO": 43 | *ae = AEnumTwo 44 | default: 45 | return ErrInvalidAEnum(str) 46 | } 47 | return nil 48 | } 49 | 50 | // Value satisfies the [driver.Valuer] interface. 51 | func (ae AEnum) Value() (driver.Value, error) { 52 | return ae.String(), nil 53 | } 54 | 55 | // Scan satisfies the [sql.Scanner] interface. 56 | func (ae *AEnum) Scan(v any) error { 57 | switch x := v.(type) { 58 | case []byte: 59 | return ae.UnmarshalText(x) 60 | case string: 61 | return ae.UnmarshalText([]byte(x)) 62 | } 63 | return ErrInvalidAEnum(fmt.Sprintf("%T", v)) 64 | } 65 | 66 | // NullAEnum represents a null 'a_enum' enum for schema 'a_bit_of_everything'. 67 | type NullAEnum struct { 68 | AEnum AEnum 69 | // Valid is true if [AEnum] is not null. 70 | Valid bool 71 | } 72 | 73 | // Value satisfies the [driver.Valuer] interface. 74 | func (nae NullAEnum) Value() (driver.Value, error) { 75 | if !nae.Valid { 76 | return nil, nil 77 | } 78 | return nae.AEnum.Value() 79 | } 80 | 81 | // Scan satisfies the [sql.Scanner] interface. 82 | func (nae *NullAEnum) Scan(v any) error { 83 | if v == nil { 84 | nae.AEnum, nae.Valid = 0, false 85 | return nil 86 | } 87 | err := nae.AEnum.Scan(v) 88 | nae.Valid = err == nil 89 | return err 90 | } 91 | 92 | // ErrInvalidAEnum is the invalid [AEnum] error. 93 | type ErrInvalidAEnum string 94 | 95 | // Error satisfies the error interface. 96 | func (err ErrInvalidAEnum) Error() string { 97 | return fmt.Sprintf("invalid AEnum(%s)", string(err)) 98 | } 99 | -------------------------------------------------------------------------------- /_examples/a_bit_of_everything/mysql/aenumnullable.dbtpl.go: -------------------------------------------------------------------------------- 1 | package mysql 2 | 3 | // Code generated by dbtpl. DO NOT EDIT. 4 | 5 | import ( 6 | "database/sql/driver" 7 | "fmt" 8 | ) 9 | 10 | // AEnumNullable is the 'a_enum_nullable' enum type from schema 'a_bit_of_everything'. 11 | type AEnumNullable uint16 12 | 13 | // AEnumNullable values. 14 | const ( 15 | // AEnumNullableOne is the 'ONE' a_enum_nullable. 16 | AEnumNullableOne AEnumNullable = 1 17 | // AEnumNullableTwo is the 'TWO' a_enum_nullable. 18 | AEnumNullableTwo AEnumNullable = 2 19 | ) 20 | 21 | // String satisfies the [fmt.Stringer] interface. 22 | func (aen AEnumNullable) String() string { 23 | switch aen { 24 | case AEnumNullableOne: 25 | return "ONE" 26 | case AEnumNullableTwo: 27 | return "TWO" 28 | } 29 | return fmt.Sprintf("AEnumNullable(%d)", aen) 30 | } 31 | 32 | // MarshalText marshals [AEnumNullable] into text. 33 | func (aen AEnumNullable) MarshalText() ([]byte, error) { 34 | return []byte(aen.String()), nil 35 | } 36 | 37 | // UnmarshalText unmarshals [AEnumNullable] from text. 38 | func (aen *AEnumNullable) UnmarshalText(buf []byte) error { 39 | switch str := string(buf); str { 40 | case "ONE": 41 | *aen = AEnumNullableOne 42 | case "TWO": 43 | *aen = AEnumNullableTwo 44 | default: 45 | return ErrInvalidAEnumNullable(str) 46 | } 47 | return nil 48 | } 49 | 50 | // Value satisfies the [driver.Valuer] interface. 51 | func (aen AEnumNullable) Value() (driver.Value, error) { 52 | return aen.String(), nil 53 | } 54 | 55 | // Scan satisfies the [sql.Scanner] interface. 56 | func (aen *AEnumNullable) Scan(v any) error { 57 | switch x := v.(type) { 58 | case []byte: 59 | return aen.UnmarshalText(x) 60 | case string: 61 | return aen.UnmarshalText([]byte(x)) 62 | } 63 | return ErrInvalidAEnumNullable(fmt.Sprintf("%T", v)) 64 | } 65 | 66 | // NullAEnumNullable represents a null 'a_enum_nullable' enum for schema 'a_bit_of_everything'. 67 | type NullAEnumNullable struct { 68 | AEnumNullable AEnumNullable 69 | // Valid is true if [AEnumNullable] is not null. 70 | Valid bool 71 | } 72 | 73 | // Value satisfies the [driver.Valuer] interface. 74 | func (naen NullAEnumNullable) Value() (driver.Value, error) { 75 | if !naen.Valid { 76 | return nil, nil 77 | } 78 | return naen.AEnumNullable.Value() 79 | } 80 | 81 | // Scan satisfies the [sql.Scanner] interface. 82 | func (naen *NullAEnumNullable) Scan(v any) error { 83 | if v == nil { 84 | naen.AEnumNullable, naen.Valid = 0, false 85 | return nil 86 | } 87 | err := naen.AEnumNullable.Scan(v) 88 | naen.Valid = err == nil 89 | return err 90 | } 91 | 92 | // ErrInvalidAEnumNullable is the invalid [AEnumNullable] error. 93 | type ErrInvalidAEnumNullable string 94 | 95 | // Error satisfies the error interface. 96 | func (err ErrInvalidAEnumNullable) Error() string { 97 | return fmt.Sprintf("invalid AEnumNullable(%s)", string(err)) 98 | } 99 | -------------------------------------------------------------------------------- /_examples/a_bit_of_everything/mysql/aforeignkey.dbtpl.go: -------------------------------------------------------------------------------- 1 | package mysql 2 | 3 | // Code generated by dbtpl. DO NOT EDIT. 4 | 5 | import ( 6 | "context" 7 | "database/sql" 8 | ) 9 | 10 | // AForeignKey represents a row from 'a_bit_of_everything.a_foreign_key'. 11 | type AForeignKey struct { 12 | AKey sql.NullInt64 `json:"a_key"` // a_key 13 | } 14 | 15 | // AForeignKeyByAKey retrieves a row from 'a_bit_of_everything.a_foreign_key' as a [AForeignKey]. 16 | // 17 | // Generated from index 'a_key'. 18 | func AForeignKeyByAKey(ctx context.Context, db DB, aKey sql.NullInt64) ([]*AForeignKey, error) { 19 | // query 20 | const sqlstr = `SELECT ` + 21 | `a_key ` + 22 | `FROM a_bit_of_everything.a_foreign_key ` + 23 | `WHERE a_key = ?` 24 | // run 25 | logf(sqlstr, aKey) 26 | rows, err := db.QueryContext(ctx, sqlstr, aKey) 27 | if err != nil { 28 | return nil, logerror(err) 29 | } 30 | defer rows.Close() 31 | // process 32 | var res []*AForeignKey 33 | for rows.Next() { 34 | afk := AForeignKey{} 35 | // scan 36 | if err := rows.Scan(&afk.AKey); err != nil { 37 | return nil, logerror(err) 38 | } 39 | res = append(res, &afk) 40 | } 41 | if err := rows.Err(); err != nil { 42 | return nil, logerror(err) 43 | } 44 | return res, nil 45 | } 46 | 47 | // APrimary returns the APrimary associated with the [AForeignKey]'s (AKey). 48 | // 49 | // Generated from foreign key 'a_foreign_key_ibfk_1'. 50 | func (afk *AForeignKey) APrimary(ctx context.Context, db DB) (*APrimary, error) { 51 | return APrimaryByAKey(ctx, db, int(afk.AKey.Int64)) 52 | } 53 | -------------------------------------------------------------------------------- /_examples/a_bit_of_everything/mysql/aforeignkeycomposite.dbtpl.go: -------------------------------------------------------------------------------- 1 | package mysql 2 | 3 | // Code generated by dbtpl. DO NOT EDIT. 4 | 5 | import ( 6 | "context" 7 | "database/sql" 8 | ) 9 | 10 | // AForeignKeyComposite represents a row from 'a_bit_of_everything.a_foreign_key_composite'. 11 | type AForeignKeyComposite struct { 12 | AKey1 sql.NullInt64 `json:"a_key1"` // a_key1 13 | AKey2 sql.NullInt64 `json:"a_key2"` // a_key2 14 | } 15 | 16 | // AForeignKeyCompositeByAKey1AKey2 retrieves a row from 'a_bit_of_everything.a_foreign_key_composite' as a [AForeignKeyComposite]. 17 | // 18 | // Generated from index 'a_key1'. 19 | func AForeignKeyCompositeByAKey1AKey2(ctx context.Context, db DB, aKey1, aKey2 sql.NullInt64) ([]*AForeignKeyComposite, error) { 20 | // query 21 | const sqlstr = `SELECT ` + 22 | `a_key1, a_key2 ` + 23 | `FROM a_bit_of_everything.a_foreign_key_composite ` + 24 | `WHERE a_key1 = ? AND a_key2 = ?` 25 | // run 26 | logf(sqlstr, aKey1, aKey2) 27 | rows, err := db.QueryContext(ctx, sqlstr, aKey1, aKey2) 28 | if err != nil { 29 | return nil, logerror(err) 30 | } 31 | defer rows.Close() 32 | // process 33 | var res []*AForeignKeyComposite 34 | for rows.Next() { 35 | afkc := AForeignKeyComposite{} 36 | // scan 37 | if err := rows.Scan(&afkc.AKey1, &afkc.AKey2); err != nil { 38 | return nil, logerror(err) 39 | } 40 | res = append(res, &afkc) 41 | } 42 | if err := rows.Err(); err != nil { 43 | return nil, logerror(err) 44 | } 45 | return res, nil 46 | } 47 | 48 | // APrimaryComposite returns the APrimaryComposite associated with the [AForeignKeyComposite]'s (AKey1, AKey2). 49 | // 50 | // Generated from foreign key 'a_foreign_key_composite_ibfk_1'. 51 | func (afkc *AForeignKeyComposite) APrimaryComposite(ctx context.Context, db DB) (*APrimaryComposite, error) { 52 | return APrimaryCompositeByAKey1AKey2(ctx, db, int(afkc.AKey1.Int64), int(afkc.AKey2.Int64)) 53 | } 54 | -------------------------------------------------------------------------------- /_examples/a_bit_of_everything/mysql/aindex.dbtpl.go: -------------------------------------------------------------------------------- 1 | package mysql 2 | 3 | // Code generated by dbtpl. DO NOT EDIT. 4 | 5 | import ( 6 | "context" 7 | "database/sql" 8 | ) 9 | 10 | // AIndex represents a row from 'a_bit_of_everything.a_index'. 11 | type AIndex struct { 12 | AKey sql.NullInt64 `json:"a_key"` // a_key 13 | } 14 | 15 | // AIndexByAKey retrieves a row from 'a_bit_of_everything.a_index' as a [AIndex]. 16 | // 17 | // Generated from index 'a_index_idx'. 18 | func AIndexByAKey(ctx context.Context, db DB, aKey sql.NullInt64) ([]*AIndex, error) { 19 | // query 20 | const sqlstr = `SELECT ` + 21 | `a_key ` + 22 | `FROM a_bit_of_everything.a_index ` + 23 | `WHERE a_key = ?` 24 | // run 25 | logf(sqlstr, aKey) 26 | rows, err := db.QueryContext(ctx, sqlstr, aKey) 27 | if err != nil { 28 | return nil, logerror(err) 29 | } 30 | defer rows.Close() 31 | // process 32 | var res []*AIndex 33 | for rows.Next() { 34 | ai := AIndex{} 35 | // scan 36 | if err := rows.Scan(&ai.AKey); err != nil { 37 | return nil, logerror(err) 38 | } 39 | res = append(res, &ai) 40 | } 41 | if err := rows.Err(); err != nil { 42 | return nil, logerror(err) 43 | } 44 | return res, nil 45 | } 46 | -------------------------------------------------------------------------------- /_examples/a_bit_of_everything/mysql/aindexcomposite.dbtpl.go: -------------------------------------------------------------------------------- 1 | package mysql 2 | 3 | // Code generated by dbtpl. DO NOT EDIT. 4 | 5 | import ( 6 | "context" 7 | "database/sql" 8 | ) 9 | 10 | // AIndexComposite represents a row from 'a_bit_of_everything.a_index_composite'. 11 | type AIndexComposite struct { 12 | AKey1 sql.NullInt64 `json:"a_key1"` // a_key1 13 | AKey2 sql.NullInt64 `json:"a_key2"` // a_key2 14 | } 15 | 16 | // AIndexCompositeByAKey1AKey2 retrieves a row from 'a_bit_of_everything.a_index_composite' as a [AIndexComposite]. 17 | // 18 | // Generated from index 'a_index_composite_idx'. 19 | func AIndexCompositeByAKey1AKey2(ctx context.Context, db DB, aKey1, aKey2 sql.NullInt64) ([]*AIndexComposite, error) { 20 | // query 21 | const sqlstr = `SELECT ` + 22 | `a_key1, a_key2 ` + 23 | `FROM a_bit_of_everything.a_index_composite ` + 24 | `WHERE a_key1 = ? AND a_key2 = ?` 25 | // run 26 | logf(sqlstr, aKey1, aKey2) 27 | rows, err := db.QueryContext(ctx, sqlstr, aKey1, aKey2) 28 | if err != nil { 29 | return nil, logerror(err) 30 | } 31 | defer rows.Close() 32 | // process 33 | var res []*AIndexComposite 34 | for rows.Next() { 35 | aic := AIndexComposite{} 36 | // scan 37 | if err := rows.Scan(&aic.AKey1, &aic.AKey2); err != nil { 38 | return nil, logerror(err) 39 | } 40 | res = append(res, &aic) 41 | } 42 | if err := rows.Err(); err != nil { 43 | return nil, logerror(err) 44 | } 45 | return res, nil 46 | } 47 | -------------------------------------------------------------------------------- /_examples/a_bit_of_everything/mysql/amanualtable.dbtpl.go: -------------------------------------------------------------------------------- 1 | package mysql 2 | 3 | // Code generated by dbtpl. DO NOT EDIT. 4 | 5 | import ( 6 | "database/sql" 7 | ) 8 | 9 | // AManualTable represents a row from 'a_bit_of_everything.a_manual_table'. 10 | type AManualTable struct { 11 | AText sql.NullString `json:"a_text"` // a_text 12 | } 13 | -------------------------------------------------------------------------------- /_examples/a_bit_of_everything/mysql/aprimary.dbtpl.go: -------------------------------------------------------------------------------- 1 | package mysql 2 | 3 | // Code generated by dbtpl. DO NOT EDIT. 4 | 5 | import ( 6 | "context" 7 | ) 8 | 9 | // APrimary represents a row from 'a_bit_of_everything.a_primary'. 10 | type APrimary struct { 11 | AKey int `json:"a_key"` // a_key 12 | // xo fields 13 | _exists, _deleted bool 14 | } 15 | 16 | // Exists returns true when the [APrimary] exists in the database. 17 | func (ap *APrimary) Exists() bool { 18 | return ap._exists 19 | } 20 | 21 | // Deleted returns true when the [APrimary] has been marked for deletion 22 | // from the database. 23 | func (ap *APrimary) Deleted() bool { 24 | return ap._deleted 25 | } 26 | 27 | // Insert inserts the [APrimary] to the database. 28 | func (ap *APrimary) Insert(ctx context.Context, db DB) error { 29 | switch { 30 | case ap._exists: // already exists 31 | return logerror(&ErrInsertFailed{ErrAlreadyExists}) 32 | case ap._deleted: // deleted 33 | return logerror(&ErrInsertFailed{ErrMarkedForDeletion}) 34 | } 35 | // insert (manual) 36 | const sqlstr = `INSERT INTO a_bit_of_everything.a_primary (` + 37 | `a_key` + 38 | `) VALUES (` + 39 | `?` + 40 | `)` 41 | // run 42 | logf(sqlstr, ap.AKey) 43 | if _, err := db.ExecContext(ctx, sqlstr, ap.AKey); err != nil { 44 | return logerror(err) 45 | } 46 | // set exists 47 | ap._exists = true 48 | return nil 49 | } 50 | 51 | // ------ NOTE: Update statements omitted due to lack of fields other than primary key ------ 52 | 53 | // Delete deletes the [APrimary] from the database. 54 | func (ap *APrimary) Delete(ctx context.Context, db DB) error { 55 | switch { 56 | case !ap._exists: // doesn't exist 57 | return nil 58 | case ap._deleted: // deleted 59 | return nil 60 | } 61 | // delete with single primary key 62 | const sqlstr = `DELETE FROM a_bit_of_everything.a_primary ` + 63 | `WHERE a_key = ?` 64 | // run 65 | logf(sqlstr, ap.AKey) 66 | if _, err := db.ExecContext(ctx, sqlstr, ap.AKey); err != nil { 67 | return logerror(err) 68 | } 69 | // set deleted 70 | ap._deleted = true 71 | return nil 72 | } 73 | 74 | // APrimaryByAKey retrieves a row from 'a_bit_of_everything.a_primary' as a [APrimary]. 75 | // 76 | // Generated from index 'a_primary_a_key_pkey'. 77 | func APrimaryByAKey(ctx context.Context, db DB, aKey int) (*APrimary, error) { 78 | // query 79 | const sqlstr = `SELECT ` + 80 | `a_key ` + 81 | `FROM a_bit_of_everything.a_primary ` + 82 | `WHERE a_key = ?` 83 | // run 84 | logf(sqlstr, aKey) 85 | ap := APrimary{ 86 | _exists: true, 87 | } 88 | if err := db.QueryRowContext(ctx, sqlstr, aKey).Scan(&ap.AKey); err != nil { 89 | return nil, logerror(err) 90 | } 91 | return &ap, nil 92 | } 93 | -------------------------------------------------------------------------------- /_examples/a_bit_of_everything/mysql/auniqueindex.dbtpl.go: -------------------------------------------------------------------------------- 1 | package mysql 2 | 3 | // Code generated by dbtpl. DO NOT EDIT. 4 | 5 | import ( 6 | "context" 7 | "database/sql" 8 | ) 9 | 10 | // AUniqueIndex represents a row from 'a_bit_of_everything.a_unique_index'. 11 | type AUniqueIndex struct { 12 | AKey sql.NullInt64 `json:"a_key"` // a_key 13 | } 14 | 15 | // AUniqueIndexByAKey retrieves a row from 'a_bit_of_everything.a_unique_index' as a [AUniqueIndex]. 16 | // 17 | // Generated from index 'a_key'. 18 | func AUniqueIndexByAKey(ctx context.Context, db DB, aKey sql.NullInt64) (*AUniqueIndex, error) { 19 | // query 20 | const sqlstr = `SELECT ` + 21 | `a_key ` + 22 | `FROM a_bit_of_everything.a_unique_index ` + 23 | `WHERE a_key = ?` 24 | // run 25 | logf(sqlstr, aKey) 26 | aui := AUniqueIndex{} 27 | if err := db.QueryRowContext(ctx, sqlstr, aKey).Scan(&aui.AKey); err != nil { 28 | return nil, logerror(err) 29 | } 30 | return &aui, nil 31 | } 32 | -------------------------------------------------------------------------------- /_examples/a_bit_of_everything/mysql/auniqueindexcomposite.dbtpl.go: -------------------------------------------------------------------------------- 1 | package mysql 2 | 3 | // Code generated by dbtpl. DO NOT EDIT. 4 | 5 | import ( 6 | "context" 7 | "database/sql" 8 | ) 9 | 10 | // AUniqueIndexComposite represents a row from 'a_bit_of_everything.a_unique_index_composite'. 11 | type AUniqueIndexComposite struct { 12 | AKey1 sql.NullInt64 `json:"a_key1"` // a_key1 13 | AKey2 sql.NullInt64 `json:"a_key2"` // a_key2 14 | } 15 | 16 | // AUniqueIndexCompositeByAKey1AKey2 retrieves a row from 'a_bit_of_everything.a_unique_index_composite' as a [AUniqueIndexComposite]. 17 | // 18 | // Generated from index 'a_key1'. 19 | func AUniqueIndexCompositeByAKey1AKey2(ctx context.Context, db DB, aKey1, aKey2 sql.NullInt64) (*AUniqueIndexComposite, error) { 20 | // query 21 | const sqlstr = `SELECT ` + 22 | `a_key1, a_key2 ` + 23 | `FROM a_bit_of_everything.a_unique_index_composite ` + 24 | `WHERE a_key1 = ? AND a_key2 = ?` 25 | // run 26 | logf(sqlstr, aKey1, aKey2) 27 | auic := AUniqueIndexComposite{} 28 | if err := db.QueryRowContext(ctx, sqlstr, aKey1, aKey2).Scan(&auic.AKey1, &auic.AKey2); err != nil { 29 | return nil, logerror(err) 30 | } 31 | return &auic, nil 32 | } 33 | -------------------------------------------------------------------------------- /_examples/a_bit_of_everything/mysql/aviewofeverythingsome.dbtpl.go: -------------------------------------------------------------------------------- 1 | package mysql 2 | 3 | // Code generated by dbtpl. DO NOT EDIT. 4 | 5 | // select `a_bit_of_everything`.`a_bit_of_everything`.`a_bool` AS `a_bool`,`a_bit_of_everything`.`a_bit_of_everything`.`a_text` AS `a_text` from `a_bit_of_everything`.`a_bit_of_everything` 6 | type AViewOfEverythingSome struct { 7 | ABool bool `json:"a_bool"` // a_bool 8 | AText string `json:"a_text"` // a_text 9 | } 10 | -------------------------------------------------------------------------------- /_examples/a_bit_of_everything/mysql/sf_afunc0in.dbtpl.go: -------------------------------------------------------------------------------- 1 | package mysql 2 | 3 | // Code generated by dbtpl. DO NOT EDIT. 4 | 5 | import ( 6 | "context" 7 | ) 8 | 9 | // AFunc0In calls the stored function 'a_bit_of_everything.a_func_0_in() int' on db. 10 | func AFunc0In(ctx context.Context, db DB) (int, error) { 11 | // call a_bit_of_everything.a_func_0_in 12 | const sqlstr = `SELECT a_bit_of_everything.a_func_0_in()` 13 | // run 14 | var r0 int 15 | logf(sqlstr) 16 | if err := db.QueryRowContext(ctx, sqlstr).Scan(&r0); err != nil { 17 | return 0, logerror(err) 18 | } 19 | return r0, nil 20 | } 21 | -------------------------------------------------------------------------------- /_examples/a_bit_of_everything/mysql/sf_afunc1in.dbtpl.go: -------------------------------------------------------------------------------- 1 | package mysql 2 | 3 | // Code generated by dbtpl. DO NOT EDIT. 4 | 5 | import ( 6 | "context" 7 | ) 8 | 9 | // AFunc1In calls the stored function 'a_bit_of_everything.a_func_1_in(int) int' on db. 10 | func AFunc1In(ctx context.Context, db DB, aParam int) (int, error) { 11 | // call a_bit_of_everything.a_func_1_in 12 | const sqlstr = `SELECT a_bit_of_everything.a_func_1_in(?)` 13 | // run 14 | var r0 int 15 | logf(sqlstr, aParam) 16 | if err := db.QueryRowContext(ctx, sqlstr, aParam).Scan(&r0); err != nil { 17 | return 0, logerror(err) 18 | } 19 | return r0, nil 20 | } 21 | -------------------------------------------------------------------------------- /_examples/a_bit_of_everything/mysql/sf_afunc2in.dbtpl.go: -------------------------------------------------------------------------------- 1 | package mysql 2 | 3 | // Code generated by dbtpl. DO NOT EDIT. 4 | 5 | import ( 6 | "context" 7 | ) 8 | 9 | // AFunc2In calls the stored function 'a_bit_of_everything.a_func_2_in(int, int) int' on db. 10 | func AFunc2In(ctx context.Context, db DB, paramOne, paramTwo int) (int, error) { 11 | // call a_bit_of_everything.a_func_2_in 12 | const sqlstr = `SELECT a_bit_of_everything.a_func_2_in(?, ?)` 13 | // run 14 | var r0 int 15 | logf(sqlstr, paramOne, paramTwo) 16 | if err := db.QueryRowContext(ctx, sqlstr, paramOne, paramTwo).Scan(&r0); err != nil { 17 | return 0, logerror(err) 18 | } 19 | return r0, nil 20 | } 21 | -------------------------------------------------------------------------------- /_examples/a_bit_of_everything/mysql/sp_a0in0out.dbtpl.go: -------------------------------------------------------------------------------- 1 | package mysql 2 | 3 | // Code generated by dbtpl. DO NOT EDIT. 4 | 5 | import ( 6 | "context" 7 | ) 8 | 9 | // A0In0Out calls the stored procedure 'a_bit_of_everything.a_0_in_0_out()' on db. 10 | func A0In0Out(ctx context.Context, db DB) error { 11 | // call a_bit_of_everything.a_0_in_0_out 12 | const sqlstr = `CALL a_bit_of_everything.a_0_in_0_out()` 13 | // run 14 | logf(sqlstr) 15 | if _, err := db.ExecContext(ctx, sqlstr); err != nil { 16 | return logerror(err) 17 | } 18 | return nil 19 | } 20 | -------------------------------------------------------------------------------- /_examples/a_bit_of_everything/mysql/sp_a0in1out.dbtpl.go: -------------------------------------------------------------------------------- 1 | package mysql 2 | 3 | // Code generated by dbtpl. DO NOT EDIT. 4 | 5 | import ( 6 | "context" 7 | "fmt" 8 | ) 9 | 10 | // A0In1Out calls the stored procedure 'a_bit_of_everything.a_0_in_1_out() int' on db. 11 | func A0In1Out(ctx context.Context, db DB) (int, error) { 12 | // At the moment, the Go MySQL driver does not support stored procedures 13 | // with out parameters 14 | return 0, fmt.Errorf("unsupported") 15 | } 16 | -------------------------------------------------------------------------------- /_examples/a_bit_of_everything/mysql/sp_a1in0out.dbtpl.go: -------------------------------------------------------------------------------- 1 | package mysql 2 | 3 | // Code generated by dbtpl. DO NOT EDIT. 4 | 5 | import ( 6 | "context" 7 | ) 8 | 9 | // A1In0Out calls the stored procedure 'a_bit_of_everything.a_1_in_0_out(int)' on db. 10 | func A1In0Out(ctx context.Context, db DB, aParam int) error { 11 | // call a_bit_of_everything.a_1_in_0_out 12 | const sqlstr = `CALL a_bit_of_everything.a_1_in_0_out(?)` 13 | // run 14 | logf(sqlstr) 15 | if _, err := db.ExecContext(ctx, sqlstr, aParam); err != nil { 16 | return logerror(err) 17 | } 18 | return nil 19 | } 20 | -------------------------------------------------------------------------------- /_examples/a_bit_of_everything/mysql/sp_a1in1out.dbtpl.go: -------------------------------------------------------------------------------- 1 | package mysql 2 | 3 | // Code generated by dbtpl. DO NOT EDIT. 4 | 5 | import ( 6 | "context" 7 | "fmt" 8 | ) 9 | 10 | // A1In1Out calls the stored procedure 'a_bit_of_everything.a_1_in_1_out(int) int' on db. 11 | func A1In1Out(ctx context.Context, db DB, aParam int) (int, error) { 12 | // At the moment, the Go MySQL driver does not support stored procedures 13 | // with out parameters 14 | return 0, fmt.Errorf("unsupported") 15 | } 16 | -------------------------------------------------------------------------------- /_examples/a_bit_of_everything/mysql/sp_a2in2out.dbtpl.go: -------------------------------------------------------------------------------- 1 | package mysql 2 | 3 | // Code generated by dbtpl. DO NOT EDIT. 4 | 5 | import ( 6 | "context" 7 | "fmt" 8 | ) 9 | 10 | // A2In2Out calls the stored procedure 'a_bit_of_everything.a_2_in_2_out(int, int) (int, int)' on db. 11 | func A2In2Out(ctx context.Context, db DB, paramOne, paramTwo int) (int, int, error) { 12 | // At the moment, the Go MySQL driver does not support stored procedures 13 | // with out parameters 14 | return 0, 0, fmt.Errorf("unsupported") 15 | } 16 | -------------------------------------------------------------------------------- /_examples/a_bit_of_everything/oracle.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "context" 5 | "database/sql" 6 | "fmt" 7 | 8 | models "github.com/xo/dbtpl/_examples/a_bit_of_everything/oracle" 9 | ) 10 | 11 | func runOracle(ctx context.Context, db *sql.DB) error { 12 | var res1, res2 int64 13 | var err error 14 | // run procs 15 | if err := models.A0In0Out(ctx, db); err != nil { 16 | return fmt.Errorf("0 in 0 out: %v", err) 17 | } 18 | // 1 in 0 out 19 | if err := models.A1In0Out(ctx, db, 10); err != nil { 20 | return fmt.Errorf("1 in 0 out: %v", err) 21 | } 22 | // 0 in 1 out 23 | if res1, err = models.A0In1Out(ctx, db); err != nil { 24 | return fmt.Errorf("0 in 1 out: %v", err) 25 | } 26 | fmt.Printf("a_0_in_1_out(): %d\n", res1) 27 | // 1 in 1 out 28 | if res1, err = models.A1In1Out(ctx, db, 10); err != nil { 29 | return fmt.Errorf("1 in 0 out: %v", err) 30 | } 31 | fmt.Printf("a_1_in_1_out(%d): %d\n", 10, res1) 32 | // 2 in 2 out 33 | if res1, res2, err = models.A2In2Out(ctx, db, 10, 20); err != nil { 34 | return fmt.Errorf("2 in 2 out: %v", err) 35 | } 36 | fmt.Printf("a_2_in_2_out(%d, %d): %d, %d\n", 10, 20, res1, res2) 37 | // run funcs 38 | // 0 in 39 | if res1, err = models.AFunc0In(ctx, db); err != nil { 40 | return fmt.Errorf("a func 0 in: %v", err) 41 | } 42 | fmt.Printf("a_func_0_in(): %d\n", res1) 43 | // 1 in 44 | if res1, err = models.AFunc1In(ctx, db, 10); err != nil { 45 | return fmt.Errorf("a func 1 in: %v", err) 46 | } 47 | fmt.Printf("a_func_1_in(%d): %d\n", 10, res1) 48 | // 1 in 49 | if res1, err = models.AFunc2In(ctx, db, 10, 20); err != nil { 50 | return fmt.Errorf("a func 2 in: %v", err) 51 | } 52 | fmt.Printf("a_func_2_in(%d, %d): %d\n", 10, 20, res1) 53 | return nil 54 | } 55 | -------------------------------------------------------------------------------- /_examples/a_bit_of_everything/oracle/aforeignkey.dbtpl.go: -------------------------------------------------------------------------------- 1 | package oracle 2 | 3 | // Code generated by dbtpl. DO NOT EDIT. 4 | 5 | import ( 6 | "context" 7 | "database/sql" 8 | ) 9 | 10 | // AForeignKey represents a row from 'a_bit_of_everything.a_foreign_key'. 11 | type AForeignKey struct { 12 | AKey sql.NullInt64 `json:"a_key"` // a_key 13 | } 14 | 15 | // APrimary returns the APrimary associated with the [AForeignKey]'s (AKey). 16 | // 17 | // Generated from foreign key 'a_key_fkey'. 18 | func (afk *AForeignKey) APrimary(ctx context.Context, db DB) (*APrimary, error) { 19 | return APrimaryByAKey(ctx, db, int(afk.AKey.Int64)) 20 | } 21 | -------------------------------------------------------------------------------- /_examples/a_bit_of_everything/oracle/aforeignkeycomposite.dbtpl.go: -------------------------------------------------------------------------------- 1 | package oracle 2 | 3 | // Code generated by dbtpl. DO NOT EDIT. 4 | 5 | import ( 6 | "context" 7 | "database/sql" 8 | ) 9 | 10 | // AForeignKeyComposite represents a row from 'a_bit_of_everything.a_foreign_key_composite'. 11 | type AForeignKeyComposite struct { 12 | AKey1 sql.NullInt64 `json:"a_key1"` // a_key1 13 | AKey2 sql.NullInt64 `json:"a_key2"` // a_key2 14 | } 15 | 16 | // APrimaryComposite returns the APrimaryComposite associated with the [AForeignKeyComposite]'s (AKey1, AKey2). 17 | // 18 | // Generated from foreign key 'a_foreign_key_composite_fkey'. 19 | func (afkc *AForeignKeyComposite) APrimaryComposite(ctx context.Context, db DB) (*APrimaryComposite, error) { 20 | return APrimaryCompositeByAKey1AKey2(ctx, db, int(afkc.AKey1.Int64), int(afkc.AKey2.Int64)) 21 | } 22 | -------------------------------------------------------------------------------- /_examples/a_bit_of_everything/oracle/aindex.dbtpl.go: -------------------------------------------------------------------------------- 1 | package oracle 2 | 3 | // Code generated by dbtpl. DO NOT EDIT. 4 | 5 | import ( 6 | "context" 7 | "database/sql" 8 | ) 9 | 10 | // AIndex represents a row from 'a_bit_of_everything.a_index'. 11 | type AIndex struct { 12 | AKey sql.NullInt64 `json:"a_key"` // a_key 13 | } 14 | 15 | // AIndexByAKey retrieves a row from 'a_bit_of_everything.a_index' as a [AIndex]. 16 | // 17 | // Generated from index 'a_index_idx'. 18 | func AIndexByAKey(ctx context.Context, db DB, aKey sql.NullInt64) ([]*AIndex, error) { 19 | // query 20 | const sqlstr = `SELECT ` + 21 | `a_key ` + 22 | `FROM a_bit_of_everything.a_index ` + 23 | `WHERE a_key = :1` 24 | // run 25 | logf(sqlstr, aKey) 26 | rows, err := db.QueryContext(ctx, sqlstr, aKey) 27 | if err != nil { 28 | return nil, logerror(err) 29 | } 30 | defer rows.Close() 31 | // process 32 | var res []*AIndex 33 | for rows.Next() { 34 | ai := AIndex{} 35 | // scan 36 | if err := rows.Scan(&ai.AKey); err != nil { 37 | return nil, logerror(err) 38 | } 39 | res = append(res, &ai) 40 | } 41 | if err := rows.Err(); err != nil { 42 | return nil, logerror(err) 43 | } 44 | return res, nil 45 | } 46 | -------------------------------------------------------------------------------- /_examples/a_bit_of_everything/oracle/aindexcomposite.dbtpl.go: -------------------------------------------------------------------------------- 1 | package oracle 2 | 3 | // Code generated by dbtpl. DO NOT EDIT. 4 | 5 | import ( 6 | "context" 7 | "database/sql" 8 | ) 9 | 10 | // AIndexComposite represents a row from 'a_bit_of_everything.a_index_composite'. 11 | type AIndexComposite struct { 12 | AKey1 sql.NullInt64 `json:"a_key1"` // a_key1 13 | AKey2 sql.NullInt64 `json:"a_key2"` // a_key2 14 | } 15 | 16 | // AIndexCompositeByAKey1AKey2 retrieves a row from 'a_bit_of_everything.a_index_composite' as a [AIndexComposite]. 17 | // 18 | // Generated from index 'a_index_composite_idx'. 19 | func AIndexCompositeByAKey1AKey2(ctx context.Context, db DB, aKey1, aKey2 sql.NullInt64) ([]*AIndexComposite, error) { 20 | // query 21 | const sqlstr = `SELECT ` + 22 | `a_key1, a_key2 ` + 23 | `FROM a_bit_of_everything.a_index_composite ` + 24 | `WHERE a_key1 = :1 AND a_key2 = :2` 25 | // run 26 | logf(sqlstr, aKey1, aKey2) 27 | rows, err := db.QueryContext(ctx, sqlstr, aKey1, aKey2) 28 | if err != nil { 29 | return nil, logerror(err) 30 | } 31 | defer rows.Close() 32 | // process 33 | var res []*AIndexComposite 34 | for rows.Next() { 35 | aic := AIndexComposite{} 36 | // scan 37 | if err := rows.Scan(&aic.AKey1, &aic.AKey2); err != nil { 38 | return nil, logerror(err) 39 | } 40 | res = append(res, &aic) 41 | } 42 | if err := rows.Err(); err != nil { 43 | return nil, logerror(err) 44 | } 45 | return res, nil 46 | } 47 | -------------------------------------------------------------------------------- /_examples/a_bit_of_everything/oracle/amanualtable.dbtpl.go: -------------------------------------------------------------------------------- 1 | package oracle 2 | 3 | // Code generated by dbtpl. DO NOT EDIT. 4 | 5 | import ( 6 | "database/sql" 7 | ) 8 | 9 | // AManualTable represents a row from 'a_bit_of_everything.a_manual_table'. 10 | type AManualTable struct { 11 | AText sql.NullString `json:"a_text"` // a_text 12 | } 13 | -------------------------------------------------------------------------------- /_examples/a_bit_of_everything/oracle/aprimary.dbtpl.go: -------------------------------------------------------------------------------- 1 | package oracle 2 | 3 | // Code generated by dbtpl. DO NOT EDIT. 4 | 5 | import ( 6 | "context" 7 | ) 8 | 9 | // APrimary represents a row from 'a_bit_of_everything.a_primary'. 10 | type APrimary struct { 11 | AKey int `json:"a_key"` // a_key 12 | // xo fields 13 | _exists, _deleted bool 14 | } 15 | 16 | // Exists returns true when the [APrimary] exists in the database. 17 | func (ap *APrimary) Exists() bool { 18 | return ap._exists 19 | } 20 | 21 | // Deleted returns true when the [APrimary] has been marked for deletion 22 | // from the database. 23 | func (ap *APrimary) Deleted() bool { 24 | return ap._deleted 25 | } 26 | 27 | // Insert inserts the [APrimary] to the database. 28 | func (ap *APrimary) Insert(ctx context.Context, db DB) error { 29 | switch { 30 | case ap._exists: // already exists 31 | return logerror(&ErrInsertFailed{ErrAlreadyExists}) 32 | case ap._deleted: // deleted 33 | return logerror(&ErrInsertFailed{ErrMarkedForDeletion}) 34 | } 35 | // insert (manual) 36 | const sqlstr = `INSERT INTO a_bit_of_everything.a_primary (` + 37 | `a_key` + 38 | `) VALUES (` + 39 | `:1` + 40 | `)` 41 | // run 42 | logf(sqlstr, ap.AKey) 43 | if _, err := db.ExecContext(ctx, sqlstr, ap.AKey); err != nil { 44 | return logerror(err) 45 | } 46 | // set exists 47 | ap._exists = true 48 | return nil 49 | } 50 | 51 | // ------ NOTE: Update statements omitted due to lack of fields other than primary key ------ 52 | 53 | // Delete deletes the [APrimary] from the database. 54 | func (ap *APrimary) Delete(ctx context.Context, db DB) error { 55 | switch { 56 | case !ap._exists: // doesn't exist 57 | return nil 58 | case ap._deleted: // deleted 59 | return nil 60 | } 61 | // delete with single primary key 62 | const sqlstr = `DELETE FROM a_bit_of_everything.a_primary ` + 63 | `WHERE a_key = :1` 64 | // run 65 | logf(sqlstr, ap.AKey) 66 | if _, err := db.ExecContext(ctx, sqlstr, ap.AKey); err != nil { 67 | return logerror(err) 68 | } 69 | // set deleted 70 | ap._deleted = true 71 | return nil 72 | } 73 | 74 | // APrimaryByAKey retrieves a row from 'a_bit_of_everything.a_primary' as a [APrimary]. 75 | // 76 | // Generated from index 'a_primary_pkey'. 77 | func APrimaryByAKey(ctx context.Context, db DB, aKey int) (*APrimary, error) { 78 | // query 79 | const sqlstr = `SELECT ` + 80 | `a_key ` + 81 | `FROM a_bit_of_everything.a_primary ` + 82 | `WHERE a_key = :1` 83 | // run 84 | logf(sqlstr, aKey) 85 | ap := APrimary{ 86 | _exists: true, 87 | } 88 | if err := db.QueryRowContext(ctx, sqlstr, aKey).Scan(&ap.AKey); err != nil { 89 | return nil, logerror(err) 90 | } 91 | return &ap, nil 92 | } 93 | -------------------------------------------------------------------------------- /_examples/a_bit_of_everything/oracle/asequence.dbtpl.go: -------------------------------------------------------------------------------- 1 | package oracle 2 | 3 | // Code generated by dbtpl. DO NOT EDIT. 4 | 5 | import ( 6 | "context" 7 | "database/sql" 8 | ) 9 | 10 | // ASequence represents a row from 'a_bit_of_everything.a_sequence'. 11 | type ASequence struct { 12 | ASeq int `json:"a_seq"` // a_seq 13 | // xo fields 14 | _exists, _deleted bool 15 | } 16 | 17 | // Exists returns true when the [ASequence] exists in the database. 18 | func (as *ASequence) Exists() bool { 19 | return as._exists 20 | } 21 | 22 | // Deleted returns true when the [ASequence] has been marked for deletion 23 | // from the database. 24 | func (as *ASequence) Deleted() bool { 25 | return as._deleted 26 | } 27 | 28 | // Insert inserts the [ASequence] to the database. 29 | func (as *ASequence) Insert(ctx context.Context, db DB) error { 30 | switch { 31 | case as._exists: // already exists 32 | return logerror(&ErrInsertFailed{ErrAlreadyExists}) 33 | case as._deleted: // deleted 34 | return logerror(&ErrInsertFailed{ErrMarkedForDeletion}) 35 | } 36 | // insert (primary key generated and returned by database) 37 | const sqlstr = `INSERT INTO a_bit_of_everything.a_sequence (` + 38 | `` + 39 | `) VALUES (` + 40 | `` + 41 | `) RETURNING a_seq INTO :1` 42 | // run 43 | logf(sqlstr) 44 | var id int64 45 | if _, err := db.ExecContext(ctx, sqlstr, sql.Out{Dest: &id}); err != nil { 46 | return logerror(err) 47 | } // set primary key 48 | as.ASeq = int(id) 49 | // set exists 50 | as._exists = true 51 | return nil 52 | } 53 | 54 | // ------ NOTE: Update statements omitted due to lack of fields other than primary key ------ 55 | 56 | // Delete deletes the [ASequence] from the database. 57 | func (as *ASequence) Delete(ctx context.Context, db DB) error { 58 | switch { 59 | case !as._exists: // doesn't exist 60 | return nil 61 | case as._deleted: // deleted 62 | return nil 63 | } 64 | // delete with single primary key 65 | const sqlstr = `DELETE FROM a_bit_of_everything.a_sequence ` + 66 | `WHERE a_seq = :1` 67 | // run 68 | logf(sqlstr, as.ASeq) 69 | if _, err := db.ExecContext(ctx, sqlstr, as.ASeq); err != nil { 70 | return logerror(err) 71 | } 72 | // set deleted 73 | as._deleted = true 74 | return nil 75 | } 76 | 77 | // ASequenceByASeq retrieves a row from 'a_bit_of_everything.a_sequence' as a [ASequence]. 78 | // 79 | // Generated from index 'a_sequence_pkey'. 80 | func ASequenceByASeq(ctx context.Context, db DB, aSeq int) (*ASequence, error) { 81 | // query 82 | const sqlstr = `SELECT ` + 83 | `a_seq ` + 84 | `FROM a_bit_of_everything.a_sequence ` + 85 | `WHERE a_seq = :1` 86 | // run 87 | logf(sqlstr, aSeq) 88 | as := ASequence{ 89 | _exists: true, 90 | } 91 | if err := db.QueryRowContext(ctx, sqlstr, aSeq).Scan(&as.ASeq); err != nil { 92 | return nil, logerror(err) 93 | } 94 | return &as, nil 95 | } 96 | -------------------------------------------------------------------------------- /_examples/a_bit_of_everything/oracle/auniqueindex.dbtpl.go: -------------------------------------------------------------------------------- 1 | package oracle 2 | 3 | // Code generated by dbtpl. DO NOT EDIT. 4 | 5 | import ( 6 | "context" 7 | "database/sql" 8 | ) 9 | 10 | // AUniqueIndex represents a row from 'a_bit_of_everything.a_unique_index'. 11 | type AUniqueIndex struct { 12 | AKey sql.NullInt64 `json:"a_key"` // a_key 13 | } 14 | 15 | // AUniqueIndexByAKey retrieves a row from 'a_bit_of_everything.a_unique_index' as a [AUniqueIndex]. 16 | // 17 | // Generated from index 'a_unique_index_idx'. 18 | func AUniqueIndexByAKey(ctx context.Context, db DB, aKey sql.NullInt64) (*AUniqueIndex, error) { 19 | // query 20 | const sqlstr = `SELECT ` + 21 | `a_key ` + 22 | `FROM a_bit_of_everything.a_unique_index ` + 23 | `WHERE a_key = :1` 24 | // run 25 | logf(sqlstr, aKey) 26 | aui := AUniqueIndex{} 27 | if err := db.QueryRowContext(ctx, sqlstr, aKey).Scan(&aui.AKey); err != nil { 28 | return nil, logerror(err) 29 | } 30 | return &aui, nil 31 | } 32 | -------------------------------------------------------------------------------- /_examples/a_bit_of_everything/oracle/auniqueindexcomposite.dbtpl.go: -------------------------------------------------------------------------------- 1 | package oracle 2 | 3 | // Code generated by dbtpl. DO NOT EDIT. 4 | 5 | import ( 6 | "context" 7 | "database/sql" 8 | ) 9 | 10 | // AUniqueIndexComposite represents a row from 'a_bit_of_everything.a_unique_index_composite'. 11 | type AUniqueIndexComposite struct { 12 | AKey1 sql.NullInt64 `json:"a_key1"` // a_key1 13 | AKey2 sql.NullInt64 `json:"a_key2"` // a_key2 14 | } 15 | 16 | // AUniqueIndexCompositeByAKey1AKey2 retrieves a row from 'a_bit_of_everything.a_unique_index_composite' as a [AUniqueIndexComposite]. 17 | // 18 | // Generated from index 'a_unique_index_composite_idx'. 19 | func AUniqueIndexCompositeByAKey1AKey2(ctx context.Context, db DB, aKey1, aKey2 sql.NullInt64) (*AUniqueIndexComposite, error) { 20 | // query 21 | const sqlstr = `SELECT ` + 22 | `a_key1, a_key2 ` + 23 | `FROM a_bit_of_everything.a_unique_index_composite ` + 24 | `WHERE a_key1 = :1 AND a_key2 = :2` 25 | // run 26 | logf(sqlstr, aKey1, aKey2) 27 | auic := AUniqueIndexComposite{} 28 | if err := db.QueryRowContext(ctx, sqlstr, aKey1, aKey2).Scan(&auic.AKey1, &auic.AKey2); err != nil { 29 | return nil, logerror(err) 30 | } 31 | return &auic, nil 32 | } 33 | -------------------------------------------------------------------------------- /_examples/a_bit_of_everything/oracle/aviewofeverythingsome.dbtpl.go: -------------------------------------------------------------------------------- 1 | package oracle 2 | 3 | // Code generated by dbtpl. DO NOT EDIT. 4 | 5 | // SELECT a_bool, a_nclob FROM a_bit_of_everything 6 | type AViewOfEverythingSome struct { 7 | ABool bool `json:"a_bool"` // a_bool 8 | ANclob string `json:"a_nclob"` // a_nclob 9 | } 10 | -------------------------------------------------------------------------------- /_examples/a_bit_of_everything/oracle/sf_afunc0in.dbtpl.go: -------------------------------------------------------------------------------- 1 | package oracle 2 | 3 | // Code generated by dbtpl. DO NOT EDIT. 4 | 5 | import ( 6 | "context" 7 | ) 8 | 9 | // AFunc0In calls the stored function 'a_bit_of_everything.a_func_0_in() number' on db. 10 | func AFunc0In(ctx context.Context, db DB) (int64, error) { 11 | // call a_bit_of_everything.a_func_0_in 12 | const sqlstr = `SELECT a_bit_of_everything.a_func_0_in() FROM dual` 13 | // run 14 | var r0 int64 15 | logf(sqlstr) 16 | if err := db.QueryRowContext(ctx, sqlstr).Scan(&r0); err != nil { 17 | return 0, logerror(err) 18 | } 19 | return r0, nil 20 | } 21 | -------------------------------------------------------------------------------- /_examples/a_bit_of_everything/oracle/sf_afunc1in.dbtpl.go: -------------------------------------------------------------------------------- 1 | package oracle 2 | 3 | // Code generated by dbtpl. DO NOT EDIT. 4 | 5 | import ( 6 | "context" 7 | ) 8 | 9 | // AFunc1In calls the stored function 'a_bit_of_everything.a_func_1_in(number) number' on db. 10 | func AFunc1In(ctx context.Context, db DB, aParam int64) (int64, error) { 11 | // call a_bit_of_everything.a_func_1_in 12 | const sqlstr = `SELECT a_bit_of_everything.a_func_1_in(:1) FROM dual` 13 | // run 14 | var r0 int64 15 | logf(sqlstr, aParam) 16 | if err := db.QueryRowContext(ctx, sqlstr, aParam).Scan(&r0); err != nil { 17 | return 0, logerror(err) 18 | } 19 | return r0, nil 20 | } 21 | -------------------------------------------------------------------------------- /_examples/a_bit_of_everything/oracle/sf_afunc2in.dbtpl.go: -------------------------------------------------------------------------------- 1 | package oracle 2 | 3 | // Code generated by dbtpl. DO NOT EDIT. 4 | 5 | import ( 6 | "context" 7 | ) 8 | 9 | // AFunc2In calls the stored function 'a_bit_of_everything.a_func_2_in(number, number) number' on db. 10 | func AFunc2In(ctx context.Context, db DB, paramOne, paramTwo int64) (int64, error) { 11 | // call a_bit_of_everything.a_func_2_in 12 | const sqlstr = `SELECT a_bit_of_everything.a_func_2_in(:1, :2) FROM dual` 13 | // run 14 | var r0 int64 15 | logf(sqlstr, paramOne, paramTwo) 16 | if err := db.QueryRowContext(ctx, sqlstr, paramOne, paramTwo).Scan(&r0); err != nil { 17 | return 0, logerror(err) 18 | } 19 | return r0, nil 20 | } 21 | -------------------------------------------------------------------------------- /_examples/a_bit_of_everything/oracle/sp_a0in0out.dbtpl.go: -------------------------------------------------------------------------------- 1 | package oracle 2 | 3 | // Code generated by dbtpl. DO NOT EDIT. 4 | 5 | import ( 6 | "context" 7 | ) 8 | 9 | // A0In0Out calls the stored procedure 'a_bit_of_everything.a_0_in_0_out()' on db. 10 | func A0In0Out(ctx context.Context, db DB) error { 11 | // call a_bit_of_everything.a_0_in_0_out 12 | const sqlstr = `BEGIN a_0_in_0_out(); END;` 13 | // run 14 | logf(sqlstr) 15 | if _, err := db.ExecContext(ctx, sqlstr); err != nil { 16 | return logerror(err) 17 | } 18 | return nil 19 | } 20 | -------------------------------------------------------------------------------- /_examples/a_bit_of_everything/oracle/sp_a0in1out.dbtpl.go: -------------------------------------------------------------------------------- 1 | package oracle 2 | 3 | // Code generated by dbtpl. DO NOT EDIT. 4 | 5 | import ( 6 | "context" 7 | "database/sql" 8 | ) 9 | 10 | // A0In1Out calls the stored procedure 'a_bit_of_everything.a_0_in_1_out() number' on db. 11 | func A0In1Out(ctx context.Context, db DB) (int64, error) { 12 | // call a_bit_of_everything.a_0_in_1_out 13 | const sqlstr = `BEGIN a_0_in_1_out(:a_return); END;` 14 | // run 15 | var aReturn int64 16 | logf(sqlstr) 17 | if _, err := db.ExecContext(ctx, sqlstr, sql.Out{Dest: &aReturn}); err != nil { 18 | return 0, logerror(err) 19 | } 20 | return aReturn, nil 21 | } 22 | -------------------------------------------------------------------------------- /_examples/a_bit_of_everything/oracle/sp_a1in0out.dbtpl.go: -------------------------------------------------------------------------------- 1 | package oracle 2 | 3 | // Code generated by dbtpl. DO NOT EDIT. 4 | 5 | import ( 6 | "context" 7 | "database/sql" 8 | ) 9 | 10 | // A1In0Out calls the stored procedure 'a_bit_of_everything.a_1_in_0_out(number)' on db. 11 | func A1In0Out(ctx context.Context, db DB, aParam int64) error { 12 | // call a_bit_of_everything.a_1_in_0_out 13 | const sqlstr = `BEGIN a_1_in_0_out(:a_param); END;` 14 | // run 15 | logf(sqlstr) 16 | if _, err := db.ExecContext(ctx, sqlstr, sql.Named("a_param", aParam)); err != nil { 17 | return logerror(err) 18 | } 19 | return nil 20 | } 21 | -------------------------------------------------------------------------------- /_examples/a_bit_of_everything/oracle/sp_a1in1out.dbtpl.go: -------------------------------------------------------------------------------- 1 | package oracle 2 | 3 | // Code generated by dbtpl. DO NOT EDIT. 4 | 5 | import ( 6 | "context" 7 | "database/sql" 8 | ) 9 | 10 | // A1In1Out calls the stored procedure 'a_bit_of_everything.a_1_in_1_out(number) number' on db. 11 | func A1In1Out(ctx context.Context, db DB, aParam int64) (int64, error) { 12 | // call a_bit_of_everything.a_1_in_1_out 13 | const sqlstr = `BEGIN a_1_in_1_out(:a_param, :a_return); END;` 14 | // run 15 | var aReturn int64 16 | logf(sqlstr, aParam) 17 | if _, err := db.ExecContext(ctx, sqlstr, sql.Named("a_param", aParam), sql.Out{Dest: &aReturn}); err != nil { 18 | return 0, logerror(err) 19 | } 20 | return aReturn, nil 21 | } 22 | -------------------------------------------------------------------------------- /_examples/a_bit_of_everything/oracle/sp_a2in2out.dbtpl.go: -------------------------------------------------------------------------------- 1 | package oracle 2 | 3 | // Code generated by dbtpl. DO NOT EDIT. 4 | 5 | import ( 6 | "context" 7 | "database/sql" 8 | ) 9 | 10 | // A2In2Out calls the stored procedure 'a_bit_of_everything.a_2_in_2_out(number, number) (number, number)' on db. 11 | func A2In2Out(ctx context.Context, db DB, paramOne, paramTwo int64) (int64, int64, error) { 12 | // call a_bit_of_everything.a_2_in_2_out 13 | const sqlstr = `BEGIN a_2_in_2_out(:param_one, :param_two, :return_one, :return_two); END;` 14 | // run 15 | var returnOne int64 16 | var returnTwo int64 17 | logf(sqlstr, paramOne, paramTwo) 18 | if _, err := db.ExecContext(ctx, sqlstr, sql.Named("param_one", paramOne), sql.Named("param_two", paramTwo), sql.Out{Dest: &returnOne}, sql.Out{Dest: &returnTwo}); err != nil { 19 | return 0, 0, logerror(err) 20 | } 21 | return returnOne, returnTwo, nil 22 | } 23 | -------------------------------------------------------------------------------- /_examples/a_bit_of_everything/postgres.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "context" 5 | "database/sql" 6 | "fmt" 7 | 8 | models "github.com/xo/dbtpl/_examples/a_bit_of_everything/postgres" 9 | ) 10 | 11 | func runPostgres(ctx context.Context, db *sql.DB) error { 12 | // run procs 13 | if err := models.A0In0Out(ctx, db); err != nil { 14 | return fmt.Errorf("0 in 0 out: %v", err) 15 | } 16 | // 1 in 0 out 17 | if err := models.A1In0Out(ctx, db, 10); err != nil { 18 | return fmt.Errorf("1 in 0 out: %v", err) 19 | } 20 | var res1, res2 int 21 | var err error 22 | // 0 in 1 out 23 | if res1, err = models.A0In1Out(ctx, db); err != nil { 24 | return fmt.Errorf("0 in 1 out: %v", err) 25 | } 26 | fmt.Printf("a_0_in_1_out(): %d\n", res1) 27 | // 1 in 1 out 28 | if res1, err = models.A1In1Out(ctx, db, 10); err != nil { 29 | return fmt.Errorf("1 in 0 out: %v", err) 30 | } 31 | fmt.Printf("a_1_in_1_out(%d): %d\n", 10, res1) 32 | // 2 in 2 out 33 | if res1, res2, err = models.A2In2Out(ctx, db, 10, 20); err != nil { 34 | return fmt.Errorf("2 in 2 out: %v", err) 35 | } 36 | fmt.Printf("a_2_in_2_out(%d, %d): %d, %d\n", 10, 20, res1, res2) 37 | // run funcs 38 | // 0 in 39 | if res1, err = models.AFunc0In(ctx, db); err != nil { 40 | return fmt.Errorf("a func 0 in: %v", err) 41 | } 42 | fmt.Printf("a_func_0_in(): %d\n", res1) 43 | // 1 in 44 | if res1, err = models.AFunc1In(ctx, db, 10); err != nil { 45 | return fmt.Errorf("a func 1 in: %v", err) 46 | } 47 | fmt.Printf("a_func_1_in(%d): %d\n", 10, res1) 48 | // 1 in 49 | if res1, err = models.AFunc2In(ctx, db, 10, 20); err != nil { 50 | return fmt.Errorf("a func 2 in: %v", err) 51 | } 52 | fmt.Printf("a_func_2_in(%d, %d): %d\n", 10, 20, res1) 53 | return nil 54 | } 55 | -------------------------------------------------------------------------------- /_examples/a_bit_of_everything/postgres/aenum.dbtpl.go: -------------------------------------------------------------------------------- 1 | package postgres 2 | 3 | // Code generated by dbtpl. DO NOT EDIT. 4 | 5 | import ( 6 | "database/sql/driver" 7 | "fmt" 8 | ) 9 | 10 | // AEnum is the 'a_enum' enum type from schema 'public'. 11 | type AEnum uint16 12 | 13 | // AEnum values. 14 | const ( 15 | // AEnumOne is the 'ONE' a_enum. 16 | AEnumOne AEnum = 1 17 | // AEnumTwo is the 'TWO' a_enum. 18 | AEnumTwo AEnum = 2 19 | ) 20 | 21 | // String satisfies the [fmt.Stringer] interface. 22 | func (ae AEnum) String() string { 23 | switch ae { 24 | case AEnumOne: 25 | return "ONE" 26 | case AEnumTwo: 27 | return "TWO" 28 | } 29 | return fmt.Sprintf("AEnum(%d)", ae) 30 | } 31 | 32 | // MarshalText marshals [AEnum] into text. 33 | func (ae AEnum) MarshalText() ([]byte, error) { 34 | return []byte(ae.String()), nil 35 | } 36 | 37 | // UnmarshalText unmarshals [AEnum] from text. 38 | func (ae *AEnum) UnmarshalText(buf []byte) error { 39 | switch str := string(buf); str { 40 | case "ONE": 41 | *ae = AEnumOne 42 | case "TWO": 43 | *ae = AEnumTwo 44 | default: 45 | return ErrInvalidAEnum(str) 46 | } 47 | return nil 48 | } 49 | 50 | // Value satisfies the [driver.Valuer] interface. 51 | func (ae AEnum) Value() (driver.Value, error) { 52 | return ae.String(), nil 53 | } 54 | 55 | // Scan satisfies the [sql.Scanner] interface. 56 | func (ae *AEnum) Scan(v any) error { 57 | switch x := v.(type) { 58 | case []byte: 59 | return ae.UnmarshalText(x) 60 | case string: 61 | return ae.UnmarshalText([]byte(x)) 62 | } 63 | return ErrInvalidAEnum(fmt.Sprintf("%T", v)) 64 | } 65 | 66 | // NullAEnum represents a null 'a_enum' enum for schema 'public'. 67 | type NullAEnum struct { 68 | AEnum AEnum 69 | // Valid is true if [AEnum] is not null. 70 | Valid bool 71 | } 72 | 73 | // Value satisfies the [driver.Valuer] interface. 74 | func (nae NullAEnum) Value() (driver.Value, error) { 75 | if !nae.Valid { 76 | return nil, nil 77 | } 78 | return nae.AEnum.Value() 79 | } 80 | 81 | // Scan satisfies the [sql.Scanner] interface. 82 | func (nae *NullAEnum) Scan(v any) error { 83 | if v == nil { 84 | nae.AEnum, nae.Valid = 0, false 85 | return nil 86 | } 87 | err := nae.AEnum.Scan(v) 88 | nae.Valid = err == nil 89 | return err 90 | } 91 | 92 | // ErrInvalidAEnum is the invalid [AEnum] error. 93 | type ErrInvalidAEnum string 94 | 95 | // Error satisfies the error interface. 96 | func (err ErrInvalidAEnum) Error() string { 97 | return fmt.Sprintf("invalid AEnum(%s)", string(err)) 98 | } 99 | -------------------------------------------------------------------------------- /_examples/a_bit_of_everything/postgres/aforeignkey.dbtpl.go: -------------------------------------------------------------------------------- 1 | package postgres 2 | 3 | // Code generated by dbtpl. DO NOT EDIT. 4 | 5 | import ( 6 | "context" 7 | "database/sql" 8 | ) 9 | 10 | // AForeignKey represents a row from 'public.a_foreign_key'. 11 | type AForeignKey struct { 12 | AKey sql.NullInt64 `json:"a_key"` // a_key 13 | } 14 | 15 | // APrimary returns the APrimary associated with the [AForeignKey]'s (AKey). 16 | // 17 | // Generated from foreign key 'a_foreign_key_a_key_fkey'. 18 | func (afk *AForeignKey) APrimary(ctx context.Context, db DB) (*APrimary, error) { 19 | return APrimaryByAKey(ctx, db, int(afk.AKey.Int64)) 20 | } 21 | -------------------------------------------------------------------------------- /_examples/a_bit_of_everything/postgres/aforeignkeycomposite.dbtpl.go: -------------------------------------------------------------------------------- 1 | package postgres 2 | 3 | // Code generated by dbtpl. DO NOT EDIT. 4 | 5 | import ( 6 | "context" 7 | "database/sql" 8 | ) 9 | 10 | // AForeignKeyComposite represents a row from 'public.a_foreign_key_composite'. 11 | type AForeignKeyComposite struct { 12 | AKey1 sql.NullInt64 `json:"a_key1"` // a_key1 13 | AKey2 sql.NullInt64 `json:"a_key2"` // a_key2 14 | } 15 | 16 | // APrimaryComposite returns the APrimaryComposite associated with the [AForeignKeyComposite]'s (AKey1, AKey2). 17 | // 18 | // Generated from foreign key 'a_foreign_key_composite_a_key1_a_key2_fkey'. 19 | func (afkc *AForeignKeyComposite) APrimaryComposite(ctx context.Context, db DB) (*APrimaryComposite, error) { 20 | return APrimaryCompositeByAKey1AKey2(ctx, db, int(afkc.AKey1.Int64), int(afkc.AKey2.Int64)) 21 | } 22 | -------------------------------------------------------------------------------- /_examples/a_bit_of_everything/postgres/aindex.dbtpl.go: -------------------------------------------------------------------------------- 1 | package postgres 2 | 3 | // Code generated by dbtpl. DO NOT EDIT. 4 | 5 | import ( 6 | "context" 7 | "database/sql" 8 | ) 9 | 10 | // AIndex represents a row from 'public.a_index'. 11 | type AIndex struct { 12 | AKey sql.NullInt64 `json:"a_key"` // a_key 13 | } 14 | 15 | // AIndexByAKey retrieves a row from 'public.a_index' as a [AIndex]. 16 | // 17 | // Generated from index 'a_index_idx'. 18 | func AIndexByAKey(ctx context.Context, db DB, aKey sql.NullInt64) ([]*AIndex, error) { 19 | // query 20 | const sqlstr = `SELECT ` + 21 | `a_key ` + 22 | `FROM public.a_index ` + 23 | `WHERE a_key = $1` 24 | // run 25 | logf(sqlstr, aKey) 26 | rows, err := db.QueryContext(ctx, sqlstr, aKey) 27 | if err != nil { 28 | return nil, logerror(err) 29 | } 30 | defer rows.Close() 31 | // process 32 | var res []*AIndex 33 | for rows.Next() { 34 | ai := AIndex{} 35 | // scan 36 | if err := rows.Scan(&ai.AKey); err != nil { 37 | return nil, logerror(err) 38 | } 39 | res = append(res, &ai) 40 | } 41 | if err := rows.Err(); err != nil { 42 | return nil, logerror(err) 43 | } 44 | return res, nil 45 | } 46 | -------------------------------------------------------------------------------- /_examples/a_bit_of_everything/postgres/aindexcomposite.dbtpl.go: -------------------------------------------------------------------------------- 1 | package postgres 2 | 3 | // Code generated by dbtpl. DO NOT EDIT. 4 | 5 | import ( 6 | "context" 7 | "database/sql" 8 | ) 9 | 10 | // AIndexComposite represents a row from 'public.a_index_composite'. 11 | type AIndexComposite struct { 12 | AKey1 sql.NullInt64 `json:"a_key1"` // a_key1 13 | AKey2 sql.NullInt64 `json:"a_key2"` // a_key2 14 | } 15 | 16 | // AIndexCompositeByAKey1AKey2 retrieves a row from 'public.a_index_composite' as a [AIndexComposite]. 17 | // 18 | // Generated from index 'a_index_composite_idx'. 19 | func AIndexCompositeByAKey1AKey2(ctx context.Context, db DB, aKey1, aKey2 sql.NullInt64) ([]*AIndexComposite, error) { 20 | // query 21 | const sqlstr = `SELECT ` + 22 | `a_key1, a_key2 ` + 23 | `FROM public.a_index_composite ` + 24 | `WHERE a_key1 = $1 AND a_key2 = $2` 25 | // run 26 | logf(sqlstr, aKey1, aKey2) 27 | rows, err := db.QueryContext(ctx, sqlstr, aKey1, aKey2) 28 | if err != nil { 29 | return nil, logerror(err) 30 | } 31 | defer rows.Close() 32 | // process 33 | var res []*AIndexComposite 34 | for rows.Next() { 35 | aic := AIndexComposite{} 36 | // scan 37 | if err := rows.Scan(&aic.AKey1, &aic.AKey2); err != nil { 38 | return nil, logerror(err) 39 | } 40 | res = append(res, &aic) 41 | } 42 | if err := rows.Err(); err != nil { 43 | return nil, logerror(err) 44 | } 45 | return res, nil 46 | } 47 | -------------------------------------------------------------------------------- /_examples/a_bit_of_everything/postgres/amanualtable.dbtpl.go: -------------------------------------------------------------------------------- 1 | package postgres 2 | 3 | // Code generated by dbtpl. DO NOT EDIT. 4 | 5 | import ( 6 | "database/sql" 7 | ) 8 | 9 | // AManualTable represents a row from 'public.a_manual_table'. 10 | type AManualTable struct { 11 | AText sql.NullString `json:"a_text"` // a_text 12 | } 13 | -------------------------------------------------------------------------------- /_examples/a_bit_of_everything/postgres/aprimary.dbtpl.go: -------------------------------------------------------------------------------- 1 | package postgres 2 | 3 | // Code generated by dbtpl. DO NOT EDIT. 4 | 5 | import ( 6 | "context" 7 | ) 8 | 9 | // APrimary represents a row from 'public.a_primary'. 10 | type APrimary struct { 11 | AKey int `json:"a_key"` // a_key 12 | // xo fields 13 | _exists, _deleted bool 14 | } 15 | 16 | // Exists returns true when the [APrimary] exists in the database. 17 | func (ap *APrimary) Exists() bool { 18 | return ap._exists 19 | } 20 | 21 | // Deleted returns true when the [APrimary] has been marked for deletion 22 | // from the database. 23 | func (ap *APrimary) Deleted() bool { 24 | return ap._deleted 25 | } 26 | 27 | // Insert inserts the [APrimary] to the database. 28 | func (ap *APrimary) Insert(ctx context.Context, db DB) error { 29 | switch { 30 | case ap._exists: // already exists 31 | return logerror(&ErrInsertFailed{ErrAlreadyExists}) 32 | case ap._deleted: // deleted 33 | return logerror(&ErrInsertFailed{ErrMarkedForDeletion}) 34 | } 35 | // insert (manual) 36 | const sqlstr = `INSERT INTO public.a_primary (` + 37 | `a_key` + 38 | `) VALUES (` + 39 | `$1` + 40 | `)` 41 | // run 42 | logf(sqlstr, ap.AKey) 43 | if _, err := db.ExecContext(ctx, sqlstr, ap.AKey); err != nil { 44 | return logerror(err) 45 | } 46 | // set exists 47 | ap._exists = true 48 | return nil 49 | } 50 | 51 | // ------ NOTE: Update statements omitted due to lack of fields other than primary key ------ 52 | 53 | // Delete deletes the [APrimary] from the database. 54 | func (ap *APrimary) Delete(ctx context.Context, db DB) error { 55 | switch { 56 | case !ap._exists: // doesn't exist 57 | return nil 58 | case ap._deleted: // deleted 59 | return nil 60 | } 61 | // delete with single primary key 62 | const sqlstr = `DELETE FROM public.a_primary ` + 63 | `WHERE a_key = $1` 64 | // run 65 | logf(sqlstr, ap.AKey) 66 | if _, err := db.ExecContext(ctx, sqlstr, ap.AKey); err != nil { 67 | return logerror(err) 68 | } 69 | // set deleted 70 | ap._deleted = true 71 | return nil 72 | } 73 | 74 | // APrimaryByAKey retrieves a row from 'public.a_primary' as a [APrimary]. 75 | // 76 | // Generated from index 'a_primary_pkey'. 77 | func APrimaryByAKey(ctx context.Context, db DB, aKey int) (*APrimary, error) { 78 | // query 79 | const sqlstr = `SELECT ` + 80 | `a_key ` + 81 | `FROM public.a_primary ` + 82 | `WHERE a_key = $1` 83 | // run 84 | logf(sqlstr, aKey) 85 | ap := APrimary{ 86 | _exists: true, 87 | } 88 | if err := db.QueryRowContext(ctx, sqlstr, aKey).Scan(&ap.AKey); err != nil { 89 | return nil, logerror(err) 90 | } 91 | return &ap, nil 92 | } 93 | -------------------------------------------------------------------------------- /_examples/a_bit_of_everything/postgres/asamefkname1.dbtpl.go: -------------------------------------------------------------------------------- 1 | package postgres 2 | 3 | // Code generated by dbtpl. DO NOT EDIT. 4 | 5 | import ( 6 | "context" 7 | "database/sql" 8 | ) 9 | 10 | // ASameFkName1 represents a row from 'public.a_same_fk_name_1'. 11 | type ASameFkName1 struct { 12 | AFkey sql.NullInt64 `json:"a_fkey"` // a_fkey 13 | } 14 | 15 | // APrimary returns the APrimary associated with the [ASameFkName1]'s (AFkey). 16 | // 17 | // Generated from foreign key 'foreign_key_1'. 18 | func (asfn *ASameFkName1) APrimary(ctx context.Context, db DB) (*APrimary, error) { 19 | return APrimaryByAKey(ctx, db, int(asfn.AFkey.Int64)) 20 | } 21 | -------------------------------------------------------------------------------- /_examples/a_bit_of_everything/postgres/asamefkname2.dbtpl.go: -------------------------------------------------------------------------------- 1 | package postgres 2 | 3 | // Code generated by dbtpl. DO NOT EDIT. 4 | 5 | import ( 6 | "context" 7 | "database/sql" 8 | ) 9 | 10 | // ASameFkName2 represents a row from 'public.a_same_fk_name_2'. 11 | type ASameFkName2 struct { 12 | AFkey sql.NullInt64 `json:"a_fkey"` // a_fkey 13 | } 14 | 15 | // APrimary returns the APrimary associated with the [ASameFkName2]'s (AFkey). 16 | // 17 | // Generated from foreign key 'foreign_key_1'. 18 | func (asfn *ASameFkName2) APrimary(ctx context.Context, db DB) (*APrimary, error) { 19 | return APrimaryByAKey(ctx, db, int(asfn.AFkey.Int64)) 20 | } 21 | -------------------------------------------------------------------------------- /_examples/a_bit_of_everything/postgres/asequence.dbtpl.go: -------------------------------------------------------------------------------- 1 | package postgres 2 | 3 | // Code generated by dbtpl. DO NOT EDIT. 4 | 5 | import ( 6 | "context" 7 | ) 8 | 9 | // ASequence represents a row from 'public.a_sequence'. 10 | type ASequence struct { 11 | ASeq int `json:"a_seq"` // a_seq 12 | // xo fields 13 | _exists, _deleted bool 14 | } 15 | 16 | // Exists returns true when the [ASequence] exists in the database. 17 | func (as *ASequence) Exists() bool { 18 | return as._exists 19 | } 20 | 21 | // Deleted returns true when the [ASequence] has been marked for deletion 22 | // from the database. 23 | func (as *ASequence) Deleted() bool { 24 | return as._deleted 25 | } 26 | 27 | // Insert inserts the [ASequence] to the database. 28 | func (as *ASequence) Insert(ctx context.Context, db DB) error { 29 | switch { 30 | case as._exists: // already exists 31 | return logerror(&ErrInsertFailed{ErrAlreadyExists}) 32 | case as._deleted: // deleted 33 | return logerror(&ErrInsertFailed{ErrMarkedForDeletion}) 34 | } 35 | // insert (primary key generated and returned by database) 36 | const sqlstr = `INSERT INTO public.a_sequence (` + 37 | `` + 38 | `) VALUES (` + 39 | `` + 40 | `) RETURNING a_seq` 41 | // run 42 | logf(sqlstr) 43 | if err := db.QueryRowContext(ctx, sqlstr).Scan(&as.ASeq); err != nil { 44 | return logerror(err) 45 | } 46 | // set exists 47 | as._exists = true 48 | return nil 49 | } 50 | 51 | // ------ NOTE: Update statements omitted due to lack of fields other than primary key ------ 52 | 53 | // Delete deletes the [ASequence] from the database. 54 | func (as *ASequence) Delete(ctx context.Context, db DB) error { 55 | switch { 56 | case !as._exists: // doesn't exist 57 | return nil 58 | case as._deleted: // deleted 59 | return nil 60 | } 61 | // delete with single primary key 62 | const sqlstr = `DELETE FROM public.a_sequence ` + 63 | `WHERE a_seq = $1` 64 | // run 65 | logf(sqlstr, as.ASeq) 66 | if _, err := db.ExecContext(ctx, sqlstr, as.ASeq); err != nil { 67 | return logerror(err) 68 | } 69 | // set deleted 70 | as._deleted = true 71 | return nil 72 | } 73 | 74 | // ASequenceByASeq retrieves a row from 'public.a_sequence' as a [ASequence]. 75 | // 76 | // Generated from index 'a_sequence_pkey'. 77 | func ASequenceByASeq(ctx context.Context, db DB, aSeq int) (*ASequence, error) { 78 | // query 79 | const sqlstr = `SELECT ` + 80 | `a_seq ` + 81 | `FROM public.a_sequence ` + 82 | `WHERE a_seq = $1` 83 | // run 84 | logf(sqlstr, aSeq) 85 | as := ASequence{ 86 | _exists: true, 87 | } 88 | if err := db.QueryRowContext(ctx, sqlstr, aSeq).Scan(&as.ASeq); err != nil { 89 | return nil, logerror(err) 90 | } 91 | return &as, nil 92 | } 93 | -------------------------------------------------------------------------------- /_examples/a_bit_of_everything/postgres/auniqueindex.dbtpl.go: -------------------------------------------------------------------------------- 1 | package postgres 2 | 3 | // Code generated by dbtpl. DO NOT EDIT. 4 | 5 | import ( 6 | "context" 7 | "database/sql" 8 | ) 9 | 10 | // AUniqueIndex represents a row from 'public.a_unique_index'. 11 | type AUniqueIndex struct { 12 | AKey sql.NullInt64 `json:"a_key"` // a_key 13 | } 14 | 15 | // AUniqueIndexByAKey retrieves a row from 'public.a_unique_index' as a [AUniqueIndex]. 16 | // 17 | // Generated from index 'a_unique_index_a_key_key'. 18 | func AUniqueIndexByAKey(ctx context.Context, db DB, aKey sql.NullInt64) (*AUniqueIndex, error) { 19 | // query 20 | const sqlstr = `SELECT ` + 21 | `a_key ` + 22 | `FROM public.a_unique_index ` + 23 | `WHERE a_key = $1` 24 | // run 25 | logf(sqlstr, aKey) 26 | aui := AUniqueIndex{} 27 | if err := db.QueryRowContext(ctx, sqlstr, aKey).Scan(&aui.AKey); err != nil { 28 | return nil, logerror(err) 29 | } 30 | return &aui, nil 31 | } 32 | -------------------------------------------------------------------------------- /_examples/a_bit_of_everything/postgres/auniqueindexcomposite.dbtpl.go: -------------------------------------------------------------------------------- 1 | package postgres 2 | 3 | // Code generated by dbtpl. DO NOT EDIT. 4 | 5 | import ( 6 | "context" 7 | "database/sql" 8 | ) 9 | 10 | // AUniqueIndexComposite represents a row from 'public.a_unique_index_composite'. 11 | type AUniqueIndexComposite struct { 12 | AKey1 sql.NullInt64 `json:"a_key1"` // a_key1 13 | AKey2 sql.NullInt64 `json:"a_key2"` // a_key2 14 | } 15 | 16 | // AUniqueIndexCompositeByAKey1AKey2 retrieves a row from 'public.a_unique_index_composite' as a [AUniqueIndexComposite]. 17 | // 18 | // Generated from index 'a_unique_index_composite_a_key1_a_key2_key'. 19 | func AUniqueIndexCompositeByAKey1AKey2(ctx context.Context, db DB, aKey1, aKey2 sql.NullInt64) (*AUniqueIndexComposite, error) { 20 | // query 21 | const sqlstr = `SELECT ` + 22 | `a_key1, a_key2 ` + 23 | `FROM public.a_unique_index_composite ` + 24 | `WHERE a_key1 = $1 AND a_key2 = $2` 25 | // run 26 | logf(sqlstr, aKey1, aKey2) 27 | auic := AUniqueIndexComposite{} 28 | if err := db.QueryRowContext(ctx, sqlstr, aKey1, aKey2).Scan(&auic.AKey1, &auic.AKey2); err != nil { 29 | return nil, logerror(err) 30 | } 31 | return &auic, nil 32 | } 33 | -------------------------------------------------------------------------------- /_examples/a_bit_of_everything/postgres/aviewofeverythingsome.dbtpl.go: -------------------------------------------------------------------------------- 1 | package postgres 2 | 3 | // Code generated by dbtpl. DO NOT EDIT. 4 | 5 | import ( 6 | "database/sql" 7 | ) 8 | 9 | // SELECT a_bool, a_text FROM a_bit_of_everything; 10 | type AViewOfEverythingSome struct { 11 | ABool sql.NullBool `json:"a_bool"` // a_bool 12 | AText sql.NullString `json:"a_text"` // a_text 13 | } 14 | -------------------------------------------------------------------------------- /_examples/a_bit_of_everything/postgres/notes.txt: -------------------------------------------------------------------------------- 1 | test: 2 | interval -- non-nullable 3 | -------------------------------------------------------------------------------- /_examples/a_bit_of_everything/postgres/sf_a0in1out.dbtpl.go: -------------------------------------------------------------------------------- 1 | package postgres 2 | 3 | // Code generated by dbtpl. DO NOT EDIT. 4 | 5 | import ( 6 | "context" 7 | ) 8 | 9 | // A0In1Out calls the stored function 'public.a_0_in_1_out() integer' on db. 10 | func A0In1Out(ctx context.Context, db DB) (int, error) { 11 | // call public.a_0_in_1_out 12 | const sqlstr = `SELECT * FROM public.a_0_in_1_out()` 13 | // run 14 | var aReturn int 15 | logf(sqlstr) 16 | if err := db.QueryRowContext(ctx, sqlstr).Scan(&aReturn); err != nil { 17 | return 0, logerror(err) 18 | } 19 | return aReturn, nil 20 | } 21 | -------------------------------------------------------------------------------- /_examples/a_bit_of_everything/postgres/sf_a1in1out.dbtpl.go: -------------------------------------------------------------------------------- 1 | package postgres 2 | 3 | // Code generated by dbtpl. DO NOT EDIT. 4 | 5 | import ( 6 | "context" 7 | ) 8 | 9 | // A1In1Out calls the stored function 'public.a_1_in_1_out(integer) integer' on db. 10 | func A1In1Out(ctx context.Context, db DB, aParam int) (int, error) { 11 | // call public.a_1_in_1_out 12 | const sqlstr = `SELECT * FROM public.a_1_in_1_out($1)` 13 | // run 14 | var aReturn int 15 | logf(sqlstr, aParam) 16 | if err := db.QueryRowContext(ctx, sqlstr, aParam).Scan(&aReturn); err != nil { 17 | return 0, logerror(err) 18 | } 19 | return aReturn, nil 20 | } 21 | -------------------------------------------------------------------------------- /_examples/a_bit_of_everything/postgres/sf_a2in2out.dbtpl.go: -------------------------------------------------------------------------------- 1 | package postgres 2 | 3 | // Code generated by dbtpl. DO NOT EDIT. 4 | 5 | import ( 6 | "context" 7 | ) 8 | 9 | // A2In2Out calls the stored function 'public.a_2_in_2_out(integer, integer) (integer, integer)' on db. 10 | func A2In2Out(ctx context.Context, db DB, paramOne, paramTwo int) (int, int, error) { 11 | // call public.a_2_in_2_out 12 | const sqlstr = `SELECT * FROM public.a_2_in_2_out($1, $2)` 13 | // run 14 | var returnOne int 15 | var returnTwo int 16 | logf(sqlstr, paramOne, paramTwo) 17 | if err := db.QueryRowContext(ctx, sqlstr, paramOne, paramTwo).Scan(&returnOne, &returnTwo); err != nil { 18 | return 0, 0, logerror(err) 19 | } 20 | return returnOne, returnTwo, nil 21 | } 22 | -------------------------------------------------------------------------------- /_examples/a_bit_of_everything/postgres/sf_afunc0in.dbtpl.go: -------------------------------------------------------------------------------- 1 | package postgres 2 | 3 | // Code generated by dbtpl. DO NOT EDIT. 4 | 5 | import ( 6 | "context" 7 | ) 8 | 9 | // AFunc0In calls the stored function 'public.a_func_0_in() integer' on db. 10 | func AFunc0In(ctx context.Context, db DB) (int, error) { 11 | // call public.a_func_0_in 12 | const sqlstr = `SELECT * FROM public.a_func_0_in()` 13 | // run 14 | var r0 int 15 | logf(sqlstr) 16 | if err := db.QueryRowContext(ctx, sqlstr).Scan(&r0); err != nil { 17 | return 0, logerror(err) 18 | } 19 | return r0, nil 20 | } 21 | -------------------------------------------------------------------------------- /_examples/a_bit_of_everything/postgres/sf_afunc1in.dbtpl.go: -------------------------------------------------------------------------------- 1 | package postgres 2 | 3 | // Code generated by dbtpl. DO NOT EDIT. 4 | 5 | import ( 6 | "context" 7 | ) 8 | 9 | // AFunc1In calls the stored function 'public.a_func_1_in(integer) integer' on db. 10 | func AFunc1In(ctx context.Context, db DB, aParam int) (int, error) { 11 | // call public.a_func_1_in 12 | const sqlstr = `SELECT * FROM public.a_func_1_in($1)` 13 | // run 14 | var r0 int 15 | logf(sqlstr, aParam) 16 | if err := db.QueryRowContext(ctx, sqlstr, aParam).Scan(&r0); err != nil { 17 | return 0, logerror(err) 18 | } 19 | return r0, nil 20 | } 21 | -------------------------------------------------------------------------------- /_examples/a_bit_of_everything/postgres/sf_afunc2in.dbtpl.go: -------------------------------------------------------------------------------- 1 | package postgres 2 | 3 | // Code generated by dbtpl. DO NOT EDIT. 4 | 5 | import ( 6 | "context" 7 | ) 8 | 9 | // AFunc2In calls the stored function 'public.a_func_2_in(integer, integer) integer' on db. 10 | func AFunc2In(ctx context.Context, db DB, paramOne, paramTwo int) (int, error) { 11 | // call public.a_func_2_in 12 | const sqlstr = `SELECT * FROM public.a_func_2_in($1, $2)` 13 | // run 14 | var r0 int 15 | logf(sqlstr, paramOne, paramTwo) 16 | if err := db.QueryRowContext(ctx, sqlstr, paramOne, paramTwo).Scan(&r0); err != nil { 17 | return 0, logerror(err) 18 | } 19 | return r0, nil 20 | } 21 | -------------------------------------------------------------------------------- /_examples/a_bit_of_everything/postgres/sp_a0in0out.dbtpl.go: -------------------------------------------------------------------------------- 1 | package postgres 2 | 3 | // Code generated by dbtpl. DO NOT EDIT. 4 | 5 | import ( 6 | "context" 7 | ) 8 | 9 | // A0In0Out calls the stored procedure 'public.a_0_in_0_out()' on db. 10 | func A0In0Out(ctx context.Context, db DB) error { 11 | // call public.a_0_in_0_out 12 | const sqlstr = `CALL public.a_0_in_0_out()` 13 | // run 14 | logf(sqlstr) 15 | if _, err := db.ExecContext(ctx, sqlstr); err != nil { 16 | return logerror(err) 17 | } 18 | return nil 19 | } 20 | -------------------------------------------------------------------------------- /_examples/a_bit_of_everything/postgres/sp_a1in0out.dbtpl.go: -------------------------------------------------------------------------------- 1 | package postgres 2 | 3 | // Code generated by dbtpl. DO NOT EDIT. 4 | 5 | import ( 6 | "context" 7 | ) 8 | 9 | // A1In0Out calls the stored procedure 'public.a_1_in_0_out(integer)' on db. 10 | func A1In0Out(ctx context.Context, db DB, aParam int) error { 11 | // call public.a_1_in_0_out 12 | const sqlstr = `CALL public.a_1_in_0_out($1)` 13 | // run 14 | logf(sqlstr) 15 | if _, err := db.ExecContext(ctx, sqlstr, aParam); err != nil { 16 | return logerror(err) 17 | } 18 | return nil 19 | } 20 | -------------------------------------------------------------------------------- /_examples/a_bit_of_everything/postgres/sp_aoverloaded.dbtpl.go: -------------------------------------------------------------------------------- 1 | package postgres 2 | 3 | // Code generated by dbtpl. DO NOT EDIT. 4 | 5 | import ( 6 | "context" 7 | ) 8 | 9 | // AOverloadedByParamOne calls the stored procedure 'public.a_overloaded(integer)' on db. 10 | func AOverloadedByParamOne(ctx context.Context, db DB, paramOne int) error { 11 | // call public.a_overloaded 12 | const sqlstr = `CALL public.a_overloaded($1)` 13 | // run 14 | logf(sqlstr) 15 | if _, err := db.ExecContext(ctx, sqlstr, paramOne); err != nil { 16 | return logerror(err) 17 | } 18 | return nil 19 | } 20 | 21 | // AOverloadedByParamOneAndParamTwo calls the stored procedure 'public.a_overloaded(integer, integer)' on db. 22 | func AOverloadedByParamOneAndParamTwo(ctx context.Context, db DB, paramOne, paramTwo int) error { 23 | // call public.a_overloaded 24 | const sqlstr = `CALL public.a_overloaded($1, $2)` 25 | // run 26 | logf(sqlstr) 27 | if _, err := db.ExecContext(ctx, sqlstr, paramOne, paramTwo); err != nil { 28 | return logerror(err) 29 | } 30 | return nil 31 | } 32 | -------------------------------------------------------------------------------- /_examples/a_bit_of_everything/sqlite3.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "context" 5 | "database/sql" 6 | ) 7 | 8 | func runSqlite3(ctx context.Context, db *sql.DB) error { 9 | return nil 10 | } 11 | -------------------------------------------------------------------------------- /_examples/a_bit_of_everything/sqlite3/aforeignkey.dbtpl.go: -------------------------------------------------------------------------------- 1 | package sqlite3 2 | 3 | // Code generated by dbtpl. DO NOT EDIT. 4 | 5 | import ( 6 | "context" 7 | "database/sql" 8 | ) 9 | 10 | // AForeignKey represents a row from 'a_foreign_key'. 11 | type AForeignKey struct { 12 | AKey sql.NullInt64 `json:"a_key"` // a_key 13 | } 14 | 15 | // APrimary returns the APrimary associated with the [AForeignKey]'s (AKey). 16 | // 17 | // Generated from foreign key 'a_foreign_key_a_key_fkey'. 18 | func (afk *AForeignKey) APrimary(ctx context.Context, db DB) (*APrimary, error) { 19 | return APrimaryByAKey(ctx, db, int(afk.AKey.Int64)) 20 | } 21 | -------------------------------------------------------------------------------- /_examples/a_bit_of_everything/sqlite3/aforeignkeycomposite.dbtpl.go: -------------------------------------------------------------------------------- 1 | package sqlite3 2 | 3 | // Code generated by dbtpl. DO NOT EDIT. 4 | 5 | import ( 6 | "context" 7 | "database/sql" 8 | ) 9 | 10 | // AForeignKeyComposite represents a row from 'a_foreign_key_composite'. 11 | type AForeignKeyComposite struct { 12 | AKey1 sql.NullInt64 `json:"a_key1"` // a_key1 13 | AKey2 sql.NullInt64 `json:"a_key2"` // a_key2 14 | } 15 | 16 | // APrimaryComposite returns the APrimaryComposite associated with the [AForeignKeyComposite]'s (AKey1, AKey2). 17 | // 18 | // Generated from foreign key 'a_foreign_key_composite_a_key1_a_key2_fkey'. 19 | func (afkc *AForeignKeyComposite) APrimaryComposite(ctx context.Context, db DB) (*APrimaryComposite, error) { 20 | return APrimaryCompositeByAKey1AKey2(ctx, db, int(afkc.AKey1.Int64), int(afkc.AKey2.Int64)) 21 | } 22 | -------------------------------------------------------------------------------- /_examples/a_bit_of_everything/sqlite3/aindex.dbtpl.go: -------------------------------------------------------------------------------- 1 | package sqlite3 2 | 3 | // Code generated by dbtpl. DO NOT EDIT. 4 | 5 | import ( 6 | "context" 7 | "database/sql" 8 | ) 9 | 10 | // AIndex represents a row from 'a_index'. 11 | type AIndex struct { 12 | AKey sql.NullInt64 `json:"a_key"` // a_key 13 | } 14 | 15 | // AIndexByAKey retrieves a row from 'a_index' as a [AIndex]. 16 | // 17 | // Generated from index 'a_index_idx'. 18 | func AIndexByAKey(ctx context.Context, db DB, aKey sql.NullInt64) ([]*AIndex, error) { 19 | // query 20 | const sqlstr = `SELECT ` + 21 | `a_key ` + 22 | `FROM a_index ` + 23 | `WHERE a_key = $1` 24 | // run 25 | logf(sqlstr, aKey) 26 | rows, err := db.QueryContext(ctx, sqlstr, aKey) 27 | if err != nil { 28 | return nil, logerror(err) 29 | } 30 | defer rows.Close() 31 | // process 32 | var res []*AIndex 33 | for rows.Next() { 34 | ai := AIndex{} 35 | // scan 36 | if err := rows.Scan(&ai.AKey); err != nil { 37 | return nil, logerror(err) 38 | } 39 | res = append(res, &ai) 40 | } 41 | if err := rows.Err(); err != nil { 42 | return nil, logerror(err) 43 | } 44 | return res, nil 45 | } 46 | -------------------------------------------------------------------------------- /_examples/a_bit_of_everything/sqlite3/aindexcomposite.dbtpl.go: -------------------------------------------------------------------------------- 1 | package sqlite3 2 | 3 | // Code generated by dbtpl. DO NOT EDIT. 4 | 5 | import ( 6 | "context" 7 | "database/sql" 8 | ) 9 | 10 | // AIndexComposite represents a row from 'a_index_composite'. 11 | type AIndexComposite struct { 12 | AKey1 sql.NullInt64 `json:"a_key1"` // a_key1 13 | AKey2 sql.NullInt64 `json:"a_key2"` // a_key2 14 | } 15 | 16 | // AIndexCompositeByAKey1AKey2 retrieves a row from 'a_index_composite' as a [AIndexComposite]. 17 | // 18 | // Generated from index 'a_index_composite_idx'. 19 | func AIndexCompositeByAKey1AKey2(ctx context.Context, db DB, aKey1, aKey2 sql.NullInt64) ([]*AIndexComposite, error) { 20 | // query 21 | const sqlstr = `SELECT ` + 22 | `a_key1, a_key2 ` + 23 | `FROM a_index_composite ` + 24 | `WHERE a_key1 = $1 AND a_key2 = $2` 25 | // run 26 | logf(sqlstr, aKey1, aKey2) 27 | rows, err := db.QueryContext(ctx, sqlstr, aKey1, aKey2) 28 | if err != nil { 29 | return nil, logerror(err) 30 | } 31 | defer rows.Close() 32 | // process 33 | var res []*AIndexComposite 34 | for rows.Next() { 35 | aic := AIndexComposite{} 36 | // scan 37 | if err := rows.Scan(&aic.AKey1, &aic.AKey2); err != nil { 38 | return nil, logerror(err) 39 | } 40 | res = append(res, &aic) 41 | } 42 | if err := rows.Err(); err != nil { 43 | return nil, logerror(err) 44 | } 45 | return res, nil 46 | } 47 | -------------------------------------------------------------------------------- /_examples/a_bit_of_everything/sqlite3/amanualtable.dbtpl.go: -------------------------------------------------------------------------------- 1 | package sqlite3 2 | 3 | // Code generated by dbtpl. DO NOT EDIT. 4 | 5 | import ( 6 | "database/sql" 7 | ) 8 | 9 | // AManualTable represents a row from 'a_manual_table'. 10 | type AManualTable struct { 11 | AText sql.NullString `json:"a_text"` // a_text 12 | } 13 | -------------------------------------------------------------------------------- /_examples/a_bit_of_everything/sqlite3/aprimary.dbtpl.go: -------------------------------------------------------------------------------- 1 | package sqlite3 2 | 3 | // Code generated by dbtpl. DO NOT EDIT. 4 | 5 | import ( 6 | "context" 7 | ) 8 | 9 | // APrimary represents a row from 'a_primary'. 10 | type APrimary struct { 11 | AKey int `json:"a_key"` // a_key 12 | // xo fields 13 | _exists, _deleted bool 14 | } 15 | 16 | // Exists returns true when the [APrimary] exists in the database. 17 | func (ap *APrimary) Exists() bool { 18 | return ap._exists 19 | } 20 | 21 | // Deleted returns true when the [APrimary] has been marked for deletion 22 | // from the database. 23 | func (ap *APrimary) Deleted() bool { 24 | return ap._deleted 25 | } 26 | 27 | // Insert inserts the [APrimary] to the database. 28 | func (ap *APrimary) Insert(ctx context.Context, db DB) error { 29 | switch { 30 | case ap._exists: // already exists 31 | return logerror(&ErrInsertFailed{ErrAlreadyExists}) 32 | case ap._deleted: // deleted 33 | return logerror(&ErrInsertFailed{ErrMarkedForDeletion}) 34 | } 35 | // insert (manual) 36 | const sqlstr = `INSERT INTO a_primary (` + 37 | `a_key` + 38 | `) VALUES (` + 39 | `$1` + 40 | `)` 41 | // run 42 | logf(sqlstr, ap.AKey) 43 | if _, err := db.ExecContext(ctx, sqlstr, ap.AKey); err != nil { 44 | return logerror(err) 45 | } 46 | // set exists 47 | ap._exists = true 48 | return nil 49 | } 50 | 51 | // ------ NOTE: Update statements omitted due to lack of fields other than primary key ------ 52 | 53 | // Delete deletes the [APrimary] from the database. 54 | func (ap *APrimary) Delete(ctx context.Context, db DB) error { 55 | switch { 56 | case !ap._exists: // doesn't exist 57 | return nil 58 | case ap._deleted: // deleted 59 | return nil 60 | } 61 | // delete with single primary key 62 | const sqlstr = `DELETE FROM a_primary ` + 63 | `WHERE a_key = $1` 64 | // run 65 | logf(sqlstr, ap.AKey) 66 | if _, err := db.ExecContext(ctx, sqlstr, ap.AKey); err != nil { 67 | return logerror(err) 68 | } 69 | // set deleted 70 | ap._deleted = true 71 | return nil 72 | } 73 | 74 | // APrimaryByAKey retrieves a row from 'a_primary' as a [APrimary]. 75 | // 76 | // Generated from index 'a_primary_a_key_pkey'. 77 | func APrimaryByAKey(ctx context.Context, db DB, aKey int) (*APrimary, error) { 78 | // query 79 | const sqlstr = `SELECT ` + 80 | `a_key ` + 81 | `FROM a_primary ` + 82 | `WHERE a_key = $1` 83 | // run 84 | logf(sqlstr, aKey) 85 | ap := APrimary{ 86 | _exists: true, 87 | } 88 | if err := db.QueryRowContext(ctx, sqlstr, aKey).Scan(&ap.AKey); err != nil { 89 | return nil, logerror(err) 90 | } 91 | return &ap, nil 92 | } 93 | -------------------------------------------------------------------------------- /_examples/a_bit_of_everything/sqlite3/auniqueindex.dbtpl.go: -------------------------------------------------------------------------------- 1 | package sqlite3 2 | 3 | // Code generated by dbtpl. DO NOT EDIT. 4 | 5 | import ( 6 | "context" 7 | "database/sql" 8 | ) 9 | 10 | // AUniqueIndex represents a row from 'a_unique_index'. 11 | type AUniqueIndex struct { 12 | AKey sql.NullInt64 `json:"a_key"` // a_key 13 | } 14 | 15 | // AUniqueIndexByAKey retrieves a row from 'a_unique_index' as a [AUniqueIndex]. 16 | // 17 | // Generated from index 'sqlite_autoindex_a_unique_index_1'. 18 | func AUniqueIndexByAKey(ctx context.Context, db DB, aKey sql.NullInt64) (*AUniqueIndex, error) { 19 | // query 20 | const sqlstr = `SELECT ` + 21 | `a_key ` + 22 | `FROM a_unique_index ` + 23 | `WHERE a_key = $1` 24 | // run 25 | logf(sqlstr, aKey) 26 | aui := AUniqueIndex{} 27 | if err := db.QueryRowContext(ctx, sqlstr, aKey).Scan(&aui.AKey); err != nil { 28 | return nil, logerror(err) 29 | } 30 | return &aui, nil 31 | } 32 | -------------------------------------------------------------------------------- /_examples/a_bit_of_everything/sqlite3/auniqueindexcomposite.dbtpl.go: -------------------------------------------------------------------------------- 1 | package sqlite3 2 | 3 | // Code generated by dbtpl. DO NOT EDIT. 4 | 5 | import ( 6 | "context" 7 | "database/sql" 8 | ) 9 | 10 | // AUniqueIndexComposite represents a row from 'a_unique_index_composite'. 11 | type AUniqueIndexComposite struct { 12 | AKey1 sql.NullInt64 `json:"a_key1"` // a_key1 13 | AKey2 sql.NullInt64 `json:"a_key2"` // a_key2 14 | } 15 | 16 | // AUniqueIndexCompositeByAKey1AKey2 retrieves a row from 'a_unique_index_composite' as a [AUniqueIndexComposite]. 17 | // 18 | // Generated from index 'sqlite_autoindex_a_unique_index_composite_1'. 19 | func AUniqueIndexCompositeByAKey1AKey2(ctx context.Context, db DB, aKey1, aKey2 sql.NullInt64) (*AUniqueIndexComposite, error) { 20 | // query 21 | const sqlstr = `SELECT ` + 22 | `a_key1, a_key2 ` + 23 | `FROM a_unique_index_composite ` + 24 | `WHERE a_key1 = $1 AND a_key2 = $2` 25 | // run 26 | logf(sqlstr, aKey1, aKey2) 27 | auic := AUniqueIndexComposite{} 28 | if err := db.QueryRowContext(ctx, sqlstr, aKey1, aKey2).Scan(&auic.AKey1, &auic.AKey2); err != nil { 29 | return nil, logerror(err) 30 | } 31 | return &auic, nil 32 | } 33 | -------------------------------------------------------------------------------- /_examples/a_bit_of_everything/sqlite3/aviewofeverythingsome.dbtpl.go: -------------------------------------------------------------------------------- 1 | package sqlite3 2 | 3 | // Code generated by dbtpl. DO NOT EDIT. 4 | 5 | import ( 6 | "database/sql" 7 | ) 8 | 9 | // CREATE VIEW a_view_of_everything_some AS SELECT a_bool, a_text FROM a_bit_of_everything 10 | type AViewOfEverythingSome struct { 11 | ABool sql.NullBool `json:"a_bool"` // a_bool 12 | AText sql.NullString `json:"a_text"` // a_text 13 | } 14 | -------------------------------------------------------------------------------- /_examples/a_bit_of_everything/sqlserver.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "context" 5 | "database/sql" 6 | "fmt" 7 | 8 | models "github.com/xo/dbtpl/_examples/a_bit_of_everything/sqlserver" 9 | ) 10 | 11 | func runSqlserver(ctx context.Context, db *sql.DB) error { 12 | // run procs 13 | if err := models.A0In0Out(ctx, db); err != nil { 14 | return fmt.Errorf("0 in 0 out: %v", err) 15 | } 16 | // 1 in 0 out 17 | if err := models.A1In0Out(ctx, db, 10); err != nil { 18 | return fmt.Errorf("1 in 0 out: %v", err) 19 | } 20 | var res1, res2 int 21 | var err error 22 | // 0 in 1 out 23 | if res1, err = models.A0In1Out(ctx, db); err != nil { 24 | return fmt.Errorf("0 in 1 out: %v", err) 25 | } 26 | fmt.Printf("a_0_in_1_out(): %d\n", res1) 27 | // 1 in 1 out 28 | if res1, err = models.A1In1Out(ctx, db, 10); err != nil { 29 | return fmt.Errorf("1 in 0 out: %v", err) 30 | } 31 | fmt.Printf("a_1_in_1_out(%d): %d\n", 10, res1) 32 | // 2 in 2 out 33 | if res1, res2, err = models.A2In2Out(ctx, db, 10, 20); err != nil { 34 | return fmt.Errorf("2 in 2 out: %v", err) 35 | } 36 | fmt.Printf("a_2_in_2_out(%d, %d): %d, %d\n", 10, 20, res1, res2) 37 | // run funcs 38 | // 0 in 39 | if res1, err = models.AFunc0In(ctx, db); err != nil { 40 | return fmt.Errorf("a func 0 in: %v", err) 41 | } 42 | fmt.Printf("a_func_0_in(): %d\n", res1) 43 | // 1 in 44 | if res1, err = models.AFunc1In(ctx, db, 10); err != nil { 45 | return fmt.Errorf("a func 1 in: %v", err) 46 | } 47 | fmt.Printf("a_func_1_in(%d): %d\n", 10, res1) 48 | // 1 in 49 | if res1, err = models.AFunc2In(ctx, db, 10, 20); err != nil { 50 | return fmt.Errorf("a func 2 in: %v", err) 51 | } 52 | fmt.Printf("a_func_2_in(%d, %d): %d\n", 10, 20, res1) 53 | return nil 54 | } 55 | -------------------------------------------------------------------------------- /_examples/a_bit_of_everything/sqlserver/aforeignkey.dbtpl.go: -------------------------------------------------------------------------------- 1 | package sqlserver 2 | 3 | // Code generated by dbtpl. DO NOT EDIT. 4 | 5 | import ( 6 | "context" 7 | "database/sql" 8 | ) 9 | 10 | // AForeignKey represents a row from 'a_bit_of_everything.a_foreign_key'. 11 | type AForeignKey struct { 12 | AKey sql.NullInt64 `json:"a_key"` // a_key 13 | } 14 | 15 | // APrimary returns the APrimary associated with the [AForeignKey]'s (AKey). 16 | // 17 | // Generated from foreign key 'a_key_fkey'. 18 | func (afk *AForeignKey) APrimary(ctx context.Context, db DB) (*APrimary, error) { 19 | return APrimaryByAKey(ctx, db, int(afk.AKey.Int64)) 20 | } 21 | -------------------------------------------------------------------------------- /_examples/a_bit_of_everything/sqlserver/aforeignkeycomposite.dbtpl.go: -------------------------------------------------------------------------------- 1 | package sqlserver 2 | 3 | // Code generated by dbtpl. DO NOT EDIT. 4 | 5 | import ( 6 | "context" 7 | "database/sql" 8 | ) 9 | 10 | // AForeignKeyComposite represents a row from 'a_bit_of_everything.a_foreign_key_composite'. 11 | type AForeignKeyComposite struct { 12 | AKey1 sql.NullInt64 `json:"a_key1"` // a_key1 13 | AKey2 sql.NullInt64 `json:"a_key2"` // a_key2 14 | } 15 | 16 | // APrimaryComposite returns the APrimaryComposite associated with the [AForeignKeyComposite]'s (AKey1, AKey2). 17 | // 18 | // Generated from foreign key 'a_foreign_key_composite_fkey'. 19 | func (afkc *AForeignKeyComposite) APrimaryComposite(ctx context.Context, db DB) (*APrimaryComposite, error) { 20 | return APrimaryCompositeByAKey1AKey2(ctx, db, int(afkc.AKey1.Int64), int(afkc.AKey2.Int64)) 21 | } 22 | -------------------------------------------------------------------------------- /_examples/a_bit_of_everything/sqlserver/aindex.dbtpl.go: -------------------------------------------------------------------------------- 1 | package sqlserver 2 | 3 | // Code generated by dbtpl. DO NOT EDIT. 4 | 5 | import ( 6 | "context" 7 | "database/sql" 8 | ) 9 | 10 | // AIndex represents a row from 'a_bit_of_everything.a_index'. 11 | type AIndex struct { 12 | AKey sql.NullInt64 `json:"a_key"` // a_key 13 | } 14 | 15 | // AIndexByAKey retrieves a row from 'a_bit_of_everything.a_index' as a [AIndex]. 16 | // 17 | // Generated from index 'a_index_idx'. 18 | func AIndexByAKey(ctx context.Context, db DB, aKey sql.NullInt64) ([]*AIndex, error) { 19 | // query 20 | const sqlstr = `SELECT ` + 21 | `a_key ` + 22 | `FROM a_bit_of_everything.a_index ` + 23 | `WHERE a_key = @p1` 24 | // run 25 | logf(sqlstr, aKey) 26 | rows, err := db.QueryContext(ctx, sqlstr, aKey) 27 | if err != nil { 28 | return nil, logerror(err) 29 | } 30 | defer rows.Close() 31 | // process 32 | var res []*AIndex 33 | for rows.Next() { 34 | ai := AIndex{} 35 | // scan 36 | if err := rows.Scan(&ai.AKey); err != nil { 37 | return nil, logerror(err) 38 | } 39 | res = append(res, &ai) 40 | } 41 | if err := rows.Err(); err != nil { 42 | return nil, logerror(err) 43 | } 44 | return res, nil 45 | } 46 | -------------------------------------------------------------------------------- /_examples/a_bit_of_everything/sqlserver/aindexcomposite.dbtpl.go: -------------------------------------------------------------------------------- 1 | package sqlserver 2 | 3 | // Code generated by dbtpl. DO NOT EDIT. 4 | 5 | import ( 6 | "context" 7 | "database/sql" 8 | ) 9 | 10 | // AIndexComposite represents a row from 'a_bit_of_everything.a_index_composite'. 11 | type AIndexComposite struct { 12 | AKey1 sql.NullInt64 `json:"a_key1"` // a_key1 13 | AKey2 sql.NullInt64 `json:"a_key2"` // a_key2 14 | } 15 | 16 | // AIndexCompositeByAKey1AKey2 retrieves a row from 'a_bit_of_everything.a_index_composite' as a [AIndexComposite]. 17 | // 18 | // Generated from index 'a_index_composite_idx'. 19 | func AIndexCompositeByAKey1AKey2(ctx context.Context, db DB, aKey1, aKey2 sql.NullInt64) ([]*AIndexComposite, error) { 20 | // query 21 | const sqlstr = `SELECT ` + 22 | `a_key1, a_key2 ` + 23 | `FROM a_bit_of_everything.a_index_composite ` + 24 | `WHERE a_key1 = @p1 AND a_key2 = @p2` 25 | // run 26 | logf(sqlstr, aKey1, aKey2) 27 | rows, err := db.QueryContext(ctx, sqlstr, aKey1, aKey2) 28 | if err != nil { 29 | return nil, logerror(err) 30 | } 31 | defer rows.Close() 32 | // process 33 | var res []*AIndexComposite 34 | for rows.Next() { 35 | aic := AIndexComposite{} 36 | // scan 37 | if err := rows.Scan(&aic.AKey1, &aic.AKey2); err != nil { 38 | return nil, logerror(err) 39 | } 40 | res = append(res, &aic) 41 | } 42 | if err := rows.Err(); err != nil { 43 | return nil, logerror(err) 44 | } 45 | return res, nil 46 | } 47 | -------------------------------------------------------------------------------- /_examples/a_bit_of_everything/sqlserver/amanualtable.dbtpl.go: -------------------------------------------------------------------------------- 1 | package sqlserver 2 | 3 | // Code generated by dbtpl. DO NOT EDIT. 4 | 5 | import ( 6 | "database/sql" 7 | ) 8 | 9 | // AManualTable represents a row from 'a_bit_of_everything.a_manual_table'. 10 | type AManualTable struct { 11 | AText sql.NullString `json:"a_text"` // a_text 12 | } 13 | -------------------------------------------------------------------------------- /_examples/a_bit_of_everything/sqlserver/aprimary.dbtpl.go: -------------------------------------------------------------------------------- 1 | package sqlserver 2 | 3 | // Code generated by dbtpl. DO NOT EDIT. 4 | 5 | import ( 6 | "context" 7 | ) 8 | 9 | // APrimary represents a row from 'a_bit_of_everything.a_primary'. 10 | type APrimary struct { 11 | AKey int `json:"a_key"` // a_key 12 | // xo fields 13 | _exists, _deleted bool 14 | } 15 | 16 | // Exists returns true when the [APrimary] exists in the database. 17 | func (ap *APrimary) Exists() bool { 18 | return ap._exists 19 | } 20 | 21 | // Deleted returns true when the [APrimary] has been marked for deletion 22 | // from the database. 23 | func (ap *APrimary) Deleted() bool { 24 | return ap._deleted 25 | } 26 | 27 | // Insert inserts the [APrimary] to the database. 28 | func (ap *APrimary) Insert(ctx context.Context, db DB) error { 29 | switch { 30 | case ap._exists: // already exists 31 | return logerror(&ErrInsertFailed{ErrAlreadyExists}) 32 | case ap._deleted: // deleted 33 | return logerror(&ErrInsertFailed{ErrMarkedForDeletion}) 34 | } 35 | // insert (manual) 36 | const sqlstr = `INSERT INTO a_bit_of_everything.a_primary (` + 37 | `a_key` + 38 | `) VALUES (` + 39 | `@p1` + 40 | `)` 41 | // run 42 | logf(sqlstr, ap.AKey) 43 | if _, err := db.ExecContext(ctx, sqlstr, ap.AKey); err != nil { 44 | return logerror(err) 45 | } 46 | // set exists 47 | ap._exists = true 48 | return nil 49 | } 50 | 51 | // ------ NOTE: Update statements omitted due to lack of fields other than primary key ------ 52 | 53 | // Delete deletes the [APrimary] from the database. 54 | func (ap *APrimary) Delete(ctx context.Context, db DB) error { 55 | switch { 56 | case !ap._exists: // doesn't exist 57 | return nil 58 | case ap._deleted: // deleted 59 | return nil 60 | } 61 | // delete with single primary key 62 | const sqlstr = `DELETE FROM a_bit_of_everything.a_primary ` + 63 | `WHERE a_key = @p1` 64 | // run 65 | logf(sqlstr, ap.AKey) 66 | if _, err := db.ExecContext(ctx, sqlstr, ap.AKey); err != nil { 67 | return logerror(err) 68 | } 69 | // set deleted 70 | ap._deleted = true 71 | return nil 72 | } 73 | 74 | // APrimaryByAKey retrieves a row from 'a_bit_of_everything.a_primary' as a [APrimary]. 75 | // 76 | // Generated from index 'a_primary_pkey'. 77 | func APrimaryByAKey(ctx context.Context, db DB, aKey int) (*APrimary, error) { 78 | // query 79 | const sqlstr = `SELECT ` + 80 | `a_key ` + 81 | `FROM a_bit_of_everything.a_primary ` + 82 | `WHERE a_key = @p1` 83 | // run 84 | logf(sqlstr, aKey) 85 | ap := APrimary{ 86 | _exists: true, 87 | } 88 | if err := db.QueryRowContext(ctx, sqlstr, aKey).Scan(&ap.AKey); err != nil { 89 | return nil, logerror(err) 90 | } 91 | return &ap, nil 92 | } 93 | -------------------------------------------------------------------------------- /_examples/a_bit_of_everything/sqlserver/auniqueindex.dbtpl.go: -------------------------------------------------------------------------------- 1 | package sqlserver 2 | 3 | // Code generated by dbtpl. DO NOT EDIT. 4 | 5 | import ( 6 | "context" 7 | "database/sql" 8 | ) 9 | 10 | // AUniqueIndex represents a row from 'a_bit_of_everything.a_unique_index'. 11 | type AUniqueIndex struct { 12 | AKey sql.NullInt64 `json:"a_key"` // a_key 13 | } 14 | 15 | // AUniqueIndexByAKey retrieves a row from 'a_bit_of_everything.a_unique_index' as a [AUniqueIndex]. 16 | // 17 | // Generated from index 'a_unique_index_idx'. 18 | func AUniqueIndexByAKey(ctx context.Context, db DB, aKey sql.NullInt64) (*AUniqueIndex, error) { 19 | // query 20 | const sqlstr = `SELECT ` + 21 | `a_key ` + 22 | `FROM a_bit_of_everything.a_unique_index ` + 23 | `WHERE a_key = @p1` 24 | // run 25 | logf(sqlstr, aKey) 26 | aui := AUniqueIndex{} 27 | if err := db.QueryRowContext(ctx, sqlstr, aKey).Scan(&aui.AKey); err != nil { 28 | return nil, logerror(err) 29 | } 30 | return &aui, nil 31 | } 32 | -------------------------------------------------------------------------------- /_examples/a_bit_of_everything/sqlserver/auniqueindexcomposite.dbtpl.go: -------------------------------------------------------------------------------- 1 | package sqlserver 2 | 3 | // Code generated by dbtpl. DO NOT EDIT. 4 | 5 | import ( 6 | "context" 7 | "database/sql" 8 | ) 9 | 10 | // AUniqueIndexComposite represents a row from 'a_bit_of_everything.a_unique_index_composite'. 11 | type AUniqueIndexComposite struct { 12 | AKey1 sql.NullInt64 `json:"a_key1"` // a_key1 13 | AKey2 sql.NullInt64 `json:"a_key2"` // a_key2 14 | } 15 | 16 | // AUniqueIndexCompositeByAKey1AKey2 retrieves a row from 'a_bit_of_everything.a_unique_index_composite' as a [AUniqueIndexComposite]. 17 | // 18 | // Generated from index 'a_unique_index_composite_idx'. 19 | func AUniqueIndexCompositeByAKey1AKey2(ctx context.Context, db DB, aKey1, aKey2 sql.NullInt64) (*AUniqueIndexComposite, error) { 20 | // query 21 | const sqlstr = `SELECT ` + 22 | `a_key1, a_key2 ` + 23 | `FROM a_bit_of_everything.a_unique_index_composite ` + 24 | `WHERE a_key1 = @p1 AND a_key2 = @p2` 25 | // run 26 | logf(sqlstr, aKey1, aKey2) 27 | auic := AUniqueIndexComposite{} 28 | if err := db.QueryRowContext(ctx, sqlstr, aKey1, aKey2).Scan(&auic.AKey1, &auic.AKey2); err != nil { 29 | return nil, logerror(err) 30 | } 31 | return &auic, nil 32 | } 33 | -------------------------------------------------------------------------------- /_examples/a_bit_of_everything/sqlserver/aviewofeverythingsome.dbtpl.go: -------------------------------------------------------------------------------- 1 | package sqlserver 2 | 3 | // Code generated by dbtpl. DO NOT EDIT. 4 | 5 | // CREATE VIEW a_view_of_everything_some AS SELECT a_bit, a_text FROM a_bit_of_everything; 6 | type AViewOfEverythingSome struct { 7 | ABit bool `json:"a_bit"` // a_bit 8 | AText string `json:"a_text"` // a_text 9 | } 10 | -------------------------------------------------------------------------------- /_examples/a_bit_of_everything/sqlserver/sf_afunc0in.dbtpl.go: -------------------------------------------------------------------------------- 1 | package sqlserver 2 | 3 | // Code generated by dbtpl. DO NOT EDIT. 4 | 5 | import ( 6 | "context" 7 | ) 8 | 9 | // AFunc0In calls the stored function 'a_bit_of_everything.a_func_0_in() int' on db. 10 | func AFunc0In(ctx context.Context, db DB) (int, error) { 11 | // call a_bit_of_everything.a_func_0_in 12 | const sqlstr = `SELECT a_bit_of_everything.a_func_0_in() AS OUT` 13 | // run 14 | var r0 int 15 | logf(sqlstr) 16 | if err := db.QueryRowContext(ctx, sqlstr).Scan(&r0); err != nil { 17 | return 0, logerror(err) 18 | } 19 | return r0, nil 20 | } 21 | -------------------------------------------------------------------------------- /_examples/a_bit_of_everything/sqlserver/sf_afunc1in.dbtpl.go: -------------------------------------------------------------------------------- 1 | package sqlserver 2 | 3 | // Code generated by dbtpl. DO NOT EDIT. 4 | 5 | import ( 6 | "context" 7 | ) 8 | 9 | // AFunc1In calls the stored function 'a_bit_of_everything.a_func_1_in(int) int' on db. 10 | func AFunc1In(ctx context.Context, db DB, aParam int) (int, error) { 11 | // call a_bit_of_everything.a_func_1_in 12 | const sqlstr = `SELECT a_bit_of_everything.a_func_1_in(@p1) AS OUT` 13 | // run 14 | var r0 int 15 | logf(sqlstr, aParam) 16 | if err := db.QueryRowContext(ctx, sqlstr, aParam).Scan(&r0); err != nil { 17 | return 0, logerror(err) 18 | } 19 | return r0, nil 20 | } 21 | -------------------------------------------------------------------------------- /_examples/a_bit_of_everything/sqlserver/sf_afunc2in.dbtpl.go: -------------------------------------------------------------------------------- 1 | package sqlserver 2 | 3 | // Code generated by dbtpl. DO NOT EDIT. 4 | 5 | import ( 6 | "context" 7 | ) 8 | 9 | // AFunc2In calls the stored function 'a_bit_of_everything.a_func_2_in(int, int) int' on db. 10 | func AFunc2In(ctx context.Context, db DB, paramOne, paramTwo int) (int, error) { 11 | // call a_bit_of_everything.a_func_2_in 12 | const sqlstr = `SELECT a_bit_of_everything.a_func_2_in(@p1, @p2) AS OUT` 13 | // run 14 | var r0 int 15 | logf(sqlstr, paramOne, paramTwo) 16 | if err := db.QueryRowContext(ctx, sqlstr, paramOne, paramTwo).Scan(&r0); err != nil { 17 | return 0, logerror(err) 18 | } 19 | return r0, nil 20 | } 21 | -------------------------------------------------------------------------------- /_examples/a_bit_of_everything/sqlserver/sp_a0in0out.dbtpl.go: -------------------------------------------------------------------------------- 1 | package sqlserver 2 | 3 | // Code generated by dbtpl. DO NOT EDIT. 4 | 5 | import ( 6 | "context" 7 | ) 8 | 9 | // A0In0Out calls the stored procedure 'a_bit_of_everything.a_0_in_0_out()' on db. 10 | func A0In0Out(ctx context.Context, db DB) error { 11 | // call a_bit_of_everything.a_0_in_0_out 12 | const sqlstr = `a_bit_of_everything.a_0_in_0_out` 13 | // run 14 | logf(sqlstr) 15 | if _, err := db.ExecContext(ctx, sqlstr); err != nil { 16 | return logerror(err) 17 | } 18 | return nil 19 | } 20 | -------------------------------------------------------------------------------- /_examples/a_bit_of_everything/sqlserver/sp_a0in1out.dbtpl.go: -------------------------------------------------------------------------------- 1 | package sqlserver 2 | 3 | // Code generated by dbtpl. DO NOT EDIT. 4 | 5 | import ( 6 | "context" 7 | "database/sql" 8 | ) 9 | 10 | // A0In1Out calls the stored procedure 'a_bit_of_everything.a_0_in_1_out() int' on db. 11 | func A0In1Out(ctx context.Context, db DB) (int, error) { 12 | // call a_bit_of_everything.a_0_in_1_out 13 | const sqlstr = `a_bit_of_everything.a_0_in_1_out` 14 | // run 15 | var aReturn int 16 | logf(sqlstr) 17 | if _, err := db.ExecContext(ctx, sqlstr, sql.Named("a_return", sql.Out{Dest: &aReturn})); err != nil { 18 | return 0, logerror(err) 19 | } 20 | return aReturn, nil 21 | } 22 | -------------------------------------------------------------------------------- /_examples/a_bit_of_everything/sqlserver/sp_a1in0out.dbtpl.go: -------------------------------------------------------------------------------- 1 | package sqlserver 2 | 3 | // Code generated by dbtpl. DO NOT EDIT. 4 | 5 | import ( 6 | "context" 7 | "database/sql" 8 | ) 9 | 10 | // A1In0Out calls the stored procedure 'a_bit_of_everything.a_1_in_0_out(int)' on db. 11 | func A1In0Out(ctx context.Context, db DB, aParam int) error { 12 | // call a_bit_of_everything.a_1_in_0_out 13 | const sqlstr = `a_bit_of_everything.a_1_in_0_out` 14 | // run 15 | logf(sqlstr) 16 | if _, err := db.ExecContext(ctx, sqlstr, sql.Named("a_param", aParam)); err != nil { 17 | return logerror(err) 18 | } 19 | return nil 20 | } 21 | -------------------------------------------------------------------------------- /_examples/a_bit_of_everything/sqlserver/sp_a1in1out.dbtpl.go: -------------------------------------------------------------------------------- 1 | package sqlserver 2 | 3 | // Code generated by dbtpl. DO NOT EDIT. 4 | 5 | import ( 6 | "context" 7 | "database/sql" 8 | ) 9 | 10 | // A1In1Out calls the stored procedure 'a_bit_of_everything.a_1_in_1_out(int) int' on db. 11 | func A1In1Out(ctx context.Context, db DB, aParam int) (int, error) { 12 | // call a_bit_of_everything.a_1_in_1_out 13 | const sqlstr = `a_bit_of_everything.a_1_in_1_out` 14 | // run 15 | var aReturn int 16 | logf(sqlstr, aParam) 17 | if _, err := db.ExecContext(ctx, sqlstr, sql.Named("a_param", aParam), sql.Named("a_return", sql.Out{Dest: &aReturn})); err != nil { 18 | return 0, logerror(err) 19 | } 20 | return aReturn, nil 21 | } 22 | -------------------------------------------------------------------------------- /_examples/a_bit_of_everything/sqlserver/sp_a2in2out.dbtpl.go: -------------------------------------------------------------------------------- 1 | package sqlserver 2 | 3 | // Code generated by dbtpl. DO NOT EDIT. 4 | 5 | import ( 6 | "context" 7 | "database/sql" 8 | ) 9 | 10 | // A2In2Out calls the stored procedure 'a_bit_of_everything.a_2_in_2_out(int, int) (int, int)' on db. 11 | func A2In2Out(ctx context.Context, db DB, paramOne, paramTwo int) (int, int, error) { 12 | // call a_bit_of_everything.a_2_in_2_out 13 | const sqlstr = `a_bit_of_everything.a_2_in_2_out` 14 | // run 15 | var returnOne int 16 | var returnTwo int 17 | logf(sqlstr, paramOne, paramTwo) 18 | if _, err := db.ExecContext(ctx, sqlstr, sql.Named("param_one", paramOne), sql.Named("param_two", paramTwo), sql.Named("return_one", sql.Out{Dest: &returnOne}), sql.Named("return_two", sql.Out{Dest: &returnTwo})); err != nil { 19 | return 0, 0, logerror(err) 20 | } 21 | return returnOne, returnTwo, nil 22 | } 23 | -------------------------------------------------------------------------------- /_examples/booktest/.gitignore: -------------------------------------------------------------------------------- 1 | /booktest 2 | /booktest.exe 3 | -------------------------------------------------------------------------------- /_examples/booktest/README.md: -------------------------------------------------------------------------------- 1 | # About 2 | 3 | This `booktest` directory contains the canonical `dbtpl` example demonstrate an 4 | end-to-end use of `dbtpl`. Generates code from a simple schema and custom query 5 | for each database. Additionally, showcases a practical use of generated Go 6 | code. 7 | 8 | This examples are also used by the `dbtpl` developers to compare generated code 9 | for/between databases and template revisions. 10 | 11 | Contained in this directory is a subdirectory for each supported `` 12 | by `dbtpl`: 13 | 14 | | Database | Generated Code | 15 | | -------------------- | ----------------------- | 16 | | Microsoft SQL Server | [sqlserver](sqlserver/) | 17 | | MySQL | [mysql](mysql/) | 18 | | Oracle | [oracle](oracle/) | 19 | | PostgreSQL | [postgres](postgres/) | 20 | | SQLite3 | [sqlite3](sqlite3/) | 21 | 22 | Each database has a `sql/_schema.sql` and `sql/_query.sql` 23 | containing a basic `authors` and `books` schema, and a custom retrieval query 24 | the database. 25 | 26 | See [`gen.sh`](gen.sh) to see how the various database model code was 27 | generated. 28 | -------------------------------------------------------------------------------- /_examples/booktest/gen.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | SRC=$(realpath $(cd -P "$(dirname "${BASH_SOURCE[0]}")" && pwd)) 4 | 5 | TEST=$(basename $SRC) 6 | 7 | declare -A DSNS 8 | DSNS+=( 9 | [mysql]=my://$TEST:$TEST@localhost/$TEST 10 | [oracle]=or://$TEST:$TEST@localhost/free 11 | [postgres]=pg://$TEST:$TEST@localhost/$TEST 12 | [sqlite3]=sq:$TEST.db 13 | [sqlserver]=ms://$TEST:$TEST@localhost/$TEST 14 | ) 15 | 16 | APPLY=0 17 | BUILD=0 18 | DATABASES="mysql oracle postgres sqlite3 sqlserver" 19 | ARGS=() 20 | 21 | OPTIND=1 22 | while getopts "abd:vD" opt; do 23 | case "$opt" in 24 | a) APPLY=1 ;; 25 | b) BUILD=1 ;; 26 | d) DATABASES=$OPTARG ;; 27 | v) ARGS+=(-v) ;; 28 | D) ARGS+=(-D) ;; 29 | esac 30 | done 31 | 32 | if [ "$BUILD" = "1" ]; then 33 | pushd $SRC/../../ &> /dev/null 34 | (set -x; 35 | go build 36 | ) 37 | popd &> /dev/null 38 | fi 39 | 40 | DBTPLBIN=$(which dbtpl) 41 | if [ -e $SRC/../../dbtpl ]; then 42 | DBTPLBIN=$SRC/../../dbtpl 43 | fi 44 | DBTPLBIN=$(realpath $DBTPLBIN) 45 | 46 | pushd $SRC &> /dev/null 47 | 48 | for TYPE in $DATABASES; do 49 | DB=${DSNS[$TYPE]} 50 | if [ -z "$DB" ]; then 51 | echo "$TYPE has no defined DSN" 52 | exit 1 53 | fi 54 | mkdir -p $TYPE 55 | rm -f $TYPE/*.dbtpl.* 56 | echo "------------------------------------------------------" 57 | echo "$TYPE: $DB" 58 | if [ "$APPLY" = "1" ]; then 59 | if [[ "$TYPE" = "sqlite3" && -f $TEST.db ]]; then 60 | (set -ex; 61 | rm $TEST.db 62 | ) 63 | fi 64 | (set -ex; 65 | $SRC/../createdb.sh -d $TYPE -n $TEST 66 | usql -f sql/${TYPE}_schema.sql $DB 67 | ) 68 | if [ -f sql/${TYPE}_data.sql ]; then 69 | (set -ex; 70 | usql -f sql/${TYPE}_data.sql $DB 71 | ) 72 | fi 73 | fi 74 | (set -ex; 75 | $DBTPLBIN schema $DB -o $TYPE ${ARGS[@]} --go-initialism ISBN 76 | $DBTPLBIN schema $DB -o $TYPE -t createdb ${ARGS[@]} --createdb-fmt="" 77 | $DBTPLBIN schema $DB -o $TYPE -t json ${ARGS[@]} 78 | $DBTPLBIN schema $DB -o $TYPE -t yaml ${ARGS[@]} 79 | $DBTPLBIN schema $DB -o $TYPE -t dot ${ARGS[@]} 80 | $DBTPLBIN query $DB ${ARGS[@]} < sql/${TYPE}_query.sql \ 81 | --go-initialism ISBN \ 82 | -o $TYPE \ 83 | -M \ 84 | -B \ 85 | -2 \ 86 | -T AuthorBookResult \ 87 | --type-comment='{{ . }} is the result of a search.' 88 | go build ./$TYPE 89 | go build 90 | ./$TEST -dsn $DB ${ARGS[@]} 91 | usql -c 'select * from books;' $DB 92 | ) 93 | done 94 | 95 | popd &> /dev/null 96 | -------------------------------------------------------------------------------- /_examples/booktest/main.go: -------------------------------------------------------------------------------- 1 | // Command booktest is an example of using a similar schema on different 2 | // databases. 3 | // 4 | //go:debug x509negativeserial=1 5 | package main 6 | 7 | import ( 8 | "context" 9 | "database/sql" 10 | "flag" 11 | "fmt" 12 | "os" 13 | "os/user" 14 | 15 | // drivers 16 | _ "github.com/go-sql-driver/mysql" 17 | _ "github.com/lib/pq" 18 | _ "github.com/mattn/go-sqlite3" 19 | _ "github.com/microsoft/go-mssqldb" 20 | _ "github.com/sijms/go-ora/v2" 21 | 22 | // models 23 | "github.com/xo/dbtpl/_examples/booktest/mysql" 24 | "github.com/xo/dbtpl/_examples/booktest/oracle" 25 | "github.com/xo/dbtpl/_examples/booktest/postgres" 26 | "github.com/xo/dbtpl/_examples/booktest/sqlite3" 27 | "github.com/xo/dbtpl/_examples/booktest/sqlserver" 28 | 29 | "github.com/xo/dburl" 30 | "github.com/xo/dburl/passfile" 31 | ) 32 | 33 | func main() { 34 | verbose := flag.Bool("v", false, "verbose") 35 | dsn := flag.String("dsn", "", "dsn") 36 | flag.Parse() 37 | if err := run(context.Background(), *verbose, *dsn); err != nil { 38 | fmt.Fprintln(os.Stderr, "error:", err) 39 | os.Exit(1) 40 | } 41 | } 42 | 43 | func run(ctx context.Context, verbose bool, dsn string) error { 44 | if verbose { 45 | logger := func(s string, v ...any) { 46 | fmt.Printf("-------------------------------------\nQUERY: %s\n VAL: %v\n\n", s, v) 47 | } 48 | mysql.SetLogger(logger) 49 | oracle.SetLogger(logger) 50 | postgres.SetLogger(logger) 51 | sqlite3.SetLogger(logger) 52 | sqlserver.SetLogger(logger) 53 | } 54 | v, err := user.Current() 55 | if err != nil { 56 | return err 57 | } 58 | // parse url 59 | u, err := parse(dsn) 60 | if err != nil { 61 | return err 62 | } 63 | // open database 64 | db, err := passfile.OpenURL(u, v.HomeDir, "dbtplpass") 65 | if err != nil { 66 | return err 67 | } 68 | var f func(context.Context, *sql.DB) error 69 | switch u.Driver { 70 | case "mysql": 71 | f = runMysql 72 | case "oracle": 73 | f = runOracle 74 | case "postgres": 75 | f = runPostgres 76 | case "sqlite3": 77 | f = runSqlite3 78 | case "sqlserver": 79 | f = runSqlserver 80 | } 81 | return f(ctx, db) 82 | } 83 | 84 | func parse(dsn string) (*dburl.URL, error) { 85 | v, err := dburl.Parse(dsn) 86 | if err != nil { 87 | return nil, err 88 | } 89 | switch v.Driver { 90 | case "mysql": 91 | q := v.Query() 92 | q.Set("parseTime", "true") 93 | v.RawQuery = q.Encode() 94 | return dburl.Parse(v.String()) 95 | case "sqlite3": 96 | q := v.Query() 97 | q.Set("_loc", "auto") 98 | v.RawQuery = q.Encode() 99 | return dburl.Parse(v.String()) 100 | } 101 | return v, nil 102 | } 103 | -------------------------------------------------------------------------------- /_examples/booktest/mysql/authorbookresult.dbtpl.go: -------------------------------------------------------------------------------- 1 | package mysql 2 | 3 | // Code generated by dbtpl. DO NOT EDIT. 4 | 5 | import ( 6 | "context" 7 | ) 8 | 9 | // AuthorBookResult is the result of a search. 10 | type AuthorBookResult struct { 11 | AuthorID int `json:"author_id"` // author_id 12 | AuthorName string `json:"author_name"` // author_name 13 | BookID int `json:"book_id"` // book_id 14 | BookISBN string `json:"book_isbn"` // book_isbn 15 | BookTitle string `json:"book_title"` // book_title 16 | BookTags string `json:"book_tags"` // book_tags 17 | } 18 | 19 | // AuthorBookResultsByTag runs a custom query, returning results as [AuthorBookResult]. 20 | func AuthorBookResultsByTag(ctx context.Context, db DB, tag string) ([]*AuthorBookResult, error) { 21 | // query 22 | const sqlstr = `SELECT ` + 23 | `a.author_id AS author_id, ` + 24 | `a.name AS author_name, ` + 25 | `b.book_id AS book_id, ` + 26 | `b.isbn AS book_isbn, ` + 27 | `b.title AS book_title, ` + 28 | `b.tags AS book_tags ` + 29 | `FROM books b ` + 30 | `JOIN authors a ON a.author_id = b.author_id ` + 31 | `WHERE b.tags LIKE CONCAT('%', ?, '%')` 32 | // run 33 | logf(sqlstr, tag) 34 | rows, err := db.QueryContext(ctx, sqlstr, tag) 35 | if err != nil { 36 | return nil, logerror(err) 37 | } 38 | defer rows.Close() 39 | // load results 40 | var res []*AuthorBookResult 41 | for rows.Next() { 42 | var abr AuthorBookResult 43 | // scan 44 | if err := rows.Scan(&abr.AuthorID, &abr.AuthorName, &abr.BookID, &abr.BookISBN, &abr.BookTitle, &abr.BookTags); err != nil { 45 | return nil, logerror(err) 46 | } 47 | res = append(res, &abr) 48 | } 49 | if err := rows.Err(); err != nil { 50 | return nil, logerror(err) 51 | } 52 | return res, nil 53 | } 54 | -------------------------------------------------------------------------------- /_examples/booktest/mysql/booktype.dbtpl.go: -------------------------------------------------------------------------------- 1 | package mysql 2 | 3 | // Code generated by dbtpl. DO NOT EDIT. 4 | 5 | import ( 6 | "database/sql/driver" 7 | "fmt" 8 | ) 9 | 10 | // BookType is the 'book_type' enum type from schema 'booktest'. 11 | type BookType uint16 12 | 13 | // BookType values. 14 | const ( 15 | // BookTypeFiction is the 'FICTION' book_type. 16 | BookTypeFiction BookType = 1 17 | // BookTypeNonfiction is the 'NONFICTION' book_type. 18 | BookTypeNonfiction BookType = 2 19 | ) 20 | 21 | // String satisfies the [fmt.Stringer] interface. 22 | func (bt BookType) String() string { 23 | switch bt { 24 | case BookTypeFiction: 25 | return "FICTION" 26 | case BookTypeNonfiction: 27 | return "NONFICTION" 28 | } 29 | return fmt.Sprintf("BookType(%d)", bt) 30 | } 31 | 32 | // MarshalText marshals [BookType] into text. 33 | func (bt BookType) MarshalText() ([]byte, error) { 34 | return []byte(bt.String()), nil 35 | } 36 | 37 | // UnmarshalText unmarshals [BookType] from text. 38 | func (bt *BookType) UnmarshalText(buf []byte) error { 39 | switch str := string(buf); str { 40 | case "FICTION": 41 | *bt = BookTypeFiction 42 | case "NONFICTION": 43 | *bt = BookTypeNonfiction 44 | default: 45 | return ErrInvalidBookType(str) 46 | } 47 | return nil 48 | } 49 | 50 | // Value satisfies the [driver.Valuer] interface. 51 | func (bt BookType) Value() (driver.Value, error) { 52 | return bt.String(), nil 53 | } 54 | 55 | // Scan satisfies the [sql.Scanner] interface. 56 | func (bt *BookType) Scan(v any) error { 57 | switch x := v.(type) { 58 | case []byte: 59 | return bt.UnmarshalText(x) 60 | case string: 61 | return bt.UnmarshalText([]byte(x)) 62 | } 63 | return ErrInvalidBookType(fmt.Sprintf("%T", v)) 64 | } 65 | 66 | // NullBookType represents a null 'book_type' enum for schema 'booktest'. 67 | type NullBookType struct { 68 | BookType BookType 69 | // Valid is true if [BookType] is not null. 70 | Valid bool 71 | } 72 | 73 | // Value satisfies the [driver.Valuer] interface. 74 | func (nbt NullBookType) Value() (driver.Value, error) { 75 | if !nbt.Valid { 76 | return nil, nil 77 | } 78 | return nbt.BookType.Value() 79 | } 80 | 81 | // Scan satisfies the [sql.Scanner] interface. 82 | func (nbt *NullBookType) Scan(v any) error { 83 | if v == nil { 84 | nbt.BookType, nbt.Valid = 0, false 85 | return nil 86 | } 87 | err := nbt.BookType.Scan(v) 88 | nbt.Valid = err == nil 89 | return err 90 | } 91 | 92 | // ErrInvalidBookType is the invalid [BookType] error. 93 | type ErrInvalidBookType string 94 | 95 | // Error satisfies the error interface. 96 | func (err ErrInvalidBookType) Error() string { 97 | return fmt.Sprintf("invalid BookType(%s)", string(err)) 98 | } 99 | -------------------------------------------------------------------------------- /_examples/booktest/mysql/dbtpl.dbtpl.dot: -------------------------------------------------------------------------------- 1 | // Generated by dbtpl for the booktest schema. 2 | digraph booktest { 3 | // Nodes (tables) 4 | "booktest.authors" [ label=< 5 | 6 | 7 | 8 | 9 |
"booktest.authors"
author_id: int
name: varchar
> ] 10 | 11 | "booktest.books" [ label=< 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 |
"booktest.books"
book_id: int
author_id: int
isbn: varchar
book_type: book_type
title: varchar
year: int
available: datetime
description: text
tags: text
> ] 24 | 25 | "booktest.books":"author_id":e -> "booktest.authors":"author_id":w [ 26 | headlabel="books_ibfk_1"] 27 | } 28 | -------------------------------------------------------------------------------- /_examples/booktest/mysql/dbtpl.dbtpl.sql: -------------------------------------------------------------------------------- 1 | -- Generated by dbtpl for the booktest schema. 2 | 3 | -- table authors 4 | CREATE TABLE authors ( 5 | author_id INT(11) AUTO_INCREMENT, 6 | name VARCHAR(255) DEFAULT '' NOT NULL, 7 | PRIMARY KEY (author_id) 8 | ) ENGINE=InnoDB; 9 | 10 | -- index authors_name_idx 11 | CREATE INDEX authors_name_idx ON authors (name); 12 | 13 | -- table books 14 | CREATE TABLE books ( 15 | book_id INT(11) AUTO_INCREMENT, 16 | author_id INT(11) NOT NULL REFERENCES authors (author_id), 17 | isbn VARCHAR(255) DEFAULT '' NOT NULL, 18 | book_type ENUM('FICTION', 'NONFICTION') DEFAULT 'FICTION' NOT NULL, 19 | title VARCHAR(255) DEFAULT '' NOT NULL, 20 | year INT(11) DEFAULT 2000 NOT NULL, 21 | available DATETIME DEFAULT CURRENT_TIMESTAMP NOT NULL, 22 | description TEXT DEFAULT '' NOT NULL, 23 | tags TEXT DEFAULT '' NOT NULL, 24 | UNIQUE (isbn), 25 | PRIMARY KEY (book_id) 26 | ) ENGINE=InnoDB; 27 | 28 | -- index author_id 29 | CREATE INDEX author_id ON books (author_id); 30 | 31 | -- index books_title_idx 32 | CREATE INDEX books_title_idx ON books (title, year); 33 | 34 | -- function say_hello 35 | CREATE FUNCTION say_hello(name VARCHAR(255)) RETURNS VARCHAR(255) 36 | BEGIN 37 | RETURN CONCAT('hello ', name)\; 38 | END; 39 | -------------------------------------------------------------------------------- /_examples/booktest/mysql/sf_sayhello.dbtpl.go: -------------------------------------------------------------------------------- 1 | package mysql 2 | 3 | // Code generated by dbtpl. DO NOT EDIT. 4 | 5 | import ( 6 | "context" 7 | ) 8 | 9 | // SayHello calls the stored function 'booktest.say_hello(varchar) varchar' on db. 10 | func SayHello(ctx context.Context, db DB, name string) (string, error) { 11 | // call booktest.say_hello 12 | const sqlstr = `SELECT booktest.say_hello(?)` 13 | // run 14 | var r0 string 15 | logf(sqlstr, name) 16 | if err := db.QueryRowContext(ctx, sqlstr, name).Scan(&r0); err != nil { 17 | return "", logerror(err) 18 | } 19 | return r0, nil 20 | } 21 | -------------------------------------------------------------------------------- /_examples/booktest/oracle/authorbookresult.dbtpl.go: -------------------------------------------------------------------------------- 1 | package oracle 2 | 3 | // Code generated by dbtpl. DO NOT EDIT. 4 | 5 | import ( 6 | "context" 7 | ) 8 | 9 | // AuthorBookResult is the result of a search. 10 | type AuthorBookResult struct { 11 | AuthorID int `json:"author_id"` // author_id 12 | AuthorName string `json:"author_name"` // author_name 13 | BookID int `json:"book_id"` // book_id 14 | BookISBN string `json:"book_isbn"` // book_isbn 15 | BookTitle string `json:"book_title"` // book_title 16 | BookTags string `json:"book_tags"` // book_tags 17 | } 18 | 19 | // AuthorBookResultsByTags runs a custom query, returning results as [AuthorBookResult]. 20 | func AuthorBookResultsByTags(ctx context.Context, db DB, tags string) ([]*AuthorBookResult, error) { 21 | // query 22 | const sqlstr = `SELECT ` + 23 | `a.author_id AS author_id, ` + 24 | `a.name AS author_name, ` + 25 | `b.book_id AS book_id, ` + 26 | `b.isbn AS book_isbn, ` + 27 | `b.title AS book_title, ` + 28 | `b.tags AS book_tags ` + 29 | `FROM books b ` + 30 | `JOIN authors a ON a.author_id = b.author_id ` + 31 | `WHERE b.tags LIKE '%' || :1 || '%'` 32 | // run 33 | logf(sqlstr, tags) 34 | rows, err := db.QueryContext(ctx, sqlstr, tags) 35 | if err != nil { 36 | return nil, logerror(err) 37 | } 38 | defer rows.Close() 39 | // load results 40 | var res []*AuthorBookResult 41 | for rows.Next() { 42 | var abr AuthorBookResult 43 | // scan 44 | if err := rows.Scan(&abr.AuthorID, &abr.AuthorName, &abr.BookID, &abr.BookISBN, &abr.BookTitle, &abr.BookTags); err != nil { 45 | return nil, logerror(err) 46 | } 47 | res = append(res, &abr) 48 | } 49 | if err := rows.Err(); err != nil { 50 | return nil, logerror(err) 51 | } 52 | return res, nil 53 | } 54 | -------------------------------------------------------------------------------- /_examples/booktest/oracle/dbtpl.dbtpl.dot: -------------------------------------------------------------------------------- 1 | // Generated by dbtpl for the booktest schema. 2 | digraph booktest { 3 | // Nodes (tables) 4 | "booktest.authors" [ label=< 5 | 6 | 7 | 8 | 9 |
"booktest.authors"
author_id: number
name: nvarchar2
> ] 10 | 11 | "booktest.books" [ label=< 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 |
"booktest.books"
book_id: number
author_id: number
isbn: nvarchar2
title: nvarchar2
year: number
available: timestamp with time zone
description: nclob
tags: nclob
> ] 23 | 24 | "booktest.books":"author_id":e -> "booktest.authors":"author_id":w [ 25 | headlabel="books_author_id_fkey"] 26 | } 27 | -------------------------------------------------------------------------------- /_examples/booktest/oracle/dbtpl.dbtpl.sql: -------------------------------------------------------------------------------- 1 | -- Generated by dbtpl for the booktest schema. 2 | 3 | -- table authors 4 | CREATE TABLE authors ( 5 | author_id NUMBER GENERATED ALWAYS AS IDENTITY, 6 | name NVARCHAR2(255) NOT NULL, 7 | CONSTRAINT authors_pkey PRIMARY KEY (author_id) 8 | ); 9 | 10 | -- index authors_name_idx 11 | CREATE INDEX authors_name_idx ON authors (name); 12 | 13 | -- table books 14 | CREATE TABLE books ( 15 | book_id NUMBER GENERATED ALWAYS AS IDENTITY, 16 | author_id NUMBER NOT NULL CONSTRAINT books_author_id_fkey REFERENCES authors (author_id), 17 | isbn NVARCHAR2(255) NOT NULL, 18 | title NVARCHAR2(255) NOT NULL, 19 | year NUMBER NOT NULL, 20 | available TIMESTAMP WITH TIME ZONE NOT NULL, 21 | description NCLOB, 22 | tags NCLOB NOT NULL, 23 | CONSTRAINT books_isbn_key UNIQUE (isbn), 24 | CONSTRAINT books_pkey PRIMARY KEY (book_id) 25 | ); 26 | 27 | -- index books_title_idx 28 | CREATE INDEX books_title_idx ON books (title, year); 29 | 30 | -- function say_hello 31 | CREATE FUNCTION say_hello(name IN NVARCHAR2) RETURN NVARCHAR2 AS 32 | BEGIN 33 | RETURN 'hello ' || name\; 34 | END\;; 35 | -------------------------------------------------------------------------------- /_examples/booktest/oracle/sf_sayhello.dbtpl.go: -------------------------------------------------------------------------------- 1 | package oracle 2 | 3 | // Code generated by dbtpl. DO NOT EDIT. 4 | 5 | import ( 6 | "context" 7 | ) 8 | 9 | // SayHello calls the stored function 'booktest.say_hello(nvarchar2) nvarchar2' on db. 10 | func SayHello(ctx context.Context, db DB, name string) (string, error) { 11 | // call booktest.say_hello 12 | const sqlstr = `SELECT booktest.say_hello(:1) FROM dual` 13 | // run 14 | var r0 string 15 | logf(sqlstr, name) 16 | if err := db.QueryRowContext(ctx, sqlstr, name).Scan(&r0); err != nil { 17 | return "", logerror(err) 18 | } 19 | return r0, nil 20 | } 21 | -------------------------------------------------------------------------------- /_examples/booktest/postgres/authorbookresult.dbtpl.go: -------------------------------------------------------------------------------- 1 | package postgres 2 | 3 | // Code generated by dbtpl. DO NOT EDIT. 4 | 5 | import ( 6 | "context" 7 | 8 | "github.com/lib/pq" 9 | ) 10 | 11 | // AuthorBookResult is the result of a search. 12 | type AuthorBookResult struct { 13 | AuthorID int `json:"author_id"` // author_id 14 | AuthorName string `json:"author_name"` // author_name 15 | BookID int `json:"book_id"` // book_id 16 | BookISBN string `json:"book_isbn"` // book_isbn 17 | BookTitle string `json:"book_title"` // book_title 18 | BookTags pq.StringArray `json:"book_tags"` // book_tags 19 | } 20 | 21 | // AuthorBookResultsByTags runs a custom query, returning results as [AuthorBookResult]. 22 | func AuthorBookResultsByTags(ctx context.Context, db DB, tags pq.StringArray) ([]*AuthorBookResult, error) { 23 | // query 24 | const sqlstr = `SELECT ` + 25 | `a.author_id, ` + // ::integer AS author_id 26 | `a.name, ` + // ::text AS author_name 27 | `b.book_id, ` + // ::integer AS book_id 28 | `b.isbn, ` + // ::text AS book_isbn 29 | `b.title, ` + // ::text AS book_title 30 | `b.tags::text[] AS book_tags ` + 31 | `FROM books b ` + 32 | `JOIN authors a ON a.author_id = b.author_id ` + 33 | `WHERE b.tags && $1::varchar[]` 34 | // run 35 | logf(sqlstr, tags) 36 | rows, err := db.QueryContext(ctx, sqlstr, tags) 37 | if err != nil { 38 | return nil, logerror(err) 39 | } 40 | defer rows.Close() 41 | // load results 42 | var res []*AuthorBookResult 43 | for rows.Next() { 44 | var abr AuthorBookResult 45 | // scan 46 | if err := rows.Scan(&abr.AuthorID, &abr.AuthorName, &abr.BookID, &abr.BookISBN, &abr.BookTitle, &abr.BookTags); err != nil { 47 | return nil, logerror(err) 48 | } 49 | res = append(res, &abr) 50 | } 51 | if err := rows.Err(); err != nil { 52 | return nil, logerror(err) 53 | } 54 | return res, nil 55 | } 56 | -------------------------------------------------------------------------------- /_examples/booktest/postgres/booktype.dbtpl.go: -------------------------------------------------------------------------------- 1 | package postgres 2 | 3 | // Code generated by dbtpl. DO NOT EDIT. 4 | 5 | import ( 6 | "database/sql/driver" 7 | "fmt" 8 | ) 9 | 10 | // BookType is the 'book_type' enum type from schema 'public'. 11 | type BookType uint16 12 | 13 | // BookType values. 14 | const ( 15 | // BookTypeFiction is the 'FICTION' book_type. 16 | BookTypeFiction BookType = 1 17 | // BookTypeNonfiction is the 'NONFICTION' book_type. 18 | BookTypeNonfiction BookType = 2 19 | ) 20 | 21 | // String satisfies the [fmt.Stringer] interface. 22 | func (bt BookType) String() string { 23 | switch bt { 24 | case BookTypeFiction: 25 | return "FICTION" 26 | case BookTypeNonfiction: 27 | return "NONFICTION" 28 | } 29 | return fmt.Sprintf("BookType(%d)", bt) 30 | } 31 | 32 | // MarshalText marshals [BookType] into text. 33 | func (bt BookType) MarshalText() ([]byte, error) { 34 | return []byte(bt.String()), nil 35 | } 36 | 37 | // UnmarshalText unmarshals [BookType] from text. 38 | func (bt *BookType) UnmarshalText(buf []byte) error { 39 | switch str := string(buf); str { 40 | case "FICTION": 41 | *bt = BookTypeFiction 42 | case "NONFICTION": 43 | *bt = BookTypeNonfiction 44 | default: 45 | return ErrInvalidBookType(str) 46 | } 47 | return nil 48 | } 49 | 50 | // Value satisfies the [driver.Valuer] interface. 51 | func (bt BookType) Value() (driver.Value, error) { 52 | return bt.String(), nil 53 | } 54 | 55 | // Scan satisfies the [sql.Scanner] interface. 56 | func (bt *BookType) Scan(v any) error { 57 | switch x := v.(type) { 58 | case []byte: 59 | return bt.UnmarshalText(x) 60 | case string: 61 | return bt.UnmarshalText([]byte(x)) 62 | } 63 | return ErrInvalidBookType(fmt.Sprintf("%T", v)) 64 | } 65 | 66 | // NullBookType represents a null 'book_type' enum for schema 'public'. 67 | type NullBookType struct { 68 | BookType BookType 69 | // Valid is true if [BookType] is not null. 70 | Valid bool 71 | } 72 | 73 | // Value satisfies the [driver.Valuer] interface. 74 | func (nbt NullBookType) Value() (driver.Value, error) { 75 | if !nbt.Valid { 76 | return nil, nil 77 | } 78 | return nbt.BookType.Value() 79 | } 80 | 81 | // Scan satisfies the [sql.Scanner] interface. 82 | func (nbt *NullBookType) Scan(v any) error { 83 | if v == nil { 84 | nbt.BookType, nbt.Valid = 0, false 85 | return nil 86 | } 87 | err := nbt.BookType.Scan(v) 88 | nbt.Valid = err == nil 89 | return err 90 | } 91 | 92 | // ErrInvalidBookType is the invalid [BookType] error. 93 | type ErrInvalidBookType string 94 | 95 | // Error satisfies the error interface. 96 | func (err ErrInvalidBookType) Error() string { 97 | return fmt.Sprintf("invalid BookType(%s)", string(err)) 98 | } 99 | -------------------------------------------------------------------------------- /_examples/booktest/postgres/dbtpl.dbtpl.dot: -------------------------------------------------------------------------------- 1 | // Generated by dbtpl for the public schema. 2 | digraph public { 3 | // Nodes (tables) 4 | "public.authors" [ label=< 5 | 6 | 7 | 8 | 9 |
"public.authors"
author_id: integer
name: character varying
> ] 10 | 11 | "public.books" [ label=< 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 |
"public.books"
book_id: integer
author_id: integer
isbn: character varying
book_type: book_type
title: character varying
year: integer
available: timestamp with time zone
description: text
tags: character varying
> ] 24 | 25 | "public.books":"author_id":e -> "public.authors":"author_id":w [ 26 | headlabel="books_author_id_fkey"] 27 | } 28 | -------------------------------------------------------------------------------- /_examples/booktest/postgres/dbtpl.dbtpl.sql: -------------------------------------------------------------------------------- 1 | -- Generated by dbtpl for the public schema. 2 | 3 | -- enum book_type 4 | CREATE TYPE book_type AS ENUM ( 5 | 'FICTION', 6 | 'NONFICTION' 7 | ); 8 | 9 | -- table authors 10 | CREATE TABLE authors ( 11 | author_id SERIAL, 12 | name VARCHAR(255) DEFAULT '' NOT NULL, 13 | PRIMARY KEY (author_id) 14 | ); 15 | 16 | -- index authors_name_idx 17 | CREATE INDEX authors_name_idx ON authors (name); 18 | 19 | -- table books 20 | CREATE TABLE books ( 21 | book_id SERIAL, 22 | author_id INTEGER NOT NULL REFERENCES authors (author_id), 23 | isbn VARCHAR(255) DEFAULT '' NOT NULL, 24 | book_type BOOK_TYPE DEFAULT 'FICTION' NOT NULL, 25 | title VARCHAR(255) DEFAULT '' NOT NULL, 26 | year INTEGER DEFAULT 2000 NOT NULL, 27 | available TIMESTAMPTZ DEFAULT CURRENT_TIMESTAMP NOT NULL, 28 | description TEXT DEFAULT '' NOT NULL, 29 | tags VARCHAR[] DEFAULT '{}' NOT NULL, 30 | UNIQUE (isbn), 31 | PRIMARY KEY (book_id) 32 | ); 33 | 34 | -- index books_title_idx 35 | CREATE INDEX books_title_idx ON books (title, year); 36 | 37 | -- function say_hello 38 | CREATE FUNCTION say_hello(name VARCHAR) RETURNS VARCHAR AS $$ 39 | BEGIN 40 | RETURN 'hello ' || name; 41 | END; 42 | $$ LANGUAGE plpgsql; 43 | -------------------------------------------------------------------------------- /_examples/booktest/postgres/sf_sayhello.dbtpl.go: -------------------------------------------------------------------------------- 1 | package postgres 2 | 3 | // Code generated by dbtpl. DO NOT EDIT. 4 | 5 | import ( 6 | "context" 7 | ) 8 | 9 | // SayHello calls the stored function 'public.say_hello(character varying) character varying' on db. 10 | func SayHello(ctx context.Context, db DB, name string) (string, error) { 11 | // call public.say_hello 12 | const sqlstr = `SELECT * FROM public.say_hello($1)` 13 | // run 14 | var r0 string 15 | logf(sqlstr, name) 16 | if err := db.QueryRowContext(ctx, sqlstr, name).Scan(&r0); err != nil { 17 | return "", logerror(err) 18 | } 19 | return r0, nil 20 | } 21 | -------------------------------------------------------------------------------- /_examples/booktest/sql/mysql_query.sql: -------------------------------------------------------------------------------- 1 | SELECT 2 | a.author_id AS author_id, 3 | a.name AS author_name, 4 | b.book_id AS book_id, 5 | b.isbn AS book_isbn, 6 | b.title AS book_title, 7 | b.tags AS book_tags 8 | FROM books b 9 | JOIN authors a ON a.author_id = b.author_id 10 | WHERE b.tags LIKE CONCAT('%', %%tag string%%, '%') 11 | -------------------------------------------------------------------------------- /_examples/booktest/sql/mysql_schema.sql: -------------------------------------------------------------------------------- 1 | CREATE TABLE authors ( 2 | author_id INTEGER AUTO_INCREMENT NOT NULL PRIMARY KEY, 3 | name VARCHAR(255) DEFAULT '' NOT NULL 4 | ) ENGINE=InnoDB; 5 | 6 | CREATE INDEX authors_name_idx ON authors (name); 7 | 8 | CREATE TABLE books ( 9 | book_id INTEGER AUTO_INCREMENT NOT NULL PRIMARY KEY, 10 | author_id INTEGER NOT NULL REFERENCES authors (author_id), 11 | isbn VARCHAR(255) DEFAULT '' NOT NULL UNIQUE, 12 | book_type ENUM('FICTION', 'NONFICTION') DEFAULT 'FICTION' NOT NULL, 13 | title VARCHAR(255) DEFAULT '' NOT NULL, 14 | year INTEGER DEFAULT 2000 NOT NULL, 15 | available DATETIME DEFAULT CURRENT_TIMESTAMP NOT NULL, 16 | description TEXT DEFAULT '' NOT NULL, 17 | tags TEXT DEFAULT '' NOT NULL 18 | ) ENGINE=InnoDB; 19 | 20 | CREATE INDEX books_title_idx ON books (title, year); 21 | 22 | CREATE FUNCTION say_hello(name VARCHAR(255)) RETURNS VARCHAR(255) 23 | DETERMINISTIC 24 | BEGIN 25 | RETURN CONCAT('hello ', name)\; 26 | END; 27 | -------------------------------------------------------------------------------- /_examples/booktest/sql/oracle_query.sql: -------------------------------------------------------------------------------- 1 | SELECT 2 | a.author_id AS author_id, 3 | a.name AS author_name, 4 | b.book_id AS book_id, 5 | b.isbn AS book_isbn, 6 | b.title AS book_title, 7 | b.tags AS book_tags 8 | FROM books b 9 | JOIN authors a ON a.author_id = b.author_id 10 | WHERE b.tags LIKE '%' || %%tags string%% || '%' 11 | -------------------------------------------------------------------------------- /_examples/booktest/sql/oracle_schema.sql: -------------------------------------------------------------------------------- 1 | CREATE TABLE authors ( 2 | author_id INTEGER GENERATED ALWAYS AS IDENTITY NOT NULL CONSTRAINT authors_pkey PRIMARY KEY, 3 | name NVARCHAR2(255) DEFAULT '' NOT NULL 4 | ); 5 | 6 | CREATE INDEX authors_name_idx ON authors (name); 7 | 8 | CREATE TABLE books ( 9 | book_id INTEGER GENERATED ALWAYS AS IDENTITY NOT NULL CONSTRAINT books_pkey PRIMARY KEY, 10 | author_id INTEGER NOT NULL CONSTRAINT books_author_id_fkey REFERENCES authors (author_id), 11 | isbn NVARCHAR2(255) DEFAULT '' NOT NULL CONSTRAINT books_isbn_key UNIQUE, 12 | title NVARCHAR2(255) DEFAULT '' NOT NULL, 13 | year INTEGER DEFAULT 2000 NOT NULL, 14 | available TIMESTAMP WITH TIME ZONE DEFAULT CURRENT_TIMESTAMP NOT NULL, 15 | description NCLOB, 16 | tags NCLOB DEFAULT '' NOT NULL 17 | ); 18 | 19 | CREATE INDEX books_title_idx ON books (title, year); 20 | 21 | CREATE FUNCTION say_hello(name IN NVARCHAR2) RETURN NVARCHAR2 AS 22 | BEGIN 23 | RETURN 'hello ' || name\; 24 | END\;; 25 | -------------------------------------------------------------------------------- /_examples/booktest/sql/postgres_query.sql: -------------------------------------------------------------------------------- 1 | SELECT 2 | a.author_id::integer AS author_id, 3 | a.name::text AS author_name, 4 | b.book_id::integer AS book_id, 5 | b.isbn::text AS book_isbn, 6 | b.title::text AS book_title, 7 | b.tags::text[] AS book_tags 8 | FROM books b 9 | JOIN authors a ON a.author_id = b.author_id 10 | WHERE b.tags && %%tags pq.StringArray%%::varchar[] 11 | -------------------------------------------------------------------------------- /_examples/booktest/sql/postgres_schema.sql: -------------------------------------------------------------------------------- 1 | CREATE TABLE authors ( 2 | author_id SERIAL PRIMARY KEY, 3 | name VARCHAR(255) NOT NULL DEFAULT '' 4 | ); 5 | 6 | CREATE INDEX authors_name_idx ON authors (name); 7 | 8 | CREATE TYPE book_type AS ENUM ( 9 | 'FICTION', 10 | 'NONFICTION' 11 | ); 12 | 13 | CREATE TABLE books ( 14 | book_id SERIAL PRIMARY KEY, 15 | author_id INTEGER NOT NULL REFERENCES authors (author_id), 16 | isbn VARCHAR(255) DEFAULT '' NOT NULL UNIQUE, 17 | book_type book_type DEFAULT 'FICTION' NOT NULL, 18 | title VARCHAR(255) DEFAULT '' NOT NULL, 19 | year INTEGER DEFAULT 2000 NOT NULL, 20 | available TIMESTAMP WITH TIME ZONE DEFAULT CURRENT_TIMESTAMP NOT NULL, 21 | description TEXT DEFAULT '' NOT NULL, 22 | tags VARCHAR[] DEFAULT '{}' NOT NULL 23 | ); 24 | 25 | CREATE INDEX books_title_idx ON books (title, year); 26 | 27 | CREATE FUNCTION say_hello(name VARCHAR(255)) RETURNS VARCHAR(255) AS $$ 28 | BEGIN 29 | RETURN 'hello ' || name; 30 | END; 31 | $$ LANGUAGE plpgsql; 32 | -------------------------------------------------------------------------------- /_examples/booktest/sql/sqlite3_query.sql: -------------------------------------------------------------------------------- 1 | SELECT 2 | a.author_id, 3 | a.name AS author_name, 4 | b.book_id, 5 | b.isbn AS book_isbn, 6 | b.title AS book_title, 7 | b.tags AS book_tags 8 | FROM books b 9 | JOIN authors a ON a.author_id = b.author_id 10 | WHERE b.tags LIKE '%' || %%tag string%% || '%' 11 | -------------------------------------------------------------------------------- /_examples/booktest/sql/sqlite3_schema.sql: -------------------------------------------------------------------------------- 1 | CREATE TABLE authors ( 2 | author_id INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT, 3 | name VARCHAR(255) DEFAULT '' NOT NULL 4 | ); 5 | 6 | CREATE INDEX authors_name_idx ON authors (name); 7 | 8 | CREATE TABLE books ( 9 | book_id INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT, 10 | author_id INTEGER NOT NULL REFERENCES authors (author_id), 11 | isbn VARCHAR(255) DEFAULT '' NOT NULL UNIQUE, 12 | title VARCHAR(255) DEFAULT '' NOT NULL, 13 | year INTEGER DEFAULT 2000 NOT NULL, 14 | available TIMESTAMP DEFAULT (STRFTIME('%Y-%m-%dT%H:%M:%fZ', 'NOW')) NOT NULL, 15 | description TEXT DEFAULT '' NOT NULL, 16 | tags TEXT DEFAULT '{}' NOT NULL 17 | ); 18 | 19 | CREATE INDEX books_title_idx ON books (title, year); 20 | -------------------------------------------------------------------------------- /_examples/booktest/sql/sqlserver_query.sql: -------------------------------------------------------------------------------- 1 | SELECT 2 | a.author_id AS author_id, 3 | a.name AS author_name, 4 | b.book_id AS book_id, 5 | b.isbn AS book_isbn, 6 | b.title AS book_title, 7 | b.tags AS book_tags 8 | FROM books b 9 | JOIN authors a ON a.author_id = b.author_id 10 | WHERE b.tags LIKE '%' + %%tags string%% + '%' 11 | -------------------------------------------------------------------------------- /_examples/booktest/sql/sqlserver_schema.sql: -------------------------------------------------------------------------------- 1 | CREATE TABLE authors ( 2 | author_id INTEGER NOT NULL IDENTITY(1,1) CONSTRAINT authors_pkey PRIMARY KEY, 3 | name VARCHAR(255) NOT NULL DEFAULT '' 4 | ); 5 | 6 | CREATE INDEX authors_name_idx ON authors (name); 7 | 8 | CREATE TABLE books ( 9 | book_id INTEGER NOT NULL IDENTITY CONSTRAINT books_pkey PRIMARY KEY, 10 | author_id INTEGER NOT NULL CONSTRAINT books_author_id_fkey FOREIGN KEY REFERENCES authors (author_id), 11 | isbn NVARCHAR(255) NOT NULL DEFAULT '' CONSTRAINT books_isbn_key UNIQUE, 12 | title NVARCHAR(255) NOT NULL DEFAULT '', 13 | year INTEGER NOT NULL DEFAULT 2000, 14 | available DATETIME2 DEFAULT CURRENT_TIMESTAMP NOT NULL, 15 | description NTEXT DEFAULT '' NOT NULL, 16 | tags TEXT DEFAULT '' NOT NULL 17 | ); 18 | 19 | CREATE INDEX books_title_idx ON books (title, year); 20 | 21 | CREATE PROCEDURE say_hello @name NVARCHAR(255), @result NVARCHAR(255) OUTPUT AS 22 | BEGIN 23 | SELECT @result = CONCAT('hello ', @name)\; 24 | END; 25 | -------------------------------------------------------------------------------- /_examples/booktest/sqlite3/authorbookresult.dbtpl.go: -------------------------------------------------------------------------------- 1 | package sqlite3 2 | 3 | // Code generated by dbtpl. DO NOT EDIT. 4 | 5 | import ( 6 | "context" 7 | ) 8 | 9 | // AuthorBookResult is the result of a search. 10 | type AuthorBookResult struct { 11 | AuthorID int `json:"author_id"` // author_id 12 | AuthorName string `json:"author_name"` // author_name 13 | BookID int `json:"book_id"` // book_id 14 | BookISBN string `json:"book_isbn"` // book_isbn 15 | BookTitle string `json:"book_title"` // book_title 16 | BookTags string `json:"book_tags"` // book_tags 17 | } 18 | 19 | // AuthorBookResultsByTag runs a custom query, returning results as [AuthorBookResult]. 20 | func AuthorBookResultsByTag(ctx context.Context, db DB, tag string) ([]*AuthorBookResult, error) { 21 | // query 22 | const sqlstr = `SELECT ` + 23 | `a.author_id, ` + 24 | `a.name AS author_name, ` + 25 | `b.book_id, ` + 26 | `b.isbn AS book_isbn, ` + 27 | `b.title AS book_title, ` + 28 | `b.tags AS book_tags ` + 29 | `FROM books b ` + 30 | `JOIN authors a ON a.author_id = b.author_id ` + 31 | `WHERE b.tags LIKE '%' || $1 || '%'` 32 | // run 33 | logf(sqlstr, tag) 34 | rows, err := db.QueryContext(ctx, sqlstr, tag) 35 | if err != nil { 36 | return nil, logerror(err) 37 | } 38 | defer rows.Close() 39 | // load results 40 | var res []*AuthorBookResult 41 | for rows.Next() { 42 | var abr AuthorBookResult 43 | // scan 44 | if err := rows.Scan(&abr.AuthorID, &abr.AuthorName, &abr.BookID, &abr.BookISBN, &abr.BookTitle, &abr.BookTags); err != nil { 45 | return nil, logerror(err) 46 | } 47 | res = append(res, &abr) 48 | } 49 | if err := rows.Err(); err != nil { 50 | return nil, logerror(err) 51 | } 52 | return res, nil 53 | } 54 | -------------------------------------------------------------------------------- /_examples/booktest/sqlite3/dbtpl.dbtpl.dot: -------------------------------------------------------------------------------- 1 | // Generated by dbtpl for the booktest.db schema. 2 | digraph booktest_db { 3 | // Nodes (tables) 4 | "authors" [ label=< 5 | 6 | 7 | 8 | 9 |
"authors"
author_id: integer
name: varchar
> ] 10 | 11 | "books" [ label=< 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 |
"books"
book_id: integer
author_id: integer
isbn: varchar
title: varchar
year: integer
available: timestamp
description: text
tags: text
> ] 23 | 24 | "books":"author_id":e -> "authors":"author_id":w [ 25 | headlabel="books_author_id_fkey"] 26 | } 27 | -------------------------------------------------------------------------------- /_examples/booktest/sqlite3/dbtpl.dbtpl.sql: -------------------------------------------------------------------------------- 1 | -- Generated by dbtpl for the booktest.db schema. 2 | 3 | -- table authors 4 | CREATE TABLE authors ( 5 | author_id INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT, 6 | name VARCHAR(255) DEFAULT '' NOT NULL 7 | ); 8 | 9 | -- index authors_name_idx 10 | CREATE INDEX authors_name_idx ON authors (name); 11 | 12 | -- table books 13 | CREATE TABLE books ( 14 | book_id INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT, 15 | author_id INTEGER NOT NULL REFERENCES authors (author_id), 16 | isbn VARCHAR(255) DEFAULT '' NOT NULL, 17 | title VARCHAR(255) DEFAULT '' NOT NULL, 18 | year INTEGER DEFAULT 2000 NOT NULL, 19 | available TIMESTAMP DEFAULT (STRFTIME('%Y-%m-%dT%H:%M:%fZ', 'NOW')) NOT NULL, 20 | description TEXT DEFAULT '' NOT NULL, 21 | tags TEXT DEFAULT '{}' NOT NULL, 22 | UNIQUE (isbn) 23 | ); 24 | 25 | -- index books_title_idx 26 | CREATE INDEX books_title_idx ON books (title, year); 27 | -------------------------------------------------------------------------------- /_examples/booktest/sqlserver/authorbookresult.dbtpl.go: -------------------------------------------------------------------------------- 1 | package sqlserver 2 | 3 | // Code generated by dbtpl. DO NOT EDIT. 4 | 5 | import ( 6 | "context" 7 | ) 8 | 9 | // AuthorBookResult is the result of a search. 10 | type AuthorBookResult struct { 11 | AuthorID int `json:"author_id"` // author_id 12 | AuthorName string `json:"author_name"` // author_name 13 | BookID int `json:"book_id"` // book_id 14 | BookISBN string `json:"book_isbn"` // book_isbn 15 | BookTitle string `json:"book_title"` // book_title 16 | BookTags string `json:"book_tags"` // book_tags 17 | } 18 | 19 | // AuthorBookResultsByTags runs a custom query, returning results as [AuthorBookResult]. 20 | func AuthorBookResultsByTags(ctx context.Context, db DB, tags string) ([]*AuthorBookResult, error) { 21 | // query 22 | const sqlstr = `SELECT ` + 23 | `a.author_id AS author_id, ` + 24 | `a.name AS author_name, ` + 25 | `b.book_id AS book_id, ` + 26 | `b.isbn AS book_isbn, ` + 27 | `b.title AS book_title, ` + 28 | `b.tags AS book_tags ` + 29 | `FROM books b ` + 30 | `JOIN authors a ON a.author_id = b.author_id ` + 31 | `WHERE b.tags LIKE '%' + @p1 + '%'` 32 | // run 33 | logf(sqlstr, tags) 34 | rows, err := db.QueryContext(ctx, sqlstr, tags) 35 | if err != nil { 36 | return nil, logerror(err) 37 | } 38 | defer rows.Close() 39 | // load results 40 | var res []*AuthorBookResult 41 | for rows.Next() { 42 | var abr AuthorBookResult 43 | // scan 44 | if err := rows.Scan(&abr.AuthorID, &abr.AuthorName, &abr.BookID, &abr.BookISBN, &abr.BookTitle, &abr.BookTags); err != nil { 45 | return nil, logerror(err) 46 | } 47 | res = append(res, &abr) 48 | } 49 | if err := rows.Err(); err != nil { 50 | return nil, logerror(err) 51 | } 52 | return res, nil 53 | } 54 | -------------------------------------------------------------------------------- /_examples/booktest/sqlserver/dbtpl.dbtpl.dot: -------------------------------------------------------------------------------- 1 | // Generated by dbtpl for the booktest schema. 2 | digraph booktest { 3 | // Nodes (tables) 4 | "booktest.authors" [ label=< 5 | 6 | 7 | 8 | 9 |
"booktest.authors"
author_id: int
name: varchar
> ] 10 | 11 | "booktest.books" [ label=< 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 |
"booktest.books"
book_id: int
author_id: int
isbn: nvarchar
title: nvarchar
year: int
available: datetime2
description: ntext
tags: text
> ] 23 | 24 | "booktest.books":"author_id":e -> "booktest.authors":"author_id":w [ 25 | headlabel="books_author_id_fkey"] 26 | } 27 | -------------------------------------------------------------------------------- /_examples/booktest/sqlserver/dbtpl.dbtpl.sql: -------------------------------------------------------------------------------- 1 | -- Generated by dbtpl for the booktest schema. 2 | 3 | -- table authors 4 | CREATE TABLE authors ( 5 | author_id INT IDENTITY(1, 1), 6 | name VARCHAR(255) DEFAULT ('') NOT NULL, 7 | CONSTRAINT authors_pkey PRIMARY KEY (author_id) 8 | ); 9 | 10 | -- index authors_name_idx 11 | CREATE INDEX authors_name_idx ON authors (name); 12 | 13 | -- table books 14 | CREATE TABLE books ( 15 | book_id INT IDENTITY(1, 1), 16 | author_id INT NOT NULL CONSTRAINT books_author_id_fkey REFERENCES authors (author_id), 17 | isbn NVARCHAR(255) DEFAULT ('') NOT NULL, 18 | title NVARCHAR(255) DEFAULT ('') NOT NULL, 19 | year INT DEFAULT ((2000)) NOT NULL, 20 | available DATETIME2 DEFAULT (getdate()) NOT NULL, 21 | description NTEXT DEFAULT ('') NOT NULL, 22 | tags TEXT DEFAULT ('') NOT NULL, 23 | CONSTRAINT books_isbn_key UNIQUE (isbn), 24 | CONSTRAINT books_pkey PRIMARY KEY (book_id) 25 | ); 26 | 27 | -- index books_title_idx 28 | CREATE INDEX books_title_idx ON books (title, year); 29 | 30 | -- procedure say_hello 31 | CREATE PROCEDURE say_hello @name NVARCHAR(255), @result NVARCHAR(255) OUTPUT AS 32 | BEGIN 33 | SELECT @result = CONCAT('hello ', @name)\; 34 | END; 35 | -------------------------------------------------------------------------------- /_examples/booktest/sqlserver/sp_sayhello.dbtpl.go: -------------------------------------------------------------------------------- 1 | package sqlserver 2 | 3 | // Code generated by dbtpl. DO NOT EDIT. 4 | 5 | import ( 6 | "context" 7 | "database/sql" 8 | ) 9 | 10 | // SayHello calls the stored procedure 'booktest.say_hello(nvarchar) nvarchar' on db. 11 | func SayHello(ctx context.Context, db DB, name string) (string, error) { 12 | // call booktest.say_hello 13 | const sqlstr = `booktest.say_hello` 14 | // run 15 | var result string 16 | logf(sqlstr, name) 17 | if _, err := db.ExecContext(ctx, sqlstr, sql.Named("name", name), sql.Named("result", sql.Out{Dest: &result})); err != nil { 18 | return "", logerror(err) 19 | } 20 | return result, nil 21 | } 22 | -------------------------------------------------------------------------------- /_examples/createdb.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | SRC=$(realpath $(cd -P "$(dirname "${BASH_SOURCE[0]}")" && pwd)) 4 | 5 | DATABASES="mysql oracle postgres sqlite3 sqlserver" 6 | NAME= 7 | PASS= 8 | DATAFILE= 9 | 10 | declare -A INIT 11 | INIT+=( 12 | [mysql]=my://localhost/ 13 | [oracle]=or://localhost/free 14 | [postgres]=pg://localhost/ 15 | [sqlserver]=ms://localhost/ 16 | ) 17 | 18 | OPTIND=1 19 | while getopts "d:f:n:" opt; do 20 | case "$opt" in 21 | d) DATABASES=$OPTARG ;; 22 | f) DATAFILE=$OPTARG ;; 23 | n) NAME=$OPTARG ;; 24 | p) PASS=$OPTARG ;; 25 | esac 26 | done 27 | 28 | if [ -z "$NAME" ]; then 29 | echo "usage: $0 [-d DATABASES] [-f DATAFILE] [-p PASS] -n " 30 | exit 1 31 | fi 32 | 33 | if [ -z "$PASS" ]; then 34 | PASS="$NAME" 35 | fi 36 | 37 | pushd $SRC &> /dev/null 38 | for TYPE in $DATABASES; do 39 | if [ "$TYPE" = "sqlite3" ]; then 40 | if [ -e "$NAME.db" ]; then 41 | (set -x; 42 | rm "$NAME.db" 43 | ) 44 | fi 45 | continue 46 | fi 47 | DB=${INIT[$TYPE]} 48 | if [[ -z "$DATAFILE" && "$TYPE" = "oracle" ]]; then 49 | DATAFILE=$(printf '/opt/oracle/oradata/FREE/%s.dbf' "$NAME") 50 | elif [[ "$TYPE" != "oracle" ]]; then 51 | DATAFILE="" 52 | fi 53 | (set -x; 54 | usql $DB \ 55 | --set=DB="$DB" \ 56 | --set=NAME="$NAME" \ 57 | --set=PASS="$PASS" \ 58 | --set=DATAFILE="$DATAFILE" \ 59 | -c "\\echo 'DB: ':'DB'" \ 60 | -c "\\echo 'NAME: ':'NAME'" \ 61 | -c "\\echo 'PASS: ':'PASS'" \ 62 | -c "\\echo 'DATAFILE: ':'DATAFILE'" \ 63 | -f $SRC/init/$TYPE.sql 64 | ) 65 | done 66 | popd &> /dev/null 67 | -------------------------------------------------------------------------------- /_examples/django/.gitignore: -------------------------------------------------------------------------------- 1 | /django 2 | /django.exe 3 | /*/proj 4 | -------------------------------------------------------------------------------- /_examples/django/Pipfile: -------------------------------------------------------------------------------- 1 | [[source]] 2 | url = "https://pypi.org/simple" 3 | verify_ssl = true 4 | name = "pypi" 5 | 6 | [packages] 7 | django = ">=5" 8 | cx-oracle = "*" 9 | psycopg2 = "*" 10 | mysqlclient = "*" 11 | mssql-django = "*" 12 | lock = "*" 13 | pyodbc = "*" 14 | 15 | [dev-packages] 16 | 17 | [requires] 18 | python_version = "3" 19 | 20 | [pipenv] 21 | allow_prereleases = true 22 | -------------------------------------------------------------------------------- /_examples/django/README.md: -------------------------------------------------------------------------------- 1 | # About django examples 2 | 3 | The `django` example is the result of running `dbtpl` against all the supported 4 | databases that Django and `dbtpl` supports, with Django models similar to the 5 | `dbtpl` booktest schema. 6 | 7 | ## Setup 8 | 9 | Install packages: 10 | 11 | ```sh 12 | # install mysql, postgres, sqlite3 dependencies 13 | $ sudo apt install libpq-dev libmysqlclient-dev libsqlite3-dev 14 | 15 | # install sqlserver dependenices 16 | # manually add the microsoft-prod ppa -- see: https://docs.microsoft.com/en-us/sql/connect/odbc/linux-mac/installing-the-microsoft-odbc-driver-for-sql-server?view=sql-server-ver15 17 | $ sudo apt install unixodbc unixodbc-dev odbcinst msodbcsql18 18 | 19 | # aur dependencies: 20 | $ yay -S python-pip python-pipenv unixodbc msodbcsql oracle-instantclient-sdk oracle-instantclient-sqlplus oracle-instantclient-tools 21 | 22 | # ensure odbcinst.ini has the relevant sqlserver entry 23 | $ cat /etc/odbcinst.ini 24 | [ODBC Driver 18 for SQL Server] 25 | Description=Microsoft ODBC Driver 18 for SQL Server 26 | Driver=/opt/microsoft/msodbcsql18/lib64/libmsodbcsql-18.0.so.1.1 27 | UsageCount=1 28 | 29 | # install oracle dependencies 30 | $ cd /path/to/usql/contrib/godror 31 | $ sudo ./grab-instantclient.sh 32 | 33 | # fix oob issue with oracle driver 34 | $ cd /path/to/usql/contrib/godror 35 | $ ./fix-oob-config.sh 36 | 37 | # install pipenv 38 | $ pip install --user pipenv 39 | 40 | # install packages 41 | $ pipenv install 42 | 43 | # update packages 44 | $ pipenv update 45 | ``` 46 | -------------------------------------------------------------------------------- /_examples/django/gen.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | SRC=$(realpath $(cd -P "$(dirname "${BASH_SOURCE[0]}")" && pwd)) 4 | 5 | TEST=$(basename $SRC) 6 | 7 | declare -A DSNS 8 | DSNS+=( 9 | [mysql]=my://$TEST:$TEST@localhost/$TEST 10 | [oracle]=or://$TEST:$TEST@localhost/free 11 | [postgres]=pg://$TEST:$TEST@localhost/$TEST 12 | [sqlite3]=sq:$TEST.db 13 | [sqlserver]=ms://$TEST:$TEST@localhost/$TEST 14 | ) 15 | 16 | APPLY=0 17 | BUILD=0 18 | DATABASES="mysql oracle postgres sqlite3 sqlserver" 19 | ARGS=() 20 | 21 | OPTIND=1 22 | while getopts "abd:v" opt; do 23 | case "$opt" in 24 | a) APPLY=1 ;; 25 | b) BUILD=1 ;; 26 | d) DATABASES=$OPTARG ;; 27 | v) ARGS+=(-v) ;; 28 | esac 29 | done 30 | 31 | if [ "$BUILD" = "1" ]; then 32 | pushd $SRC/../../ &> /dev/null 33 | (set -x; 34 | go build 35 | ) 36 | popd &> /dev/null 37 | fi 38 | 39 | DBTPLBIN=$(which dbtpl) 40 | if [ -e $SRC/../../dbtpl ]; then 41 | DBTPLBIN=$SRC/../../dbtpl 42 | fi 43 | DBTPLBIN=$(realpath $DBTPLBIN) 44 | 45 | pushd $SRC &> /dev/null 46 | 47 | for TYPE in $DATABASES; do 48 | DB=${DSNS[$TYPE]} 49 | if [ -z "$DB" ]; then 50 | echo "$TYPE has no defined DSN" 51 | exit 1 52 | fi 53 | mkdir -p $TYPE 54 | rm -f $TYPE/*.dbtpl.* 55 | echo "------------------------------------------------------" 56 | echo "$TYPE: $DB" 57 | if [ "$APPLY" = "1" ]; then 58 | if [[ "$TYPE" = "sqlite3" && -f $TEST.db ]]; then 59 | (set -ex; 60 | rm $TEST.db 61 | ) 62 | fi 63 | (set -ex; 64 | $SRC/../createdb.sh -d $TYPE -n $TEST 65 | ./init.sh -d $TYPE 66 | ) 67 | if [ -f sql/${TYPE}_data.sql ]; then 68 | (set -ex; 69 | usql -f sql/${TYPE}_data.sql $DB 70 | ) 71 | fi 72 | if [ -f sql/${TYPE}_post.sql ]; then 73 | (set -ex; 74 | usql -f sql/${TYPE}_post.sql $DB 75 | ) 76 | fi 77 | fi 78 | (set -ex; 79 | $DBTPLBIN schema $DB -o $TYPE ${ARGS[@]} --go-initialism ISBN 80 | $DBTPLBIN schema $DB -o $TYPE -t createdb ${ARGS[@]} --createdb-fmt="" 81 | $DBTPLBIN schema $DB -o $TYPE -t json ${ARGS[@]} 82 | $DBTPLBIN schema $DB -o $TYPE -t yaml ${ARGS[@]} 83 | $DBTPLBIN schema $DB -o $TYPE -t dot ${ARGS[@]} 84 | go build ./$TYPE 85 | go build 86 | ./$TEST -dsn $DB ${ARGS[@]} 87 | ) 88 | done 89 | 90 | popd &> /dev/null 91 | -------------------------------------------------------------------------------- /_examples/django/init.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | SRC=$(realpath $(cd -P "$(dirname "${BASH_SOURCE[0]}")" && pwd)) 4 | 5 | set -e 6 | 7 | PROJECT=proj 8 | APP=booktest 9 | APP_CLASS="${APP^}" 10 | TYPE= 11 | 12 | OPTIND=1 13 | while getopts "d:" opt; do 14 | case "$opt" in 15 | d) TYPE=$OPTARG ;; 16 | esac 17 | done 18 | 19 | if [ -z "$TYPE" ]; then 20 | echo "usage: $0 -d " 21 | exit 1 22 | fi 23 | 24 | declare -A CONFIG 25 | case $TYPE in 26 | mysql) 27 | CONFIG+=( 28 | [engine]=django.db.backends.mysql 29 | [host]=127.0.0.1 30 | [user]=django 31 | [pass]=django 32 | [name]=django 33 | ) 34 | ;; 35 | oracle) 36 | CONFIG+=( 37 | [engine]=django.db.backends.oracle 38 | [user]=django 39 | [pass]=django 40 | [name]=127.0.0.1:1521/free 41 | ) 42 | ;; 43 | postgres) 44 | CONFIG+=( 45 | [engine]=django.db.backends.postgresql 46 | [host]=127.0.0.1 47 | [user]=django 48 | [pass]=django 49 | [name]=django 50 | ) 51 | ;; 52 | sqlite3) 53 | CONFIG+=( 54 | [engine]=django.db.backends.sqlite3 55 | [name]=$SRC/django.db 56 | ) 57 | ;; 58 | sqlserver) 59 | CONFIG+=( 60 | [engine]=mssql 61 | [host]=127.0.0.1 62 | [user]=django 63 | [pass]=django 64 | [name]=django 65 | [options]="'OPTIONS': {'driver': 'ODBC Driver 18 for SQL Server', 'extra_params': 'Encrypt=no'}," 66 | ) 67 | ;; 68 | esac 69 | 70 | mkdir -p $SRC/$TYPE 71 | pushd $SRC/$TYPE &> /dev/null 72 | # remove 73 | (set -x; 74 | rm -rf $PROJECT 75 | ) 76 | # create django project 77 | (set -x; 78 | pipenv run django-admin startproject $PROJECT 79 | ) 80 | popd &> /dev/null 81 | 82 | pushd $SRC/$TYPE/$PROJECT &> /dev/null 83 | # create app 84 | (set -x; 85 | pipenv run ./manage.py startapp $APP 86 | ) 87 | 88 | # config database 89 | DATABASES=$(cat << END 90 | DATABASES = { 91 | 'default': { 92 | 'ENGINE': '${CONFIG[engine]}', 93 | 'HOST': '${CONFIG[host]}', 94 | 'PORT': '${CONFIG[port]}', 95 | 'USER': '${CONFIG[user]}', 96 | 'PASSWORD': '${CONFIG[pass]}', 97 | 'NAME': '${CONFIG[name]}', 98 | ${CONFIG[options]} 99 | }, 100 | } 101 | END 102 | ) 103 | echo "$DATABASES" 104 | awk -i inplace -v x="$DATABASES" '/DATABASES\s*=\s*\{/{f=1} !f{print} /^}$/{print x; f=0}' $PROJECT/settings.py 105 | 106 | # config models 107 | perl -pi -e "s/^(INSTALLED_APPS.*)/\1\n '$APP.apps.${APP_CLASS}Config',/" $PROJECT/settings.py 108 | cp $SRC/models.py $APP/ 109 | 110 | # migrate 111 | (set -x; 112 | pipenv run ./manage.py makemigrations $APP 113 | pipenv run ./manage.py migrate 114 | ) 115 | popd &> /dev/null 116 | -------------------------------------------------------------------------------- /_examples/django/main.go: -------------------------------------------------------------------------------- 1 | // Command django is an example of using a similar schema on different 2 | // databases. 3 | // 4 | //go:debug x509negativeserial=1 5 | package main 6 | 7 | import ( 8 | "context" 9 | "database/sql" 10 | "flag" 11 | "fmt" 12 | "os" 13 | "os/user" 14 | 15 | // drivers 16 | _ "github.com/go-sql-driver/mysql" 17 | _ "github.com/lib/pq" 18 | _ "github.com/mattn/go-sqlite3" 19 | _ "github.com/microsoft/go-mssqldb" 20 | _ "github.com/sijms/go-ora/v2" 21 | 22 | // models 23 | "github.com/xo/dbtpl/_examples/django/mysql" 24 | "github.com/xo/dbtpl/_examples/django/oracle" 25 | "github.com/xo/dbtpl/_examples/django/postgres" 26 | "github.com/xo/dbtpl/_examples/django/sqlite3" 27 | "github.com/xo/dbtpl/_examples/django/sqlserver" 28 | 29 | "github.com/xo/dburl" 30 | "github.com/xo/dburl/passfile" 31 | ) 32 | 33 | func main() { 34 | verbose := flag.Bool("v", false, "verbose") 35 | dsn := flag.String("dsn", "", "dsn") 36 | flag.Parse() 37 | if err := run(context.Background(), *verbose, *dsn); err != nil { 38 | fmt.Fprintln(os.Stderr, "error:", err) 39 | os.Exit(1) 40 | } 41 | } 42 | 43 | func run(ctx context.Context, verbose bool, dsn string) error { 44 | if verbose { 45 | logger := func(s string, v ...any) { 46 | fmt.Printf("-------------------------------------\nQUERY: %s\n VAL: %v\n\n", s, v) 47 | } 48 | mysql.SetLogger(logger) 49 | oracle.SetLogger(logger) 50 | postgres.SetLogger(logger) 51 | sqlite3.SetLogger(logger) 52 | sqlserver.SetLogger(logger) 53 | } 54 | v, err := user.Current() 55 | if err != nil { 56 | return err 57 | } 58 | // parse url 59 | u, err := parse(dsn) 60 | if err != nil { 61 | return err 62 | } 63 | // open database 64 | db, err := passfile.OpenURL(u, v.HomeDir, "dbtplpass") 65 | if err != nil { 66 | return err 67 | } 68 | var f func(context.Context, *sql.DB) error 69 | switch u.Driver { 70 | case "mysql": 71 | f = runMysql 72 | case "oracle": 73 | f = runOracle 74 | case "postgres": 75 | f = runPostgres 76 | case "sqlite3": 77 | f = runSqlite3 78 | case "sqlserver": 79 | f = runSqlserver 80 | } 81 | return f(ctx, db) 82 | } 83 | 84 | func parse(dsn string) (*dburl.URL, error) { 85 | v, err := dburl.Parse(dsn) 86 | if err != nil { 87 | return nil, err 88 | } 89 | switch v.Driver { 90 | case "mysql": 91 | q := v.Query() 92 | q.Set("parseTime", "true") 93 | v.RawQuery = q.Encode() 94 | return dburl.Parse(v.String()) 95 | case "sqlite3": 96 | q := v.Query() 97 | q.Set("_loc", "auto") 98 | v.RawQuery = q.Encode() 99 | return dburl.Parse(v.String()) 100 | } 101 | return v, nil 102 | } 103 | -------------------------------------------------------------------------------- /_examples/django/models.py: -------------------------------------------------------------------------------- 1 | from django.db import models 2 | from django.utils import timezone 3 | 4 | class Tag(models.Model): 5 | tag_id = models.BigAutoField(primary_key=True) 6 | tag = models.CharField(max_length=50) 7 | class Meta: 8 | db_table = 'tags' 9 | 10 | class Author(models.Model): 11 | author_id = models.BigAutoField(primary_key=True) 12 | name = models.TextField() 13 | class Meta: 14 | db_table = 'authors' 15 | 16 | class Book(models.Model): 17 | FICTION = 1 18 | NONFICTION = 2 19 | BOOK_TYPE_CHOICES = [ 20 | (FICTION, 'FICTION'), 21 | (NONFICTION, 'NONFICTION'), 22 | ] 23 | book_id = models.BigAutoField(primary_key=True) 24 | author = models.ForeignKey(Author, on_delete=models.CASCADE, db_column='books_author_id_fkey') 25 | isbn = models.CharField(max_length=255) 26 | book_type = models.IntegerField(choices = BOOK_TYPE_CHOICES) 27 | title = models.CharField(max_length=255) 28 | year = models.IntegerField(default=2000) 29 | available = models.DateTimeField(default=timezone.now) 30 | tags = models.ManyToManyField(Tag) 31 | class Meta: 32 | db_table = 'books' 33 | -------------------------------------------------------------------------------- /_examples/django/mysql.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "context" 5 | "database/sql" 6 | "fmt" 7 | "os" 8 | "time" 9 | 10 | models "github.com/xo/dbtpl/_examples/django/mysql" 11 | ) 12 | 13 | func runMysql(ctx context.Context, db *sql.DB) error { 14 | now := time.Now() 15 | a := &models.Author{ 16 | Name: "author 1", 17 | } 18 | if err := a.Insert(ctx, db); err != nil { 19 | return err 20 | } 21 | t := &models.Tag{ 22 | Tag: "tag 1", 23 | } 24 | if err := t.Insert(ctx, db); err != nil { 25 | return err 26 | } 27 | b := &models.Book{ 28 | ISBN: "1", 29 | BookType: 1, 30 | Title: "book 1", 31 | Year: now.Year(), 32 | Available: now, 33 | BooksAuthorIDFkey: a.AuthorID, 34 | } 35 | if err := b.Insert(ctx, db); err != nil { 36 | return err 37 | } 38 | bt := &models.BooksTag{ 39 | BookID: b.BookID, 40 | TagID: t.TagID, 41 | } 42 | if err := bt.Insert(ctx, db); err != nil { 43 | return err 44 | } 45 | books, err := models.BooksByBooksAuthorIDFkey(ctx, db, a.AuthorID) 46 | if err != nil { 47 | return err 48 | } 49 | for _, book := range books { 50 | fmt.Fprintf(os.Stdout, "book %d: %q (%s) %d\n", book.BookID, book.Title, book.ISBN, book.Year) 51 | } 52 | return nil 53 | } 54 | -------------------------------------------------------------------------------- /_examples/django/oracle.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "context" 5 | "database/sql" 6 | "fmt" 7 | "os" 8 | "time" 9 | 10 | models "github.com/xo/dbtpl/_examples/django/oracle" 11 | ) 12 | 13 | func runOracle(ctx context.Context, db *sql.DB) error { 14 | now := time.Now() 15 | a := &models.Author{ 16 | Name: sql.NullString{"author 1", true}, 17 | } 18 | if err := a.Insert(ctx, db); err != nil { 19 | return err 20 | } 21 | t := &models.Tag{ 22 | Tag: sql.NullString{"tag 1", true}, 23 | } 24 | if err := t.Insert(ctx, db); err != nil { 25 | return err 26 | } 27 | b := &models.Book{ 28 | ISBN: sql.NullString{"1", true}, 29 | BookType: 1, 30 | Title: sql.NullString{"book 1", true}, 31 | Year: int64(now.Year()), 32 | Available: now, 33 | BooksAuthorIDFkey: a.AuthorID, 34 | } 35 | if err := b.Insert(ctx, db); err != nil { 36 | return err 37 | } 38 | bt := &models.BooksTag{ 39 | BookID: b.BookID, 40 | TagID: t.TagID, 41 | } 42 | if err := bt.Insert(ctx, db); err != nil { 43 | return err 44 | } 45 | books, err := models.BooksByBooksAuthorIDFkey(ctx, db, a.AuthorID) 46 | if err != nil { 47 | return err 48 | } 49 | for _, book := range books { 50 | fmt.Fprintf(os.Stdout, "book %d: %q (%s) %d\n", book.BookID, book.Title.String, book.ISBN.String, book.Year) 51 | } 52 | return nil 53 | } 54 | -------------------------------------------------------------------------------- /_examples/django/postgres.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "context" 5 | "database/sql" 6 | "fmt" 7 | "os" 8 | "time" 9 | 10 | models "github.com/xo/dbtpl/_examples/django/postgres" 11 | ) 12 | 13 | func runPostgres(ctx context.Context, db *sql.DB) error { 14 | now := time.Now() 15 | a := &models.Author{ 16 | Name: "author 1", 17 | } 18 | if err := a.Insert(ctx, db); err != nil { 19 | return err 20 | } 21 | t := &models.Tag{ 22 | Tag: "tag 1", 23 | } 24 | if err := t.Insert(ctx, db); err != nil { 25 | return err 26 | } 27 | b := &models.Book{ 28 | ISBN: "1", 29 | BookType: 1, 30 | Title: "book 1", 31 | Year: now.Year(), 32 | Available: now, 33 | BooksAuthorIDFkey: a.AuthorID, 34 | } 35 | if err := b.Insert(ctx, db); err != nil { 36 | return err 37 | } 38 | bt := &models.BooksTag{ 39 | BookID: b.BookID, 40 | TagID: t.TagID, 41 | } 42 | if err := bt.Insert(ctx, db); err != nil { 43 | return err 44 | } 45 | books, err := models.BooksByBooksAuthorIDFkey(ctx, db, a.AuthorID) 46 | if err != nil { 47 | return err 48 | } 49 | for _, book := range books { 50 | fmt.Fprintf(os.Stdout, "book %d: %q (%s) %d\n", book.BookID, book.Title, book.ISBN, book.Year) 51 | } 52 | return nil 53 | } 54 | -------------------------------------------------------------------------------- /_examples/django/sql/oracle_post.sql: -------------------------------------------------------------------------------- 1 | WITH idx_names AS ( 2 | SELECT 3 | LOWER(i.owner) AS owner, 4 | i.index_name AS index_name, 5 | LOWER(i.table_name) AS table_name, 6 | LISTAGG(LOWER(c.column_name), '_') AS column_names 7 | FROM all_indexes i 8 | JOIN all_ind_columns c 9 | ON i.index_name = c.index_name 10 | WHERE i.owner = 'DJANGO' 11 | GROUP BY i.owner, i.index_name, i.table_name 12 | ) 13 | SELECT 14 | 'ALTER INDEX ' || index_name || ' RENAME TO ' || table_name || '_' || column_names || '_idx' AS cmd 15 | FROM idx_names 16 | WHERE owner = 'django' 17 | AND index_name LIKE 'SYS_%' 18 | \gexec 19 | -------------------------------------------------------------------------------- /_examples/django/sql/postgres_post.sql: -------------------------------------------------------------------------------- 1 | DROP INDEX auth_user_username_6821ab7c_like; 2 | DROP INDEX auth_group_name_a6ea08ec_like; 3 | DROP INDEX django_session_session_key_c0390e0f_like; 4 | -------------------------------------------------------------------------------- /_examples/django/sql/sqlserver_post.sql: -------------------------------------------------------------------------------- 1 | WITH pk_names AS ( 2 | SELECT 3 | name AS index_name, 4 | OBJECT_NAME(object_id) AS table_name, 5 | OBJECT_SCHEMA_NAME(object_id) AS schema_name, 6 | (SELECT 7 | CONCAT('', c.name) 8 | FROM sys.index_columns ic 9 | INNER JOIN sys.columns c ON ic.object_id = c.object_id 10 | AND ic.index_column_id = c.column_id 11 | WHERE ic.object_id = i.object_id 12 | AND ic.index_id = i.index_id 13 | AND ic.is_included_column = 0 14 | ORDER BY ic.key_ordinal 15 | FOR XML PATH('')) COLLATE SQL_Latin1_General_CP1_CI_AS AS column_names 16 | FROM sys.indexes i 17 | WHERE i.is_primary_key = 1 18 | ) 19 | SELECT 20 | CONCAT( 21 | 'EXEC sp_rename ''', 22 | QUOTENAME(schema_name), 23 | '.', 24 | QUOTENAME(index_name), 25 | ''', ''', 26 | table_name, 27 | '_', 28 | column_names, 29 | '_pkey''' 30 | ) 31 | FROM pk_names 32 | WHERE index_name LIKE 'PK_%' 33 | \gexec 34 | -------------------------------------------------------------------------------- /_examples/django/sqlite3.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "context" 5 | "database/sql" 6 | "fmt" 7 | "os" 8 | "time" 9 | 10 | models "github.com/xo/dbtpl/_examples/django/sqlite3" 11 | ) 12 | 13 | func runSqlite3(ctx context.Context, db *sql.DB) error { 14 | now := time.Now() 15 | a := &models.Author{ 16 | Name: "author 1", 17 | } 18 | if err := a.Insert(ctx, db); err != nil { 19 | return err 20 | } 21 | t := &models.Tag{ 22 | Tag: "tag 1", 23 | } 24 | if err := t.Insert(ctx, db); err != nil { 25 | return err 26 | } 27 | b := &models.Book{ 28 | ISBN: "1", 29 | BookType: 1, 30 | Title: "book 1", 31 | Year: now.Year(), 32 | Available: models.NewTime(now), 33 | BooksAuthorIDFkey: int64(a.AuthorID), 34 | } 35 | if err := b.Insert(ctx, db); err != nil { 36 | return err 37 | } 38 | bt := &models.BooksTag{ 39 | BookID: int64(b.BookID), 40 | TagID: int64(t.TagID), 41 | } 42 | if err := bt.Insert(ctx, db); err != nil { 43 | return err 44 | } 45 | books, err := models.BooksByBooksAuthorIDFkey(ctx, db, int64(a.AuthorID)) 46 | if err != nil { 47 | return err 48 | } 49 | for _, book := range books { 50 | fmt.Fprintf(os.Stdout, "book %d: %q (%s) %d\n", book.BookID, book.Title, book.ISBN, book.Year) 51 | } 52 | return nil 53 | } 54 | -------------------------------------------------------------------------------- /_examples/django/sqlserver.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "context" 5 | "database/sql" 6 | "fmt" 7 | "os" 8 | "time" 9 | 10 | models "github.com/xo/dbtpl/_examples/django/sqlserver" 11 | ) 12 | 13 | func runSqlserver(ctx context.Context, db *sql.DB) error { 14 | now := time.Now() 15 | a := &models.Author{ 16 | Name: "author 1", 17 | } 18 | if err := a.Insert(ctx, db); err != nil { 19 | return err 20 | } 21 | t := &models.Tag{ 22 | Tag: "tag 1", 23 | } 24 | if err := t.Insert(ctx, db); err != nil { 25 | return err 26 | } 27 | b := &models.Book{ 28 | ISBN: "1", 29 | BookType: 1, 30 | Title: "book 1", 31 | Year: now.Year(), 32 | Available: now, 33 | BooksAuthorIDFkey: a.AuthorID, 34 | } 35 | if err := b.Insert(ctx, db); err != nil { 36 | return err 37 | } 38 | bt := &models.BooksTag{ 39 | BookID: b.BookID, 40 | TagID: t.TagID, 41 | } 42 | if err := bt.Insert(ctx, db); err != nil { 43 | return err 44 | } 45 | books, err := models.BooksByBooksAuthorIDFkey(ctx, db, a.AuthorID) 46 | if err != nil { 47 | return err 48 | } 49 | for _, book := range books { 50 | fmt.Fprintf(os.Stdout, "book %d: %q (%s) %d\n", book.BookID, book.Title, book.ISBN, book.Year) 51 | } 52 | return nil 53 | } 54 | -------------------------------------------------------------------------------- /_examples/init/mysql.sql: -------------------------------------------------------------------------------- 1 | DROP USER IF EXISTS :NAME; 2 | 3 | DROP DATABASE IF EXISTS :NAME; 4 | 5 | CREATE DATABASE :NAME; 6 | 7 | CREATE USER :NAME@'%' IDENTIFIED BY :'PASS'; 8 | 9 | GRANT ALL PRIVILEGES ON :NAME.* TO :'NAME'@'%'; 10 | 11 | FLUSH PRIVILEGES; 12 | -------------------------------------------------------------------------------- /_examples/init/oracle.sql: -------------------------------------------------------------------------------- 1 | DROP TABLESPACE :NAME INCLUDING CONTENTS AND DATAFILES; 2 | 3 | DROP USER :NAME CASCADE; 4 | 5 | CREATE TABLESPACE :NAME NOLOGGING DATAFILE :'DATAFILE' SIZE 100M AUTOEXTEND ON; 6 | 7 | ALTER SESSION SET "_oracle_script"=true; 8 | 9 | CREATE 10 | USER :NAME 11 | IDENTIFIED BY :PASS 12 | DEFAULT TABLESPACE :NAME; 13 | 14 | GRANT 15 | CONNECT, 16 | CREATE SESSION, 17 | CREATE TABLE, 18 | CREATE VIEW, 19 | CREATE SEQUENCE, 20 | CREATE PROCEDURE, 21 | CREATE TRIGGER, 22 | UNLIMITED TABLESPACE, 23 | SELECT ANY DICTIONARY 24 | TO :NAME; 25 | -------------------------------------------------------------------------------- /_examples/init/postgres.sql: -------------------------------------------------------------------------------- 1 | DROP DATABASE IF EXISTS :NAME; 2 | 3 | DROP USER IF EXISTS :NAME; 4 | 5 | CREATE USER :NAME WITH PASSWORD :'PASS'; 6 | 7 | CREATE DATABASE :NAME OWNER :NAME; 8 | -------------------------------------------------------------------------------- /_examples/init/sqlserver.sql: -------------------------------------------------------------------------------- 1 | EXEC sp_configure 2 | 'contained database authentication', 1; 3 | 4 | RECONFIGURE; 5 | 6 | DROP LOGIN :NAME; 7 | 8 | DROP DATABASE :NAME; 9 | 10 | CREATE DATABASE :NAME 11 | CONTAINMENT=PARTIAL; 12 | 13 | \set QNAME "''":NAME"''" 14 | 15 | \set SQL 'CREATE LOGIN ':NAME' WITH PASSWORD=':QNAME', CHECK_POLICY=OFF, DEFAULT_DATABASE=':NAME';' 16 | EXEC [:NAME].[dbo].[sp_executesql] N:'SQL' 17 | 18 | \set SQL 'CREATE USER ':NAME' FOR LOGIN ':NAME' WITH DEFAULT_SCHEMA=':NAME';' 19 | EXEC [:NAME].[dbo].[sp_executesql] N:'SQL'; 20 | 21 | \set SQL 'CREATE SCHEMA ':NAME' AUTHORIZATION ':NAME';' 22 | EXEC [:NAME].[dbo].[sp_executesql] N:'SQL'; 23 | 24 | \set SQL 'EXEC sp_addrolemember db_owner, ':QNAME';' 25 | EXEC [:NAME].[dbo].[sp_executesql] N:'SQL'; 26 | 27 | -- original reconnect version: 28 | -- 29 | --\connect 'ms://localhost/':NAME 30 | -- 31 | --CREATE LOGIN :NAME 32 | -- WITH 33 | -- PASSWORD=:'PASS', 34 | -- CHECK_POLICY=OFF, 35 | -- DEFAULT_DATABASE=:NAME; 36 | -- 37 | --CREATE USER :NAME 38 | -- FOR LOGIN :NAME 39 | -- WITH DEFAULT_SCHEMA=:NAME; 40 | -- 41 | --CREATE SCHEMA :NAME AUTHORIZATION :NAME; 42 | -- 43 | --EXEC sp_addrolemember 'db_owner', :'NAME'; 44 | -------------------------------------------------------------------------------- /_examples/northwind/.gitignore: -------------------------------------------------------------------------------- 1 | /northwind 2 | /northwind.exe 3 | -------------------------------------------------------------------------------- /_examples/northwind/README.md: -------------------------------------------------------------------------------- 1 | # About 2 | 3 | Northwind data taken from [Yugabyte database's][yugabyte] Git repository [data samples][yugabyte-git]. 4 | 5 | [yugabyte]: https://www.yugabyte.com 6 | [yugabyte-git]: https://github.com/yugabyte/yugabyte-db/tree/master/sample 7 | -------------------------------------------------------------------------------- /_examples/northwind/gen.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | SRC=$(realpath $(cd -P "$(dirname "${BASH_SOURCE[0]}")" && pwd)) 4 | 5 | TEST=$(basename $SRC) 6 | 7 | declare -A DSNS 8 | DSNS+=( 9 | [mysql]=my://$TEST:$TEST@localhost/$TEST 10 | [oracle]=or://$TEST:$TEST@localhost/free 11 | [postgres]=pg://$TEST:$TEST@localhost/$TEST 12 | [sqlite3]=sq:$TEST.db 13 | [sqlserver]=ms://$TEST:$TEST@localhost/$TEST 14 | ) 15 | 16 | APPLY=0 17 | BUILD=0 18 | DATABASES="mysql oracle postgres sqlite3 sqlserver" 19 | ARGS=() 20 | 21 | OPTIND=1 22 | while getopts "abd:vD" opt; do 23 | case "$opt" in 24 | a) APPLY=1 ;; 25 | b) BUILD=1 ;; 26 | d) DATABASES=$OPTARG ;; 27 | v) ARGS+=(-v) ;; 28 | D) ARGS+=(-D) ;; 29 | esac 30 | done 31 | 32 | if [ "$BUILD" = "1" ]; then 33 | pushd $SRC/../../ &> /dev/null 34 | (set -x; 35 | go build 36 | ) 37 | popd &> /dev/null 38 | fi 39 | 40 | DBTPLBIN=$(which dbtpl) 41 | if [ -e $SRC/../../dbtpl ]; then 42 | DBTPLBIN=$SRC/../../dbtpl 43 | fi 44 | DBTPLBIN=$(realpath $DBTPLBIN) 45 | 46 | pushd $SRC &> /dev/null 47 | 48 | for TYPE in $DATABASES; do 49 | DB=${DSNS[$TYPE]} 50 | if [ -z "$DB" ]; then 51 | echo "$TYPE has no defined DSN" 52 | exit 1 53 | fi 54 | mkdir -p $TYPE 55 | rm -f $TYPE/*.dbtpl.* 56 | echo "------------------------------------------------------" 57 | echo "$TYPE: $DB" 58 | if [ "$APPLY" = "1" ]; then 59 | if [[ "$TYPE" = "sqlite3" && -f $TEST.db ]]; then 60 | (set -ex; 61 | rm $TEST.db 62 | ) 63 | fi 64 | (set -ex; 65 | $SRC/../createdb.sh -d $TYPE -n $TEST 66 | usql -f sql/${TYPE}_schema.sql $DB 67 | ) 68 | if [ -f sql/${TYPE}_data.sql ]; then 69 | (set -ex; 70 | usql -f sql/${TYPE}_data.sql $DB 71 | ) 72 | fi 73 | fi 74 | (set -ex; 75 | $DBTPLBIN schema $DB -o $TYPE ${ARGS[@]} 76 | $DBTPLBIN schema $DB -o $TYPE -t createdb ${ARGS[@]} --createdb-fmt="" 77 | $DBTPLBIN schema $DB -o $TYPE -t json ${ARGS[@]} 78 | $DBTPLBIN schema $DB -o $TYPE -t yaml ${ARGS[@]} 79 | $DBTPLBIN schema $DB -o $TYPE -t dot ${ARGS[@]} 80 | go build ./$TYPE 81 | go build 82 | ./$TEST -dsn $DB ${ARGS[@]} 83 | ) 84 | done 85 | 86 | popd &> /dev/null 87 | -------------------------------------------------------------------------------- /_examples/northwind/main.go: -------------------------------------------------------------------------------- 1 | // Command northwind demonstrates using generated models for the northwind 2 | // sample database. 3 | // 4 | // Schema/data comes from the Yugabyte Database Git repository. See README.md 5 | // for more information. 6 | // 7 | //go:debug x509negativeserial=1 8 | package main 9 | 10 | import ( 11 | "context" 12 | "database/sql" 13 | "flag" 14 | "fmt" 15 | "os" 16 | "os/user" 17 | 18 | // drivers 19 | _ "github.com/go-sql-driver/mysql" 20 | _ "github.com/lib/pq" 21 | _ "github.com/mattn/go-sqlite3" 22 | _ "github.com/microsoft/go-mssqldb" 23 | _ "github.com/sijms/go-ora/v2" 24 | 25 | // models 26 | "github.com/xo/dbtpl/_examples/northwind/mysql" 27 | "github.com/xo/dbtpl/_examples/northwind/oracle" 28 | "github.com/xo/dbtpl/_examples/northwind/postgres" 29 | "github.com/xo/dbtpl/_examples/northwind/sqlite3" 30 | "github.com/xo/dbtpl/_examples/northwind/sqlserver" 31 | 32 | "github.com/xo/dburl" 33 | "github.com/xo/dburl/passfile" 34 | ) 35 | 36 | func main() { 37 | verbose := flag.Bool("v", false, "verbose") 38 | dsn := flag.String("dsn", "", "dsn") 39 | flag.Parse() 40 | if err := run(context.Background(), *verbose, *dsn); err != nil { 41 | fmt.Fprintln(os.Stderr, "error:", err) 42 | os.Exit(1) 43 | } 44 | } 45 | 46 | func run(ctx context.Context, verbose bool, dsn string) error { 47 | if verbose { 48 | logger := func(s string, v ...any) { 49 | fmt.Printf("-------------------------------------\nQUERY: %s\n VAL: %v\n\n", s, v) 50 | } 51 | mysql.SetLogger(logger) 52 | oracle.SetLogger(logger) 53 | postgres.SetLogger(logger) 54 | sqlite3.SetLogger(logger) 55 | sqlserver.SetLogger(logger) 56 | } 57 | v, err := user.Current() 58 | if err != nil { 59 | return err 60 | } 61 | // parse url 62 | u, err := parse(dsn) 63 | if err != nil { 64 | return err 65 | } 66 | // open database 67 | db, err := passfile.OpenURL(u, v.HomeDir, "dbtplpass") 68 | if err != nil { 69 | return err 70 | } 71 | var f func(context.Context, *sql.DB) error 72 | switch u.Driver { 73 | case "mysql": 74 | f = runMysql 75 | case "oracle": 76 | f = runOracle 77 | case "postgres": 78 | f = runPostgres 79 | case "sqlite3": 80 | f = runSqlite3 81 | case "sqlserver": 82 | f = runSqlserver 83 | } 84 | return f(ctx, db) 85 | } 86 | 87 | func parse(dsn string) (*dburl.URL, error) { 88 | v, err := dburl.Parse(dsn) 89 | if err != nil { 90 | return nil, err 91 | } 92 | switch v.Driver { 93 | case "mysql": 94 | q := v.Query() 95 | q.Set("parseTime", "true") 96 | v.RawQuery = q.Encode() 97 | return dburl.Parse(v.String()) 98 | case "sqlite3": 99 | q := v.Query() 100 | q.Set("_loc", "auto") 101 | v.RawQuery = q.Encode() 102 | return dburl.Parse(v.String()) 103 | } 104 | return v, nil 105 | } 106 | -------------------------------------------------------------------------------- /_examples/northwind/mysql.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "context" 5 | "database/sql" 6 | "fmt" 7 | 8 | models "github.com/xo/dbtpl/_examples/northwind/mysql" 9 | ) 10 | 11 | func runMysql(ctx context.Context, db *sql.DB) error { 12 | p, err := models.ProductByProductID(ctx, db, 16) 13 | if err != nil { 14 | return err 15 | } 16 | fmt.Printf("product %d: %q\n", p.ProductID, p.ProductName) 17 | return nil 18 | } 19 | -------------------------------------------------------------------------------- /_examples/northwind/oracle.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "context" 5 | "database/sql" 6 | "fmt" 7 | 8 | models "github.com/xo/dbtpl/_examples/northwind/oracle" 9 | ) 10 | 11 | func runOracle(ctx context.Context, db *sql.DB) error { 12 | p, err := models.ProductByProductID(ctx, db, 16) 13 | if err != nil { 14 | return err 15 | } 16 | fmt.Printf("product %d: %q\n", p.ProductID, p.ProductName) 17 | return nil 18 | } 19 | -------------------------------------------------------------------------------- /_examples/northwind/postgres.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "context" 5 | "database/sql" 6 | "fmt" 7 | 8 | models "github.com/xo/dbtpl/_examples/northwind/postgres" 9 | ) 10 | 11 | func runPostgres(ctx context.Context, db *sql.DB) error { 12 | p, err := models.ProductByProductID(ctx, db, 16) 13 | if err != nil { 14 | return err 15 | } 16 | fmt.Printf("product %d: %q\n", p.ProductID, p.ProductName) 17 | return nil 18 | } 19 | -------------------------------------------------------------------------------- /_examples/northwind/sqlite3.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "context" 5 | "database/sql" 6 | "fmt" 7 | 8 | models "github.com/xo/dbtpl/_examples/northwind/sqlite3" 9 | ) 10 | 11 | func runSqlite3(ctx context.Context, db *sql.DB) error { 12 | p, err := models.ProductByProductID(ctx, db, 16) 13 | if err != nil { 14 | return err 15 | } 16 | fmt.Printf("product %d: %q\n", p.ProductID, p.ProductName) 17 | return nil 18 | } 19 | -------------------------------------------------------------------------------- /_examples/northwind/sqlserver.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "context" 5 | "database/sql" 6 | "fmt" 7 | 8 | models "github.com/xo/dbtpl/_examples/northwind/sqlserver" 9 | ) 10 | 11 | func runSqlserver(ctx context.Context, db *sql.DB) error { 12 | p, err := models.ProductByProductID(ctx, db, 16) 13 | if err != nil { 14 | return err 15 | } 16 | fmt.Printf("product %d: %q\n", p.ProductID, p.ProductName) 17 | return nil 18 | } 19 | -------------------------------------------------------------------------------- /go.mod: -------------------------------------------------------------------------------- 1 | module github.com/xo/dbtpl 2 | 3 | go 1.24.3 4 | 5 | require ( 6 | github.com/Masterminds/sprig/v3 v3.3.0 7 | github.com/go-sql-driver/mysql v1.9.2 8 | github.com/goccy/go-yaml v1.17.1 9 | github.com/kenshaw/glob v0.0.0-20250507233341-2ccc24e5a073 10 | github.com/kenshaw/inflector v0.3.0 11 | github.com/kenshaw/snaker v0.4.3 12 | github.com/lib/pq v1.10.9 13 | github.com/mattn/go-sqlite3 v1.14.28 14 | github.com/microsoft/go-mssqldb v1.8.1 15 | github.com/sijms/go-ora/v2 v2.8.24 16 | github.com/traefik/yaegi v0.16.1 17 | github.com/xo/dburl v0.23.8 18 | github.com/xo/ox v0.0.0-20250525220337-fc21a8d340e6 19 | github.com/yookoala/realpath v1.0.0 20 | golang.org/x/tools v0.33.0 21 | mvdan.cc/gofumpt v0.8.0 22 | ) 23 | 24 | require ( 25 | dario.cat/mergo v1.0.2 // indirect 26 | filippo.io/edwards25519 v1.1.0 // indirect 27 | github.com/Masterminds/goutils v1.1.1 // indirect 28 | github.com/Masterminds/semver/v3 v3.3.1 // indirect 29 | github.com/golang-sql/civil v0.0.0-20220223132316-b832511892a9 // indirect 30 | github.com/golang-sql/sqlexp v0.1.0 // indirect 31 | github.com/google/go-cmp v0.7.0 // indirect 32 | github.com/google/uuid v1.6.0 // indirect 33 | github.com/huandu/xstrings v1.5.0 // indirect 34 | github.com/mitchellh/copystructure v1.2.0 // indirect 35 | github.com/mitchellh/reflectwalk v1.0.2 // indirect 36 | github.com/shopspring/decimal v1.4.0 // indirect 37 | github.com/spf13/cast v1.8.0 // indirect 38 | golang.org/x/crypto v0.38.0 // indirect 39 | golang.org/x/mod v0.24.0 // indirect 40 | golang.org/x/sync v0.14.0 // indirect 41 | golang.org/x/text v0.25.0 // indirect 42 | ) 43 | -------------------------------------------------------------------------------- /internal/gen.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | set -ex 4 | 5 | yaegi extract github.com/xo/dbtpl/loader 6 | yaegi extract github.com/xo/dbtpl/types 7 | yaegi extract os/exec 8 | yaegi extract github.com/kenshaw/glob 9 | yaegi extract github.com/goccy/go-yaml 10 | yaegi extract github.com/kenshaw/inflector 11 | yaegi extract github.com/kenshaw/snaker 12 | yaegi extract github.com/Masterminds/sprig/v3 13 | yaegi extract github.com/yookoala/realpath 14 | yaegi extract golang.org/x/tools/imports 15 | yaegi extract mvdan.cc/gofumpt/format 16 | perl -pi -e 's/.*\n// if /Custom/' github_com-goccy-go-yaml.go 17 | -------------------------------------------------------------------------------- /internal/github_com-Masterminds-sprig-v3.go: -------------------------------------------------------------------------------- 1 | // Code generated by 'yaegi extract github.com/Masterminds/sprig/v3'. DO NOT EDIT. 2 | 3 | package internal 4 | 5 | import ( 6 | "github.com/Masterminds/sprig/v3" 7 | "reflect" 8 | ) 9 | 10 | func init() { 11 | Symbols["github.com/Masterminds/sprig/v3/sprig"] = map[string]reflect.Value{ 12 | // function, constant and variable definitions 13 | "FuncMap": reflect.ValueOf(sprig.FuncMap), 14 | "GenericFuncMap": reflect.ValueOf(sprig.GenericFuncMap), 15 | "HermeticHtmlFuncMap": reflect.ValueOf(sprig.HermeticHtmlFuncMap), 16 | "HermeticTxtFuncMap": reflect.ValueOf(sprig.HermeticTxtFuncMap), 17 | "HtmlFuncMap": reflect.ValueOf(sprig.HtmlFuncMap), 18 | "TxtFuncMap": reflect.ValueOf(sprig.TxtFuncMap), 19 | 20 | // type definitions 21 | "DSAKeyFormat": reflect.ValueOf((*sprig.DSAKeyFormat)(nil)), 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /internal/github_com-kenshaw-glob.go: -------------------------------------------------------------------------------- 1 | // Code generated by 'yaegi extract github.com/kenshaw/glob'. DO NOT EDIT. 2 | 3 | package internal 4 | 5 | import ( 6 | "github.com/kenshaw/glob" 7 | "reflect" 8 | ) 9 | 10 | func init() { 11 | Symbols["github.com/kenshaw/glob/glob"] = map[string]reflect.Value{ 12 | // function, constant and variable definitions 13 | "Compile": reflect.ValueOf(glob.Compile), 14 | "Must": reflect.ValueOf(glob.Must), 15 | "New": reflect.ValueOf(glob.New), 16 | "Quote": reflect.ValueOf(glob.Quote), 17 | 18 | // type definitions 19 | "Glob": reflect.ValueOf((*glob.Glob)(nil)), 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /internal/github_com-kenshaw-inflector.go: -------------------------------------------------------------------------------- 1 | // Code generated by 'yaegi extract github.com/kenshaw/inflector'. DO NOT EDIT. 2 | 3 | package internal 4 | 5 | import ( 6 | "github.com/kenshaw/inflector" 7 | "go/constant" 8 | "go/token" 9 | "reflect" 10 | ) 11 | 12 | func init() { 13 | Symbols["github.com/kenshaw/inflector/inflector"] = map[string]reflect.Value{ 14 | // function, constant and variable definitions 15 | "Pluralize": reflect.ValueOf(inflector.Pluralize), 16 | "RulePlural": reflect.ValueOf(constant.MakeFromLiteral("0", token.INT, 0)), 17 | "RuleSingular": reflect.ValueOf(constant.MakeFromLiteral("1", token.INT, 0)), 18 | "Singularize": reflect.ValueOf(inflector.Singularize), 19 | 20 | // type definitions 21 | "InflectorRule": reflect.ValueOf((*inflector.InflectorRule)(nil)), 22 | "Rule": reflect.ValueOf((*inflector.Rule)(nil)), 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /internal/github_com-kenshaw-snaker.go: -------------------------------------------------------------------------------- 1 | // Code generated by 'yaegi extract github.com/kenshaw/snaker'. DO NOT EDIT. 2 | 3 | package internal 4 | 5 | import ( 6 | "github.com/kenshaw/snaker" 7 | "reflect" 8 | ) 9 | 10 | func init() { 11 | Symbols["github.com/kenshaw/snaker/snaker"] = map[string]reflect.Value{ 12 | // function, constant and variable definitions 13 | "CamelToSnake": reflect.ValueOf(snaker.CamelToSnake), 14 | "CamelToSnakeIdentifier": reflect.ValueOf(snaker.CamelToSnakeIdentifier), 15 | "CommonInitialisms": reflect.ValueOf(snaker.CommonInitialisms), 16 | "CommonPlurals": reflect.ValueOf(snaker.CommonPlurals), 17 | "DefaultInitialisms": reflect.ValueOf(&snaker.DefaultInitialisms).Elem(), 18 | "ForceCamelIdentifier": reflect.ValueOf(snaker.ForceCamelIdentifier), 19 | "ForceLowerCamelIdentifier": reflect.ValueOf(snaker.ForceLowerCamelIdentifier), 20 | "IsInitialism": reflect.ValueOf(snaker.IsInitialism), 21 | "New": reflect.ValueOf(snaker.New), 22 | "NewDefaultInitialisms": reflect.ValueOf(snaker.NewDefaultInitialisms), 23 | "SnakeToCamel": reflect.ValueOf(snaker.SnakeToCamel), 24 | "SnakeToCamelIdentifier": reflect.ValueOf(snaker.SnakeToCamelIdentifier), 25 | "ToIdentifier": reflect.ValueOf(snaker.ToIdentifier), 26 | "ToKebab": reflect.ValueOf(snaker.ToKebab), 27 | 28 | // type definitions 29 | "Initialisms": reflect.ValueOf((*snaker.Initialisms)(nil)), 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /internal/github_com-xo-dbtpl-loader.go: -------------------------------------------------------------------------------- 1 | // Code generated by 'yaegi extract github.com/xo/dbtpl/loader'. DO NOT EDIT. 2 | 3 | package internal 4 | 5 | import ( 6 | "github.com/xo/dbtpl/loader" 7 | "reflect" 8 | ) 9 | 10 | func init() { 11 | Symbols["github.com/xo/dbtpl/loader/loader"] = map[string]reflect.Value{ 12 | // function, constant and variable definitions 13 | "EnumValues": reflect.ValueOf(loader.EnumValues), 14 | "Enums": reflect.ValueOf(loader.Enums), 15 | "Flags": reflect.ValueOf(loader.Flags), 16 | "IndexColumns": reflect.ValueOf(loader.IndexColumns), 17 | "MysqlEnumValues": reflect.ValueOf(loader.MysqlEnumValues), 18 | "MysqlGoType": reflect.ValueOf(loader.MysqlGoType), 19 | "NthParam": reflect.ValueOf(loader.NthParam), 20 | "OracleGoType": reflect.ValueOf(loader.OracleGoType), 21 | "PQPostgresGoType": reflect.ValueOf(loader.PQPostgresGoType), 22 | "PostgresFlags": reflect.ValueOf(loader.PostgresFlags), 23 | "PostgresGoType": reflect.ValueOf(loader.PostgresGoType), 24 | "PostgresIndexColumns": reflect.ValueOf(loader.PostgresIndexColumns), 25 | "PostgresTableColumns": reflect.ValueOf(loader.PostgresTableColumns), 26 | "PostgresViewStrip": reflect.ValueOf(loader.PostgresViewStrip), 27 | "ProcParams": reflect.ValueOf(loader.ProcParams), 28 | "Procs": reflect.ValueOf(loader.Procs), 29 | "Register": reflect.ValueOf(loader.Register), 30 | "Schema": reflect.ValueOf(loader.Schema), 31 | "Sqlite3GoType": reflect.ValueOf(loader.Sqlite3GoType), 32 | "SqlserverGoType": reflect.ValueOf(loader.SqlserverGoType), 33 | "SqlserverViewStrip": reflect.ValueOf(loader.SqlserverViewStrip), 34 | "StdlibPostgresGoType": reflect.ValueOf(loader.StdlibPostgresGoType), 35 | "TableColumns": reflect.ValueOf(loader.TableColumns), 36 | "TableForeignKeys": reflect.ValueOf(loader.TableForeignKeys), 37 | "TableIndexes": reflect.ValueOf(loader.TableIndexes), 38 | "TableSequences": reflect.ValueOf(loader.TableSequences), 39 | "Tables": reflect.ValueOf(loader.Tables), 40 | "ViewCreate": reflect.ValueOf(loader.ViewCreate), 41 | "ViewDrop": reflect.ValueOf(loader.ViewDrop), 42 | "ViewSchema": reflect.ValueOf(loader.ViewSchema), 43 | "ViewStrip": reflect.ValueOf(loader.ViewStrip), 44 | "ViewTruncate": reflect.ValueOf(loader.ViewTruncate), 45 | 46 | // type definitions 47 | "Loader": reflect.ValueOf((*loader.Loader)(nil)), 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /internal/github_com-xo-dbtpl-types.go: -------------------------------------------------------------------------------- 1 | // Code generated by 'yaegi extract github.com/xo/dbtpl/types'. DO NOT EDIT. 2 | 3 | package internal 4 | 5 | import ( 6 | "github.com/xo/dbtpl/types" 7 | "reflect" 8 | ) 9 | 10 | func init() { 11 | Symbols["github.com/xo/dbtpl/types/types"] = map[string]reflect.Value{ 12 | // function, constant and variable definitions 13 | "Append": reflect.ValueOf(types.Append), 14 | "AppendKey": reflect.ValueOf(types.AppendKey), 15 | "DbKey": reflect.ValueOf(types.DbKey), 16 | "DriverDbSchema": reflect.ValueOf(types.DriverDbSchema), 17 | "DriverKey": reflect.ValueOf(types.DriverKey), 18 | "Out": reflect.ValueOf(types.Out), 19 | "OutKey": reflect.ValueOf(types.OutKey), 20 | "ParseType": reflect.ValueOf(types.ParseType), 21 | "SchemaKey": reflect.ValueOf(types.SchemaKey), 22 | "Single": reflect.ValueOf(types.Single), 23 | "SingleKey": reflect.ValueOf(types.SingleKey), 24 | 25 | // type definitions 26 | "ContextKey": reflect.ValueOf((*types.ContextKey)(nil)), 27 | "Enum": reflect.ValueOf((*types.Enum)(nil)), 28 | "Field": reflect.ValueOf((*types.Field)(nil)), 29 | "Flag": reflect.ValueOf((*types.Flag)(nil)), 30 | "FlagSet": reflect.ValueOf((*types.FlagSet)(nil)), 31 | "ForeignKey": reflect.ValueOf((*types.ForeignKey)(nil)), 32 | "Index": reflect.ValueOf((*types.Index)(nil)), 33 | "Proc": reflect.ValueOf((*types.Proc)(nil)), 34 | "Query": reflect.ValueOf((*types.Query)(nil)), 35 | "Schema": reflect.ValueOf((*types.Schema)(nil)), 36 | "Set": reflect.ValueOf((*types.Set)(nil)), 37 | "Table": reflect.ValueOf((*types.Table)(nil)), 38 | "Template": reflect.ValueOf((*types.Template)(nil)), 39 | "TemplateType": reflect.ValueOf((*types.TemplateType)(nil)), 40 | "Type": reflect.ValueOf((*types.Type)(nil)), 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /internal/github_com-yookoala-realpath.go: -------------------------------------------------------------------------------- 1 | // Code generated by 'yaegi extract github.com/yookoala/realpath'. DO NOT EDIT. 2 | 3 | package internal 4 | 5 | import ( 6 | "github.com/yookoala/realpath" 7 | "reflect" 8 | ) 9 | 10 | func init() { 11 | Symbols["github.com/yookoala/realpath/realpath"] = map[string]reflect.Value{ 12 | // function, constant and variable definitions 13 | "Realpath": reflect.ValueOf(realpath.Realpath), 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /internal/golang_org-x-tools-imports.go: -------------------------------------------------------------------------------- 1 | // Code generated by 'yaegi extract golang.org/x/tools/imports'. DO NOT EDIT. 2 | 3 | package internal 4 | 5 | import ( 6 | "golang.org/x/tools/imports" 7 | "reflect" 8 | ) 9 | 10 | func init() { 11 | Symbols["golang.org/x/tools/imports/imports"] = map[string]reflect.Value{ 12 | // function, constant and variable definitions 13 | "Debug": reflect.ValueOf(&imports.Debug).Elem(), 14 | "LocalPrefix": reflect.ValueOf(&imports.LocalPrefix).Elem(), 15 | "Process": reflect.ValueOf(imports.Process), 16 | "VendorlessPath": reflect.ValueOf(imports.VendorlessPath), 17 | 18 | // type definitions 19 | "Options": reflect.ValueOf((*imports.Options)(nil)), 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /internal/internal.go: -------------------------------------------------------------------------------- 1 | // Package internal contains internal code for xo. 2 | package internal 3 | 4 | import ( 5 | "reflect" 6 | ) 7 | 8 | // Symbols are extracted (generated) symbols from the types package. 9 | // 10 | //go:generate ./gen.sh 11 | var Symbols map[string]map[string]reflect.Value = make(map[string]map[string]reflect.Value) 12 | -------------------------------------------------------------------------------- /internal/mvdan_cc-gofumpt-format.go: -------------------------------------------------------------------------------- 1 | // Code generated by 'yaegi extract mvdan.cc/gofumpt/format'. DO NOT EDIT. 2 | 3 | package internal 4 | 5 | import ( 6 | "mvdan.cc/gofumpt/format" 7 | "reflect" 8 | ) 9 | 10 | func init() { 11 | Symbols["mvdan.cc/gofumpt/format/format"] = map[string]reflect.Value{ 12 | // function, constant and variable definitions 13 | "File": reflect.ValueOf(format.File), 14 | "Source": reflect.ValueOf(format.Source), 15 | 16 | // type definitions 17 | "Options": reflect.ValueOf((*format.Options)(nil)), 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /internal/os-exec.go: -------------------------------------------------------------------------------- 1 | // Code generated by 'yaegi extract os/exec'. DO NOT EDIT. 2 | 3 | //go:build go1.24 4 | // +build go1.24 5 | 6 | package internal 7 | 8 | import ( 9 | "os/exec" 10 | "reflect" 11 | ) 12 | 13 | func init() { 14 | Symbols["os/exec/exec"] = map[string]reflect.Value{ 15 | // function, constant and variable definitions 16 | "Command": reflect.ValueOf(exec.Command), 17 | "CommandContext": reflect.ValueOf(exec.CommandContext), 18 | "ErrDot": reflect.ValueOf(&exec.ErrDot).Elem(), 19 | "ErrNotFound": reflect.ValueOf(&exec.ErrNotFound).Elem(), 20 | "ErrWaitDelay": reflect.ValueOf(&exec.ErrWaitDelay).Elem(), 21 | "LookPath": reflect.ValueOf(exec.LookPath), 22 | 23 | // type definitions 24 | "Cmd": reflect.ValueOf((*exec.Cmd)(nil)), 25 | "Error": reflect.ValueOf((*exec.Error)(nil)), 26 | "ExitError": reflect.ValueOf((*exec.ExitError)(nil)), 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /loader/oracle.go: -------------------------------------------------------------------------------- 1 | package loader 2 | 3 | import ( 4 | "regexp" 5 | 6 | "github.com/xo/dbtpl/models" 7 | xo "github.com/xo/dbtpl/types" 8 | ) 9 | 10 | func init() { 11 | Register("oracle", Loader{ 12 | Mask: ":%d", 13 | Schema: models.OracleSchema, 14 | Procs: models.OracleProcs, 15 | ProcParams: models.OracleProcParams, 16 | Tables: models.OracleTables, 17 | TableColumns: models.OracleTableColumns, 18 | TableSequences: models.OracleTableSequences, 19 | TableForeignKeys: models.OracleTableForeignKeys, 20 | TableIndexes: models.OracleTableIndexes, 21 | IndexColumns: models.OracleIndexColumns, 22 | ViewCreate: models.OracleViewCreate, 23 | ViewTruncate: models.OracleViewTruncate, 24 | ViewDrop: models.OracleViewDrop, 25 | }) 26 | } 27 | 28 | // OracleGoType parse a oracle type into a Go type based on the column 29 | // definition. 30 | func OracleGoType(d xo.Type, schema, itype, utype string) (string, string, error) { 31 | var goType, zero string 32 | // strip remaining length (on things like timestamp) 33 | switch orLenRE.ReplaceAllString(d.Type, "") { 34 | case "char", "nchar", "varchar", "varchar2", "nvarchar2", "clob", "nclob", "rowid": 35 | goType, zero = "string", `""` 36 | if d.Nullable { 37 | goType, zero = "sql.NullString", "sql.NullString{}" 38 | } 39 | case "number": 40 | switch { 41 | case d.Prec == 0 && d.Scale == 0 && !d.Nullable: 42 | goType, zero = "int", "0" 43 | case d.Scale != 0 && !d.Nullable: 44 | goType, zero = "float64", "0.0" 45 | case d.Scale != 0 && d.Nullable: 46 | goType, zero = "sql.NullFloat64", "sql.NullFloat64{}" 47 | case !d.Nullable: 48 | goType, zero = "int64", "0" 49 | default: 50 | goType, zero = "sql.NullInt64", "sql.NullInt64{}" 51 | } 52 | case "float": 53 | goType, zero = "float64", "0.0" 54 | if d.Nullable { 55 | goType, zero = "sql.NullFloat64", "sql.NullFloat64{}" 56 | } 57 | case "date", "timestamp", "timestamp with time zone", "timestamp with local time zone": 58 | goType, zero = "time.Time", "time.Time{}" 59 | if d.Nullable { 60 | goType, zero = "sql.NullTime", "sql.NullTime{}" 61 | } 62 | case "blob", "long raw", "raw", "xmltype": 63 | goType, zero = "[]byte", "nil" 64 | default: 65 | goType, zero = schemaType(d.Type, d.Nullable, schema) 66 | } 67 | // handle bools 68 | switch { 69 | case goType == "int64" && d.Prec == 1 && !d.Nullable: 70 | goType, zero = "bool", "false" 71 | case goType == "sql.NullInt64" && d.Prec == 1 && d.Nullable: 72 | goType, zero = "sql.NullBool", "sql.NullBool{}" 73 | } 74 | return goType, zero, nil 75 | } 76 | 77 | // orLenRE is a regexp that matches lengths. 78 | var orLenRE = regexp.MustCompile(`\([0-9]+\)`) 79 | -------------------------------------------------------------------------------- /loader/sqlite3.go: -------------------------------------------------------------------------------- 1 | package loader 2 | 3 | import ( 4 | "github.com/xo/dbtpl/models" 5 | xo "github.com/xo/dbtpl/types" 6 | ) 7 | 8 | func init() { 9 | Register("sqlite3", Loader{ 10 | Mask: "$%d", 11 | Schema: models.Sqlite3Schema, 12 | Tables: models.Sqlite3Tables, 13 | TableColumns: models.Sqlite3TableColumns, 14 | TableSequences: models.Sqlite3TableSequences, 15 | TableForeignKeys: models.Sqlite3TableForeignKeys, 16 | TableIndexes: models.Sqlite3TableIndexes, 17 | IndexColumns: models.Sqlite3IndexColumns, 18 | ViewCreate: models.Sqlite3ViewCreate, 19 | ViewDrop: models.Sqlite3ViewDrop, 20 | }) 21 | } 22 | 23 | // Sqlite3GoType parse a sqlite3 type into a Go type based on the column 24 | // definition. 25 | func Sqlite3GoType(d xo.Type, schema, itype, utype string) (string, string, error) { 26 | var goType, zero string 27 | switch d.Type { 28 | case "bool", "boolean": 29 | goType, zero = "bool", "false" 30 | if d.Nullable { 31 | goType, zero = "sql.NullBool", "sql.NullBool{}" 32 | } 33 | case "int", "integer", "tinyint", "smallint", "mediumint": 34 | goType, zero = itype, "0" 35 | if d.Nullable { 36 | goType, zero = "sql.NullInt64", "sql.NullInt64{}" 37 | } 38 | case "bigint": 39 | goType, zero = "int64", "0" 40 | if d.Nullable { 41 | goType, zero = "sql.NullInt64", "sql.NullInt64{}" 42 | } 43 | case "numeric", "real", "double", "float", "decimal": 44 | goType, zero = "float64", "0.0" 45 | if d.Nullable { 46 | goType, zero = "sql.NullFloat64", "sql.NullFloat64{}" 47 | } 48 | case "blob": 49 | goType, zero = "[]byte", "nil" 50 | case "timestamp", "datetime", "date", "timestamp with timezone", "time with timezone", "time without timezone", "timestamp without timezone": 51 | goType, zero = "Time", "Time{}" 52 | if d.Nullable { 53 | goType, zero = "*Time", "nil" 54 | } 55 | default: 56 | // case "varchar", "character", "varying character", "nchar", "native character", "nvarchar", "text", "clob", "time": 57 | goType, zero = "string", `""` 58 | if d.Nullable { 59 | goType, zero = "sql.NullString", "sql.NullString{}" 60 | } 61 | } 62 | // if unsigned ... 63 | if intRE.MatchString(goType) && d.Unsigned { 64 | if goType == itype { 65 | goType, zero = utype, "0" 66 | } else { 67 | goType = "u" + goType 68 | } 69 | } 70 | return goType, zero, nil 71 | } 72 | -------------------------------------------------------------------------------- /main.go: -------------------------------------------------------------------------------- 1 | // Command dbtpl generates code from database schemas and custom queries. Works 2 | // with PostgreSQL, MySQL, Microsoft SQL Server, Oracle Database, and SQLite3. 3 | // 4 | //go:debug x509negativeserial=1 5 | package main 6 | 7 | //go:generate ./gen.sh models 8 | //go:generate go generate ./internal 9 | 10 | import ( 11 | "context" 12 | 13 | // drivers 14 | _ "github.com/go-sql-driver/mysql" 15 | _ "github.com/lib/pq" 16 | _ "github.com/mattn/go-sqlite3" 17 | _ "github.com/microsoft/go-mssqldb" 18 | _ "github.com/sijms/go-ora/v2" 19 | 20 | "github.com/xo/dbtpl/cmd" 21 | ) 22 | 23 | func main() { 24 | cmd.Run(context.Background(), "dbtpl") 25 | } 26 | -------------------------------------------------------------------------------- /models/enum.dbtpl.go: -------------------------------------------------------------------------------- 1 | package models 2 | 3 | // Code generated by dbtpl. DO NOT EDIT. 4 | 5 | import ( 6 | "context" 7 | ) 8 | 9 | // Enum is a enum. 10 | type Enum struct { 11 | EnumName string `json:"enum_name"` // enum_name 12 | } 13 | 14 | // PostgresEnums runs a custom query, returning results as [Enum]. 15 | func PostgresEnums(ctx context.Context, db DB, schema string) ([]*Enum, error) { 16 | // query 17 | const sqlstr = `SELECT ` + 18 | `DISTINCT t.typname ` + // ::varchar AS enum_name 19 | `FROM pg_type t ` + 20 | `JOIN ONLY pg_namespace n ON n.oid = t.typnamespace ` + 21 | `JOIN ONLY pg_enum e ON t.oid = e.enumtypid ` + 22 | `WHERE n.nspname = $1` 23 | // run 24 | logf(sqlstr, schema) 25 | rows, err := db.QueryContext(ctx, sqlstr, schema) 26 | if err != nil { 27 | return nil, logerror(err) 28 | } 29 | defer rows.Close() 30 | // load results 31 | var res []*Enum 32 | for rows.Next() { 33 | var e Enum 34 | // scan 35 | if err := rows.Scan(&e.EnumName); err != nil { 36 | return nil, logerror(err) 37 | } 38 | res = append(res, &e) 39 | } 40 | if err := rows.Err(); err != nil { 41 | return nil, logerror(err) 42 | } 43 | return res, nil 44 | } 45 | 46 | // MysqlEnums runs a custom query, returning results as [Enum]. 47 | func MysqlEnums(ctx context.Context, db DB, schema string) ([]*Enum, error) { 48 | // query 49 | const sqlstr = `SELECT ` + 50 | `DISTINCT column_name AS enum_name ` + 51 | `FROM information_schema.columns ` + 52 | `WHERE data_type = 'enum' ` + 53 | `AND table_schema = ?` 54 | // run 55 | logf(sqlstr, schema) 56 | rows, err := db.QueryContext(ctx, sqlstr, schema) 57 | if err != nil { 58 | return nil, logerror(err) 59 | } 60 | defer rows.Close() 61 | // load results 62 | var res []*Enum 63 | for rows.Next() { 64 | var e Enum 65 | // scan 66 | if err := rows.Scan(&e.EnumName); err != nil { 67 | return nil, logerror(err) 68 | } 69 | res = append(res, &e) 70 | } 71 | if err := rows.Err(); err != nil { 72 | return nil, logerror(err) 73 | } 74 | return res, nil 75 | } 76 | -------------------------------------------------------------------------------- /models/enumvalue.dbtpl.go: -------------------------------------------------------------------------------- 1 | package models 2 | 3 | // Code generated by dbtpl. DO NOT EDIT. 4 | 5 | import ( 6 | "context" 7 | ) 8 | 9 | // EnumValue is a enum value. 10 | type EnumValue struct { 11 | EnumValue string `json:"enum_value"` // enum_value 12 | ConstValue int `json:"const_value"` // const_value 13 | } 14 | 15 | // PostgresEnumValues runs a custom query, returning results as [EnumValue]. 16 | func PostgresEnumValues(ctx context.Context, db DB, schema, enum string) ([]*EnumValue, error) { 17 | // query 18 | const sqlstr = `SELECT ` + 19 | `e.enumlabel, ` + // ::varchar AS enum_value 20 | `e.enumsortorder ` + // ::integer AS const_value 21 | `FROM pg_type t ` + 22 | `JOIN ONLY pg_namespace n ON n.oid = t.typnamespace ` + 23 | `LEFT JOIN pg_enum e ON t.oid = e.enumtypid ` + 24 | `WHERE n.nspname = $1 ` + 25 | `AND t.typname = $2` 26 | // run 27 | logf(sqlstr, schema, enum) 28 | rows, err := db.QueryContext(ctx, sqlstr, schema, enum) 29 | if err != nil { 30 | return nil, logerror(err) 31 | } 32 | defer rows.Close() 33 | // load results 34 | var res []*EnumValue 35 | for rows.Next() { 36 | var ev EnumValue 37 | // scan 38 | if err := rows.Scan(&ev.EnumValue, &ev.ConstValue); err != nil { 39 | return nil, logerror(err) 40 | } 41 | res = append(res, &ev) 42 | } 43 | if err := rows.Err(); err != nil { 44 | return nil, logerror(err) 45 | } 46 | return res, nil 47 | } 48 | -------------------------------------------------------------------------------- /models/mysqlenumvalue.dbtpl.go: -------------------------------------------------------------------------------- 1 | package models 2 | 3 | // Code generated by dbtpl. DO NOT EDIT. 4 | 5 | import ( 6 | "context" 7 | ) 8 | 9 | // MysqlEnumValue represents a row from 'mysql.mysql_enum_value'. 10 | type MysqlEnumValue struct { 11 | EnumValues string `json:"enum_values"` // enum_values 12 | } 13 | 14 | // MysqlEnumValues runs a custom query, returning results as [MysqlEnumValue]. 15 | func MysqlEnumValues(ctx context.Context, db DB, schema, enum string) (*MysqlEnumValue, error) { 16 | // query 17 | const sqlstr = `SELECT ` + 18 | `SUBSTRING(column_type, 6, CHAR_LENGTH(column_type) - 6) AS enum_values ` + 19 | `FROM information_schema.columns ` + 20 | `WHERE data_type = 'enum' ` + 21 | `AND table_schema = ? ` + 22 | `AND column_name = ?` 23 | // run 24 | logf(sqlstr, schema, enum) 25 | var mev MysqlEnumValue 26 | if err := db.QueryRowContext(ctx, sqlstr, schema, enum).Scan(&mev.EnumValues); err != nil { 27 | return nil, logerror(err) 28 | } 29 | return &mev, nil 30 | } 31 | -------------------------------------------------------------------------------- /models/postgrescolorder.dbtpl.go: -------------------------------------------------------------------------------- 1 | package models 2 | 3 | // Code generated by dbtpl. DO NOT EDIT. 4 | 5 | import ( 6 | "context" 7 | ) 8 | 9 | // PostgresColOrder is a index column order. 10 | type PostgresColOrder struct { 11 | Ord string `json:"ord"` // ord 12 | } 13 | 14 | // PostgresGetColOrder runs a custom query, returning results as [PostgresColOrder]. 15 | func PostgresGetColOrder(ctx context.Context, db DB, schema, index string) (*PostgresColOrder, error) { 16 | // query 17 | const sqlstr = `SELECT ` + 18 | `i.indkey ` + // ::varchar AS ord 19 | `FROM pg_index i ` + 20 | `JOIN ONLY pg_class c ON c.oid = i.indrelid ` + 21 | `JOIN ONLY pg_namespace n ON n.oid = c.relnamespace ` + 22 | `JOIN ONLY pg_class ic ON ic.oid = i.indexrelid ` + 23 | `WHERE n.nspname = $1 ` + 24 | `AND ic.relname = $2` 25 | // run 26 | logf(sqlstr, schema, index) 27 | var pco PostgresColOrder 28 | if err := db.QueryRowContext(ctx, sqlstr, schema, index).Scan(&pco.Ord); err != nil { 29 | return nil, logerror(err) 30 | } 31 | return &pco, nil 32 | } 33 | -------------------------------------------------------------------------------- /templates/createdb/dbtpl.dbtpl.sql.tpl: -------------------------------------------------------------------------------- 1 | {{ define "header" -}} 2 | {{- $s := .Data -}} 3 | -- Generated by dbtpl for the {{ .Data.Name }} schema. 4 | {{ end }} 5 | 6 | {{ define "createdb" -}} 7 | {{- $s := .Data -}} 8 | {{ if and $s.Enums (driver "postgres") }} 9 | {{ range $e := $s.Enums }} 10 | -- enum {{ $e.Name }} 11 | CREATE TYPE {{ esc $e.Name }} AS ENUM ( 12 | {{- range $i, $v := $e.Values }} 13 | {{ literal $v.Name }}{{ comma $i $e.Values }} 14 | {{- end }} 15 | ); 16 | {{ end -}} 17 | {{- end -}} 18 | {{- if $s.Tables }} 19 | {{- range $t := $s.Tables }} 20 | -- table {{ $t.Name }} 21 | CREATE TABLE {{ esc $t.Name }} ( 22 | {{- range $i, $c := $t.Columns }} 23 | {{ coldef $t $c }}{{ comma $i $t.Columns }} 24 | {{- end -}} 25 | {{- range $idx := $t.Indexes -}}{{- if isEndConstraint $idx }}, 26 | {{ constraint $idx.Name -}} {{ if $idx.IsPrimary }}PRIMARY KEY{{ else }}UNIQUE{{ end }} ({{ fields $idx.Fields }}) 27 | {{- end -}}{{- end -}} 28 | {{- range $fk := $t.ForeignKeys -}}{{- if gt (len $fk.Fields) 1 }}, 29 | {{ constraint $fk.Name -}} FOREIGN KEY ({{ fields $fk.Fields }}) REFERENCES {{ esc $fk.RefTable }} ({{ fields $fk.RefFields }}) 30 | {{- end -}}{{- end }} 31 | ){{ engine }}; 32 | {{- if $t.Indexes }} 33 | {{ range $idx := $t.Indexes }}{{ if not (or $idx.IsPrimary $idx.IsUnique) }} 34 | -- index {{ $idx.Name }} 35 | CREATE INDEX {{ esc $idx.Name }} ON {{ esc $t.Name }} ({{ fields $idx.Fields }}); 36 | {{ end -}}{{- end -}}{{- end }} 37 | {{ end -}} 38 | {{- end -}} 39 | {{- if $s.Views }} 40 | {{- range $v := $s.Views }} 41 | -- view {{ $v.Name }} 42 | {{ viewdef $v }}; 43 | {{ end }} 44 | {{ end -}} 45 | {{- if $s.Procs }} 46 | {{- range $p := $s.Procs }} 47 | -- {{ $p.Type }} {{ $p.Name }} 48 | {{ procdef $p }}; 49 | {{ end -}} 50 | {{ end -}} 51 | {{ end -}} 52 | -------------------------------------------------------------------------------- /templates/dot/dbtpl.dbtpl.dot.tpl: -------------------------------------------------------------------------------- 1 | {{ define "dot" -}} 2 | {{ $s := .Data -}} 3 | // Generated by dbtpl for the {{ $s.Name }} schema. 4 | digraph {{ $s.Name|normalize }} { 5 | {{ if defaults -}} 6 | // Defaults 7 | {{ range defaults -}} 8 | {{ . }} 9 | {{ end }} 10 | {{ end -}} 11 | 12 | // Nodes (tables) 13 | {{- range $s.Tables }} 14 | {{ schema .Name }} [ label=< 15 | 16 | {{ header (schema .Name) }} 17 | {{ range .Columns -}} 18 | {{ row . }} 19 | {{ end -}} 20 |
> ] 21 | {{ end }} 22 | 23 | {{- range $s.Tables }} 24 | {{- $t := . -}} 25 | {{- range $t.ForeignKeys -}} 26 | {{- $fkey := . -}} 27 | {{- range $i, $field := .Fields }} 28 | {{ edge $t $fkey $i }} [ 29 | headlabel={{ quotes $fkey.Name }}] 30 | {{- end }} 31 | {{- end -}} 32 | {{- end }} 33 | } 34 | {{ end }} 35 | -------------------------------------------------------------------------------- /templates/go/hdr.dbtpl.go.tpl: -------------------------------------------------------------------------------- 1 | {{ define "header" }} 2 | {{- $tags := tags -}} 3 | {{- $inject := inject -}} 4 | {{- if $tags -}} 5 | //go:build{{ range $tags }} {{ . }}{{ end }} 6 | 7 | {{ end -}} 8 | {{- if first -}} 9 | // Package {{ pkg }} contains generated code for schema '{{ schema }}'. 10 | {{ end -}} 11 | package {{ pkg }} 12 | 13 | // Code generated by dbtpl. DO NOT EDIT. 14 | 15 | import ( 16 | "context" 17 | "database/sql" 18 | "database/sql/driver" 19 | "encoding/csv" 20 | "errors" 21 | "fmt" 22 | "io" 23 | "os" 24 | "regexp" 25 | "strings" 26 | "time" 27 | {{- if driver "postgres" }} 28 | "github.com/lib/pq" 29 | "github.com/lib/pq/hstore" 30 | {{ end }}{{ range imports }} 31 | {{ with .Alias }}{{ . }} {{ end }}{{ .Pkg }} 32 | {{ end }} 33 | ) 34 | 35 | {{- if $inject }} 36 | {{ $inject }} 37 | {{- end }} 38 | {{ end }} 39 | -------------------------------------------------------------------------------- /templates/go/query.dbtpl.go.tpl: -------------------------------------------------------------------------------- 1 | {{ define "query" }} 2 | {{- $q := .Data -}} 3 | {{- if $q.Comment -}} 4 | // {{ $q.Comment | eval (func_name_context $q) }} 5 | {{- else -}} 6 | // {{ func_name_context $q }} runs a custom query{{ if $q.Exec }} as a [sql.Result]{{ else if not $q.Flat }}, returning results as [{{ $q.Type.GoName }}]{{ end }}. 7 | {{- end }} 8 | {{ func_context $q }} { 9 | // query 10 | {{ querystr $q }} 11 | // run 12 | logf({{ names "" "sqlstr" $q }}) 13 | {{ if $q.Exec -}} 14 | return {{ db "Exec" $q }} 15 | {{- else if $q.Flat -}} 16 | {{- range $q.Type.Fields -}} 17 | var {{ .GoName }} {{ type .Type }} 18 | {{ end -}} 19 | if err := {{ db "QueryRow" $q }}.Scan({{ names "&" $q.Type.Fields }}); err != nil { 20 | return {{ zero $q.Type.Fields "logerror(err)" }} 21 | } 22 | return {{ names "" $q.Type "nil" }} 23 | {{- else if $q.One -}} 24 | var {{ short $q.Type }} {{ type $q.Type.GoName }} 25 | if err := {{ db "QueryRow" $q }}.Scan({{ names (print "&" (short $q.Type) ".") $q.Type.Fields }}); err != nil { 26 | return nil, logerror(err) 27 | } 28 | return &{{ short $q.Type }}, nil 29 | {{- else -}} 30 | rows, err := {{ db "Query" $q }} 31 | if err != nil { 32 | return nil, logerror(err) 33 | } 34 | defer rows.Close() 35 | // load results 36 | var res []*{{ type $q.Type.GoName }} 37 | for rows.Next() { 38 | var {{ short $q.Type}} {{ type $q.Type.GoName }} 39 | // scan 40 | if err := rows.Scan({{ names (print "&" (short $q.Type) ".") $q.Type.Fields }}); err != nil { 41 | return nil, logerror(err) 42 | } 43 | res = append(res, &{{ short $q.Type }}) 44 | } 45 | if err := rows.Err(); err != nil { 46 | return nil, logerror(err) 47 | } 48 | return res, nil 49 | {{- end }} 50 | } 51 | 52 | {{ if context_both -}} 53 | {{- if $q.Comment -}} 54 | // {{ $q.Comment | eval (func_name $q) }} 55 | {{- else -}} 56 | // {{ func_name $q }} runs a custom query{{ if $q.Exec }} as a [sql.Result]{{ else if not $q.Flat }}, returning results as [{{ $q.Type.GoName }}]{{ end }}. 57 | {{- end }} 58 | {{ func $q }} { 59 | return {{ func_name_context $q }}({{ names_all "" "context.Background()" "db" $q }}) 60 | } 61 | {{- end }} 62 | {{ end }} 63 | 64 | {{ define "typedef" }} 65 | {{- $q := .Data -}} 66 | {{- if $q.Comment -}} 67 | // {{ $q.Comment | eval $q.GoName }} 68 | {{- else -}} 69 | // {{ $q.GoName }} represents a row from '{{ schema $q.SQLName }}'. 70 | {{- end }} 71 | type {{ $q.GoName }} struct { 72 | {{ range $q.Fields -}} 73 | {{ field . }} 74 | {{ end -}} 75 | } 76 | {{ end }} 77 | -------------------------------------------------------------------------------- /templates/json/dbtpl.dbtpl.json.tpl: -------------------------------------------------------------------------------- 1 | {{ define "json" -}} 2 | {{ .Data | json }} 3 | {{ end }} 4 | -------------------------------------------------------------------------------- /templates/json/json.go: -------------------------------------------------------------------------------- 1 | //go:build dbtpl 2 | 3 | package json 4 | 5 | import ( 6 | "bytes" 7 | "context" 8 | "encoding/json" 9 | "strings" 10 | "text/template" 11 | 12 | xo "github.com/xo/dbtpl/types" 13 | ) 14 | 15 | // Init registers the template. 16 | func Init(ctx context.Context, f func(xo.TemplateType)) error { 17 | f(xo.TemplateType{ 18 | Modes: []string{"query", "schema"}, 19 | Flags: []xo.Flag{ 20 | { 21 | ContextKey: IndentKey, 22 | Type: "string", 23 | Desc: "indent spacing", 24 | Default: " ", 25 | }, 26 | { 27 | ContextKey: UglyKey, 28 | Type: "bool", 29 | Desc: "disable indentation", 30 | }, 31 | }, 32 | Funcs: func(ctx context.Context, _ string) (template.FuncMap, error) { 33 | return template.FuncMap{ 34 | // json marshals v as json. 35 | "json": func(v any) (string, error) { 36 | buf := new(bytes.Buffer) 37 | enc := json.NewEncoder(buf) 38 | if !Ugly(ctx) { 39 | enc.SetIndent("", Indent(ctx)) 40 | } 41 | if err := enc.Encode(v); err != nil { 42 | return "", err 43 | } 44 | return strings.TrimSpace(buf.String()), nil 45 | }, 46 | }, nil 47 | }, 48 | Process: func(ctx context.Context, _ string, set *xo.Set, emit func(xo.Template)) error { 49 | emit(xo.Template{ 50 | Partial: "json", 51 | Dest: "dbtpl.dbtpl.json", 52 | Data: set, 53 | }) 54 | return nil 55 | }, 56 | }) 57 | return nil 58 | } 59 | 60 | // Context keys. 61 | var ( 62 | IndentKey xo.ContextKey = "indent" 63 | UglyKey xo.ContextKey = "ugly" 64 | ) 65 | 66 | // Indent returns indent from the context. 67 | func Indent(ctx context.Context) string { 68 | s, _ := ctx.Value(IndentKey).(string) 69 | return s 70 | } 71 | 72 | // Ugly returns ugly from the context. 73 | func Ugly(ctx context.Context) bool { 74 | b, _ := ctx.Value(UglyKey).(bool) 75 | return b 76 | } 77 | -------------------------------------------------------------------------------- /templates/yaml/dbtpl.dbtpl.yaml.tpl: -------------------------------------------------------------------------------- 1 | {{ define "yaml" -}} 2 | --- 3 | # Generated by dbtpl. 4 | {{ .Data | yaml -}} 5 | {{ end }} 6 | -------------------------------------------------------------------------------- /templates/yaml/yaml.go: -------------------------------------------------------------------------------- 1 | //go:build dbtpl 2 | 3 | package yaml 4 | 5 | import ( 6 | "context" 7 | "text/template" 8 | 9 | "github.com/goccy/go-yaml" 10 | xo "github.com/xo/dbtpl/types" 11 | ) 12 | 13 | // Init registers the template. 14 | func Init(ctx context.Context, f func(xo.TemplateType)) error { 15 | f(xo.TemplateType{ 16 | Modes: []string{"query", "schema"}, 17 | Flags: []xo.Flag{}, 18 | Funcs: func(ctx context.Context, _ string) (template.FuncMap, error) { 19 | return template.FuncMap{ 20 | // yaml marshals v as yaml. 21 | "yaml": func(v any) (string, error) { 22 | buf, err := yaml.MarshalWithOptions(v) 23 | if err != nil { 24 | return "", err 25 | } 26 | return string(buf), nil 27 | }, 28 | }, nil 29 | }, 30 | Process: func(ctx context.Context, _ string, set *xo.Set, emit func(xo.Template)) error { 31 | emit(xo.Template{ 32 | Partial: "yaml", 33 | Dest: "dbtpl.dbtpl.yaml", 34 | Data: set, 35 | }) 36 | return nil 37 | }, 38 | }) 39 | return nil 40 | } 41 | --------------------------------------------------------------------------------