Skip to content

Commit

Permalink
update docs
Browse files Browse the repository at this point in the history
  • Loading branch information
Thanh Nguyen committed May 31, 2023
1 parent cb579e1 commit 92f0942
Show file tree
Hide file tree
Showing 3 changed files with 99 additions and 0 deletions.
38 changes: 38 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,44 @@ func main() {
- `UPDATE`
- `DELETE`

## Transaction support

`godynamo` supports transactions that consist of write statements (e.g. `INSERT`, `UPDATE` and `DELETE`) since [v0.2.0](RELEASE-NOTES.md). Please note the following:

- Any limitation set by [DynamoDB/PartiQL](https://docs.aws.amazon.com/amazondynamodb/latest/developerguide/ql-reference.multiplestatements.transactions.html) will apply.
- [Table](SQL_TABLE.md) and [Index](SQL_INDEX.md) statements are not supported.
- `UPDATE`/`DELETE` with `RETURNING` and `SELECT` statements are not supported.

Example:
```go
tx, err := db.Begin()
if err != nil {
panic(err)
}
defer tx.Rollback()
result1, _ := tx.Exec(`INSERT INTO "tbltest" VALUE {'app': ?, 'user': ?, 'active': ?}`, "app0", "user1", true)
result2, _ := tx.Exec(`INSERT INTO "tbltest" VALUE {'app': ?, 'user': ?, 'duration': ?}`, "app0", "user2", 1.23)
err = tx.Commit()
if err != nil {
panic(err)
}
rowsAffected1, err1 := fmt.Println(result1.RowsAffected())
if err1 != nil {
panic(err1)
}
fmt.Println("RowsAffected:", rowsAffected1) // output "RowsAffected: 1"

rowsAffected2, err2 := fmt.Println(result2.RowsAffected())
if err2 != nil {
panic(err2)
}
fmt.Println("RowsAffected:", rowsAffected2) // output "RowsAffected: 1"
```

> If a statement's condition check fails (e.g. deleting non-existing item), the whole transaction will also fail. This behaviour is different from executing statements in non-transactional mode where failed condition check results in `0` affected row without error.
>
> You can use [`EXISTS` function](https://docs.aws.amazon.com/amazondynamodb/latest/developerguide/ql-functions.exists.html) for condition checking.
## License

MIT - See [LICENSE.md](LICENSE.md).
3 changes: 3 additions & 0 deletions stmt_document.go
Original file line number Diff line number Diff line change
Expand Up @@ -101,6 +101,9 @@ func (s *StmtSelect) Query(values []driver.Value) (driver.Rows, error) {
// @Available since v0.2.0
func (s *StmtSelect) QueryContext(ctx context.Context, values []driver.NamedValue) (driver.Rows, error) {
outputFn, err := s.conn.executeContext(ctx, s.Stmt, values)
if err == ErrInTx {
return &TxResultResultSet{wrap: ResultResultSet{err: err}, outputFn: outputFn}, nil
}
result := &ResultResultSet{stmtOutput: outputFn()}
if err == nil {
result.init()
Expand Down
58 changes: 58 additions & 0 deletions tx.go
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
package godynamo

import (
"database/sql/driver"
"fmt"
"reflect"
)

// TxResultNoResultSet is transaction-aware version of ResultNoResultSet.
Expand Down Expand Up @@ -33,6 +35,62 @@ func (t *TxResultNoResultSet) RowsAffected() (int64, error) {
return t.affectedRows, nil
}

// TxResultResultSet is transaction-aware version of ResultResultSet.
//
// @Available since v0.2.0
type TxResultResultSet struct {
wrap ResultResultSet
hasOutput bool
outputFn executeStatementOutputWrapper
}

func (r *TxResultResultSet) checkOutput() {
if !r.hasOutput {
r.wrap.stmtOutput = r.outputFn()
fmt.Println("DEBUG", r.wrap.stmtOutput)
if r.wrap.stmtOutput != nil {
r.wrap.err = nil
r.hasOutput = true
r.wrap.init()
}
}
}

// Columns implements driver.Rows/Columns.
func (r *TxResultResultSet) Columns() []string {
r.checkOutput()
return r.wrap.Columns()
}

// ColumnTypeScanType implements driver.RowsColumnTypeScanType/ColumnTypeScanType
func (r *TxResultResultSet) ColumnTypeScanType(index int) reflect.Type {
r.checkOutput()
return r.wrap.ColumnTypeScanType(index)
}

// ColumnTypeDatabaseTypeName implements driver.RowsColumnTypeDatabaseTypeName/ColumnTypeDatabaseTypeName
func (r *TxResultResultSet) ColumnTypeDatabaseTypeName(index int) string {
r.checkOutput()
return r.wrap.ColumnTypeDatabaseTypeName(index)
}

// Close implements driver.Rows/Close.
func (r *TxResultResultSet) Close() error {
r.checkOutput()
if !r.hasOutput {
return ErrInTx
}
return nil
}

// Next implements driver.Rows/Next.
func (r *TxResultResultSet) Next(dest []driver.Value) error {
r.checkOutput()
return r.wrap.Next(dest)
}

/*----------------------------------------------------------------------*/

// Tx is AWS DynamoDB implementation of driver.Tx.
//
// @Available since v0.2.0
Expand Down

0 comments on commit 92f0942

Please sign in to comment.