-
Notifications
You must be signed in to change notification settings - Fork 5
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
0 parents
commit fc293fc
Showing
18 changed files
with
2,903 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,74 @@ | ||
# travel audience 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 [email protected]. All | ||
complaints will be reviewed and investigated and will result in a response that | ||
is deemed necessary and 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 [https://contributor-covenant.org/version/1/4][version] | ||
|
||
[homepage]: https://contributor-covenant.org | ||
[version]: https://contributor-covenant.org/version/1/4/ |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,16 @@ | ||
<!--- STOP! Before you open an issue please search this repository's issues to see if it has already been reported. This helps reduce duplicate issues from being created. --> | ||
|
||
### Expected Behaviour | ||
|
||
### Actual Behaviour | ||
|
||
### Steps to Reproduce | ||
|
||
|
||
### Optional additional info: | ||
|
||
#### Platform and Version | ||
|
||
#### Sample Code that illustrates the problem | ||
|
||
#### Logs taken while reproducing problem |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,11 @@ | ||
<!-- Thanks for sending a pull request! Here are some tips for you: | ||
If this PR closes another issue, add 'closes #<issue number>' somewhere in the PR summary. GitHub will automatically close that issue when this PR gets merged. Alternatively, adding 'refs #<issue number>' will not close the issue, but help provide the reviewer more context.--> | ||
|
||
**What this PR does / why we need it**: | ||
|
||
**Special notes for your reviewer**: | ||
|
||
**If applicable**: | ||
- [ ] this PR contains documentation | ||
- [ ] this PR contains unit tests | ||
- [ ] this PR has been tested for backwards compatibility |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,38 @@ | ||
# Contributing | ||
|
||
Thanks for choosing to contribute! | ||
|
||
The following are a set of guidelines to follow when contributing to this project. | ||
|
||
## Have A Question? | ||
|
||
Start by filing an issue. The existing committers on this project work to reach | ||
consensus around project direction and issue solutions within issue threads | ||
(when appropriate). | ||
|
||
## How to Contribute Code | ||
|
||
1. Fork the repo, develop and test your code changes. | ||
1. Submit a pull request. | ||
|
||
Lastly, please follow the [pull request template](.github/PULL_REQUEST_TEMPLATE.md) when | ||
submitting a pull request! | ||
|
||
#### Documentation PRs | ||
|
||
Documentation PRs will follow the same lifecycle as other PRs. They should also be labeled with the | ||
`docs` label. For documentation, special attention will be paid to spelling, grammar, and clarity | ||
(whereas those things don't matter *as* much for comments in code). | ||
|
||
<!-- ## Security Issues | ||
Security issues shouldn't be reported on this issue tracker. Instead, please email a report to | ||
[[email protected]](mailto:[email protected]). This will give | ||
us a chance to try to fix the issue before it is exploited in the wild. | ||
--> | ||
|
||
## Code Of Conduct | ||
|
||
This project adheres to the travel audience [code of conduct](.github/CODE_OF_CONDUCT.md). By participating, | ||
you are expected to uphold this code. Please report unacceptable behavior to | ||
[[email protected]](mailto:[email protected]). |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,21 @@ | ||
MIT License | ||
|
||
© Copyright 2019 travel audience. All rights reserved. | ||
|
||
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. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,216 @@ | ||
# Some simple SQL extensions for Go | ||
|
||
**go-sx** provides some extensions to the standard library `database/sql` package. It is designed for the programmer who wishes to use the full power of SQL without a heavy abstraction layer. | ||
|
||
## Goals | ||
|
||
The primary goal of **go-sx** is to eliminate boilerplate code. Specifically, **go-sx** attempts to address the following pain points: | ||
|
||
1. Transactions are clumsy. It would be nice to have a simple function to run a callback in a transaction. | ||
2. Error handling is clumsy. It would be nice to have errors within a transaction automatically exit the transaction and trigger a rollback. (This is nearly always what we want to do.) | ||
3. Scanning multiple columns is clumsy. It would be nice to have a simple way to scan into multiple struct fields at once. | ||
4. Constructing queries is clumsy, especially when there are a lot of columns. | ||
5. Iterating over result sets is clumsy. | ||
|
||
## Non-goals | ||
|
||
These are considered to be out of scope: | ||
|
||
1. Be an ORM. | ||
2. Write your queries for you. | ||
3. Suggest that we need 1:1 relationship between struct types and tables. | ||
4. Maintain database schemas. | ||
5. Abstract away differences between SQL dialects. | ||
6. Automatic type-manipulation. | ||
7. Magic. | ||
|
||
## Pain point #1: Transactions are clumsy. | ||
|
||
**go-sx** provides a function `Do` to run a transaction in a callback, automatically committing on success or rolling back on failure. | ||
|
||
Here is some simple code to run two queries in a transaction. The second query returns two values, which are read into variables `x` and `y`. | ||
|
||
```go | ||
tx, err := db.Begin() | ||
if err != nil { | ||
return err | ||
} | ||
if _, err := tx.Exec(query0); err != nil { | ||
tx.Rollback() | ||
return err | ||
} | ||
if err := tx.QueryRow(query1).Scan(&x, &y); err != nil { | ||
tx.Rollback() | ||
return err | ||
} | ||
if err := tx.Commit(); err != nil { | ||
return err | ||
} | ||
``` | ||
|
||
Using the `Do` function, we put the business logic into a callback function and have **go-sx** take care of the transaction logic. | ||
|
||
The `sx.Tx` object provided to the callback is the `sql.Tx` transaction object, extended with a few methods. If we call `tx.Fail()`, then the transaction is immediately aborted and rolled back. | ||
|
||
```go | ||
err := sx.Do(db, func (tx *sx.Tx) { | ||
if _, err := tx.Exec(query0); err != nil { | ||
tx.Fail(err) | ||
} | ||
if err := tx.QueryRow(query1).Scan(&x, &y); err != nil { | ||
tx.Fail(err) | ||
} | ||
}) | ||
``` | ||
|
||
Under the hood, `tx.Fail()` generates a panic which is recovered by `Do`. | ||
|
||
## Pain point #2: Error handling is clumsy. | ||
|
||
**go-sx** provides a collection of `Must***` methods which may be used inside of the callback to `Do`. Any error encountered while in a `Must***` method causes the transaction to be aborted and rolled back. | ||
|
||
Here is the code above, rewritten to use `Do`'s error handling. | ||
|
||
```go | ||
err := sx.Do(db, func (tx *sx.Tx) { | ||
tx.MustExec(query0) | ||
tx.MustQueryRow(query1).MustScan(&x, &y) | ||
}) | ||
``` | ||
|
||
## Pain point #3: Scanning multiple columns is clumsy. | ||
|
||
**go-sx** provides an `Addrs` function, which takes a struct and returns a slice of pointers to the elements. So instead of: | ||
|
||
```go | ||
row.Scan(&a.Width, &a.Height, &a.Depth) | ||
``` | ||
|
||
We can write: | ||
|
||
```go | ||
row.Scan(sx.Addrs(&a)...) | ||
``` | ||
|
||
Or better yet, let **go-sx** handle the errors: | ||
|
||
```go | ||
row.MustScan(sx.Addrs(&a)...) | ||
``` | ||
|
||
This is such a common pattern that we provide a shortcut to do this in one step: | ||
|
||
```go | ||
row.MustScans(&a) | ||
``` | ||
|
||
## Pain point #4: Constructing queries is clumsy. | ||
|
||
We would like **go-sx** to be able to construct some common queries for us. To this end, we define a simple way to match struct fields with database columns, and then provide some helper functions that use this matching to construct queries. | ||
|
||
By default, all exported struct fields match database columns whose name is the the field name snake_cased. The default can be overridden by explicitly tagging fields, much like what is done with the standard json encoder. Note that we don't care about the name of the table at this point. | ||
|
||
Here is a struct that can be used to scan columns `violin`, `viola`, `cello` and `contrabass`. | ||
|
||
```go | ||
type orchestra struct { | ||
Violin string | ||
Viola string | ||
Cello string | ||
Bass string `sx:"contrabass"` | ||
} | ||
``` | ||
|
||
We can use the helper function `SelectQuery` to construct a simple query. Then we can add the WHERE clause that we need and scan the result set into our struct. | ||
|
||
```go | ||
var spo orchestra | ||
|
||
wantID := 123 | ||
query := sx.UpdateQuery("symphony", &spo) + " WHERE id=?" // SELECT violin,viola,cello,contrabass FROM symphony WHERE id=? | ||
tx.MustQueryRow(query, wantID).MustScans(&spo) | ||
``` | ||
|
||
Note that a struct need not follow the database schema exactly. It's entirely possible to have various structs mapped to different columns of the same table, or even one struct that maps to a query on joined tables. | ||
|
||
In some cases it's useful to have a struct that is used for both selects and inserts, with some of the fields being used just for selects. This can be accomplished with the "readonly" tag. | ||
|
||
```go | ||
type orchestra1 struct { | ||
Violin string `sx:",readonly"` | ||
Viola string | ||
Cello string | ||
Bass string `sx:"contrabass"` | ||
} | ||
``` | ||
|
||
In some cases it's useful to have a struct field that is ignored by **go-sx**. This can be accomplished with the "-" tag. | ||
|
||
```go | ||
type orchestra2 struct { | ||
Violin string `sx:",readonly"` | ||
Viola string `sx:"-"` | ||
Cello string | ||
Bass string `sx:"contrabass"` | ||
} | ||
``` | ||
|
||
We can construct insert queries in a similar manner. Violin is read-only and Viola is ignored, so we only need to provide values for Cello and Bass. | ||
|
||
```go | ||
spo := orchestra2{Cello: "Strad", Bass: "Cecilio"} | ||
|
||
query := sx.InsertQuery("symphony", &spo) // INSERT INTO symphony (cello,contrabass) VALUES (?,?) | ||
tx.MustExec(query, sx.Values(&spo)...) | ||
``` | ||
|
||
We can contruct update queries this way too, and there is also an option to skip fields whose values are the zero values. | ||
|
||
```go | ||
spoChanges := orchestra2{Bass: "Strad"} | ||
|
||
wantID := 123 | ||
query, values := sx.UpdateQuery("symphony", &spoChanges) + " WHERE id=?" // UPDATE symphony SET contrabass=? WHERE id=? | ||
tx.MustExec(query, append(values, wantID)...) | ||
``` | ||
|
||
## Point point #5: Iterating over result sets is clumsy. | ||
|
||
**go-sx** provides an iterator called `Each` which runs a callback function on each row of a result set. Using the iterator simplifies this code: | ||
|
||
```go | ||
var orchestras []orchestra | ||
|
||
query := sx.SelectQuery("symphony", &orchestra{}) + " WHERE length(violin)>length(cello)" | ||
rows := tx.MustQuery(query) | ||
defer rows.Close() | ||
for rows.Next() { | ||
var o orchestra | ||
rows.MustScans(&o) | ||
orchestras = append(orchestras, o) | ||
} | ||
if err := rows.Err(); err != nil { | ||
tx.Fail(err) | ||
} | ||
``` | ||
|
||
To this: | ||
|
||
```go | ||
var orchestras []orchestra | ||
|
||
query := sx.SelectQuery("symphony", &orchestra{}) + " WHERE length(violin)>length(cello)" | ||
tx.MustQuery(query).Each(func (r *sx.Rows) { | ||
var o orchestra | ||
r.MustScans(&o) | ||
orchestras = append(orchestras, o) | ||
}) | ||
``` | ||
|
||
## Contributing | ||
|
||
Contributions are welcomed! Read the [Contributing Guide](CONTRIBUTING.md) for more information. | ||
|
||
## Licensing | ||
|
||
This project is licensed under the MIT License - see the [LICENSE](LICENSE.txt) file for details |
Oops, something went wrong.