File storage rewrite (#2676)

* Restructure data layer part 2 (#2599)
* Refactor and separate image model
* Refactor image query builder
* Handle relationships in image query builder
* Remove relationship management methods
* Refactor gallery model/query builder
* Add scenes to gallery model
* Convert scene model
* Refactor scene models
* Remove unused methods
* Add unit tests for gallery
* Add image tests
* Add scene tests
* Convert unnecessary scene value pointers to values
* Convert unnecessary pointer values to values
* Refactor scene partial
* Add scene partial tests
* Refactor ImagePartial
* Add image partial tests
* Refactor gallery partial update
* Add partial gallery update tests
* Use zero/null package for null values
* Add files and scan system
* Add sqlite implementation for files/folders
* Add unit tests for files/folders
* Image refactors
* Update image data layer
* Refactor gallery model and creation
* Refactor scene model
* Refactor scenes
* Don't set title from filename
* Allow galleries to freely add/remove images
* Add multiple scene file support to graphql and UI
* Add multiple file support for images in graphql/UI
* Add multiple file for galleries in graphql/UI
* Remove use of some deprecated fields
* Remove scene path usage
* Remove gallery path usage
* Remove path from image
* Move funscript to video file
* Refactor caption detection
* Migrate existing data
* Add post commit/rollback hook system
* Lint. Comment out import/export tests
* Add WithDatabase read only wrapper
* Prepend tasks to list
* Add 32 pre-migration
* Add warnings in release and migration notes
This commit is contained in:
WithoutPants
2022-07-13 16:30:54 +10:00
parent 30877c75fb
commit 5495d72849
359 changed files with 43690 additions and 16000 deletions

4
vendor/github.com/doug-martin/goqu/v9/.gitignore generated vendored Normal file
View File

@@ -0,0 +1,4 @@
.idea
src
*.iml
coverage.*

140
vendor/github.com/doug-martin/goqu/v9/.golangci.yml generated vendored Normal file
View File

@@ -0,0 +1,140 @@
linters-settings:
funlen:
lines: 140
statements: 50
gci:
local-prefixes: github.com/golangci/golangci-lint
goconst:
min-len: 2
min-occurrences: 2
gocritic:
enabled-tags:
- diagnostic
- experimental
- opinionated
- performance
- style
disabled-checks:
- dupImport # https://github.com/go-critic/go-critic/issues/845
- ifElseChain
- octalLiteral
- whyNoLint
- wrapperFunc
- sqlQuery # used by tests
gocyclo:
min-complexity: 20
goimports:
local-prefixes: github.com/golangci/golangci-lint
golint:
min-confidence: 0
gomnd:
settings:
mnd:
# don't include the "operation" and "assign"
checks: argument,case,condition,return
govet:
check-shadowing: true
settings:
printf:
funcs:
- (github.com/golangci/golangci-lint/pkg/logutils.Log).Infof
- (github.com/golangci/golangci-lint/pkg/logutils.Log).Warnf
- (github.com/golangci/golangci-lint/pkg/logutils.Log).Errorf
- (github.com/golangci/golangci-lint/pkg/logutils.Log).Fatalf
lll:
line-length: 140
maligned:
suggest-new: true
misspell:
locale: US
nolintlint:
allow-leading-space: true # don't require machine-readable nolint directives (i.e. with no leading space)
allow-unused: false # report any unused nolint directives
require-explanation: false # don't require an explanation for nolint directives
require-specific: false # don't require nolint directives to be specific about which linter is being skipped
exhaustive:
default-signifies-exhaustive: true
linters:
# please, do not use `enable-all`: it's deprecated and will be removed soon.
# inverted configuration with `enable-all` and `disable` is not scalable during updates of golangci-lint
disable-all: true
enable:
- asciicheck
- bodyclose
- deadcode
- depguard
- dogsled
- errcheck
- exportloopref
- exhaustive
- errcheck
- errorlint
- funlen
- forbidigo
- forcetypeassert
- goconst
- gocritic
- gocyclo
- gofmt
- goimports
- golint
- gomnd
- goprintffuncname
- gosec
- gosimple
- govet
- ifshort
- ineffassign
- lll
- makezero
- misspell
- nakedret
- nilerr
- noctx
- nolintlint
- prealloc
- predeclared
- rowserrcheck
- staticcheck
- structcheck
- stylecheck
- testpackage
- typecheck
- unconvert
- unparam
- unused
- varcheck
- whitespace
# don't enable:
# - gochecknoglobals
# - gocognit
# - godot
# - godox
# - goerr113
# - interfacer
# - maligned
# - nestif
# - revive
# - wsl
# - wrapcheck
issues:
# Excluding configuration per-path, per-linter, per-text and per-source
exclude-rules:
- path: _test\.go
linters:
- gomnd
# https://github.com/go-critic/go-critic/issues/926
- linters:
- gocritic
text: "unnecessaryDefer:"
run:
skip-dirs:
- test/testdata_etc
- internal/cache
- internal/renameio
- internal/robustio

View File

@@ -0,0 +1,46 @@
# Contributor Covenant Code of Conduct
## Our Pledge
In the interest of fostering an open and welcoming environment, we as contributors and maintainers pledge to making participation in our project and our community a harassment-free experience for everyone, regardless of age, body size, disability, ethnicity, gender identity and expression, level of experience, nationality, personal appearance, race, religion, or sexual identity and orientation.
## Our Standards
Examples of behavior that contributes to creating a positive environment include:
* Using welcoming and inclusive language
* Being respectful of differing viewpoints and experiences
* Gracefully accepting constructive criticism
* Focusing on what is best for the community
* Showing empathy towards other community members
Examples of unacceptable behavior by participants include:
* The use of sexualized language or imagery and unwelcome sexual attention or advances
* Trolling, insulting/derogatory comments, and personal or political attacks
* Public or private harassment
* Publishing others' private information, such as a physical or electronic address, without explicit permission
* Other conduct which could reasonably be considered inappropriate in a professional setting
## Our Responsibilities
Project maintainers are responsible for clarifying the standards of acceptable behavior and are expected to take appropriate and fair corrective action in response to any instances of unacceptable behavior.
Project maintainers have the right and responsibility to remove, edit, or reject comments, commits, code, wiki edits, issues, and other contributions that are not aligned to this Code of Conduct, or to ban temporarily or permanently any contributor for other behaviors that they deem inappropriate, threatening, offensive, or harmful.
## Scope
This Code of Conduct applies both within project spaces and in public spaces when an individual is representing the project or its community. Examples of representing a project or community include using an official project e-mail address, posting via an official social media account, or acting as an appointed representative at an online or offline event. Representation of a project may be further defined and clarified by project maintainers.
## Enforcement
Instances of abusive, harassing, or otherwise unacceptable behavior may be reported by contacting the project team at doug@dougamartin.com. The project team will review and investigate all complaints, and will respond in a way that it deems appropriate to the circumstances. The project team is obligated to maintain confidentiality with regard to the reporter of an incident. Further details of specific enforcement policies may be posted separately.
Project maintainers who do not follow or enforce the Code of Conduct in good faith may face temporary or permanent repercussions as determined by other members of the project's leadership.
## Attribution
This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4, available at [http://contributor-covenant.org/version/1/4][version]
[homepage]: http://contributor-covenant.org
[version]: http://contributor-covenant.org/version/1/4/

31
vendor/github.com/doug-martin/goqu/v9/CONTRIBUTING.md generated vendored Normal file
View File

@@ -0,0 +1,31 @@
## Contributions
I am always welcoming contributions of any type. Please open an issue or create a PR if you find an issue with any of the following.
* An issue with Documentation
* You found the documentation lacking in some way
If you have an issue with the package please include the following
* The dialect you are using
* A description of the problem
* A short example of how to reproduce (if applicable)
Without those basics it can be difficult to reproduce your issue locally. You may be asked for more information but that is a good starting point.
### New Features
New features and/or enhancements are great and I encourage you to either submit a PR or create an issue. In both cases include the following as the need/requirement may not be readily apparent.
1. The use case
2. A short example
If you are issuing a PR also also include the following
1. Tests - otherwise the PR will not be merged
2. Documentation - otherwise the PR will not be merged
3. Examples - [If applicable] see example_test.go for examples
If you find an issue you want to work on please comment on it letting other people know you are looking at it and I will assign the issue to you.
If want to work on an issue but dont know where to start just leave a comment and I'll be more than happy to point you in the right direction.

404
vendor/github.com/doug-martin/goqu/v9/HISTORY.md generated vendored Normal file
View File

@@ -0,0 +1,404 @@
# v9.18.0
* [FEATURE] Add support for aliasing insert datasets to support upsert alias [#306](https://github.com/doug-martin/goqu/pull/306) - [@XIELongDragon](https://github.com/XIELongDragon)
* [FEATURE] Add support for aliasing BooleanExpressions [#307](https://github.com/doug-martin/goqu/pull/307) - [@XIELongDragon](https://github.com/XIELongDragon)
# v9.17.0
* [FEATURE] Add support bitwise operations [#303](https://github.com/doug-martin/goqu/pull/303) - [@XIELongDragon](https://github.com/XIELongDragon)
* [FEATURE] Add support for specifying tables to be locked in ForUpdate, ForNoKeyUpdate, ForKeyShare, ForShare [#299](https://github.com/doug-martin/goqu/pull/299) - [@jbub](https://github.com/jbub)
# v9.16.0
* [FEATURE] Allow ordering by case expression [#282](https://github.com/doug-martin/goqu/issues/282), [#292](https://github.com/doug-martin/goqu/pull/292)
# v9.15.1
* [FIX] Field indexes in the columnMap getting overridden [290](https://github.com/doug-martin/goqu/issues/290), [#291](https://github.com/doug-martin/goqu/pull/291)
# v9.15.0
* [FEATURE] Add SetDefaultPrepared which controls query value interpolation [#288](https://github.com/doug-martin/goqu/pull/288) - [@Deiz](https://github.com/Deiz)
# v9.14.0
* [FEATURE] GroupByAppend to the SelectDataset and SelectClauses [#276](https://github.com/doug-martin/goqu/pull/276), [#287](https://github.com/doug-martin/goqu/pull/287) - [@ashishkf](https://github.com/ashishkf)
* [FEATURE] Allow untagged fields to be ignored [#285](https://github.com/doug-martin/goqu/pull/285) - [@Deiz](https://github.com/Deiz)
* [FIX] Nil valuer [#277](https://github.com/doug-martin/goqu/pull/277) - [@benzolium](https://github.com/benzolium), [@Diggs](https://github.com/Diggs)
* [FIX] Fix old import URL in doc comments [#286](https://github.com/doug-martin/goqu/pull/286) - [@maito1201](https://github.com/maito1201)
# v9.13.0
* [ADDED] ScanStructs, ScanVals to Scanner interface [#273](https://github.com/doug-martin/goqu/issues/273) - [@vlanse](https://github.com/vlanse)
# v9.12.0
* Update golangci-lint and updates for all associated linters
* Update dependencies
* github.com/DATA-DOG/go-sqlmock v1.3.3 -> v1.5.0
* github.com/denisenkom/go-mssqldb v0.0.0-20200206145737-bbfc9a55622e -> v0.10.0
* github.com/go-sql-driver/mysql v1.4.1 -> v1.6.0
* github.com/lib/pq v1.2.0 -> v1.10.1
* github.com/mattn/go-sqlite3 v1.11.0 -> v1.14.7
* github.com/stretchr/testify -> v1.4.0 -> v1.7.0
# v9.11.1
* [FIXED] Avoid mutation of join slice for separate datasets when joins slice capacity is not yet reached [#261](https://github.com/doug-martin/goqu/pull/261) - [@fhaifler](https://github.com/fhaifler)
# v9.11.0
* [FIXED] Use valid 'IS' operator for sqlserver dialect [#240](https://github.com/doug-martin/goqu/pull/240), [#239](https://github.com/doug-martin/goqu/pull/229) - [@vlanse](https://github.com/vlanse)
* [ADDED] Implement Orderable interface for SQL Functions [#251](https://github.com/doug-martin/goqu/pull/251) - [@GlebBeloded](https://github.com/GlebBeloded)
* [ADDED] Support for table hint in multi-table MySQL DELETE queries [#252](https://github.com/doug-martin/goqu/pull/252) - [@vlanse](https://github.com/vlanse)
# v9.10.0
* [FIXED] SELECT inherits dialect from INSERT in INSERT FROM SELECT. [#229](https://github.com/doug-martin/goqu/pull/229), [#223](https://github.com/doug-martin/goqu/issues/223) - [@vlanse](https://github.com/vlanse)
* [FIXED] SQLServer dialect: support prepared statements with TOP. [#230](https://github.com/doug-martin/goqu/pull/230), [#225](https://github.com/doug-martin/goqu/issues/225) - [@vlanse](https://github.com/vlanse)
* [ADDED] IsPrepared to SQLExpression interface. [#231](https://github.com/doug-martin/goqu/pull/231) - [@vlanse](https://github.com/vlanse)
# v9.9.0
* [FIXED] SQLite do not add FOR UPDATE in SELECT. [#218](https://github.com/doug-martin/goqu/pull/218) - [@vlanse](https://github.com/vlanse)
* [ADDED] Support for INSERT ON CONFLICT in SQLite. [#218](https://github.com/doug-martin/goqu/pull/218) - [@vlanse](https://github.com/vlanse)
# v9.8.0
* [ADDED] Support for ANY and ALL operators. [#196](https://github.com/doug-martin/goqu/issues/196)
* [ADDED] Support for CASE statements [#193](https://github.com/doug-martin/goqu/issues/193)
* [ADDED] Support for getting column identifiers from AliasExpressions. [#203](https://github.com/doug-martin/goqu/issues/203)
# v9.7.1
* Fix all formatting for golangci-lint
* Move to golangci-lint github action
# v9.7.0
* [ADDED] Support for sqlserver dialect [#197](https://github.com/doug-martin/goqu/issues/197),[#205](https://github.com/doug-martin/goqu/issues/205) - [@vlanse](https://github.com/vlanse)
# v9.6.0
* [ADDED] Support for Lateral queries [#182](https://github.com/doug-martin/goqu/issues/182)
# v9.5.1
* [FIXED] WITH clause without a RETURNING clause will panic [#177](https://github.com/doug-martin/goqu/issues/177)
* [FIXED] SQlite dialect escapes single quotes wrong, leads to SQL syntax error [#178](https://github.com/doug-martin/goqu/issues/178)
* [FIXED] Fix ReturnsColumns() nil pointer panic [#181](https://github.com/doug-martin/goqu/issues/181) - [@yeaha](https://github.com/yeaha)
* [FIXED] SelectDataset From with Error [#183](https://github.com/doug-martin/goqu/issues/183)
* [FIXED] Unable to execute union with order by expression [#185](https://github.com/doug-martin/goqu/issues/185)
# v9.5.0
* [ADDED] Ability to use regexp like, ilike, notlike, and notilike without a regexp [#172](https://github.com/doug-martin/goqu/issues/172)
# v9.4.0
* [ADDED] Ability to scan into struct fields from multiple tables [#160](https://github.com/doug-martin/goqu/issues/160)
# v9.3.0
* [ADDED] Using Update, Insert, or Delete datasets in sub selects and CTEs [#164](https://github.com/doug-martin/goqu/issues/164)
# v9.2.0
* [ADDED] exec.Scanner: New exposed scanner supports iterative scanning [#157](https://github.com/doug-martin/goqu/pull/157) - [@akarl](https://github.com/akarl)
# v9.1.0
* [FIXED] ExampleDoUpdate does't work in postgres [#156](https://github.com/doug-martin/goqu/issues/156)
* [FIXED] Issue with timezone being lost [#163](https://github.com/doug-martin/goqu/issues/163)
# v9.0.1
* [FIXED] Issue where `NULL`, `TRUE` and `FALSE` are interpolated when using an `IS` clause. [#165](https://github.com/doug-martin/goqu/issues/165)
# v9.0.0
* Changed `NULL`, `TRUE`, `FALSE` to not be interpolated when creating prepared statements. [#132](https://github.com/doug-martin/goqu/pull/132), [#158](https://github.com/doug-martin/goqu/pull/158) - [@marshallmcmullen](https://github.com/marshallmcmullen)
* Updated dependencies
* `github.com/lib/pq v1.1.1 -> v1.2.0`
* `github.com/mattn/go-sqlite3 v1.10.0 -> v1.11.0`
* `github.com/stretchr/testify v1.3.0 -> v1.4.0`
## v8.6.0
* [ADDED] `SetError()` and `Error()` to all datasets. [#152](https://github.com/doug-martin/goqu/pull/152) and [#150](https://github.com/doug-martin/goqu/pull/150) - [@marshallmcmullen](https://github.com/marshallmcmullen)
## v8.5.0
* [ADDED] Window Function support [#128](https://github.com/doug-martin/goqu/issues/128) - [@Xuyuanp](https://github.com/Xuyuanp)
## v8.4.1
* [FIXED] Returning func be able to handle nil [#140](https://github.com/doug-martin/goqu/issues/140)
## v8.4.0
* Created new `sqlgen` module to encapsulate sql generation
* Broke SQLDialect inti new SQL generators for each statement type.
* Test refactor
* Moved to a test case pattern to allow for quickly adding new test cases.
## v8.3.2
* [FIXED] Data race during query factory initialization [#133](https://github.com/doug-martin/goqu/issues/133) and [#136](https://github.com/doug-martin/goqu/issues/136) - [@o1egl](https://github.com/o1egl)
## v8.3.1
* [FIXED] InsertDataset.WithDialect return old dataset [#126](https://github.com/doug-martin/goqu/issues/126) - [@chen56](https://github.com/chen56)
* Test clean up and more testing pattern consistency
* Changed to use assertion methods off of suite
* Updated Equals assertions to have expected output first
* Increase overall test coverage.
## v8.3.0
* [Added] Support for `DISTINCT ON` clauses [#119](https://github.com/doug-martin/goqu/issues/119)
## v8.2.2
* [FIX] Scanner errors on pointers to primitive values [#122](https://github.com/doug-martin/goqu/issues/122)
## v8.2.1
* [FIX] Return an error when an empty identifier is encountered [#115](https://github.com/doug-martin/goqu/issues/115)
## v8.2.0
* [FIX] Fix reflection errors related to nil pointers and unexported fields [#118](https://github.com/doug-martin/goqu/issues/118)
* Unexported fields are ignored when creating a columnMap
* Nil embedded pointers will no longer cause a panic
* Fields on nil embedded pointers will be ignored when creating update or insert statements.
* [ADDED] You can now ingore embedded structs and their fields by using `db:"-"` tag on the embedded struct.
## v8.1.0
* [ADDED] Support column DEFAULT when inserting/updating via struct [#27](https://github.com/doug-martin/goqu/issues/27)
## v8.0.1
* [ADDED] Multi table update support for `mysql` and `postgres` [#60](https://github.com/doug-martin/goqu/issues/60)
* [ADDED] `goqu.V` so values can be used on the LHS of expressions [#104](https://github.com/doug-martin/goqu/issues/104)
## v8.0.0
A major change the the API was made in `v8` to seperate concerns between the different SQL statement types.
**Why the change?**
1. There were feature requests that could not be cleanly implemented with everything in a single dataset.
2. Too much functionality was encapsulated in a single datastructure.
* It was unclear what methods could be used for each SQL statement type.
* Changing a feature for one statement type had the possiblity of breaking another statement type.
* Test coverage was decent but was almost solely concerned about SELECT statements, breaking them up allowed for focused testing on each statement type.
* Most the SQL generation methods (`ToInsertSQL`, `ToUpdateSQL` etc.) took arguments which lead to an ugly API that was not uniform for each statement type, and proved to be inflexible.
**What Changed**
There are now five dataset types, `SelectDataset`, `InsertDataset`, `UpdateDataset`, `DeleteDataset` and `TruncateDataset`
Each dataset type has its own entry point.
* `goqu.From`, `Database#From`, `DialectWrapper#From` - Create SELECT
* `goqu.Insert`, `Database#Insert`, `DialectWrapper#Insert` - Create INSERT
* `goqu.Update`, `Database#db.Update`, `DialectWrapper#Update` - Create UPDATE
* `goqu.Delete`, `Database#Delete`, `DialectWrapper#Delete` - Create DELETE
* `goqu.Truncate`, `Database#Truncate`, `DialectWrapper#Truncate` - Create TRUNCATE
`ToInsertSQL`, `ToUpdateSQL`, `ToDeleteSQL`, and `ToTruncateSQL` (and variations of them) methods have been removed from the `SelectDataset`. Instead use the `ToSQL` methods on each dataset type.
Each dataset type will have an `Executor` and `ToSQL` method so a common interface can be created for each type.
## v7.4.0
* [FIXED] literalTime use t.UTC() , This behavior is different from the original sql.DB [#106](https://github.com/doug-martin/goqu/issues/106) - [chen56](https://github.com/chen56)
* [ADDED] Add new method WithTx for Database [#108](https://github.com/doug-martin/goqu/issues/108) - [Xuyuanp](https://github.com/Xuyuanp)
## v7.3.1
* [ADDED] Exposed `goqu.NewTx` to allow creating a goqu tx directly from a `sql.Tx` instead of using `goqu.Database#Begin` [#95](https://github.com/doug-martin/goqu/issues/95)
* [ADDED] `goqu.Database.BeginTx` [#98](https://github.com/doug-martin/goqu/issues/98)
## v7.3.0
* [ADDED] UPDATE and INSERT should use struct Field name if db tag is not specified [#57](https://github.com/doug-martin/goqu/issues/57)
* [CHANGE] Changed goqu.Database to accept a SQLDatabase interface to allow using goqu.Database with other libraries such as `sqlx` [#95](https://github.com/doug-martin/goqu/issues/95)
## v7.2.0
* [FIXED] Sqlite3 does not accept SELECT * UNION (SELECT *) [#79](https://github.com/doug-martin/goqu/issues/79)
* [FIXED] Where(Ex{}) causes panics [mysql] [#49](https://github.com/doug-martin/goqu/issues/49)
* [ADDED] Support for OrderPrepend [#61](https://github.com/doug-martin/goqu/issues/61)
* [DOCS] Added new section about loading a dialect and using it to build SQL [#44](https://github.com/doug-martin/goqu/issues/44)
## v7.1.0
* [FIXED] Embedded pointers with property names that duplicate parent struct properties. [#23](https://github.com/doug-martin/goqu/issues/23)
* [FIXED] Can't scan values using []byte or []string [#90](https://github.com/doug-martin/goqu/issues/90)
* When a slice that is `*sql.RawBytes`, `*[]byte` or `sql.Scanner` no errors will be returned.
## v7.0.1
* Fix issue where structs with pointer fields where not set properly [#86](https://github.com/doug-martin/goqu/pull/86) and [#89](https://github.com/doug-martin/goqu/pull/89) - [@efureev](https://github.com/efureev)
## v7.0.0
**Linting**
* Add linting checks and fixed errors
* Renamed all snake_case variables to be camelCase.
* Fixed examples to always map to a defined method
* Renamed `adapters` to `dialect` to more closely match their intended purpose.
**API Changes**
* Updated all sql generations methods to from `Sql` to `SQL`
* `ToSql` -> `ToSQL`
* `ToInsertSql` -> `ToInsertSQL`
* `ToUpdateSql` -> `ToUpdateSQL`
* `ToDeleteSql` -> `ToDeleteSQL`
* `ToTruncateSql` -> `ToTruncateSQL`
* Abstracted out `dialect_options` from the adapter to make the dialect self contained.
* This also removed the dataset<->adapter co dependency making the dialect self contained.
* Refactored the `goqu.I` method.
* Added new `goqu.S`, `goqu.T` and `goqu.C` methods to clarify why type of identifier you are using.
* `goqu.I` should only be used when you have a qualified identifier (e.g. `goqu.I("my_schema.my_table.my_col")
* Added new `goqu.Dialect` method to make using `goqu` as an SQL builder easier.
**Internal Changes**
* Pulled expressions into their own package
* Broke up expressions.go into multiple files to make working with and defining them easier.
* Moved the user facing methods into the main `goqu` to keep the same API as before.
* Added more examples
* Moved non-user facing structs and interfaces to internal modules to clean up API.
* Increased test coverage.
## v6.1.0
* Handle nil *time.Time Literal [#73](https://github.com/doug-martin/goqu/pull/73) and [#52](https://github.com/doug-martin/goqu/pull/52) - [@RoarkeRandall](https://github.com/RoarkeRandall) and [@quetz](https://github.com/quetz)
* Add ability to change column rename function [#66](https://github.com/doug-martin/goqu/pull/66) - [@blainehansen](https://github.com/blainehansen)
## v6.0.0
* Updated go support to `1.10`, `1.11` and `1.12`
* Change testify dependency from c2fo/testify back to stretchr/testify.
* Add support for "FOR UPDATE" and "SKIP LOCKED" [#62](https://github.com/doug-martin/goqu/pull/62) - [@btubbs](https://github.com/btubbs)
* Changed to use go modules
## v5.0.0
* Drop go 1.6 support, supported versions are `1.8`, `1.9` and latest
* Add context support [#64](https://github.com/doug-martin/goqu/pull/64) - [@cmoad](https://github.com/cmoad)
## v4.2.0
* Add support for ON CONFLICT when using a dataset [#55](https://github.com/doug-martin/goqu/pull/55) - [@bobrnor](https://github.com/bobrnor)
## v4.1.0
* Support for defining WITH clauses for Common Table Expressions (CTE) [#39](https://github.com/doug-martin/goqu/pull/39) - [@Oscil8](https://github.com/Oscil8)
## v4.0
* Prepared(true) issues when using IS NULL comparisson operation [#33](https://github.com/doug-martin/goqu/pull/33) - [@danielfbm](https://github.com/danielfbm)
## v3.3
* Add `upsert` support via `InsertIgnore` and `InsertConflict` methods - [#25](https://github.com/doug-martin/goqu/pull/28) - [@aheuermann](https://github.com/aheuermann)
* Adding vendor dependencies and updating tests to run in docker containers [#29](https://github.com/doug-martin/goqu/pull/29) - [@aheuermann](https://github.com/aheuermann)
## v3.2
* Add range clauses ([NOT] BETWEEN) support - [#25](https://github.com/doug-martin/goqu/pull/25) - [@denisvm](https://github.com/denisvm)
* Readmefix [#26](https://github.com/doug-martin/goqu/pull/26) - [@tiagopotencia](https://github.com/tiagopotencia)
## v3.1.3
* Bugfix for chained Where() [#20](https://github.com/doug-martin/goqu/pull/20) - [@Emreu](https://github.com/Emreu)
## v3.1.2
* Fixing ScanStruct issue with embedded pointers in crud_exec [#20](https://github.com/doug-martin/goqu/pull/20) - [@ruzz311](https://github.com/ruzz311)
## v3.1.1
* Fixing race condition with struct_map_cache in crud_exec [#18](https://github.com/doug-martin/goqu/pull/18) - [@andymoon](https://github.com/andymoon), [@aheuermann](https://github.com/aheuermann)
## v3.1.0
* Version 3.1 [#14](https://github.com/doug-martin/goqu/pull/14) - [@andymoon](https://github.com/andymoon)
* Fix an issue with a nil pointer access on the inserts and updates.
* Allowing ScanStructs to take a struct with an embedded pointer to a struct.
* Change to check if struct is Anonymous when recursing through an embedded struct.
* Updated to use the latest version of github.com/DATA-DOG/go-sqlmock.
## v3.0.1
* Add literal bytes and update to c2fo testify [#15](https://github.com/doug-martin/goqu/pull/15) - [@TechnotronicOz](https://github.com/TechnotronicOz)
## v3.0.0
* Added support for embedded structs when inserting or updating. [#13](https://github.com/doug-martin/goqu/pull/13) - [@andymoon](https://github.com/andymoon)
## v2.0.3
* Fixed issue with transient columns and the auto select of columns.
## v2.0.2
* Changed references to "github.com/doug-martin/goqu" to "gopkg.in/doug-martin/goqu.v2"
## v2.0.1
* Fixed issue when `ScanStruct(s)` was used with `SelectDistinct` and caused a panic.
## v2.0.0
* When scanning a struct or slice of structs, the struct(s) will be parsed for the column names to select. [#9](https://github.com/doug-martin/goqu/pull/9) - [@technotronicoz](https://github.com/TechnotronicOz)
## v1.0.0
* You can now passed an IdentiferExpression to `As` [#8](https://github.com/doug-martin/goqu/pull/8) - [@croachrose](https://github.com/croachrose)
* Added info about installation through [gopkg.in](http://labix.org/gopkg.in)
## v0.3.1
* Fixed issue setting Logger when starting a new transaction.
## v0.3.0
* Changed sql generation methods to use a common naming convention. `To(Sql|Insert|Update|Delete)`
* Also changed to have common return values `string, []interface{}, error)`
* Added `Dataset.Prepared` which allows a user to specify whether or not SQL should be interpolated. [#7](https://github.com/doug-martin/goqu/issues/7)
* Updated Docs
* More examples
* Increased test coverage.
## v0.2.0
* Changed `CrudExec` to not wrap driver errors in a GoquError [#2](https://github.com/doug-martin/goqu/issues/2)
* Added ability to use a dataset in an `Ex` map or `Eq` expression without having to use `In` [#3](https://github.com/doug-martin/goqu/issues/3)
* `db.From("test").Where(goqu.Ex{"a": db.From("test").Select("b")})`
* Updated readme with links to [`DefaultAdapter`](https://godoc.org/github.com/doug-martin/goqu#DefaultAdapter)
## v0.1.1
* Added SQLite3 adapter [#1](https://github.com/doug-martin/goqu/pull/1) - [@mattn](https://github.com/mattn)
## v0.1.0
* Added:
* [`Ex`](https://godoc.org/github.com/doug-martin/goqu#Ex)
* [`ExOr`](https://godoc.org/github.com/doug-martin/goqu#ExOr)
* [`Op`](https://godoc.org/github.com/doug-martin/goqu#Op)
* More tests and examples
* Added CONTRIBUTING.md
* Added LICENSE information
* Removed godoc introduction in favor of just maintaining the README.
## v0.0.2
* Fixed issue with goqu.New not returning a pointer to a Database
## v0.0.1
* Initial release

21
vendor/github.com/doug-martin/goqu/v9/LICENSE generated vendored Normal file
View File

@@ -0,0 +1,21 @@
MIT License
Copyright (c) 2017 Doug Martin
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

6
vendor/github.com/doug-martin/goqu/v9/Makefile generated vendored Normal file
View File

@@ -0,0 +1,6 @@
#phony dependency task that does nothing
#"make executable" does not run if there is a ./executable directory, unless the task has a dependency
phony:
lint:
docker run --rm -v ${CURDIR}:/app -w /app golangci/golangci-lint:v1.23.8 golangci-lint run -v

310
vendor/github.com/doug-martin/goqu/v9/README.md generated vendored Normal file
View File

@@ -0,0 +1,310 @@
```
__ _ ___ __ _ _ _
/ _` |/ _ \ / _` | | | |
| (_| | (_) | (_| | |_| |
\__, |\___/ \__, |\__,_|
|___/ |_|
```
[![GitHub tag](https://img.shields.io/github/tag/doug-martin/goqu.svg?style=flat)](https://github.com/doug-martin/goqu/releases)
[![Test](https://github.com/doug-martin/goqu/workflows/Test/badge.svg?branch=master&event=push)](https://github.com/doug-martin/goqu/actions?query=workflow%3ATest+and+branch%3Amaster+)
[![Go Reference](https://pkg.go.dev/badge/github.com/doug-martin/goqu/v9.svg)](https://pkg.go.dev/github.com/doug-martin/goqu/v9)
[![codecov](https://codecov.io/gh/doug-martin/goqu/branch/master/graph/badge.svg)](https://codecov.io/gh/doug-martin/goqu)
[![Go Report Card](https://goreportcard.com/badge/github.com/doug-martin/goqu/v9)](https://goreportcard.com/report/github.com/doug-martin/goqu/v9)
`goqu` is an expressive SQL builder and executor
If you are upgrading from an older version please read the [Migrating Between Versions](./docs/version_migration.md) docs.
## Installation
If using go modules.
```sh
go get -u github.com/doug-martin/goqu/v9
```
If you are not using go modules...
**NOTE** You should still be able to use this package if you are using go version `>v1.10` but, you will need to drop the version from the package. `import "github.com/doug-martin/goqu/v9` -> `import "github.com/doug-martin/goqu"`
```sh
go get -u github.com/doug-martin/goqu
```
### [Migrating Between Versions](./docs/version_migration.md)
## Features
`goqu` comes with many features but here are a few of the more notable ones
* Query Builder
* Parameter interpolation (e.g `SELECT * FROM "items" WHERE "id" = ?` -> `SELECT * FROM "items" WHERE "id" = 1`)
* Built from the ground up with multiple dialects in mind
* Insert, Multi Insert, Update, and Delete support
* Scanning of rows to struct[s] or primitive value[s]
While goqu may support the scanning of rows into structs it is not intended to be used as an ORM if you are looking for common ORM features like associations,
or hooks I would recommend looking at some of the great ORM libraries such as:
* [gorm](https://github.com/jinzhu/gorm)
* [hood](https://github.com/eaigner/hood)
## Why?
We tried a few other sql builders but each was a thin wrapper around sql fragments that we found error prone. `goqu` was built with the following goals in mind:
* Make the generation of SQL easy and enjoyable
* Create an expressive DSL that would find common errors with SQL at compile time.
* Provide a DSL that accounts for the common SQL expressions, NOT every nuance for each database.
* Provide developers the ability to:
* Use SQL when desired
* Easily scan results into primitive values and structs
* Use the native sql.Db methods when desired
## Docs
* [Dialect](./docs/dialect.md) - Introduction to different dialects (`mysql`, `postgres`, `sqlite3`, `sqlserver` etc)
* [Expressions](./docs/expressions.md) - Introduction to `goqu` expressions and common examples.
* [Select Dataset](./docs/selecting.md) - Docs and examples about creating and executing SELECT sql statements.
* [Insert Dataset](./docs/inserting.md) - Docs and examples about creating and executing INSERT sql statements.
* [Update Dataset](./docs/updating.md) - Docs and examples about creating and executing UPDATE sql statements.
* [Delete Dataset](./docs/deleting.md) - Docs and examples about creating and executing DELETE sql statements.
* [Prepared Statements](./docs/interpolation.md) - Docs about interpolation and prepared statements in `goqu`.
* [Database](./docs/database.md) - Docs and examples of using a Database to execute queries in `goqu`
* [Working with time.Time](./docs/time.md) - Docs on how to use alternate time locations.
## Quick Examples
### Select
See the [select dataset](./docs/selecting.md) docs for more in depth examples
```go
sql, _, _ := goqu.From("test").ToSQL()
fmt.Println(sql)
```
Output:
```
SELECT * FROM "test"
```
```go
sql, _, _ := goqu.From("test").Where(goqu.Ex{
"d": []string{"a", "b", "c"},
}).ToSQL()
fmt.Println(sql)
```
Output:
```
SELECT * FROM "test" WHERE ("d" IN ('a', 'b', 'c'))
```
### Insert
See the [insert dataset](./docs/inserting.md) docs for more in depth examples
```go
ds := goqu.Insert("user").
Cols("first_name", "last_name").
Vals(
goqu.Vals{"Greg", "Farley"},
goqu.Vals{"Jimmy", "Stewart"},
goqu.Vals{"Jeff", "Jeffers"},
)
insertSQL, args, _ := ds.ToSQL()
fmt.Println(insertSQL, args)
```
Output:
```sql
INSERT INTO "user" ("first_name", "last_name") VALUES ('Greg', 'Farley'), ('Jimmy', 'Stewart'), ('Jeff', 'Jeffers') []
```
```go
ds := goqu.Insert("user").Rows(
goqu.Record{"first_name": "Greg", "last_name": "Farley"},
goqu.Record{"first_name": "Jimmy", "last_name": "Stewart"},
goqu.Record{"first_name": "Jeff", "last_name": "Jeffers"},
)
insertSQL, args, _ := ds.ToSQL()
fmt.Println(insertSQL, args)
```
Output:
```
INSERT INTO "user" ("first_name", "last_name") VALUES ('Greg', 'Farley'), ('Jimmy', 'Stewart'), ('Jeff', 'Jeffers') []
```
```go
type User struct {
FirstName string `db:"first_name"`
LastName string `db:"last_name"`
}
ds := goqu.Insert("user").Rows(
User{FirstName: "Greg", LastName: "Farley"},
User{FirstName: "Jimmy", LastName: "Stewart"},
User{FirstName: "Jeff", LastName: "Jeffers"},
)
insertSQL, args, _ := ds.ToSQL()
fmt.Println(insertSQL, args)
```
Output:
```
INSERT INTO "user" ("first_name", "last_name") VALUES ('Greg', 'Farley'), ('Jimmy', 'Stewart'), ('Jeff', 'Jeffers') []
```
```go
ds := goqu.Insert("user").Prepared(true).
FromQuery(goqu.From("other_table"))
insertSQL, args, _ := ds.ToSQL()
fmt.Println(insertSQL, args)
```
Output:
```
INSERT INTO "user" SELECT * FROM "other_table" []
```
```go
ds := goqu.Insert("user").Prepared(true).
Cols("first_name", "last_name").
FromQuery(goqu.From("other_table").Select("fn", "ln"))
insertSQL, args, _ := ds.ToSQL()
fmt.Println(insertSQL, args)
```
Output:
```
INSERT INTO "user" ("first_name", "last_name") SELECT "fn", "ln" FROM "other_table" []
```
### Update
See the [update dataset](./docs/updating.md) docs for more in depth examples
```go
sql, args, _ := goqu.Update("items").Set(
goqu.Record{"name": "Test", "address": "111 Test Addr"},
).ToSQL()
fmt.Println(sql, args)
```
Output:
```
UPDATE "items" SET "address"='111 Test Addr',"name"='Test' []
```
```go
type item struct {
Address string `db:"address"`
Name string `db:"name" goqu:"skipupdate"`
}
sql, args, _ := goqu.Update("items").Set(
item{Name: "Test", Address: "111 Test Addr"},
).ToSQL()
fmt.Println(sql, args)
```
Output:
```
UPDATE "items" SET "address"='111 Test Addr' []
```
```go
sql, _, _ := goqu.Update("test").
Set(goqu.Record{"foo": "bar"}).
Where(goqu.Ex{
"a": goqu.Op{"gt": 10}
}).ToSQL()
fmt.Println(sql)
```
Output:
```
UPDATE "test" SET "foo"='bar' WHERE ("a" > 10)
```
### Delete
See the [delete dataset](./docs/deleting.md) docs for more in depth examples
```go
ds := goqu.Delete("items")
sql, args, _ := ds.ToSQL()
fmt.Println(sql, args)
```
```go
sql, _, _ := goqu.Delete("test").Where(goqu.Ex{
"c": nil
}).ToSQL()
fmt.Println(sql)
```
Output:
```
DELETE FROM "test" WHERE ("c" IS NULL)
```
<a name="contributions"></a>
## Contributions
I am always welcoming contributions of any type. Please open an issue or create a PR if you find an issue with any of the following.
* An issue with Documentation
* You found the documentation lacking in some way
If you have an issue with the package please include the following
* The dialect you are using
* A description of the problem
* A short example of how to reproduce (if applicable)
Without those basics it can be difficult to reproduce your issue locally. You may be asked for more information but that is a good starting point.
### New Features
New features and/or enhancements are great and I encourage you to either submit a PR or create an issue. In both cases include the following as the need/requirement may not be readily apparent.
1. The use case
2. A short example
If you are issuing a PR also include the following
1. Tests - otherwise the PR will not be merged
2. Documentation - otherwise the PR will not be merged
3. Examples - [If applicable] see example_test.go for examples
If you find an issue you want to work on please comment on it letting other people know you are looking at it and I will assign the issue to you.
If want to work on an issue but dont know where to start just leave a comment and I'll be more than happy to point you in the right direction.
### Running tests
The test suite requires a postgres, mysql and sqlserver databases. You can override the connection strings with the [`MYSQL_URI`, `PG_URI`, `SQLSERVER_URI` environment variables](https://github.com/doug-martin/goqu/blob/2fe3349/docker-compose.yml#L26)*
```sh
go test -v -race ./...
```
You can also run the tests in a container using [docker-compose](https://docs.docker.com/compose/).
```sh
MYSQL_VERSION=8 POSTGRES_VERSION=13.4 SQLSERVER_VERSION=2017-CU8-ubuntu GO_VERSION=latest docker-compose run goqu
```
## License
`goqu` is released under the [MIT License](http://www.opensource.org/licenses/MIT).

1
vendor/github.com/doug-martin/goqu/v9/_config.yml generated vendored Normal file
View File

@@ -0,0 +1 @@
theme: jekyll-theme-cayman

3
vendor/github.com/doug-martin/goqu/v9/codecov.yml generated vendored Normal file
View File

@@ -0,0 +1,3 @@
ignore:
- "**/mocks/**" # glob accepted
- "mocks/**" # glob accepted

648
vendor/github.com/doug-martin/goqu/v9/database.go generated vendored Normal file
View File

@@ -0,0 +1,648 @@
package goqu
import (
"context"
"database/sql"
"sync"
"github.com/doug-martin/goqu/v9/exec"
)
type (
Logger interface {
Printf(format string, v ...interface{})
}
// Interface for sql.DB, an interface is used so you can use with other
// libraries such as sqlx instead of the native sql.DB
SQLDatabase interface {
Begin() (*sql.Tx, error)
BeginTx(ctx context.Context, opts *sql.TxOptions) (*sql.Tx, error)
ExecContext(ctx context.Context, query string, args ...interface{}) (sql.Result, error)
PrepareContext(ctx context.Context, query string) (*sql.Stmt, error)
QueryContext(ctx context.Context, query string, args ...interface{}) (*sql.Rows, error)
QueryRowContext(ctx context.Context, query string, args ...interface{}) *sql.Row
}
// This struct is the wrapper for a Db. The struct delegates most calls to either an Exec instance or to the Db
// passed into the constructor.
Database struct {
logger Logger
dialect string
// nolint: stylecheck // keep for backwards compatibility
Db SQLDatabase
qf exec.QueryFactory
qfOnce sync.Once
}
)
// This is the common entry point into goqu.
//
// dialect: This is the adapter dialect, you should see your database adapter for the string to use. Built in adapters
// can be found at https://github.com/doug-martin/goqu/tree/master/adapters
//
// db: A sql.Db to use for querying the database
// import (
// "database/sql"
// "fmt"
// "github.com/doug-martin/goqu/v9"
// _ "github.com/doug-martin/goqu/v9/dialect/postgres"
// _ "github.com/lib/pq"
// )
//
// func main() {
// sqlDb, err := sql.Open("postgres", "user=postgres dbname=goqupostgres sslmode=disable ")
// if err != nil {
// panic(err.Error())
// }
// db := goqu.New("postgres", sqlDb)
// }
// The most commonly used Database method is From, which creates a new Dataset that uses the correct adapter and
// supports queries.
// var ids []uint32
// if err := db.From("items").Where(goqu.I("id").Gt(10)).Pluck("id", &ids); err != nil {
// panic(err.Error())
// }
// fmt.Printf("%+v", ids)
func newDatabase(dialect string, db SQLDatabase) *Database {
return &Database{
logger: nil,
dialect: dialect,
Db: db,
qf: nil,
qfOnce: sync.Once{},
}
}
// returns this databases dialect
func (d *Database) Dialect() string {
return d.dialect
}
// Starts a new Transaction.
func (d *Database) Begin() (*TxDatabase, error) {
sqlTx, err := d.Db.Begin()
if err != nil {
return nil, err
}
tx := NewTx(d.dialect, sqlTx)
tx.Logger(d.logger)
return tx, nil
}
// Starts a new Transaction. See sql.DB#BeginTx for option description
func (d *Database) BeginTx(ctx context.Context, opts *sql.TxOptions) (*TxDatabase, error) {
sqlTx, err := d.Db.BeginTx(ctx, opts)
if err != nil {
return nil, err
}
tx := NewTx(d.dialect, sqlTx)
tx.Logger(d.logger)
return tx, nil
}
// WithTx starts a new transaction and executes it in Wrap method
func (d *Database) WithTx(fn func(*TxDatabase) error) error {
tx, err := d.Begin()
if err != nil {
return err
}
return tx.Wrap(func() error { return fn(tx) })
}
// Creates a new Dataset that uses the correct adapter and supports queries.
// var ids []uint32
// if err := db.From("items").Where(goqu.I("id").Gt(10)).Pluck("id", &ids); err != nil {
// panic(err.Error())
// }
// fmt.Printf("%+v", ids)
//
// from...: Sources for you dataset, could be table names (strings), a goqu.Literal or another goqu.Dataset
func (d *Database) From(from ...interface{}) *SelectDataset {
return newDataset(d.dialect, d.queryFactory()).From(from...)
}
func (d *Database) Select(cols ...interface{}) *SelectDataset {
return newDataset(d.dialect, d.queryFactory()).Select(cols...)
}
func (d *Database) Update(table interface{}) *UpdateDataset {
return newUpdateDataset(d.dialect, d.queryFactory()).Table(table)
}
func (d *Database) Insert(table interface{}) *InsertDataset {
return newInsertDataset(d.dialect, d.queryFactory()).Into(table)
}
func (d *Database) Delete(table interface{}) *DeleteDataset {
return newDeleteDataset(d.dialect, d.queryFactory()).From(table)
}
func (d *Database) Truncate(table ...interface{}) *TruncateDataset {
return newTruncateDataset(d.dialect, d.queryFactory()).Table(table...)
}
// Sets the logger for to use when logging queries
func (d *Database) Logger(logger Logger) {
d.logger = logger
}
// Logs a given operation with the specified sql and arguments
func (d *Database) Trace(op, sqlString string, args ...interface{}) {
if d.logger != nil {
if sqlString != "" {
if len(args) != 0 {
d.logger.Printf("[goqu] %s [query:=`%s` args:=%+v]", op, sqlString, args)
} else {
d.logger.Printf("[goqu] %s [query:=`%s`]", op, sqlString)
}
} else {
d.logger.Printf("[goqu] %s", op)
}
}
}
// Uses the db to Execute the query with arguments and return the sql.Result
//
// query: The SQL to execute
//
// args...: for any placeholder parameters in the query
func (d *Database) Exec(query string, args ...interface{}) (sql.Result, error) {
return d.ExecContext(context.Background(), query, args...)
}
// Uses the db to Execute the query with arguments and return the sql.Result
//
// query: The SQL to execute
//
// args...: for any placeholder parameters in the query
func (d *Database) ExecContext(ctx context.Context, query string, args ...interface{}) (sql.Result, error) {
d.Trace("EXEC", query, args...)
return d.Db.ExecContext(ctx, query, args...)
}
// Can be used to prepare a query.
//
// You can use this in tandem with a dataset by doing the following.
// sql, args, err := db.From("items").Where(goqu.I("id").Gt(10)).ToSQL(true)
// if err != nil{
// panic(err.Error()) //you could gracefully handle the error also
// }
// stmt, err := db.Prepare(sql)
// if err != nil{
// panic(err.Error()) //you could gracefully handle the error also
// }
// defer stmt.Close()
// rows, err := stmt.Query(args)
// if err != nil{
// panic(err.Error()) //you could gracefully handle the error also
// }
// defer rows.Close()
// for rows.Next(){
// //scan your rows
// }
// if rows.Err() != nil{
// panic(err.Error()) //you could gracefully handle the error also
// }
//
// query: The SQL statement to prepare.
func (d *Database) Prepare(query string) (*sql.Stmt, error) {
return d.PrepareContext(context.Background(), query)
}
// Can be used to prepare a query.
//
// You can use this in tandem with a dataset by doing the following.
// sql, args, err := db.From("items").Where(goqu.I("id").Gt(10)).ToSQL(true)
// if err != nil{
// panic(err.Error()) //you could gracefully handle the error also
// }
// stmt, err := db.Prepare(sql)
// if err != nil{
// panic(err.Error()) //you could gracefully handle the error also
// }
// defer stmt.Close()
// rows, err := stmt.QueryContext(ctx, args)
// if err != nil{
// panic(err.Error()) //you could gracefully handle the error also
// }
// defer rows.Close()
// for rows.Next(){
// //scan your rows
// }
// if rows.Err() != nil{
// panic(err.Error()) //you could gracefully handle the error also
// }
//
// query: The SQL statement to prepare.
func (d *Database) PrepareContext(ctx context.Context, query string) (*sql.Stmt, error) {
d.Trace("PREPARE", query)
return d.Db.PrepareContext(ctx, query)
}
// Used to query for multiple rows.
//
// You can use this in tandem with a dataset by doing the following.
// sql, err := db.From("items").Where(goqu.I("id").Gt(10)).ToSQL()
// if err != nil{
// panic(err.Error()) //you could gracefully handle the error also
// }
// rows, err := stmt.Query(args)
// if err != nil{
// panic(err.Error()) //you could gracefully handle the error also
// }
// defer rows.Close()
// for rows.Next(){
// //scan your rows
// }
// if rows.Err() != nil{
// panic(err.Error()) //you could gracefully handle the error also
// }
//
// query: The SQL to execute
//
// args...: for any placeholder parameters in the query
func (d *Database) Query(query string, args ...interface{}) (*sql.Rows, error) {
return d.QueryContext(context.Background(), query, args...)
}
// Used to query for multiple rows.
//
// You can use this in tandem with a dataset by doing the following.
// sql, err := db.From("items").Where(goqu.I("id").Gt(10)).ToSQL()
// if err != nil{
// panic(err.Error()) //you could gracefully handle the error also
// }
// rows, err := stmt.QueryContext(ctx, args)
// if err != nil{
// panic(err.Error()) //you could gracefully handle the error also
// }
// defer rows.Close()
// for rows.Next(){
// //scan your rows
// }
// if rows.Err() != nil{
// panic(err.Error()) //you could gracefully handle the error also
// }
//
// query: The SQL to execute
//
// args...: for any placeholder parameters in the query
func (d *Database) QueryContext(ctx context.Context, query string, args ...interface{}) (*sql.Rows, error) {
d.Trace("QUERY", query, args...)
return d.Db.QueryContext(ctx, query, args...)
}
// Used to query for a single row.
//
// You can use this in tandem with a dataset by doing the following.
// sql, err := db.From("items").Where(goqu.I("id").Gt(10)).Limit(1).ToSQL()
// if err != nil{
// panic(err.Error()) //you could gracefully handle the error also
// }
// rows, err := stmt.QueryRow(args)
// if err != nil{
// panic(err.Error()) //you could gracefully handle the error also
// }
// //scan your row
//
// query: The SQL to execute
//
// args...: for any placeholder parameters in the query
func (d *Database) QueryRow(query string, args ...interface{}) *sql.Row {
return d.QueryRowContext(context.Background(), query, args...)
}
// Used to query for a single row.
//
// You can use this in tandem with a dataset by doing the following.
// sql, err := db.From("items").Where(goqu.I("id").Gt(10)).Limit(1).ToSQL()
// if err != nil{
// panic(err.Error()) //you could gracefully handle the error also
// }
// rows, err := stmt.QueryRowContext(ctx, args)
// if err != nil{
// panic(err.Error()) //you could gracefully handle the error also
// }
// //scan your row
//
// query: The SQL to execute
//
// args...: for any placeholder parameters in the query
func (d *Database) QueryRowContext(ctx context.Context, query string, args ...interface{}) *sql.Row {
d.Trace("QUERY ROW", query, args...)
return d.Db.QueryRowContext(ctx, query, args...)
}
func (d *Database) queryFactory() exec.QueryFactory {
d.qfOnce.Do(func() {
d.qf = exec.NewQueryFactory(d)
})
return d.qf
}
// Queries the database using the supplied query, and args and uses CrudExec.ScanStructs to scan the results into a
// slice of structs
//
// i: A pointer to a slice of structs
//
// query: The SQL to execute
//
// args...: for any placeholder parameters in the query
func (d *Database) ScanStructs(i interface{}, query string, args ...interface{}) error {
return d.ScanStructsContext(context.Background(), i, query, args...)
}
// Queries the database using the supplied context, query, and args and uses CrudExec.ScanStructsContext to scan the
// results into a slice of structs
//
// i: A pointer to a slice of structs
//
// query: The SQL to execute
//
// args...: for any placeholder parameters in the query
func (d *Database) ScanStructsContext(ctx context.Context, i interface{}, query string, args ...interface{}) error {
return d.queryFactory().FromSQL(query, args...).ScanStructsContext(ctx, i)
}
// Queries the database using the supplied query, and args and uses CrudExec.ScanStruct to scan the results into a
// struct
//
// i: A pointer to a struct
//
// query: The SQL to execute
//
// args...: for any placeholder parameters in the query
func (d *Database) ScanStruct(i interface{}, query string, args ...interface{}) (bool, error) {
return d.ScanStructContext(context.Background(), i, query, args...)
}
// Queries the database using the supplied context, query, and args and uses CrudExec.ScanStructContext to scan the
// results into a struct
//
// i: A pointer to a struct
//
// query: The SQL to execute
//
// args...: for any placeholder parameters in the query
func (d *Database) ScanStructContext(ctx context.Context, i interface{}, query string, args ...interface{}) (bool, error) {
return d.queryFactory().FromSQL(query, args...).ScanStructContext(ctx, i)
}
// Queries the database using the supplied query, and args and uses CrudExec.ScanVals to scan the results into a slice
// of primitive values
//
// i: A pointer to a slice of primitive values
//
// query: The SQL to execute
//
// args...: for any placeholder parameters in the query
func (d *Database) ScanVals(i interface{}, query string, args ...interface{}) error {
return d.ScanValsContext(context.Background(), i, query, args...)
}
// Queries the database using the supplied context, query, and args and uses CrudExec.ScanValsContext to scan the
// results into a slice of primitive values
//
// i: A pointer to a slice of primitive values
//
// query: The SQL to execute
//
// args...: for any placeholder parameters in the query
func (d *Database) ScanValsContext(ctx context.Context, i interface{}, query string, args ...interface{}) error {
return d.queryFactory().FromSQL(query, args...).ScanValsContext(ctx, i)
}
// Queries the database using the supplied query, and args and uses CrudExec.ScanVal to scan the results into a
// primitive value
//
// i: A pointer to a primitive value
//
// query: The SQL to execute
//
// args...: for any placeholder parameters in the query
func (d *Database) ScanVal(i interface{}, query string, args ...interface{}) (bool, error) {
return d.ScanValContext(context.Background(), i, query, args...)
}
// Queries the database using the supplied context, query, and args and uses CrudExec.ScanValContext to scan the
// results into a primitive value
//
// i: A pointer to a primitive value
//
// query: The SQL to execute
//
// args...: for any placeholder parameters in the query
func (d *Database) ScanValContext(ctx context.Context, i interface{}, query string, args ...interface{}) (bool, error) {
return d.queryFactory().FromSQL(query, args...).ScanValContext(ctx, i)
}
// A wrapper around a sql.Tx and works the same way as Database
type (
// Interface for sql.Tx, an interface is used so you can use with other
// libraries such as sqlx instead of the native sql.DB
SQLTx interface {
ExecContext(ctx context.Context, query string, args ...interface{}) (sql.Result, error)
PrepareContext(ctx context.Context, query string) (*sql.Stmt, error)
QueryContext(ctx context.Context, query string, args ...interface{}) (*sql.Rows, error)
QueryRowContext(ctx context.Context, query string, args ...interface{}) *sql.Row
Commit() error
Rollback() error
}
TxDatabase struct {
logger Logger
dialect string
Tx SQLTx
qf exec.QueryFactory
qfOnce sync.Once
}
)
// Creates a new TxDatabase
func NewTx(dialect string, tx SQLTx) *TxDatabase {
return &TxDatabase{dialect: dialect, Tx: tx}
}
// returns this databases dialect
func (td *TxDatabase) Dialect() string {
return td.dialect
}
// Creates a new Dataset for querying a Database.
func (td *TxDatabase) From(cols ...interface{}) *SelectDataset {
return newDataset(td.dialect, td.queryFactory()).From(cols...)
}
func (td *TxDatabase) Select(cols ...interface{}) *SelectDataset {
return newDataset(td.dialect, td.queryFactory()).Select(cols...)
}
func (td *TxDatabase) Update(table interface{}) *UpdateDataset {
return newUpdateDataset(td.dialect, td.queryFactory()).Table(table)
}
func (td *TxDatabase) Insert(table interface{}) *InsertDataset {
return newInsertDataset(td.dialect, td.queryFactory()).Into(table)
}
func (td *TxDatabase) Delete(table interface{}) *DeleteDataset {
return newDeleteDataset(td.dialect, td.queryFactory()).From(table)
}
func (td *TxDatabase) Truncate(table ...interface{}) *TruncateDataset {
return newTruncateDataset(td.dialect, td.queryFactory()).Table(table...)
}
// Sets the logger
func (td *TxDatabase) Logger(logger Logger) {
td.logger = logger
}
func (td *TxDatabase) Trace(op, sqlString string, args ...interface{}) {
if td.logger != nil {
if sqlString != "" {
if len(args) != 0 {
td.logger.Printf("[goqu - transaction] %s [query:=`%s` args:=%+v] ", op, sqlString, args)
} else {
td.logger.Printf("[goqu - transaction] %s [query:=`%s`] ", op, sqlString)
}
} else {
td.logger.Printf("[goqu - transaction] %s", op)
}
}
}
// See Database#Exec
func (td *TxDatabase) Exec(query string, args ...interface{}) (sql.Result, error) {
return td.ExecContext(context.Background(), query, args...)
}
// See Database#ExecContext
func (td *TxDatabase) ExecContext(ctx context.Context, query string, args ...interface{}) (sql.Result, error) {
td.Trace("EXEC", query, args...)
return td.Tx.ExecContext(ctx, query, args...)
}
// See Database#Prepare
func (td *TxDatabase) Prepare(query string) (*sql.Stmt, error) {
return td.PrepareContext(context.Background(), query)
}
// See Database#PrepareContext
func (td *TxDatabase) PrepareContext(ctx context.Context, query string) (*sql.Stmt, error) {
td.Trace("PREPARE", query)
return td.Tx.PrepareContext(ctx, query)
}
// See Database#Query
func (td *TxDatabase) Query(query string, args ...interface{}) (*sql.Rows, error) {
return td.QueryContext(context.Background(), query, args...)
}
// See Database#QueryContext
func (td *TxDatabase) QueryContext(ctx context.Context, query string, args ...interface{}) (*sql.Rows, error) {
td.Trace("QUERY", query, args...)
return td.Tx.QueryContext(ctx, query, args...)
}
// See Database#QueryRow
func (td *TxDatabase) QueryRow(query string, args ...interface{}) *sql.Row {
return td.QueryRowContext(context.Background(), query, args...)
}
// See Database#QueryRowContext
func (td *TxDatabase) QueryRowContext(ctx context.Context, query string, args ...interface{}) *sql.Row {
td.Trace("QUERY ROW", query, args...)
return td.Tx.QueryRowContext(ctx, query, args...)
}
func (td *TxDatabase) queryFactory() exec.QueryFactory {
td.qfOnce.Do(func() {
td.qf = exec.NewQueryFactory(td)
})
return td.qf
}
// See Database#ScanStructs
func (td *TxDatabase) ScanStructs(i interface{}, query string, args ...interface{}) error {
return td.ScanStructsContext(context.Background(), i, query, args...)
}
// See Database#ScanStructsContext
func (td *TxDatabase) ScanStructsContext(ctx context.Context, i interface{}, query string, args ...interface{}) error {
return td.queryFactory().FromSQL(query, args...).ScanStructsContext(ctx, i)
}
// See Database#ScanStruct
func (td *TxDatabase) ScanStruct(i interface{}, query string, args ...interface{}) (bool, error) {
return td.ScanStructContext(context.Background(), i, query, args...)
}
// See Database#ScanStructContext
func (td *TxDatabase) ScanStructContext(ctx context.Context, i interface{}, query string, args ...interface{}) (bool, error) {
return td.queryFactory().FromSQL(query, args...).ScanStructContext(ctx, i)
}
// See Database#ScanVals
func (td *TxDatabase) ScanVals(i interface{}, query string, args ...interface{}) error {
return td.ScanValsContext(context.Background(), i, query, args...)
}
// See Database#ScanValsContext
func (td *TxDatabase) ScanValsContext(ctx context.Context, i interface{}, query string, args ...interface{}) error {
return td.queryFactory().FromSQL(query, args...).ScanValsContext(ctx, i)
}
// See Database#ScanVal
func (td *TxDatabase) ScanVal(i interface{}, query string, args ...interface{}) (bool, error) {
return td.ScanValContext(context.Background(), i, query, args...)
}
// See Database#ScanValContext
func (td *TxDatabase) ScanValContext(ctx context.Context, i interface{}, query string, args ...interface{}) (bool, error) {
return td.queryFactory().FromSQL(query, args...).ScanValContext(ctx, i)
}
// COMMIT the transaction
func (td *TxDatabase) Commit() error {
td.Trace("COMMIT", "")
return td.Tx.Commit()
}
// ROLLBACK the transaction
func (td *TxDatabase) Rollback() error {
td.Trace("ROLLBACK", "")
return td.Tx.Rollback()
}
// A helper method that will automatically COMMIT or ROLLBACK once the supplied function is done executing
//
// tx, err := db.Begin()
// if err != nil{
// panic(err.Error()) // you could gracefully handle the error also
// }
// if err := tx.Wrap(func() error{
// if _, err := tx.From("test").Insert(Record{"a":1, "b": "b"}).Exec(){
// // this error will be the return error from the Wrap call
// return err
// }
// return nil
// }); err != nil{
// panic(err.Error()) // you could gracefully handle the error also
// }
func (td *TxDatabase) Wrap(fn func() error) (err error) {
defer func() {
if p := recover(); p != nil {
_ = td.Rollback()
panic(p)
}
if err != nil {
if rollbackErr := td.Rollback(); rollbackErr != nil {
err = rollbackErr
}
} else {
if commitErr := td.Commit(); commitErr != nil {
err = commitErr
}
}
}()
return fn()
}

244
vendor/github.com/doug-martin/goqu/v9/delete_dataset.go generated vendored Normal file
View File

@@ -0,0 +1,244 @@
package goqu
import (
"github.com/doug-martin/goqu/v9/exec"
"github.com/doug-martin/goqu/v9/exp"
"github.com/doug-martin/goqu/v9/internal/errors"
"github.com/doug-martin/goqu/v9/internal/sb"
)
var ErrBadFromArgument = errors.New("unsupported DeleteDataset#From argument, a string or identifier expression is required")
type DeleteDataset struct {
dialect SQLDialect
clauses exp.DeleteClauses
isPrepared prepared
queryFactory exec.QueryFactory
err error
}
// used internally by database to create a database with a specific adapter
func newDeleteDataset(d string, queryFactory exec.QueryFactory) *DeleteDataset {
return &DeleteDataset{
clauses: exp.NewDeleteClauses(),
dialect: GetDialect(d),
queryFactory: queryFactory,
isPrepared: preparedNoPreference,
err: nil,
}
}
func Delete(table interface{}) *DeleteDataset {
return newDeleteDataset("default", nil).From(table)
}
func (dd *DeleteDataset) Expression() exp.Expression {
return dd
}
// Clones the dataset
func (dd *DeleteDataset) Clone() exp.Expression {
return dd.copy(dd.clauses)
}
// Set the parameter interpolation behavior. See examples
//
// prepared: If true the dataset WILL NOT interpolate the parameters.
func (dd *DeleteDataset) Prepared(prepared bool) *DeleteDataset {
ret := dd.copy(dd.clauses)
ret.isPrepared = preparedFromBool(prepared)
return ret
}
// Returns true if Prepared(true) has been called on this dataset
func (dd *DeleteDataset) IsPrepared() bool {
return dd.isPrepared.Bool()
}
// Sets the adapter used to serialize values and create the SQL statement
func (dd *DeleteDataset) WithDialect(dl string) *DeleteDataset {
ds := dd.copy(dd.GetClauses())
ds.dialect = GetDialect(dl)
return ds
}
// Returns the current SQLDialect on the dataset
func (dd *DeleteDataset) Dialect() SQLDialect {
return dd.dialect
}
// Set the dialect for this dataset.
func (dd *DeleteDataset) SetDialect(dialect SQLDialect) *DeleteDataset {
cd := dd.copy(dd.GetClauses())
cd.dialect = dialect
return cd
}
// Returns the current clauses on the dataset.
func (dd *DeleteDataset) GetClauses() exp.DeleteClauses {
return dd.clauses
}
// used interally to copy the dataset
func (dd *DeleteDataset) copy(clauses exp.DeleteClauses) *DeleteDataset {
return &DeleteDataset{
dialect: dd.dialect,
clauses: clauses,
isPrepared: dd.isPrepared,
queryFactory: dd.queryFactory,
err: dd.err,
}
}
// Creates a WITH clause for a common table expression (CTE).
//
// The name will be available to SELECT from in the associated query; and can optionally
// contain a list of column names "name(col1, col2, col3)".
//
// The name will refer to the results of the specified subquery.
func (dd *DeleteDataset) With(name string, subquery exp.Expression) *DeleteDataset {
return dd.copy(dd.clauses.CommonTablesAppend(exp.NewCommonTableExpression(false, name, subquery)))
}
// Creates a WITH RECURSIVE clause for a common table expression (CTE)
//
// The name will be available to SELECT from in the associated query; and must
// contain a list of column names "name(col1, col2, col3)" for a recursive clause.
//
// The name will refer to the results of the specified subquery. The subquery for
// a recursive query will always end with a UNION or UNION ALL with a clause that
// refers to the CTE by name.
func (dd *DeleteDataset) WithRecursive(name string, subquery exp.Expression) *DeleteDataset {
return dd.copy(dd.clauses.CommonTablesAppend(exp.NewCommonTableExpression(true, name, subquery)))
}
// Adds a FROM clause. This return a new dataset with the original sources replaced. See examples.
// You can pass in the following.
// string: Will automatically be turned into an identifier
// Dataset: Will be added as a sub select. If the Dataset is not aliased it will automatically be aliased
// LiteralExpression: (See Literal) Will use the literal SQL
func (dd *DeleteDataset) From(table interface{}) *DeleteDataset {
switch t := table.(type) {
case exp.IdentifierExpression:
return dd.copy(dd.clauses.SetFrom(t))
case string:
return dd.copy(dd.clauses.SetFrom(exp.ParseIdentifier(t)))
default:
panic(ErrBadFromArgument)
}
}
// Adds a WHERE clause. See examples.
func (dd *DeleteDataset) Where(expressions ...exp.Expression) *DeleteDataset {
return dd.copy(dd.clauses.WhereAppend(expressions...))
}
// Removes the WHERE clause. See examples.
func (dd *DeleteDataset) ClearWhere() *DeleteDataset {
return dd.copy(dd.clauses.ClearWhere())
}
// Adds a ORDER clause. If the ORDER is currently set it replaces it. See examples.
func (dd *DeleteDataset) Order(order ...exp.OrderedExpression) *DeleteDataset {
return dd.copy(dd.clauses.SetOrder(order...))
}
// Adds a more columns to the current ORDER BY clause. If no order has be previously specified it is the same as
// calling Order. See examples.
func (dd *DeleteDataset) OrderAppend(order ...exp.OrderedExpression) *DeleteDataset {
return dd.copy(dd.clauses.OrderAppend(order...))
}
// Adds a more columns to the beginning of the current ORDER BY clause. If no order has be previously specified it is the same as
// calling Order. See examples.
func (dd *DeleteDataset) OrderPrepend(order ...exp.OrderedExpression) *DeleteDataset {
return dd.copy(dd.clauses.OrderPrepend(order...))
}
// Removes the ORDER BY clause. See examples.
func (dd *DeleteDataset) ClearOrder() *DeleteDataset {
return dd.copy(dd.clauses.ClearOrder())
}
// Adds a LIMIT clause. If the LIMIT is currently set it replaces it. See examples.
func (dd *DeleteDataset) Limit(limit uint) *DeleteDataset {
if limit > 0 {
return dd.copy(dd.clauses.SetLimit(limit))
}
return dd.copy(dd.clauses.ClearLimit())
}
// Adds a LIMIT ALL clause. If the LIMIT is currently set it replaces it. See examples.
func (dd *DeleteDataset) LimitAll() *DeleteDataset {
return dd.copy(dd.clauses.SetLimit(L("ALL")))
}
// Removes the LIMIT clause.
func (dd *DeleteDataset) ClearLimit() *DeleteDataset {
return dd.copy(dd.clauses.ClearLimit())
}
// Adds a RETURNING clause to the dataset if the adapter supports it.
func (dd *DeleteDataset) Returning(returning ...interface{}) *DeleteDataset {
return dd.copy(dd.clauses.SetReturning(exp.NewColumnListExpression(returning...)))
}
// Get any error that has been set or nil if no error has been set.
func (dd *DeleteDataset) Error() error {
return dd.err
}
// Set an error on the dataset if one has not already been set. This error will be returned by a future call to Error
// or as part of ToSQL. This can be used by end users to record errors while building up queries without having to
// track those separately.
func (dd *DeleteDataset) SetError(err error) *DeleteDataset {
if dd.err == nil {
dd.err = err
}
return dd
}
// Generates a DELETE sql statement, if Prepared has been called with true then the parameters will not be interpolated.
// See examples.
//
// Errors:
// * There is an error generating the SQL
func (dd *DeleteDataset) ToSQL() (sql string, params []interface{}, err error) {
return dd.deleteSQLBuilder().ToSQL()
}
// Appends this Dataset's DELETE statement to the SQLBuilder
// This is used internally when using deletes in CTEs
func (dd *DeleteDataset) AppendSQL(b sb.SQLBuilder) {
if dd.err != nil {
b.SetError(dd.err)
return
}
dd.dialect.ToDeleteSQL(b, dd.GetClauses())
}
func (dd *DeleteDataset) GetAs() exp.IdentifierExpression {
return nil
}
func (dd *DeleteDataset) ReturnsColumns() bool {
return dd.clauses.HasReturning()
}
// Creates an QueryExecutor to execute the query.
// db.Delete("test").Exec()
//
// See Dataset#ToUpdateSQL for arguments
func (dd *DeleteDataset) Executor() exec.QueryExecutor {
return dd.queryFactory.FromSQLBuilder(dd.deleteSQLBuilder())
}
func (dd *DeleteDataset) deleteSQLBuilder() sb.SQLBuilder {
buf := sb.NewSQLBuilder(dd.isPrepared.Bool())
if dd.err != nil {
return buf.SetError(dd.err)
}
dd.dialect.ToDeleteSQL(buf, dd.clauses)
return buf
}

View File

@@ -0,0 +1,76 @@
package sqlite3
import (
"time"
"github.com/doug-martin/goqu/v9"
"github.com/doug-martin/goqu/v9/exp"
)
func DialectOptions() *goqu.SQLDialectOptions {
opts := goqu.DefaultDialectOptions()
opts.SupportsReturn = false
opts.SupportsOrderByOnUpdate = true
opts.SupportsLimitOnUpdate = true
opts.SupportsOrderByOnDelete = true
opts.SupportsLimitOnDelete = true
opts.SupportsConflictUpdateWhere = false
opts.SupportsInsertIgnoreSyntax = true
opts.SupportsConflictTarget = true
opts.SupportsMultipleUpdateTables = false
opts.WrapCompoundsInParens = false
opts.SupportsDistinctOn = false
opts.SupportsWindowFunction = false
opts.SupportsLateral = false
opts.PlaceHolderFragment = []byte("?")
opts.IncludePlaceholderNum = false
opts.QuoteRune = '`'
opts.DefaultValuesFragment = []byte("")
opts.True = []byte("1")
opts.False = []byte("0")
opts.TimeFormat = time.RFC3339Nano
opts.BooleanOperatorLookup = map[exp.BooleanOperation][]byte{
exp.EqOp: []byte("="),
exp.NeqOp: []byte("!="),
exp.GtOp: []byte(">"),
exp.GteOp: []byte(">="),
exp.LtOp: []byte("<"),
exp.LteOp: []byte("<="),
exp.InOp: []byte("IN"),
exp.NotInOp: []byte("NOT IN"),
exp.IsOp: []byte("IS"),
exp.IsNotOp: []byte("IS NOT"),
exp.LikeOp: []byte("LIKE"),
exp.NotLikeOp: []byte("NOT LIKE"),
exp.ILikeOp: []byte("LIKE"),
exp.NotILikeOp: []byte("NOT LIKE"),
exp.RegexpLikeOp: []byte("REGEXP"),
exp.RegexpNotLikeOp: []byte("NOT REGEXP"),
exp.RegexpILikeOp: []byte("REGEXP"),
exp.RegexpNotILikeOp: []byte("NOT REGEXP"),
}
opts.UseLiteralIsBools = false
opts.BitwiseOperatorLookup = map[exp.BitwiseOperation][]byte{
exp.BitwiseOrOp: []byte("|"),
exp.BitwiseAndOp: []byte("&"),
exp.BitwiseLeftShiftOp: []byte("<<"),
exp.BitwiseRightShiftOp: []byte(">>"),
}
opts.EscapedRunes = map[rune][]byte{
'\'': []byte("''"),
}
opts.InsertIgnoreClause = []byte("INSERT OR IGNORE INTO ")
opts.ConflictFragment = []byte(" ON CONFLICT ")
opts.ConflictDoUpdateFragment = []byte(" DO UPDATE SET ")
opts.ConflictDoNothingFragment = []byte(" DO NOTHING ")
opts.ForUpdateFragment = []byte("")
opts.OfFragment = []byte("")
opts.NowaitFragment = []byte("")
return opts
}
func init() {
goqu.RegisterDialect("sqlite3", DialectOptions())
}

View File

@@ -0,0 +1,62 @@
version: "2"
services:
postgres:
image: "postgres:${POSTGRES_VERSION}"
environment:
- "POSTGRES_USER=postgres"
- "POSTGRES_DB=goqupostgres"
- "POSTGRES_HOST_AUTH_METHOD=trust"
expose:
- "5432"
ports:
- "5432:5432"
mysql:
image: "mysql:${MYSQL_VERSION}"
environment:
- "MYSQL_DATABASE=goqumysql"
- "MYSQL_ALLOW_EMPTY_PASSWORD=yes"
expose:
- "3306"
ports:
- "3306:3306"
sqlserver:
image: "mcr.microsoft.com/mssql/server:${SQLSERVER_VERSION}"
environment:
- "ACCEPT_EULA=Y"
- "SA_PASSWORD=qwe123QWE"
expose:
- "1433"
ports:
- "1433:1433"
goqu:
image: "golang:${GO_VERSION}"
command: ["./wait-for-it.sh", "postgres:5432", "--", "./wait-for-it.sh", "mysql:3306", "--", "go test -v -race ./..."]
working_dir: /go/src/github.com/doug-martin/goqu
volumes:
- "./:/go/src/github.com/doug-martin/goqu"
environment:
MYSQL_URI: 'root@tcp(mysql:3306)/goqumysql?parseTime=true'
PG_URI: 'postgres://postgres:@postgres:5432/goqupostgres?sslmode=disable'
SQLSERVER_URI: 'sqlserver://sa:qwe123QWE@sqlserver:1433?database=master&connection+timeout=30'
depends_on:
- postgres
- mysql
- sqlserver
goqu-coverage:
image: "golang:${GO_VERSION}"
command: ["./wait-for-it.sh", "postgres:5432", "--", "./wait-for-it.sh", "mysql:3306", "--", "./go.test.sh"]
working_dir: /go/src/github.com/doug-martin/goqu
volumes:
- "./:/go/src/github.com/doug-martin/goqu"
environment:
MYSQL_URI: 'root@tcp(mysql:3306)/goqumysql?parseTime=true'
PG_URI: 'postgres://postgres:@postgres:5432/goqupostgres?sslmode=disable'
SQLSERVER_URI: 'sqlserver://sa:qwe123QWE@sqlserver:1433?database=master&connection+timeout=30'
depends_on:
- postgres
- mysql
- sqlserver

View File

@@ -0,0 +1,247 @@
package exec
import (
"context"
gsql "database/sql"
"reflect"
"github.com/doug-martin/goqu/v9/internal/errors"
"github.com/doug-martin/goqu/v9/internal/util"
)
type (
QueryExecutor struct {
de DbExecutor
err error
query string
args []interface{}
}
)
var (
errUnsupportedScanStructType = errors.New("type must be a pointer to a struct when scanning into a struct")
errUnsupportedScanStructsType = errors.New("type must be a pointer to a slice when scanning into structs")
errUnsupportedScanValsType = errors.New("type must be a pointer to a slice when scanning into vals")
errScanValPointer = errors.New("type must be a pointer when scanning into val")
errScanValNonSlice = errors.New("type cannot be a pointer to a slice when scanning into val")
)
func newQueryExecutor(de DbExecutor, err error, query string, args ...interface{}) QueryExecutor {
return QueryExecutor{de: de, err: err, query: query, args: args}
}
func (q QueryExecutor) ToSQL() (sql string, args []interface{}, err error) {
return q.query, q.args, q.err
}
func (q QueryExecutor) Exec() (gsql.Result, error) {
return q.ExecContext(context.Background())
}
func (q QueryExecutor) ExecContext(ctx context.Context) (gsql.Result, error) {
if q.err != nil {
return nil, q.err
}
return q.de.ExecContext(ctx, q.query, q.args...)
}
func (q QueryExecutor) Query() (*gsql.Rows, error) {
return q.QueryContext(context.Background())
}
func (q QueryExecutor) QueryContext(ctx context.Context) (*gsql.Rows, error) {
if q.err != nil {
return nil, q.err
}
return q.de.QueryContext(ctx, q.query, q.args...)
}
// This will execute the SQL and append results to the slice
// var myStructs []MyStruct
// if err := db.From("test").ScanStructs(&myStructs); err != nil{
// panic(err.Error()
// }
// //use your structs
//
//
// i: A pointer to a slice of structs.
func (q QueryExecutor) ScanStructs(i interface{}) error {
return q.ScanStructsContext(context.Background(), i)
}
// This will execute the SQL and append results to the slice
// var myStructs []MyStruct
// if err := db.From("test").ScanStructsContext(ctx, &myStructs); err != nil{
// panic(err.Error()
// }
// //use your structs
//
//
// i: A pointer to a slice of structs.
func (q QueryExecutor) ScanStructsContext(ctx context.Context, i interface{}) error {
scanner, err := q.ScannerContext(ctx)
if err != nil {
return err
}
defer func() { _ = scanner.Close() }()
return scanner.ScanStructs(i)
}
// This will execute the SQL and fill out the struct with the fields returned.
// This method returns a boolean value that is false if no record was found
// var myStruct MyStruct
// found, err := db.From("test").Limit(1).ScanStruct(&myStruct)
// if err != nil{
// panic(err.Error()
// }
// if !found{
// fmt.Println("NOT FOUND")
// }
//
// i: A pointer to a struct
func (q QueryExecutor) ScanStruct(i interface{}) (bool, error) {
return q.ScanStructContext(context.Background(), i)
}
// This will execute the SQL and fill out the struct with the fields returned.
// This method returns a boolean value that is false if no record was found
// var myStruct MyStruct
// found, err := db.From("test").Limit(1).ScanStructContext(ctx, &myStruct)
// if err != nil{
// panic(err.Error()
// }
// if !found{
// fmt.Println("NOT FOUND")
// }
//
// i: A pointer to a struct
func (q QueryExecutor) ScanStructContext(ctx context.Context, i interface{}) (bool, error) {
val := reflect.ValueOf(i)
if !util.IsPointer(val.Kind()) {
return false, errUnsupportedScanStructType
}
val = reflect.Indirect(val)
if !util.IsStruct(val.Kind()) {
return false, errUnsupportedScanStructType
}
scanner, err := q.ScannerContext(ctx)
if err != nil {
return false, err
}
defer func() { _ = scanner.Close() }()
if scanner.Next() {
err = scanner.ScanStruct(i)
if err != nil {
return false, err
}
return true, scanner.Err()
}
return false, scanner.Err()
}
// This will execute the SQL and append results to the slice.
// var ids []uint32
// if err := db.From("test").Select("id").ScanVals(&ids); err != nil{
// panic(err.Error()
// }
//
// i: Takes a pointer to a slice of primitive values.
func (q QueryExecutor) ScanVals(i interface{}) error {
return q.ScanValsContext(context.Background(), i)
}
// This will execute the SQL and append results to the slice.
// var ids []uint32
// if err := db.From("test").Select("id").ScanValsContext(ctx, &ids); err != nil{
// panic(err.Error()
// }
//
// i: Takes a pointer to a slice of primitive values.
func (q QueryExecutor) ScanValsContext(ctx context.Context, i interface{}) error {
scanner, err := q.ScannerContext(ctx)
if err != nil {
return err
}
defer func() { _ = scanner.Close() }()
return scanner.ScanVals(i)
}
// This will execute the SQL and set the value of the primitive. This method will return false if no record is found.
// var id uint32
// found, err := db.From("test").Select("id").Limit(1).ScanVal(&id)
// if err != nil{
// panic(err.Error()
// }
// if !found{
// fmt.Println("NOT FOUND")
// }
//
// i: Takes a pointer to a primitive value.
func (q QueryExecutor) ScanVal(i interface{}) (bool, error) {
return q.ScanValContext(context.Background(), i)
}
// This will execute the SQL and set the value of the primitive. This method will return false if no record is found.
// var id uint32
// found, err := db.From("test").Select("id").Limit(1).ScanValContext(ctx, &id)
// if err != nil{
// panic(err.Error()
// }
// if !found{
// fmt.Println("NOT FOUND")
// }
//
// i: Takes a pointer to a primitive value.
func (q QueryExecutor) ScanValContext(ctx context.Context, i interface{}) (bool, error) {
val := reflect.ValueOf(i)
if !util.IsPointer(val.Kind()) {
return false, errScanValPointer
}
val = reflect.Indirect(val)
if util.IsSlice(val.Kind()) {
switch i.(type) {
case *gsql.RawBytes: // do nothing
case *[]byte: // do nothing
case gsql.Scanner: // do nothing
default:
return false, errScanValNonSlice
}
}
scanner, err := q.ScannerContext(ctx)
if err != nil {
return false, err
}
defer func() { _ = scanner.Close() }()
if scanner.Next() {
err = scanner.ScanVal(i)
if err != nil {
return false, err
}
return true, scanner.Err()
}
return false, scanner.Err()
}
// Scanner will return a Scanner that can be used for manually scanning rows.
func (q QueryExecutor) Scanner() (Scanner, error) {
return q.ScannerContext(context.Background())
}
// ScannerContext will return a Scanner that can be used for manually scanning rows.
func (q QueryExecutor) ScannerContext(ctx context.Context) (Scanner, error) {
rows, err := q.QueryContext(ctx)
if err != nil {
return nil, err
}
return NewScanner(rows), nil
}

View File

@@ -0,0 +1,36 @@
package exec
import (
"context"
"database/sql"
"github.com/doug-martin/goqu/v9/internal/sb"
)
type (
// nolint:stylecheck // keep name for backwards compatibility
DbExecutor interface {
ExecContext(ctx context.Context, query string, args ...interface{}) (sql.Result, error)
QueryContext(ctx context.Context, query string, args ...interface{}) (*sql.Rows, error)
}
QueryFactory interface {
FromSQL(sql string, args ...interface{}) QueryExecutor
FromSQLBuilder(b sb.SQLBuilder) QueryExecutor
}
querySupport struct {
de DbExecutor
}
)
func NewQueryFactory(de DbExecutor) QueryFactory {
return &querySupport{de}
}
func (qs *querySupport) FromSQL(query string, args ...interface{}) QueryExecutor {
return newQueryExecutor(qs.de, nil, query, args...)
}
func (qs *querySupport) FromSQLBuilder(b sb.SQLBuilder) QueryExecutor {
query, args, err := b.ToSQL()
return newQueryExecutor(qs.de, err, query, args...)
}

168
vendor/github.com/doug-martin/goqu/v9/exec/scanner.go generated vendored Normal file
View File

@@ -0,0 +1,168 @@
package exec
import (
"database/sql"
"reflect"
"github.com/doug-martin/goqu/v9/exp"
"github.com/doug-martin/goqu/v9/internal/errors"
"github.com/doug-martin/goqu/v9/internal/util"
)
type (
// Scanner knows how to scan sql.Rows into structs.
Scanner interface {
Next() bool
ScanStruct(i interface{}) error
ScanStructs(i interface{}) error
ScanVal(i interface{}) error
ScanVals(i interface{}) error
Close() error
Err() error
}
scanner struct {
rows *sql.Rows
columnMap util.ColumnMap
columns []string
}
)
func unableToFindFieldError(col string) error {
return errors.New(`unable to find corresponding field to column "%s" returned by query`, col)
}
// NewScanner returns a scanner that can be used for scanning rows into structs.
func NewScanner(rows *sql.Rows) Scanner {
return &scanner{rows: rows}
}
// Next prepares the next row for Scanning. See sql.Rows#Next for more
// information.
func (s *scanner) Next() bool {
return s.rows.Next()
}
// Err returns the error, if any that was encountered during iteration. See
// sql.Rows#Err for more information.
func (s *scanner) Err() error {
return s.rows.Err()
}
// ScanStruct will scan the current row into i.
func (s *scanner) ScanStruct(i interface{}) error {
// Setup columnMap and columns, but only once.
if s.columnMap == nil || s.columns == nil {
cm, err := util.GetColumnMap(i)
if err != nil {
return err
}
cols, err := s.rows.Columns()
if err != nil {
return err
}
s.columnMap = cm
s.columns = cols
}
scans := make([]interface{}, 0, len(s.columns))
for _, col := range s.columns {
data, ok := s.columnMap[col]
switch {
case !ok:
return unableToFindFieldError(col)
default:
scans = append(scans, reflect.New(data.GoType).Interface())
}
}
if err := s.rows.Scan(scans...); err != nil {
return err
}
record := exp.Record{}
for index, col := range s.columns {
record[col] = scans[index]
}
util.AssignStructVals(i, record, s.columnMap)
return s.Err()
}
// ScanStructs scans results in slice of structs
func (s *scanner) ScanStructs(i interface{}) error {
val, err := checkScanStructsTarget(i)
if err != nil {
return err
}
return s.scanIntoSlice(val, func(i interface{}) error {
return s.ScanStruct(i)
})
}
// ScanVal will scan the current row and column into i.
func (s *scanner) ScanVal(i interface{}) error {
if err := s.rows.Scan(i); err != nil {
return err
}
return s.Err()
}
// ScanStructs scans results in slice of values
func (s *scanner) ScanVals(i interface{}) error {
val, err := checkScanValsTarget(i)
if err != nil {
return err
}
return s.scanIntoSlice(val, func(i interface{}) error {
return s.ScanVal(i)
})
}
// Close closes the Rows, preventing further enumeration. See sql.Rows#Close
// for more info.
func (s *scanner) Close() error {
return s.rows.Close()
}
func (s *scanner) scanIntoSlice(val reflect.Value, it func(i interface{}) error) error {
elemType := util.GetSliceElementType(val)
for s.Next() {
row := reflect.New(elemType)
if rowErr := it(row.Interface()); rowErr != nil {
return rowErr
}
util.AppendSliceElement(val, row)
}
return s.Err()
}
func checkScanStructsTarget(i interface{}) (reflect.Value, error) {
val := reflect.ValueOf(i)
if !util.IsPointer(val.Kind()) {
return val, errUnsupportedScanStructsType
}
val = reflect.Indirect(val)
if !util.IsSlice(val.Kind()) {
return val, errUnsupportedScanStructsType
}
return val, nil
}
func checkScanValsTarget(i interface{}) (reflect.Value, error) {
val := reflect.ValueOf(i)
if !util.IsPointer(val.Kind()) {
return val, errUnsupportedScanValsType
}
val = reflect.Indirect(val)
if !util.IsSlice(val.Kind()) {
return val, errUnsupportedScanValsType
}
return val, nil
}

59
vendor/github.com/doug-martin/goqu/v9/exp/alias.go generated vendored Normal file
View File

@@ -0,0 +1,59 @@
package exp
import "fmt"
type (
aliasExpression struct {
aliased Expression
alias IdentifierExpression
}
)
// Creates a new AliasedExpression for the Expression and alias
func NewAliasExpression(exp Expression, alias interface{}) AliasedExpression {
switch v := alias.(type) {
case string:
return aliasExpression{aliased: exp, alias: ParseIdentifier(v)}
case IdentifierExpression:
return aliasExpression{aliased: exp, alias: v}
default:
panic(fmt.Sprintf("Cannot create alias from %+v", v))
}
}
func (ae aliasExpression) Clone() Expression {
return NewAliasExpression(ae.aliased, ae.alias.Clone())
}
func (ae aliasExpression) Expression() Expression {
return ae
}
func (ae aliasExpression) Aliased() Expression {
return ae.aliased
}
func (ae aliasExpression) GetAs() IdentifierExpression {
return ae.alias
}
// Returns a new IdentifierExpression with the specified schema
func (ae aliasExpression) Schema(schema string) IdentifierExpression {
return ae.alias.Schema(schema)
}
// Returns a new IdentifierExpression with the specified table
func (ae aliasExpression) Table(table string) IdentifierExpression {
return ae.alias.Table(table)
}
// Returns a new IdentifierExpression with the specified column
func (ae aliasExpression) Col(col interface{}) IdentifierExpression {
return ae.alias.Col(col)
}
// Returns a new IdentifierExpression with the column set to *
// I("my_table").As("t").All() //"t".*
func (ae aliasExpression) All() IdentifierExpression {
return ae.alias.All()
}

89
vendor/github.com/doug-martin/goqu/v9/exp/bitwise.go generated vendored Normal file
View File

@@ -0,0 +1,89 @@
package exp
type bitwise struct {
lhs Expression
rhs interface{}
op BitwiseOperation
}
func NewBitwiseExpression(op BitwiseOperation, lhs Expression, rhs interface{}) BitwiseExpression {
return bitwise{op: op, lhs: lhs, rhs: rhs}
}
func (b bitwise) Clone() Expression {
return NewBitwiseExpression(b.op, b.lhs.Clone(), b.rhs)
}
func (b bitwise) RHS() interface{} {
return b.rhs
}
func (b bitwise) LHS() Expression {
return b.lhs
}
func (b bitwise) Op() BitwiseOperation {
return b.op
}
func (b bitwise) Expression() Expression { return b }
func (b bitwise) As(val interface{}) AliasedExpression { return NewAliasExpression(b, val) }
func (b bitwise) Eq(val interface{}) BooleanExpression { return eq(b, val) }
func (b bitwise) Neq(val interface{}) BooleanExpression { return neq(b, val) }
func (b bitwise) Gt(val interface{}) BooleanExpression { return gt(b, val) }
func (b bitwise) Gte(val interface{}) BooleanExpression { return gte(b, val) }
func (b bitwise) Lt(val interface{}) BooleanExpression { return lt(b, val) }
func (b bitwise) Lte(val interface{}) BooleanExpression { return lte(b, val) }
func (b bitwise) Asc() OrderedExpression { return asc(b) }
func (b bitwise) Desc() OrderedExpression { return desc(b) }
func (b bitwise) Like(i interface{}) BooleanExpression { return like(b, i) }
func (b bitwise) NotLike(i interface{}) BooleanExpression { return notLike(b, i) }
func (b bitwise) ILike(i interface{}) BooleanExpression { return iLike(b, i) }
func (b bitwise) NotILike(i interface{}) BooleanExpression { return notILike(b, i) }
func (b bitwise) RegexpLike(val interface{}) BooleanExpression { return regexpLike(b, val) }
func (b bitwise) RegexpNotLike(val interface{}) BooleanExpression { return regexpNotLike(b, val) }
func (b bitwise) RegexpILike(val interface{}) BooleanExpression { return regexpILike(b, val) }
func (b bitwise) RegexpNotILike(val interface{}) BooleanExpression { return regexpNotILike(b, val) }
func (b bitwise) In(i ...interface{}) BooleanExpression { return in(b, i...) }
func (b bitwise) NotIn(i ...interface{}) BooleanExpression { return notIn(b, i...) }
func (b bitwise) Is(i interface{}) BooleanExpression { return is(b, i) }
func (b bitwise) IsNot(i interface{}) BooleanExpression { return isNot(b, i) }
func (b bitwise) IsNull() BooleanExpression { return is(b, nil) }
func (b bitwise) IsNotNull() BooleanExpression { return isNot(b, nil) }
func (b bitwise) IsTrue() BooleanExpression { return is(b, true) }
func (b bitwise) IsNotTrue() BooleanExpression { return isNot(b, true) }
func (b bitwise) IsFalse() BooleanExpression { return is(b, false) }
func (b bitwise) IsNotFalse() BooleanExpression { return isNot(b, false) }
func (b bitwise) Distinct() SQLFunctionExpression { return NewSQLFunctionExpression("DISTINCT", b) }
func (b bitwise) Between(val RangeVal) RangeExpression { return between(b, val) }
func (b bitwise) NotBetween(val RangeVal) RangeExpression { return notBetween(b, val) }
// used internally to create a Bitwise Inversion BitwiseExpression
func bitwiseInversion(rhs Expression) BitwiseExpression {
return NewBitwiseExpression(BitwiseInversionOp, nil, rhs)
}
// used internally to create a Bitwise OR BitwiseExpression
func bitwiseOr(lhs Expression, rhs interface{}) BitwiseExpression {
return NewBitwiseExpression(BitwiseOrOp, lhs, rhs)
}
// used internally to create a Bitwise AND BitwiseExpression
func bitwiseAnd(lhs Expression, rhs interface{}) BitwiseExpression {
return NewBitwiseExpression(BitwiseAndOp, lhs, rhs)
}
// used internally to create a Bitwise XOR BitwiseExpression
func bitwiseXor(lhs Expression, rhs interface{}) BitwiseExpression {
return NewBitwiseExpression(BitwiseXorOp, lhs, rhs)
}
// used internally to create a Bitwise LEFT SHIFT BitwiseExpression
func bitwiseLeftShift(lhs Expression, rhs interface{}) BitwiseExpression {
return NewBitwiseExpression(BitwiseLeftShiftOp, lhs, rhs)
}
// used internally to create a Bitwise RIGHT SHIFT BitwiseExpression
func bitwiseRightShift(lhs Expression, rhs interface{}) BitwiseExpression {
return NewBitwiseExpression(BitwiseRightShiftOp, lhs, rhs)
}

185
vendor/github.com/doug-martin/goqu/v9/exp/bool.go generated vendored Normal file
View File

@@ -0,0 +1,185 @@
package exp
import (
"reflect"
"regexp"
)
type boolean struct {
lhs Expression
rhs interface{}
op BooleanOperation
}
func NewBooleanExpression(op BooleanOperation, lhs Expression, rhs interface{}) BooleanExpression {
return boolean{op: op, lhs: lhs, rhs: rhs}
}
func (b boolean) Clone() Expression {
return NewBooleanExpression(b.op, b.lhs.Clone(), b.rhs)
}
func (b boolean) Expression() Expression {
return b
}
func (b boolean) RHS() interface{} {
return b.rhs
}
func (b boolean) LHS() Expression {
return b.lhs
}
func (b boolean) Op() BooleanOperation {
return b.op
}
func (b boolean) As(val interface{}) AliasedExpression {
return NewAliasExpression(b, val)
}
// used internally to create an equality BooleanExpression
func eq(lhs Expression, rhs interface{}) BooleanExpression {
return checkBoolExpType(EqOp, lhs, rhs, false)
}
// used internally to create an in-equality BooleanExpression
func neq(lhs Expression, rhs interface{}) BooleanExpression {
return checkBoolExpType(EqOp, lhs, rhs, true)
}
// used internally to create an gt comparison BooleanExpression
func gt(lhs Expression, rhs interface{}) BooleanExpression {
return NewBooleanExpression(GtOp, lhs, rhs)
}
// used internally to create an gte comparison BooleanExpression
func gte(lhs Expression, rhs interface{}) BooleanExpression {
return NewBooleanExpression(GteOp, lhs, rhs)
}
// used internally to create an lt comparison BooleanExpression
func lt(lhs Expression, rhs interface{}) BooleanExpression {
return NewBooleanExpression(LtOp, lhs, rhs)
}
// used internally to create an lte comparison BooleanExpression
func lte(lhs Expression, rhs interface{}) BooleanExpression {
return NewBooleanExpression(LteOp, lhs, rhs)
}
// used internally to create an IN BooleanExpression
func in(lhs Expression, vals ...interface{}) BooleanExpression {
if len(vals) == 1 && reflect.Indirect(reflect.ValueOf(vals[0])).Kind() == reflect.Slice {
return NewBooleanExpression(InOp, lhs, vals[0])
}
return NewBooleanExpression(InOp, lhs, vals)
}
// used internally to create a NOT IN BooleanExpression
func notIn(lhs Expression, vals ...interface{}) BooleanExpression {
if len(vals) == 1 && reflect.Indirect(reflect.ValueOf(vals[0])).Kind() == reflect.Slice {
return NewBooleanExpression(NotInOp, lhs, vals[0])
}
return NewBooleanExpression(NotInOp, lhs, vals)
}
// used internally to create an IS BooleanExpression
func is(lhs Expression, val interface{}) BooleanExpression {
return checkBoolExpType(IsOp, lhs, val, false)
}
// used internally to create an IS NOT BooleanExpression
func isNot(lhs Expression, val interface{}) BooleanExpression {
return checkBoolExpType(IsOp, lhs, val, true)
}
// used internally to create a LIKE BooleanExpression
func like(lhs Expression, val interface{}) BooleanExpression {
return checkLikeExp(LikeOp, lhs, val, false)
}
// used internally to create an ILIKE BooleanExpression
func iLike(lhs Expression, val interface{}) BooleanExpression {
return checkLikeExp(ILikeOp, lhs, val, false)
}
// used internally to create a NOT LIKE BooleanExpression
func notLike(lhs Expression, val interface{}) BooleanExpression {
return checkLikeExp(LikeOp, lhs, val, true)
}
// used internally to create a NOT ILIKE BooleanExpression
func notILike(lhs Expression, val interface{}) BooleanExpression {
return checkLikeExp(ILikeOp, lhs, val, true)
}
// used internally to create a LIKE BooleanExpression
func regexpLike(lhs Expression, val interface{}) BooleanExpression {
return checkLikeExp(RegexpLikeOp, lhs, val, false)
}
// used internally to create an ILIKE BooleanExpression
func regexpILike(lhs Expression, val interface{}) BooleanExpression {
return checkLikeExp(RegexpILikeOp, lhs, val, false)
}
// used internally to create a NOT LIKE BooleanExpression
func regexpNotLike(lhs Expression, val interface{}) BooleanExpression {
return checkLikeExp(RegexpLikeOp, lhs, val, true)
}
// used internally to create a NOT ILIKE BooleanExpression
func regexpNotILike(lhs Expression, val interface{}) BooleanExpression {
return checkLikeExp(RegexpILikeOp, lhs, val, true)
}
// checks an like rhs to create the proper like expression for strings or regexps
func checkLikeExp(op BooleanOperation, lhs Expression, val interface{}, invert bool) BooleanExpression {
rhs := val
if t, ok := val.(*regexp.Regexp); ok {
if op == LikeOp {
op = RegexpLikeOp
} else if op == ILikeOp {
op = RegexpILikeOp
}
rhs = t.String()
}
if invert {
op = operatorInversions[op]
}
return NewBooleanExpression(op, lhs, rhs)
}
// checks a boolean operation normalizing the operation based on the RHS (e.g. "a" = true vs "a" IS TRUE
func checkBoolExpType(op BooleanOperation, lhs Expression, rhs interface{}, invert bool) BooleanExpression {
if rhs == nil {
op = IsOp
} else {
switch reflect.Indirect(reflect.ValueOf(rhs)).Kind() {
case reflect.Bool:
op = IsOp
case reflect.Slice:
// if its a slice of bytes dont treat as an IN
if _, ok := rhs.([]byte); !ok {
op = InOp
}
case reflect.Struct:
switch rhs.(type) {
case SQLExpression:
op = InOp
case AppendableExpression:
op = InOp
case *regexp.Regexp:
return checkLikeExp(LikeOp, lhs, rhs, invert)
}
default:
}
}
if invert {
op = operatorInversions[op]
}
return NewBooleanExpression(op, lhs, rhs)
}

78
vendor/github.com/doug-martin/goqu/v9/exp/case.go generated vendored Normal file
View File

@@ -0,0 +1,78 @@
package exp
type (
caseElse struct {
result interface{}
}
caseWhen struct {
caseElse
condition interface{}
}
caseExpression struct {
value interface{}
whens []CaseWhen
elseCondition CaseElse
}
)
func NewCaseElse(result interface{}) CaseElse {
return caseElse{result: result}
}
func (ce caseElse) Result() interface{} {
return ce.result
}
func NewCaseWhen(condition, result interface{}) CaseWhen {
return caseWhen{caseElse: caseElse{result: result}, condition: condition}
}
func (cw caseWhen) Condition() interface{} {
return cw.condition
}
func NewCaseExpression() CaseExpression {
return caseExpression{value: nil, whens: []CaseWhen{}, elseCondition: nil}
}
func (c caseExpression) Expression() Expression {
return c
}
func (c caseExpression) Clone() Expression {
return caseExpression{value: c.value, whens: c.whens, elseCondition: c.elseCondition}
}
func (c caseExpression) As(alias interface{}) AliasedExpression {
return NewAliasExpression(c, alias)
}
func (c caseExpression) GetValue() interface{} {
return c.value
}
func (c caseExpression) GetWhens() []CaseWhen {
return c.whens
}
func (c caseExpression) GetElse() CaseElse {
return c.elseCondition
}
func (c caseExpression) Value(value interface{}) CaseExpression {
c.value = value
return c
}
func (c caseExpression) When(condition, result interface{}) CaseExpression {
c.whens = append(c.whens, NewCaseWhen(condition, result))
return c
}
func (c caseExpression) Else(result interface{}) CaseExpression {
c.elseCondition = NewCaseElse(result)
return c
}
func (c caseExpression) Asc() OrderedExpression { return asc(c) }
func (c caseExpression) Desc() OrderedExpression { return desc(c) }

56
vendor/github.com/doug-martin/goqu/v9/exp/cast.go generated vendored Normal file
View File

@@ -0,0 +1,56 @@
package exp
type cast struct {
casted Expression
t LiteralExpression
}
// Creates a new Casted expression
// Cast(I("a"), "NUMERIC") -> CAST("a" AS NUMERIC)
func NewCastExpression(e Expression, t string) CastExpression {
return cast{casted: e, t: NewLiteralExpression(t)}
}
func (c cast) Casted() Expression {
return c.casted
}
func (c cast) Type() LiteralExpression {
return c.t
}
func (c cast) Clone() Expression {
return cast{casted: c.casted.Clone(), t: c.t}
}
func (c cast) Expression() Expression { return c }
func (c cast) As(val interface{}) AliasedExpression { return NewAliasExpression(c, val) }
func (c cast) Eq(val interface{}) BooleanExpression { return eq(c, val) }
func (c cast) Neq(val interface{}) BooleanExpression { return neq(c, val) }
func (c cast) Gt(val interface{}) BooleanExpression { return gt(c, val) }
func (c cast) Gte(val interface{}) BooleanExpression { return gte(c, val) }
func (c cast) Lt(val interface{}) BooleanExpression { return lt(c, val) }
func (c cast) Lte(val interface{}) BooleanExpression { return lte(c, val) }
func (c cast) Asc() OrderedExpression { return asc(c) }
func (c cast) Desc() OrderedExpression { return desc(c) }
func (c cast) Like(i interface{}) BooleanExpression { return like(c, i) }
func (c cast) NotLike(i interface{}) BooleanExpression { return notLike(c, i) }
func (c cast) ILike(i interface{}) BooleanExpression { return iLike(c, i) }
func (c cast) NotILike(i interface{}) BooleanExpression { return notILike(c, i) }
func (c cast) RegexpLike(val interface{}) BooleanExpression { return regexpLike(c, val) }
func (c cast) RegexpNotLike(val interface{}) BooleanExpression { return regexpNotLike(c, val) }
func (c cast) RegexpILike(val interface{}) BooleanExpression { return regexpILike(c, val) }
func (c cast) RegexpNotILike(val interface{}) BooleanExpression { return regexpNotILike(c, val) }
func (c cast) In(i ...interface{}) BooleanExpression { return in(c, i...) }
func (c cast) NotIn(i ...interface{}) BooleanExpression { return notIn(c, i...) }
func (c cast) Is(i interface{}) BooleanExpression { return is(c, i) }
func (c cast) IsNot(i interface{}) BooleanExpression { return isNot(c, i) }
func (c cast) IsNull() BooleanExpression { return is(c, nil) }
func (c cast) IsNotNull() BooleanExpression { return isNot(c, nil) }
func (c cast) IsTrue() BooleanExpression { return is(c, true) }
func (c cast) IsNotTrue() BooleanExpression { return isNot(c, true) }
func (c cast) IsFalse() BooleanExpression { return is(c, false) }
func (c cast) IsNotFalse() BooleanExpression { return isNot(c, false) }
func (c cast) Distinct() SQLFunctionExpression { return NewSQLFunctionExpression("DISTINCT", c) }
func (c cast) Between(val RangeVal) RangeExpression { return between(c, val) }
func (c cast) NotBetween(val RangeVal) RangeExpression { return notBetween(c, val) }

84
vendor/github.com/doug-martin/goqu/v9/exp/col.go generated vendored Normal file
View File

@@ -0,0 +1,84 @@
package exp
import (
"fmt"
"reflect"
"github.com/doug-martin/goqu/v9/internal/util"
)
type columnList struct {
columns []Expression
}
func NewColumnListExpression(vals ...interface{}) ColumnListExpression {
cols := []Expression{}
for _, val := range vals {
switch t := val.(type) {
case nil: // do nothing
case string:
cols = append(cols, ParseIdentifier(t))
case ColumnListExpression:
cols = append(cols, t.Columns()...)
case Expression:
cols = append(cols, t)
default:
_, valKind := util.GetTypeInfo(val, reflect.Indirect(reflect.ValueOf(val)))
if valKind == reflect.Struct {
cm, err := util.GetColumnMap(val)
if err != nil {
panic(err.Error())
}
structCols := cm.Cols()
for _, col := range structCols {
i := ParseIdentifier(col)
var sc Expression = i
if i.IsQualified() {
sc = i.As(NewIdentifierExpression("", "", col))
}
cols = append(cols, sc)
}
} else {
panic(fmt.Sprintf("Cannot created expression from %+v", val))
}
}
}
return columnList{columns: cols}
}
func NewOrderedColumnList(vals ...OrderedExpression) ColumnListExpression {
exps := make([]interface{}, 0, len(vals))
for _, col := range vals {
exps = append(exps, col.Expression())
}
return NewColumnListExpression(exps...)
}
func (cl columnList) Clone() Expression {
newExps := make([]Expression, 0, len(cl.columns))
for _, exp := range cl.columns {
newExps = append(newExps, exp.Clone())
}
return columnList{columns: newExps}
}
func (cl columnList) Expression() Expression {
return cl
}
func (cl columnList) IsEmpty() bool {
return len(cl.columns) == 0
}
func (cl columnList) Columns() []Expression {
return cl.columns
}
func (cl columnList) Append(cols ...Expression) ColumnListExpression {
ret := columnList{}
exps := append(ret.columns, cl.columns...)
exps = append(exps, cols...)
ret.columns = exps
return ret
}

19
vendor/github.com/doug-martin/goqu/v9/exp/compound.go generated vendored Normal file
View File

@@ -0,0 +1,19 @@
package exp
type compound struct {
t CompoundType
rhs AppendableExpression
}
func NewCompoundExpression(ct CompoundType, rhs AppendableExpression) CompoundExpression {
return compound{t: ct, rhs: rhs}
}
func (c compound) Expression() Expression { return c }
func (c compound) Clone() Expression {
return compound{t: c.t, rhs: c.rhs.Clone().(AppendableExpression)}
}
func (c compound) Type() CompoundType { return c.t }
func (c compound) RHS() AppendableExpression { return c.rhs }

86
vendor/github.com/doug-martin/goqu/v9/exp/conflict.go generated vendored Normal file
View File

@@ -0,0 +1,86 @@
package exp
type (
doNothingConflict struct{}
// ConflictUpdate is the struct that represents the UPDATE fragment of an
// INSERT ... ON CONFLICT/ON DUPLICATE KEY DO UPDATE statement
conflictUpdate struct {
target string
update interface{}
whereClause ExpressionList
}
)
// Creates a conflict struct to be passed to InsertConflict to ignore constraint errors
// InsertConflict(DoNothing(),...) -> INSERT INTO ... ON CONFLICT DO NOTHING
func NewDoNothingConflictExpression() ConflictExpression {
return &doNothingConflict{}
}
func (c doNothingConflict) Expression() Expression {
return c
}
func (c doNothingConflict) Clone() Expression {
return c
}
func (c doNothingConflict) Action() ConflictAction {
return DoNothingConflictAction
}
// Creates a ConflictUpdate struct to be passed to InsertConflict
// Represents a ON CONFLICT DO UPDATE portion of an INSERT statement (ON DUPLICATE KEY UPDATE for mysql)
//
// InsertConflict(DoUpdate("target_column", update),...) ->
// INSERT INTO ... ON CONFLICT DO UPDATE SET a=b
// InsertConflict(DoUpdate("target_column", update).Where(Ex{"a": 1},...) ->
// INSERT INTO ... ON CONFLICT DO UPDATE SET a=b WHERE a=1
func NewDoUpdateConflictExpression(target string, update interface{}) ConflictUpdateExpression {
return &conflictUpdate{target: target, update: update}
}
func (c conflictUpdate) Expression() Expression {
return c
}
func (c conflictUpdate) Clone() Expression {
return &conflictUpdate{
target: c.target,
update: c.update,
whereClause: c.whereClause.Clone().(ExpressionList),
}
}
func (c conflictUpdate) Action() ConflictAction {
return DoUpdateConflictAction
}
// Returns the target conflict column. Only necessary for Postgres.
// Will return an error for mysql/sqlite. Will also return an error if missing from a postgres ConflictUpdate.
func (c conflictUpdate) TargetColumn() string {
return c.target
}
// Returns the Updates which represent the ON CONFLICT DO UPDATE portion of an insert statement. If nil,
// there are no updates.
func (c conflictUpdate) Update() interface{} {
return c.update
}
// Append to the existing Where clause for an ON CONFLICT DO UPDATE ... WHERE ...
// InsertConflict(DoNothing(),...) -> INSERT INTO ... ON CONFLICT DO NOTHING
func (c *conflictUpdate) Where(expressions ...Expression) ConflictUpdateExpression {
if c.whereClause == nil {
c.whereClause = NewExpressionList(AndType, expressions...)
} else {
c.whereClause = c.whereClause.Append(expressions...)
}
return c
}
// Append to the existing Where clause for an ON CONFLICT DO UPDATE ... WHERE ...
// InsertConflict(DoNothing(),...) -> INSERT INTO ... ON CONFLICT DO NOTHING
func (c *conflictUpdate) WhereClause() ExpressionList {
return c.whereClause
}

23
vendor/github.com/doug-martin/goqu/v9/exp/cte.go generated vendored Normal file
View File

@@ -0,0 +1,23 @@
package exp
type commonExpr struct {
recursive bool
name LiteralExpression
subQuery Expression
}
// Creates a new WITH common table expression for a SQLExpression, typically Datasets'. This function is used
// internally by Dataset when a CTE is added to another Dataset
func NewCommonTableExpression(recursive bool, name string, subQuery Expression) CommonTableExpression {
return commonExpr{recursive: recursive, name: NewLiteralExpression(name), subQuery: subQuery}
}
func (ce commonExpr) Expression() Expression { return ce }
func (ce commonExpr) Clone() Expression {
return commonExpr{recursive: ce.recursive, name: ce.name, subQuery: ce.subQuery.Clone().(SQLExpression)}
}
func (ce commonExpr) IsRecursive() bool { return ce.recursive }
func (ce commonExpr) Name() LiteralExpression { return ce.name }
func (ce commonExpr) SubQuery() Expression { return ce.subQuery }

View File

@@ -0,0 +1,177 @@
package exp
type (
DeleteClauses interface {
HasFrom() bool
clone() *deleteClauses
CommonTables() []CommonTableExpression
CommonTablesAppend(cte CommonTableExpression) DeleteClauses
From() IdentifierExpression
SetFrom(table IdentifierExpression) DeleteClauses
Where() ExpressionList
ClearWhere() DeleteClauses
WhereAppend(expressions ...Expression) DeleteClauses
Order() ColumnListExpression
HasOrder() bool
ClearOrder() DeleteClauses
SetOrder(oes ...OrderedExpression) DeleteClauses
OrderAppend(...OrderedExpression) DeleteClauses
OrderPrepend(...OrderedExpression) DeleteClauses
Limit() interface{}
HasLimit() bool
ClearLimit() DeleteClauses
SetLimit(limit interface{}) DeleteClauses
Returning() ColumnListExpression
HasReturning() bool
SetReturning(cl ColumnListExpression) DeleteClauses
}
deleteClauses struct {
commonTables []CommonTableExpression
from IdentifierExpression
where ExpressionList
order ColumnListExpression
limit interface{}
returning ColumnListExpression
}
)
func NewDeleteClauses() DeleteClauses {
return &deleteClauses{}
}
func (dc *deleteClauses) HasFrom() bool {
return dc.from != nil
}
func (dc *deleteClauses) clone() *deleteClauses {
return &deleteClauses{
commonTables: dc.commonTables,
from: dc.from,
where: dc.where,
order: dc.order,
limit: dc.limit,
returning: dc.returning,
}
}
func (dc *deleteClauses) CommonTables() []CommonTableExpression {
return dc.commonTables
}
func (dc *deleteClauses) CommonTablesAppend(cte CommonTableExpression) DeleteClauses {
ret := dc.clone()
ret.commonTables = append(ret.commonTables, cte)
return ret
}
func (dc *deleteClauses) From() IdentifierExpression {
return dc.from
}
func (dc *deleteClauses) SetFrom(table IdentifierExpression) DeleteClauses {
ret := dc.clone()
ret.from = table
return ret
}
func (dc *deleteClauses) Where() ExpressionList {
return dc.where
}
func (dc *deleteClauses) ClearWhere() DeleteClauses {
ret := dc.clone()
ret.where = nil
return ret
}
func (dc *deleteClauses) WhereAppend(expressions ...Expression) DeleteClauses {
if len(expressions) == 0 {
return dc
}
ret := dc.clone()
if ret.where == nil {
ret.where = NewExpressionList(AndType, expressions...)
} else {
ret.where = ret.where.Append(expressions...)
}
return ret
}
func (dc *deleteClauses) Order() ColumnListExpression {
return dc.order
}
func (dc *deleteClauses) HasOrder() bool {
return dc.order != nil
}
func (dc *deleteClauses) ClearOrder() DeleteClauses {
ret := dc.clone()
ret.order = nil
return ret
}
func (dc *deleteClauses) SetOrder(oes ...OrderedExpression) DeleteClauses {
ret := dc.clone()
ret.order = NewOrderedColumnList(oes...)
return ret
}
func (dc *deleteClauses) OrderAppend(oes ...OrderedExpression) DeleteClauses {
if dc.order == nil {
return dc.SetOrder(oes...)
}
ret := dc.clone()
ret.order = ret.order.Append(NewOrderedColumnList(oes...).Columns()...)
return ret
}
func (dc *deleteClauses) OrderPrepend(oes ...OrderedExpression) DeleteClauses {
if dc.order == nil {
return dc.SetOrder(oes...)
}
ret := dc.clone()
ret.order = NewOrderedColumnList(oes...).Append(ret.order.Columns()...)
return ret
}
func (dc *deleteClauses) Limit() interface{} {
return dc.limit
}
func (dc *deleteClauses) HasLimit() bool {
return dc.limit != nil
}
func (dc *deleteClauses) ClearLimit() DeleteClauses {
ret := dc.clone()
ret.limit = nil
return ret
}
func (dc *deleteClauses) SetLimit(limit interface{}) DeleteClauses {
ret := dc.clone()
ret.limit = limit
return ret
}
func (dc *deleteClauses) Returning() ColumnListExpression {
return dc.returning
}
func (dc *deleteClauses) HasReturning() bool {
return dc.returning != nil && !dc.returning.IsEmpty()
}
func (dc *deleteClauses) SetReturning(cl ColumnListExpression) DeleteClauses {
ret := dc.clone()
ret.returning = cl
return ret
}

734
vendor/github.com/doug-martin/goqu/v9/exp/exp.go generated vendored Normal file
View File

@@ -0,0 +1,734 @@
package exp
import (
"fmt"
"github.com/doug-martin/goqu/v9/internal/sb"
)
// Behaviors
type (
// Interface that an expression should implement if it can be aliased.
Aliaseable interface {
// Returns an AliasedExpression
// I("col").As("other_col") //"col" AS "other_col"
// I("col").As(I("other_col")) //"col" AS "other_col"
As(interface{}) AliasedExpression
}
// Interface that an expression should implement if it can be casted to another SQL type .
Castable interface {
// Casts an expression to the specified type
// I("a").Cast("numeric")//CAST("a" AS numeric)
Cast(val string) CastExpression
}
Inable interface {
// Creates a Boolean expression for IN clauses
// I("col").In([]string{"a", "b", "c"}) //("col" IN ('a', 'b', 'c'))
In(...interface{}) BooleanExpression
// Creates a Boolean expression for NOT IN clauses
// I("col").NotIn([]string{"a", "b", "c"}) //("col" NOT IN ('a', 'b', 'c'))
NotIn(...interface{}) BooleanExpression
}
Isable interface {
// Creates an Boolean expression IS clauses
// ds.Where(I("a").Is(nil)) //("a" IS NULL)
// ds.Where(I("a").Is(true)) //("a" IS TRUE)
// ds.Where(I("a").Is(false)) //("a" IS FALSE)
Is(interface{}) BooleanExpression
// Creates an Boolean expression IS NOT clauses
// ds.Where(I("a").IsNot(nil)) //("a" IS NOT NULL)
// ds.Where(I("a").IsNot(true)) //("a" IS NOT TRUE)
// ds.Where(I("a").IsNot(false)) //("a" IS NOT FALSE)
IsNot(interface{}) BooleanExpression
// Shortcut for Is(nil)
IsNull() BooleanExpression
// Shortcut for IsNot(nil)
IsNotNull() BooleanExpression
// Shortcut for Is(true)
IsTrue() BooleanExpression
// Shortcut for IsNot(true)
IsNotTrue() BooleanExpression
// Shortcut for Is(false)
IsFalse() BooleanExpression
// Shortcut for IsNot(false)
IsNotFalse() BooleanExpression
}
Likeable interface {
// Creates an Boolean expression for LIKE clauses
// ds.Where(I("a").Like("a%")) //("a" LIKE 'a%')
Like(interface{}) BooleanExpression
// Creates an Boolean expression for NOT LIKE clauses
// ds.Where(I("a").NotLike("a%")) //("a" NOT LIKE 'a%')
NotLike(interface{}) BooleanExpression
// Creates an Boolean expression for case insensitive LIKE clauses
// ds.Where(I("a").ILike("a%")) //("a" ILIKE 'a%')
ILike(interface{}) BooleanExpression
// Creates an Boolean expression for case insensitive NOT LIKE clauses
// ds.Where(I("a").NotILike("a%")) //("a" NOT ILIKE 'a%')
NotILike(interface{}) BooleanExpression
// Creates an Boolean expression for REGEXP LIKE clauses
// ds.Where(I("a").RegexpLike("a%")) //("a" ~ 'a%')
RegexpLike(interface{}) BooleanExpression
// Creates an Boolean expression for REGEXP NOT LIKE clauses
// ds.Where(I("a").RegexpNotLike("a%")) //("a" !~ 'a%')
RegexpNotLike(interface{}) BooleanExpression
// Creates an Boolean expression for case insensitive REGEXP ILIKE clauses
// ds.Where(I("a").RegexpILike("a%")) //("a" ~* 'a%')
RegexpILike(interface{}) BooleanExpression
// Creates an Boolean expression for case insensitive REGEXP NOT ILIKE clauses
// ds.Where(I("a").RegexpNotILike("a%")) //("a" !~* 'a%')
RegexpNotILike(interface{}) BooleanExpression
}
// Interface that an expression should implement if it can be compared with other values.
Comparable interface {
// Creates a Boolean expression comparing equality
// I("col").Eq(1) //("col" = 1)
Eq(interface{}) BooleanExpression
// Creates a Boolean expression comparing in-equality
// I("col").Neq(1) //("col" != 1)
Neq(interface{}) BooleanExpression
// Creates a Boolean expression for greater than comparisons
// I("col").Gt(1) //("col" > 1)
Gt(interface{}) BooleanExpression
// Creates a Boolean expression for greater than or equal to than comparisons
// I("col").Gte(1) //("col" >= 1)
Gte(interface{}) BooleanExpression
// Creates a Boolean expression for less than comparisons
// I("col").Lt(1) //("col" < 1)
Lt(interface{}) BooleanExpression
// Creates a Boolean expression for less than or equal to comparisons
// I("col").Lte(1) //("col" <= 1)
Lte(interface{}) BooleanExpression
}
// Interface that an expression should implement if it can be used in a DISTINCT epxression.
Distinctable interface {
// Creates a DISTINCT clause
// I("a").Distinct() //DISTINCT("a")
Distinct() SQLFunctionExpression
}
// Interface that an expression should implement if it can be ORDERED.
Orderable interface {
// Creates an Ordered Expression for sql ASC order
// ds.Order(I("a").Asc()) //ORDER BY "a" ASC
Asc() OrderedExpression
// Creates an Ordered Expression for sql DESC order
// ds.Order(I("a").Desc()) //ORDER BY "a" DESC
Desc() OrderedExpression
}
Rangeable interface {
// Creates a Range expression for between comparisons
// I("col").Between(RangeVal{Start:1, End:10}) //("col" BETWEEN 1 AND 10)
Between(RangeVal) RangeExpression
// Creates a Range expression for between comparisons
// I("col").NotBetween(RangeVal{Start:1, End:10}) //("col" NOT BETWEEN 1 AND 10)
NotBetween(RangeVal) RangeExpression
}
Updateable interface {
// Used internally by update sql
Set(interface{}) UpdateExpression
}
Bitwiseable interface {
// Creates a Bit Operation Expresion for sql ~
// I("col").BitiInversion() // (~ "col")
BitwiseInversion() BitwiseExpression
// Creates a Bit Operation Expresion for sql |
// I("col").BitOr(1) // ("col" | 1)
BitwiseOr(interface{}) BitwiseExpression
// Creates a Bit Operation Expresion for sql &
// I("col").BitAnd(1) // ("col" & 1)
BitwiseAnd(interface{}) BitwiseExpression
// Creates a Bit Operation Expresion for sql ^
// I("col").BitXor(1) // ("col" ^ 1)
BitwiseXor(interface{}) BitwiseExpression
// Creates a Bit Operation Expresion for sql <<
// I("col").BitLeftShift(1) // ("col" << 1)
BitwiseLeftShift(interface{}) BitwiseExpression
// Creates a Bit Operation Expresion for sql >>
// I("col").BitRighttShift(1) // ("col" >> 1)
BitwiseRightShift(interface{}) BitwiseExpression
}
)
type (
Vals []interface{}
// Parent of all expression types
Expression interface {
Clone() Expression
Expression() Expression
}
// An Expression that generates its own sql (e.g Dataset)
SQLExpression interface {
Expression
ToSQL() (string, []interface{}, error)
IsPrepared() bool
}
AppendableExpression interface {
Expression
AppendSQL(b sb.SQLBuilder)
// Returns the alias value as an identiier expression
GetAs() IdentifierExpression
// Returns true if this expression returns columns.
// Used to determine if a Select, Update, Insert, or Delete query returns columns
ReturnsColumns() bool
}
// Expression for Aliased expressions
// I("a").As("b") -> "a" AS "b"
// SUM("a").As(I("a_sum")) -> SUM("a") AS "a_sum"
AliasedExpression interface {
Expression
// Returns the Epxression being aliased
Aliased() Expression
// Returns the alias value as an identiier expression
GetAs() IdentifierExpression
// Returns a new IdentifierExpression with the specified schema
Schema(string) IdentifierExpression
// Returns a new IdentifierExpression with the specified table
Table(string) IdentifierExpression
// Returns a new IdentifierExpression with the specified column
Col(interface{}) IdentifierExpression
// Returns a new IdentifierExpression with the column set to *
// I("my_table").All() //"my_table".*
All() IdentifierExpression
}
BooleanOperation int
BooleanExpression interface {
Expression
Aliaseable
// Returns the operator for the expression
Op() BooleanOperation
// The left hand side of the expression (e.g. I("a")
LHS() Expression
// The right hand side of the expression could be a primitive value, dataset, or expression
RHS() interface{}
}
BitwiseOperation int
BitwiseExpression interface {
Expression
Aliaseable
Comparable
Isable
Inable
Likeable
Rangeable
Orderable
Distinctable
// Returns the operator for the expression
Op() BitwiseOperation
// The left hand side of the expression (e.g. I("a")
LHS() Expression
// The right hand side of the expression could be a primitive value, dataset, or expression
RHS() interface{}
}
// An Expression that represents another Expression casted to a SQL type
CastExpression interface {
Expression
Aliaseable
Comparable
Inable
Isable
Likeable
Orderable
Distinctable
Rangeable
// The exression being casted
Casted() Expression
// The the SQL type to cast the expression to
Type() LiteralExpression
}
// A list of columns. Typically used internally by Select, Order, From
ColumnListExpression interface {
Expression
// Returns the list of columns
Columns() []Expression
// Returns true if the column list is empty
IsEmpty() bool
// Returns a new ColumnListExpression with the columns appended.
Append(...Expression) ColumnListExpression
}
CompoundType int
CompoundExpression interface {
Expression
Type() CompoundType
RHS() AppendableExpression
}
// An Expression that the ON CONFLICT/ON DUPLICATE KEY portion of an INSERT statement
ConflictAction int
ConflictExpression interface {
Expression
Action() ConflictAction
}
ConflictUpdateExpression interface {
ConflictExpression
TargetColumn() string
Where(expressions ...Expression) ConflictUpdateExpression
WhereClause() ExpressionList
Update() interface{}
}
CommonTableExpression interface {
Expression
IsRecursive() bool
// Returns the alias name for the extracted expression
Name() LiteralExpression
// Returns the Expression being extracted
SubQuery() Expression
}
ExpressionListType int
// A list of expressions that should be joined together
// And(I("a").Eq(10), I("b").Eq(11)) //(("a" = 10) AND ("b" = 11))
// Or(I("a").Eq(10), I("b").Eq(11)) //(("a" = 10) OR ("b" = 11))
ExpressionList interface {
Expression
// Returns type (e.g. OR, AND)
Type() ExpressionListType
// Slice of expressions that should be joined together
Expressions() []Expression
// Returns a new expression list with the given expressions appended to the current Expressions list
Append(...Expression) ExpressionList
IsEmpty() bool
}
// An Identifier that can contain schema, table and column identifiers
IdentifierExpression interface {
Expression
Aliaseable
Comparable
Inable
Isable
Likeable
Rangeable
Orderable
Updateable
Distinctable
Castable
Bitwiseable
// returns true if this identifier has more more than on part (Schema, Table or Col)
// "schema" -> true //cant qualify anymore
// "schema.table" -> true
// "table" -> false
// "schema"."table"."col" -> true
// "table"."col" -> true
// "col" -> false
IsQualified() bool
// Returns a new IdentifierExpression with the specified schema
Schema(string) IdentifierExpression
// Returns the current schema
GetSchema() string
// Returns a new IdentifierExpression with the specified table
Table(string) IdentifierExpression
// Returns the current table
GetTable() string
// Returns a new IdentifierExpression with the specified column
Col(interface{}) IdentifierExpression
// Returns the current column
GetCol() interface{}
// Returns a new IdentifierExpression with the column set to *
// I("my_table").All() //"my_table".*
All() IdentifierExpression
// Returns true if schema table and identifier are all zero values.
IsEmpty() bool
}
InsertExpression interface {
Expression
IsEmpty() bool
IsInsertFrom() bool
From() AppendableExpression
Cols() ColumnListExpression
SetCols(cols ColumnListExpression) InsertExpression
Vals() [][]interface{}
SetVals([][]interface{}) InsertExpression
}
JoinType int
JoinExpression interface {
Expression
JoinType() JoinType
IsConditioned() bool
Table() Expression
}
// Parent type for join expressions
ConditionedJoinExpression interface {
JoinExpression
Condition() JoinCondition
IsConditionEmpty() bool
}
LateralExpression interface {
Expression
Aliaseable
Table() AppendableExpression
}
// Expression for representing "literal" sql.
// L("col = 1") -> col = 1)
// L("? = ?", I("col"), 1) -> "col" = 1
LiteralExpression interface {
Expression
Aliaseable
Comparable
Isable
Inable
Likeable
Rangeable
Orderable
Bitwiseable
// Returns the literal sql
Literal() string
// Arguments to be replaced within the sql
Args() []interface{}
}
NullSortType int
SortDirection int
// An expression for specifying sort order and options
OrderedExpression interface {
Expression
// The expression being sorted
SortExpression() Expression
// Sort direction (e.g. ASC, DESC)
IsAsc() bool
// If the adapter supports it null sort type (e.g. NULLS FIRST, NULLS LAST)
NullSortType() NullSortType
// Returns a new OrderedExpression with NullSortType set to NULLS_FIRST
NullsFirst() OrderedExpression
// Returns a new OrderedExpression with NullSortType set to NULLS_LAST
NullsLast() OrderedExpression
}
RangeOperation int
RangeExpression interface {
Expression
// Returns the operator for the expression
Op() RangeOperation
// The left hand side of the expression (e.g. I("a")
LHS() Expression
// The right hand side of the expression could be a primitive value, dataset, or expression
RHS() RangeVal
}
RangeVal interface {
Start() interface{}
End() interface{}
}
Windowable interface {
Over(WindowExpression) SQLWindowFunctionExpression
OverName(IdentifierExpression) SQLWindowFunctionExpression
}
// Expression for representing a SQLFunction(e.g. COUNT, SUM, MIN, MAX...)
SQLFunctionExpression interface {
Expression
Aliaseable
Rangeable
Comparable
Orderable
Isable
Inable
Likeable
Windowable
// The function name
Name() string
// Arguments to be passed to the function
Args() []interface{}
}
UpdateExpression interface {
Col() IdentifierExpression
Val() interface{}
}
SQLWindowFunctionExpression interface {
Expression
Aliaseable
Rangeable
Comparable
Orderable
Isable
Inable
Likeable
Func() SQLFunctionExpression
Window() WindowExpression
WindowName() IdentifierExpression
HasWindow() bool
HasWindowName() bool
}
WindowExpression interface {
Expression
Name() IdentifierExpression
HasName() bool
Parent() IdentifierExpression
HasParent() bool
PartitionCols() ColumnListExpression
HasPartitionBy() bool
OrderCols() ColumnListExpression
HasOrder() bool
Inherit(parent string) WindowExpression
PartitionBy(cols ...interface{}) WindowExpression
OrderBy(cols ...interface{}) WindowExpression
}
CaseElse interface {
Result() interface{}
}
CaseWhen interface {
Condition() interface{}
Result() interface{}
}
CaseExpression interface {
Expression
Aliaseable
Orderable
GetValue() interface{}
GetWhens() []CaseWhen
GetElse() CaseElse
Value(val interface{}) CaseExpression
When(condition, result interface{}) CaseExpression
Else(result interface{}) CaseExpression
}
)
const (
UnionCompoundType CompoundType = iota
UnionAllCompoundType
IntersectCompoundType
IntersectAllCompoundType
DoNothingConflictAction ConflictAction = iota
DoUpdateConflictAction
AndType ExpressionListType = iota
OrType
InnerJoinType JoinType = iota
FullOuterJoinType
RightOuterJoinType
LeftOuterJoinType
FullJoinType
RightJoinType
LeftJoinType
NaturalJoinType
NaturalLeftJoinType
NaturalRightJoinType
NaturalFullJoinType
CrossJoinType
UsingJoinCondType JoinConditionType = iota
OnJoinCondType
// Default null sort type with no null sort order
NoNullsSortType NullSortType = iota
// NULLS FIRST
NullsFirstSortType
// NULLS LAST
NullsLastSortType
// ASC
AscDir SortDirection = iota
// DESC
DescSortDir
// BETWEEN
BetweenOp RangeOperation = iota
// NOT BETWEEN
NotBetweenOp
// =
EqOp BooleanOperation = iota
// != or <>
NeqOp
// IS
IsOp
// IS NOT
IsNotOp
// >
GtOp
// >=
GteOp
// <
LtOp
// <=
LteOp
// IN
InOp
// NOT IN
NotInOp
// LIKE, LIKE BINARY...
LikeOp
// NOT LIKE, NOT LIKE BINARY...
NotLikeOp
// ILIKE, LIKE
ILikeOp
// NOT ILIKE, NOT LIKE
NotILikeOp
// ~, REGEXP BINARY
RegexpLikeOp
// !~, NOT REGEXP BINARY
RegexpNotLikeOp
// ~*, REGEXP
RegexpILikeOp
// !~*, NOT REGEXP
RegexpNotILikeOp
betweenStr = "between"
BitwiseInversionOp BitwiseOperation = iota
BitwiseOrOp
BitwiseAndOp
BitwiseXorOp
BitwiseLeftShiftOp
BitwiseRightShiftOp
)
var (
ConditionedJoinTypes = map[JoinType]bool{
InnerJoinType: true,
FullOuterJoinType: true,
RightOuterJoinType: true,
LeftOuterJoinType: true,
FullJoinType: true,
RightJoinType: true,
LeftJoinType: true,
}
// used internally for inverting operators
operatorInversions = map[BooleanOperation]BooleanOperation{
IsOp: IsNotOp,
EqOp: NeqOp,
GtOp: LteOp,
GteOp: LtOp,
LtOp: GteOp,
LteOp: GtOp,
InOp: NotInOp,
LikeOp: NotLikeOp,
ILikeOp: NotILikeOp,
RegexpLikeOp: RegexpNotLikeOp,
RegexpILikeOp: RegexpNotILikeOp,
IsNotOp: IsOp,
NeqOp: EqOp,
NotInOp: InOp,
NotLikeOp: LikeOp,
NotILikeOp: ILikeOp,
RegexpNotLikeOp: RegexpLikeOp,
RegexpNotILikeOp: RegexpILikeOp,
}
)
func (bo BooleanOperation) String() string {
switch bo {
case EqOp:
return "eq"
case NeqOp:
return "neq"
case IsOp:
return "is"
case IsNotOp:
return "isnot"
case GtOp:
return "gt"
case GteOp:
return "gte"
case LtOp:
return "lt"
case LteOp:
return "lte"
case InOp:
return "in"
case NotInOp:
return "notin"
case LikeOp:
return "like"
case NotLikeOp:
return "notlike"
case ILikeOp:
return "ilike"
case NotILikeOp:
return "notilike"
case RegexpLikeOp:
return "regexplike"
case RegexpNotLikeOp:
return "regexpnotlike"
case RegexpILikeOp:
return "regexpilike"
case RegexpNotILikeOp:
return "regexpnotilike"
}
return fmt.Sprintf("%d", bo)
}
func (bi BitwiseOperation) String() string {
switch bi {
case BitwiseInversionOp:
return "Inversion"
case BitwiseOrOp:
return "OR"
case BitwiseAndOp:
return "AND"
case BitwiseXorOp:
return "XOR"
case BitwiseLeftShiftOp:
return "Left Shift"
case BitwiseRightShiftOp:
return "Right Shift"
}
return fmt.Sprintf("%d", bi)
}
func (ro RangeOperation) String() string {
switch ro {
case BetweenOp:
return betweenStr
case NotBetweenOp:
return "not between"
}
return fmt.Sprintf("%d", ro)
}
func (jt JoinType) String() string {
switch jt {
case InnerJoinType:
return "InnerJoinType"
case FullOuterJoinType:
return "FullOuterJoinType"
case RightOuterJoinType:
return "RightOuterJoinType"
case LeftOuterJoinType:
return "LeftOuterJoinType"
case FullJoinType:
return "FullJoinType"
case RightJoinType:
return "RightJoinType"
case LeftJoinType:
return "LeftJoinType"
case NaturalJoinType:
return "NaturalJoinType"
case NaturalLeftJoinType:
return "NaturalLeftJoinType"
case NaturalRightJoinType:
return "NaturalRightJoinType"
case NaturalFullJoinType:
return "NaturalFullJoinType"
case CrossJoinType:
return "CrossJoinType"
}
return fmt.Sprintf("%d", jt)
}

66
vendor/github.com/doug-martin/goqu/v9/exp/exp_list.go generated vendored Normal file
View File

@@ -0,0 +1,66 @@
package exp
type (
expressionList struct {
operator ExpressionListType
expressions []Expression
}
)
// A list of expressions that should be ORed together
// Or(I("a").Eq(10), I("b").Eq(11)) //(("a" = 10) OR ("b" = 11))
func NewExpressionList(operator ExpressionListType, expressions ...Expression) ExpressionList {
el := expressionList{operator: operator}
exps := make([]Expression, 0, len(el.expressions))
for _, e := range expressions {
switch t := e.(type) {
case ExpressionList:
if !t.IsEmpty() {
exps = append(exps, e)
}
case Ex:
if len(t) > 0 {
exps = append(exps, e)
}
case ExOr:
if len(t) > 0 {
exps = append(exps, e)
}
default:
exps = append(exps, e)
}
}
el.expressions = exps
return el
}
func (el expressionList) Clone() Expression {
newExps := make([]Expression, 0, len(el.expressions))
for _, exp := range el.expressions {
newExps = append(newExps, exp.Clone())
}
return expressionList{operator: el.operator, expressions: newExps}
}
func (el expressionList) Expression() Expression {
return el
}
func (el expressionList) IsEmpty() bool {
return len(el.expressions) == 0
}
func (el expressionList) Type() ExpressionListType {
return el.operator
}
func (el expressionList) Expressions() []Expression {
return el.expressions
}
func (el expressionList) Append(expressions ...Expression) ExpressionList {
exps := make([]Expression, 0, len(el.expressions)+len(expressions))
exps = append(exps, el.expressions...)
exps = append(exps, expressions...)
return NewExpressionList(el.operator, exps...)
}

164
vendor/github.com/doug-martin/goqu/v9/exp/exp_map.go generated vendored Normal file
View File

@@ -0,0 +1,164 @@
package exp
import (
"sort"
"strings"
"github.com/doug-martin/goqu/v9/internal/errors"
)
type (
// A map of expressions to be ANDed together where the keys are string that will be used as Identifiers and values
// will be used in a boolean operation.
// The Ex map can be used in tandem with Op map to create more complex expression such as LIKE, GT, LT...
// See examples.
Ex map[string]interface{}
// A map of expressions to be ORed together where the keys are string that will be used as Identifiers and values
// will be used in a boolean operation.
// The Ex map can be used in tandem with Op map to create more complex expression such as LIKE, GT, LT...
// See examples.
ExOr map[string]interface{}
// Used in tandem with the Ex map to create complex comparisons such as LIKE, GT, LT... See examples
Op map[string]interface{}
)
func (e Ex) Expression() Expression {
return e
}
func (e Ex) Clone() Expression {
ret := Ex{}
for key, val := range e {
ret[key] = val
}
return ret
}
func (e Ex) IsEmpty() bool {
return len(e) == 0
}
func (e Ex) ToExpressions() (ExpressionList, error) {
return mapToExpressionList(e, AndType)
}
func (eo ExOr) Expression() Expression {
return eo
}
func (eo ExOr) Clone() Expression {
ret := ExOr{}
for key, val := range eo {
ret[key] = val
}
return ret
}
func (eo ExOr) IsEmpty() bool {
return len(eo) == 0
}
func (eo ExOr) ToExpressions() (ExpressionList, error) {
return mapToExpressionList(eo, OrType)
}
func getExMapKeys(ex map[string]interface{}) []string {
keys := make([]string, 0, len(ex))
for key := range ex {
keys = append(keys, key)
}
sort.Strings(keys)
return keys
}
func mapToExpressionList(ex map[string]interface{}, eType ExpressionListType) (ExpressionList, error) {
keys := getExMapKeys(ex)
ret := make([]Expression, 0, len(keys))
for _, key := range keys {
lhs := ParseIdentifier(key)
rhs := ex[key]
var exp Expression
if op, ok := rhs.(Op); ok {
ors, err := createOredExpressionFromMap(lhs, op)
if err != nil {
return nil, err
}
exp = NewExpressionList(OrType, ors...)
} else {
exp = lhs.Eq(rhs)
}
ret = append(ret, exp)
}
if eType == OrType {
return NewExpressionList(OrType, ret...), nil
}
return NewExpressionList(AndType, ret...), nil
}
func createOredExpressionFromMap(lhs IdentifierExpression, op Op) ([]Expression, error) {
opKeys := getExMapKeys(op)
ors := make([]Expression, 0, len(opKeys))
for _, opKey := range opKeys {
if exp, err := createExpressionFromOp(lhs, opKey, op); err != nil {
return nil, err
} else if exp != nil {
ors = append(ors, exp)
}
}
return ors, nil
}
// nolint:gocyclo // not complex just long
func createExpressionFromOp(lhs IdentifierExpression, opKey string, op Op) (exp Expression, err error) {
switch strings.ToLower(opKey) {
case EqOp.String():
exp = lhs.Eq(op[opKey])
case NeqOp.String():
exp = lhs.Neq(op[opKey])
case IsOp.String():
exp = lhs.Is(op[opKey])
case IsNotOp.String():
exp = lhs.IsNot(op[opKey])
case GtOp.String():
exp = lhs.Gt(op[opKey])
case GteOp.String():
exp = lhs.Gte(op[opKey])
case LtOp.String():
exp = lhs.Lt(op[opKey])
case LteOp.String():
exp = lhs.Lte(op[opKey])
case InOp.String():
exp = lhs.In(op[opKey])
case NotInOp.String():
exp = lhs.NotIn(op[opKey])
case LikeOp.String():
exp = lhs.Like(op[opKey])
case NotLikeOp.String():
exp = lhs.NotLike(op[opKey])
case ILikeOp.String():
exp = lhs.ILike(op[opKey])
case NotILikeOp.String():
exp = lhs.NotILike(op[opKey])
case RegexpLikeOp.String():
exp = lhs.RegexpLike(op[opKey])
case RegexpNotLikeOp.String():
exp = lhs.RegexpNotLike(op[opKey])
case RegexpILikeOp.String():
exp = lhs.RegexpILike(op[opKey])
case RegexpNotILikeOp.String():
exp = lhs.RegexpNotILike(op[opKey])
case betweenStr:
rangeVal, ok := op[opKey].(RangeVal)
if ok {
exp = lhs.Between(rangeVal)
}
case "notbetween":
rangeVal, ok := op[opKey].(RangeVal)
if ok {
exp = lhs.NotBetween(rangeVal)
}
default:
err = errors.New("unsupported expression type %s", opKey)
}
return exp, err
}

89
vendor/github.com/doug-martin/goqu/v9/exp/func.go generated vendored Normal file
View File

@@ -0,0 +1,89 @@
package exp
type (
sqlFunctionExpression struct {
name string
args []interface{}
}
)
// Creates a new SQLFunctionExpression with the given name and arguments
func NewSQLFunctionExpression(name string, args ...interface{}) SQLFunctionExpression {
return sqlFunctionExpression{name: name, args: args}
}
func (sfe sqlFunctionExpression) Clone() Expression {
return sqlFunctionExpression{name: sfe.name, args: sfe.args}
}
func (sfe sqlFunctionExpression) Expression() Expression { return sfe }
func (sfe sqlFunctionExpression) Args() []interface{} { return sfe.args }
func (sfe sqlFunctionExpression) Name() string { return sfe.name }
func (sfe sqlFunctionExpression) As(val interface{}) AliasedExpression {
return NewAliasExpression(sfe, val)
}
func (sfe sqlFunctionExpression) Eq(val interface{}) BooleanExpression { return eq(sfe, val) }
func (sfe sqlFunctionExpression) Neq(val interface{}) BooleanExpression { return neq(sfe, val) }
func (sfe sqlFunctionExpression) Gt(val interface{}) BooleanExpression { return gt(sfe, val) }
func (sfe sqlFunctionExpression) Gte(val interface{}) BooleanExpression { return gte(sfe, val) }
func (sfe sqlFunctionExpression) Lt(val interface{}) BooleanExpression { return lt(sfe, val) }
func (sfe sqlFunctionExpression) Lte(val interface{}) BooleanExpression { return lte(sfe, val) }
func (sfe sqlFunctionExpression) Between(val RangeVal) RangeExpression { return between(sfe, val) }
func (sfe sqlFunctionExpression) NotBetween(val RangeVal) RangeExpression {
return notBetween(sfe, val)
}
func (sfe sqlFunctionExpression) Like(val interface{}) BooleanExpression { return like(sfe, val) }
func (sfe sqlFunctionExpression) NotLike(val interface{}) BooleanExpression { return notLike(sfe, val) }
func (sfe sqlFunctionExpression) ILike(val interface{}) BooleanExpression { return iLike(sfe, val) }
func (sfe sqlFunctionExpression) NotILike(val interface{}) BooleanExpression {
return notILike(sfe, val)
}
func (sfe sqlFunctionExpression) RegexpLike(val interface{}) BooleanExpression {
return regexpLike(sfe, val)
}
func (sfe sqlFunctionExpression) RegexpNotLike(val interface{}) BooleanExpression {
return regexpNotLike(sfe, val)
}
func (sfe sqlFunctionExpression) RegexpILike(val interface{}) BooleanExpression {
return regexpILike(sfe, val)
}
func (sfe sqlFunctionExpression) RegexpNotILike(val interface{}) BooleanExpression {
return regexpNotILike(sfe, val)
}
func (sfe sqlFunctionExpression) In(vals ...interface{}) BooleanExpression { return in(sfe, vals...) }
func (sfe sqlFunctionExpression) NotIn(vals ...interface{}) BooleanExpression {
return notIn(sfe, vals...)
}
func (sfe sqlFunctionExpression) Is(val interface{}) BooleanExpression { return is(sfe, val) }
func (sfe sqlFunctionExpression) IsNot(val interface{}) BooleanExpression { return isNot(sfe, val) }
func (sfe sqlFunctionExpression) IsNull() BooleanExpression { return is(sfe, nil) }
func (sfe sqlFunctionExpression) IsNotNull() BooleanExpression { return isNot(sfe, nil) }
func (sfe sqlFunctionExpression) IsTrue() BooleanExpression { return is(sfe, true) }
func (sfe sqlFunctionExpression) IsNotTrue() BooleanExpression { return isNot(sfe, true) }
func (sfe sqlFunctionExpression) IsFalse() BooleanExpression { return is(sfe, false) }
func (sfe sqlFunctionExpression) IsNotFalse() BooleanExpression { return isNot(sfe, false) }
func (sfe sqlFunctionExpression) Over(we WindowExpression) SQLWindowFunctionExpression {
return NewSQLWindowFunctionExpression(sfe, nil, we)
}
func (sfe sqlFunctionExpression) OverName(windowName IdentifierExpression) SQLWindowFunctionExpression {
return NewSQLWindowFunctionExpression(sfe, windowName, nil)
}
func (sfe sqlFunctionExpression) Asc() OrderedExpression { return asc(sfe) }
func (sfe sqlFunctionExpression) Desc() OrderedExpression { return desc(sfe) }

213
vendor/github.com/doug-martin/goqu/v9/exp/ident.go generated vendored Normal file
View File

@@ -0,0 +1,213 @@
package exp
import (
"strings"
)
type (
identifier struct {
schema string
table string
col interface{}
}
)
var (
tableAndColumnParts = 2
schemaTableAndColumnIdentifierParts = 3
)
func ParseIdentifier(ident string) IdentifierExpression {
parts := strings.Split(ident, ".")
switch len(parts) {
case tableAndColumnParts:
return NewIdentifierExpression("", parts[0], parts[1])
case schemaTableAndColumnIdentifierParts:
return NewIdentifierExpression(parts[0], parts[1], parts[2])
}
return NewIdentifierExpression("", "", ident)
}
func NewIdentifierExpression(schema, table string, col interface{}) IdentifierExpression {
return identifier{}.Schema(schema).Table(table).Col(col)
}
func (i identifier) clone() identifier {
return identifier{schema: i.schema, table: i.table, col: i.col}
}
func (i identifier) Clone() Expression {
return i.clone()
}
func (i identifier) IsQualified() bool {
schema, table, col := i.schema, i.table, i.col
switch c := col.(type) {
case string:
if c != "" {
return len(table) > 0 || len(schema) > 0
}
default:
if c != nil {
return len(table) > 0 || len(schema) > 0
}
}
if len(table) > 0 {
return len(schema) > 0
}
return false
}
// Sets the table on the current identifier
// I("col").Table("table") -> "table"."col" //postgres
// I("col").Table("table") -> `table`.`col` //mysql
// I("col").Table("table") -> `table`.`col` //sqlite3
func (i identifier) Table(table string) IdentifierExpression {
i.table = table
return i
}
func (i identifier) GetTable() string {
return i.table
}
// Sets the table on the current identifier
// I("table").Schema("schema") -> "schema"."table" //postgres
// I("col").Schema("table") -> `schema`.`table` //mysql
// I("col").Schema("table") -> `schema`.`table` //sqlite3
func (i identifier) Schema(schema string) IdentifierExpression {
i.schema = schema
return i
}
func (i identifier) GetSchema() string {
return i.schema
}
// Sets the table on the current identifier
// I("table").Col("col") -> "table"."col" //postgres
// I("table").Schema("col") -> `table`.`col` //mysql
// I("table").Schema("col") -> `table`.`col` //sqlite3
func (i identifier) Col(col interface{}) IdentifierExpression {
if col == "*" {
i.col = Star()
} else {
i.col = col
}
return i
}
func (i identifier) Expression() Expression { return i }
// Qualifies the epression with a * literal (e.g. "table".*)
func (i identifier) All() IdentifierExpression { return i.Col("*") }
func (i identifier) IsEmpty() bool {
isEmpty := i.schema == "" && i.table == ""
if isEmpty {
switch t := i.col.(type) {
case nil:
return true
case string:
return t == ""
default:
return false
}
}
return isEmpty
}
// Gets the column identifier
func (i identifier) GetCol() interface{} { return i.col }
// Used within updates to set a column value
func (i identifier) Set(val interface{}) UpdateExpression { return set(i, val) }
// Alias an identifier (e.g "my_col" AS "other_col")
func (i identifier) As(val interface{}) AliasedExpression {
if v, ok := val.(string); ok {
ident := ParseIdentifier(v)
if i.col != nil && i.col != "" {
return NewAliasExpression(i, ident)
}
aliasCol := ident.GetCol()
if i.table != "" {
return NewAliasExpression(i, NewIdentifierExpression("", aliasCol.(string), nil))
} else if i.schema != "" {
return NewAliasExpression(i, NewIdentifierExpression(aliasCol.(string), "", nil))
}
}
return NewAliasExpression(i, val)
}
// Returns a BooleanExpression for equality (e.g "my_col" = 1)
func (i identifier) Eq(val interface{}) BooleanExpression { return eq(i, val) }
// Returns a BooleanExpression for in equality (e.g "my_col" != 1)
func (i identifier) Neq(val interface{}) BooleanExpression { return neq(i, val) }
// Returns a BooleanExpression for checking that a identifier is greater than another value (e.g "my_col" > 1)
func (i identifier) Gt(val interface{}) BooleanExpression { return gt(i, val) }
// Returns a BooleanExpression for checking that a identifier is greater than or equal to another value
// (e.g "my_col" >= 1)
func (i identifier) Gte(val interface{}) BooleanExpression { return gte(i, val) }
// Returns a BooleanExpression for checking that a identifier is less than another value (e.g "my_col" < 1)
func (i identifier) Lt(val interface{}) BooleanExpression { return lt(i, val) }
// Returns a BooleanExpression for checking that a identifier is less than or equal to another value
// (e.g "my_col" <= 1)
func (i identifier) Lte(val interface{}) BooleanExpression { return lte(i, val) }
// Returns a BooleanExpression for bit inversion (e.g ~ "my_col")
func (i identifier) BitwiseInversion() BitwiseExpression { return bitwiseInversion(i) }
// Returns a BooleanExpression for bit OR (e.g "my_col" | 1)
func (i identifier) BitwiseOr(val interface{}) BitwiseExpression { return bitwiseOr(i, val) }
// Returns a BooleanExpression for bit AND (e.g "my_col" & 1)
func (i identifier) BitwiseAnd(val interface{}) BitwiseExpression { return bitwiseAnd(i, val) }
// Returns a BooleanExpression for bit XOR (e.g "my_col" ^ 1)
func (i identifier) BitwiseXor(val interface{}) BitwiseExpression { return bitwiseXor(i, val) }
// Returns a BooleanExpression for bit LEFT shift (e.g "my_col" << 1)
func (i identifier) BitwiseLeftShift(val interface{}) BitwiseExpression {
return bitwiseLeftShift(i, val)
}
// Returns a BooleanExpression for bit RIGHT shift (e.g "my_col" >> 1)
func (i identifier) BitwiseRightShift(val interface{}) BitwiseExpression {
return bitwiseRightShift(i, val)
}
// Returns a BooleanExpression for checking that a identifier is in a list of values or (e.g "my_col" > 1)
func (i identifier) In(vals ...interface{}) BooleanExpression { return in(i, vals...) }
func (i identifier) NotIn(vals ...interface{}) BooleanExpression { return notIn(i, vals...) }
func (i identifier) Like(val interface{}) BooleanExpression { return like(i, val) }
func (i identifier) NotLike(val interface{}) BooleanExpression { return notLike(i, val) }
func (i identifier) ILike(val interface{}) BooleanExpression { return iLike(i, val) }
func (i identifier) NotILike(val interface{}) BooleanExpression { return notILike(i, val) }
func (i identifier) RegexpLike(val interface{}) BooleanExpression { return regexpLike(i, val) }
func (i identifier) RegexpNotLike(val interface{}) BooleanExpression { return regexpNotLike(i, val) }
func (i identifier) RegexpILike(val interface{}) BooleanExpression { return regexpILike(i, val) }
func (i identifier) RegexpNotILike(val interface{}) BooleanExpression { return regexpNotILike(i, val) }
func (i identifier) Is(val interface{}) BooleanExpression { return is(i, val) }
func (i identifier) IsNot(val interface{}) BooleanExpression { return isNot(i, val) }
func (i identifier) IsNull() BooleanExpression { return is(i, nil) }
func (i identifier) IsNotNull() BooleanExpression { return isNot(i, nil) }
func (i identifier) IsTrue() BooleanExpression { return is(i, true) }
func (i identifier) IsNotTrue() BooleanExpression { return isNot(i, true) }
func (i identifier) IsFalse() BooleanExpression { return is(i, false) }
func (i identifier) IsNotFalse() BooleanExpression { return isNot(i, false) }
func (i identifier) Asc() OrderedExpression { return asc(i) }
func (i identifier) Desc() OrderedExpression { return desc(i) }
func (i identifier) Distinct() SQLFunctionExpression { return NewSQLFunctionExpression("DISTINCT", i) }
func (i identifier) Cast(t string) CastExpression { return NewCastExpression(i, t) }
// Returns a RangeExpression for checking that a identifier is between two values (e.g "my_col" BETWEEN 1 AND 10)
func (i identifier) Between(val RangeVal) RangeExpression { return between(i, val) }
// Returns a RangeExpression for checking that a identifier is between two values (e.g "my_col" BETWEEN 1 AND 10)
func (i identifier) NotBetween(val RangeVal) RangeExpression { return notBetween(i, val) }

163
vendor/github.com/doug-martin/goqu/v9/exp/insert.go generated vendored Normal file
View File

@@ -0,0 +1,163 @@
package exp
import (
"reflect"
"sort"
"github.com/doug-martin/goqu/v9/internal/errors"
"github.com/doug-martin/goqu/v9/internal/util"
)
type (
insert struct {
from AppendableExpression
cols ColumnListExpression
vals [][]interface{}
}
)
func NewInsertExpression(rows ...interface{}) (insertExpression InsertExpression, err error) {
switch len(rows) {
case 0:
return new(insert), nil
case 1:
val := reflect.ValueOf(rows[0])
if val.Kind() == reflect.Slice {
vals := make([]interface{}, 0, val.Len())
for i := 0; i < val.Len(); i++ {
vals = append(vals, val.Index(i).Interface())
}
return NewInsertExpression(vals...)
}
if ae, ok := rows[0].(AppendableExpression); ok {
return &insert{from: ae}, nil
}
}
return newInsert(rows...)
}
func (i *insert) Expression() Expression {
return i
}
func (i *insert) Clone() Expression {
return i.clone()
}
func (i *insert) clone() *insert {
return &insert{from: i.from, cols: i.cols, vals: i.vals}
}
func (i *insert) IsEmpty() bool {
return i.from == nil && (i.cols == nil || i.cols.IsEmpty())
}
func (i *insert) IsInsertFrom() bool {
return i.from != nil
}
func (i *insert) From() AppendableExpression {
return i.from
}
func (i *insert) Cols() ColumnListExpression {
return i.cols
}
func (i *insert) SetCols(cols ColumnListExpression) InsertExpression {
ci := i.clone()
ci.cols = cols
return ci
}
func (i *insert) Vals() [][]interface{} {
return i.vals
}
func (i *insert) SetVals(vals [][]interface{}) InsertExpression {
ci := i.clone()
ci.vals = vals
return ci
}
// parses the rows gathering and sorting unique columns and values for each record
func newInsert(rows ...interface{}) (insertExp InsertExpression, err error) {
var mapKeys util.ValueSlice
rowValue := reflect.Indirect(reflect.ValueOf(rows[0]))
rowType := rowValue.Type()
rowKind := rowValue.Kind()
if rowKind == reflect.Struct {
return createStructSliceInsert(rows...)
}
vals := make([][]interface{}, 0, len(rows))
var columns ColumnListExpression
for _, row := range rows {
if rowType != reflect.Indirect(reflect.ValueOf(row)).Type() {
return nil, errors.New(
"rows must be all the same type expected %+v got %+v",
rowType,
reflect.Indirect(reflect.ValueOf(row)).Type(),
)
}
newRowValue := reflect.Indirect(reflect.ValueOf(row))
switch rowKind {
case reflect.Map:
if columns == nil {
mapKeys = util.ValueSlice(newRowValue.MapKeys())
sort.Sort(mapKeys)
colKeys := make([]interface{}, 0, len(mapKeys))
for _, key := range mapKeys {
colKeys = append(colKeys, key.Interface())
}
columns = NewColumnListExpression(colKeys...)
}
newMapKeys := util.ValueSlice(newRowValue.MapKeys())
if len(newMapKeys) != len(mapKeys) {
return nil, errors.New("rows with different value length expected %d got %d", len(mapKeys), len(newMapKeys))
}
if !mapKeys.Equal(newMapKeys) {
return nil, errors.New("rows with different keys expected %s got %s", mapKeys.String(), newMapKeys.String())
}
rowVals := make([]interface{}, 0, len(mapKeys))
for _, key := range mapKeys {
rowVals = append(rowVals, newRowValue.MapIndex(key).Interface())
}
vals = append(vals, rowVals)
default:
return nil, errors.New(
"unsupported insert must be map, goqu.Record, or struct type got: %T",
row,
)
}
}
return &insert{cols: columns, vals: vals}, nil
}
func createStructSliceInsert(rows ...interface{}) (insertExp InsertExpression, err error) {
rowValue := reflect.Indirect(reflect.ValueOf(rows[0]))
rowType := rowValue.Type()
recordRows := make([]interface{}, 0, len(rows))
for _, row := range rows {
if rowType != reflect.Indirect(reflect.ValueOf(row)).Type() {
return nil, errors.New(
"rows must be all the same type expected %+v got %+v",
rowType,
reflect.Indirect(reflect.ValueOf(row)).Type(),
)
}
newRowValue := reflect.Indirect(reflect.ValueOf(row))
record, err := getFieldsValuesFromStruct(newRowValue)
if err != nil {
return nil, err
}
recordRows = append(recordRows, record)
}
return newInsert(recordRows...)
}
func getFieldsValuesFromStruct(value reflect.Value) (row Record, err error) {
if value.IsValid() {
return NewRecordFromStruct(value.Interface(), true, false)
}
return
}

View File

@@ -0,0 +1,205 @@
package exp
type (
InsertClauses interface {
CommonTables() []CommonTableExpression
CommonTablesAppend(cte CommonTableExpression) InsertClauses
HasInto() bool
clone() *insertClauses
Cols() ColumnListExpression
HasCols() bool
ColsAppend(cols ColumnListExpression) InsertClauses
SetCols(cols ColumnListExpression) InsertClauses
Into() Expression
SetInto(cl Expression) InsertClauses
Returning() ColumnListExpression
HasReturning() bool
SetReturning(cl ColumnListExpression) InsertClauses
From() AppendableExpression
HasFrom() bool
SetFrom(ae AppendableExpression) InsertClauses
Rows() []interface{}
HasRows() bool
SetRows(rows []interface{}) InsertClauses
HasAlias() bool
Alias() IdentifierExpression
SetAlias(ie IdentifierExpression) InsertClauses
Vals() [][]interface{}
HasVals() bool
SetVals(vals [][]interface{}) InsertClauses
ValsAppend(vals [][]interface{}) InsertClauses
OnConflict() ConflictExpression
SetOnConflict(expression ConflictExpression) InsertClauses
}
insertClauses struct {
commonTables []CommonTableExpression
cols ColumnListExpression
into Expression
returning ColumnListExpression
alias IdentifierExpression
rows []interface{}
values [][]interface{}
from AppendableExpression
conflict ConflictExpression
}
)
func NewInsertClauses() InsertClauses {
return &insertClauses{}
}
func (ic *insertClauses) HasInto() bool {
return ic.into != nil
}
func (ic *insertClauses) clone() *insertClauses {
return &insertClauses{
commonTables: ic.commonTables,
cols: ic.cols,
into: ic.into,
returning: ic.returning,
alias: ic.alias,
rows: ic.rows,
values: ic.values,
from: ic.from,
conflict: ic.conflict,
}
}
func (ic *insertClauses) CommonTables() []CommonTableExpression {
return ic.commonTables
}
func (ic *insertClauses) CommonTablesAppend(cte CommonTableExpression) InsertClauses {
ret := ic.clone()
ret.commonTables = append(ret.commonTables, cte)
return ret
}
func (ic *insertClauses) Cols() ColumnListExpression {
return ic.cols
}
func (ic *insertClauses) HasCols() bool {
return ic.cols != nil && !ic.cols.IsEmpty()
}
func (ic *insertClauses) ColsAppend(cl ColumnListExpression) InsertClauses {
ret := ic.clone()
ret.cols = ret.cols.Append(cl.Columns()...)
return ret
}
func (ic *insertClauses) SetCols(cl ColumnListExpression) InsertClauses {
ret := ic.clone()
ret.cols = cl
return ret
}
func (ic *insertClauses) Into() Expression {
return ic.into
}
func (ic *insertClauses) SetInto(into Expression) InsertClauses {
ret := ic.clone()
ret.into = into
return ret
}
func (ic *insertClauses) Returning() ColumnListExpression {
return ic.returning
}
func (ic *insertClauses) HasReturning() bool {
return ic.returning != nil && !ic.returning.IsEmpty()
}
func (ic *insertClauses) HasAlias() bool {
return ic.alias != nil
}
func (ic *insertClauses) Alias() IdentifierExpression {
return ic.alias
}
func (ic *insertClauses) SetAlias(ie IdentifierExpression) InsertClauses {
ret := ic.clone()
ret.alias = ie
return ret
}
func (ic *insertClauses) SetReturning(cl ColumnListExpression) InsertClauses {
ret := ic.clone()
ret.returning = cl
return ret
}
func (ic *insertClauses) From() AppendableExpression {
return ic.from
}
func (ic *insertClauses) HasFrom() bool {
return ic.from != nil
}
func (ic *insertClauses) SetFrom(ae AppendableExpression) InsertClauses {
ret := ic.clone()
ret.from = ae
return ret
}
func (ic *insertClauses) Rows() []interface{} {
return ic.rows
}
func (ic *insertClauses) HasRows() bool {
return ic.rows != nil && len(ic.rows) > 0
}
func (ic *insertClauses) SetRows(rows []interface{}) InsertClauses {
ret := ic.clone()
ret.rows = rows
return ret
}
func (ic *insertClauses) Vals() [][]interface{} {
return ic.values
}
func (ic *insertClauses) HasVals() bool {
return ic.values != nil && len(ic.values) > 0
}
func (ic *insertClauses) SetVals(vals [][]interface{}) InsertClauses {
ret := ic.clone()
ret.values = vals
return ret
}
func (ic *insertClauses) ValsAppend(vals [][]interface{}) InsertClauses {
ret := ic.clone()
newVals := make([][]interface{}, 0, len(ic.values)+len(vals))
newVals = append(newVals, ic.values...)
newVals = append(newVals, vals...)
ret.values = newVals
return ret
}
func (ic *insertClauses) OnConflict() ConflictExpression {
return ic.conflict
}
func (ic *insertClauses) SetOnConflict(expression ConflictExpression) InsertClauses {
ret := ic.clone()
ret.conflict = expression
return ret
}

139
vendor/github.com/doug-martin/goqu/v9/exp/join.go generated vendored Normal file
View File

@@ -0,0 +1,139 @@
package exp
type (
joinExpression struct {
isConditioned bool
// The JoinType
joinType JoinType
// The table expressions (e.g. LEFT JOIN "my_table", ON (....))
table Expression
}
// Container for all joins within a dataset
conditionedJoin struct {
joinExpression
// The condition to join (e.g. USING("a", "b"), ON("my_table"."fkey" = "other_table"."id")
condition JoinCondition
}
JoinExpressions []JoinExpression
)
func NewUnConditionedJoinExpression(joinType JoinType, table Expression) JoinExpression {
return joinExpression{
joinType: joinType,
table: table,
isConditioned: false,
}
}
func (je joinExpression) Clone() Expression {
return je
}
func (je joinExpression) Expression() Expression {
return je
}
func (je joinExpression) IsConditioned() bool {
return je.isConditioned
}
func (je joinExpression) JoinType() JoinType {
return je.joinType
}
func (je joinExpression) Table() Expression {
return je.table
}
func NewConditionedJoinExpression(joinType JoinType, table Expression, condition JoinCondition) ConditionedJoinExpression {
return conditionedJoin{
joinExpression: joinExpression{
joinType: joinType,
table: table,
isConditioned: true,
},
condition: condition,
}
}
func (je conditionedJoin) Clone() Expression {
return je
}
func (je conditionedJoin) Expression() Expression {
return je
}
func (je conditionedJoin) Condition() JoinCondition {
return je.condition
}
func (je conditionedJoin) IsConditionEmpty() bool {
return je.condition == nil || je.condition.IsEmpty()
}
func (jes JoinExpressions) Clone() JoinExpressions {
ret := make(JoinExpressions, 0, len(jes))
for _, jc := range jes {
ret = append(ret, jc.Clone().(JoinExpression))
}
return ret
}
type (
JoinConditionType int
JoinCondition interface {
Type() JoinConditionType
IsEmpty() bool
}
JoinOnCondition interface {
JoinCondition
On() ExpressionList
}
JoinUsingCondition interface {
JoinCondition
Using() ColumnListExpression
}
joinOnCondition struct {
on ExpressionList
}
joinUsingCondition struct {
using ColumnListExpression
}
)
// Creates a new ON clause to be used within a join
// ds.Join(I("my_table"), On(I("my_table.fkey").Eq(I("other_table.id")))
func NewJoinOnCondition(expressions ...Expression) JoinCondition {
return joinOnCondition{on: NewExpressionList(AndType, expressions...)}
}
func (joc joinOnCondition) Type() JoinConditionType {
return OnJoinCondType
}
func (joc joinOnCondition) On() ExpressionList {
return joc.on
}
func (joc joinOnCondition) IsEmpty() bool {
return len(joc.on.Expressions()) == 0
}
// Creates a new USING clause to be used within a join
func NewJoinUsingCondition(expressions ...interface{}) JoinCondition {
return joinUsingCondition{using: NewColumnListExpression(expressions...)}
}
func (juc joinUsingCondition) Type() JoinConditionType {
return UsingJoinCondType
}
func (juc joinUsingCondition) Using() ColumnListExpression {
return juc.using
}
func (juc joinUsingCondition) IsEmpty() bool {
return len(juc.using.Columns()) == 0
}

24
vendor/github.com/doug-martin/goqu/v9/exp/lateral.go generated vendored Normal file
View File

@@ -0,0 +1,24 @@
package exp
type (
lateral struct {
table AppendableExpression
}
)
// Creates a new SQL lateral expression
// L(From("test")) -> LATERAL (SELECT * FROM "tests")
func NewLateralExpression(table AppendableExpression) LateralExpression {
return lateral{table: table}
}
func (l lateral) Clone() Expression {
return NewLateralExpression(l.table)
}
func (l lateral) Table() AppendableExpression {
return l.table
}
func (l lateral) Expression() Expression { return l }
func (l lateral) As(val interface{}) AliasedExpression { return NewAliasExpression(l, val) }

80
vendor/github.com/doug-martin/goqu/v9/exp/literal.go generated vendored Normal file
View File

@@ -0,0 +1,80 @@
package exp
type (
literal struct {
literal string
args []interface{}
}
)
// Creates a new SQL literal with the provided arguments.
// L("a = 1") -> a = 1
// You can also you placeholders. All placeholders within a Literal are represented by '?'
// L("a = ?", "b") -> a = 'b'
// Literals can also contain placeholders for other expressions
// L("(? AND ?) OR (?)", I("a").Eq(1), I("b").Eq("b"), I("c").In([]string{"a", "b", "c"}))
func NewLiteralExpression(sql string, args ...interface{}) LiteralExpression {
return literal{literal: sql, args: args}
}
// Returns a literal for the '*' operator
func Star() LiteralExpression {
return NewLiteralExpression("*")
}
// Returns a literal for the 'DEFAULT'
func Default() LiteralExpression {
return NewLiteralExpression("DEFAULT")
}
func (l literal) Clone() Expression {
return NewLiteralExpression(l.literal, l.args...)
}
func (l literal) Literal() string {
return l.literal
}
func (l literal) Args() []interface{} {
return l.args
}
func (l literal) Expression() Expression { return l }
func (l literal) As(val interface{}) AliasedExpression { return NewAliasExpression(l, val) }
func (l literal) Eq(val interface{}) BooleanExpression { return eq(l, val) }
func (l literal) Neq(val interface{}) BooleanExpression { return neq(l, val) }
func (l literal) Gt(val interface{}) BooleanExpression { return gt(l, val) }
func (l literal) Gte(val interface{}) BooleanExpression { return gte(l, val) }
func (l literal) Lt(val interface{}) BooleanExpression { return lt(l, val) }
func (l literal) Lte(val interface{}) BooleanExpression { return lte(l, val) }
func (l literal) Asc() OrderedExpression { return asc(l) }
func (l literal) Desc() OrderedExpression { return desc(l) }
func (l literal) Between(val RangeVal) RangeExpression { return between(l, val) }
func (l literal) NotBetween(val RangeVal) RangeExpression { return notBetween(l, val) }
func (l literal) Like(val interface{}) BooleanExpression { return like(l, val) }
func (l literal) NotLike(val interface{}) BooleanExpression { return notLike(l, val) }
func (l literal) ILike(val interface{}) BooleanExpression { return iLike(l, val) }
func (l literal) NotILike(val interface{}) BooleanExpression { return notILike(l, val) }
func (l literal) RegexpLike(val interface{}) BooleanExpression { return regexpLike(l, val) }
func (l literal) RegexpNotLike(val interface{}) BooleanExpression { return regexpNotLike(l, val) }
func (l literal) RegexpILike(val interface{}) BooleanExpression { return regexpILike(l, val) }
func (l literal) RegexpNotILike(val interface{}) BooleanExpression { return regexpNotILike(l, val) }
func (l literal) In(vals ...interface{}) BooleanExpression { return in(l, vals...) }
func (l literal) NotIn(vals ...interface{}) BooleanExpression { return notIn(l, vals...) }
func (l literal) Is(val interface{}) BooleanExpression { return is(l, val) }
func (l literal) IsNot(val interface{}) BooleanExpression { return isNot(l, val) }
func (l literal) IsNull() BooleanExpression { return is(l, nil) }
func (l literal) IsNotNull() BooleanExpression { return isNot(l, nil) }
func (l literal) IsTrue() BooleanExpression { return is(l, true) }
func (l literal) IsNotTrue() BooleanExpression { return isNot(l, true) }
func (l literal) IsFalse() BooleanExpression { return is(l, false) }
func (l literal) IsNotFalse() BooleanExpression { return isNot(l, false) }
func (l literal) BitwiseInversion() BitwiseExpression { return bitwiseInversion(l) }
func (l literal) BitwiseOr(val interface{}) BitwiseExpression { return bitwiseOr(l, val) }
func (l literal) BitwiseAnd(val interface{}) BitwiseExpression { return bitwiseAnd(l, val) }
func (l literal) BitwiseXor(val interface{}) BitwiseExpression { return bitwiseXor(l, val) }
func (l literal) BitwiseLeftShift(val interface{}) BitwiseExpression { return bitwiseLeftShift(l, val) }
func (l literal) BitwiseRightShift(val interface{}) BitwiseExpression {
return bitwiseRightShift(l, val)
}

48
vendor/github.com/doug-martin/goqu/v9/exp/lock.go generated vendored Normal file
View File

@@ -0,0 +1,48 @@
package exp
type (
LockStrength int
WaitOption int
Lock interface {
Strength() LockStrength
WaitOption() WaitOption
Of() []IdentifierExpression
}
lock struct {
strength LockStrength
waitOption WaitOption
of []IdentifierExpression
}
)
const (
ForNolock LockStrength = iota
ForUpdate
ForNoKeyUpdate
ForShare
ForKeyShare
Wait WaitOption = iota
NoWait
SkipLocked
)
func NewLock(strength LockStrength, option WaitOption, of ...IdentifierExpression) Lock {
return lock{
strength: strength,
waitOption: option,
of: of,
}
}
func (l lock) Strength() LockStrength {
return l.strength
}
func (l lock) WaitOption() WaitOption {
return l.waitOption
}
func (l lock) Of() []IdentifierExpression {
return l.of
}

52
vendor/github.com/doug-martin/goqu/v9/exp/order.go generated vendored Normal file
View File

@@ -0,0 +1,52 @@
package exp
type (
orderedExpression struct {
sortExpression Expression
direction SortDirection
nullSortType NullSortType
}
)
// used internally to create a new SORT_ASC OrderedExpression
func asc(exp Expression) OrderedExpression {
return NewOrderedExpression(exp, AscDir, NoNullsSortType)
}
// used internally to create a new SORT_DESC OrderedExpression
func desc(exp Expression) OrderedExpression {
return NewOrderedExpression(exp, DescSortDir, NoNullsSortType)
}
// used internally to create a new SORT_ASC OrderedExpression
func NewOrderedExpression(exp Expression, direction SortDirection, sortType NullSortType) OrderedExpression {
return orderedExpression{sortExpression: exp, direction: direction, nullSortType: sortType}
}
func (oe orderedExpression) Clone() Expression {
return NewOrderedExpression(oe.sortExpression, oe.direction, oe.nullSortType)
}
func (oe orderedExpression) Expression() Expression {
return oe
}
func (oe orderedExpression) SortExpression() Expression {
return oe.sortExpression
}
func (oe orderedExpression) IsAsc() bool {
return oe.direction == AscDir
}
func (oe orderedExpression) NullSortType() NullSortType {
return oe.nullSortType
}
func (oe orderedExpression) NullsFirst() OrderedExpression {
return NewOrderedExpression(oe.sortExpression, oe.direction, NullsFirstSortType)
}
func (oe orderedExpression) NullsLast() OrderedExpression {
return NewOrderedExpression(oe.sortExpression, oe.direction, NullsLastSortType)
}

61
vendor/github.com/doug-martin/goqu/v9/exp/range.go generated vendored Normal file
View File

@@ -0,0 +1,61 @@
package exp
type (
ranged struct {
lhs Expression
rhs RangeVal
op RangeOperation
}
rangeVal struct {
start interface{}
end interface{}
}
)
// used internally to create an BETWEEN comparison RangeExpression
func between(lhs Expression, rhs RangeVal) RangeExpression {
return NewRangeExpression(BetweenOp, lhs, rhs)
}
// used internally to create an NOT BETWEEN comparison RangeExpression
func notBetween(lhs Expression, rhs RangeVal) RangeExpression {
return NewRangeExpression(NotBetweenOp, lhs, rhs)
}
func NewRangeExpression(op RangeOperation, lhs Expression, rhs RangeVal) RangeExpression {
return ranged{op: op, lhs: lhs, rhs: rhs}
}
func (r ranged) Clone() Expression {
return NewRangeExpression(r.op, r.lhs.Clone(), r.rhs)
}
func (r ranged) Expression() Expression {
return r
}
func (r ranged) RHS() RangeVal {
return r.rhs
}
func (r ranged) LHS() Expression {
return r.lhs
}
func (r ranged) Op() RangeOperation {
return r.op
}
// Creates a new Range to be used with a Between expression
// exp.C("col").Between(exp.Range(1, 10))
func NewRangeVal(start, end interface{}) RangeVal {
return rangeVal{start: start, end: end}
}
func (rv rangeVal) Start() interface{} {
return rv.start
}
func (rv rangeVal) End() interface{} {
return rv.end
}

59
vendor/github.com/doug-martin/goqu/v9/exp/record.go generated vendored Normal file
View File

@@ -0,0 +1,59 @@
package exp
import (
"reflect"
"sort"
"github.com/doug-martin/goqu/v9/internal/util"
)
// Alternative to writing map[string]interface{}. Can be used for Inserts, Updates or Deletes
type Record map[string]interface{}
func (r Record) Cols() []string {
cols := make([]string, 0, len(r))
for col := range r {
cols = append(cols, col)
}
sort.Strings(cols)
return cols
}
func NewRecordFromStruct(i interface{}, forInsert, forUpdate bool) (r Record, err error) {
value := reflect.ValueOf(i)
if value.IsValid() {
cm, err := util.GetColumnMap(value.Interface())
if err != nil {
return nil, err
}
cols := cm.Cols()
r = make(map[string]interface{}, len(cols))
for _, col := range cols {
f := cm[col]
if !shouldSkipField(f, forInsert, forUpdate) {
if ok, fieldVal := getFieldValue(value, f); ok {
r[f.ColumnName] = fieldVal
}
}
}
}
return
}
func shouldSkipField(f util.ColumnData, forInsert, forUpdate bool) bool {
shouldSkipInsert := forInsert && !f.ShouldInsert
shouldSkipUpdate := forUpdate && !f.ShouldUpdate
return shouldSkipInsert || shouldSkipUpdate
}
func getFieldValue(val reflect.Value, f util.ColumnData) (ok bool, fieldVal interface{}) {
if v, isAvailable := util.SafeGetFieldByIndex(val, f.FieldIndex); !isAvailable {
return false, nil
} else if f.DefaultIfEmpty && util.IsEmptyValue(v) {
return true, Default()
} else if v.IsValid() {
return true, v.Interface()
} else {
return true, reflect.Zero(f.GoType).Interface()
}
}

View File

@@ -0,0 +1,379 @@
package exp
type (
SelectClauses interface {
HasSources() bool
IsDefaultSelect() bool
clone() *selectClauses
Select() ColumnListExpression
SelectAppend(cl ColumnListExpression) SelectClauses
SetSelect(cl ColumnListExpression) SelectClauses
Distinct() ColumnListExpression
SetDistinct(cle ColumnListExpression) SelectClauses
From() ColumnListExpression
SetFrom(cl ColumnListExpression) SelectClauses
HasAlias() bool
Alias() IdentifierExpression
SetAlias(ie IdentifierExpression) SelectClauses
Joins() JoinExpressions
JoinsAppend(jc JoinExpression) SelectClauses
Where() ExpressionList
ClearWhere() SelectClauses
WhereAppend(expressions ...Expression) SelectClauses
Having() ExpressionList
ClearHaving() SelectClauses
HavingAppend(expressions ...Expression) SelectClauses
Order() ColumnListExpression
HasOrder() bool
ClearOrder() SelectClauses
SetOrder(oes ...OrderedExpression) SelectClauses
OrderAppend(...OrderedExpression) SelectClauses
OrderPrepend(...OrderedExpression) SelectClauses
GroupBy() ColumnListExpression
SetGroupBy(cl ColumnListExpression) SelectClauses
GroupByAppend(cl ColumnListExpression) SelectClauses
Limit() interface{}
HasLimit() bool
ClearLimit() SelectClauses
SetLimit(limit interface{}) SelectClauses
Offset() uint
ClearOffset() SelectClauses
SetOffset(offset uint) SelectClauses
Compounds() []CompoundExpression
CompoundsAppend(ce CompoundExpression) SelectClauses
Lock() Lock
SetLock(l Lock) SelectClauses
CommonTables() []CommonTableExpression
CommonTablesAppend(cte CommonTableExpression) SelectClauses
Windows() []WindowExpression
SetWindows(ws []WindowExpression) SelectClauses
WindowsAppend(ws ...WindowExpression) SelectClauses
ClearWindows() SelectClauses
}
selectClauses struct {
commonTables []CommonTableExpression
selectColumns ColumnListExpression
distinct ColumnListExpression
from ColumnListExpression
joins JoinExpressions
where ExpressionList
alias IdentifierExpression
groupBy ColumnListExpression
having ExpressionList
order ColumnListExpression
limit interface{}
offset uint
compounds []CompoundExpression
lock Lock
windows []WindowExpression
}
)
func NewSelectClauses() SelectClauses {
return &selectClauses{
selectColumns: NewColumnListExpression(Star()),
}
}
func (c *selectClauses) HasSources() bool {
return c.from != nil && len(c.from.Columns()) > 0
}
func (c *selectClauses) IsDefaultSelect() bool {
ret := false
if c.selectColumns != nil {
selects := c.selectColumns.Columns()
if len(selects) == 1 {
if l, ok := selects[0].(LiteralExpression); ok && l.Literal() == "*" {
ret = true
}
}
}
return ret
}
func (c *selectClauses) clone() *selectClauses {
return &selectClauses{
commonTables: c.commonTables,
selectColumns: c.selectColumns,
distinct: c.distinct,
from: c.from,
joins: c.joins[0:len(c.joins):len(c.joins)],
where: c.where,
alias: c.alias,
groupBy: c.groupBy,
having: c.having,
order: c.order,
limit: c.limit,
offset: c.offset,
compounds: c.compounds,
lock: c.lock,
windows: c.windows,
}
}
func (c *selectClauses) CommonTables() []CommonTableExpression {
return c.commonTables
}
func (c *selectClauses) CommonTablesAppend(cte CommonTableExpression) SelectClauses {
ret := c.clone()
ret.commonTables = append(ret.commonTables, cte)
return ret
}
func (c *selectClauses) Select() ColumnListExpression {
return c.selectColumns
}
func (c *selectClauses) SelectAppend(cl ColumnListExpression) SelectClauses {
ret := c.clone()
ret.selectColumns = ret.selectColumns.Append(cl.Columns()...)
return ret
}
func (c *selectClauses) SetSelect(cl ColumnListExpression) SelectClauses {
ret := c.clone()
ret.selectColumns = cl
return ret
}
func (c *selectClauses) Distinct() ColumnListExpression {
return c.distinct
}
func (c *selectClauses) SetDistinct(cle ColumnListExpression) SelectClauses {
ret := c.clone()
ret.distinct = cle
return ret
}
func (c *selectClauses) From() ColumnListExpression {
return c.from
}
func (c *selectClauses) SetFrom(cl ColumnListExpression) SelectClauses {
ret := c.clone()
ret.from = cl
return ret
}
func (c *selectClauses) HasAlias() bool {
return c.alias != nil
}
func (c *selectClauses) Alias() IdentifierExpression {
return c.alias
}
func (c *selectClauses) SetAlias(ie IdentifierExpression) SelectClauses {
ret := c.clone()
ret.alias = ie
return ret
}
func (c *selectClauses) Joins() JoinExpressions {
return c.joins
}
func (c *selectClauses) JoinsAppend(jc JoinExpression) SelectClauses {
ret := c.clone()
ret.joins = append(ret.joins, jc)
return ret
}
func (c *selectClauses) Where() ExpressionList {
return c.where
}
func (c *selectClauses) ClearWhere() SelectClauses {
ret := c.clone()
ret.where = nil
return ret
}
func (c *selectClauses) WhereAppend(expressions ...Expression) SelectClauses {
if len(expressions) == 0 {
return c
}
ret := c.clone()
if ret.where == nil {
ret.where = NewExpressionList(AndType, expressions...)
} else {
ret.where = ret.where.Append(expressions...)
}
return ret
}
func (c *selectClauses) Having() ExpressionList {
return c.having
}
func (c *selectClauses) ClearHaving() SelectClauses {
ret := c.clone()
ret.having = nil
return ret
}
func (c *selectClauses) HavingAppend(expressions ...Expression) SelectClauses {
if len(expressions) == 0 {
return c
}
ret := c.clone()
if ret.having == nil {
ret.having = NewExpressionList(AndType, expressions...)
} else {
ret.having = ret.having.Append(expressions...)
}
return ret
}
func (c *selectClauses) Lock() Lock {
return c.lock
}
func (c *selectClauses) SetLock(l Lock) SelectClauses {
ret := c.clone()
ret.lock = l
return ret
}
func (c *selectClauses) Order() ColumnListExpression {
return c.order
}
func (c *selectClauses) HasOrder() bool {
return c.order != nil
}
func (c *selectClauses) ClearOrder() SelectClauses {
ret := c.clone()
ret.order = nil
return ret
}
func (c *selectClauses) SetOrder(oes ...OrderedExpression) SelectClauses {
ret := c.clone()
ret.order = NewOrderedColumnList(oes...)
return ret
}
func (c *selectClauses) OrderAppend(oes ...OrderedExpression) SelectClauses {
if c.order == nil {
return c.SetOrder(oes...)
}
ret := c.clone()
ret.order = ret.order.Append(NewOrderedColumnList(oes...).Columns()...)
return ret
}
func (c *selectClauses) OrderPrepend(oes ...OrderedExpression) SelectClauses {
if c.order == nil {
return c.SetOrder(oes...)
}
ret := c.clone()
ret.order = NewOrderedColumnList(oes...).Append(ret.order.Columns()...)
return ret
}
func (c *selectClauses) GroupBy() ColumnListExpression {
return c.groupBy
}
func (c *selectClauses) GroupByAppend(cl ColumnListExpression) SelectClauses {
if c.groupBy == nil {
return c.SetGroupBy(cl)
}
ret := c.clone()
ret.groupBy = ret.groupBy.Append(cl.Columns()...)
return ret
}
func (c *selectClauses) SetGroupBy(cl ColumnListExpression) SelectClauses {
ret := c.clone()
ret.groupBy = cl
return ret
}
func (c *selectClauses) Limit() interface{} {
return c.limit
}
func (c *selectClauses) HasLimit() bool {
return c.limit != nil
}
func (c *selectClauses) ClearLimit() SelectClauses {
ret := c.clone()
ret.limit = nil
return ret
}
func (c *selectClauses) SetLimit(limit interface{}) SelectClauses {
ret := c.clone()
ret.limit = limit
return ret
}
func (c *selectClauses) Offset() uint {
return c.offset
}
func (c *selectClauses) ClearOffset() SelectClauses {
ret := c.clone()
ret.offset = 0
return ret
}
func (c *selectClauses) SetOffset(offset uint) SelectClauses {
ret := c.clone()
ret.offset = offset
return ret
}
func (c *selectClauses) Compounds() []CompoundExpression {
return c.compounds
}
func (c *selectClauses) CompoundsAppend(ce CompoundExpression) SelectClauses {
ret := c.clone()
ret.compounds = append(ret.compounds, ce)
return ret
}
func (c *selectClauses) Windows() []WindowExpression {
return c.windows
}
func (c *selectClauses) SetWindows(ws []WindowExpression) SelectClauses {
ret := c.clone()
ret.windows = ws
return ret
}
func (c *selectClauses) WindowsAppend(ws ...WindowExpression) SelectClauses {
ret := c.clone()
ret.windows = append(ret.windows, ws...)
return ret
}
func (c *selectClauses) ClearWindows() SelectClauses {
ret := c.clone()
ret.windows = nil
return ret
}

11
vendor/github.com/doug-martin/goqu/v9/exp/truncate.go generated vendored Normal file
View File

@@ -0,0 +1,11 @@
package exp
// Options to use when generating a TRUNCATE statement
type TruncateOptions struct {
// Set to true to add CASCADE to the TRUNCATE statement
Cascade bool
// Set to true to add RESTRICT to the TRUNCATE statement
Restrict bool
// Set to true to specify IDENTITY options, (e.g. RESTART, CONTINUE) to the TRUNCATE statement
Identity string
}

View File

@@ -0,0 +1,50 @@
package exp
type (
TruncateClauses interface {
HasTable() bool
clone() *truncateClauses
Table() ColumnListExpression
SetTable(tables ColumnListExpression) TruncateClauses
Options() TruncateOptions
SetOptions(opts TruncateOptions) TruncateClauses
}
truncateClauses struct {
tables ColumnListExpression
options TruncateOptions
}
)
func NewTruncateClauses() TruncateClauses {
return &truncateClauses{}
}
func (tc *truncateClauses) HasTable() bool {
return tc.tables != nil
}
func (tc *truncateClauses) clone() *truncateClauses {
return &truncateClauses{
tables: tc.tables,
}
}
func (tc *truncateClauses) Table() ColumnListExpression {
return tc.tables
}
func (tc *truncateClauses) SetTable(tables ColumnListExpression) TruncateClauses {
ret := tc.clone()
ret.tables = tables
return ret
}
func (tc *truncateClauses) Options() TruncateOptions {
return tc.options
}
func (tc *truncateClauses) SetOptions(opts TruncateOptions) TruncateClauses {
ret := tc.clone()
ret.options = opts
return ret
}

69
vendor/github.com/doug-martin/goqu/v9/exp/update.go generated vendored Normal file
View File

@@ -0,0 +1,69 @@
package exp
import (
"reflect"
"sort"
"github.com/doug-martin/goqu/v9/internal/errors"
"github.com/doug-martin/goqu/v9/internal/util"
)
type (
update struct {
col IdentifierExpression
val interface{}
}
)
func set(col IdentifierExpression, val interface{}) UpdateExpression {
return update{col: col, val: val}
}
func NewUpdateExpressions(update interface{}) (updates []UpdateExpression, err error) {
if u, ok := update.(UpdateExpression); ok {
updates = append(updates, u)
return updates, nil
}
updateValue := reflect.Indirect(reflect.ValueOf(update))
switch updateValue.Kind() {
case reflect.Map:
keys := util.ValueSlice(updateValue.MapKeys())
sort.Sort(keys)
for _, key := range keys {
updates = append(updates, ParseIdentifier(key.String()).Set(updateValue.MapIndex(key).Interface()))
}
case reflect.Struct:
return getUpdateExpressionsStruct(updateValue)
default:
return nil, errors.New("unsupported update interface type %+v", updateValue.Type())
}
return updates, nil
}
func getUpdateExpressionsStruct(value reflect.Value) (updates []UpdateExpression, err error) {
r, err := NewRecordFromStruct(value.Interface(), false, true)
if err != nil {
return updates, err
}
cols := r.Cols()
for _, col := range cols {
updates = append(updates, ParseIdentifier(col).Set(r[col]))
}
return updates, nil
}
func (u update) Expression() Expression {
return u
}
func (u update) Clone() Expression {
return update{col: u.col.Clone().(IdentifierExpression), val: u.val}
}
func (u update) Col() IdentifierExpression {
return u.col
}
func (u update) Val() interface{} {
return u.val
}

View File

@@ -0,0 +1,216 @@
package exp
type (
UpdateClauses interface {
HasTable() bool
clone() *updateClauses
CommonTables() []CommonTableExpression
CommonTablesAppend(cte CommonTableExpression) UpdateClauses
Table() Expression
SetTable(table Expression) UpdateClauses
SetValues() interface{}
HasSetValues() bool
SetSetValues(values interface{}) UpdateClauses
From() ColumnListExpression
HasFrom() bool
SetFrom(tables ColumnListExpression) UpdateClauses
Where() ExpressionList
ClearWhere() UpdateClauses
WhereAppend(expressions ...Expression) UpdateClauses
Order() ColumnListExpression
HasOrder() bool
ClearOrder() UpdateClauses
SetOrder(oes ...OrderedExpression) UpdateClauses
OrderAppend(...OrderedExpression) UpdateClauses
OrderPrepend(...OrderedExpression) UpdateClauses
Limit() interface{}
HasLimit() bool
ClearLimit() UpdateClauses
SetLimit(limit interface{}) UpdateClauses
Returning() ColumnListExpression
HasReturning() bool
SetReturning(cl ColumnListExpression) UpdateClauses
}
updateClauses struct {
commonTables []CommonTableExpression
table Expression
setValues interface{}
from ColumnListExpression
where ExpressionList
order ColumnListExpression
limit interface{}
returning ColumnListExpression
}
)
func NewUpdateClauses() UpdateClauses {
return &updateClauses{}
}
func (uc *updateClauses) HasTable() bool {
return uc.table != nil
}
func (uc *updateClauses) clone() *updateClauses {
return &updateClauses{
commonTables: uc.commonTables,
table: uc.table,
setValues: uc.setValues,
from: uc.from,
where: uc.where,
order: uc.order,
limit: uc.limit,
returning: uc.returning,
}
}
func (uc *updateClauses) CommonTables() []CommonTableExpression {
return uc.commonTables
}
func (uc *updateClauses) CommonTablesAppend(cte CommonTableExpression) UpdateClauses {
ret := uc.clone()
ret.commonTables = append(ret.commonTables, cte)
return ret
}
func (uc *updateClauses) Table() Expression {
return uc.table
}
func (uc *updateClauses) SetTable(table Expression) UpdateClauses {
ret := uc.clone()
ret.table = table
return ret
}
func (uc *updateClauses) SetValues() interface{} {
return uc.setValues
}
func (uc *updateClauses) HasSetValues() bool {
return uc.setValues != nil
}
func (uc *updateClauses) SetSetValues(values interface{}) UpdateClauses {
ret := uc.clone()
ret.setValues = values
return ret
}
func (uc *updateClauses) From() ColumnListExpression {
return uc.from
}
func (uc *updateClauses) HasFrom() bool {
return uc.from != nil && !uc.from.IsEmpty()
}
func (uc *updateClauses) SetFrom(from ColumnListExpression) UpdateClauses {
ret := uc.clone()
ret.from = from
return ret
}
func (uc *updateClauses) Where() ExpressionList {
return uc.where
}
func (uc *updateClauses) ClearWhere() UpdateClauses {
ret := uc.clone()
ret.where = nil
return ret
}
func (uc *updateClauses) WhereAppend(expressions ...Expression) UpdateClauses {
if len(expressions) == 0 {
return uc
}
ret := uc.clone()
if ret.where == nil {
ret.where = NewExpressionList(AndType, expressions...)
} else {
ret.where = ret.where.Append(expressions...)
}
return ret
}
func (uc *updateClauses) Order() ColumnListExpression {
return uc.order
}
func (uc *updateClauses) HasOrder() bool {
return uc.order != nil
}
func (uc *updateClauses) ClearOrder() UpdateClauses {
ret := uc.clone()
ret.order = nil
return ret
}
func (uc *updateClauses) SetOrder(oes ...OrderedExpression) UpdateClauses {
ret := uc.clone()
ret.order = NewOrderedColumnList(oes...)
return ret
}
func (uc *updateClauses) OrderAppend(oes ...OrderedExpression) UpdateClauses {
if uc.order == nil {
return uc.SetOrder(oes...)
}
ret := uc.clone()
ret.order = ret.order.Append(NewOrderedColumnList(oes...).Columns()...)
return ret
}
func (uc *updateClauses) OrderPrepend(oes ...OrderedExpression) UpdateClauses {
if uc.order == nil {
return uc.SetOrder(oes...)
}
ret := uc.clone()
ret.order = NewOrderedColumnList(oes...).Append(ret.order.Columns()...)
return ret
}
func (uc *updateClauses) Limit() interface{} {
return uc.limit
}
func (uc *updateClauses) HasLimit() bool {
return uc.limit != nil
}
func (uc *updateClauses) ClearLimit() UpdateClauses {
ret := uc.clone()
ret.limit = nil
return ret
}
func (uc *updateClauses) SetLimit(limit interface{}) UpdateClauses {
ret := uc.clone()
ret.limit = limit
return ret
}
func (uc *updateClauses) Returning() ColumnListExpression {
return uc.returning
}
func (uc *updateClauses) HasReturning() bool {
return uc.returning != nil && !uc.returning.IsEmpty()
}
func (uc *updateClauses) SetReturning(cl ColumnListExpression) UpdateClauses {
ret := uc.clone()
ret.returning = cl
return ret
}

90
vendor/github.com/doug-martin/goqu/v9/exp/window.go generated vendored Normal file
View File

@@ -0,0 +1,90 @@
package exp
type sqlWindowExpression struct {
name IdentifierExpression
parent IdentifierExpression
partitionCols ColumnListExpression
orderCols ColumnListExpression
}
func NewWindowExpression(window, parent IdentifierExpression, partitionCols, orderCols ColumnListExpression) WindowExpression {
if partitionCols == nil {
partitionCols = NewColumnListExpression()
}
if orderCols == nil {
orderCols = NewColumnListExpression()
}
return sqlWindowExpression{
name: window,
parent: parent,
partitionCols: partitionCols,
orderCols: orderCols,
}
}
func (we sqlWindowExpression) clone() sqlWindowExpression {
return sqlWindowExpression{
name: we.name,
parent: we.parent,
partitionCols: we.partitionCols.Clone().(ColumnListExpression),
orderCols: we.orderCols.Clone().(ColumnListExpression),
}
}
func (we sqlWindowExpression) Clone() Expression {
return we.clone()
}
func (we sqlWindowExpression) Expression() Expression {
return we
}
func (we sqlWindowExpression) Name() IdentifierExpression {
return we.name
}
func (we sqlWindowExpression) HasName() bool {
return we.name != nil
}
func (we sqlWindowExpression) Parent() IdentifierExpression {
return we.parent
}
func (we sqlWindowExpression) HasParent() bool {
return we.parent != nil
}
func (we sqlWindowExpression) PartitionCols() ColumnListExpression {
return we.partitionCols
}
func (we sqlWindowExpression) HasPartitionBy() bool {
return we.partitionCols != nil && !we.partitionCols.IsEmpty()
}
func (we sqlWindowExpression) OrderCols() ColumnListExpression {
return we.orderCols
}
func (we sqlWindowExpression) HasOrder() bool {
return we.orderCols != nil && !we.orderCols.IsEmpty()
}
func (we sqlWindowExpression) PartitionBy(cols ...interface{}) WindowExpression {
ret := we.clone()
ret.partitionCols = NewColumnListExpression(cols...)
return ret
}
func (we sqlWindowExpression) OrderBy(cols ...interface{}) WindowExpression {
ret := we.clone()
ret.orderCols = NewColumnListExpression(cols...)
return ret
}
func (we sqlWindowExpression) Inherit(parent string) WindowExpression {
ret := we.clone()
ret.parent = ParseIdentifier(parent)
return ret
}

View File

@@ -0,0 +1,124 @@
package exp
type sqlWindowFunctionExpression struct {
fn SQLFunctionExpression
windowName IdentifierExpression
window WindowExpression
}
func NewSQLWindowFunctionExpression(
fn SQLFunctionExpression,
windowName IdentifierExpression,
window WindowExpression) SQLWindowFunctionExpression {
return sqlWindowFunctionExpression{
fn: fn,
windowName: windowName,
window: window,
}
}
func (swfe sqlWindowFunctionExpression) clone() sqlWindowFunctionExpression {
return sqlWindowFunctionExpression{
fn: swfe.fn.Clone().(SQLFunctionExpression),
windowName: swfe.windowName,
window: swfe.window,
}
}
func (swfe sqlWindowFunctionExpression) Clone() Expression {
return swfe.clone()
}
func (swfe sqlWindowFunctionExpression) Expression() Expression {
return swfe
}
func (swfe sqlWindowFunctionExpression) As(val interface{}) AliasedExpression {
return NewAliasExpression(swfe, val)
}
func (swfe sqlWindowFunctionExpression) Eq(val interface{}) BooleanExpression { return eq(swfe, val) }
func (swfe sqlWindowFunctionExpression) Neq(val interface{}) BooleanExpression { return neq(swfe, val) }
func (swfe sqlWindowFunctionExpression) Gt(val interface{}) BooleanExpression { return gt(swfe, val) }
func (swfe sqlWindowFunctionExpression) Gte(val interface{}) BooleanExpression { return gte(swfe, val) }
func (swfe sqlWindowFunctionExpression) Lt(val interface{}) BooleanExpression { return lt(swfe, val) }
func (swfe sqlWindowFunctionExpression) Lte(val interface{}) BooleanExpression { return lte(swfe, val) }
func (swfe sqlWindowFunctionExpression) Between(val RangeVal) RangeExpression {
return between(swfe, val)
}
func (swfe sqlWindowFunctionExpression) NotBetween(val RangeVal) RangeExpression {
return notBetween(swfe, val)
}
func (swfe sqlWindowFunctionExpression) Like(val interface{}) BooleanExpression {
return like(swfe, val)
}
func (swfe sqlWindowFunctionExpression) NotLike(val interface{}) BooleanExpression {
return notLike(swfe, val)
}
func (swfe sqlWindowFunctionExpression) ILike(val interface{}) BooleanExpression {
return iLike(swfe, val)
}
func (swfe sqlWindowFunctionExpression) NotILike(val interface{}) BooleanExpression {
return notILike(swfe, val)
}
func (swfe sqlWindowFunctionExpression) RegexpLike(val interface{}) BooleanExpression {
return regexpLike(swfe, val)
}
func (swfe sqlWindowFunctionExpression) RegexpNotLike(val interface{}) BooleanExpression {
return regexpNotLike(swfe, val)
}
func (swfe sqlWindowFunctionExpression) RegexpILike(val interface{}) BooleanExpression {
return regexpILike(swfe, val)
}
func (swfe sqlWindowFunctionExpression) RegexpNotILike(val interface{}) BooleanExpression {
return regexpNotILike(swfe, val)
}
func (swfe sqlWindowFunctionExpression) In(vals ...interface{}) BooleanExpression {
return in(swfe, vals...)
}
func (swfe sqlWindowFunctionExpression) NotIn(vals ...interface{}) BooleanExpression {
return notIn(swfe, vals...)
}
func (swfe sqlWindowFunctionExpression) Is(val interface{}) BooleanExpression { return is(swfe, val) }
func (swfe sqlWindowFunctionExpression) IsNot(val interface{}) BooleanExpression {
return isNot(swfe, val)
}
func (swfe sqlWindowFunctionExpression) IsNull() BooleanExpression { return is(swfe, nil) }
func (swfe sqlWindowFunctionExpression) IsNotNull() BooleanExpression { return isNot(swfe, nil) }
func (swfe sqlWindowFunctionExpression) IsTrue() BooleanExpression { return is(swfe, true) }
func (swfe sqlWindowFunctionExpression) IsNotTrue() BooleanExpression { return isNot(swfe, true) }
func (swfe sqlWindowFunctionExpression) IsFalse() BooleanExpression { return is(swfe, false) }
func (swfe sqlWindowFunctionExpression) IsNotFalse() BooleanExpression { return isNot(swfe, false) }
func (swfe sqlWindowFunctionExpression) Asc() OrderedExpression { return asc(swfe) }
func (swfe sqlWindowFunctionExpression) Desc() OrderedExpression { return desc(swfe) }
func (swfe sqlWindowFunctionExpression) Func() SQLFunctionExpression {
return swfe.fn
}
func (swfe sqlWindowFunctionExpression) Window() WindowExpression {
return swfe.window
}
func (swfe sqlWindowFunctionExpression) WindowName() IdentifierExpression {
return swfe.windowName
}
func (swfe sqlWindowFunctionExpression) HasWindow() bool {
return swfe.window != nil
}
func (swfe sqlWindowFunctionExpression) HasWindowName() bool {
return swfe.windowName != nil
}

303
vendor/github.com/doug-martin/goqu/v9/expressions.go generated vendored Normal file
View File

@@ -0,0 +1,303 @@
package goqu
import (
"github.com/doug-martin/goqu/v9/exp"
)
type (
Expression = exp.Expression
Ex = exp.Ex
ExOr = exp.ExOr
Op = exp.Op
Record = exp.Record
Vals = exp.Vals
// Options to use when generating a TRUNCATE statement
TruncateOptions = exp.TruncateOptions
)
// emptyWindow is an empty WINDOW clause without name
var emptyWindow = exp.NewWindowExpression(nil, nil, nil, nil)
const (
Wait = exp.Wait
NoWait = exp.NoWait
SkipLocked = exp.SkipLocked
)
// Creates a new Casted expression
// Cast(I("a"), "NUMERIC") -> CAST("a" AS NUMERIC)
func Cast(e exp.Expression, t string) exp.CastExpression {
return exp.NewCastExpression(e, t)
}
// Creates a conflict struct to be passed to InsertConflict to ignore constraint errors
// InsertConflict(DoNothing(),...) -> INSERT INTO ... ON CONFLICT DO NOTHING
func DoNothing() exp.ConflictExpression {
return exp.NewDoNothingConflictExpression()
}
// Creates a ConflictUpdate struct to be passed to InsertConflict
// Represents a ON CONFLICT DO UPDATE portion of an INSERT statement (ON DUPLICATE KEY UPDATE for mysql)
//
// InsertConflict(DoUpdate("target_column", update),...) ->
// INSERT INTO ... ON CONFLICT DO UPDATE SET a=b
// InsertConflict(DoUpdate("target_column", update).Where(Ex{"a": 1},...) ->
// INSERT INTO ... ON CONFLICT DO UPDATE SET a=b WHERE a=1
func DoUpdate(target string, update interface{}) exp.ConflictUpdateExpression {
return exp.NewDoUpdateConflictExpression(target, update)
}
// A list of expressions that should be ORed together
// Or(I("a").Eq(10), I("b").Eq(11)) //(("a" = 10) OR ("b" = 11))
func Or(expressions ...exp.Expression) exp.ExpressionList {
return exp.NewExpressionList(exp.OrType, expressions...)
}
// A list of expressions that should be ANDed together
// And(I("a").Eq(10), I("b").Eq(11)) //(("a" = 10) AND ("b" = 11))
func And(expressions ...exp.Expression) exp.ExpressionList {
return exp.NewExpressionList(exp.AndType, expressions...)
}
// Creates a new SQLFunctionExpression with the given name and arguments
func Func(name string, args ...interface{}) exp.SQLFunctionExpression {
return exp.NewSQLFunctionExpression(name, args...)
}
// used internally to normalize the column name if passed in as a string it should be turned into an identifier
func newIdentifierFunc(name string, col interface{}) exp.SQLFunctionExpression {
if s, ok := col.(string); ok {
col = I(s)
}
return Func(name, col)
}
// Creates a new DISTINCT sql function
// DISTINCT("a") -> DISTINCT("a")
// DISTINCT(I("a")) -> DISTINCT("a")
func DISTINCT(col interface{}) exp.SQLFunctionExpression { return newIdentifierFunc("DISTINCT", col) }
// Creates a new COUNT sql function
// COUNT("a") -> COUNT("a")
// COUNT("*") -> COUNT("*")
// COUNT(I("a")) -> COUNT("a")
func COUNT(col interface{}) exp.SQLFunctionExpression { return newIdentifierFunc("COUNT", col) }
// Creates a new MIN sql function
// MIN("a") -> MIN("a")
// MIN(I("a")) -> MIN("a")
func MIN(col interface{}) exp.SQLFunctionExpression { return newIdentifierFunc("MIN", col) }
// Creates a new MAX sql function
// MAX("a") -> MAX("a")
// MAX(I("a")) -> MAX("a")
func MAX(col interface{}) exp.SQLFunctionExpression { return newIdentifierFunc("MAX", col) }
// Creates a new AVG sql function
// AVG("a") -> AVG("a")
// AVG(I("a")) -> AVG("a")
func AVG(col interface{}) exp.SQLFunctionExpression { return newIdentifierFunc("AVG", col) }
// Creates a new FIRST sql function
// FIRST("a") -> FIRST("a")
// FIRST(I("a")) -> FIRST("a")
func FIRST(col interface{}) exp.SQLFunctionExpression { return newIdentifierFunc("FIRST", col) }
// Creates a new LAST sql function
// LAST("a") -> LAST("a")
// LAST(I("a")) -> LAST("a")
func LAST(col interface{}) exp.SQLFunctionExpression { return newIdentifierFunc("LAST", col) }
// Creates a new SUM sql function
// SUM("a") -> SUM("a")
// SUM(I("a")) -> SUM("a")
func SUM(col interface{}) exp.SQLFunctionExpression { return newIdentifierFunc("SUM", col) }
// Creates a new COALESCE sql function
// COALESCE(I("a"), "a") -> COALESCE("a", 'a')
// COALESCE(I("a"), I("b"), nil) -> COALESCE("a", "b", NULL)
func COALESCE(vals ...interface{}) exp.SQLFunctionExpression {
return Func("COALESCE", vals...)
}
//nolint:stylecheck,golint // sql function name
func ROW_NUMBER() exp.SQLFunctionExpression {
return Func("ROW_NUMBER")
}
func RANK() exp.SQLFunctionExpression {
return Func("RANK")
}
//nolint:stylecheck,golint // sql function name
func DENSE_RANK() exp.SQLFunctionExpression {
return Func("DENSE_RANK")
}
//nolint:stylecheck,golint // sql function name
func PERCENT_RANK() exp.SQLFunctionExpression {
return Func("PERCENT_RANK")
}
//nolint:stylecheck,golint //sql function name
func CUME_DIST() exp.SQLFunctionExpression {
return Func("CUME_DIST")
}
func NTILE(n int) exp.SQLFunctionExpression {
return Func("NTILE", n)
}
//nolint:stylecheck,golint //sql function name
func FIRST_VALUE(val interface{}) exp.SQLFunctionExpression {
return newIdentifierFunc("FIRST_VALUE", val)
}
//nolint:stylecheck,golint //sql function name
func LAST_VALUE(val interface{}) exp.SQLFunctionExpression {
return newIdentifierFunc("LAST_VALUE", val)
}
//nolint:stylecheck,golint //sql function name
func NTH_VALUE(val interface{}, nth int) exp.SQLFunctionExpression {
if s, ok := val.(string); ok {
val = I(s)
}
return Func("NTH_VALUE", val, nth)
}
// Creates a new Identifier, the generated sql will use adapter specific quoting or '"' by default, this ensures case
// sensitivity and in certain databases allows for special characters, (e.g. "curr-table", "my table").
//
// The identifier will be split by '.'
//
// Table and Column example
// I("table.column") -> "table"."column" //A Column and table
// Schema table and column
// I("schema.table.column") -> "schema"."table"."column"
// Table with star
// I("table.*") -> "table".*
func I(ident string) exp.IdentifierExpression {
return exp.ParseIdentifier(ident)
}
// Creates a new Column Identifier, the generated sql will use adapter specific quoting or '"' by default, this ensures case
// sensitivity and in certain databases allows for special characters, (e.g. "curr-table", "my table").
// An Identifier can represent a one or a combination of schema, table, and/or column.
// C("column") -> "column" //A Column
// C("column").Table("table") -> "table"."column" //A Column and table
// C("column").Table("table").Schema("schema") //Schema table and column
// C("*") //Also handles the * operator
func C(col string) exp.IdentifierExpression {
return exp.NewIdentifierExpression("", "", col)
}
// Creates a new Schema Identifier, the generated sql will use adapter specific quoting or '"' by default, this ensures case
// sensitivity and in certain databases allows for special characters, (e.g. "curr-schema", "my schema").
// S("schema") -> "schema" //A Schema
// S("schema").Table("table") -> "schema"."table" //A Schema and table
// S("schema").Table("table").Col("col") //Schema table and column
// S("schema").Table("table").Col("*") //Schema table and all columns
func S(schema string) exp.IdentifierExpression {
return exp.NewIdentifierExpression(schema, "", "")
}
// Creates a new Table Identifier, the generated sql will use adapter specific quoting or '"' by default, this ensures case
// sensitivity and in certain databases allows for special characters, (e.g. "curr-table", "my table").
// T("table") -> "table" //A Column
// T("table").Col("col") -> "table"."column" //A Column and table
// T("table").Schema("schema").Col("col) -> "schema"."table"."column" //Schema table and column
// T("table").Schema("schema").Col("*") -> "schema"."table".* //Also handles the * operator
func T(table string) exp.IdentifierExpression {
return exp.NewIdentifierExpression("", table, "")
}
// Create a new WINDOW clause
// W() -> ()
// W().PartitionBy("a") -> (PARTITION BY "a")
// W().PartitionBy("a").OrderBy("b") -> (PARTITION BY "a" ORDER BY "b")
// W().PartitionBy("a").OrderBy("b").Inherit("w1") -> ("w1" PARTITION BY "a" ORDER BY "b")
// W().PartitionBy("a").OrderBy(I("b").Desc()).Inherit("w1") -> ("w1" PARTITION BY "a" ORDER BY "b" DESC)
// W("w") -> "w" AS ()
// W("w", "w1") -> "w" AS ("w1")
// W("w").Inherit("w1") -> "w" AS ("w1")
// W("w").PartitionBy("a") -> "w" AS (PARTITION BY "a")
// W("w", "w1").PartitionBy("a") -> "w" AS ("w1" PARTITION BY "a")
// W("w", "w1").PartitionBy("a").OrderBy("b") -> "w" AS ("w1" PARTITION BY "a" ORDER BY "b")
func W(ws ...string) exp.WindowExpression {
switch len(ws) {
case 0:
return emptyWindow
case 1:
return exp.NewWindowExpression(I(ws[0]), nil, nil, nil)
default:
return exp.NewWindowExpression(I(ws[0]), I(ws[1]), nil, nil)
}
}
// Creates a new ON clause to be used within a join
// ds.Join(goqu.T("my_table"), goqu.On(
// goqu.I("my_table.fkey").Eq(goqu.I("other_table.id")),
// ))
func On(expressions ...exp.Expression) exp.JoinCondition {
return exp.NewJoinOnCondition(expressions...)
}
// Creates a new USING clause to be used within a join
// ds.Join(goqu.T("my_table"), goqu.Using("fkey"))
func Using(columns ...interface{}) exp.JoinCondition {
return exp.NewJoinUsingCondition(columns...)
}
// Creates a new SQL literal with the provided arguments.
// L("a = 1") -> a = 1
// You can also you placeholders. All placeholders within a Literal are represented by '?'
// L("a = ?", "b") -> a = 'b'
// Literals can also contain placeholders for other expressions
// L("(? AND ?) OR (?)", I("a").Eq(1), I("b").Eq("b"), I("c").In([]string{"a", "b", "c"}))
func L(sql string, args ...interface{}) exp.LiteralExpression {
return Literal(sql, args...)
}
// Alias for goqu.L
func Literal(sql string, args ...interface{}) exp.LiteralExpression {
return exp.NewLiteralExpression(sql, args...)
}
// Create a new SQL value ( alias for goqu.L("?", val) ). The prrimary use case for this would be in selects.
// See examples.
func V(val interface{}) exp.LiteralExpression {
return exp.NewLiteralExpression("?", val)
}
// Creates a new Range to be used with a Between expression
// exp.C("col").Between(exp.Range(1, 10))
func Range(start, end interface{}) exp.RangeVal {
return exp.NewRangeVal(start, end)
}
// Creates a literal *
func Star() exp.LiteralExpression { return exp.Star() }
// Returns a literal for DEFAULT sql keyword
func Default() exp.LiteralExpression {
return exp.Default()
}
func Lateral(table exp.AppendableExpression) exp.LateralExpression {
return exp.NewLateralExpression(table)
}
// Create a new ANY comparison
func Any(val interface{}) exp.SQLFunctionExpression {
return Func("ANY ", val)
}
// Create a new ALL comparison
func All(val interface{}) exp.SQLFunctionExpression {
return Func("ALL ", val)
}
func Case() exp.CaseExpression {
return exp.NewCaseExpression()
}

6
vendor/github.com/doug-martin/goqu/v9/go.test.sh generated vendored Normal file
View File

@@ -0,0 +1,6 @@
#!/usr/bin/env bash
set -e
echo "" > coverage.txt
go test -race -coverprofile=coverage.txt -coverpkg=./... ./...

86
vendor/github.com/doug-martin/goqu/v9/goqu.go generated vendored Normal file
View File

@@ -0,0 +1,86 @@
/*
goqu an idiomatch SQL builder, and query package.
__ _ ___ __ _ _ _
/ _` |/ _ \ / _` | | | |
| (_| | (_) | (_| | |_| |
\__, |\___/ \__, |\__,_|
|___/ |_|
Please see https://github.com/doug-martin/goqu for an introduction to goqu.
*/
package goqu
import (
"time"
"github.com/doug-martin/goqu/v9/internal/util"
"github.com/doug-martin/goqu/v9/sqlgen"
)
type DialectWrapper struct {
dialect string
}
// Creates a new DialectWrapper to create goqu.Datasets or goqu.Databases with the specified dialect.
func Dialect(dialect string) DialectWrapper {
return DialectWrapper{dialect: dialect}
}
// Create a new dataset for creating SELECT sql statements
func (dw DialectWrapper) From(table ...interface{}) *SelectDataset {
return From(table...).WithDialect(dw.dialect)
}
// Create a new dataset for creating SELECT sql statements
func (dw DialectWrapper) Select(cols ...interface{}) *SelectDataset {
return newDataset(dw.dialect, nil).Select(cols...)
}
// Create a new dataset for creating UPDATE sql statements
func (dw DialectWrapper) Update(table interface{}) *UpdateDataset {
return Update(table).WithDialect(dw.dialect)
}
// Create a new dataset for creating INSERT sql statements
func (dw DialectWrapper) Insert(table interface{}) *InsertDataset {
return Insert(table).WithDialect(dw.dialect)
}
// Create a new dataset for creating DELETE sql statements
func (dw DialectWrapper) Delete(table interface{}) *DeleteDataset {
return Delete(table).WithDialect(dw.dialect)
}
// Create a new dataset for creating TRUNCATE sql statements
func (dw DialectWrapper) Truncate(table ...interface{}) *TruncateDataset {
return Truncate(table...).WithDialect(dw.dialect)
}
func (dw DialectWrapper) DB(db SQLDatabase) *Database {
return newDatabase(dw.dialect, db)
}
func New(dialect string, db SQLDatabase) *Database {
return newDatabase(dialect, db)
}
// Set the behavior when encountering struct fields that do not have a db tag.
// By default this is false; if set to true any field without a db tag will not
// be targeted by Select or Scan operations.
func SetIgnoreUntaggedFields(ignore bool) {
util.SetIgnoreUntaggedFields(ignore)
}
// Set the column rename function. This is used for struct fields that do not have a db tag to specify the column name
// By default all struct fields that do not have a db tag will be converted lowercase
func SetColumnRenameFunction(renameFunc func(string) string) {
util.SetColumnRenameFunction(renameFunc)
}
// Set the location to use when interpolating time.Time instances. See https://golang.org/pkg/time/#LoadLocation
// NOTE: This has no effect when using prepared statements.
func SetTimeLocation(loc *time.Location) {
sqlgen.SetTimeLocation(loc)
}

271
vendor/github.com/doug-martin/goqu/v9/insert_dataset.go generated vendored Normal file
View File

@@ -0,0 +1,271 @@
package goqu
import (
"fmt"
"github.com/doug-martin/goqu/v9/exec"
"github.com/doug-martin/goqu/v9/exp"
"github.com/doug-martin/goqu/v9/internal/errors"
"github.com/doug-martin/goqu/v9/internal/sb"
)
type InsertDataset struct {
dialect SQLDialect
clauses exp.InsertClauses
isPrepared prepared
queryFactory exec.QueryFactory
err error
}
var ErrUnsupportedIntoType = errors.New("unsupported table type, a string or identifier expression is required")
// used internally by database to create a database with a specific adapter
func newInsertDataset(d string, queryFactory exec.QueryFactory) *InsertDataset {
return &InsertDataset{
clauses: exp.NewInsertClauses(),
dialect: GetDialect(d),
queryFactory: queryFactory,
}
}
// Creates a new InsertDataset for the provided table. Using this method will only allow you
// to create SQL user Database#From to create an InsertDataset with query capabilities
func Insert(table interface{}) *InsertDataset {
return newInsertDataset("default", nil).Into(table)
}
// Set the parameter interpolation behavior. See examples
//
// prepared: If true the dataset WILL NOT interpolate the parameters.
func (id *InsertDataset) Prepared(prepared bool) *InsertDataset {
ret := id.copy(id.clauses)
ret.isPrepared = preparedFromBool(prepared)
return ret
}
func (id *InsertDataset) IsPrepared() bool {
return id.isPrepared.Bool()
}
// Sets the adapter used to serialize values and create the SQL statement
func (id *InsertDataset) WithDialect(dl string) *InsertDataset {
ds := id.copy(id.GetClauses())
ds.dialect = GetDialect(dl)
return ds
}
// Returns the current adapter on the dataset
func (id *InsertDataset) Dialect() SQLDialect {
return id.dialect
}
// Returns the current adapter on the dataset
func (id *InsertDataset) SetDialect(dialect SQLDialect) *InsertDataset {
cd := id.copy(id.GetClauses())
cd.dialect = dialect
return cd
}
func (id *InsertDataset) Expression() exp.Expression {
return id
}
// Clones the dataset
func (id *InsertDataset) Clone() exp.Expression {
return id.copy(id.clauses)
}
// Returns the current clauses on the dataset.
func (id *InsertDataset) GetClauses() exp.InsertClauses {
return id.clauses
}
// used interally to copy the dataset
func (id *InsertDataset) copy(clauses exp.InsertClauses) *InsertDataset {
return &InsertDataset{
dialect: id.dialect,
clauses: clauses,
isPrepared: id.isPrepared,
queryFactory: id.queryFactory,
err: id.err,
}
}
// Creates a WITH clause for a common table expression (CTE).
//
// The name will be available to SELECT from in the associated query; and can optionally
// contain a list of column names "name(col1, col2, col3)".
//
// The name will refer to the results of the specified subquery.
func (id *InsertDataset) With(name string, subquery exp.Expression) *InsertDataset {
return id.copy(id.clauses.CommonTablesAppend(exp.NewCommonTableExpression(false, name, subquery)))
}
// Creates a WITH RECURSIVE clause for a common table expression (CTE)
//
// The name will be available to SELECT from in the associated query; and must
// contain a list of column names "name(col1, col2, col3)" for a recursive clause.
//
// The name will refer to the results of the specified subquery. The subquery for
// a recursive query will always end with a UNION or UNION ALL with a clause that
// refers to the CTE by name.
func (id *InsertDataset) WithRecursive(name string, subquery exp.Expression) *InsertDataset {
return id.copy(id.clauses.CommonTablesAppend(exp.NewCommonTableExpression(true, name, subquery)))
}
// Sets the table to insert INTO. This return a new dataset with the original table replaced. See examples.
// You can pass in the following.
// string: Will automatically be turned into an identifier
// Expression: Any valid expression (IdentifierExpression, AliasedExpression, Literal, etc.)
func (id *InsertDataset) Into(into interface{}) *InsertDataset {
switch t := into.(type) {
case exp.Expression:
return id.copy(id.clauses.SetInto(t))
case string:
return id.copy(id.clauses.SetInto(exp.ParseIdentifier(t)))
default:
panic(ErrUnsupportedIntoType)
}
}
// Sets the Columns to insert into
func (id *InsertDataset) Cols(cols ...interface{}) *InsertDataset {
return id.copy(id.clauses.SetCols(exp.NewColumnListExpression(cols...)))
}
// Clears the Columns to insert into
func (id *InsertDataset) ClearCols() *InsertDataset {
return id.copy(id.clauses.SetCols(nil))
}
// Adds columns to the current list of columns clause. See examples
func (id *InsertDataset) ColsAppend(cols ...interface{}) *InsertDataset {
return id.copy(id.clauses.ColsAppend(exp.NewColumnListExpression(cols...)))
}
// Adds a subquery to the insert. See examples.
func (id *InsertDataset) FromQuery(from exp.AppendableExpression) *InsertDataset {
if sds, ok := from.(*SelectDataset); ok {
if sds.dialect != GetDialect("default") && id.Dialect() != sds.dialect {
panic(
fmt.Errorf(
"incompatible dialects for INSERT (%q) and SELECT (%q)",
id.dialect.Dialect(), sds.dialect.Dialect(),
),
)
}
sds.dialect = id.dialect
}
return id.copy(id.clauses.SetFrom(from))
}
// Manually set values to insert See examples.
func (id *InsertDataset) Vals(vals ...[]interface{}) *InsertDataset {
return id.copy(id.clauses.ValsAppend(vals))
}
// Clears the values. See examples.
func (id *InsertDataset) ClearVals() *InsertDataset {
return id.copy(id.clauses.SetVals(nil))
}
// Insert rows. Rows can be a map, goqu.Record or struct. See examples.
func (id *InsertDataset) Rows(rows ...interface{}) *InsertDataset {
return id.copy(id.clauses.SetRows(rows))
}
// Clears the rows for this insert dataset. See examples.
func (id *InsertDataset) ClearRows() *InsertDataset {
return id.copy(id.clauses.SetRows(nil))
}
// Adds a RETURNING clause to the dataset if the adapter supports it See examples.
func (id *InsertDataset) Returning(returning ...interface{}) *InsertDataset {
return id.copy(id.clauses.SetReturning(exp.NewColumnListExpression(returning...)))
}
// Adds an (ON CONFLICT/ON DUPLICATE KEY) clause to the dataset if the dialect supports it. See examples.
func (id *InsertDataset) OnConflict(conflict exp.ConflictExpression) *InsertDataset {
return id.copy(id.clauses.SetOnConflict(conflict))
}
// Clears the on conflict clause. See example
func (id *InsertDataset) ClearOnConflict() *InsertDataset {
return id.OnConflict(nil)
}
// Get any error that has been set or nil if no error has been set.
func (id *InsertDataset) Error() error {
return id.err
}
// Set an error on the dataset if one has not already been set. This error will be returned by a future call to Error
// or as part of ToSQL. This can be used by end users to record errors while building up queries without having to
// track those separately.
func (id *InsertDataset) SetError(err error) *InsertDataset {
if id.err == nil {
id.err = err
}
return id
}
// Generates the default INSERT statement. If Prepared has been called with true then the statement will not be
// interpolated. See examples. When using structs you may specify a column to be skipped in the insert, (e.g. id) by
// specifying a goqu tag with `skipinsert`
// type Item struct{
// Id uint32 `db:"id" goqu:"skipinsert"`
// Name string `db:"name"`
// }
//
// rows: variable number arguments of either map[string]interface, Record, struct, or a single slice argument of the
// accepted types.
//
// Errors:
// * There is no INTO clause
// * Different row types passed in, all rows must be of the same type
// * Maps with different numbers of K/V pairs
// * Rows of different lengths, (i.e. (Record{"name": "a"}, Record{"name": "a", "age": 10})
// * Error generating SQL
func (id *InsertDataset) ToSQL() (sql string, params []interface{}, err error) {
return id.insertSQLBuilder().ToSQL()
}
// Appends this Dataset's INSERT statement to the SQLBuilder
// This is used internally when using inserts in CTEs
func (id *InsertDataset) AppendSQL(b sb.SQLBuilder) {
if id.err != nil {
b.SetError(id.err)
return
}
id.dialect.ToInsertSQL(b, id.GetClauses())
}
func (id *InsertDataset) GetAs() exp.IdentifierExpression {
return id.clauses.Alias()
}
// Sets the alias for this dataset. This is typically used when using a Dataset as MySQL upsert
func (id *InsertDataset) As(alias string) *InsertDataset {
return id.copy(id.clauses.SetAlias(T(alias)))
}
func (id *InsertDataset) ReturnsColumns() bool {
return id.clauses.HasReturning()
}
// Generates the INSERT sql, and returns an QueryExecutor struct with the sql set to the INSERT statement
// db.Insert("test").Rows(Record{"name":"Bob"}).Executor().Exec()
//
func (id *InsertDataset) Executor() exec.QueryExecutor {
return id.queryFactory.FromSQLBuilder(id.insertSQLBuilder())
}
func (id *InsertDataset) insertSQLBuilder() sb.SQLBuilder {
buf := sb.NewSQLBuilder(id.isPrepared.Bool())
if id.err != nil {
return buf.SetError(id.err)
}
id.dialect.ToInsertSQL(buf, id.clauses)
return buf
}

View File

@@ -0,0 +1,19 @@
package errors
import "fmt"
type Error struct {
err string
}
func New(message string, args ...interface{}) error {
return Error{err: "goqu: " + fmt.Sprintf(message, args...)}
}
func NewEncodeError(t interface{}) error {
return Error{err: "goqu_encode_error: " + fmt.Sprintf("Unable to encode value %+v", t)}
}
func (e Error) Error() string {
return e.err
}

View File

@@ -0,0 +1,101 @@
package sb
import (
"bytes"
)
// Builder that is composed of a bytes.Buffer. It is used internally and by adapters to build SQL statements
type (
SQLBuilder interface {
Error() error
SetError(err error) SQLBuilder
WriteArg(i ...interface{}) SQLBuilder
Write(p []byte) SQLBuilder
WriteStrings(ss ...string) SQLBuilder
WriteRunes(r ...rune) SQLBuilder
IsPrepared() bool
CurrentArgPosition() int
ToSQL() (sql string, args []interface{}, err error)
}
sqlBuilder struct {
buf *bytes.Buffer
// True if the sql should not be interpolated
isPrepared bool
// Current Number of arguments, used by adapters that need positional placeholders
currentArgPosition int
args []interface{}
err error
}
)
func NewSQLBuilder(isPrepared bool) SQLBuilder {
return &sqlBuilder{
buf: &bytes.Buffer{},
isPrepared: isPrepared,
args: make([]interface{}, 0),
currentArgPosition: 1,
}
}
func (b *sqlBuilder) Error() error {
return b.err
}
func (b *sqlBuilder) SetError(err error) SQLBuilder {
if b.err == nil {
b.err = err
}
return b
}
func (b *sqlBuilder) Write(bs []byte) SQLBuilder {
if b.err == nil {
b.buf.Write(bs)
}
return b
}
func (b *sqlBuilder) WriteStrings(ss ...string) SQLBuilder {
if b.err == nil {
for _, s := range ss {
b.buf.WriteString(s)
}
}
return b
}
func (b *sqlBuilder) WriteRunes(rs ...rune) SQLBuilder {
if b.err == nil {
for _, r := range rs {
b.buf.WriteRune(r)
}
}
return b
}
// Returns true if the sql is a prepared statement
func (b *sqlBuilder) IsPrepared() bool {
return b.isPrepared
}
// Returns true if the sql is a prepared statement
func (b *sqlBuilder) CurrentArgPosition() int {
return b.currentArgPosition
}
// Adds an argument to the builder, used when IsPrepared is false
func (b *sqlBuilder) WriteArg(i ...interface{}) SQLBuilder {
if b.err == nil {
b.currentArgPosition += len(i)
b.args = append(b.args, i...)
}
return b
}
// Returns the sql string, and arguments.
func (b *sqlBuilder) ToSQL() (sql string, args []interface{}, err error) {
if b.err != nil {
return sql, args, b.err
}
return b.buf.String(), b.args, nil
}

View File

@@ -0,0 +1,51 @@
package tag
import (
"reflect"
"strings"
)
// tagOptions is the string following a comma in a struct field's "json"
// tag, or the empty string. It does not include the leading comma.
type Options string
func New(tagName string, st reflect.StructTag) Options {
return Options(st.Get(tagName))
}
func (o Options) Values() []string {
if string(o) == "" {
return []string{}
}
return strings.Split(string(o), ",")
}
// Contains reports whether a comma-separated list of options
// contains a particular substr flag. substr must be surrounded by a
// string boundary or commas.
func (o Options) Contains(optionName string) bool {
if o.IsEmpty() {
return false
}
values := o.Values()
for _, s := range values {
if s == optionName {
return true
}
}
return false
}
// Contains reports whether a comma-separated list of options
// contains a particular substr flag. substr must be surrounded by a
// string boundary or commas.
func (o Options) Equals(val string) bool {
if len(o) == 0 {
return false
}
return string(o) == val
}
func (o Options) IsEmpty() bool {
return len(o) == 0
}

View File

@@ -0,0 +1,130 @@
package util
import (
"reflect"
"sort"
"strings"
"github.com/doug-martin/goqu/v9/internal/tag"
)
type (
ColumnData struct {
ColumnName string
FieldIndex []int
ShouldInsert bool
ShouldUpdate bool
DefaultIfEmpty bool
GoType reflect.Type
}
ColumnMap map[string]ColumnData
)
func newColumnMap(t reflect.Type, fieldIndex []int, prefixes []string) ColumnMap {
cm, n := ColumnMap{}, t.NumField()
var subColMaps []ColumnMap
for i := 0; i < n; i++ {
f := t.Field(i)
if f.Anonymous && (f.Type.Kind() == reflect.Struct || f.Type.Kind() == reflect.Ptr) {
goquTag := tag.New("db", f.Tag)
if !goquTag.Contains("-") {
subColMaps = append(subColMaps, getStructColumnMap(&f, fieldIndex, goquTag.Values(), prefixes))
}
} else if f.PkgPath == "" {
dbTag := tag.New("db", f.Tag)
// if PkgPath is empty then it is an exported field
columnName := getColumnName(&f, dbTag)
if !shouldIgnoreField(dbTag) {
if !implementsScanner(f.Type) {
subCm := getStructColumnMap(&f, fieldIndex, []string{columnName}, prefixes)
if len(subCm) != 0 {
subColMaps = append(subColMaps, subCm)
continue
}
}
goquTag := tag.New("goqu", f.Tag)
columnName = strings.Join(append(prefixes, columnName), ".")
cm[columnName] = newColumnData(&f, columnName, fieldIndex, goquTag)
}
}
}
return cm.Merge(subColMaps)
}
func (cm ColumnMap) Cols() []string {
structCols := make([]string, 0, len(cm))
for key := range cm {
structCols = append(structCols, key)
}
sort.Strings(structCols)
return structCols
}
func (cm ColumnMap) Merge(colMaps []ColumnMap) ColumnMap {
for _, subCm := range colMaps {
for key, val := range subCm {
if _, ok := cm[key]; !ok {
cm[key] = val
}
}
}
return cm
}
func implementsScanner(t reflect.Type) bool {
if IsPointer(t.Kind()) {
t = t.Elem()
}
if reflect.PtrTo(t).Implements(scannerType) {
return true
}
if !IsStruct(t.Kind()) {
return true
}
return false
}
func newColumnData(f *reflect.StructField, columnName string, fieldIndex []int, goquTag tag.Options) ColumnData {
return ColumnData{
ColumnName: columnName,
ShouldInsert: !goquTag.Contains(skipInsertTagName),
ShouldUpdate: !goquTag.Contains(skipUpdateTagName),
DefaultIfEmpty: goquTag.Contains(defaultIfEmptyTagName),
FieldIndex: concatFieldIndexes(fieldIndex, f.Index),
GoType: f.Type,
}
}
func getStructColumnMap(f *reflect.StructField, fieldIndex []int, fieldNames, prefixes []string) ColumnMap {
subFieldIndexes := concatFieldIndexes(fieldIndex, f.Index)
subPrefixes := append(prefixes, fieldNames...)
if f.Type.Kind() == reflect.Ptr {
return newColumnMap(f.Type.Elem(), subFieldIndexes, subPrefixes)
}
return newColumnMap(f.Type, subFieldIndexes, subPrefixes)
}
func getColumnName(f *reflect.StructField, dbTag tag.Options) string {
if dbTag.IsEmpty() {
return columnRenameFunction(f.Name)
}
return dbTag.Values()[0]
}
func shouldIgnoreField(dbTag tag.Options) bool {
if dbTag.Equals("-") {
return true
} else if dbTag.IsEmpty() && ignoreUntaggedFields {
return true
}
return false
}
// safely concat two fieldIndex slices into one.
func concatFieldIndexes(fieldIndexPath, fieldIndex []int) []int {
fieldIndexes := make([]int, 0, len(fieldIndexPath)+len(fieldIndex))
fieldIndexes = append(fieldIndexes, fieldIndexPath...)
return append(fieldIndexes, fieldIndex...)
}

View File

@@ -0,0 +1,220 @@
package util
import (
"database/sql"
"reflect"
"strings"
"sync"
"github.com/doug-martin/goqu/v9/internal/errors"
)
const (
skipUpdateTagName = "skipupdate"
skipInsertTagName = "skipinsert"
defaultIfEmptyTagName = "defaultifempty"
)
var scannerType = reflect.TypeOf((*sql.Scanner)(nil)).Elem()
func IsUint(k reflect.Kind) bool {
return (k == reflect.Uint) ||
(k == reflect.Uint8) ||
(k == reflect.Uint16) ||
(k == reflect.Uint32) ||
(k == reflect.Uint64)
}
func IsInt(k reflect.Kind) bool {
return (k == reflect.Int) ||
(k == reflect.Int8) ||
(k == reflect.Int16) ||
(k == reflect.Int32) ||
(k == reflect.Int64)
}
func IsFloat(k reflect.Kind) bool {
return (k == reflect.Float32) ||
(k == reflect.Float64)
}
func IsString(k reflect.Kind) bool {
return k == reflect.String
}
func IsBool(k reflect.Kind) bool {
return k == reflect.Bool
}
func IsSlice(k reflect.Kind) bool {
return k == reflect.Slice
}
func IsStruct(k reflect.Kind) bool {
return k == reflect.Struct
}
func IsInvalid(k reflect.Kind) bool {
return k == reflect.Invalid
}
func IsPointer(k reflect.Kind) bool {
return k == reflect.Ptr
}
func IsEmptyValue(v reflect.Value) bool {
switch v.Kind() {
case reflect.Array, reflect.Map, reflect.Slice, reflect.String:
return v.Len() == 0
case reflect.Bool:
return !v.Bool()
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
return v.Int() == 0
case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr:
return v.Uint() == 0
case reflect.Float32, reflect.Float64:
return v.Float() == 0
case reflect.Interface, reflect.Ptr:
return v.IsNil()
case reflect.Invalid:
return true
default:
return false
}
}
var (
structMapCache = make(map[interface{}]ColumnMap)
structMapCacheLock = sync.Mutex{}
)
var (
DefaultColumnRenameFunction = strings.ToLower
columnRenameFunction = DefaultColumnRenameFunction
ignoreUntaggedFields = false
)
func SetIgnoreUntaggedFields(ignore bool) {
// If the value here is changing, reset the struct map cache
if ignore != ignoreUntaggedFields {
ignoreUntaggedFields = ignore
structMapCacheLock.Lock()
defer structMapCacheLock.Unlock()
structMapCache = make(map[interface{}]ColumnMap)
}
}
func SetColumnRenameFunction(newFunction func(string) string) {
columnRenameFunction = newFunction
}
// GetSliceElementType returns the type for a slices elements.
func GetSliceElementType(val reflect.Value) reflect.Type {
elemType := val.Type().Elem()
if elemType.Kind() == reflect.Ptr {
elemType = elemType.Elem()
}
return elemType
}
// AppendSliceElement will append val to slice. Handles slice of pointers and
// not pointers. Val needs to be a pointer.
func AppendSliceElement(slice, val reflect.Value) {
if slice.Type().Elem().Kind() == reflect.Ptr {
slice.Set(reflect.Append(slice, val))
} else {
slice.Set(reflect.Append(slice, reflect.Indirect(val)))
}
}
func GetTypeInfo(i interface{}, val reflect.Value) (reflect.Type, reflect.Kind) {
var t reflect.Type
valKind := val.Kind()
if valKind == reflect.Slice {
if reflect.ValueOf(i).Kind() == reflect.Ptr {
t = reflect.TypeOf(i).Elem().Elem()
} else {
t = reflect.TypeOf(i).Elem()
}
if t.Kind() == reflect.Ptr {
t = t.Elem()
}
valKind = t.Kind()
} else {
t = val.Type()
}
return t, valKind
}
func SafeGetFieldByIndex(v reflect.Value, fieldIndex []int) (result reflect.Value, isAvailable bool) {
switch len(fieldIndex) {
case 0:
return v, true
case 1:
return v.FieldByIndex(fieldIndex), true
default:
if f := reflect.Indirect(v.Field(fieldIndex[0])); f.IsValid() {
return SafeGetFieldByIndex(f, fieldIndex[1:])
}
}
return reflect.ValueOf(nil), false
}
func SafeSetFieldByIndex(v reflect.Value, fieldIndex []int, src interface{}) (result reflect.Value) {
v = reflect.Indirect(v)
switch len(fieldIndex) {
case 0:
return v
case 1:
f := v.FieldByIndex(fieldIndex)
srcVal := reflect.ValueOf(src)
f.Set(reflect.Indirect(srcVal))
default:
f := v.Field(fieldIndex[0])
switch f.Kind() {
case reflect.Ptr:
s := f
if f.IsNil() || !f.IsValid() {
s = reflect.New(f.Type().Elem())
f.Set(s)
}
SafeSetFieldByIndex(reflect.Indirect(s), fieldIndex[1:], src)
case reflect.Struct:
SafeSetFieldByIndex(f, fieldIndex[1:], src)
default: // use the original value
}
}
return v
}
type rowData = map[string]interface{}
// AssignStructVals will assign the data from rd to i.
func AssignStructVals(i interface{}, rd rowData, cm ColumnMap) {
val := reflect.Indirect(reflect.ValueOf(i))
for name, data := range cm {
src, ok := rd[name]
if ok {
SafeSetFieldByIndex(val, data.FieldIndex, src)
}
}
}
func GetColumnMap(i interface{}) (ColumnMap, error) {
val := reflect.Indirect(reflect.ValueOf(i))
t, valKind := GetTypeInfo(i, val)
if valKind != reflect.Struct {
return nil, errors.New("cannot scan into this type: %v", t) // #nosec
}
structMapCacheLock.Lock()
defer structMapCacheLock.Unlock()
if _, ok := structMapCache[t]; !ok {
structMapCache[t] = newColumnMap(t, []int{}, []string{})
}
return structMapCache[t], nil
}

View File

@@ -0,0 +1,33 @@
package util
import (
"fmt"
"reflect"
"sort"
"strings"
)
type ValueSlice []reflect.Value
func (vs ValueSlice) Len() int { return len(vs) }
func (vs ValueSlice) Less(i, j int) bool { return vs[i].String() < vs[j].String() }
func (vs ValueSlice) Swap(i, j int) { vs[i], vs[j] = vs[j], vs[i] }
func (vs ValueSlice) Equal(other ValueSlice) bool {
sort.Sort(other)
for i, key := range vs {
if other[i].String() != key.String() {
return false
}
}
return true
}
func (vs ValueSlice) String() string {
vals := make([]string, vs.Len())
for i, key := range vs {
vals[i] = fmt.Sprintf(`"%s"`, key.String())
}
sort.Strings(vals)
return fmt.Sprintf("[%s]", strings.Join(vals, ","))
}

48
vendor/github.com/doug-martin/goqu/v9/prepared.go generated vendored Normal file
View File

@@ -0,0 +1,48 @@
package goqu
var (
// defaultPrepared is controlled by SetDefaultPrepared
defaultPrepared bool
)
type prepared int
const (
// zero value that defers to defaultPrepared
preparedNoPreference prepared = iota
// explicitly enabled via Prepared(true) on a dataset
preparedEnabled
// explicitly disabled via Prepared(false) on a dataset
preparedDisabled
)
// Bool converts the ternary prepared state into a boolean. If the prepared
// state is preparedNoPreference, the value depends on the last value that
// SetDefaultPrepared was called with which is false by default.
func (p prepared) Bool() bool {
if p == preparedNoPreference {
return defaultPrepared
} else if p == preparedEnabled {
return true
}
return false
}
// preparedFromBool converts a bool from e.g. Prepared(true) into a prepared
// const.
func preparedFromBool(prepared bool) prepared {
if prepared {
return preparedEnabled
}
return preparedDisabled
}
// SetDefaultPrepared controls the default Prepared state of all datasets. If
// set to true, any new dataset will use prepared queries by default.
func SetDefaultPrepared(prepared bool) {
defaultPrepared = prepared
}

695
vendor/github.com/doug-martin/goqu/v9/select_dataset.go generated vendored Normal file
View File

@@ -0,0 +1,695 @@
package goqu
import (
"context"
"fmt"
"github.com/doug-martin/goqu/v9/exec"
"github.com/doug-martin/goqu/v9/exp"
"github.com/doug-martin/goqu/v9/internal/errors"
"github.com/doug-martin/goqu/v9/internal/sb"
)
// Dataset for creating and/or executing SELECT SQL statements.
type SelectDataset struct {
dialect SQLDialect
clauses exp.SelectClauses
isPrepared prepared
queryFactory exec.QueryFactory
err error
}
var ErrQueryFactoryNotFoundError = errors.New(
"unable to execute query did you use goqu.Database#From to create the dataset",
)
// used internally by database to create a database with a specific adapter
func newDataset(d string, queryFactory exec.QueryFactory) *SelectDataset {
return &SelectDataset{
clauses: exp.NewSelectClauses(),
dialect: GetDialect(d),
queryFactory: queryFactory,
}
}
func From(table ...interface{}) *SelectDataset {
return newDataset("default", nil).From(table...)
}
func Select(cols ...interface{}) *SelectDataset {
return newDataset("default", nil).Select(cols...)
}
// Sets the adapter used to serialize values and create the SQL statement
func (sd *SelectDataset) WithDialect(dl string) *SelectDataset {
ds := sd.copy(sd.GetClauses())
ds.dialect = GetDialect(dl)
return ds
}
// Set the parameter interpolation behavior. See examples
//
// prepared: If true the dataset WILL NOT interpolate the parameters.
func (sd *SelectDataset) Prepared(prepared bool) *SelectDataset {
ret := sd.copy(sd.clauses)
ret.isPrepared = preparedFromBool(prepared)
return ret
}
func (sd *SelectDataset) IsPrepared() bool {
return sd.isPrepared.Bool()
}
// Returns the current adapter on the dataset
func (sd *SelectDataset) Dialect() SQLDialect {
return sd.dialect
}
// Returns the current adapter on the dataset
func (sd *SelectDataset) SetDialect(dialect SQLDialect) *SelectDataset {
cd := sd.copy(sd.GetClauses())
cd.dialect = dialect
return cd
}
func (sd *SelectDataset) Expression() exp.Expression {
return sd
}
// Clones the dataset
func (sd *SelectDataset) Clone() exp.Expression {
return sd.copy(sd.clauses)
}
// Returns the current clauses on the dataset.
func (sd *SelectDataset) GetClauses() exp.SelectClauses {
return sd.clauses
}
// used interally to copy the dataset
func (sd *SelectDataset) copy(clauses exp.SelectClauses) *SelectDataset {
return &SelectDataset{
dialect: sd.dialect,
clauses: clauses,
isPrepared: sd.isPrepared,
queryFactory: sd.queryFactory,
err: sd.err,
}
}
// Creates a new UpdateDataset using the FROM of this dataset. This method will also copy over the `WITH`, `WHERE`,
// `ORDER , and `LIMIT`
func (sd *SelectDataset) Update() *UpdateDataset {
u := newUpdateDataset(sd.dialect.Dialect(), sd.queryFactory).
Prepared(sd.isPrepared.Bool())
if sd.clauses.HasSources() {
u = u.Table(sd.GetClauses().From().Columns()[0])
}
c := u.clauses
for _, ce := range sd.clauses.CommonTables() {
c = c.CommonTablesAppend(ce)
}
if sd.clauses.Where() != nil {
c = c.WhereAppend(sd.clauses.Where())
}
if sd.clauses.HasLimit() {
c = c.SetLimit(sd.clauses.Limit())
}
if sd.clauses.HasOrder() {
for _, oe := range sd.clauses.Order().Columns() {
c = c.OrderAppend(oe.(exp.OrderedExpression))
}
}
u.clauses = c
return u
}
// Creates a new InsertDataset using the FROM of this dataset. This method will also copy over the `WITH` clause to the
// insert.
func (sd *SelectDataset) Insert() *InsertDataset {
i := newInsertDataset(sd.dialect.Dialect(), sd.queryFactory).
Prepared(sd.isPrepared.Bool())
if sd.clauses.HasSources() {
i = i.Into(sd.GetClauses().From().Columns()[0])
}
c := i.clauses
for _, ce := range sd.clauses.CommonTables() {
c = c.CommonTablesAppend(ce)
}
i.clauses = c
return i
}
// Creates a new DeleteDataset using the FROM of this dataset. This method will also copy over the `WITH`, `WHERE`,
// `ORDER , and `LIMIT`
func (sd *SelectDataset) Delete() *DeleteDataset {
d := newDeleteDataset(sd.dialect.Dialect(), sd.queryFactory).
Prepared(sd.isPrepared.Bool())
if sd.clauses.HasSources() {
d = d.From(sd.clauses.From().Columns()[0])
}
c := d.clauses
for _, ce := range sd.clauses.CommonTables() {
c = c.CommonTablesAppend(ce)
}
if sd.clauses.Where() != nil {
c = c.WhereAppend(sd.clauses.Where())
}
if sd.clauses.HasLimit() {
c = c.SetLimit(sd.clauses.Limit())
}
if sd.clauses.HasOrder() {
for _, oe := range sd.clauses.Order().Columns() {
c = c.OrderAppend(oe.(exp.OrderedExpression))
}
}
d.clauses = c
return d
}
// Creates a new TruncateDataset using the FROM of this dataset.
func (sd *SelectDataset) Truncate() *TruncateDataset {
td := newTruncateDataset(sd.dialect.Dialect(), sd.queryFactory)
if sd.clauses.HasSources() {
td = td.Table(sd.clauses.From())
}
return td
}
// Creates a WITH clause for a common table expression (CTE).
//
// The name will be available to SELECT from in the associated query; and can optionally
// contain a list of column names "name(col1, col2, col3)".
//
// The name will refer to the results of the specified subquery.
func (sd *SelectDataset) With(name string, subquery exp.Expression) *SelectDataset {
return sd.copy(sd.clauses.CommonTablesAppend(exp.NewCommonTableExpression(false, name, subquery)))
}
// Creates a WITH RECURSIVE clause for a common table expression (CTE)
//
// The name will be available to SELECT from in the associated query; and must
// contain a list of column names "name(col1, col2, col3)" for a recursive clause.
//
// The name will refer to the results of the specified subquery. The subquery for
// a recursive query will always end with a UNION or UNION ALL with a clause that
// refers to the CTE by name.
func (sd *SelectDataset) WithRecursive(name string, subquery exp.Expression) *SelectDataset {
return sd.copy(sd.clauses.CommonTablesAppend(exp.NewCommonTableExpression(true, name, subquery)))
}
// Adds columns to the SELECT clause. See examples
// You can pass in the following.
// string: Will automatically be turned into an identifier
// Dataset: Will use the SQL generated from that Dataset. If the dataset is aliased it will use that alias as the
// column name.
// LiteralExpression: (See Literal) Will use the literal SQL
// SQLFunction: (See Func, MIN, MAX, COUNT....)
// Struct: If passing in an instance of a struct, we will parse the struct for the column names to select.
// See examples
func (sd *SelectDataset) Select(selects ...interface{}) *SelectDataset {
if len(selects) == 0 {
return sd.ClearSelect()
}
return sd.copy(sd.clauses.SetSelect(exp.NewColumnListExpression(selects...)))
}
// Adds columns to the SELECT DISTINCT clause. See examples
// You can pass in the following.
// string: Will automatically be turned into an identifier
// Dataset: Will use the SQL generated from that Dataset. If the dataset is aliased it will use that alias as the
// column name.
// LiteralExpression: (See Literal) Will use the literal SQL
// SQLFunction: (See Func, MIN, MAX, COUNT....)
// Struct: If passing in an instance of a struct, we will parse the struct for the column names to select.
// See examples
// Deprecated: Use Distinct() instead.
func (sd *SelectDataset) SelectDistinct(selects ...interface{}) *SelectDataset {
if len(selects) == 0 {
cleared := sd.ClearSelect()
return cleared.copy(cleared.clauses.SetDistinct(nil))
}
return sd.copy(sd.clauses.SetSelect(exp.NewColumnListExpression(selects...)).SetDistinct(exp.NewColumnListExpression()))
}
// Resets to SELECT *. If the SelectDistinct or Distinct was used the returned Dataset will have the the dataset set to SELECT *.
// See examples.
func (sd *SelectDataset) ClearSelect() *SelectDataset {
return sd.copy(sd.clauses.SetSelect(exp.NewColumnListExpression(exp.Star())).SetDistinct(nil))
}
// Adds columns to the SELECT clause. See examples
// You can pass in the following.
// string: Will automatically be turned into an identifier
// Dataset: Will use the SQL generated from that Dataset. If the dataset is aliased it will use that alias as the
// column name.
// LiteralExpression: (See Literal) Will use the literal SQL
// SQLFunction: (See Func, MIN, MAX, COUNT....)
func (sd *SelectDataset) SelectAppend(selects ...interface{}) *SelectDataset {
return sd.copy(sd.clauses.SelectAppend(exp.NewColumnListExpression(selects...)))
}
func (sd *SelectDataset) Distinct(on ...interface{}) *SelectDataset {
return sd.copy(sd.clauses.SetDistinct(exp.NewColumnListExpression(on...)))
}
// Adds a FROM clause. This return a new dataset with the original sources replaced. See examples.
// You can pass in the following.
// string: Will automatically be turned into an identifier
// Dataset: Will be added as a sub select. If the Dataset is not aliased it will automatically be aliased
// LiteralExpression: (See Literal) Will use the literal SQL
func (sd *SelectDataset) From(from ...interface{}) *SelectDataset {
var sources []interface{}
numSources := 0
for _, source := range from {
if ds, ok := source.(*SelectDataset); ok && !ds.clauses.HasAlias() {
numSources++
sources = append(sources, ds.As(fmt.Sprintf("t%d", numSources)))
} else {
sources = append(sources, source)
}
}
return sd.copy(sd.clauses.SetFrom(exp.NewColumnListExpression(sources...)))
}
// Returns a new Dataset with the current one as an source. If the current Dataset is not aliased (See Dataset#As) then
// it will automatically be aliased. See examples.
func (sd *SelectDataset) FromSelf() *SelectDataset {
return sd.copy(exp.NewSelectClauses()).From(sd)
}
// Alias to InnerJoin. See examples.
func (sd *SelectDataset) Join(table exp.Expression, condition exp.JoinCondition) *SelectDataset {
return sd.InnerJoin(table, condition)
}
// Adds an INNER JOIN clause. See examples.
func (sd *SelectDataset) InnerJoin(table exp.Expression, condition exp.JoinCondition) *SelectDataset {
return sd.joinTable(exp.NewConditionedJoinExpression(exp.InnerJoinType, table, condition))
}
// Adds a FULL OUTER JOIN clause. See examples.
func (sd *SelectDataset) FullOuterJoin(table exp.Expression, condition exp.JoinCondition) *SelectDataset {
return sd.joinTable(exp.NewConditionedJoinExpression(exp.FullOuterJoinType, table, condition))
}
// Adds a RIGHT OUTER JOIN clause. See examples.
func (sd *SelectDataset) RightOuterJoin(table exp.Expression, condition exp.JoinCondition) *SelectDataset {
return sd.joinTable(exp.NewConditionedJoinExpression(exp.RightOuterJoinType, table, condition))
}
// Adds a LEFT OUTER JOIN clause. See examples.
func (sd *SelectDataset) LeftOuterJoin(table exp.Expression, condition exp.JoinCondition) *SelectDataset {
return sd.joinTable(exp.NewConditionedJoinExpression(exp.LeftOuterJoinType, table, condition))
}
// Adds a FULL JOIN clause. See examples.
func (sd *SelectDataset) FullJoin(table exp.Expression, condition exp.JoinCondition) *SelectDataset {
return sd.joinTable(exp.NewConditionedJoinExpression(exp.FullJoinType, table, condition))
}
// Adds a RIGHT JOIN clause. See examples.
func (sd *SelectDataset) RightJoin(table exp.Expression, condition exp.JoinCondition) *SelectDataset {
return sd.joinTable(exp.NewConditionedJoinExpression(exp.RightJoinType, table, condition))
}
// Adds a LEFT JOIN clause. See examples.
func (sd *SelectDataset) LeftJoin(table exp.Expression, condition exp.JoinCondition) *SelectDataset {
return sd.joinTable(exp.NewConditionedJoinExpression(exp.LeftJoinType, table, condition))
}
// Adds a NATURAL JOIN clause. See examples.
func (sd *SelectDataset) NaturalJoin(table exp.Expression) *SelectDataset {
return sd.joinTable(exp.NewUnConditionedJoinExpression(exp.NaturalJoinType, table))
}
// Adds a NATURAL LEFT JOIN clause. See examples.
func (sd *SelectDataset) NaturalLeftJoin(table exp.Expression) *SelectDataset {
return sd.joinTable(exp.NewUnConditionedJoinExpression(exp.NaturalLeftJoinType, table))
}
// Adds a NATURAL RIGHT JOIN clause. See examples.
func (sd *SelectDataset) NaturalRightJoin(table exp.Expression) *SelectDataset {
return sd.joinTable(exp.NewUnConditionedJoinExpression(exp.NaturalRightJoinType, table))
}
// Adds a NATURAL FULL JOIN clause. See examples.
func (sd *SelectDataset) NaturalFullJoin(table exp.Expression) *SelectDataset {
return sd.joinTable(exp.NewUnConditionedJoinExpression(exp.NaturalFullJoinType, table))
}
// Adds a CROSS JOIN clause. See examples.
func (sd *SelectDataset) CrossJoin(table exp.Expression) *SelectDataset {
return sd.joinTable(exp.NewUnConditionedJoinExpression(exp.CrossJoinType, table))
}
// Joins this Datasets table with another
func (sd *SelectDataset) joinTable(join exp.JoinExpression) *SelectDataset {
return sd.copy(sd.clauses.JoinsAppend(join))
}
// Adds a WHERE clause. See examples.
func (sd *SelectDataset) Where(expressions ...exp.Expression) *SelectDataset {
return sd.copy(sd.clauses.WhereAppend(expressions...))
}
// Removes the WHERE clause. See examples.
func (sd *SelectDataset) ClearWhere() *SelectDataset {
return sd.copy(sd.clauses.ClearWhere())
}
// Adds a FOR UPDATE clause. See examples.
func (sd *SelectDataset) ForUpdate(waitOption exp.WaitOption, of ...exp.IdentifierExpression) *SelectDataset {
return sd.withLock(exp.ForUpdate, waitOption, of...)
}
// Adds a FOR NO KEY UPDATE clause. See examples.
func (sd *SelectDataset) ForNoKeyUpdate(waitOption exp.WaitOption, of ...exp.IdentifierExpression) *SelectDataset {
return sd.withLock(exp.ForNoKeyUpdate, waitOption, of...)
}
// Adds a FOR KEY SHARE clause. See examples.
func (sd *SelectDataset) ForKeyShare(waitOption exp.WaitOption, of ...exp.IdentifierExpression) *SelectDataset {
return sd.withLock(exp.ForKeyShare, waitOption, of...)
}
// Adds a FOR SHARE clause. See examples.
func (sd *SelectDataset) ForShare(waitOption exp.WaitOption, of ...exp.IdentifierExpression) *SelectDataset {
return sd.withLock(exp.ForShare, waitOption, of...)
}
func (sd *SelectDataset) withLock(strength exp.LockStrength, option exp.WaitOption, of ...exp.IdentifierExpression) *SelectDataset {
return sd.copy(sd.clauses.SetLock(exp.NewLock(strength, option, of...)))
}
// Adds a GROUP BY clause. See examples.
func (sd *SelectDataset) GroupBy(groupBy ...interface{}) *SelectDataset {
return sd.copy(sd.clauses.SetGroupBy(exp.NewColumnListExpression(groupBy...)))
}
// Adds more columns to the current GROUP BY clause. See examples.
func (sd *SelectDataset) GroupByAppend(groupBy ...interface{}) *SelectDataset {
return sd.copy(sd.clauses.GroupByAppend(exp.NewColumnListExpression(groupBy...)))
}
// Adds a HAVING clause. See examples.
func (sd *SelectDataset) Having(expressions ...exp.Expression) *SelectDataset {
return sd.copy(sd.clauses.HavingAppend(expressions...))
}
// Adds a ORDER clause. If the ORDER is currently set it replaces it. See examples.
func (sd *SelectDataset) Order(order ...exp.OrderedExpression) *SelectDataset {
return sd.copy(sd.clauses.SetOrder(order...))
}
// Adds a more columns to the current ORDER BY clause. If no order has be previously specified it is the same as
// calling Order. See examples.
func (sd *SelectDataset) OrderAppend(order ...exp.OrderedExpression) *SelectDataset {
return sd.copy(sd.clauses.OrderAppend(order...))
}
// Adds a more columns to the beginning of the current ORDER BY clause. If no order has be previously specified it is the same as
// calling Order. See examples.
func (sd *SelectDataset) OrderPrepend(order ...exp.OrderedExpression) *SelectDataset {
return sd.copy(sd.clauses.OrderPrepend(order...))
}
// Removes the ORDER BY clause. See examples.
func (sd *SelectDataset) ClearOrder() *SelectDataset {
return sd.copy(sd.clauses.ClearOrder())
}
// Adds a LIMIT clause. If the LIMIT is currently set it replaces it. See examples.
func (sd *SelectDataset) Limit(limit uint) *SelectDataset {
if limit > 0 {
return sd.copy(sd.clauses.SetLimit(limit))
}
return sd.copy(sd.clauses.ClearLimit())
}
// Adds a LIMIT ALL clause. If the LIMIT is currently set it replaces it. See examples.
func (sd *SelectDataset) LimitAll() *SelectDataset {
return sd.copy(sd.clauses.SetLimit(L("ALL")))
}
// Removes the LIMIT clause.
func (sd *SelectDataset) ClearLimit() *SelectDataset {
return sd.copy(sd.clauses.ClearLimit())
}
// Adds an OFFSET clause. If the OFFSET is currently set it replaces it. See examples.
func (sd *SelectDataset) Offset(offset uint) *SelectDataset {
return sd.copy(sd.clauses.SetOffset(offset))
}
// Removes the OFFSET clause from the Dataset
func (sd *SelectDataset) ClearOffset() *SelectDataset {
return sd.copy(sd.clauses.ClearOffset())
}
// Creates an UNION statement with another dataset.
// If this or the other dataset has a limit or offset it will use that dataset as a subselect in the FROM clause.
// See examples.
func (sd *SelectDataset) Union(other *SelectDataset) *SelectDataset {
return sd.withCompound(exp.UnionCompoundType, other.CompoundFromSelf())
}
// Creates an UNION ALL statement with another dataset.
// If this or the other dataset has a limit or offset it will use that dataset as a subselect in the FROM clause.
// See examples.
func (sd *SelectDataset) UnionAll(other *SelectDataset) *SelectDataset {
return sd.withCompound(exp.UnionAllCompoundType, other.CompoundFromSelf())
}
// Creates an INTERSECT statement with another dataset.
// If this or the other dataset has a limit or offset it will use that dataset as a subselect in the FROM clause.
// See examples.
func (sd *SelectDataset) Intersect(other *SelectDataset) *SelectDataset {
return sd.withCompound(exp.IntersectCompoundType, other.CompoundFromSelf())
}
// Creates an INTERSECT ALL statement with another dataset.
// If this or the other dataset has a limit or offset it will use that dataset as a subselect in the FROM clause.
// See examples.
func (sd *SelectDataset) IntersectAll(other *SelectDataset) *SelectDataset {
return sd.withCompound(exp.IntersectAllCompoundType, other.CompoundFromSelf())
}
func (sd *SelectDataset) withCompound(ct exp.CompoundType, other exp.AppendableExpression) *SelectDataset {
ce := exp.NewCompoundExpression(ct, other)
ret := sd.CompoundFromSelf()
ret.clauses = ret.clauses.CompoundsAppend(ce)
return ret
}
// Used internally to determine if the dataset needs to use iteself as a source.
// If the dataset has an order or limit it will select from itself
func (sd *SelectDataset) CompoundFromSelf() *SelectDataset {
if sd.clauses.HasOrder() || sd.clauses.HasLimit() {
return sd.FromSelf()
}
return sd.copy(sd.clauses)
}
// Sets the alias for this dataset. This is typically used when using a Dataset as a subselect. See examples.
func (sd *SelectDataset) As(alias string) *SelectDataset {
return sd.copy(sd.clauses.SetAlias(T(alias)))
}
// Returns the alias value as an identiier expression
func (sd *SelectDataset) GetAs() exp.IdentifierExpression {
return sd.clauses.Alias()
}
// Sets the WINDOW clauses
func (sd *SelectDataset) Window(ws ...exp.WindowExpression) *SelectDataset {
return sd.copy(sd.clauses.SetWindows(ws))
}
// Sets the WINDOW clauses
func (sd *SelectDataset) WindowAppend(ws ...exp.WindowExpression) *SelectDataset {
return sd.copy(sd.clauses.WindowsAppend(ws...))
}
// Sets the WINDOW clauses
func (sd *SelectDataset) ClearWindow() *SelectDataset {
return sd.copy(sd.clauses.ClearWindows())
}
// Get any error that has been set or nil if no error has been set.
func (sd *SelectDataset) Error() error {
return sd.err
}
// Set an error on the dataset if one has not already been set. This error will be returned by a future call to Error
// or as part of ToSQL. This can be used by end users to record errors while building up queries without having to
// track those separately.
func (sd *SelectDataset) SetError(err error) *SelectDataset {
if sd.err == nil {
sd.err = err
}
return sd
}
// Generates a SELECT sql statement, if Prepared has been called with true then the parameters will not be interpolated.
// See examples.
//
// Errors:
// * There is an error generating the SQL
func (sd *SelectDataset) ToSQL() (sql string, params []interface{}, err error) {
return sd.selectSQLBuilder().ToSQL()
}
// Generates the SELECT sql, and returns an Exec struct with the sql set to the SELECT statement
// db.From("test").Select("col").Executor()
//
// See Dataset#ToUpdateSQL for arguments
func (sd *SelectDataset) Executor() exec.QueryExecutor {
return sd.queryFactory.FromSQLBuilder(sd.selectSQLBuilder())
}
// Appends this Dataset's SELECT statement to the SQLBuilder
// This is used internally for sub-selects by the dialect
func (sd *SelectDataset) AppendSQL(b sb.SQLBuilder) {
if sd.err != nil {
b.SetError(sd.err)
return
}
sd.dialect.ToSelectSQL(b, sd.GetClauses())
}
func (sd *SelectDataset) ReturnsColumns() bool {
return true
}
// Generates the SELECT sql for this dataset and uses Exec#ScanStructs to scan the results into a slice of structs.
//
// ScanStructs will only select the columns that can be scanned in to the struct unless you have explicitly selected
// certain columns. See examples.
//
// i: A pointer to a slice of structs
func (sd *SelectDataset) ScanStructs(i interface{}) error {
return sd.ScanStructsContext(context.Background(), i)
}
// Generates the SELECT sql for this dataset and uses Exec#ScanStructsContext to scan the results into a slice of
// structs.
//
// ScanStructsContext will only select the columns that can be scanned in to the struct unless you have explicitly
// selected certain columns. See examples.
//
// i: A pointer to a slice of structs
func (sd *SelectDataset) ScanStructsContext(ctx context.Context, i interface{}) error {
if sd.queryFactory == nil {
return ErrQueryFactoryNotFoundError
}
ds := sd
if sd.GetClauses().IsDefaultSelect() {
ds = sd.Select(i)
}
return ds.Executor().ScanStructsContext(ctx, i)
}
// Generates the SELECT sql for this dataset and uses Exec#ScanStruct to scan the result into a slice of structs
//
// ScanStruct will only select the columns that can be scanned in to the struct unless you have explicitly selected
// certain columns. See examples.
//
// i: A pointer to a structs
func (sd *SelectDataset) ScanStruct(i interface{}) (bool, error) {
return sd.ScanStructContext(context.Background(), i)
}
// Generates the SELECT sql for this dataset and uses Exec#ScanStructContext to scan the result into a slice of structs
//
// ScanStructContext will only select the columns that can be scanned in to the struct unless you have explicitly
// selected certain columns. See examples.
//
// i: A pointer to a structs
func (sd *SelectDataset) ScanStructContext(ctx context.Context, i interface{}) (bool, error) {
if sd.queryFactory == nil {
return false, ErrQueryFactoryNotFoundError
}
ds := sd
if sd.GetClauses().IsDefaultSelect() {
ds = sd.Select(i)
}
return ds.Limit(1).Executor().ScanStructContext(ctx, i)
}
// Generates the SELECT sql for this dataset and uses Exec#ScanVals to scan the results into a slice of primitive values
//
// i: A pointer to a slice of primitive values
func (sd *SelectDataset) ScanVals(i interface{}) error {
return sd.ScanValsContext(context.Background(), i)
}
// Generates the SELECT sql for this dataset and uses Exec#ScanValsContext to scan the results into a slice of primitive
// values
//
// i: A pointer to a slice of primitive values
func (sd *SelectDataset) ScanValsContext(ctx context.Context, i interface{}) error {
if sd.queryFactory == nil {
return ErrQueryFactoryNotFoundError
}
return sd.Executor().ScanValsContext(ctx, i)
}
// Generates the SELECT sql for this dataset and uses Exec#ScanVal to scan the result into a primitive value
//
// i: A pointer to a primitive value
func (sd *SelectDataset) ScanVal(i interface{}) (bool, error) {
return sd.ScanValContext(context.Background(), i)
}
// Generates the SELECT sql for this dataset and uses Exec#ScanValContext to scan the result into a primitive value
//
// i: A pointer to a primitive value
func (sd *SelectDataset) ScanValContext(ctx context.Context, i interface{}) (bool, error) {
if sd.queryFactory == nil {
return false, ErrQueryFactoryNotFoundError
}
return sd.Limit(1).Executor().ScanValContext(ctx, i)
}
// Generates the SELECT COUNT(*) sql for this dataset and uses Exec#ScanVal to scan the result into an int64.
func (sd *SelectDataset) Count() (int64, error) {
return sd.CountContext(context.Background())
}
// Generates the SELECT COUNT(*) sql for this dataset and uses Exec#ScanValContext to scan the result into an int64.
func (sd *SelectDataset) CountContext(ctx context.Context) (int64, error) {
var count int64
_, err := sd.Select(COUNT(Star()).As("count")).ScanValContext(ctx, &count)
return count, err
}
// Generates the SELECT sql only selecting the passed in column and uses Exec#ScanVals to scan the result into a slice
// of primitive values.
//
// i: A slice of primitive values
//
// col: The column to select when generative the SQL
func (sd *SelectDataset) Pluck(i interface{}, col string) error {
return sd.PluckContext(context.Background(), i, col)
}
// Generates the SELECT sql only selecting the passed in column and uses Exec#ScanValsContext to scan the result into a
// slice of primitive values.
//
// i: A slice of primitive values
//
// col: The column to select when generative the SQL
func (sd *SelectDataset) PluckContext(ctx context.Context, i interface{}, col string) error {
return sd.Select(col).ScanValsContext(ctx, i)
}
func (sd *SelectDataset) selectSQLBuilder() sb.SQLBuilder {
buf := sb.NewSQLBuilder(sd.isPrepared.Bool())
if sd.err != nil {
return buf.SetError(sd.err)
}
sd.dialect.ToSelectSQL(buf, sd.GetClauses())
return buf
}

103
vendor/github.com/doug-martin/goqu/v9/sql_dialect.go generated vendored Normal file
View File

@@ -0,0 +1,103 @@
package goqu
import (
"strings"
"sync"
"github.com/doug-martin/goqu/v9/exp"
"github.com/doug-martin/goqu/v9/internal/sb"
"github.com/doug-martin/goqu/v9/sqlgen"
)
type (
SQLDialectOptions = sqlgen.SQLDialectOptions
// An adapter interface to be used by a Dataset to generate SQL for a specific dialect.
// See DefaultAdapter for a concrete implementation and examples.
SQLDialect interface {
Dialect() string
ToSelectSQL(b sb.SQLBuilder, clauses exp.SelectClauses)
ToUpdateSQL(b sb.SQLBuilder, clauses exp.UpdateClauses)
ToInsertSQL(b sb.SQLBuilder, clauses exp.InsertClauses)
ToDeleteSQL(b sb.SQLBuilder, clauses exp.DeleteClauses)
ToTruncateSQL(b sb.SQLBuilder, clauses exp.TruncateClauses)
}
// The default adapter. This class should be used when building a new adapter. When creating a new adapter you can
// either override methods, or more typically update default values.
// See (github.com/doug-martin/goqu/dialect/postgres)
sqlDialect struct {
dialect string
dialectOptions *SQLDialectOptions
selectGen sqlgen.SelectSQLGenerator
updateGen sqlgen.UpdateSQLGenerator
insertGen sqlgen.InsertSQLGenerator
deleteGen sqlgen.DeleteSQLGenerator
truncateGen sqlgen.TruncateSQLGenerator
}
)
var (
dialects = make(map[string]SQLDialect)
DefaultDialectOptions = sqlgen.DefaultDialectOptions
dialectsMu sync.RWMutex
)
func init() {
RegisterDialect("default", DefaultDialectOptions())
}
func RegisterDialect(name string, do *SQLDialectOptions) {
dialectsMu.Lock()
defer dialectsMu.Unlock()
lowerName := strings.ToLower(name)
dialects[lowerName] = newDialect(lowerName, do)
}
func DeregisterDialect(name string) {
dialectsMu.Lock()
defer dialectsMu.Unlock()
delete(dialects, strings.ToLower(name))
}
func GetDialect(name string) SQLDialect {
name = strings.ToLower(name)
if d, ok := dialects[name]; ok {
return d
}
return newDialect("default", DefaultDialectOptions())
}
func newDialect(dialect string, do *SQLDialectOptions) SQLDialect {
return &sqlDialect{
dialect: dialect,
dialectOptions: do,
selectGen: sqlgen.NewSelectSQLGenerator(dialect, do),
updateGen: sqlgen.NewUpdateSQLGenerator(dialect, do),
insertGen: sqlgen.NewInsertSQLGenerator(dialect, do),
deleteGen: sqlgen.NewDeleteSQLGenerator(dialect, do),
truncateGen: sqlgen.NewTruncateSQLGenerator(dialect, do),
}
}
func (d *sqlDialect) Dialect() string {
return d.dialect
}
func (d *sqlDialect) ToSelectSQL(b sb.SQLBuilder, clauses exp.SelectClauses) {
d.selectGen.Generate(b, clauses)
}
func (d *sqlDialect) ToUpdateSQL(b sb.SQLBuilder, clauses exp.UpdateClauses) {
d.updateGen.Generate(b, clauses)
}
func (d *sqlDialect) ToInsertSQL(b sb.SQLBuilder, clauses exp.InsertClauses) {
d.insertGen.Generate(b, clauses)
}
func (d *sqlDialect) ToDeleteSQL(b sb.SQLBuilder, clauses exp.DeleteClauses) {
d.deleteGen.Generate(b, clauses)
}
func (d *sqlDialect) ToTruncateSQL(b sb.SQLBuilder, clauses exp.TruncateClauses) {
d.truncateGen.Generate(b, clauses)
}

View File

@@ -0,0 +1,155 @@
package sqlgen
import (
"github.com/doug-martin/goqu/v9/exp"
"github.com/doug-martin/goqu/v9/internal/errors"
"github.com/doug-martin/goqu/v9/internal/sb"
)
var ErrNoUpdatedValuesProvided = errors.New("no update values provided")
func ErrCTENotSupported(dialect string) error {
return errors.New("dialect does not support CTE WITH clause [dialect=%s]", dialect)
}
func ErrRecursiveCTENotSupported(dialect string) error {
return errors.New("dialect does not support CTE WITH RECURSIVE clause [dialect=%s]", dialect)
}
func ErrReturnNotSupported(dialect string) error {
return errors.New("dialect does not support RETURNING clause [dialect=%s]", dialect)
}
func ErrNotSupportedFragment(sqlType string, f SQLFragmentType) error {
return errors.New("unsupported %s SQL fragment %s", sqlType, f)
}
type (
CommonSQLGenerator interface {
Dialect() string
DialectOptions() *SQLDialectOptions
ExpressionSQLGenerator() ExpressionSQLGenerator
ReturningSQL(b sb.SQLBuilder, returns exp.ColumnListExpression)
FromSQL(b sb.SQLBuilder, from exp.ColumnListExpression)
SourcesSQL(b sb.SQLBuilder, from exp.ColumnListExpression)
WhereSQL(b sb.SQLBuilder, where exp.ExpressionList)
OrderSQL(b sb.SQLBuilder, order exp.ColumnListExpression)
OrderWithOffsetFetchSQL(b sb.SQLBuilder, order exp.ColumnListExpression, offset uint, limit interface{})
LimitSQL(b sb.SQLBuilder, limit interface{})
UpdateExpressionSQL(b sb.SQLBuilder, updates ...exp.UpdateExpression)
}
commonSQLGenerator struct {
dialect string
esg ExpressionSQLGenerator
dialectOptions *SQLDialectOptions
}
)
func NewCommonSQLGenerator(dialect string, do *SQLDialectOptions) CommonSQLGenerator {
return &commonSQLGenerator{dialect: dialect, esg: NewExpressionSQLGenerator(dialect, do), dialectOptions: do}
}
func (csg *commonSQLGenerator) Dialect() string {
return csg.dialect
}
func (csg *commonSQLGenerator) DialectOptions() *SQLDialectOptions {
return csg.dialectOptions
}
func (csg *commonSQLGenerator) ExpressionSQLGenerator() ExpressionSQLGenerator {
return csg.esg
}
func (csg *commonSQLGenerator) ReturningSQL(b sb.SQLBuilder, returns exp.ColumnListExpression) {
if returns != nil && len(returns.Columns()) > 0 {
if csg.dialectOptions.SupportsReturn {
b.Write(csg.dialectOptions.ReturningFragment)
csg.esg.Generate(b, returns)
} else {
b.SetError(ErrReturnNotSupported(csg.dialect))
}
}
}
// Adds the FROM clause and tables to an sql statement
func (csg *commonSQLGenerator) FromSQL(b sb.SQLBuilder, from exp.ColumnListExpression) {
if from != nil && !from.IsEmpty() {
b.Write(csg.dialectOptions.FromFragment)
csg.SourcesSQL(b, from)
}
}
// Adds the generates the SQL for a column list
func (csg *commonSQLGenerator) SourcesSQL(b sb.SQLBuilder, from exp.ColumnListExpression) {
b.WriteRunes(csg.dialectOptions.SpaceRune)
csg.esg.Generate(b, from)
}
// Generates the WHERE clause for an SQL statement
func (csg *commonSQLGenerator) WhereSQL(b sb.SQLBuilder, where exp.ExpressionList) {
if where != nil && !where.IsEmpty() {
b.Write(csg.dialectOptions.WhereFragment)
csg.esg.Generate(b, where)
}
}
// Generates the ORDER BY clause for an SQL statement
func (csg *commonSQLGenerator) OrderSQL(b sb.SQLBuilder, order exp.ColumnListExpression) {
if order != nil && len(order.Columns()) > 0 {
b.Write(csg.dialectOptions.OrderByFragment)
csg.esg.Generate(b, order)
}
}
func (csg *commonSQLGenerator) OrderWithOffsetFetchSQL(
b sb.SQLBuilder,
order exp.ColumnListExpression,
offset uint,
limit interface{},
) {
if order == nil {
return
}
csg.OrderSQL(b, order)
if offset > 0 {
b.Write(csg.dialectOptions.OffsetFragment)
csg.esg.Generate(b, offset)
b.Write([]byte(" ROWS"))
if limit != nil {
b.Write(csg.dialectOptions.FetchFragment)
csg.esg.Generate(b, limit)
b.Write([]byte(" ROWS ONLY"))
}
}
}
// Generates the LIMIT clause for an SQL statement
func (csg *commonSQLGenerator) LimitSQL(b sb.SQLBuilder, limit interface{}) {
if limit != nil {
b.Write(csg.dialectOptions.LimitFragment)
if csg.dialectOptions.SurroundLimitWithParentheses {
b.WriteRunes(csg.dialectOptions.LeftParenRune)
}
csg.esg.Generate(b, limit)
if csg.dialectOptions.SurroundLimitWithParentheses {
b.WriteRunes(csg.dialectOptions.RightParenRune)
}
}
}
func (csg *commonSQLGenerator) UpdateExpressionSQL(b sb.SQLBuilder, updates ...exp.UpdateExpression) {
if len(updates) == 0 {
b.SetError(ErrNoUpdatedValuesProvided)
return
}
updateLen := len(updates)
for i, update := range updates {
csg.esg.Generate(b, update)
if i < updateLen-1 {
b.WriteRunes(csg.dialectOptions.CommaRune)
}
}
}

View File

@@ -0,0 +1,72 @@
package sqlgen
import (
"github.com/doug-martin/goqu/v9/exp"
"github.com/doug-martin/goqu/v9/internal/errors"
"github.com/doug-martin/goqu/v9/internal/sb"
)
type (
// An adapter interface to be used by a Dataset to generate SQL for a specific dialect.
// See DefaultAdapter for a concrete implementation and examples.
DeleteSQLGenerator interface {
Dialect() string
Generate(b sb.SQLBuilder, clauses exp.DeleteClauses)
}
// The default adapter. This class should be used when building a new adapter. When creating a new adapter you can
// either override methods, or more typically update default values.
// See (github.com/doug-martin/goqu/dialect/postgres)
deleteSQLGenerator struct {
CommonSQLGenerator
}
)
var ErrNoSourceForDelete = errors.New("no source found when generating delete sql")
func NewDeleteSQLGenerator(dialect string, do *SQLDialectOptions) DeleteSQLGenerator {
return &deleteSQLGenerator{NewCommonSQLGenerator(dialect, do)}
}
func (dsg *deleteSQLGenerator) Generate(b sb.SQLBuilder, clauses exp.DeleteClauses) {
if !clauses.HasFrom() {
b.SetError(ErrNoSourceForDelete)
return
}
for _, f := range dsg.DialectOptions().DeleteSQLOrder {
if b.Error() != nil {
return
}
switch f {
case CommonTableSQLFragment:
dsg.ExpressionSQLGenerator().Generate(b, clauses.CommonTables())
case DeleteBeginSQLFragment:
dsg.DeleteBeginSQL(
b, exp.NewColumnListExpression(clauses.From()), !(clauses.HasLimit() || clauses.HasOrder()),
)
case FromSQLFragment:
dsg.FromSQL(b, exp.NewColumnListExpression(clauses.From()))
case WhereSQLFragment:
dsg.WhereSQL(b, clauses.Where())
case OrderSQLFragment:
if dsg.DialectOptions().SupportsOrderByOnDelete {
dsg.OrderSQL(b, clauses.Order())
}
case LimitSQLFragment:
if dsg.DialectOptions().SupportsLimitOnDelete {
dsg.LimitSQL(b, clauses.Limit())
}
case ReturningSQLFragment:
dsg.ReturningSQL(b, clauses.Returning())
default:
b.SetError(ErrNotSupportedFragment("DELETE", f))
}
}
}
// Adds the correct fragment to being an DELETE statement
func (dsg *deleteSQLGenerator) DeleteBeginSQL(b sb.SQLBuilder, from exp.ColumnListExpression, multiTable bool) {
b.Write(dsg.DialectOptions().DeleteClause)
if multiTable && dsg.DialectOptions().SupportsDeleteTableHint {
dsg.SourcesSQL(b, from)
}
}

View File

@@ -0,0 +1,733 @@
package sqlgen
import (
"database/sql/driver"
"reflect"
"strconv"
"time"
"unicode/utf8"
"github.com/doug-martin/goqu/v9/exp"
"github.com/doug-martin/goqu/v9/internal/errors"
"github.com/doug-martin/goqu/v9/internal/sb"
"github.com/doug-martin/goqu/v9/internal/util"
)
type (
// An adapter interface to be used by a Dataset to generate SQL for a specific dialect.
// See DefaultAdapter for a concrete implementation and examples.
ExpressionSQLGenerator interface {
Dialect() string
Generate(b sb.SQLBuilder, val interface{})
}
// The default adapter. This class should be used when building a new adapter. When creating a new adapter you can
// either override methods, or more typically update default values.
// See (github.com/doug-martin/goqu/dialect/postgres)
expressionSQLGenerator struct {
dialect string
dialectOptions *SQLDialectOptions
}
)
var (
replacementRune = '?'
TrueLiteral = exp.NewLiteralExpression("TRUE")
FalseLiteral = exp.NewLiteralExpression("FALSE")
ErrEmptyIdentifier = errors.New(
`a empty identifier was encountered, please specify a "schema", "table" or "column"`,
)
ErrUnexpectedNamedWindow = errors.New(`unexpected named window function`)
ErrEmptyCaseWhens = errors.New(`when conditions not found for case statement`)
)
func errUnsupportedExpressionType(e exp.Expression) error {
return errors.New("unsupported expression type %T", e)
}
func errUnsupportedIdentifierExpression(t interface{}) error {
return errors.New("unexpected col type must be string or LiteralExpression received %T", t)
}
func errUnsupportedBooleanExpressionOperator(op exp.BooleanOperation) error {
return errors.New("boolean operator '%+v' not supported", op)
}
func errUnsupportedBitwiseExpressionOperator(op exp.BitwiseOperation) error {
return errors.New("bitwise operator '%+v' not supported", op)
}
func errUnsupportedRangeExpressionOperator(op exp.RangeOperation) error {
return errors.New("range operator %+v not supported", op)
}
func errLateralNotSupported(dialect string) error {
return errors.New("dialect does not support lateral expressions [dialect=%s]", dialect)
}
func NewExpressionSQLGenerator(dialect string, do *SQLDialectOptions) ExpressionSQLGenerator {
return &expressionSQLGenerator{dialect: dialect, dialectOptions: do}
}
func (esg *expressionSQLGenerator) Dialect() string {
return esg.dialect
}
var valuerReflectType = reflect.TypeOf((*driver.Valuer)(nil)).Elem()
func (esg *expressionSQLGenerator) Generate(b sb.SQLBuilder, val interface{}) {
if b.Error() != nil {
return
}
if val == nil {
esg.literalNil(b)
return
}
switch v := val.(type) {
case exp.Expression:
esg.expressionSQL(b, v)
case int:
esg.literalInt(b, int64(v))
case int32:
esg.literalInt(b, int64(v))
case int64:
esg.literalInt(b, v)
case float32:
esg.literalFloat(b, float64(v))
case float64:
esg.literalFloat(b, v)
case string:
esg.literalString(b, v)
case bool:
esg.literalBool(b, v)
case time.Time:
esg.literalTime(b, v)
case *time.Time:
if v == nil {
esg.literalNil(b)
return
}
esg.literalTime(b, *v)
case driver.Valuer:
// See https://github.com/golang/go/commit/0ce1d79a6a771f7449ec493b993ed2a720917870
if rv := reflect.ValueOf(val); rv.Kind() == reflect.Ptr &&
rv.IsNil() &&
rv.Type().Elem().Implements(valuerReflectType) {
esg.literalNil(b)
return
}
dVal, err := v.Value()
if err != nil {
b.SetError(err)
return
}
esg.Generate(b, dVal)
default:
esg.reflectSQL(b, val)
}
}
func (esg *expressionSQLGenerator) reflectSQL(b sb.SQLBuilder, val interface{}) {
v := reflect.Indirect(reflect.ValueOf(val))
valKind := v.Kind()
switch {
case util.IsInvalid(valKind):
esg.literalNil(b)
case util.IsSlice(valKind):
switch t := val.(type) {
case []byte:
esg.literalBytes(b, t)
case []exp.CommonTableExpression:
esg.commonTablesSliceSQL(b, t)
default:
esg.sliceValueSQL(b, v)
}
case util.IsInt(valKind):
esg.Generate(b, v.Int())
case util.IsUint(valKind):
esg.Generate(b, int64(v.Uint()))
case util.IsFloat(valKind):
esg.Generate(b, v.Float())
case util.IsString(valKind):
esg.Generate(b, v.String())
case util.IsBool(valKind):
esg.Generate(b, v.Bool())
default:
b.SetError(errors.NewEncodeError(val))
}
}
// nolint:gocyclo // not complex just long
func (esg *expressionSQLGenerator) expressionSQL(b sb.SQLBuilder, expression exp.Expression) {
switch e := expression.(type) {
case exp.ColumnListExpression:
esg.columnListSQL(b, e)
case exp.ExpressionList:
esg.expressionListSQL(b, e)
case exp.LiteralExpression:
esg.literalExpressionSQL(b, e)
case exp.IdentifierExpression:
esg.identifierExpressionSQL(b, e)
case exp.LateralExpression:
esg.lateralExpressionSQL(b, e)
case exp.AliasedExpression:
esg.aliasedExpressionSQL(b, e)
case exp.BooleanExpression:
esg.booleanExpressionSQL(b, e)
case exp.BitwiseExpression:
esg.bitwiseExpressionSQL(b, e)
case exp.RangeExpression:
esg.rangeExpressionSQL(b, e)
case exp.OrderedExpression:
esg.orderedExpressionSQL(b, e)
case exp.UpdateExpression:
esg.updateExpressionSQL(b, e)
case exp.SQLFunctionExpression:
esg.sqlFunctionExpressionSQL(b, e)
case exp.SQLWindowFunctionExpression:
esg.sqlWindowFunctionExpression(b, e)
case exp.WindowExpression:
esg.windowExpressionSQL(b, e)
case exp.CastExpression:
esg.castExpressionSQL(b, e)
case exp.AppendableExpression:
esg.appendableExpressionSQL(b, e)
case exp.CommonTableExpression:
esg.commonTableExpressionSQL(b, e)
case exp.CompoundExpression:
esg.compoundExpressionSQL(b, e)
case exp.CaseExpression:
esg.caseExpressionSQL(b, e)
case exp.Ex:
esg.expressionMapSQL(b, e)
case exp.ExOr:
esg.expressionOrMapSQL(b, e)
default:
b.SetError(errUnsupportedExpressionType(e))
}
}
// Generates a placeholder (e.g. ?, $1)
func (esg *expressionSQLGenerator) placeHolderSQL(b sb.SQLBuilder, i interface{}) {
b.Write(esg.dialectOptions.PlaceHolderFragment)
if esg.dialectOptions.IncludePlaceholderNum {
b.WriteStrings(strconv.FormatInt(int64(b.CurrentArgPosition()), 10))
}
b.WriteArg(i)
}
// Generates creates the sql for a sub select on a Dataset
func (esg *expressionSQLGenerator) appendableExpressionSQL(b sb.SQLBuilder, a exp.AppendableExpression) {
b.WriteRunes(esg.dialectOptions.LeftParenRune)
a.AppendSQL(b)
b.WriteRunes(esg.dialectOptions.RightParenRune)
if a.GetAs() != nil {
b.Write(esg.dialectOptions.AsFragment)
esg.Generate(b, a.GetAs())
}
}
// Quotes an identifier (e.g. "col", "table"."col"
func (esg *expressionSQLGenerator) identifierExpressionSQL(b sb.SQLBuilder, ident exp.IdentifierExpression) {
if ident.IsEmpty() {
b.SetError(ErrEmptyIdentifier)
return
}
schema, table, col := ident.GetSchema(), ident.GetTable(), ident.GetCol()
if schema != esg.dialectOptions.EmptyString {
b.WriteRunes(esg.dialectOptions.QuoteRune).
WriteStrings(schema).
WriteRunes(esg.dialectOptions.QuoteRune)
}
if table != esg.dialectOptions.EmptyString {
if schema != esg.dialectOptions.EmptyString {
b.WriteRunes(esg.dialectOptions.PeriodRune)
}
b.WriteRunes(esg.dialectOptions.QuoteRune).
WriteStrings(table).
WriteRunes(esg.dialectOptions.QuoteRune)
}
switch t := col.(type) {
case nil:
case string:
if col != esg.dialectOptions.EmptyString {
if table != esg.dialectOptions.EmptyString || schema != esg.dialectOptions.EmptyString {
b.WriteRunes(esg.dialectOptions.PeriodRune)
}
b.WriteRunes(esg.dialectOptions.QuoteRune).
WriteStrings(t).
WriteRunes(esg.dialectOptions.QuoteRune)
}
case exp.LiteralExpression:
if table != esg.dialectOptions.EmptyString || schema != esg.dialectOptions.EmptyString {
b.WriteRunes(esg.dialectOptions.PeriodRune)
}
esg.Generate(b, t)
default:
b.SetError(errUnsupportedIdentifierExpression(col))
}
}
func (esg *expressionSQLGenerator) lateralExpressionSQL(b sb.SQLBuilder, le exp.LateralExpression) {
if !esg.dialectOptions.SupportsLateral {
b.SetError(errLateralNotSupported(esg.dialect))
return
}
b.Write(esg.dialectOptions.LateralFragment)
esg.Generate(b, le.Table())
}
// Generates SQL NULL value
func (esg *expressionSQLGenerator) literalNil(b sb.SQLBuilder) {
if b.IsPrepared() {
esg.placeHolderSQL(b, nil)
return
}
b.Write(esg.dialectOptions.Null)
}
// Generates SQL bool literal, (e.g. TRUE, FALSE, mysql 1, 0, sqlite3 1, 0)
func (esg *expressionSQLGenerator) literalBool(b sb.SQLBuilder, bl bool) {
if b.IsPrepared() {
esg.placeHolderSQL(b, bl)
return
}
if bl {
b.Write(esg.dialectOptions.True)
} else {
b.Write(esg.dialectOptions.False)
}
}
// Generates SQL for a time.Time value
func (esg *expressionSQLGenerator) literalTime(b sb.SQLBuilder, t time.Time) {
if b.IsPrepared() {
esg.placeHolderSQL(b, t)
return
}
esg.Generate(b, t.In(timeLocation).Format(esg.dialectOptions.TimeFormat))
}
// Generates SQL for a Float Value
func (esg *expressionSQLGenerator) literalFloat(b sb.SQLBuilder, f float64) {
if b.IsPrepared() {
esg.placeHolderSQL(b, f)
return
}
b.WriteStrings(strconv.FormatFloat(f, 'f', -1, 64))
}
// Generates SQL for an int value
func (esg *expressionSQLGenerator) literalInt(b sb.SQLBuilder, i int64) {
if b.IsPrepared() {
esg.placeHolderSQL(b, i)
return
}
b.WriteStrings(strconv.FormatInt(i, 10))
}
// Generates SQL for a string
func (esg *expressionSQLGenerator) literalString(b sb.SQLBuilder, s string) {
if b.IsPrepared() {
esg.placeHolderSQL(b, s)
return
}
b.WriteRunes(esg.dialectOptions.StringQuote)
for _, char := range s {
if e, ok := esg.dialectOptions.EscapedRunes[char]; ok {
b.Write(e)
} else {
b.WriteRunes(char)
}
}
b.WriteRunes(esg.dialectOptions.StringQuote)
}
// Generates SQL for a slice of bytes
func (esg *expressionSQLGenerator) literalBytes(b sb.SQLBuilder, bs []byte) {
if b.IsPrepared() {
esg.placeHolderSQL(b, bs)
return
}
b.WriteRunes(esg.dialectOptions.StringQuote)
i := 0
for len(bs) > 0 {
char, l := utf8.DecodeRune(bs)
if e, ok := esg.dialectOptions.EscapedRunes[char]; ok {
b.Write(e)
} else {
b.WriteRunes(char)
}
i++
bs = bs[l:]
}
b.WriteRunes(esg.dialectOptions.StringQuote)
}
// Generates SQL for a slice of values (e.g. []int64{1,2,3,4} -> (1,2,3,4)
func (esg *expressionSQLGenerator) sliceValueSQL(b sb.SQLBuilder, slice reflect.Value) {
b.WriteRunes(esg.dialectOptions.LeftParenRune)
for i, l := 0, slice.Len(); i < l; i++ {
esg.Generate(b, slice.Index(i).Interface())
if i < l-1 {
b.WriteRunes(esg.dialectOptions.CommaRune, esg.dialectOptions.SpaceRune)
}
}
b.WriteRunes(esg.dialectOptions.RightParenRune)
}
// Generates SQL for an AliasedExpression (e.g. I("a").As("b") -> "a" AS "b")
func (esg *expressionSQLGenerator) aliasedExpressionSQL(b sb.SQLBuilder, aliased exp.AliasedExpression) {
esg.Generate(b, aliased.Aliased())
b.Write(esg.dialectOptions.AsFragment)
esg.Generate(b, aliased.GetAs())
}
// Generates SQL for a BooleanExpresion (e.g. I("a").Eq(2) -> "a" = 2)
func (esg *expressionSQLGenerator) booleanExpressionSQL(b sb.SQLBuilder, operator exp.BooleanExpression) {
b.WriteRunes(esg.dialectOptions.LeftParenRune)
esg.Generate(b, operator.LHS())
b.WriteRunes(esg.dialectOptions.SpaceRune)
operatorOp := operator.Op()
if val, ok := esg.dialectOptions.BooleanOperatorLookup[operatorOp]; ok {
b.Write(val)
} else {
b.SetError(errUnsupportedBooleanExpressionOperator(operatorOp))
return
}
rhs := operator.RHS()
if (operatorOp == exp.IsOp || operatorOp == exp.IsNotOp) && rhs != nil && !esg.dialectOptions.BooleanDataTypeSupported {
b.SetError(errors.New("boolean data type is not supported by dialect %q", esg.dialect))
return
}
if (operatorOp == exp.IsOp || operatorOp == exp.IsNotOp) && esg.dialectOptions.UseLiteralIsBools {
// these values must be interpolated because preparing them generates invalid SQL
switch rhs {
case true:
rhs = TrueLiteral
case false:
rhs = FalseLiteral
case nil:
rhs = exp.NewLiteralExpression(string(esg.dialectOptions.Null))
}
}
b.WriteRunes(esg.dialectOptions.SpaceRune)
if (operatorOp == exp.IsOp || operatorOp == exp.IsNotOp) && rhs == nil && !esg.dialectOptions.BooleanDataTypeSupported {
// e.g. for SQL server dialect which does not support "IS @p1" for "IS NULL"
b.Write(esg.dialectOptions.Null)
} else {
esg.Generate(b, rhs)
}
b.WriteRunes(esg.dialectOptions.RightParenRune)
}
// Generates SQL for a BitwiseExpresion (e.g. I("a").BitwiseOr(2) - > "a" | 2)
func (esg *expressionSQLGenerator) bitwiseExpressionSQL(b sb.SQLBuilder, operator exp.BitwiseExpression) {
b.WriteRunes(esg.dialectOptions.LeftParenRune)
if operator.LHS() != nil {
esg.Generate(b, operator.LHS())
b.WriteRunes(esg.dialectOptions.SpaceRune)
}
operatorOp := operator.Op()
if val, ok := esg.dialectOptions.BitwiseOperatorLookup[operatorOp]; ok {
b.Write(val)
} else {
b.SetError(errUnsupportedBitwiseExpressionOperator(operatorOp))
return
}
b.WriteRunes(esg.dialectOptions.SpaceRune)
esg.Generate(b, operator.RHS())
b.WriteRunes(esg.dialectOptions.RightParenRune)
}
// Generates SQL for a RangeExpresion (e.g. I("a").Between(RangeVal{Start:2,End:5}) -> "a" BETWEEN 2 AND 5)
func (esg *expressionSQLGenerator) rangeExpressionSQL(b sb.SQLBuilder, operator exp.RangeExpression) {
b.WriteRunes(esg.dialectOptions.LeftParenRune)
esg.Generate(b, operator.LHS())
b.WriteRunes(esg.dialectOptions.SpaceRune)
operatorOp := operator.Op()
if val, ok := esg.dialectOptions.RangeOperatorLookup[operatorOp]; ok {
b.Write(val)
} else {
b.SetError(errUnsupportedRangeExpressionOperator(operatorOp))
return
}
rhs := operator.RHS()
b.WriteRunes(esg.dialectOptions.SpaceRune)
esg.Generate(b, rhs.Start())
b.Write(esg.dialectOptions.AndFragment)
esg.Generate(b, rhs.End())
b.WriteRunes(esg.dialectOptions.RightParenRune)
}
// Generates SQL for an OrderedExpression (e.g. I("a").Asc() -> "a" ASC)
func (esg *expressionSQLGenerator) orderedExpressionSQL(b sb.SQLBuilder, order exp.OrderedExpression) {
esg.Generate(b, order.SortExpression())
if order.IsAsc() {
b.Write(esg.dialectOptions.AscFragment)
} else {
b.Write(esg.dialectOptions.DescFragment)
}
switch order.NullSortType() {
case exp.NoNullsSortType:
return
case exp.NullsFirstSortType:
b.Write(esg.dialectOptions.NullsFirstFragment)
case exp.NullsLastSortType:
b.Write(esg.dialectOptions.NullsLastFragment)
}
}
// Generates SQL for an ExpressionList (e.g. And(I("a").Eq("a"), I("b").Eq("b")) -> (("a" = 'a') AND ("b" = 'b')))
func (esg *expressionSQLGenerator) expressionListSQL(b sb.SQLBuilder, expressionList exp.ExpressionList) {
if expressionList.IsEmpty() {
return
}
var op []byte
if expressionList.Type() == exp.AndType {
op = esg.dialectOptions.AndFragment
} else {
op = esg.dialectOptions.OrFragment
}
exps := expressionList.Expressions()
expLen := len(exps) - 1
if expLen > 0 {
b.WriteRunes(esg.dialectOptions.LeftParenRune)
} else {
esg.Generate(b, exps[0])
return
}
for i, e := range exps {
esg.Generate(b, e)
if i < expLen {
b.Write(op)
}
}
b.WriteRunes(esg.dialectOptions.RightParenRune)
}
// Generates SQL for a ColumnListExpression
func (esg *expressionSQLGenerator) columnListSQL(b sb.SQLBuilder, columnList exp.ColumnListExpression) {
cols := columnList.Columns()
colLen := len(cols)
for i, col := range cols {
esg.Generate(b, col)
if i < colLen-1 {
b.WriteRunes(esg.dialectOptions.CommaRune, esg.dialectOptions.SpaceRune)
}
}
}
// Generates SQL for an UpdateEpxresion
func (esg *expressionSQLGenerator) updateExpressionSQL(b sb.SQLBuilder, update exp.UpdateExpression) {
esg.Generate(b, update.Col())
b.WriteRunes(esg.dialectOptions.SetOperatorRune)
esg.Generate(b, update.Val())
}
// Generates SQL for a LiteralExpression
// L("a + b") -> a + b
// L("a = ?", 1) -> a = 1
func (esg *expressionSQLGenerator) literalExpressionSQL(b sb.SQLBuilder, literal exp.LiteralExpression) {
l := literal.Literal()
args := literal.Args()
if argsLen := len(args); argsLen > 0 {
currIndex := 0
for _, char := range l {
if char == replacementRune && currIndex < argsLen {
esg.Generate(b, args[currIndex])
currIndex++
} else {
b.WriteRunes(char)
}
}
return
}
b.WriteStrings(l)
}
// Generates SQL for a SQLFunctionExpression
// COUNT(I("a")) -> COUNT("a")
func (esg *expressionSQLGenerator) sqlFunctionExpressionSQL(b sb.SQLBuilder, sqlFunc exp.SQLFunctionExpression) {
b.WriteStrings(sqlFunc.Name())
esg.Generate(b, sqlFunc.Args())
}
func (esg *expressionSQLGenerator) sqlWindowFunctionExpression(b sb.SQLBuilder, sqlWinFunc exp.SQLWindowFunctionExpression) {
if !esg.dialectOptions.SupportsWindowFunction {
b.SetError(ErrWindowNotSupported(esg.dialect))
return
}
esg.Generate(b, sqlWinFunc.Func())
b.Write(esg.dialectOptions.WindowOverFragment)
switch {
case sqlWinFunc.HasWindowName():
esg.Generate(b, sqlWinFunc.WindowName())
case sqlWinFunc.HasWindow():
if sqlWinFunc.Window().HasName() {
b.SetError(ErrUnexpectedNamedWindow)
return
}
esg.Generate(b, sqlWinFunc.Window())
default:
esg.Generate(b, exp.NewWindowExpression(nil, nil, nil, nil))
}
}
func (esg *expressionSQLGenerator) windowExpressionSQL(b sb.SQLBuilder, we exp.WindowExpression) {
if !esg.dialectOptions.SupportsWindowFunction {
b.SetError(ErrWindowNotSupported(esg.dialect))
return
}
if we.HasName() {
esg.Generate(b, we.Name())
b.Write(esg.dialectOptions.AsFragment)
}
b.WriteRunes(esg.dialectOptions.LeftParenRune)
hasPartition := we.HasPartitionBy()
hasOrder := we.HasOrder()
if we.HasParent() {
esg.Generate(b, we.Parent())
if hasPartition || hasOrder {
b.WriteRunes(esg.dialectOptions.SpaceRune)
}
}
if hasPartition {
b.Write(esg.dialectOptions.WindowPartitionByFragment)
esg.Generate(b, we.PartitionCols())
if hasOrder {
b.WriteRunes(esg.dialectOptions.SpaceRune)
}
}
if hasOrder {
b.Write(esg.dialectOptions.WindowOrderByFragment)
esg.Generate(b, we.OrderCols())
}
b.WriteRunes(esg.dialectOptions.RightParenRune)
}
// Generates SQL for a CastExpression
// I("a").Cast("NUMERIC") -> CAST("a" AS NUMERIC)
func (esg *expressionSQLGenerator) castExpressionSQL(b sb.SQLBuilder, cast exp.CastExpression) {
b.Write(esg.dialectOptions.CastFragment).WriteRunes(esg.dialectOptions.LeftParenRune)
esg.Generate(b, cast.Casted())
b.Write(esg.dialectOptions.AsFragment)
esg.Generate(b, cast.Type())
b.WriteRunes(esg.dialectOptions.RightParenRune)
}
// Generates the sql for the WITH clauses for common table expressions (CTE)
func (esg *expressionSQLGenerator) commonTablesSliceSQL(b sb.SQLBuilder, ctes []exp.CommonTableExpression) {
l := len(ctes)
if l == 0 {
return
}
if !esg.dialectOptions.SupportsWithCTE {
b.SetError(ErrCTENotSupported(esg.dialect))
return
}
b.Write(esg.dialectOptions.WithFragment)
anyRecursive := false
for _, cte := range ctes {
anyRecursive = anyRecursive || cte.IsRecursive()
}
if anyRecursive {
if !esg.dialectOptions.SupportsWithCTERecursive {
b.SetError(ErrRecursiveCTENotSupported(esg.dialect))
return
}
b.Write(esg.dialectOptions.RecursiveFragment)
}
for i, cte := range ctes {
esg.Generate(b, cte)
if i < l-1 {
b.WriteRunes(esg.dialectOptions.CommaRune, esg.dialectOptions.SpaceRune)
}
}
b.WriteRunes(esg.dialectOptions.SpaceRune)
}
// Generates SQL for a CommonTableExpression
func (esg *expressionSQLGenerator) commonTableExpressionSQL(b sb.SQLBuilder, cte exp.CommonTableExpression) {
esg.Generate(b, cte.Name())
b.Write(esg.dialectOptions.AsFragment)
esg.Generate(b, cte.SubQuery())
}
// Generates SQL for a CompoundExpression
func (esg *expressionSQLGenerator) compoundExpressionSQL(b sb.SQLBuilder, compound exp.CompoundExpression) {
switch compound.Type() {
case exp.UnionCompoundType:
b.Write(esg.dialectOptions.UnionFragment)
case exp.UnionAllCompoundType:
b.Write(esg.dialectOptions.UnionAllFragment)
case exp.IntersectCompoundType:
b.Write(esg.dialectOptions.IntersectFragment)
case exp.IntersectAllCompoundType:
b.Write(esg.dialectOptions.IntersectAllFragment)
}
if esg.dialectOptions.WrapCompoundsInParens {
b.WriteRunes(esg.dialectOptions.LeftParenRune)
compound.RHS().AppendSQL(b)
b.WriteRunes(esg.dialectOptions.RightParenRune)
} else {
compound.RHS().AppendSQL(b)
}
}
// Generates SQL for a CaseExpression
func (esg *expressionSQLGenerator) caseExpressionSQL(b sb.SQLBuilder, caseExpression exp.CaseExpression) {
caseVal := caseExpression.GetValue()
whens := caseExpression.GetWhens()
elseResult := caseExpression.GetElse()
if len(whens) == 0 {
b.SetError(ErrEmptyCaseWhens)
return
}
b.Write(esg.dialectOptions.CaseFragment)
if caseVal != nil {
esg.Generate(b, caseVal)
}
for _, when := range whens {
b.Write(esg.dialectOptions.WhenFragment)
esg.Generate(b, when.Condition())
b.Write(esg.dialectOptions.ThenFragment)
esg.Generate(b, when.Result())
}
if elseResult != nil {
b.Write(esg.dialectOptions.ElseFragment)
esg.Generate(b, elseResult.Result())
}
b.Write(esg.dialectOptions.EndFragment)
}
func (esg *expressionSQLGenerator) expressionMapSQL(b sb.SQLBuilder, ex exp.Ex) {
expressionList, err := ex.ToExpressions()
if err != nil {
b.SetError(err)
return
}
esg.Generate(b, expressionList)
}
func (esg *expressionSQLGenerator) expressionOrMapSQL(b sb.SQLBuilder, ex exp.ExOr) {
expressionList, err := ex.ToExpressions()
if err != nil {
b.SetError(err)
return
}
esg.Generate(b, expressionList)
}

View File

@@ -0,0 +1,203 @@
package sqlgen
import (
"strings"
"github.com/doug-martin/goqu/v9/exp"
"github.com/doug-martin/goqu/v9/internal/errors"
"github.com/doug-martin/goqu/v9/internal/sb"
)
type (
// An adapter interface to be used by a Dataset to generate SQL for a specific dialect.
// See DefaultAdapter for a concrete implementation and examples.
InsertSQLGenerator interface {
Dialect() string
Generate(b sb.SQLBuilder, clauses exp.InsertClauses)
}
// The default adapter. This class should be used when building a new adapter. When creating a new adapter you can
// either override methods, or more typically update default values.
// See (github.com/doug-martin/goqu/dialect/postgres)
insertSQLGenerator struct {
CommonSQLGenerator
}
)
var (
ErrConflictUpdateValuesRequired = errors.New("values are required for on conflict update expression")
ErrNoSourceForInsert = errors.New("no source found when generating insert sql")
)
func errMisMatchedRowLength(expectedL, actualL int) error {
return errors.New("rows with different value length expected %d got %d", expectedL, actualL)
}
func errUpsertWithWhereNotSupported(dialect string) error {
return errors.New("dialect does not support upsert with where clause [dialect=%s]", dialect)
}
func NewInsertSQLGenerator(dialect string, do *SQLDialectOptions) InsertSQLGenerator {
return &insertSQLGenerator{NewCommonSQLGenerator(dialect, do)}
}
func (isg *insertSQLGenerator) Generate(
b sb.SQLBuilder,
clauses exp.InsertClauses,
) {
if !clauses.HasInto() {
b.SetError(ErrNoSourceForInsert)
return
}
for _, f := range isg.DialectOptions().InsertSQLOrder {
if b.Error() != nil {
return
}
switch f {
case CommonTableSQLFragment:
isg.ExpressionSQLGenerator().Generate(b, clauses.CommonTables())
case InsertBeingSQLFragment:
isg.InsertBeginSQL(b, clauses.OnConflict())
case IntoSQLFragment:
b.WriteRunes(isg.DialectOptions().SpaceRune)
isg.ExpressionSQLGenerator().Generate(b, clauses.Into())
case InsertSQLFragment:
isg.InsertSQL(b, clauses)
case ReturningSQLFragment:
isg.ReturningSQL(b, clauses.Returning())
default:
b.SetError(ErrNotSupportedFragment("INSERT", f))
}
}
}
// Adds the correct fragment to being an INSERT statement
func (isg *insertSQLGenerator) InsertBeginSQL(b sb.SQLBuilder, o exp.ConflictExpression) {
if isg.DialectOptions().SupportsInsertIgnoreSyntax && o != nil {
b.Write(isg.DialectOptions().InsertIgnoreClause)
} else {
b.Write(isg.DialectOptions().InsertClause)
}
}
// Adds the columns list to an insert statement
func (isg *insertSQLGenerator) InsertSQL(b sb.SQLBuilder, ic exp.InsertClauses) {
switch {
case ic.HasRows():
ie, err := exp.NewInsertExpression(ic.Rows()...)
if err != nil {
b.SetError(err)
return
}
isg.InsertExpressionSQL(b, ie)
case ic.HasCols() && ic.HasVals():
isg.insertColumnsSQL(b, ic.Cols())
isg.insertValuesSQL(b, ic.Vals())
case ic.HasCols() && ic.HasFrom():
isg.insertColumnsSQL(b, ic.Cols())
isg.insertFromSQL(b, ic.From())
case ic.HasFrom():
isg.insertFromSQL(b, ic.From())
default:
isg.defaultValuesSQL(b)
}
if ic.HasAlias() {
b.Write(isg.DialectOptions().AsFragment)
isg.ExpressionSQLGenerator().Generate(b, ic.Alias())
}
isg.onConflictSQL(b, ic.OnConflict())
}
func (isg *insertSQLGenerator) InsertExpressionSQL(b sb.SQLBuilder, ie exp.InsertExpression) {
switch {
case ie.IsInsertFrom():
isg.insertFromSQL(b, ie.From())
case ie.IsEmpty():
isg.defaultValuesSQL(b)
default:
isg.insertColumnsSQL(b, ie.Cols())
isg.insertValuesSQL(b, ie.Vals())
}
}
// Adds the DefaultValuesFragment to an SQL statement
func (isg *insertSQLGenerator) defaultValuesSQL(b sb.SQLBuilder) {
b.Write(isg.DialectOptions().DefaultValuesFragment)
}
func (isg *insertSQLGenerator) insertFromSQL(b sb.SQLBuilder, ae exp.AppendableExpression) {
b.WriteRunes(isg.DialectOptions().SpaceRune)
ae.AppendSQL(b)
}
// Adds the columns list to an insert statement
func (isg *insertSQLGenerator) insertColumnsSQL(b sb.SQLBuilder, cols exp.ColumnListExpression) {
b.WriteRunes(isg.DialectOptions().SpaceRune, isg.DialectOptions().LeftParenRune)
isg.ExpressionSQLGenerator().Generate(b, cols)
b.WriteRunes(isg.DialectOptions().RightParenRune)
}
// Adds the values clause to an SQL statement
func (isg *insertSQLGenerator) insertValuesSQL(b sb.SQLBuilder, values [][]interface{}) {
b.Write(isg.DialectOptions().ValuesFragment)
rowLen := len(values[0])
valueLen := len(values)
for i, row := range values {
if len(row) != rowLen {
b.SetError(errMisMatchedRowLength(rowLen, len(row)))
return
}
isg.ExpressionSQLGenerator().Generate(b, row)
if i < valueLen-1 {
b.WriteRunes(isg.DialectOptions().CommaRune, isg.DialectOptions().SpaceRune)
}
}
}
// Adds the DefaultValuesFragment to an SQL statement
func (isg *insertSQLGenerator) onConflictSQL(b sb.SQLBuilder, o exp.ConflictExpression) {
if o == nil {
return
}
b.Write(isg.DialectOptions().ConflictFragment)
switch t := o.(type) {
case exp.ConflictUpdateExpression:
target := t.TargetColumn()
if isg.DialectOptions().SupportsConflictTarget && target != "" {
wrapParens := !strings.HasPrefix(strings.ToLower(target), "on constraint")
b.WriteRunes(isg.DialectOptions().SpaceRune)
if wrapParens {
b.WriteRunes(isg.DialectOptions().LeftParenRune).
WriteStrings(target).
WriteRunes(isg.DialectOptions().RightParenRune)
} else {
b.Write([]byte(target))
}
}
isg.onConflictDoUpdateSQL(b, t)
default:
b.Write(isg.DialectOptions().ConflictDoNothingFragment)
}
}
func (isg *insertSQLGenerator) onConflictDoUpdateSQL(b sb.SQLBuilder, o exp.ConflictUpdateExpression) {
b.Write(isg.DialectOptions().ConflictDoUpdateFragment)
update := o.Update()
if update == nil {
b.SetError(ErrConflictUpdateValuesRequired)
return
}
ue, err := exp.NewUpdateExpressions(update)
if err != nil {
b.SetError(err)
return
}
isg.UpdateExpressionSQL(b, ue...)
if b.Error() == nil && o.WhereClause() != nil {
if !isg.DialectOptions().SupportsConflictUpdateWhere {
b.SetError(errUpsertWithWhereNotSupported(isg.Dialect()))
return
}
isg.WhereSQL(b, o.WhereClause())
}
}

View File

@@ -0,0 +1,266 @@
package sqlgen
import (
"github.com/doug-martin/goqu/v9/exp"
"github.com/doug-martin/goqu/v9/internal/errors"
"github.com/doug-martin/goqu/v9/internal/sb"
)
type (
// An adapter interface to be used by a Dataset to generate SQL for a specific dialect.
// See DefaultAdapter for a concrete implementation and examples.
SelectSQLGenerator interface {
Dialect() string
Generate(b sb.SQLBuilder, clauses exp.SelectClauses)
}
// The default adapter. This class should be used when building a new adapter. When creating a new adapter you can
// either override methods, or more typically update default values.
// See (github.com/doug-martin/goqu/dialect/postgres)
selectSQLGenerator struct {
CommonSQLGenerator
}
)
func ErrNotSupportedJoinType(j exp.JoinExpression) error {
return errors.New("dialect does not support %v", j.JoinType())
}
func ErrJoinConditionRequired(j exp.JoinExpression) error {
return errors.New("join condition required for conditioned join %v", j.JoinType())
}
func ErrDistinctOnNotSupported(dialect string) error {
return errors.New("dialect does not support DISTINCT ON clause [dialect=%s]", dialect)
}
func ErrWindowNotSupported(dialect string) error {
return errors.New("dialect does not support WINDOW clause [dialect=%s]", dialect)
}
var ErrNoWindowName = errors.New("window expresion has no valid name")
func NewSelectSQLGenerator(dialect string, do *SQLDialectOptions) SelectSQLGenerator {
return &selectSQLGenerator{NewCommonSQLGenerator(dialect, do)}
}
func (ssg *selectSQLGenerator) Generate(b sb.SQLBuilder, clauses exp.SelectClauses) {
for _, f := range ssg.DialectOptions().SelectSQLOrder {
if b.Error() != nil {
return
}
switch f {
case CommonTableSQLFragment:
ssg.ExpressionSQLGenerator().Generate(b, clauses.CommonTables())
case SelectSQLFragment:
ssg.SelectSQL(b, clauses)
case SelectWithLimitSQLFragment:
ssg.SelectWithLimitSQL(b, clauses)
case FromSQLFragment:
ssg.FromSQL(b, clauses.From())
case JoinSQLFragment:
ssg.JoinSQL(b, clauses.Joins())
case WhereSQLFragment:
ssg.WhereSQL(b, clauses.Where())
case GroupBySQLFragment:
ssg.GroupBySQL(b, clauses.GroupBy())
case HavingSQLFragment:
ssg.HavingSQL(b, clauses.Having())
case WindowSQLFragment:
ssg.WindowSQL(b, clauses.Windows())
case CompoundsSQLFragment:
ssg.CompoundsSQL(b, clauses.Compounds())
case OrderSQLFragment:
ssg.OrderSQL(b, clauses.Order())
case OrderWithOffsetFetchSQLFragment:
ssg.OrderWithOffsetFetchSQL(b, clauses.Order(), clauses.Offset(), clauses.Limit())
case LimitSQLFragment:
ssg.LimitSQL(b, clauses.Limit())
case OffsetSQLFragment:
ssg.OffsetSQL(b, clauses.Offset())
case ForSQLFragment:
ssg.ForSQL(b, clauses.Lock())
default:
b.SetError(ErrNotSupportedFragment("SELECT", f))
}
}
}
func (ssg *selectSQLGenerator) selectSQLCommon(b sb.SQLBuilder, clauses exp.SelectClauses) {
dc := clauses.Distinct()
if dc != nil {
b.Write(ssg.DialectOptions().DistinctFragment)
if !dc.IsEmpty() {
if ssg.DialectOptions().SupportsDistinctOn {
b.Write(ssg.DialectOptions().OnFragment).WriteRunes(ssg.DialectOptions().LeftParenRune)
ssg.ExpressionSQLGenerator().Generate(b, dc)
b.WriteRunes(ssg.DialectOptions().RightParenRune, ssg.DialectOptions().SpaceRune)
} else {
b.SetError(ErrDistinctOnNotSupported(ssg.Dialect()))
return
}
} else {
b.WriteRunes(ssg.DialectOptions().SpaceRune)
}
}
if cols := clauses.Select(); clauses.IsDefaultSelect() || len(cols.Columns()) == 0 {
b.WriteRunes(ssg.DialectOptions().StarRune)
} else {
ssg.ExpressionSQLGenerator().Generate(b, cols)
}
}
// Adds the SELECT clause and columns to a sql statement
func (ssg *selectSQLGenerator) SelectSQL(b sb.SQLBuilder, clauses exp.SelectClauses) {
b.Write(ssg.DialectOptions().SelectClause).WriteRunes(ssg.DialectOptions().SpaceRune)
ssg.selectSQLCommon(b, clauses)
}
// Adds the SELECT clause along with LIMIT to a SQL statement (e.g. MSSQL dialect: SELECT TOP 10 ...)
func (ssg *selectSQLGenerator) SelectWithLimitSQL(b sb.SQLBuilder, clauses exp.SelectClauses) {
b.Write(ssg.DialectOptions().SelectClause).WriteRunes(ssg.DialectOptions().SpaceRune)
if clauses.Offset() == 0 && clauses.Limit() != nil {
ssg.LimitSQL(b, clauses.Limit())
b.WriteRunes(ssg.DialectOptions().SpaceRune)
}
ssg.selectSQLCommon(b, clauses)
}
// Generates the JOIN clauses for an SQL statement
func (ssg *selectSQLGenerator) JoinSQL(b sb.SQLBuilder, joins exp.JoinExpressions) {
if len(joins) > 0 {
for _, j := range joins {
joinType, ok := ssg.DialectOptions().JoinTypeLookup[j.JoinType()]
if !ok {
b.SetError(ErrNotSupportedJoinType(j))
return
}
b.Write(joinType)
ssg.ExpressionSQLGenerator().Generate(b, j.Table())
if t, ok := j.(exp.ConditionedJoinExpression); ok {
if t.IsConditionEmpty() {
b.SetError(ErrJoinConditionRequired(j))
return
}
ssg.joinConditionSQL(b, t.Condition())
}
}
}
}
// Generates the GROUP BY clause for an SQL statement
func (ssg *selectSQLGenerator) GroupBySQL(b sb.SQLBuilder, groupBy exp.ColumnListExpression) {
if groupBy != nil && len(groupBy.Columns()) > 0 {
b.Write(ssg.DialectOptions().GroupByFragment)
ssg.ExpressionSQLGenerator().Generate(b, groupBy)
}
}
// Generates the HAVING clause for an SQL statement
func (ssg *selectSQLGenerator) HavingSQL(b sb.SQLBuilder, having exp.ExpressionList) {
if having != nil && len(having.Expressions()) > 0 {
b.Write(ssg.DialectOptions().HavingFragment)
ssg.ExpressionSQLGenerator().Generate(b, having)
}
}
// Generates the OFFSET clause for an SQL statement
func (ssg *selectSQLGenerator) OffsetSQL(b sb.SQLBuilder, offset uint) {
if offset > 0 {
b.Write(ssg.DialectOptions().OffsetFragment)
ssg.ExpressionSQLGenerator().Generate(b, offset)
}
}
// Generates the compound sql clause for an SQL statement (e.g. UNION, INTERSECT)
func (ssg *selectSQLGenerator) CompoundsSQL(b sb.SQLBuilder, compounds []exp.CompoundExpression) {
for _, compound := range compounds {
ssg.ExpressionSQLGenerator().Generate(b, compound)
}
}
// Generates the FOR (aka "locking") clause for an SQL statement
func (ssg *selectSQLGenerator) ForSQL(b sb.SQLBuilder, lockingClause exp.Lock) {
if lockingClause == nil {
return
}
switch lockingClause.Strength() {
case exp.ForNolock:
return
case exp.ForUpdate:
b.Write(ssg.DialectOptions().ForUpdateFragment)
case exp.ForNoKeyUpdate:
b.Write(ssg.DialectOptions().ForNoKeyUpdateFragment)
case exp.ForShare:
b.Write(ssg.DialectOptions().ForShareFragment)
case exp.ForKeyShare:
b.Write(ssg.DialectOptions().ForKeyShareFragment)
}
of := lockingClause.Of()
if ofLen := len(of); ofLen > 0 {
if ofFragment := ssg.DialectOptions().OfFragment; len(ofFragment) > 0 {
b.Write(ofFragment)
for i, table := range of {
ssg.ExpressionSQLGenerator().Generate(b, table)
if i < ofLen-1 {
b.WriteRunes(ssg.DialectOptions().CommaRune, ssg.DialectOptions().SpaceRune)
}
}
b.WriteRunes(ssg.DialectOptions().SpaceRune)
}
}
// the WAIT case is the default in Postgres, and is what you get if you don't specify NOWAIT or
// SKIP LOCKED. There's no special syntax for it in PG, so we don't do anything for it here
switch lockingClause.WaitOption() {
case exp.Wait:
return
case exp.NoWait:
b.Write(ssg.DialectOptions().NowaitFragment)
case exp.SkipLocked:
b.Write(ssg.DialectOptions().SkipLockedFragment)
}
}
func (ssg *selectSQLGenerator) WindowSQL(b sb.SQLBuilder, windows []exp.WindowExpression) {
weLen := len(windows)
if weLen == 0 {
return
}
if !ssg.DialectOptions().SupportsWindowFunction {
b.SetError(ErrWindowNotSupported(ssg.Dialect()))
return
}
b.Write(ssg.DialectOptions().WindowFragment)
for i, we := range windows {
if !we.HasName() {
b.SetError(ErrNoWindowName)
}
ssg.ExpressionSQLGenerator().Generate(b, we)
if i < weLen-1 {
b.WriteRunes(ssg.DialectOptions().CommaRune, ssg.DialectOptions().SpaceRune)
}
}
}
func (ssg *selectSQLGenerator) joinConditionSQL(b sb.SQLBuilder, jc exp.JoinCondition) {
switch t := jc.(type) {
case exp.JoinOnCondition:
ssg.joinOnConditionSQL(b, t)
case exp.JoinUsingCondition:
ssg.joinUsingConditionSQL(b, t)
}
}
func (ssg *selectSQLGenerator) joinUsingConditionSQL(b sb.SQLBuilder, jc exp.JoinUsingCondition) {
b.Write(ssg.DialectOptions().UsingFragment).
WriteRunes(ssg.DialectOptions().LeftParenRune)
ssg.ExpressionSQLGenerator().Generate(b, jc.Using())
b.WriteRunes(ssg.DialectOptions().RightParenRune)
}
func (ssg *selectSQLGenerator) joinOnConditionSQL(b sb.SQLBuilder, jc exp.JoinOnCondition) {
b.Write(ssg.DialectOptions().OnFragment)
ssg.ExpressionSQLGenerator().Generate(b, jc.On())
}

View File

@@ -0,0 +1,607 @@
package sqlgen
import (
"fmt"
"time"
"github.com/doug-martin/goqu/v9/exp"
)
type (
SQLFragmentType int
SQLDialectOptions struct {
// Set to true if the dialect supports ORDER BY expressions in DELETE statements (DEFAULT=false)
SupportsOrderByOnDelete bool
// Set to true if the dialect supports table hint for DELETE statements (DELETE t FROM t ...), DEFAULT=false
SupportsDeleteTableHint bool
// Set to true if the dialect supports ORDER BY expressions in UPDATE statements (DEFAULT=false)
SupportsOrderByOnUpdate bool
// Set to true if the dialect supports LIMIT expressions in DELETE statements (DEFAULT=false)
SupportsLimitOnDelete bool
// Set to true if the dialect supports LIMIT expressions in UPDATE statements (DEFAULT=false)
SupportsLimitOnUpdate bool
// Set to true if the dialect supports RETURN expressions (DEFAULT=true)
SupportsReturn bool
// Set to true if the dialect supports Conflict Target (DEFAULT=true)
SupportsConflictTarget bool
// Set to true if the dialect supports Conflict Target (DEFAULT=true)
SupportsConflictUpdateWhere bool
// Set to true if the dialect supports Insert Ignore syntax (DEFAULT=false)
SupportsInsertIgnoreSyntax bool
// Set to true if the dialect supports Common Table Expressions (DEFAULT=true)
SupportsWithCTE bool
// Set to true if the dialect supports recursive Common Table Expressions (DEFAULT=true)
SupportsWithCTERecursive bool
// Set to true if multiple tables are supported in UPDATE statement. (DEFAULT=true)
SupportsMultipleUpdateTables bool
// Set to true if DISTINCT ON is supported (DEFAULT=true)
SupportsDistinctOn bool
// Set to true if LATERAL queries are supported (DEFAULT=true)
SupportsLateral bool
// Set to false if the dialect does not require expressions to be wrapped in parens (DEFAULT=true)
WrapCompoundsInParens bool
// Set to true if window function are supported in SELECT statement. (DEFAULT=true)
SupportsWindowFunction bool
// Set to true if the dialect requires join tables in UPDATE to be in a FROM clause (DEFAULT=true).
UseFromClauseForMultipleUpdateTables bool
// Surround LIMIT parameter with parentheses, like in MSSQL: SELECT TOP (10) ...
SurroundLimitWithParentheses bool
// The UPDATE fragment to use when generating sql. (DEFAULT=[]byte("UPDATE"))
UpdateClause []byte
// The INSERT fragment to use when generating sql. (DEFAULT=[]byte("INSERT INTO"))
InsertClause []byte
// The INSERT IGNORE INTO fragment to use when generating sql. (DEFAULT=[]byte("INSERT IGNORE INTO"))
InsertIgnoreClause []byte
// The SELECT fragment to use when generating sql. (DEFAULT=[]byte("SELECT"))
SelectClause []byte
// The DELETE fragment to use when generating sql. (DEFAULT=[]byte("DELETE"))
DeleteClause []byte
// The TRUNCATE fragment to use when generating sql. (DEFAULT=[]byte("TRUNCATE"))
TruncateClause []byte
// The WITH fragment to use when generating sql. (DEFAULT=[]byte("WITH "))
WithFragment []byte
// The RECURSIVE fragment to use when generating sql (after WITH). (DEFAULT=[]byte("RECURSIVE "))
RecursiveFragment []byte
// The CASCADE fragment to use when generating sql. (DEFAULT=[]byte(" CASCADE"))
CascadeFragment []byte
// The RESTRICT fragment to use when generating sql. (DEFAULT=[]byte(" RESTRICT"))
RestrictFragment []byte
// The SQL fragment to use when generating insert sql and using
// DEFAULT VALUES (e.g. postgres="DEFAULT VALUES", mysql="", sqlite3=""). (DEFAULT=[]byte(" DEFAULT VALUES"))
DefaultValuesFragment []byte
// The SQL fragment to use when generating insert sql and listing columns using a VALUES clause
// (DEFAULT=[]byte(" VALUES "))
ValuesFragment []byte
// The SQL fragment to use when generating truncate sql and using the IDENTITY clause
// (DEFAULT=[]byte(" IDENTITY"))
IdentityFragment []byte
// The SQL fragment to use when generating update sql and using the SET clause (DEFAULT=[]byte(" SET "))
SetFragment []byte
// The SQL DISTINCT keyword (DEFAULT=[]byte(" DISTINCT "))
DistinctFragment []byte
// The SQL RETURNING clause (DEFAULT=[]byte(" RETURNING "))
ReturningFragment []byte
// The SQL FROM clause fragment (DEFAULT=[]byte(" FROM"))
FromFragment []byte
// The SQL USING join clause fragment (DEFAULT=[]byte(" USING "))
UsingFragment []byte
// The SQL ON join clause fragment (DEFAULT=[]byte(" ON "))
OnFragment []byte
// The SQL WHERE clause fragment (DEFAULT=[]byte(" WHERE "))
WhereFragment []byte
// The SQL GROUP BY clause fragment(DEFAULT=[]byte(" GROUP BY "))
GroupByFragment []byte
// The SQL HAVING clause fragment(DEFAULT=[]byte(" HAVING "))
HavingFragment []byte
// The SQL WINDOW clause fragment(DEFAULT=[]byte(" WINDOW "))
WindowFragment []byte
// The SQL WINDOW clause PARTITION BY fragment(DEFAULT=[]byte("PARTITION BY "))
WindowPartitionByFragment []byte
// The SQL WINDOW clause ORDER BY fragment(DEFAULT=[]byte("ORDER BY "))
WindowOrderByFragment []byte
// The SQL WINDOW clause OVER fragment(DEFAULT=[]byte(" OVER "))
WindowOverFragment []byte
// The SQL ORDER BY clause fragment(DEFAULT=[]byte(" ORDER BY "))
OrderByFragment []byte
// The SQL FETCH fragment(DEFAULT=[]byte(" "))
FetchFragment []byte
// The SQL LIMIT BY clause fragment(DEFAULT=[]byte(" LIMIT "))
LimitFragment []byte
// The SQL OFFSET BY clause fragment(DEFAULT=[]byte(" OFFSET "))
OffsetFragment []byte
// The SQL FOR UPDATE fragment(DEFAULT=[]byte(" FOR UPDATE "))
ForUpdateFragment []byte
// The SQL FOR NO KEY UPDATE fragment(DEFAULT=[]byte(" FOR NO KEY UPDATE "))
ForNoKeyUpdateFragment []byte
// The SQL FOR SHARE fragment(DEFAULT=[]byte(" FOR SHARE "))
ForShareFragment []byte
// The SQL OF fragment(DEFAULT=[]byte("OF "))
OfFragment []byte
// The SQL FOR KEY SHARE fragment(DEFAULT=[]byte(" FOR KEY SHARE "))
ForKeyShareFragment []byte
// The SQL NOWAIT fragment(DEFAULT=[]byte("NOWAIT"))
NowaitFragment []byte
// The SQL SKIP LOCKED fragment(DEFAULT=[]byte("SKIP LOCKED"))
SkipLockedFragment []byte
// The SQL AS fragment when aliasing an Expression(DEFAULT=[]byte(" AS "))
AsFragment []byte
// The SQL LATERAL fragment used for LATERAL joins
LateralFragment []byte
// The quote rune to use when quoting identifiers(DEFAULT='"')
QuoteRune rune
// The NULL literal to use when interpolating nulls values (DEFAULT=[]byte("NULL"))
Null []byte
// The TRUE literal to use when interpolating bool true values (DEFAULT=[]byte("TRUE"))
True []byte
// The FALSE literal to use when interpolating bool false values (DEFAULT=[]byte("FALSE"))
False []byte
// The ASC fragment when specifying column order (DEFAULT=[]byte(" ASC"))
AscFragment []byte
// The DESC fragment when specifying column order (DEFAULT=[]byte(" DESC"))
DescFragment []byte
// The NULLS FIRST fragment when specifying column order (DEFAULT=[]byte(" NULLS FIRST"))
NullsFirstFragment []byte
// The NULLS LAST fragment when specifying column order (DEFAULT=[]byte(" NULLS LAST"))
NullsLastFragment []byte
// The AND keyword used when joining ExpressionLists (DEFAULT=[]byte(" AND "))
AndFragment []byte
// The OR keyword used when joining ExpressionLists (DEFAULT=[]byte(" OR "))
OrFragment []byte
// The UNION keyword used when creating compound statements (DEFAULT=[]byte(" UNION "))
UnionFragment []byte
// The UNION ALL keyword used when creating compound statements (DEFAULT=[]byte(" UNION ALL "))
UnionAllFragment []byte
// The INTERSECT keyword used when creating compound statements (DEFAULT=[]byte(" INTERSECT "))
IntersectFragment []byte
// The INTERSECT ALL keyword used when creating compound statements (DEFAULT=[]byte(" INTERSECT ALL "))
IntersectAllFragment []byte
// The CAST keyword to use when casting a value (DEFAULT=[]byte("CAST"))
CastFragment []byte
// The CASE keyword to use when when creating a CASE statement (DEFAULT=[]byte("CASE "))
CaseFragment []byte
// The WHEN keyword to use when when creating a CASE statement (DEFAULT=[]byte(" WHEN "))
WhenFragment []byte
// The THEN keyword to use when when creating a CASE statement (DEFAULT=[]byte(" THEN "))
ThenFragment []byte
// The ELSE keyword to use when when creating a CASE statement (DEFAULT=[]byte(" ELSE "))
ElseFragment []byte
// The End keyword to use when when creating a CASE statement (DEFAULT=[]byte(" END"))
EndFragment []byte
// The quote rune to use when quoting string literals (DEFAULT='\'')
StringQuote rune
// The operator to use when setting values in an update statement (DEFAULT='=')
SetOperatorRune rune
// The placeholder fragment to use when generating a non interpolated statement (DEFAULT=[]byte"?")
PlaceHolderFragment []byte
// Empty string (DEFAULT="")
EmptyString string
// Comma rune (DEFAULT=',')
CommaRune rune
// Space rune (DEFAULT=' ')
SpaceRune rune
// Left paren rune (DEFAULT='(')
LeftParenRune rune
// Right paren rune (DEFAULT=')')
RightParenRune rune
// Star rune (DEFAULT='*')
StarRune rune
// Period rune (DEFAULT='.')
PeriodRune rune
// Set to true to include positional argument numbers when creating a prepared statement (Default=false)
IncludePlaceholderNum bool
// The time format to use when serializing time.Time (DEFAULT=time.RFC3339Nano)
TimeFormat string
// A map used to look up BooleanOperations and their SQL equivalents
// (Default= map[exp.BooleanOperation][]byte{
// exp.EqOp: []byte("="),
// exp.NeqOp: []byte("!="),
// exp.GtOp: []byte(">"),
// exp.GteOp: []byte(">="),
// exp.LtOp: []byte("<"),
// exp.LteOp: []byte("<="),
// exp.InOp: []byte("IN"),
// exp.NotInOp: []byte("NOT IN"),
// exp.IsOp: []byte("IS"),
// exp.IsNotOp: []byte("IS NOT"),
// exp.LikeOp: []byte("LIKE"),
// exp.NotLikeOp: []byte("NOT LIKE"),
// exp.ILikeOp: []byte("ILIKE"),
// exp.NotILikeOp: []byte("NOT ILIKE"),
// exp.RegexpLikeOp: []byte("~"),
// exp.RegexpNotLikeOp: []byte("!~"),
// exp.RegexpILikeOp: []byte("~*"),
// exp.RegexpNotILikeOp: []byte("!~*"),
// })
BooleanOperatorLookup map[exp.BooleanOperation][]byte
// A map used to look up BitwiseOperations and their SQL equivalents
// (Default=map[exp.BitwiseOperation][]byte{
// exp.BitwiseInversionOp: []byte("~"),
// exp.BitwiseOrOp: []byte("|"),
// exp.BitwiseAndOp: []byte("&"),
// exp.BitwiseXorOp: []byte("#"),
// exp.BitwiseLeftShiftOp: []byte("<<"),
// exp.BitwiseRightShiftOp: []byte(">>"),
// }),
BitwiseOperatorLookup map[exp.BitwiseOperation][]byte
// A map used to look up RangeOperations and their SQL equivalents
// (Default=map[exp.RangeOperation][]byte{
// exp.BetweenOp: []byte("BETWEEN"),
// exp.NotBetweenOp: []byte("NOT BETWEEN"),
// })
RangeOperatorLookup map[exp.RangeOperation][]byte
// A map used to look up JoinTypes and their SQL equivalents
// (Default= map[exp.JoinType][]byte{
// exp.InnerJoinType: []byte(" INNER JOIN "),
// exp.FullOuterJoinType: []byte(" FULL OUTER JOIN "),
// exp.RightOuterJoinType: []byte(" RIGHT OUTER JOIN "),
// exp.LeftOuterJoinType: []byte(" LEFT OUTER JOIN "),
// exp.FullJoinType: []byte(" FULL JOIN "),
// exp.RightJoinType: []byte(" RIGHT JOIN "),
// exp.LeftJoinType: []byte(" LEFT JOIN "),
// exp.NaturalJoinType: []byte(" NATURAL JOIN "),
// exp.NaturalLeftJoinType: []byte(" NATURAL LEFT JOIN "),
// exp.NaturalRightJoinType: []byte(" NATURAL RIGHT JOIN "),
// exp.NaturalFullJoinType: []byte(" NATURAL FULL JOIN "),
// exp.CrossJoinType: []byte(" CROSS JOIN "),
// })
JoinTypeLookup map[exp.JoinType][]byte
// Whether or not boolean data type is supported
BooleanDataTypeSupported bool
// Whether or not to use literal TRUE or FALSE for IS statements (e.g. IS TRUE or IS 0)
UseLiteralIsBools bool
// EscapedRunes is a map of a rune and the corresponding escape sequence in bytes. Used when escaping text
// types.
// (Default= map[rune][]byte{
// '\'': []byte("''"),
// })
EscapedRunes map[rune][]byte
// The SQL fragment to use for CONFLICT (Default=[]byte(" ON CONFLICT"))
ConflictFragment []byte
// The SQL fragment to use for CONFLICT DO NOTHING (Default=[]byte(" DO NOTHING"))
ConflictDoNothingFragment []byte
// The SQL fragment to use for CONFLICT DO UPDATE (Default=[]byte(" DO UPDATE SET"))
ConflictDoUpdateFragment []byte
// The order of SQL fragments when creating a SELECT statement
// (Default=[]SQLFragmentType{
// CommonTableSQLFragment,
// SelectSQLFragment,
// FromSQLFragment,
// JoinSQLFragment,
// WhereSQLFragment,
// GroupBySQLFragment,
// HavingSQLFragment,
// CompoundsSQLFragment,
// OrderSQLFragment,
// LimitSQLFragment,
// OffsetSQLFragment,
// ForSQLFragment,
// })
SelectSQLOrder []SQLFragmentType
// The order of SQL fragments when creating an UPDATE statement
// (Default=[]SQLFragmentType{
// CommonTableSQLFragment,
// UpdateBeginSQLFragment,
// SourcesSQLFragment,
// UpdateSQLFragment,
// WhereSQLFragment,
// OrderSQLFragment,
// LimitSQLFragment,
// ReturningSQLFragment,
// })
UpdateSQLOrder []SQLFragmentType
// The order of SQL fragments when creating an INSERT statement
// (Default=[]SQLFragmentType{
// CommonTableSQLFragment,
// InsertBeingSQLFragment,
// SourcesSQLFragment,
// InsertSQLFragment,
// ReturningSQLFragment,
// })
InsertSQLOrder []SQLFragmentType
// The order of SQL fragments when creating a DELETE statement
// (Default=[]SQLFragmentType{
// CommonTableSQLFragment,
// DeleteBeginSQLFragment,
// FromSQLFragment,
// WhereSQLFragment,
// OrderSQLFragment,
// LimitSQLFragment,
// ReturningSQLFragment,
// })
DeleteSQLOrder []SQLFragmentType
// The order of SQL fragments when creating a TRUNCATE statement
// (Default=[]SQLFragmentType{
// TruncateSQLFragment,
// })
TruncateSQLOrder []SQLFragmentType
}
)
const (
CommonTableSQLFragment = iota
SelectSQLFragment
SelectWithLimitSQLFragment
FromSQLFragment
JoinSQLFragment
WhereSQLFragment
GroupBySQLFragment
HavingSQLFragment
CompoundsSQLFragment
OrderSQLFragment
OrderWithOffsetFetchSQLFragment
LimitSQLFragment
OffsetSQLFragment
ForSQLFragment
UpdateBeginSQLFragment
SourcesSQLFragment
IntoSQLFragment
UpdateSQLFragment
UpdateFromSQLFragment
ReturningSQLFragment
InsertBeingSQLFragment
InsertSQLFragment
DeleteBeginSQLFragment
TruncateSQLFragment
WindowSQLFragment
)
// nolint:gocyclo // simple type to string conversion
func (sf SQLFragmentType) String() string {
switch sf {
case CommonTableSQLFragment:
return "CommonTableSQLFragment"
case SelectSQLFragment:
return "SelectSQLFragment"
case FromSQLFragment:
return "FromSQLFragment"
case JoinSQLFragment:
return "JoinSQLFragment"
case WhereSQLFragment:
return "WhereSQLFragment"
case GroupBySQLFragment:
return "GroupBySQLFragment"
case HavingSQLFragment:
return "HavingSQLFragment"
case CompoundsSQLFragment:
return "CompoundsSQLFragment"
case OrderSQLFragment:
return "OrderSQLFragment"
case LimitSQLFragment:
return "LimitSQLFragment"
case OffsetSQLFragment:
return "OffsetSQLFragment"
case ForSQLFragment:
return "ForSQLFragment"
case UpdateBeginSQLFragment:
return "UpdateBeginSQLFragment"
case SourcesSQLFragment:
return "SourcesSQLFragment"
case IntoSQLFragment:
return "IntoSQLFragment"
case UpdateSQLFragment:
return "UpdateSQLFragment"
case UpdateFromSQLFragment:
return "UpdateFromSQLFragment"
case ReturningSQLFragment:
return "ReturningSQLFragment"
case InsertBeingSQLFragment:
return "InsertBeingSQLFragment"
case DeleteBeginSQLFragment:
return "DeleteBeginSQLFragment"
case TruncateSQLFragment:
return "TruncateSQLFragment"
case WindowSQLFragment:
return "WindowSQLFragment"
}
return fmt.Sprintf("%d", sf)
}
//nolint:funlen
func DefaultDialectOptions() *SQLDialectOptions {
return &SQLDialectOptions{
SupportsOrderByOnDelete: false,
SupportsDeleteTableHint: false,
SupportsOrderByOnUpdate: false,
SupportsLimitOnDelete: false,
SupportsLimitOnUpdate: false,
SupportsReturn: true,
SupportsConflictUpdateWhere: true,
SupportsInsertIgnoreSyntax: false,
SupportsConflictTarget: true,
SupportsWithCTE: true,
SupportsWithCTERecursive: true,
SupportsDistinctOn: true,
WrapCompoundsInParens: true,
SupportsWindowFunction: true,
SupportsLateral: true,
SupportsMultipleUpdateTables: true,
UseFromClauseForMultipleUpdateTables: true,
UpdateClause: []byte("UPDATE"),
InsertClause: []byte("INSERT INTO"),
InsertIgnoreClause: []byte("INSERT IGNORE INTO"),
SelectClause: []byte("SELECT"),
DeleteClause: []byte("DELETE"),
TruncateClause: []byte("TRUNCATE"),
WithFragment: []byte("WITH "),
RecursiveFragment: []byte("RECURSIVE "),
CascadeFragment: []byte(" CASCADE"),
RestrictFragment: []byte(" RESTRICT"),
DefaultValuesFragment: []byte(" DEFAULT VALUES"),
ValuesFragment: []byte(" VALUES "),
IdentityFragment: []byte(" IDENTITY"),
SetFragment: []byte(" SET "),
DistinctFragment: []byte("DISTINCT"),
ReturningFragment: []byte(" RETURNING "),
FromFragment: []byte(" FROM"),
UsingFragment: []byte(" USING "),
OnFragment: []byte(" ON "),
WhereFragment: []byte(" WHERE "),
GroupByFragment: []byte(" GROUP BY "),
HavingFragment: []byte(" HAVING "),
WindowFragment: []byte(" WINDOW "),
WindowPartitionByFragment: []byte("PARTITION BY "),
WindowOrderByFragment: []byte("ORDER BY "),
WindowOverFragment: []byte(" OVER "),
OrderByFragment: []byte(" ORDER BY "),
FetchFragment: []byte(" "),
LimitFragment: []byte(" LIMIT "),
OffsetFragment: []byte(" OFFSET "),
ForUpdateFragment: []byte(" FOR UPDATE "),
ForNoKeyUpdateFragment: []byte(" FOR NO KEY UPDATE "),
ForShareFragment: []byte(" FOR SHARE "),
ForKeyShareFragment: []byte(" FOR KEY SHARE "),
OfFragment: []byte("OF "),
NowaitFragment: []byte("NOWAIT"),
SkipLockedFragment: []byte("SKIP LOCKED"),
LateralFragment: []byte("LATERAL "),
AsFragment: []byte(" AS "),
AscFragment: []byte(" ASC"),
DescFragment: []byte(" DESC"),
NullsFirstFragment: []byte(" NULLS FIRST"),
NullsLastFragment: []byte(" NULLS LAST"),
AndFragment: []byte(" AND "),
OrFragment: []byte(" OR "),
UnionFragment: []byte(" UNION "),
UnionAllFragment: []byte(" UNION ALL "),
IntersectFragment: []byte(" INTERSECT "),
IntersectAllFragment: []byte(" INTERSECT ALL "),
ConflictFragment: []byte(" ON CONFLICT"),
ConflictDoUpdateFragment: []byte(" DO UPDATE SET "),
ConflictDoNothingFragment: []byte(" DO NOTHING"),
CastFragment: []byte("CAST"),
CaseFragment: []byte("CASE "),
WhenFragment: []byte(" WHEN "),
ThenFragment: []byte(" THEN "),
ElseFragment: []byte(" ELSE "),
EndFragment: []byte(" END"),
Null: []byte("NULL"),
True: []byte("TRUE"),
False: []byte("FALSE"),
PlaceHolderFragment: []byte("?"),
QuoteRune: '"',
StringQuote: '\'',
SetOperatorRune: '=',
CommaRune: ',',
SpaceRune: ' ',
LeftParenRune: '(',
RightParenRune: ')',
StarRune: '*',
PeriodRune: '.',
EmptyString: "",
BooleanOperatorLookup: map[exp.BooleanOperation][]byte{
exp.EqOp: []byte("="),
exp.NeqOp: []byte("!="),
exp.GtOp: []byte(">"),
exp.GteOp: []byte(">="),
exp.LtOp: []byte("<"),
exp.LteOp: []byte("<="),
exp.InOp: []byte("IN"),
exp.NotInOp: []byte("NOT IN"),
exp.IsOp: []byte("IS"),
exp.IsNotOp: []byte("IS NOT"),
exp.LikeOp: []byte("LIKE"),
exp.NotLikeOp: []byte("NOT LIKE"),
exp.ILikeOp: []byte("ILIKE"),
exp.NotILikeOp: []byte("NOT ILIKE"),
exp.RegexpLikeOp: []byte("~"),
exp.RegexpNotLikeOp: []byte("!~"),
exp.RegexpILikeOp: []byte("~*"),
exp.RegexpNotILikeOp: []byte("!~*"),
},
BitwiseOperatorLookup: map[exp.BitwiseOperation][]byte{
exp.BitwiseInversionOp: []byte("~"),
exp.BitwiseOrOp: []byte("|"),
exp.BitwiseAndOp: []byte("&"),
exp.BitwiseXorOp: []byte("#"),
exp.BitwiseLeftShiftOp: []byte("<<"),
exp.BitwiseRightShiftOp: []byte(">>"),
},
RangeOperatorLookup: map[exp.RangeOperation][]byte{
exp.BetweenOp: []byte("BETWEEN"),
exp.NotBetweenOp: []byte("NOT BETWEEN"),
},
JoinTypeLookup: map[exp.JoinType][]byte{
exp.InnerJoinType: []byte(" INNER JOIN "),
exp.FullOuterJoinType: []byte(" FULL OUTER JOIN "),
exp.RightOuterJoinType: []byte(" RIGHT OUTER JOIN "),
exp.LeftOuterJoinType: []byte(" LEFT OUTER JOIN "),
exp.FullJoinType: []byte(" FULL JOIN "),
exp.RightJoinType: []byte(" RIGHT JOIN "),
exp.LeftJoinType: []byte(" LEFT JOIN "),
exp.NaturalJoinType: []byte(" NATURAL JOIN "),
exp.NaturalLeftJoinType: []byte(" NATURAL LEFT JOIN "),
exp.NaturalRightJoinType: []byte(" NATURAL RIGHT JOIN "),
exp.NaturalFullJoinType: []byte(" NATURAL FULL JOIN "),
exp.CrossJoinType: []byte(" CROSS JOIN "),
},
TimeFormat: time.RFC3339Nano,
BooleanDataTypeSupported: true,
UseLiteralIsBools: true,
EscapedRunes: map[rune][]byte{
'\'': []byte("''"),
},
SelectSQLOrder: []SQLFragmentType{
CommonTableSQLFragment,
SelectSQLFragment,
FromSQLFragment,
JoinSQLFragment,
WhereSQLFragment,
GroupBySQLFragment,
HavingSQLFragment,
WindowSQLFragment,
CompoundsSQLFragment,
OrderSQLFragment,
LimitSQLFragment,
OffsetSQLFragment,
ForSQLFragment,
},
UpdateSQLOrder: []SQLFragmentType{
CommonTableSQLFragment,
UpdateBeginSQLFragment,
SourcesSQLFragment,
UpdateSQLFragment,
UpdateFromSQLFragment,
WhereSQLFragment,
OrderSQLFragment,
LimitSQLFragment,
ReturningSQLFragment,
},
InsertSQLOrder: []SQLFragmentType{
CommonTableSQLFragment,
InsertBeingSQLFragment,
IntoSQLFragment,
InsertSQLFragment,
ReturningSQLFragment,
},
DeleteSQLOrder: []SQLFragmentType{
CommonTableSQLFragment,
DeleteBeginSQLFragment,
FromSQLFragment,
WhereSQLFragment,
OrderSQLFragment,
LimitSQLFragment,
ReturningSQLFragment,
},
TruncateSQLOrder: []SQLFragmentType{
TruncateSQLFragment,
},
}
}

15
vendor/github.com/doug-martin/goqu/v9/sqlgen/sqlgen.go generated vendored Normal file
View File

@@ -0,0 +1,15 @@
package sqlgen
import "time"
var timeLocation = time.UTC
// Set the location to use when interpolating time.Time instances. See https://golang.org/pkg/time/#LoadLocation
// NOTE: This has no effect when using prepared statements.
func SetTimeLocation(loc *time.Location) {
timeLocation = loc
}
func GetTimeLocation() *time.Location {
return timeLocation
}

View File

@@ -0,0 +1,64 @@
package sqlgen
import (
"strings"
"github.com/doug-martin/goqu/v9/exp"
"github.com/doug-martin/goqu/v9/internal/errors"
"github.com/doug-martin/goqu/v9/internal/sb"
)
type (
// An adapter interface to be used by a Dataset to generate SQL for a specific dialect.
// See DefaultAdapter for a concrete implementation and examples.
TruncateSQLGenerator interface {
Dialect() string
Generate(b sb.SQLBuilder, clauses exp.TruncateClauses)
}
// The default adapter. This class should be used when building a new adapter. When creating a new adapter you can
// either override methods, or more typically update default values.
// See (github.com/doug-martin/goqu/dialect/postgres)
truncateSQLGenerator struct {
CommonSQLGenerator
}
)
var errNoSourceForTruncate = errors.New("no source found when generating truncate sql")
func NewTruncateSQLGenerator(dialect string, do *SQLDialectOptions) TruncateSQLGenerator {
return &truncateSQLGenerator{NewCommonSQLGenerator(dialect, do)}
}
func (tsg *truncateSQLGenerator) Generate(b sb.SQLBuilder, clauses exp.TruncateClauses) {
if !clauses.HasTable() {
b.SetError(errNoSourceForTruncate)
return
}
for _, f := range tsg.DialectOptions().TruncateSQLOrder {
if b.Error() != nil {
return
}
switch f {
case TruncateSQLFragment:
tsg.TruncateSQL(b, clauses.Table(), clauses.Options())
default:
b.SetError(ErrNotSupportedFragment("TRUNCATE", f))
}
}
}
// Generates a TRUNCATE statement
func (tsg *truncateSQLGenerator) TruncateSQL(b sb.SQLBuilder, from exp.ColumnListExpression, opts exp.TruncateOptions) {
b.Write(tsg.DialectOptions().TruncateClause)
tsg.SourcesSQL(b, from)
if opts.Identity != tsg.DialectOptions().EmptyString {
b.WriteRunes(tsg.DialectOptions().SpaceRune).
WriteStrings(strings.ToUpper(opts.Identity)).
Write(tsg.DialectOptions().IdentityFragment)
}
if opts.Cascade {
b.Write(tsg.DialectOptions().CascadeFragment)
} else if opts.Restrict {
b.Write(tsg.DialectOptions().RestrictFragment)
}
}

View File

@@ -0,0 +1,112 @@
package sqlgen
import (
"github.com/doug-martin/goqu/v9/exp"
"github.com/doug-martin/goqu/v9/internal/errors"
"github.com/doug-martin/goqu/v9/internal/sb"
)
type (
// An adapter interface to be used by a Dataset to generate SQL for a specific dialect.
// See DefaultAdapter for a concrete implementation and examples.
UpdateSQLGenerator interface {
Dialect() string
Generate(b sb.SQLBuilder, clauses exp.UpdateClauses)
}
// The default adapter. This class should be used when building a new adapter. When creating a new adapter you can
// either override methods, or more typically update default values.
// See (github.com/doug-martin/goqu/dialect/postgres)
updateSQLGenerator struct {
CommonSQLGenerator
}
)
var (
ErrNoSourceForUpdate = errors.New("no source found when generating update sql")
ErrNoSetValuesForUpdate = errors.New("no set values found when generating UPDATE sql")
)
func NewUpdateSQLGenerator(dialect string, do *SQLDialectOptions) UpdateSQLGenerator {
return &updateSQLGenerator{NewCommonSQLGenerator(dialect, do)}
}
func (usg *updateSQLGenerator) Generate(b sb.SQLBuilder, clauses exp.UpdateClauses) {
if !clauses.HasTable() {
b.SetError(ErrNoSourceForUpdate)
return
}
if !clauses.HasSetValues() {
b.SetError(ErrNoSetValuesForUpdate)
return
}
if !usg.DialectOptions().SupportsMultipleUpdateTables && clauses.HasFrom() {
b.SetError(errors.New("%s dialect does not support multiple tables in UPDATE", usg.Dialect()))
}
updates, err := exp.NewUpdateExpressions(clauses.SetValues())
if err != nil {
b.SetError(err)
return
}
for _, f := range usg.DialectOptions().UpdateSQLOrder {
if b.Error() != nil {
return
}
switch f {
case CommonTableSQLFragment:
usg.ExpressionSQLGenerator().Generate(b, clauses.CommonTables())
case UpdateBeginSQLFragment:
usg.UpdateBeginSQL(b)
case SourcesSQLFragment:
usg.updateTableSQL(b, clauses)
case UpdateSQLFragment:
usg.UpdateExpressionsSQL(b, updates...)
case UpdateFromSQLFragment:
usg.updateFromSQL(b, clauses.From())
case WhereSQLFragment:
usg.WhereSQL(b, clauses.Where())
case OrderSQLFragment:
if usg.DialectOptions().SupportsOrderByOnUpdate {
usg.OrderSQL(b, clauses.Order())
}
case LimitSQLFragment:
if usg.DialectOptions().SupportsLimitOnUpdate {
usg.LimitSQL(b, clauses.Limit())
}
case ReturningSQLFragment:
usg.ReturningSQL(b, clauses.Returning())
default:
b.SetError(ErrNotSupportedFragment("UPDATE", f))
}
}
}
// Adds the correct fragment to being an UPDATE statement
func (usg *updateSQLGenerator) UpdateBeginSQL(b sb.SQLBuilder) {
b.Write(usg.DialectOptions().UpdateClause)
}
// Adds column setters in an update SET clause
func (usg *updateSQLGenerator) UpdateExpressionsSQL(b sb.SQLBuilder, updates ...exp.UpdateExpression) {
b.Write(usg.DialectOptions().SetFragment)
usg.UpdateExpressionSQL(b, updates...)
}
func (usg *updateSQLGenerator) updateTableSQL(b sb.SQLBuilder, uc exp.UpdateClauses) {
b.WriteRunes(usg.DialectOptions().SpaceRune)
usg.ExpressionSQLGenerator().Generate(b, uc.Table())
if uc.HasFrom() {
if !usg.DialectOptions().UseFromClauseForMultipleUpdateTables {
b.WriteRunes(usg.DialectOptions().CommaRune)
usg.ExpressionSQLGenerator().Generate(b, uc.From())
}
}
}
func (usg *updateSQLGenerator) updateFromSQL(b sb.SQLBuilder, ce exp.ColumnListExpression) {
if ce == nil || ce.IsEmpty() {
return
}
if usg.DialectOptions().UseFromClauseForMultipleUpdateTables {
usg.FromSQL(b, ce)
}
}

View File

@@ -0,0 +1,169 @@
package goqu
import (
"github.com/doug-martin/goqu/v9/exec"
"github.com/doug-martin/goqu/v9/exp"
"github.com/doug-martin/goqu/v9/internal/sb"
)
type TruncateDataset struct {
dialect SQLDialect
clauses exp.TruncateClauses
isPrepared prepared
queryFactory exec.QueryFactory
err error
}
// used internally by database to create a database with a specific adapter
func newTruncateDataset(d string, queryFactory exec.QueryFactory) *TruncateDataset {
return &TruncateDataset{
clauses: exp.NewTruncateClauses(),
dialect: GetDialect(d),
queryFactory: queryFactory,
}
}
func Truncate(table ...interface{}) *TruncateDataset {
return newTruncateDataset("default", nil).Table(table...)
}
// Sets the adapter used to serialize values and create the SQL statement
func (td *TruncateDataset) WithDialect(dl string) *TruncateDataset {
ds := td.copy(td.GetClauses())
ds.dialect = GetDialect(dl)
return ds
}
// Set the parameter interpolation behavior. See examples
//
// prepared: If true the dataset WILL NOT interpolate the parameters.
func (td *TruncateDataset) Prepared(prepared bool) *TruncateDataset {
ret := td.copy(td.clauses)
ret.isPrepared = preparedFromBool(prepared)
return ret
}
func (td *TruncateDataset) IsPrepared() bool {
return td.isPrepared.Bool()
}
// Returns the current adapter on the dataset
func (td *TruncateDataset) Dialect() SQLDialect {
return td.dialect
}
// Returns the current adapter on the dataset
func (td *TruncateDataset) SetDialect(dialect SQLDialect) *TruncateDataset {
cd := td.copy(td.GetClauses())
cd.dialect = dialect
return cd
}
func (td *TruncateDataset) Expression() exp.Expression {
return td
}
// Clones the dataset
func (td *TruncateDataset) Clone() exp.Expression {
return td.copy(td.clauses)
}
// Returns the current clauses on the dataset.
func (td *TruncateDataset) GetClauses() exp.TruncateClauses {
return td.clauses
}
// used interally to copy the dataset
func (td *TruncateDataset) copy(clauses exp.TruncateClauses) *TruncateDataset {
return &TruncateDataset{
dialect: td.dialect,
clauses: clauses,
isPrepared: td.isPrepared,
queryFactory: td.queryFactory,
err: td.err,
}
}
// Adds a FROM clause. This return a new dataset with the original sources replaced. See examples.
// You can pass in the following.
// string: Will automatically be turned into an identifier
// IdentifierExpression
// LiteralExpression: (See Literal) Will use the literal SQL
func (td *TruncateDataset) Table(table ...interface{}) *TruncateDataset {
return td.copy(td.clauses.SetTable(exp.NewColumnListExpression(table...)))
}
// Adds a CASCADE clause
func (td *TruncateDataset) Cascade() *TruncateDataset {
opts := td.clauses.Options()
opts.Cascade = true
return td.copy(td.clauses.SetOptions(opts))
}
// Clears the CASCADE clause
func (td *TruncateDataset) NoCascade() *TruncateDataset {
opts := td.clauses.Options()
opts.Cascade = false
return td.copy(td.clauses.SetOptions(opts))
}
// Adds a RESTRICT clause
func (td *TruncateDataset) Restrict() *TruncateDataset {
opts := td.clauses.Options()
opts.Restrict = true
return td.copy(td.clauses.SetOptions(opts))
}
// Clears the RESTRICT clause
func (td *TruncateDataset) NoRestrict() *TruncateDataset {
opts := td.clauses.Options()
opts.Restrict = false
return td.copy(td.clauses.SetOptions(opts))
}
// Add a IDENTITY clause (e.g. RESTART)
func (td *TruncateDataset) Identity(identity string) *TruncateDataset {
opts := td.clauses.Options()
opts.Identity = identity
return td.copy(td.clauses.SetOptions(opts))
}
// Get any error that has been set or nil if no error has been set.
func (td *TruncateDataset) Error() error {
return td.err
}
// Set an error on the dataset if one has not already been set. This error will be returned by a future call to Error
// or as part of ToSQL. This can be used by end users to record errors while building up queries without having to
// track those separately.
func (td *TruncateDataset) SetError(err error) *TruncateDataset {
if td.err == nil {
td.err = err
}
return td
}
// Generates a TRUNCATE sql statement, if Prepared has been called with true then the parameters will not be interpolated.
// See examples.
//
// Errors:
// * There is an error generating the SQL
func (td *TruncateDataset) ToSQL() (sql string, params []interface{}, err error) {
return td.truncateSQLBuilder().ToSQL()
}
// Generates the TRUNCATE sql, and returns an Exec struct with the sql set to the TRUNCATE statement
// db.From("test").Truncate().Executor().Exec()
func (td *TruncateDataset) Executor() exec.QueryExecutor {
return td.queryFactory.FromSQLBuilder(td.truncateSQLBuilder())
}
func (td *TruncateDataset) truncateSQLBuilder() sb.SQLBuilder {
buf := sb.NewSQLBuilder(td.isPrepared.Bool())
if td.err != nil {
return buf.SetError(td.err)
}
td.dialect.ToTruncateSQL(buf, td.clauses)
return buf
}

245
vendor/github.com/doug-martin/goqu/v9/update_dataset.go generated vendored Normal file
View File

@@ -0,0 +1,245 @@
package goqu
import (
"github.com/doug-martin/goqu/v9/exec"
"github.com/doug-martin/goqu/v9/exp"
"github.com/doug-martin/goqu/v9/internal/errors"
"github.com/doug-martin/goqu/v9/internal/sb"
)
type UpdateDataset struct {
dialect SQLDialect
clauses exp.UpdateClauses
isPrepared prepared
queryFactory exec.QueryFactory
err error
}
var ErrUnsupportedUpdateTableType = errors.New("unsupported table type, a string or identifier expression is required")
// used internally by database to create a database with a specific adapter
func newUpdateDataset(d string, queryFactory exec.QueryFactory) *UpdateDataset {
return &UpdateDataset{
clauses: exp.NewUpdateClauses(),
dialect: GetDialect(d),
queryFactory: queryFactory,
}
}
func Update(table interface{}) *UpdateDataset {
return newUpdateDataset("default", nil).Table(table)
}
// Set the parameter interpolation behavior. See examples
//
// prepared: If true the dataset WILL NOT interpolate the parameters.
func (ud *UpdateDataset) Prepared(prepared bool) *UpdateDataset {
ret := ud.copy(ud.clauses)
ret.isPrepared = preparedFromBool(prepared)
return ret
}
func (ud *UpdateDataset) IsPrepared() bool {
return ud.isPrepared.Bool()
}
// Sets the adapter used to serialize values and create the SQL statement
func (ud *UpdateDataset) WithDialect(dl string) *UpdateDataset {
ds := ud.copy(ud.GetClauses())
ds.dialect = GetDialect(dl)
return ds
}
// Returns the current adapter on the dataset
func (ud *UpdateDataset) Dialect() SQLDialect {
return ud.dialect
}
// Returns the current adapter on the dataset
func (ud *UpdateDataset) SetDialect(dialect SQLDialect) *UpdateDataset {
cd := ud.copy(ud.GetClauses())
cd.dialect = dialect
return cd
}
func (ud *UpdateDataset) Expression() exp.Expression {
return ud
}
// Clones the dataset
func (ud *UpdateDataset) Clone() exp.Expression {
return ud.copy(ud.clauses)
}
// Returns the current clauses on the dataset.
func (ud *UpdateDataset) GetClauses() exp.UpdateClauses {
return ud.clauses
}
// used internally to copy the dataset
func (ud *UpdateDataset) copy(clauses exp.UpdateClauses) *UpdateDataset {
return &UpdateDataset{
dialect: ud.dialect,
clauses: clauses,
isPrepared: ud.isPrepared,
queryFactory: ud.queryFactory,
err: ud.err,
}
}
// Creates a WITH clause for a common table expression (CTE).
//
// The name will be available to use in the UPDATE from in the associated query; and can optionally
// contain a list of column names "name(col1, col2, col3)".
//
// The name will refer to the results of the specified subquery.
func (ud *UpdateDataset) With(name string, subquery exp.Expression) *UpdateDataset {
return ud.copy(ud.clauses.CommonTablesAppend(exp.NewCommonTableExpression(false, name, subquery)))
}
// Creates a WITH RECURSIVE clause for a common table expression (CTE)
//
// The name will be available to use in the UPDATE from in the associated query; and must
// contain a list of column names "name(col1, col2, col3)" for a recursive clause.
//
// The name will refer to the results of the specified subquery. The subquery for
// a recursive query will always end with a UNION or UNION ALL with a clause that
// refers to the CTE by name.
func (ud *UpdateDataset) WithRecursive(name string, subquery exp.Expression) *UpdateDataset {
return ud.copy(ud.clauses.CommonTablesAppend(exp.NewCommonTableExpression(true, name, subquery)))
}
// Sets the table to update.
func (ud *UpdateDataset) Table(table interface{}) *UpdateDataset {
switch t := table.(type) {
case exp.Expression:
return ud.copy(ud.clauses.SetTable(t))
case string:
return ud.copy(ud.clauses.SetTable(exp.ParseIdentifier(t)))
default:
panic(ErrUnsupportedUpdateTableType)
}
}
// Sets the values to use in the SET clause. See examples.
func (ud *UpdateDataset) Set(values interface{}) *UpdateDataset {
return ud.copy(ud.clauses.SetSetValues(values))
}
// Allows specifying other tables to reference in your update (If your dialect supports it). See examples.
func (ud *UpdateDataset) From(tables ...interface{}) *UpdateDataset {
return ud.copy(ud.clauses.SetFrom(exp.NewColumnListExpression(tables...)))
}
// Adds a WHERE clause. See examples.
func (ud *UpdateDataset) Where(expressions ...exp.Expression) *UpdateDataset {
return ud.copy(ud.clauses.WhereAppend(expressions...))
}
// Removes the WHERE clause. See examples.
func (ud *UpdateDataset) ClearWhere() *UpdateDataset {
return ud.copy(ud.clauses.ClearWhere())
}
// Adds a ORDER clause. If the ORDER is currently set it replaces it. See examples.
func (ud *UpdateDataset) Order(order ...exp.OrderedExpression) *UpdateDataset {
return ud.copy(ud.clauses.SetOrder(order...))
}
// Adds a more columns to the current ORDER BY clause. If no order has be previously specified it is the same as
// calling Order. See examples.
func (ud *UpdateDataset) OrderAppend(order ...exp.OrderedExpression) *UpdateDataset {
return ud.copy(ud.clauses.OrderAppend(order...))
}
// Adds a more columns to the beginning of the current ORDER BY clause. If no order has be previously specified it is the same as
// calling Order. See examples.
func (ud *UpdateDataset) OrderPrepend(order ...exp.OrderedExpression) *UpdateDataset {
return ud.copy(ud.clauses.OrderPrepend(order...))
}
// Removes the ORDER BY clause. See examples.
func (ud *UpdateDataset) ClearOrder() *UpdateDataset {
return ud.copy(ud.clauses.ClearOrder())
}
// Adds a LIMIT clause. If the LIMIT is currently set it replaces it. See examples.
func (ud *UpdateDataset) Limit(limit uint) *UpdateDataset {
if limit > 0 {
return ud.copy(ud.clauses.SetLimit(limit))
}
return ud.copy(ud.clauses.ClearLimit())
}
// Adds a LIMIT ALL clause. If the LIMIT is currently set it replaces it. See examples.
func (ud *UpdateDataset) LimitAll() *UpdateDataset {
return ud.copy(ud.clauses.SetLimit(L("ALL")))
}
// Removes the LIMIT clause.
func (ud *UpdateDataset) ClearLimit() *UpdateDataset {
return ud.copy(ud.clauses.ClearLimit())
}
// Adds a RETURNING clause to the dataset if the adapter supports it. See examples.
func (ud *UpdateDataset) Returning(returning ...interface{}) *UpdateDataset {
return ud.copy(ud.clauses.SetReturning(exp.NewColumnListExpression(returning...)))
}
// Get any error that has been set or nil if no error has been set.
func (ud *UpdateDataset) Error() error {
return ud.err
}
// Set an error on the dataset if one has not already been set. This error will be returned by a future call to Error
// or as part of ToSQL. This can be used by end users to record errors while building up queries without having to
// track those separately.
func (ud *UpdateDataset) SetError(err error) *UpdateDataset {
if ud.err == nil {
ud.err = err
}
return ud
}
// Generates an UPDATE sql statement, if Prepared has been called with true then the parameters will not be interpolated.
// See examples.
//
// Errors:
// * There is an error generating the SQL
func (ud *UpdateDataset) ToSQL() (sql string, params []interface{}, err error) {
return ud.updateSQLBuilder().ToSQL()
}
// Appends this Dataset's UPDATE statement to the SQLBuilder
// This is used internally when using updates in CTEs
func (ud *UpdateDataset) AppendSQL(b sb.SQLBuilder) {
if ud.err != nil {
b.SetError(ud.err)
return
}
ud.dialect.ToUpdateSQL(b, ud.GetClauses())
}
func (ud *UpdateDataset) GetAs() exp.IdentifierExpression {
return nil
}
func (ud *UpdateDataset) ReturnsColumns() bool {
return ud.clauses.HasReturning()
}
// Generates the UPDATE sql, and returns an exec.QueryExecutor with the sql set to the UPDATE statement
// db.Update("test").Set(Record{"name":"Bob", update: time.Now()}).Executor()
func (ud *UpdateDataset) Executor() exec.QueryExecutor {
return ud.queryFactory.FromSQLBuilder(ud.updateSQLBuilder())
}
func (ud *UpdateDataset) updateSQLBuilder() sb.SQLBuilder {
buf := sb.NewSQLBuilder(ud.isPrepared.Bool())
if ud.err != nil {
return buf.SetError(ud.err)
}
ud.dialect.ToUpdateSQL(buf, ud.clauses)
return buf
}

182
vendor/github.com/doug-martin/goqu/v9/wait-for-it.sh generated vendored Normal file
View File

@@ -0,0 +1,182 @@
#!/usr/bin/env bash
# Use this script to test if a given TCP host/port are available
WAITFORIT_cmdname=${0##*/}
echoerr() { if [[ $WAITFORIT_QUIET -ne 1 ]]; then echo "$@" 1>&2; fi }
usage()
{
cat << USAGE >&2
Usage:
$WAITFORIT_cmdname host:port [-s] [-t timeout] [-- command args]
-h HOST | --host=HOST Host or IP under test
-p PORT | --port=PORT TCP port under test
Alternatively, you specify the host and port as host:port
-s | --strict Only execute subcommand if the test succeeds
-q | --quiet Don't output any status messages
-t TIMEOUT | --timeout=TIMEOUT
Timeout in seconds, zero for no timeout
-- COMMAND ARGS Execute command with args after the test finishes
USAGE
exit 1
}
wait_for()
{
if [[ $WAITFORIT_TIMEOUT -gt 0 ]]; then
echoerr "$WAITFORIT_cmdname: waiting $WAITFORIT_TIMEOUT seconds for $WAITFORIT_HOST:$WAITFORIT_PORT"
else
echoerr "$WAITFORIT_cmdname: waiting for $WAITFORIT_HOST:$WAITFORIT_PORT without a timeout"
fi
WAITFORIT_start_ts=$(date +%s)
while :
do
if [[ $WAITFORIT_ISBUSY -eq 1 ]]; then
nc -z $WAITFORIT_HOST $WAITFORIT_PORT
WAITFORIT_result=$?
else
(echo > /dev/tcp/$WAITFORIT_HOST/$WAITFORIT_PORT) >/dev/null 2>&1
WAITFORIT_result=$?
fi
if [[ $WAITFORIT_result -eq 0 ]]; then
WAITFORIT_end_ts=$(date +%s)
echoerr "$WAITFORIT_cmdname: $WAITFORIT_HOST:$WAITFORIT_PORT is available after $((WAITFORIT_end_ts - WAITFORIT_start_ts)) seconds"
break
fi
sleep 1
done
return $WAITFORIT_result
}
wait_for_wrapper()
{
# In order to support SIGINT during timeout: http://unix.stackexchange.com/a/57692
if [[ $WAITFORIT_QUIET -eq 1 ]]; then
timeout $WAITFORIT_BUSYTIMEFLAG $WAITFORIT_TIMEOUT $0 --quiet --child --host=$WAITFORIT_HOST --port=$WAITFORIT_PORT --timeout=$WAITFORIT_TIMEOUT &
else
timeout $WAITFORIT_BUSYTIMEFLAG $WAITFORIT_TIMEOUT $0 --child --host=$WAITFORIT_HOST --port=$WAITFORIT_PORT --timeout=$WAITFORIT_TIMEOUT &
fi
WAITFORIT_PID=$!
trap "kill -INT -$WAITFORIT_PID" INT
wait $WAITFORIT_PID
WAITFORIT_RESULT=$?
if [[ $WAITFORIT_RESULT -ne 0 ]]; then
echoerr "$WAITFORIT_cmdname: timeout occurred after waiting $WAITFORIT_TIMEOUT seconds for $WAITFORIT_HOST:$WAITFORIT_PORT"
fi
return $WAITFORIT_RESULT
}
# process arguments
while [[ $# -gt 0 ]]
do
case "$1" in
*:* )
WAITFORIT_hostport=(${1//:/ })
WAITFORIT_HOST=${WAITFORIT_hostport[0]}
WAITFORIT_PORT=${WAITFORIT_hostport[1]}
shift 1
;;
--child)
WAITFORIT_CHILD=1
shift 1
;;
-q | --quiet)
WAITFORIT_QUIET=1
shift 1
;;
-s | --strict)
WAITFORIT_STRICT=1
shift 1
;;
-h)
WAITFORIT_HOST="$2"
if [[ $WAITFORIT_HOST == "" ]]; then break; fi
shift 2
;;
--host=*)
WAITFORIT_HOST="${1#*=}"
shift 1
;;
-p)
WAITFORIT_PORT="$2"
if [[ $WAITFORIT_PORT == "" ]]; then break; fi
shift 2
;;
--port=*)
WAITFORIT_PORT="${1#*=}"
shift 1
;;
-t)
WAITFORIT_TIMEOUT="$2"
if [[ $WAITFORIT_TIMEOUT == "" ]]; then break; fi
shift 2
;;
--timeout=*)
WAITFORIT_TIMEOUT="${1#*=}"
shift 1
;;
--)
shift
WAITFORIT_CLI=("$@")
break
;;
--help)
usage
;;
*)
echoerr "Unknown argument: $1"
usage
;;
esac
done
if [[ "$WAITFORIT_HOST" == "" || "$WAITFORIT_PORT" == "" ]]; then
echoerr "Error: you need to provide a host and port to test."
usage
fi
WAITFORIT_TIMEOUT=${WAITFORIT_TIMEOUT:-15}
WAITFORIT_STRICT=${WAITFORIT_STRICT:-0}
WAITFORIT_CHILD=${WAITFORIT_CHILD:-0}
WAITFORIT_QUIET=${WAITFORIT_QUIET:-0}
# Check to see if timeout is from busybox?
WAITFORIT_TIMEOUT_PATH=$(type -p timeout)
WAITFORIT_TIMEOUT_PATH=$(realpath $WAITFORIT_TIMEOUT_PATH 2>/dev/null || readlink -f $WAITFORIT_TIMEOUT_PATH)
WAITFORIT_BUSYTIMEFLAG=""
if [[ $WAITFORIT_TIMEOUT_PATH =~ "busybox" ]]; then
WAITFORIT_ISBUSY=1
# Check if busybox timeout uses -t flag
# (recent Alpine versions don't support -t anymore)
if timeout &>/dev/stdout | grep -q -e '-t '; then
WAITFORIT_BUSYTIMEFLAG="-t"
fi
else
WAITFORIT_ISBUSY=0
fi
if [[ $WAITFORIT_CHILD -gt 0 ]]; then
wait_for
WAITFORIT_RESULT=$?
exit $WAITFORIT_RESULT
else
if [[ $WAITFORIT_TIMEOUT -gt 0 ]]; then
wait_for_wrapper
WAITFORIT_RESULT=$?
else
wait_for
WAITFORIT_RESULT=$?
fi
fi
if [[ $WAITFORIT_CLI != "" ]]; then
if [[ $WAITFORIT_RESULT -ne 0 && $WAITFORIT_STRICT -eq 1 ]]; then
echoerr "$WAITFORIT_cmdname: strict mode, refusing to execute subprocess"
exit $WAITFORIT_RESULT
fi
exec "${WAITFORIT_CLI[@]}"
else
exit $WAITFORIT_RESULT
fi

File diff suppressed because it is too large Load Diff

View File

@@ -124,9 +124,9 @@ extern "C" {
** [sqlite3_libversion_number()], [sqlite3_sourceid()],
** [sqlite_version()] and [sqlite_source_id()].
*/
#define SQLITE_VERSION "3.34.0"
#define SQLITE_VERSION_NUMBER 3034000
#define SQLITE_SOURCE_ID "2020-12-01 16:14:00 a26b6597e3ae272231b96f9982c3bcc17ddec2f2b6eb4df06a224b91089fed5b"
#define SQLITE_VERSION "3.35.4"
#define SQLITE_VERSION_NUMBER 3035004
#define SQLITE_SOURCE_ID "2021-04-02 15:20:15 5d4c65779dab868b285519b19e4cf9d451d50c6048f06f653aa701ec212df45e"
/*
** CAPI3REF: Run-Time Library Version Numbers
@@ -2116,7 +2116,13 @@ struct sqlite3_mem_methods {
** The second parameter is a pointer to an integer into which
** is written 0 or 1 to indicate whether triggers are disabled or enabled
** following this call. The second parameter may be a NULL pointer, in
** which case the trigger setting is not reported back. </dd>
** which case the trigger setting is not reported back.
**
** <p>Originally this option disabled all triggers. ^(However, since
** SQLite version 3.35.0, TEMP triggers are still allowed even if
** this option is off. So, in other words, this option now only disables
** triggers in the main database schema or in the schemas of ATTACH-ed
** databases.)^ </dd>
**
** [[SQLITE_DBCONFIG_ENABLE_VIEW]]
** <dt>SQLITE_DBCONFIG_ENABLE_VIEW</dt>
@@ -2127,7 +2133,13 @@ struct sqlite3_mem_methods {
** The second parameter is a pointer to an integer into which
** is written 0 or 1 to indicate whether views are disabled or enabled
** following this call. The second parameter may be a NULL pointer, in
** which case the view setting is not reported back. </dd>
** which case the view setting is not reported back.
**
** <p>Originally this option disabled all views. ^(However, since
** SQLite version 3.35.0, TEMP views are still allowed even if
** this option is off. So, in other words, this option now only disables
** views in the main database schema or in the schemas of ATTACH-ed
** databases.)^ </dd>
**
** [[SQLITE_DBCONFIG_ENABLE_FTS3_TOKENIZER]]
** <dt>SQLITE_DBCONFIG_ENABLE_FTS3_TOKENIZER</dt>
@@ -3500,6 +3512,7 @@ SQLITE_API void sqlite3_progress_handler(sqlite3*, int, int(*)(void*), void*);
** that uses dot-files in place of posix advisory locking.
** <tr><td> file:data.db?mode=readonly <td>
** An error. "readonly" is not a valid option for the "mode" parameter.
** Use "ro" instead: "file:data.db?mode=ro".
** </table>
**
** ^URI hexadecimal escape sequences (%HH) are supported within the path and
@@ -3698,7 +3711,7 @@ SQLITE_API sqlite3_file *sqlite3_database_file_object(const char*);
** If the Y parameter to sqlite3_free_filename(Y) is anything other
** than a NULL pointer or a pointer previously acquired from
** sqlite3_create_filename(), then bad things such as heap
** corruption or segfaults may occur. The value Y should be
** corruption or segfaults may occur. The value Y should not be
** used again after sqlite3_free_filename(Y) has been called. This means
** that if the [sqlite3_vfs.xOpen()] method of a VFS has been called using Y,
** then the corresponding [sqlite3_module.xClose() method should also be
@@ -7766,7 +7779,8 @@ SQLITE_API int sqlite3_test_control(int op, ...);
#define SQLITE_TESTCTRL_PRNG_SEED 28
#define SQLITE_TESTCTRL_EXTRA_SCHEMA_CHECKS 29
#define SQLITE_TESTCTRL_SEEK_COUNT 30
#define SQLITE_TESTCTRL_LAST 30 /* Largest TESTCTRL */
#define SQLITE_TESTCTRL_TRACEFLAGS 31
#define SQLITE_TESTCTRL_LAST 31 /* Largest TESTCTRL */
/*
** CAPI3REF: SQL Keyword Checking
@@ -10439,6 +10453,14 @@ SQLITE_API int sqlite3session_patchset(
*/
SQLITE_API int sqlite3session_isempty(sqlite3_session *pSession);
/*
** CAPI3REF: Query for the amount of heap memory used by a session object.
**
** This API returns the total amount of heap memory in bytes currently
** used by the session object passed as the only argument.
*/
SQLITE_API sqlite3_int64 sqlite3session_memory_used(sqlite3_session *pSession);
/*
** CAPI3REF: Create An Iterator To Traverse A Changeset
** CONSTRUCTOR: sqlite3_changeset_iter
@@ -10541,18 +10563,23 @@ SQLITE_API int sqlite3changeset_next(sqlite3_changeset_iter *pIter);
** call to [sqlite3changeset_next()] must have returned [SQLITE_ROW]. If this
** is not the case, this function returns [SQLITE_MISUSE].
**
** If argument pzTab is not NULL, then *pzTab is set to point to a
** nul-terminated utf-8 encoded string containing the name of the table
** affected by the current change. The buffer remains valid until either
** sqlite3changeset_next() is called on the iterator or until the
** conflict-handler function returns. If pnCol is not NULL, then *pnCol is
** set to the number of columns in the table affected by the change. If
** pbIndirect is not NULL, then *pbIndirect is set to true (1) if the change
** Arguments pOp, pnCol and pzTab may not be NULL. Upon return, three
** outputs are set through these pointers:
**
** *pOp is set to one of [SQLITE_INSERT], [SQLITE_DELETE] or [SQLITE_UPDATE],
** depending on the type of change that the iterator currently points to;
**
** *pnCol is set to the number of columns in the table affected by the change; and
**
** *pzTab is set to point to a nul-terminated utf-8 encoded string containing
** the name of the table affected by the current change. The buffer remains
** valid until either sqlite3changeset_next() is called on the iterator
** or until the conflict-handler function returns.
**
** If pbIndirect is not NULL, then *pbIndirect is set to true (1) if the change
** is an indirect change, or false (0) otherwise. See the documentation for
** [sqlite3session_indirect()] for a description of direct and indirect
** changes. Finally, if pOp is not NULL, then *pOp is set to one of
** [SQLITE_INSERT], [SQLITE_DELETE] or [SQLITE_UPDATE], depending on the
** type of change that the iterator currently points to.
** changes.
**
** If no error occurs, SQLITE_OK is returned. If an error does occur, an
** SQLite error code is returned. The values of the output variables may not

View File

@@ -1676,7 +1676,7 @@ func (d *SQLiteDriver) Open(dsn string) (driver.Conn, error) {
//
// Because default is NORMAL this statement is always executed
if err := exec(fmt.Sprintf("PRAGMA synchronous = %s;", synchronousMode)); err != nil {
C.sqlite3_close_v2(db)
conn.Close()
return nil, err
}
@@ -2007,6 +2007,13 @@ func (s *SQLiteStmt) execSync(args []namedValue) (driver.Result, error) {
return &SQLiteResult{id: int64(rowid), changes: int64(changes)}, nil
}
// Readonly reports if this statement is considered readonly by SQLite.
//
// See: https://sqlite.org/c3ref/stmt_readonly.html
func (s *SQLiteStmt) Readonly() bool {
return C.sqlite3_stmt_readonly(s.s) == 1
}
// Close the rows.
func (rc *SQLiteRows) Close() error {
rc.s.mu.Lock()

View File

@@ -0,0 +1,21 @@
// +build sqlite_column_metadata
package sqlite3
/*
#ifndef USE_LIBSQLITE3
#cgo CFLAGS: -DSQLITE_ENABLE_COLUMN_METADATA
#include <sqlite3-binding.h>
#else
#include <sqlite3.h>
#endif
*/
import "C"
// ColumnTableName returns the table that is the origin of a particular result
// column in a SELECT statement.
//
// See https://www.sqlite.org/c3ref/column_database_name.html
func (s *SQLiteStmt) ColumnTableName(n int) string {
return C.GoString(C.sqlite3_column_table_name(s.s, C.int(n)))
}