[chore] Update bun and sqlite dependencies (#478)

* update bun + sqlite versions

* step bun to v1.1.3
This commit is contained in:
tobi
2022-04-24 12:26:22 +02:00
committed by GitHub
parent 8d34d5af3c
commit 88979b35d4
246 changed files with 409690 additions and 148967 deletions

View File

@ -1,3 +1,3 @@
# Patterns for files created by this project.
# For other files, use global gitignore.
*.s3db
*.prof
*.test

View File

@ -1,109 +1,287 @@
## [1.0.20](https://github.com/uptrace/bun/compare/v1.0.19...v1.0.20) (2021-12-19)
# [](https://github.com/uptrace/bun/compare/v1.1.2...v) (2022-03-29)
### Bug Fixes
* add Event.QueryTemplate and change Event.Query to be always formatted ([52b1ccd](https://github.com/uptrace/bun/commit/52b1ccdf3578418aa427adef9dcf942d90ae4fdd))
* change GetTableName to return formatted table name in case ModelTableExpr ([95144dd](https://github.com/uptrace/bun/commit/95144dde937b4ac88b36b0bd8b01372421069b44))
* change ScanAndCount to work with transactions ([5b3f2c0](https://github.com/uptrace/bun/commit/5b3f2c021c424da366caffd33589e8adde821403))
* **dbfixture:** directly call funcs bypassing template eval ([a61974b](https://github.com/uptrace/bun/commit/a61974ba2d24361c5357fb9bda1f3eceec5a45cd))
* don't append CASCADE by default in drop table/column queries ([26457ea](https://github.com/uptrace/bun/commit/26457ea5cb20862d232e6e5fa4dbdeac5d444bf1))
* **migrate:** mark migrations as applied on error so the migration can be rolled back ([8ce33fb](https://github.com/uptrace/bun/commit/8ce33fbbac8e33077c20daf19a14c5ff2291bcae))
* respect nullzero when appending struct fields. Fixes [#339](https://github.com/uptrace/bun/issues/339) ([ffd02f3](https://github.com/uptrace/bun/commit/ffd02f3170b3cccdd670a48d563cfb41094c05d6))
* reuse tx for relation join ([#366](https://github.com/uptrace/bun/issues/366)) ([60bdb1a](https://github.com/uptrace/bun/commit/60bdb1ac84c0a699429eead3b7fdfbf14fe69ac6))
* fix panic message when has-many encounter an error ([cfd2747](https://github.com/uptrace/bun/commit/cfd27475fac89a1c8cf798bfa64898bd77bbba79))
* **migrate:** change rollback to match migrate behavior ([df5af9c](https://github.com/uptrace/bun/commit/df5af9c9cbdf54ce243e037bbb2c7b154f8422b3))
### Features
* add `Dialect()` to Transaction and IDB interface ([693f1e1](https://github.com/uptrace/bun/commit/693f1e135999fc31cf83b99a2530a695b20f4e1b))
* add model embedding via embed:prefix_ ([9a2cedc](https://github.com/uptrace/bun/commit/9a2cedc8b08fa8585d4bfced338bd0a40d736b1d))
* change the default logoutput to stderr ([4bf5773](https://github.com/uptrace/bun/commit/4bf577382f19c64457cbf0d64490401450954654)), closes [#349](https://github.com/uptrace/bun/issues/349)
* added QueryBuilder interface for SelectQuery, UpdateQuery, DeleteQuery ([#499](https://github.com/uptrace/bun/issues/499)) ([59fef48](https://github.com/uptrace/bun/commit/59fef48f6b3ec7f32bdda779b6693c333ff1dfdb))
# [1.1.2](https://github.com/uptrace/bun/compare/v1.1.2...v) (2022-03-22)
### Bug Fixes
- correctly handle bun.In([][]byte{...})
([800616e](https://github.com/uptrace/bun/commit/800616ed28ca600ad676319a10adb970b2b4daf6))
### Features
- accept extend option to allow extending existing models
([48b80e4](https://github.com/uptrace/bun/commit/48b80e4f7e3ed8a28fd305f7853ebe7ab984a497))
# [1.1.0](https://github.com/uptrace/bun/compare/v1.1.0-beta.1...v1.1.0) (2022-02-28)
### Features
- Added [MSSQL](https://bun.uptrace.dev/guide/drivers.html#mssql) support as a 4th fully supported
DBMS.
- Added `SetColumn("col_name", "upper(?)", "hello")` in addition to
`Set("col_name = upper(?)", "hello")` which works for all 4 supported DBMS.
* improve nil ptr values handling
([b398e6b](https://github.com/uptrace/bun/commit/b398e6bea840ea2fd3e001b7879c0b00b6dcd6f7))
### Breaking changes
- Bun no longer automatically marks some fields like `ID int64` as `pk` and `autoincrement`. You
need to manually add those options:
```diff
type Model struct {
- ID int64
+ ID int64 `bun:",pk,autoincrement"`
}
```
Bun [v1.0.25](#1024-2022-02-22) prints warnings for models with missing options so you are
recommended to upgrade to v1.0.24 before upgrading to v1.1.x.
- Also, Bun no longer adds `nullzero` option to `soft_delete` fields.
- Removed `nopk` and `allowzero` options.
### Bug Fixes
- append slice values
([4a65129](https://github.com/uptrace/bun/commit/4a651294fb0f1e73079553024810c3ead9777311))
- check for nils when appeding driver.Value
([7bb1640](https://github.com/uptrace/bun/commit/7bb1640a00fceca1e1075fe6544b9a4842ab2b26))
- cleanup soft deletes for mssql
([e72e2c5](https://github.com/uptrace/bun/commit/e72e2c5d0a85f3d26c3fa22c7284c2de1dcfda8e))
- **dbfixture:** apply cascade option. Fixes [#447](https://github.com/uptrace/bun/issues/447)
([d32d988](https://github.com/uptrace/bun/commit/d32d98840bc23e74c836f8192cb4bc9529aa9233))
- create table WithForeignKey() and has-many relation
([3cf5649](https://github.com/uptrace/bun/commit/3cf56491706b5652c383dbe007ff2389ad64922e))
- do not emit m2m relations in WithForeignKeys()
([56c8c5e](https://github.com/uptrace/bun/commit/56c8c5ed44c0d6d734c3d3161c642ce8437e2248))
- accept dest in select queries
([33b5b6f](https://github.com/uptrace/bun/commit/33b5b6ff660b77238a737a543ca12675c7f0c284))
## [1.0.25](https://github.com/uptrace/bun/compare/v1.0.23...v1.0.25) (2022-02-22)
### Bug Fixes
### Deprecated
In the comming v1.1.x release, Bun will stop automatically adding `,pk,autoincrement` options on
`ID int64/int32` fields. This version (v1.0.23) only prints a warning when it encounters such
fields, but the code will continue working as before.
To fix warnings, add missing options:
```diff
type Model struct {
- ID int64
+ ID int64 `bun:",pk,autoincrement"`
}
```
To silence warnings:
```go
bun.SetWarnLogger(log.New(ioutil.Discard, "", log.LstdFlags))
```
Bun will also print a warning on [soft delete](https://bun.uptrace.dev/guide/soft-deletes.html)
fields without a `,nullzero` option. You can fix the warning by adding missing `,nullzero` or
`,allowzero` options.
In v1.1.x, such options as `,nopk` and `,allowzero` will not be necessary and will be removed.
### Bug Fixes
- fix missing autoincrement warning
([3bc9c72](https://github.com/uptrace/bun/commit/3bc9c721e1c1c5104c256a0c01c4525df6ecefc2))
* append slice values
([4a65129](https://github.com/uptrace/bun/commit/4a651294fb0f1e73079553024810c3ead9777311))
* don't automatically set pk, nullzero, and autoincrement options
([519a0df](https://github.com/uptrace/bun/commit/519a0df9707de01a418aba0d6b7482cfe4c9a532))
### Features
- add CreateTableQuery.DetectForeignKeys
([a958fcb](https://github.com/uptrace/bun/commit/a958fcbab680b0c5ad7980f369c7b73f7673db87))
## [1.0.22](https://github.com/uptrace/bun/compare/v1.0.21...v1.0.22) (2022-01-28)
### Bug Fixes
- improve scan error message
([54048b2](https://github.com/uptrace/bun/commit/54048b296b9648fd62107ce6fa6fd7e6e2a648c7))
- properly discover json.Marshaler on ptr field
([3b321b0](https://github.com/uptrace/bun/commit/3b321b08601c4b8dc6bcaa24adea20875883ac14))
### Breaking (MySQL, MariaDB)
- **insert:** get last insert id only with pk support auto increment
([79e7c79](https://github.com/uptrace/bun/commit/79e7c797beea54bfc9dc1cb0141a7520ff941b4d)). Make
sure your MySQL models have `bun:",pk,autoincrement"` options if you are using autoincrements.
### Features
- refuse to start when version check does not pass
([ff8d767](https://github.com/uptrace/bun/commit/ff8d76794894eeaebede840e5199720f3f5cf531))
- support Column in ValuesQuery
([0707679](https://github.com/uptrace/bun/commit/0707679b075cac57efa8e6fe9019b57b2da4bcc7))
## [1.0.21](https://github.com/uptrace/bun/compare/v1.0.20...v1.0.21) (2022-01-06)
### Bug Fixes
- append where to index create
([1de6cea](https://github.com/uptrace/bun/commit/1de6ceaa8bba59b69fbe0cc6916d1b27da5586d8))
- check if slice is nil when calling BeforeAppendModel
([938d9da](https://github.com/uptrace/bun/commit/938d9dadb72ceeeb906064d9575278929d20cbbe))
- **dbfixture:** directly set matching types via reflect
([780504c](https://github.com/uptrace/bun/commit/780504cf1da687fc51a22d002ea66e2ccc41e1a3))
- properly handle driver.Valuer and type:json
([a17454a](https://github.com/uptrace/bun/commit/a17454ac6b95b2a2e927d0c4e4aee96494108389))
- support scanning string into uint64
([73cc117](https://github.com/uptrace/bun/commit/73cc117a9f7a623ced1fdaedb4546e8e7470e4d3))
- unique module name for opentelemetry example
([f2054fe](https://github.com/uptrace/bun/commit/f2054fe1d11cea3b21d69dab6f6d6d7d97ba06bb))
### Features
- add anonymous fields with type name
([508375b](https://github.com/uptrace/bun/commit/508375b8f2396cb088fd4399a9259584353eb7e5))
- add baseQuery.GetConn()
([81a9bee](https://github.com/uptrace/bun/commit/81a9beecb74fed7ec3574a1d42acdf10a74e0b00))
- create new queries from baseQuery
([ae1dd61](https://github.com/uptrace/bun/commit/ae1dd611a91c2b7c79bc2bc12e9a53e857791e71))
- support INSERT ... RETURNING for MariaDB >= 10.5.0
([b6531c0](https://github.com/uptrace/bun/commit/b6531c00ecbd4c7ec56b4131fab213f9313edc1b))
## [1.0.20](https://github.com/uptrace/bun/compare/v1.0.19...v1.0.20) (2021-12-19)
### Bug Fixes
- add Event.QueryTemplate and change Event.Query to be always formatted
([52b1ccd](https://github.com/uptrace/bun/commit/52b1ccdf3578418aa427adef9dcf942d90ae4fdd))
- change GetTableName to return formatted table name in case ModelTableExpr
([95144dd](https://github.com/uptrace/bun/commit/95144dde937b4ac88b36b0bd8b01372421069b44))
- change ScanAndCount to work with transactions
([5b3f2c0](https://github.com/uptrace/bun/commit/5b3f2c021c424da366caffd33589e8adde821403))
- **dbfixture:** directly call funcs bypassing template eval
([a61974b](https://github.com/uptrace/bun/commit/a61974ba2d24361c5357fb9bda1f3eceec5a45cd))
- don't append CASCADE by default in drop table/column queries
([26457ea](https://github.com/uptrace/bun/commit/26457ea5cb20862d232e6e5fa4dbdeac5d444bf1))
- **migrate:** mark migrations as applied on error so the migration can be rolled back
([8ce33fb](https://github.com/uptrace/bun/commit/8ce33fbbac8e33077c20daf19a14c5ff2291bcae))
- respect nullzero when appending struct fields. Fixes
[#339](https://github.com/uptrace/bun/issues/339)
([ffd02f3](https://github.com/uptrace/bun/commit/ffd02f3170b3cccdd670a48d563cfb41094c05d6))
- reuse tx for relation join ([#366](https://github.com/uptrace/bun/issues/366))
([60bdb1a](https://github.com/uptrace/bun/commit/60bdb1ac84c0a699429eead3b7fdfbf14fe69ac6))
### Features
- add `Dialect()` to Transaction and IDB interface
([693f1e1](https://github.com/uptrace/bun/commit/693f1e135999fc31cf83b99a2530a695b20f4e1b))
- add model embedding via embed:prefix\_
([9a2cedc](https://github.com/uptrace/bun/commit/9a2cedc8b08fa8585d4bfced338bd0a40d736b1d))
- change the default logoutput to stderr
([4bf5773](https://github.com/uptrace/bun/commit/4bf577382f19c64457cbf0d64490401450954654)),
closes [#349](https://github.com/uptrace/bun/issues/349)
## [1.0.19](https://github.com/uptrace/bun/compare/v1.0.18...v1.0.19) (2021-11-30)
### Features
* add support for column:name to specify column name ([e37b460](https://github.com/uptrace/bun/commit/e37b4602823babc8221970e086cfed90c6ad4cf4))
- add support for column:name to specify column name
([e37b460](https://github.com/uptrace/bun/commit/e37b4602823babc8221970e086cfed90c6ad4cf4))
## [1.0.18](https://github.com/uptrace/bun/compare/v1.0.17...v1.0.18) (2021-11-24)
### Bug Fixes
* use correct operation for UpdateQuery ([687a004](https://github.com/uptrace/bun/commit/687a004ef7ec6fe1ef06c394965dd2c2d822fc82))
- use correct operation for UpdateQuery
([687a004](https://github.com/uptrace/bun/commit/687a004ef7ec6fe1ef06c394965dd2c2d822fc82))
### Features
* add pgdriver.Notify ([7ee443d](https://github.com/uptrace/bun/commit/7ee443d1b869d8ddc4746850f7425d0a9ccd012b))
* CreateTableQuery.PartitionBy and CreateTableQuery.TableSpace ([cd3ab4d](https://github.com/uptrace/bun/commit/cd3ab4d8f3682f5a30b87c2ebc2d7e551d739078))
* **pgdriver:** add CopyFrom and CopyTo ([0b97703](https://github.com/uptrace/bun/commit/0b977030b5c05f509e11d13550b5f99dfd62358d))
* support InsertQuery.Ignore on PostgreSQL ([1aa9d14](https://github.com/uptrace/bun/commit/1aa9d149da8e46e63ff79192e394fde4d18d9b60))
- add pgdriver.Notify
([7ee443d](https://github.com/uptrace/bun/commit/7ee443d1b869d8ddc4746850f7425d0a9ccd012b))
- CreateTableQuery.PartitionBy and CreateTableQuery.TableSpace
([cd3ab4d](https://github.com/uptrace/bun/commit/cd3ab4d8f3682f5a30b87c2ebc2d7e551d739078))
- **pgdriver:** add CopyFrom and CopyTo
([0b97703](https://github.com/uptrace/bun/commit/0b977030b5c05f509e11d13550b5f99dfd62358d))
- support InsertQuery.Ignore on PostgreSQL
([1aa9d14](https://github.com/uptrace/bun/commit/1aa9d149da8e46e63ff79192e394fde4d18d9b60))
## [1.0.17](https://github.com/uptrace/bun/compare/v1.0.16...v1.0.17) (2021-11-11)
### Bug Fixes
* don't call rollback when tx is already done ([8246c2a](https://github.com/uptrace/bun/commit/8246c2a63e2e6eba314201c6ba87f094edf098b9))
* **mysql:** escape backslash char in strings ([fb32029](https://github.com/uptrace/bun/commit/fb32029ea7604d066800b16df21f239b71bf121d))
- don't call rollback when tx is already done
([8246c2a](https://github.com/uptrace/bun/commit/8246c2a63e2e6eba314201c6ba87f094edf098b9))
- **mysql:** escape backslash char in strings
([fb32029](https://github.com/uptrace/bun/commit/fb32029ea7604d066800b16df21f239b71bf121d))
## [1.0.16](https://github.com/uptrace/bun/compare/v1.0.15...v1.0.16) (2021-11-07)
### Bug Fixes
* call query hook when tx is started, committed, or rolled back ([30e85b5](https://github.com/uptrace/bun/commit/30e85b5366b2e51951ef17a0cf362b58f708dab1))
* **pgdialect:** auto-enable array support if the sql type is an array ([62c1012](https://github.com/uptrace/bun/commit/62c1012b2482e83969e5c6f5faf89e655ce78138))
- call query hook when tx is started, committed, or rolled back
([30e85b5](https://github.com/uptrace/bun/commit/30e85b5366b2e51951ef17a0cf362b58f708dab1))
- **pgdialect:** auto-enable array support if the sql type is an array
([62c1012](https://github.com/uptrace/bun/commit/62c1012b2482e83969e5c6f5faf89e655ce78138))
### Features
* support multiple tag options join:left_col1=right_col1,join:left_col2=right_col2 ([78cd5aa](https://github.com/uptrace/bun/commit/78cd5aa60a5c7d1323bb89081db2b2b811113052))
* **tag:** log with bad tag name ([4e82d75](https://github.com/uptrace/bun/commit/4e82d75be2dabdba1a510df4e1fbb86092f92f4c))
- support multiple tag options join:left_col1=right_col1,join:left_col2=right_col2
([78cd5aa](https://github.com/uptrace/bun/commit/78cd5aa60a5c7d1323bb89081db2b2b811113052))
- **tag:** log with bad tag name
([4e82d75](https://github.com/uptrace/bun/commit/4e82d75be2dabdba1a510df4e1fbb86092f92f4c))
## [1.0.15](https://github.com/uptrace/bun/compare/v1.0.14...v1.0.15) (2021-10-29)
### Bug Fixes
* fixed bug creating table when model has no columns ([042c50b](https://github.com/uptrace/bun/commit/042c50bfe41caaa6e279e02c887c3a84a3acd84f))
* init table with dialect once ([9a1ce1e](https://github.com/uptrace/bun/commit/9a1ce1e492602742bb2f587e9ed24e50d7d07cad))
- fixed bug creating table when model has no columns
([042c50b](https://github.com/uptrace/bun/commit/042c50bfe41caaa6e279e02c887c3a84a3acd84f))
- init table with dialect once
([9a1ce1e](https://github.com/uptrace/bun/commit/9a1ce1e492602742bb2f587e9ed24e50d7d07cad))
### Features
* accept columns in WherePK ([b3e7035](https://github.com/uptrace/bun/commit/b3e70356db1aa4891115a10902316090fccbc8bf))
* support ADD COLUMN IF NOT EXISTS ([ca7357c](https://github.com/uptrace/bun/commit/ca7357cdfe283e2f0b94eb638372e18401c486e9))
- accept columns in WherePK
([b3e7035](https://github.com/uptrace/bun/commit/b3e70356db1aa4891115a10902316090fccbc8bf))
- support ADD COLUMN IF NOT EXISTS
([ca7357c](https://github.com/uptrace/bun/commit/ca7357cdfe283e2f0b94eb638372e18401c486e9))
## [1.0.14](https://github.com/uptrace/bun/compare/v1.0.13...v1.0.14) (2021-10-24)
### Bug Fixes
* correct binary serialization for mysql ([#259](https://github.com/uptrace/bun/issues/259)) ([e899f50](https://github.com/uptrace/bun/commit/e899f50b22ef6759ef8c029a6cd3f25f2bde17ef))
* correctly escape single quotes in pg arrays ([3010847](https://github.com/uptrace/bun/commit/3010847f5c2c50bce1969689a0b77fd8a6fb7e55))
* use BLOB sql type to encode []byte in MySQL and SQLite ([725ec88](https://github.com/uptrace/bun/commit/725ec8843824a7fc8f4058ead75ab0e62a78192a))
- correct binary serialization for mysql ([#259](https://github.com/uptrace/bun/issues/259))
([e899f50](https://github.com/uptrace/bun/commit/e899f50b22ef6759ef8c029a6cd3f25f2bde17ef))
- correctly escape single quotes in pg arrays
([3010847](https://github.com/uptrace/bun/commit/3010847f5c2c50bce1969689a0b77fd8a6fb7e55))
- use BLOB sql type to encode []byte in MySQL and SQLite
([725ec88](https://github.com/uptrace/bun/commit/725ec8843824a7fc8f4058ead75ab0e62a78192a))
### Features
* warn when there are args but no placeholders ([06dde21](https://github.com/uptrace/bun/commit/06dde215c8d0bde2b2364597190729a160e536a1))
- warn when there are args but no placeholders
([06dde21](https://github.com/uptrace/bun/commit/06dde215c8d0bde2b2364597190729a160e536a1))
## [1.0.13](https://github.com/uptrace/bun/compare/v1.0.12...v1.0.13) (2021-10-17)

View File

@ -7,6 +7,12 @@ cd internal/dbtest
./test.sh
```
To ease debugging, you can run tests and print all executed queries:
```shell
BUNDEBUG=2 TZ= go test -run=TestName
```
## Releasing
1. Run `release.sh` script which updates versions in go.mod files and pushes a new branch to GitHub:
@ -22,3 +28,7 @@ TAG=v1.0.0 ./scripts/release.sh
```shell
TAG=v1.0.0 ./scripts/tag.sh
```
## Documentation
To contribute to the docs visit https://github.com/go-bun/bun-docs

View File

@ -1,4 +1,5 @@
ALL_GO_MOD_DIRS := $(shell find . -type f -name 'go.mod' -exec dirname {} \; | sort)
EXAMPLE_GO_MOD_DIRS := $(shell find ./example/ -type f -name 'go.mod' -exec dirname {} \; | sort)
test:
set -e; for dir in $(ALL_GO_MOD_DIRS); do \
@ -10,14 +11,20 @@ test:
done
go_mod_tidy:
go get -u && go mod tidy
go get -u && go mod tidy -go=1.17
set -e; for dir in $(ALL_GO_MOD_DIRS); do \
echo "go mod tidy in $${dir}"; \
(cd "$${dir}" && \
go get -u ./... && \
go mod tidy); \
go mod tidy -go=1.17); \
done
fmt:
gofmt -w -s ./
goimports -w -local github.com/uptrace/bun ./
run-examples:
set -e; for dir in $(EXAMPLE_GO_MOD_DIRS); do \
echo "go run . in $${dir}"; \
(cd "$${dir}" && go run .); \
done

View File

@ -1,25 +1,22 @@
<p align="center">
<a href="https://uptrace.dev/?utm_source=gh-redis&utm_campaign=gh-redis-banner1">
<img src="https://raw.githubusercontent.com/uptrace/roadmap/master/banner1.png" alt="All-in-one tool to optimize performance and monitor errors & logs">
</a>
</p>
# Simple and performant client for PostgreSQL, MySQL, and SQLite
# SQL-first Go ORM for PostgreSQL, MySQL, MSSQL, and SQLite
[![build workflow](https://github.com/uptrace/bun/actions/workflows/build.yml/badge.svg)](https://github.com/uptrace/bun/actions)
[![PkgGoDev](https://pkg.go.dev/badge/github.com/uptrace/bun)](https://pkg.go.dev/github.com/uptrace/bun)
[![Documentation](https://img.shields.io/badge/bun-documentation-informational)](https://bun.uptrace.dev/)
[![Chat](https://discordapp.com/api/guilds/752070105847955518/widget.png)](https://discord.gg/rWtp5Aj)
**Status**: API freeze (stable release). Note that all sub-packages (mainly extra/\* packages) are
not part of the API freeze and are developed independently. You can think of them as of 3rd party
packages that share one repo with the core.
Bun is brought to you by :star: [**uptrace/uptrace**](https://github.com/uptrace/uptrace). Uptrace
is an open source and blazingly fast **distributed tracing** backend powered by OpenTelemetry and
ClickHouse. Give it a star as well!
Main features are:
## Features
- Works with [PostgreSQL](https://bun.uptrace.dev/guide/drivers.html#postgresql),
[MySQL](https://bun.uptrace.dev/guide/drivers.html#mysql) (including MariaDB),
[MSSQL](https://bun.uptrace.dev/guide/drivers.html#mssql),
[SQLite](https://bun.uptrace.dev/guide/drivers.html#sqlite).
- [Selecting](/example/basic/) into scalars, structs, maps, slices of maps/structs/scalars.
- [ORM-like](/example/basic/) experience using good old SQL. Bun supports structs, map, scalars, and
slices of map/structs/scalars.
- [Bulk inserts](https://bun.uptrace.dev/guide/query-insert.html).
- [Bulk updates](https://bun.uptrace.dev/guide/query-update.html) using common table expressions.
- [Bulk deletes](https://bun.uptrace.dev/guide/query-delete.html).
@ -32,19 +29,24 @@ Resources:
- [**Get started**](https://bun.uptrace.dev/guide/getting-started.html)
- [Examples](https://github.com/uptrace/bun/tree/master/example)
- [Discussions](https://github.com/uptrace/bun/discussions)
- [Newsletter](https://blog.uptrace.dev/pages/newsletter.html) to get latest updates.
- [Chat](https://discord.gg/rWtp5Aj)
- [Reference](https://pkg.go.dev/github.com/uptrace/bun)
- [Starter kit](https://github.com/go-bun/bun-starter-kit)
Projects using Bun:
- [gotosocial](https://github.com/superseriousbusiness/gotosocial) - Golang fediverse server.
- [qvalet](https://github.com/cmaster11/qvalet) listens for HTTP requests and executes commands on
demand.
- [alexedwards/scs](https://github.com/alexedwards/scs) - HTTP Session Management for Go.
- [emerald-web3-gateway](https://github.com/oasisprotocol/emerald-web3-gateway) - Web3 Gateway for
the Oasis Emerald paratime.
- [lndhub.go](https://github.com/getAlby/lndhub.go) - accounting wrapper for the Lightning Network.
- [RealWorld app](https://github.com/go-bun/bun-realworld-app)
- And hundreds more.
## Benchmark
[https://github.com/davars/dbeval](https://github.com/davars/dbeval)
<details>
<summary>results</summary>
@ -182,6 +184,7 @@ BenchmarkRecentArticles/*dbeval.PGX-4 356 3345500 ns/op 329
</details>
[https://github.com/frederikhors/orm-benchmark](https://github.com/frederikhors/orm-benchmark)
<details>
<summary>results</summary>
@ -284,16 +287,19 @@ WHERE region IN (SELECT region FROM top_regions)
GROUP BY region, product
```
And scan results into scalars, structs, maps, slices of structs/maps/scalars.
## Installation
And scan results into scalars, structs, maps, slices of structs/maps/scalars:
```go
go get github.com/uptrace/bun
```
users := make([]User, 0)
if err := db.NewSelect().Model(&users).OrderExpr("id ASC").Scan(ctx); err != nil {
panic(err)
}
You also need to install a database/sql driver and the corresponding Bun
[dialect](https://bun.uptrace.dev/guide/drivers.html).
user1 := new(User)
if err := db.NewSelect().Model(user1).Where("id = ?", 1).Scan(ctx); err != nil {
panic(err)
}
```
See [**Getting started**](https://bun.uptrace.dev/guide/getting-started.html) guide and check
[examples](example).

53
vendor/github.com/uptrace/bun/bun.go generated vendored
View File

@ -2,8 +2,6 @@ package bun
import (
"context"
"fmt"
"reflect"
"github.com/uptrace/bun/internal"
"github.com/uptrace/bun/schema"
@ -81,53 +79,6 @@ func SetLogger(logger internal.Logging) {
internal.Logger = logger
}
//------------------------------------------------------------------------------
type InValues struct {
slice reflect.Value
err error
}
var _ schema.QueryAppender = InValues{}
func In(slice interface{}) InValues {
v := reflect.ValueOf(slice)
if v.Kind() != reflect.Slice {
return InValues{
err: fmt.Errorf("bun: In(non-slice %T)", slice),
}
}
return InValues{
slice: v,
}
}
func (in InValues) AppendQuery(fmter schema.Formatter, b []byte) (_ []byte, err error) {
if in.err != nil {
return nil, in.err
}
return appendIn(fmter, b, in.slice), nil
}
func appendIn(fmter schema.Formatter, b []byte, slice reflect.Value) []byte {
sliceLen := slice.Len()
for i := 0; i < sliceLen; i++ {
if i > 0 {
b = append(b, ", "...)
}
elem := slice.Index(i)
if elem.Kind() == reflect.Interface {
elem = elem.Elem()
}
if elem.Kind() == reflect.Slice {
b = append(b, '(')
b = appendIn(fmter, b, elem)
b = append(b, ')')
} else {
b = fmter.AppendValue(b, elem)
}
}
return b
func In(slice interface{}) schema.QueryAppender {
return schema.In(slice)
}

View File

@ -10,6 +10,8 @@ func (n Name) String() string {
return "sqlite"
case MySQL:
return "mysql"
case MSSQL:
return "mssql"
default:
return "invalid"
}
@ -20,4 +22,5 @@ const (
PG
SQLite
MySQL
MSSQL
)

View File

@ -6,18 +6,27 @@ type Feature = internal.Flag
const (
CTE Feature = 1 << iota
WithValues
Returning
InsertReturning
Output // mssql
DefaultPlaceholder
DoubleColonCast
ValuesRow
UpdateMultiTable
InsertTableAlias
UpdateTableAlias
DeleteTableAlias
AutoIncrement
Identity
TableCascade
TableIdentity
TableTruncate
InsertOnConflict // INSERT ... ON CONFLICT
InsertOnDuplicateKey // INSERT ... ON DUPLICATE KEY
InsertIgnore // INSERT IGNORE ...
TableNotExists
OffsetFetch
SelectExists
UpdateFromTable
)

View File

@ -2,9 +2,11 @@ package pgdialect
import (
"database/sql"
"fmt"
"strconv"
"strings"
"github.com/uptrace/bun"
"github.com/uptrace/bun/dialect"
"github.com/uptrace/bun/dialect/feature"
"github.com/uptrace/bun/dialect/sqltype"
@ -13,6 +15,13 @@ import (
var pgDialect = New()
func init() {
if Version() != bun.Version() {
panic(fmt.Errorf("pgdialect and Bun must have the same version: v%s != v%s",
Version(), bun.Version()))
}
}
type Dialect struct {
schema.BaseDialect
@ -24,15 +33,20 @@ func New() *Dialect {
d := new(Dialect)
d.tables = schema.NewTables(d)
d.features = feature.CTE |
feature.WithValues |
feature.Returning |
feature.InsertReturning |
feature.DefaultPlaceholder |
feature.DoubleColonCast |
feature.InsertTableAlias |
feature.UpdateTableAlias |
feature.DeleteTableAlias |
feature.TableCascade |
feature.TableIdentity |
feature.TableTruncate |
feature.InsertOnConflict
feature.TableNotExists |
feature.InsertOnConflict |
feature.SelectExists
return d
}

View File

@ -53,7 +53,6 @@ func fieldSQLType(field *schema.Field) string {
if v, ok := field.Tag.Option("composite"); ok {
return v
}
if _, ok := field.Tag.Option("hstore"); ok {
return "hstore"
}

View File

@ -0,0 +1,6 @@
package pgdialect
// Version is the current release version.
func Version() string {
return "1.1.3"
}

View File

@ -3,13 +3,22 @@ package sqlitedialect
import (
"database/sql"
"encoding/hex"
"fmt"
"github.com/uptrace/bun"
"github.com/uptrace/bun/dialect"
"github.com/uptrace/bun/dialect/feature"
"github.com/uptrace/bun/dialect/sqltype"
"github.com/uptrace/bun/schema"
)
func init() {
if Version() != bun.Version() {
panic(fmt.Errorf("sqlitedialect and Bun must have the same version: v%s != v%s",
Version(), bun.Version()))
}
}
type Dialect struct {
schema.BaseDialect
@ -21,10 +30,15 @@ func New() *Dialect {
d := new(Dialect)
d.tables = schema.NewTables(d)
d.features = feature.CTE |
feature.WithValues |
feature.Returning |
feature.InsertReturning |
feature.InsertTableAlias |
feature.UpdateTableAlias |
feature.DeleteTableAlias |
feature.InsertOnConflict
feature.InsertOnConflict |
feature.TableNotExists |
feature.SelectExists
return d
}

View File

@ -0,0 +1,6 @@
package sqlitedialect
// Version is the current release version.
func Version() string {
return "1.1.3"
}

View File

@ -3,7 +3,7 @@ package internal
type Flag uint64
func (flag Flag) Has(other Flag) bool {
return flag&other == other
return flag&other != 0
}
func (flag Flag) Set(other Flag) Flag {

View File

@ -16,7 +16,7 @@ import (
type Migration struct {
bun.BaseModel
ID int64
ID int64 `bun:",pk,autoincrement"`
Name string
GroupID int64
MigratedAt time.Time `bun:",notnull,nullzero,default:current_timestamp"`

View File

@ -136,24 +136,25 @@ func (m *Migrator) Migrate(ctx context.Context, opts ...MigrationOption) (*Migra
if err != nil {
return nil, err
}
migrations = migrations.Unapplied()
group := &MigrationGroup{
Migrations: migrations.Unapplied(),
}
if len(group.Migrations) == 0 {
group := new(MigrationGroup)
if len(migrations) == 0 {
return group, nil
}
group.ID = lastGroupID + 1
for i := range group.Migrations {
migration := &group.Migrations[i]
for i := range migrations {
migration := &migrations[i]
migration.GroupID = group.ID
// Always mark migration as applied so the rollback has a chance to fix the database.
if err := m.MarkApplied(ctx, migration); err != nil {
return nil, err
return group, err
}
group.Migrations = migrations[:i+1]
if !cfg.nop && migration.Up != nil {
if err := migration.Up(ctx, m.db); err != nil {
return group, err
@ -186,15 +187,16 @@ func (m *Migrator) Rollback(ctx context.Context, opts ...MigrationOption) (*Migr
for i := len(lastGroup.Migrations) - 1; i >= 0; i-- {
migration := &lastGroup.Migrations[i]
// Always mark migration as unapplied to match migrate behavior.
if err := m.MarkUnapplied(ctx, migration); err != nil {
return nil, err
}
if !cfg.nop && migration.Down != nil {
if err := migration.Down(ctx, m.db); err != nil {
return nil, err
}
}
if err := m.MarkUnapplied(ctx, migration); err != nil {
return nil, err
}
}
return lastGroup, nil
@ -340,7 +342,7 @@ func (m *Migrator) validate() error {
//------------------------------------------------------------------------------
type migrationLock struct {
ID int64
ID int64 `bun:",pk,autoincrement"`
TableName string `bun:",unique"`
}

View File

@ -50,15 +50,6 @@ func (m *sliceTableModel) join(name string) *relationJoin {
return m._join(m.slice, name)
}
func (m *sliceTableModel) SetCap(cap int) {
if cap > 100 {
cap = 100
}
if m.slice.Cap() == 0 {
m.slice.Set(reflect.MakeSlice(m.slice.Type(), 0, cap))
}
}
func (m *sliceTableModel) ScanRows(ctx context.Context, rows *sql.Rows) (int, error) {
columns, err := rows.Columns()
if err != nil {
@ -94,7 +85,7 @@ func (m *sliceTableModel) ScanRows(ctx context.Context, rows *sql.Rows) (int, er
var _ schema.BeforeAppendModelHook = (*sliceTableModel)(nil)
func (m *sliceTableModel) BeforeAppendModel(ctx context.Context, query Query) error {
if !m.table.HasBeforeAppendModelHook() {
if !m.table.HasBeforeAppendModelHook() || !m.slice.IsValid() {
return nil
}

View File

@ -1,6 +1,6 @@
{
"name": "bun",
"version": "1.0.20",
"name": "uptrace/bun",
"version": "1.1.3",
"main": "index.js",
"repository": "git@github.com:uptrace/bun.git",
"author": "Vladimir Mihailenco <vladimir.webdev@gmail.com>",

View File

@ -65,6 +65,24 @@ var (
_ IDB = (*Tx)(nil)
)
// QueryBuilder is used for common query methods
type QueryBuilder interface {
Query
Where(query string, args ...interface{}) QueryBuilder
WhereGroup(sep string, fn func(QueryBuilder) QueryBuilder) QueryBuilder
WhereOr(query string, args ...interface{}) QueryBuilder
WhereDeleted() QueryBuilder
WhereAllWithDeleted() QueryBuilder
WherePK(cols ...string) QueryBuilder
Unwrap() interface{}
}
var (
_ QueryBuilder = (*selectQueryBuilder)(nil)
_ QueryBuilder = (*updateQueryBuilder)(nil)
_ QueryBuilder = (*deleteQueryBuilder)(nil)
)
type baseQuery struct {
db *DB
conn IConn
@ -87,6 +105,10 @@ func (q *baseQuery) DB() *DB {
return q.db
}
func (q *baseQuery) GetConn() IConn {
return q.conn
}
func (q *baseQuery) GetModel() Model {
return q.model
}
@ -105,12 +127,16 @@ func (q *baseQuery) GetTableName() string {
}
if q.modelTableName.Query != "" {
b, _ := q.modelTableName.AppendQuery(q.db.fmter, nil)
return string(b)
return q.modelTableName.Query
}
if len(q.tables) > 0 {
return q.tables[0].Query
b, _ := q.tables[0].AppendQuery(q.db.fmter, nil)
if len(b) < 64 {
return string(b)
}
}
return ""
}
@ -166,6 +192,10 @@ func (q *baseQuery) beforeAppendModel(ctx context.Context, query Query) error {
return nil
}
func (q *baseQuery) hasFeature(feature feature.Feature) bool {
return q.db.features.Has(feature)
}
//------------------------------------------------------------------------------
func (q *baseQuery) checkSoftDelete() error {
@ -228,29 +258,71 @@ func (q *baseQuery) appendWith(fmter schema.Formatter, b []byte) (_ []byte, err
b = append(b, ", "...)
}
b = fmter.AppendIdent(b, with.name)
if q, ok := with.query.(schema.ColumnsAppender); ok {
b = append(b, " ("...)
b, err = q.AppendColumns(fmter, b)
if err != nil {
return nil, err
}
b = append(b, ")"...)
}
b = append(b, " AS ("...)
b, err = with.query.AppendQuery(fmter, b)
b, err = q.appendCTE(fmter, b, with)
if err != nil {
return nil, err
}
b = append(b, ')')
}
b = append(b, ' ')
return b, nil
}
func (q *baseQuery) appendCTE(
fmter schema.Formatter, b []byte, cte withQuery,
) (_ []byte, err error) {
if !fmter.Dialect().Features().Has(feature.WithValues) {
if values, ok := cte.query.(*ValuesQuery); ok {
return q.appendSelectFromValues(fmter, b, cte, values)
}
}
b = fmter.AppendIdent(b, cte.name)
if q, ok := cte.query.(schema.ColumnsAppender); ok {
b = append(b, " ("...)
b, err = q.AppendColumns(fmter, b)
if err != nil {
return nil, err
}
b = append(b, ")"...)
}
b = append(b, " AS ("...)
b, err = cte.query.AppendQuery(fmter, b)
if err != nil {
return nil, err
}
b = append(b, ")"...)
return b, nil
}
func (q *baseQuery) appendSelectFromValues(
fmter schema.Formatter, b []byte, cte withQuery, values *ValuesQuery,
) (_ []byte, err error) {
b = fmter.AppendIdent(b, cte.name)
b = append(b, " AS (SELECT * FROM ("...)
b, err = cte.query.AppendQuery(fmter, b)
if err != nil {
return nil, err
}
b = append(b, ") AS t"...)
if q, ok := cte.query.(schema.ColumnsAppender); ok {
b = append(b, " ("...)
b, err = q.AppendColumns(fmter, b)
if err != nil {
return nil, err
}
b = append(b, ")"...)
}
b = append(b, ")"...)
return b, nil
}
//------------------------------------------------------------------------------
func (q *baseQuery) addTable(table schema.QueryWithArgs) {
@ -428,6 +500,9 @@ func (q *baseQuery) appendColumns(fmter schema.Formatter, b []byte) (_ []byte, e
func (q *baseQuery) getFields() ([]*schema.Field, error) {
if len(q.columns) == 0 {
if q.table == nil {
return nil, errNilModel
}
return q.table.Fields, nil
}
return q._getFields(false)
@ -435,6 +510,9 @@ func (q *baseQuery) getFields() ([]*schema.Field, error) {
func (q *baseQuery) getDataFields() ([]*schema.Field, error) {
if len(q.columns) == 0 {
if q.table == nil {
return nil, errNilModel
}
return q.table.DataFields, nil
}
return q._getFields(true)
@ -541,6 +619,62 @@ func (q *baseQuery) AppendNamedArg(fmter schema.Formatter, b []byte, name string
return b, false
}
//------------------------------------------------------------------------------
func (q *baseQuery) Dialect() schema.Dialect {
return q.db.Dialect()
}
func (q *baseQuery) NewValues(model interface{}) *ValuesQuery {
return NewValuesQuery(q.db, model).Conn(q.conn)
}
func (q *baseQuery) NewSelect() *SelectQuery {
return NewSelectQuery(q.db).Conn(q.conn)
}
func (q *baseQuery) NewInsert() *InsertQuery {
return NewInsertQuery(q.db).Conn(q.conn)
}
func (q *baseQuery) NewUpdate() *UpdateQuery {
return NewUpdateQuery(q.db).Conn(q.conn)
}
func (q *baseQuery) NewDelete() *DeleteQuery {
return NewDeleteQuery(q.db).Conn(q.conn)
}
func (q *baseQuery) NewCreateTable() *CreateTableQuery {
return NewCreateTableQuery(q.db).Conn(q.conn)
}
func (q *baseQuery) NewDropTable() *DropTableQuery {
return NewDropTableQuery(q.db).Conn(q.conn)
}
func (q *baseQuery) NewCreateIndex() *CreateIndexQuery {
return NewCreateIndexQuery(q.db).Conn(q.conn)
}
func (q *baseQuery) NewDropIndex() *DropIndexQuery {
return NewDropIndexQuery(q.db).Conn(q.conn)
}
func (q *baseQuery) NewTruncateTable() *TruncateTableQuery {
return NewTruncateTableQuery(q.db).Conn(q.conn)
}
func (q *baseQuery) NewAddColumn() *AddColumnQuery {
return NewAddColumnQuery(q.db).Conn(q.conn)
}
func (q *baseQuery) NewDropColumn() *DropColumnQuery {
return NewDropColumnQuery(q.db).Conn(q.conn)
}
//------------------------------------------------------------------------------
func appendColumns(b []byte, table schema.Safe, fields []*schema.Field) []byte {
for i, f := range fields {
if i > 0 {
@ -653,15 +787,18 @@ func (q *whereBaseQuery) appendWhere(
if len(b) > startLen {
b = append(b, " AND "...)
}
if withAlias {
b = append(b, q.tableModel.Table().SQLAlias...)
b = append(b, '.')
} else {
b = append(b, q.tableModel.Table().SQLName...)
}
b = append(b, '.')
field := q.tableModel.Table().SoftDeleteField
b = append(b, field.SQLName...)
if field.NullZero {
if field.IsPtr || field.NullZero {
if q.flags.Has(deletedFlag) {
b = append(b, " IS NOT NULL"...)
} else {
@ -843,27 +980,21 @@ func (q *returningQuery) addReturningField(field *schema.Field) {
q.returningFields = append(q.returningFields, field)
}
func (q *returningQuery) hasReturning() bool {
if len(q.returning) == 1 {
if ret := q.returning[0]; len(ret.Args) == 0 {
switch ret.Query {
case "", "null", "NULL":
return false
}
}
}
return len(q.returning) > 0 || len(q.returningFields) > 0
}
func (q *returningQuery) appendReturning(
fmter schema.Formatter, b []byte,
) (_ []byte, err error) {
if !q.hasReturning() {
return b, nil
}
return q._appendReturning(fmter, b, "")
}
b = append(b, " RETURNING "...)
func (q *returningQuery) appendOutput(
fmter schema.Formatter, b []byte,
) (_ []byte, err error) {
return q._appendReturning(fmter, b, "INSERTED")
}
func (q *returningQuery) _appendReturning(
fmter schema.Formatter, b []byte, table string,
) (_ []byte, err error) {
for i, f := range q.returning {
if i > 0 {
b = append(b, ", "...)
@ -878,10 +1009,22 @@ func (q *returningQuery) appendReturning(
return b, nil
}
b = appendColumns(b, "", q.returningFields)
b = appendColumns(b, schema.Safe(table), q.returningFields)
return b, nil
}
func (q *returningQuery) hasReturning() bool {
if len(q.returning) == 1 {
if ret := q.returning[0]; len(ret.Args) == 0 {
switch ret.Query {
case "", "null", "NULL":
return false
}
}
}
return len(q.returning) > 0 || len(q.returningFields) > 0
}
//------------------------------------------------------------------------------
type columnValue struct {

View File

@ -190,7 +190,8 @@ func (q *DeleteQuery) AppendQuery(fmter schema.Formatter, b []byte) (_ []byte, e
return nil, err
}
if len(q.returning) > 0 {
if q.hasFeature(feature.Returning) && q.hasReturning() {
b = append(b, " RETURNING "...)
b, err = q.appendReturning(fmter, b)
if err != nil {
return nil, err
@ -285,3 +286,48 @@ func (q *DeleteQuery) afterDeleteHook(ctx context.Context) error {
}
return nil
}
//------------------------------------------------------------------------------
type deleteQueryBuilder struct {
*DeleteQuery
}
func (q *deleteQueryBuilder) WhereGroup(sep string, fn func(QueryBuilder) QueryBuilder) QueryBuilder {
q.DeleteQuery = q.DeleteQuery.WhereGroup(sep, func(qs *DeleteQuery) *DeleteQuery {
return fn(q).(*deleteQueryBuilder).DeleteQuery
})
return q
}
func (q *deleteQueryBuilder) Where(query string, args ...interface{}) QueryBuilder {
q.DeleteQuery.Where(query, args...)
return q
}
func (q *deleteQueryBuilder) WhereOr(query string, args ...interface{}) QueryBuilder {
q.DeleteQuery.WhereOr(query, args...)
return q
}
func (q *deleteQueryBuilder) WhereDeleted() QueryBuilder {
q.DeleteQuery.WhereDeleted()
return q
}
func (q *deleteQueryBuilder) WhereAllWithDeleted() QueryBuilder {
q.DeleteQuery.WhereAllWithDeleted()
return q
}
func (q *deleteQueryBuilder) WherePK(cols ...string) QueryBuilder {
q.DeleteQuery.WherePK(cols...)
return q
}
func (q *deleteQueryBuilder) Unwrap() interface{} {
return q.DeleteQuery
}
func (q *DeleteQuery) Query() QueryBuilder {
return &deleteQueryBuilder{q}
}

View File

@ -220,6 +220,7 @@ func (q *CreateIndexQuery) AppendQuery(fmter schema.Formatter, b []byte) (_ []by
}
if len(q.where) > 0 {
b = append(b, " WHERE "...)
b, err = appendWhere(fmter, b, q.where)
if err != nil {
return nil, err

View File

@ -126,13 +126,6 @@ func (q *InsertQuery) Returning(query string, args ...interface{}) *InsertQuery
return q
}
func (q *InsertQuery) hasReturning() bool {
if !q.db.features.Has(feature.Returning) {
return false
}
return q.returningQuery.hasReturning()
}
//------------------------------------------------------------------------------
// Ignore generates different queries depending on the DBMS:
@ -148,7 +141,7 @@ func (q *InsertQuery) Ignore() *InsertQuery {
return q
}
// Replaces generates a `REPLACE INTO` query (MySQL).
// Replaces generates a `REPLACE INTO` query (MySQL and MariaDB).
func (q *InsertQuery) Replace() *InsertQuery {
q.replace = true
return q
@ -201,7 +194,8 @@ func (q *InsertQuery) AppendQuery(fmter schema.Formatter, b []byte) (_ []byte, e
return nil, err
}
if q.hasReturning() {
if q.hasFeature(feature.InsertReturning) && q.hasReturning() {
b = append(b, " RETURNING "...)
b, err = q.appendReturning(fmter, b)
if err != nil {
return nil, err
@ -224,6 +218,14 @@ func (q *InsertQuery) appendColumnsValues(
b = append(b, ")"...)
}
if q.hasFeature(feature.Output) && q.hasReturning() {
b = append(b, " OUTPUT "...)
b, err = q.appendOutput(fmter, b)
if err != nil {
return nil, err
}
}
b = append(b, " SELECT "...)
if q.columns != nil {
@ -255,6 +257,7 @@ func (q *InsertQuery) appendColumnsValues(
return nil, errNilModel
}
// Build fields to populate RETURNING clause.
fields, err := q.getFields()
if err != nil {
return nil, err
@ -262,7 +265,17 @@ func (q *InsertQuery) appendColumnsValues(
b = append(b, " ("...)
b = q.appendFields(fmter, b, fields)
b = append(b, ") VALUES ("...)
b = append(b, ")"...)
if q.hasFeature(feature.Output) && q.hasReturning() {
b = append(b, " OUTPUT "...)
b, err = q.appendOutput(fmter, b)
if err != nil {
return nil, err
}
}
b = append(b, " VALUES ("...)
switch model := q.tableModel.(type) {
case *structTableModel:
@ -306,7 +319,7 @@ func (q *InsertQuery) appendStructValues(
switch {
case isTemplate:
b = append(b, '?')
case f.NullZero && f.HasZeroValue(strct):
case (f.IsPtr && f.HasNilValue(strct)) || (f.NullZero && f.HasZeroValue(strct)):
if q.db.features.Has(feature.DefaultPlaceholder) {
b = append(b, "DEFAULT"...)
} else if f.SQLDefault != "" {
@ -353,22 +366,13 @@ func (q *InsertQuery) appendSliceValues(
}
}
for i, v := range q.extraValues {
if i > 0 || len(fields) > 0 {
b = append(b, ", "...)
}
b, err = v.value.AppendQuery(fmter, b)
if err != nil {
return nil, err
}
}
return b, nil
}
func (q *InsertQuery) getFields() ([]*schema.Field, error) {
if q.db.features.Has(feature.DefaultPlaceholder) || len(q.columns) > 0 {
hasIdentity := q.db.features.Has(feature.Identity)
if len(q.columns) > 0 || q.db.features.Has(feature.DefaultPlaceholder) && !hasIdentity {
return q.baseQuery.getFields()
}
@ -382,15 +386,23 @@ func (q *InsertQuery) getFields() ([]*schema.Field, error) {
return nil, fmt.Errorf("bun: Insert(empty %T)", model.slice.Type())
}
strct = indirect(model.slice.Index(0))
default:
return nil, errNilModel
}
fields := make([]*schema.Field, 0, len(q.table.Fields))
for _, f := range q.table.Fields {
if f.NotNull && f.NullZero && f.SQLDefault == "" && f.HasZeroValue(strct) {
if hasIdentity && f.AutoIncrement {
q.addReturningField(f)
continue
}
if f.NotNull && f.SQLDefault == "" {
if (f.IsPtr && f.HasNilValue(strct)) || (f.NullZero && f.HasZeroValue(strct)) {
q.addReturningField(f)
continue
}
}
fields = append(fields, f)
}
@ -539,7 +551,8 @@ func (q *InsertQuery) Exec(ctx context.Context, dest ...interface{}) (sql.Result
query := internal.String(queryBytes)
var res sql.Result
if hasDest := len(dest) > 0; hasDest || q.hasReturning() {
if hasDest := len(dest) > 0; hasDest ||
(q.hasReturning() && q.hasFeature(feature.InsertReturning|feature.Output)) {
model, err := q.getModel(dest)
if err != nil {
return nil, err
@ -588,7 +601,11 @@ func (q *InsertQuery) afterInsertHook(ctx context.Context) error {
}
func (q *InsertQuery) tryLastInsertID(res sql.Result, dest []interface{}) error {
if q.db.features.Has(feature.Returning) || q.table == nil || len(q.table.PKs) != 1 {
if q.db.features.Has(feature.Returning) ||
q.db.features.Has(feature.Output) ||
q.table == nil ||
len(q.table.PKs) != 1 ||
!q.table.PKs[0].AutoIncrement {
return nil
}

View File

@ -10,6 +10,7 @@ import (
"strings"
"sync"
"github.com/uptrace/bun/dialect/feature"
"github.com/uptrace/bun/internal"
"github.com/uptrace/bun/schema"
)
@ -479,14 +480,25 @@ func (q *SelectQuery) appendQuery(
return nil, err
}
if q.limit != 0 {
b = append(b, " LIMIT "...)
b = strconv.AppendInt(b, int64(q.limit), 10)
}
if fmter.Dialect().Features().Has(feature.OffsetFetch) {
if q.offset != 0 {
b = append(b, " OFFSET "...)
b = strconv.AppendInt(b, int64(q.offset), 10)
b = append(b, " ROWS"...)
if q.offset != 0 {
b = append(b, " OFFSET "...)
b = strconv.AppendInt(b, int64(q.offset), 10)
b = append(b, " FETCH NEXT "...)
b = strconv.AppendInt(b, int64(q.limit), 10)
b = append(b, " ROWS ONLY"...)
}
} else {
if q.limit != 0 {
b = append(b, " LIMIT "...)
b = strconv.AppendInt(b, int64(q.limit), 10)
}
if q.offset != 0 {
b = append(b, " OFFSET "...)
b = strconv.AppendInt(b, int64(q.offset), 10)
}
}
if !q.selFor.IsZero() {
@ -664,7 +676,7 @@ func (q *SelectQuery) Rows(ctx context.Context) (*sql.Rows, error) {
return q.conn.QueryContext(ctx, query)
}
func (q *SelectQuery) Exec(ctx context.Context) (res sql.Result, err error) {
func (q *SelectQuery) Exec(ctx context.Context, dest ...interface{}) (res sql.Result, err error) {
if q.err != nil {
return nil, q.err
}
@ -679,9 +691,21 @@ func (q *SelectQuery) Exec(ctx context.Context) (res sql.Result, err error) {
query := internal.String(queryBytes)
res, err = q.exec(ctx, q, query)
if err != nil {
return nil, err
if len(dest) > 0 {
model, err := q.getModel(dest)
if err != nil {
return nil, err
}
res, err = q.scan(ctx, q, query, model, true)
if err != nil {
return nil, err
}
} else {
res, err = q.exec(ctx, q, query)
if err != nil {
return nil, err
}
}
return res, nil
@ -697,12 +721,6 @@ func (q *SelectQuery) Scan(ctx context.Context, dest ...interface{}) error {
return err
}
if q.limit > 1 {
if model, ok := model.(interface{ SetCap(int) }); ok {
model.SetCap(int(q.limit))
}
}
if q.table != nil {
if err := q.beforeSelectHook(ctx); err != nil {
return err
@ -850,7 +868,14 @@ func (q *SelectQuery) Exists(ctx context.Context) (bool, error) {
return false, q.err
}
qq := existsQuery{q}
if q.hasFeature(feature.SelectExists) {
return q.selectExists(ctx)
}
return q.whereExists(ctx)
}
func (q *SelectQuery) selectExists(ctx context.Context) (bool, error) {
qq := selectExistsQuery{q}
queryBytes, err := qq.AppendQuery(q.db.fmter, nil)
if err != nil {
@ -868,6 +893,78 @@ func (q *SelectQuery) Exists(ctx context.Context) (bool, error) {
return exists, err
}
func (q *SelectQuery) whereExists(ctx context.Context) (bool, error) {
qq := whereExistsQuery{q}
queryBytes, err := qq.AppendQuery(q.db.fmter, nil)
if err != nil {
return false, err
}
query := internal.String(queryBytes)
ctx, event := q.db.beforeQuery(ctx, qq, query, nil, query, q.model)
res, err := q.exec(ctx, q, query)
q.db.afterQuery(ctx, event, nil, err)
if err != nil {
return false, err
}
n, err := res.RowsAffected()
if err != nil {
return false, err
}
return n == 1, nil
}
//------------------------------------------------------------------------------
type selectQueryBuilder struct {
*SelectQuery
}
func (q *selectQueryBuilder) WhereGroup(sep string, fn func(QueryBuilder) QueryBuilder) QueryBuilder {
q.SelectQuery = q.SelectQuery.WhereGroup(sep, func(qs *SelectQuery) *SelectQuery {
return fn(q).(*selectQueryBuilder).SelectQuery
})
return q
}
func (q *selectQueryBuilder) Where(query string, args ...interface{}) QueryBuilder {
q.SelectQuery.Where(query, args...)
return q
}
func (q *selectQueryBuilder) WhereOr(query string, args ...interface{}) QueryBuilder {
q.SelectQuery.WhereOr(query, args...)
return q
}
func (q *selectQueryBuilder) WhereDeleted() QueryBuilder {
q.SelectQuery.WhereDeleted()
return q
}
func (q *selectQueryBuilder) WhereAllWithDeleted() QueryBuilder {
q.SelectQuery.WhereAllWithDeleted()
return q
}
func (q *selectQueryBuilder) WherePK(cols ...string) QueryBuilder {
q.SelectQuery.WherePK(cols...)
return q
}
func (q *selectQueryBuilder) Unwrap() interface{} {
return q.SelectQuery
}
func (q *SelectQuery) Query() QueryBuilder {
return &selectQueryBuilder{q}
}
//------------------------------------------------------------------------------
type joinQuery struct {
@ -912,25 +1009,19 @@ func (q countQuery) AppendQuery(fmter schema.Formatter, b []byte) (_ []byte, err
if q.err != nil {
return nil, q.err
}
// if err := q.beforeAppendModel(q); err != nil {
// return nil, err
// }
return q.appendQuery(fmter, b, true)
}
//------------------------------------------------------------------------------
type existsQuery struct {
type selectExistsQuery struct {
*SelectQuery
}
func (q existsQuery) AppendQuery(fmter schema.Formatter, b []byte) (_ []byte, err error) {
func (q selectExistsQuery) AppendQuery(fmter schema.Formatter, b []byte) (_ []byte, err error) {
if q.err != nil {
return nil, q.err
}
// if err := q.beforeAppendModel(q); err != nil {
// return nil, err
// }
b = append(b, "SELECT EXISTS ("...)
@ -943,3 +1034,26 @@ func (q existsQuery) AppendQuery(fmter schema.Formatter, b []byte) (_ []byte, er
return b, nil
}
//------------------------------------------------------------------------------
type whereExistsQuery struct {
*SelectQuery
}
func (q whereExistsQuery) AppendQuery(fmter schema.Formatter, b []byte) (_ []byte, err error) {
if q.err != nil {
return nil, q.err
}
b = append(b, "SELECT 1 WHERE EXISTS ("...)
b, err = q.appendQuery(fmter, b, false)
if err != nil {
return nil, err
}
b = append(b, ")"...)
return b, nil
}

View File

@ -102,6 +102,21 @@ func (q *CreateTableQuery) TableSpace(tablespace string) *CreateTableQuery {
return q
}
func (q *CreateTableQuery) WithForeignKeys() *CreateTableQuery {
for _, relation := range q.tableModel.Table().Relations {
if relation.Type == schema.ManyToManyRelation ||
relation.Type == schema.HasManyRelation {
continue
}
q = q.ForeignKey("(?) REFERENCES ? (?)",
Safe(appendColumns(nil, "", relation.BaseFields)),
relation.JoinTable.SQLName,
Safe(appendColumns(nil, "", relation.JoinFields)),
)
}
return q
}
//------------------------------------------------------------------------------
func (q *CreateTableQuery) Operation() string {
@ -121,7 +136,7 @@ func (q *CreateTableQuery) AppendQuery(fmter schema.Formatter, b []byte) (_ []by
b = append(b, "TEMP "...)
}
b = append(b, "TABLE "...)
if q.ifNotExists {
if q.ifNotExists && fmter.Dialect().Features().Has(feature.TableNotExists) {
b = append(b, "IF NOT EXISTS "...)
}
b, err = q.appendFirstTable(fmter, b)
@ -142,8 +157,13 @@ func (q *CreateTableQuery) AppendQuery(fmter schema.Formatter, b []byte) (_ []by
if field.NotNull {
b = append(b, " NOT NULL"...)
}
if fmter.Dialect().Features().Has(feature.AutoIncrement) && field.AutoIncrement {
b = append(b, " AUTO_INCREMENT"...)
if field.AutoIncrement {
switch {
case fmter.Dialect().Features().Has(feature.AutoIncrement):
b = append(b, " AUTO_INCREMENT"...)
case fmter.Dialect().Features().Has(feature.Identity):
b = append(b, " IDENTITY"...)
}
}
if field.SQLDefault != "" {
b = append(b, " DEFAULT "...)

View File

@ -92,13 +92,21 @@ func (q *UpdateQuery) Set(query string, args ...interface{}) *UpdateQuery {
return q
}
func (q *UpdateQuery) SetColumn(column string, query string, args ...interface{}) *UpdateQuery {
if q.db.HasFeature(feature.UpdateMultiTable) {
column = q.table.Alias + "." + column
}
q.addSet(schema.SafeQuery(column+" = "+query, args))
return q
}
// Value overwrites model value for the column.
func (q *UpdateQuery) Value(column string, expr string, args ...interface{}) *UpdateQuery {
func (q *UpdateQuery) Value(column string, query string, args ...interface{}) *UpdateQuery {
if q.table == nil {
q.err = errNilModel
return q
}
q.addValue(q.table, column, expr, args)
q.addValue(q.table, column, query, args)
return q
}
@ -187,8 +195,10 @@ func (q *UpdateQuery) AppendQuery(fmter schema.Formatter, b []byte) (_ []byte, e
if fmter.HasFeature(feature.UpdateMultiTable) {
b, err = q.appendTablesWithAlias(fmter, b)
} else {
} else if fmter.HasFeature(feature.UpdateTableAlias) {
b, err = q.appendFirstTableWithAlias(fmter, b)
} else {
b, err = q.appendFirstTable(fmter, b)
}
if err != nil {
return nil, err
@ -206,12 +216,13 @@ func (q *UpdateQuery) AppendQuery(fmter schema.Formatter, b []byte) (_ []byte, e
}
}
b, err = q.mustAppendWhere(fmter, b, true)
b, err = q.mustAppendWhere(fmter, b, q.hasTableAlias(fmter))
if err != nil {
return nil, err
}
if len(q.returning) > 0 {
if q.hasFeature(feature.Returning) && q.hasReturning() {
b = append(b, " RETURNING "...)
b, err = q.appendReturning(fmter, b)
if err != nil {
return nil, err
@ -344,7 +355,7 @@ func (q *UpdateQuery) Bulk() *UpdateQuery {
Model(model).
TableExpr("_data").
Set(set).
Where(q.updateSliceWhere(model))
Where(q.updateSliceWhere(q.db.fmter, model))
}
func (q *UpdateQuery) updateSliceSet(
@ -371,13 +382,17 @@ func (q *UpdateQuery) updateSliceSet(
return internal.String(b), nil
}
func (db *UpdateQuery) updateSliceWhere(model *sliceTableModel) string {
func (q *UpdateQuery) updateSliceWhere(fmter schema.Formatter, model *sliceTableModel) string {
var b []byte
for i, pk := range model.table.PKs {
if i > 0 {
b = append(b, " AND "...)
}
b = append(b, model.table.SQLAlias...)
if q.hasTableAlias(fmter) {
b = append(b, model.table.SQLAlias...)
} else {
b = append(b, model.table.SQLName...)
}
b = append(b, '.')
b = append(b, pk.SQLName...)
b = append(b, " = _data."...)
@ -456,14 +471,63 @@ func (q *UpdateQuery) afterUpdateHook(ctx context.Context) error {
return nil
}
// FQN returns a fully qualified column name. For MySQL, it returns the column name with
// the table alias. For other RDBMS, it returns just the column name.
// FQN returns a fully qualified column name, for example, table_name.column_name or
// table_alias.column_alias.
func (q *UpdateQuery) FQN(column string) Ident {
if q.table == nil {
panic("UpdateQuery.FQN requires a model")
panic("UpdateQuery.SetName requires a model")
}
if q.db.HasFeature(feature.UpdateMultiTable) {
if q.hasTableAlias(q.db.fmter) {
return Ident(q.table.Alias + "." + column)
}
return Ident(column)
return Ident(q.table.Name + "." + column)
}
func (q *UpdateQuery) hasTableAlias(fmter schema.Formatter) bool {
return fmter.HasFeature(feature.UpdateMultiTable | feature.UpdateTableAlias)
}
//------------------------------------------------------------------------------
type updateQueryBuilder struct {
*UpdateQuery
}
func (q *updateQueryBuilder) WhereGroup(sep string, fn func(QueryBuilder) QueryBuilder) QueryBuilder {
q.UpdateQuery = q.UpdateQuery.WhereGroup(sep, func(qs *UpdateQuery) *UpdateQuery {
return fn(q).(*updateQueryBuilder).UpdateQuery
})
return q
}
func (q *updateQueryBuilder) Where(query string, args ...interface{}) QueryBuilder {
q.UpdateQuery.Where(query, args...)
return q
}
func (q *updateQueryBuilder) WhereOr(query string, args ...interface{}) QueryBuilder {
q.UpdateQuery.WhereOr(query, args...)
return q
}
func (q *updateQueryBuilder) WhereDeleted() QueryBuilder {
q.UpdateQuery.WhereDeleted()
return q
}
func (q *updateQueryBuilder) WhereAllWithDeleted() QueryBuilder {
q.UpdateQuery.WhereAllWithDeleted()
return q
}
func (q *updateQueryBuilder) WherePK(cols ...string) QueryBuilder {
q.UpdateQuery.WherePK(cols...)
return q
}
func (q *updateQueryBuilder) Unwrap() interface{} {
return q.UpdateQuery
}
func (q *UpdateQuery) Query() QueryBuilder {
return &updateQueryBuilder{q}
}

View File

@ -37,6 +37,13 @@ func (q *ValuesQuery) Conn(db IConn) *ValuesQuery {
return q
}
func (q *ValuesQuery) Column(columns ...string) *ValuesQuery {
for _, column := range columns {
q.addColumn(schema.UnsafeIdent(column))
}
return q
}
// Value overwrites model value for the column.
func (q *ValuesQuery) Value(column string, expr string, args ...interface{}) *ValuesQuery {
if q.table == nil {
@ -98,7 +105,7 @@ func (q *ValuesQuery) AppendColumns(fmter schema.Formatter, b []byte) (_ []byte,
}
func (q *ValuesQuery) Operation() string {
return "SELECT"
return "VALUES"
}
func (q *ValuesQuery) AppendQuery(fmter schema.Formatter, b []byte) (_ []byte, err error) {

View File

@ -1,6 +1,7 @@
package schema
import (
"fmt"
"reflect"
"strconv"
"time"
@ -47,3 +48,54 @@ func Append(fmter Formatter, b []byte, v interface{}) []byte {
return appender(fmter, b, vv)
}
}
//------------------------------------------------------------------------------
func In(slice interface{}) QueryAppender {
v := reflect.ValueOf(slice)
if v.Kind() != reflect.Slice {
return &inValues{
err: fmt.Errorf("bun: In(non-slice %T)", slice),
}
}
return &inValues{
slice: v,
}
}
type inValues struct {
slice reflect.Value
err error
}
var _ QueryAppender = (*inValues)(nil)
func (in *inValues) AppendQuery(fmter Formatter, b []byte) (_ []byte, err error) {
if in.err != nil {
return nil, in.err
}
return appendIn(fmter, b, in.slice), nil
}
func appendIn(fmter Formatter, b []byte, slice reflect.Value) []byte {
sliceLen := slice.Len()
for i := 0; i < sliceLen; i++ {
if i > 0 {
b = append(b, ", "...)
}
elem := slice.Index(i)
if elem.Kind() == reflect.Interface {
elem = elem.Elem()
}
if elem.Kind() == reflect.Slice && elem.Type() != bytesType {
b = append(b, '(')
b = appendIn(fmter, b, elem)
b = append(b, ')')
} else {
b = fmter.AppendValue(b, elem)
}
}
return b
}

View File

@ -58,12 +58,24 @@ func FieldAppender(dialect Dialect, field *Field) AppenderFunc {
return appendMsgpack
}
fieldType := field.StructField.Type
switch strings.ToUpper(field.UserSQLType) {
case sqltype.JSON, sqltype.JSONB:
if fieldType.Implements(driverValuerType) {
return appendDriverValue
}
if fieldType.Kind() != reflect.Ptr {
if reflect.PtrTo(fieldType).Implements(driverValuerType) {
return addrAppender(appendDriverValue)
}
}
return AppendJSONValue
}
return Appender(dialect, field.StructField.Type)
return Appender(dialect, fieldType)
}
func Appender(dialect Dialect, typ reflect.Type) AppenderFunc {
@ -85,6 +97,8 @@ func appender(dialect Dialect, typ reflect.Type) AppenderFunc {
return appendBytesValue
case timeType:
return appendTimeValue
case timePtrType:
return PtrAppender(appendTimeValue)
case ipType:
return appendIPValue
case ipNetType:
@ -93,15 +107,21 @@ func appender(dialect Dialect, typ reflect.Type) AppenderFunc {
return appendJSONRawMessageValue
}
kind := typ.Kind()
if typ.Implements(queryAppenderType) {
if kind == reflect.Ptr {
return nilAwareAppender(appendQueryAppenderValue)
}
return appendQueryAppenderValue
}
if typ.Implements(driverValuerType) {
if kind == reflect.Ptr {
return nilAwareAppender(appendDriverValue)
}
return appendDriverValue
}
kind := typ.Kind()
if kind != reflect.Ptr {
ptr := reflect.PtrTo(typ)
if ptr.Implements(queryAppenderType) {
@ -116,6 +136,9 @@ func appender(dialect Dialect, typ reflect.Type) AppenderFunc {
case reflect.Interface:
return ifaceAppenderFunc
case reflect.Ptr:
if typ.Implements(jsonMarshalerType) {
return nilAwareAppender(AppendJSONValue)
}
if fn := Appender(dialect, typ.Elem()); fn != nil {
return PtrAppender(fn)
}
@ -141,6 +164,15 @@ func ifaceAppenderFunc(fmter Formatter, b []byte, v reflect.Value) []byte {
return appender(fmter, b, elem)
}
func nilAwareAppender(fn AppenderFunc) AppenderFunc {
return func(fmter Formatter, b []byte, v reflect.Value) []byte {
if v.IsNil() {
return dialect.AppendNull(b)
}
return fn(fmter, b, v)
}
}
func PtrAppender(fn AppenderFunc) AppenderFunc {
return func(fmter Formatter, b []byte, v reflect.Value) []byte {
if v.IsNil() {

View File

@ -10,6 +10,7 @@ import (
type Field struct {
StructField reflect.StructField
IsPtr bool
Tag tagparser.Tag
IndirectType reflect.Type
@ -51,15 +52,36 @@ func (f *Field) Value(strct reflect.Value) reflect.Value {
return fieldByIndexAlloc(strct, f.Index)
}
func (f *Field) HasZeroValue(v reflect.Value) bool {
for _, idx := range f.Index {
func (f *Field) HasNilValue(v reflect.Value) bool {
if len(f.Index) == 1 {
return v.Field(f.Index[0]).IsNil()
}
for _, index := range f.Index {
if v.Kind() == reflect.Ptr {
if v.IsNil() {
return true
}
v = v.Elem()
}
v = v.Field(idx)
v = v.Field(index)
}
return v.IsNil()
}
func (f *Field) HasZeroValue(v reflect.Value) bool {
if len(f.Index) == 1 {
return f.IsZero(v.Field(f.Index[0]))
}
for _, index := range f.Index {
if v.Kind() == reflect.Ptr {
if v.IsNil() {
return true
}
v = v.Elem()
}
v = v.Field(index)
}
return f.IsZero(v)
}
@ -70,7 +92,7 @@ func (f *Field) AppendValue(fmter Formatter, b []byte, strct reflect.Value) []by
return dialect.AppendNull(b)
}
if f.NullZero && f.IsZero(fv) {
if (f.IsPtr && fv.IsNil()) || (f.NullZero && f.IsZero(fv)) {
return dialect.AppendNull(b)
}
if f.Append == nil {
@ -98,14 +120,6 @@ func (f *Field) ScanValue(strct reflect.Value, src interface{}) error {
return f.ScanWithCheck(fv, src)
}
func (f *Field) markAsPK() {
f.IsPK = true
f.NotNull = true
if !f.Tag.HasOption("allowzero") {
f.NullZero = true
}
}
func indexEqual(ind1, ind2 []int) bool {
if len(ind1) != len(ind2) {
return false

View File

@ -10,13 +10,15 @@ import (
var (
bytesType = reflect.TypeOf((*[]byte)(nil)).Elem()
timeType = reflect.TypeOf((*time.Time)(nil)).Elem()
timePtrType = reflect.TypeOf((*time.Time)(nil))
timeType = timePtrType.Elem()
ipType = reflect.TypeOf((*net.IP)(nil)).Elem()
ipNetType = reflect.TypeOf((*net.IPNet)(nil)).Elem()
jsonRawMessageType = reflect.TypeOf((*json.RawMessage)(nil)).Elem()
driverValuerType = reflect.TypeOf((*driver.Valuer)(nil)).Elem()
queryAppenderType = reflect.TypeOf((*QueryAppender)(nil)).Elem()
jsonMarshalerType = reflect.TypeOf((*json.Marshaler)(nil)).Elem()
)
func indirectType(t reflect.Type) reflect.Type {

View File

@ -94,6 +94,8 @@ func scanner(typ reflect.Type) ScannerFunc {
}
switch typ {
case bytesType:
return scanBytes
case timeType:
return scanTime
case ipType:
@ -134,12 +136,22 @@ func scanBool(dest reflect.Value, src interface{}) error {
dest.SetBool(src != 0)
return nil
case []byte:
if len(src) == 1 {
dest.SetBool(src[0] != '0')
return nil
f, err := strconv.ParseBool(internal.String(src))
if err != nil {
return err
}
dest.SetBool(f)
return nil
case string:
f, err := strconv.ParseBool(src)
if err != nil {
return err
}
dest.SetBool(f)
return nil
default:
return scanError(dest.Type(), src)
}
return fmt.Errorf("bun: can't scan %#v into %s", src, dest.Type())
}
func scanInt64(dest reflect.Value, src interface{}) error {
@ -167,8 +179,9 @@ func scanInt64(dest reflect.Value, src interface{}) error {
}
dest.SetInt(n)
return nil
default:
return scanError(dest.Type(), src)
}
return fmt.Errorf("bun: can't scan %#v into %s", src, dest.Type())
}
func scanUint64(dest reflect.Value, src interface{}) error {
@ -189,8 +202,16 @@ func scanUint64(dest reflect.Value, src interface{}) error {
}
dest.SetUint(n)
return nil
case string:
n, err := strconv.ParseUint(src, 10, 64)
if err != nil {
return err
}
dest.SetUint(n)
return nil
default:
return scanError(dest.Type(), src)
}
return fmt.Errorf("bun: can't scan %#v into %s", src, dest.Type())
}
func scanFloat64(dest reflect.Value, src interface{}) error {
@ -208,8 +229,16 @@ func scanFloat64(dest reflect.Value, src interface{}) error {
}
dest.SetFloat(f)
return nil
case string:
f, err := strconv.ParseFloat(src, 64)
if err != nil {
return err
}
dest.SetFloat(f)
return nil
default:
return scanError(dest.Type(), src)
}
return fmt.Errorf("bun: can't scan %#v into %s", src, dest.Type())
}
func scanString(dest reflect.Value, src interface{}) error {
@ -226,8 +255,18 @@ func scanString(dest reflect.Value, src interface{}) error {
case time.Time:
dest.SetString(src.Format(time.RFC3339Nano))
return nil
case int64:
dest.SetString(strconv.FormatInt(src, 10))
return nil
case uint64:
dest.SetString(strconv.FormatUint(src, 10))
return nil
case float64:
dest.SetString(strconv.FormatFloat(src, 'G', -1, 64))
return nil
default:
return scanError(dest.Type(), src)
}
return fmt.Errorf("bun: can't scan %#v into %s", src, dest.Type())
}
func scanBytes(dest reflect.Value, src interface{}) error {
@ -244,8 +283,9 @@ func scanBytes(dest reflect.Value, src interface{}) error {
dest.SetBytes(clone)
return nil
default:
return scanError(dest.Type(), src)
}
return fmt.Errorf("bun: can't scan %#v into %s", src, dest.Type())
}
func scanTime(dest reflect.Value, src interface{}) error {
@ -274,8 +314,9 @@ func scanTime(dest reflect.Value, src interface{}) error {
destTime := dest.Addr().Interface().(*time.Time)
*destTime = srcTime
return nil
default:
return scanError(dest.Type(), src)
}
return fmt.Errorf("bun: can't scan %#v into %s", src, dest.Type())
}
func scanScanner(dest reflect.Value, src interface{}) error {
@ -438,7 +479,7 @@ func scanJSONIntoInterface(dest reflect.Value, src interface{}) error {
if fn := Scanner(dest.Type()); fn != nil {
return fn(dest, src)
}
return fmt.Errorf("bun: can't scan %#v into %s", src, dest.Type())
return scanError(dest.Type(), src)
}
func scanInterface(dest reflect.Value, src interface{}) error {
@ -454,7 +495,7 @@ func scanInterface(dest reflect.Value, src interface{}) error {
if fn := Scanner(dest.Type()); fn != nil {
return fn(dest, src)
}
return fmt.Errorf("bun: can't scan %#v into %s", src, dest.Type())
return scanError(dest.Type(), src)
}
func nilable(kind reflect.Kind) bool {
@ -464,3 +505,7 @@ func nilable(kind reflect.Kind) bool {
}
return false
}
func scanError(dest reflect.Type, src interface{}) error {
return fmt.Errorf("bun: can't scan %#v (%T) into %s", src, src, dest.String())
}

View File

@ -49,7 +49,7 @@ func SafeQuery(query string, args []interface{}) QueryWithArgs {
if args == nil {
args = make([]interface{}, 0)
} else if len(query) > 0 && strings.IndexByte(query, '?') == -1 {
internal.Warn.Printf("query %q has args %v, but no placeholders", query, args)
internal.Warn.Printf("query %q has %v args, but no placeholders", query, args)
}
return QueryWithArgs{
Query: query,

View File

@ -4,7 +4,6 @@ import (
"bytes"
"database/sql"
"encoding/json"
"fmt"
"reflect"
"time"
@ -60,6 +59,8 @@ func DiscoverSQLType(typ reflect.Type) string {
return sqltype.BigInt
case nullStringType:
return sqltype.VarChar
case jsonRawMessageType:
return sqltype.JSON
}
switch typ.Kind() {
@ -135,6 +136,6 @@ func (tm *NullTime) Scan(src interface{}) error {
tm.Time = newtm
return nil
default:
return fmt.Errorf("bun: can't scan %#v into NullTime", src)
return scanError(bunNullTimeType, src)
}
}

View File

@ -204,30 +204,6 @@ func (t *Table) initFields() {
t.Fields = make([]*Field, 0, t.Type.NumField())
t.FieldMap = make(map[string]*Field, t.Type.NumField())
t.addFields(t.Type, "", nil)
if len(t.PKs) == 0 {
for _, name := range []string{"id", "uuid", "pk_" + t.ModelName} {
if field, ok := t.FieldMap[name]; ok {
field.markAsPK()
t.PKs = []*Field{field}
t.DataFields = removeField(t.DataFields, field)
break
}
}
}
if len(t.PKs) == 1 {
pk := t.PKs[0]
if pk.SQLDefault != "" {
return
}
switch pk.IndirectType.Kind() {
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64,
reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
pk.AutoIncrement = true
}
}
}
func (t *Table) addFields(typ reflect.Type, prefix string, index []int) {
@ -250,26 +226,27 @@ func (t *Table) addFields(typ reflect.Type, prefix string, index []int) {
continue
}
// If field is an embedded struct, add each field of the embedded struct.
fieldType := indirectType(f.Type)
if fieldType.Kind() != reflect.Struct {
if fieldType.Kind() == reflect.Struct {
t.addFields(fieldType, "", withIndex(index, f.Index))
tag := tagparser.Parse(f.Tag.Get("bun"))
if tag.HasOption("inherit") || tag.HasOption("extend") {
embeddedTable := t.dialect.Tables().Ref(fieldType)
t.TypeName = embeddedTable.TypeName
t.SQLName = embeddedTable.SQLName
t.SQLNameForSelects = embeddedTable.SQLNameForSelects
t.Alias = embeddedTable.Alias
t.SQLAlias = embeddedTable.SQLAlias
t.ModelName = embeddedTable.ModelName
}
continue
}
t.addFields(fieldType, "", withIndex(index, f.Index))
tag := tagparser.Parse(f.Tag.Get("bun"))
if _, inherit := tag.Options["inherit"]; inherit {
embeddedTable := t.dialect.Tables().Ref(fieldType)
t.TypeName = embeddedTable.TypeName
t.SQLName = embeddedTable.SQLName
t.SQLNameForSelects = embeddedTable.SQLNameForSelects
t.Alias = embeddedTable.Alias
t.SQLAlias = embeddedTable.SQLAlias
t.ModelName = embeddedTable.ModelName
}
continue
}
// If field is not a struct, add it.
// This will also add any embedded non-struct type as a field.
if field := t.newField(f, prefix, index); field != nil {
t.addField(field)
}
@ -355,6 +332,7 @@ func (t *Table) newField(f reflect.StructField, prefix string, index []int) *Fie
field := &Field{
StructField: f,
IsPtr: f.Type.Kind() == reflect.Ptr,
Tag: tag,
IndirectType: indirectType(f.Type),
@ -367,9 +345,13 @@ func (t *Table) newField(f reflect.StructField, prefix string, index []int) *Fie
field.NotNull = tag.HasOption("notnull")
field.NullZero = tag.HasOption("nullzero")
field.AutoIncrement = tag.HasOption("autoincrement")
if tag.HasOption("pk") {
field.markAsPK()
field.IsPK = true
field.NotNull = true
}
if tag.HasOption("autoincrement") {
field.AutoIncrement = true
field.NullZero = true
}
if v, ok := tag.Options["unique"]; ok {
@ -415,22 +397,10 @@ func (t *Table) newField(f reflect.StructField, prefix string, index []int) *Fie
}
if _, ok := tag.Options["soft_delete"]; ok {
field.NullZero = true
t.SoftDeleteField = field
t.UpdateSoftDeleteField = softDeleteFieldUpdater(field)
}
// Check this in the end to undo NullZero.
if tag.HasOption("allowzero") {
if tag.HasOption("nullzero") {
internal.Warn.Printf(
"%s.%s: nullzero and allowzero options are mutually exclusive",
t.TypeName, f.Name,
)
}
field.NullZero = false
}
return field
}
@ -651,7 +621,7 @@ func (t *Table) hasManyRelation(field *Field) *Relation {
rel.BaseFields = append(rel.BaseFields, f)
} else {
panic(fmt.Errorf(
"bun: %s has-one %s: %s must have column %s",
"bun: %s has-many %s: %s must have column %s",
t.TypeName, field.GoName, t.TypeName, baseColumn,
))
}
@ -660,7 +630,7 @@ func (t *Table) hasManyRelation(field *Field) *Relation {
rel.JoinFields = append(rel.JoinFields, f)
} else {
panic(fmt.Errorf(
"bun: %s has-one %s: %s must have column %s",
"bun: %s has-many %s: %s must have column %s",
t.TypeName, field.GoName, t.TypeName, baseColumn,
))
}
@ -879,7 +849,6 @@ func isKnownFieldOption(name string) bool {
"msgpack",
"notnull",
"nullzero",
"allowzero",
"default",
"unique",
"soft_delete",

View File

@ -2,5 +2,5 @@ package bun
// Version is the current release version.
func Version() string {
return "1.0.20"
return "1.1.3"
}