Skip to content

Commit

Permalink
Merge pull request #1 from btnguyen2k/dev
Browse files Browse the repository at this point in the history
Prepare to release v0.1.0
  • Loading branch information
btnguyen2k authored May 27, 2023
2 parents 5fb0f44 + 23ff7b2 commit 6289e7a
Show file tree
Hide file tree
Showing 25 changed files with 4,271 additions and 2 deletions.
34 changes: 34 additions & 0 deletions .github/workflows/godynamo.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
name: godynamo

on:
push:
branches: [ '*' ]
pull_request:
branches: [ main ]

jobs:
testLocal:
name: Test against AWS DynamoDB local
runs-on: ubuntu-latest
steps:
- name: Set up Go env
uses: actions/setup-go@v2
with:
go-version: ^1.13
- name: Check out code into the Go module directory
uses: actions/checkout@v2
- name: Start AWS DynamoDB local server
run: docker run -d --name dynamodb -p 8000:8000 amazon/dynamodb-local -jar DynamoDBLocal.jar -inMemory -sharedDb
- name: Test
run: |
export AWS_REGION="us-east-1"
export AWS_ACCESS_KEY_ID="DUMMYID"
export AWS_SECRET_ACCESS_KEY="DUMMYKEY"
export AWS_DYNAMODB_ENDPOINT="http://localhost:8000"
export AWS_DYNAMODB_URL="Endpoint=http://localhost:8000"
go test -v -timeout 9999s -count 1 -p 1 -cover -coverprofile coverage_local.txt .
- name: Codecov
uses: codecov/codecov-action@v3
with:
flags: local
name: local
9 changes: 8 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,14 @@
*.out

# Dependency directories (remove the comment below to include it)
# vendor/
vendor/

# Go workspace file
go.work

# IDE
.idea/

# Others
Qnd/
/coverage.txt
File renamed without changes.
82 changes: 81 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
@@ -1,2 +1,82 @@
# godynamo
Go driver for AWS DynamoDB

[![Go Report Card](https://goreportcard.com/badge/github.com/btnguyen2k/godynamo)](https://goreportcard.com/report/github.com/btnguyen2k/godynamo)
[![PkgGoDev](https://pkg.go.dev/badge/github.com/btnguyen2k/godynamo)](https://pkg.go.dev/github.com/btnguyen2k/godynamo)
[![Actions Status](https://github.com/btnguyen2k/godynamo/workflows/godynamo/badge.svg)](https://github.com/btnguyen2k/godynamo/actions)
[![codecov](https://codecov.io/gh/btnguyen2k/godynamo/branch/main/graph/badge.svg?token=pYdHuxbIiI)](https://codecov.io/gh/btnguyen2k/godynamo)
[![Release](https://img.shields.io/github/release/btnguyen2k/godynamo.svg?style=flat-square)](RELEASE-NOTES.md)

Go driver for [AWS DynamoDB](https://aws.amazon.com/dynamodb/) which can be used with the standard [database/sql](https://golang.org/pkg/database/sql/) package.

## Usage

```go
package main

import (
"database/sql"
"fmt"

_ "github.com/btnguyen2k/gocosmos"
)

func main() {
driver := "godynamo"
dsn := "Region=<aws-region>;AkId=<access-key-id>;SecretKey=<secret-key>"
db, err := sql.Open(driver, dsn)
if err != nil {
panic(err)
}
defer db.Close()

// db instance is ready to use
dbrows, err := db.Query(`LIST TABLES`)
if err != nil {
panic(err)
}
for dbRows.Next() {
var val interface{}
err := dbRows.Scan(&val)
if err != nil {
panic(err)
}
fmt.Println(val)
}
}
```

## Data Source Name (DSN) format for AWS Dynamo DB

`Region=<aws-region>;AkId=<aws-access-key-id>;Secret_Key=<aws-secret-key>[;Endpoint=<aws-dynamodb-endpoint>][TimeoutMs=<timeout-in-milliseconds>]`

- `Region`: AWS region, for example `us-east-1`. If not supplied, the value of the environment `AWS_REGION` is used.
- `AkId`: AWS Access Key ID, for example `AKIA1234567890ABCDEF`. If not supplied, the value of the environment `AWS_ACCESS_KEY_ID` is used.
- `Secret_Key`: AWS Secret Key, for example `0***F`. If not supplied, the value of the environment `AWS_SECRET_ACCESS_KEY` is used.
- `Endpoint`: (optional) AWS DynamoDB endpoint, for example `http://localhost:8000`; useful when AWS DynamoDB is running on local machine.
- `TimeoutMs`: (optional) timeout in milliseconds. If not specified, default value is `10000`.

## Supported statements:

- [Table](SQL_TABLE.md):
- `CREATE TABLE`
- `LIST TABLES`
- `DESCRIBE TABLE`
- `ALTER TABLE`
- `DROP TABLE`

- [Index](SQL_INDEX.md):
- `DESCRIBE LSI`
- `CREATE GSI`
- `DESCRIBE GSI`
- `ALTER GSI`
- `DROP GSI`

- [Document](SQL_DOCUMENT.md):
- `INSERT`
- `SELECT`
- `UPDATE`
- `DELETE`

## License

MIT - See [LICENSE.md](LICENSE.md).
8 changes: 8 additions & 0 deletions RELEASE-NOTES.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
# godynamo release notes

## 2023-05-27 - v0.1.0

- Driver for `database/sql`, supported statements:
- Table: `CREATE TABLE`, `LIST TABLES`, `DESCRIBE TABLE`, `ALTER TABLE`, `DROP TABLE`.
- Index: `DESCRIBE LSI`, `CREATE GSI`, `DESCRIBE GSI`, `ALTER GSI`, `DROP GSI`.
- Document: `INSERT`, `SELECT`, `UPDATE`, `DELETE`.
105 changes: 105 additions & 0 deletions SQL_DOCUMENT.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,105 @@
# godynamo - Supported statements for document

- `INSERT`
- `SELECT`
- `UPDATE`
- `DELETE`

## INSERT

Syntax: [PartiQL insert statements for DynamoDB](https://docs.aws.amazon.com/amazondynamodb/latest/developerguide/ql-reference.insert.html)

Example:
```go
result, err := db.Exec(`INSERT INTO "session" VALUE {'app': ?, 'user': ?, 'active': ?}`, "frontend", "user1", true)
if err == nil {
numAffectedRow, err := result.RowsAffected()
...
}
```

Description: use the `INSERT` statement to add an item to a table.

- If the statement is executed successfully, `RowsAffected()` returns `1, nil`.
- Note: the `INSERT` must follow [PartiQL syntax](https://docs.aws.amazon.com/amazondynamodb/latest/developerguide/ql-reference.insert.html), e.g. attribute names are enclosed by _single_ quotation marks ('attr-name'), table name is enclosed by _double_ quotation marks ("table-name"), etc.

## SELECT

Syntax: [PartiQL select statements for DynamoDB](https://docs.aws.amazon.com/amazondynamodb/latest/developerguide/ql-reference.select.html)

Example:
```go
dbrows, err := db.Query(`SELECT * FROM "session" WHERE app='frontend'`)
if err == nil {
fetchAndPrintAllRows(dbrows)
}
```

Description: use the `SELECT` statement to retrieve data from a table.

- Note: the `SELECT` must follow [PartiQL syntax](https://docs.aws.amazon.com/amazondynamodb/latest/developerguide/ql-reference.select.html).

Sample result:
|active|app|user|
|------|---|----|
|true|"frontend"|"user1"|

## UPDATE

Syntax: [PartiQL update statements for DynamoDB](https://docs.aws.amazon.com/amazondynamodb/latest/developerguide/ql-reference.update.html)

Example:
```go
result, err := db.Exec(`UPDATE "tbltest" SET location=? SET os=? WHERE "app"=? AND "user"=?`, "VN", "Ubuntu", "app0", "user1")
if err == nil {
numAffectedRow, err := result.RowsAffected()
...
}
```

Description: use the `UPDATE` statement to modify the value of one or more attributes within an item in a table.

- Note: the `UPDATE` must follow [PartiQL syntax](https://docs.aws.amazon.com/amazondynamodb/latest/developerguide/ql-reference.update.html).

`Query` can also be used to fetch returned values.
```go
dbrows, err := db.Query(`UPDATE "tbltest" SET location=? SET os=? WHERE "app"=? AND "user"=? RETURNING MODIFIED OLD *`, "VN", "Ubuntu", "app0", "user0")
if err == nil {
fetchAndPrintAllRows(dbrows)
}
```

Sample result:
|location|
|--------|
|"AU" |

## DELETE

Syntax: [PartiQL delete statements for DynamoDB](https://docs.aws.amazon.com/amazondynamodb/latest/developerguide/ql-reference.delete.html)

Example:
```go
result, err := db.Exec(`DELETE FROM "tbltest" WHERE "app"=? AND "user"=?`, "app0", "user1")
if err == nil {
numAffectedRow, err := result.RowsAffected()
...
}
```

Description: use the `DELETE` statement to delete an existing item from a table.

- Note: the `DELETE` must follow [PartiQL syntax](https://docs.aws.amazon.com/amazondynamodb/latest/developerguide/ql-reference.delete.html).

`Query` can also be used to have the content of the old item returned.
```go
dbrows, err := db.Query(`DELETE FROM "tbltest" WHERE "app"=? AND "user"=?`, "app0", "user1")
if err == nil {
fetchAndPrintAllRows(dbrows)
}
```

Sample result:
|app|location|platform|user|
|---|--------|--------|----|
|"app0"|"AU"|"Windows"|"user1"|
141 changes: 141 additions & 0 deletions SQL_INDEX.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,141 @@
# godynamo - Supported statements for index

- `DESCRIBE LSI`
- `CREATE LSI`
- `DESCRIBE GSI`
- `ALTER GSI`
- `DROP GSI`

## DESCRIBE LSI

Syntax:
```sql
DESCRIBE LSI <index-name> ON <table-name>
```

Example:
```go
result, err := db.Query(`DESCRIBE LSI idxos ON session`)
if err == nil {
...
}
```

Description: return info of a Local Secondary Index specified by `index-name` on a DynamoDB table specified by `table-name`.

Sample result:
|IndexArn|IndexName|IndexSizeBytes|ItemCount|KeySchema|Projection|
|--------|---------|--------------|---------|---------|----------|
|"arn:aws:dynamodb:ddblocal:000000000000:table/session/index/idxos"|"idxos"|0|0|[{"AttributeName":"app","KeyType":"HASH"},{"AttributeName":"os","KeyType":"RANGE"}]|{"NonKeyAttributes":["os_name","os_version"],"ProjectionType":"INCLUDE"}|

## CREATE GSI

Syntax:
```sql
CREATE GSI [IF NOT EXISTS] <index-name> ON <table-name>
<WITH PK=pk-attr-name:data-type>
[[,] WITH SK=sk-attr-name:data-type]
[[,] WITH wcu=<number>[,] WITH rcu=<number>]
[[,] WITH projection=*|attr1,attr2,attr3,...]
```

Example:
```go
result, err := db.Exec(`CREATE GSI idxname ON tablename WITH pk=grade:number, WITH rcu=1 WITH wru=2`)
if err == nil {
numAffectedRow, err := result.RowsAffected()
...
}
```

Description: create a Global Secondary Index on an existing DynamoDB table.

- If the statement is executed successfully, `RowsAffected()` returns `1, nil`.
- If the specified GSI already existed:
- If `IF NOT EXISTS` is supplied: `RowsAffected()` returns `0, nil`.
- If `IF NOT EXISTS` is _not_ supplied: `RowsAffected()` returns `_, error`.
- `RCU`: GSI's read capacity unit.
- `WCU`: GSI's write capacity unit.
- `PK`: GSI's partition key, mandatory.
- `SK`: GSI's sort key, optional.
- `data-type`: must be one of `BINARY`, `NUMBER` or `STRING`.
- `PROJECTION`:
- `*`: all attributes from the original table are included in projection (`ProjectionType=ALL`).
- `attr1,attr2,...`: specified attributes from the original table are included in projection (`ProjectionType=INCLUDE`).
- _not specified_: only key attributes are included in projection (`ProjectionType=KEYS_ONLY`).
- Note: The provisioned throughput settings of a GSI are separate from those of its base table.
- Note: GSI inherit the RCU and WCU mode from the base table. That means if the base table is in on-demand mode, then DynamoDB also creates the GSI in on-demand mode.
- Note: there must be at least one space before the WITH keyword.

## DESCRIBE GSI

Syntax:
```sql
DESCRIBE GSI <index-name> ON <table-name>
```

Example:
```go
result, err := db.Query(`DESCRIBE GSI idxos ON session`)
if err == nil {
...
}
```

Description: return info of a Local Secondary Index specified by `index-name` on a DynamoDB table specified by `table-name`.

Sample result:
|Backfilling|IndexArn|IndexName|IndexSizeBytes|IndexStatus|ItemCount|KeySchema|Projection|ProvisionedThroughput|
|-----------|--------|---------|--------------|-----------|---------|---------|----------|---------------------|
|null|"arn:aws:dynamodb:ddblocal:000000000000:table/session/index/idxbrowser"|"idxbrowser"|0|"ACTIVE"|0|[{"AttributeName":"browser","KeyType":"HASH"}]|{"NonKeyAttributes":null,"ProjectionType":"ALL"}|{"LastDecreaseDateTime":null,"LastIncreaseDateTime":null,"NumberOfDecreasesToday":null,"ReadCapacityUnits":1,"WriteCapacityUnits":1}|

## ALTER GSI

Syntax:
```sql
ALTER GSI <index-name> ON <table-name>
WITH wcu=<number>[,] WITH rcu=<number>
```

Example:
```go
result, err := db.Exec(`ALTER GSI idxname ON tablename WITH rcu=1 WITH wru=2`)
if err == nil {
numAffectedRow, err := result.RowsAffected()
...
}
```

Description: update WRU/RCU of a Global Secondary Index on an existing DynamoDB table.

- If the statement is executed successfully, `RowsAffected()` returns `1, nil`.
- `RCU`: GSI's read capacity unit.
- `WCU`: GSI's write capacity unit.
- Note: The provisioned throughput settings of a GSI are separate from those of its base table.
- Note: GSI inherit the RCU and WCU mode from the base table. That means if the base table is in on-demand mode, then DynamoDB also creates the GSI in on-demand mode.
- Note: there must be at least one space before the WITH keyword.

## DROP GSI

Syntax:
```sql
DROP GSI [IF EXIST] <index-name> ON <table-name>
```

Alias: `DELETE GSI`

Example:
```go
result, err := db.Exec(`DROP GSI IF EXISTS index ON table`)
if err == nil {
numAffectedRow, err := result.RowsAffected()
...
}
```

Description: delete an existing GSI from a DynamoDB table.

- If the statement is executed successfully, `RowsAffected()` returns `1, nil`.
- If the specified table does not exist:
- If `IF EXISTS` is supplied: `RowsAffected()` returns `0, nil`
- If `IF EXISTS` is _not_ supplied: `RowsAffected()` returns `_, error`
Loading

0 comments on commit 6289e7a

Please sign in to comment.