Skip to content

Commit

Permalink
prepare to release v0.1.0
Browse files Browse the repository at this point in the history
  • Loading branch information
Thanh Nguyen committed May 27, 2023
1 parent 1bf77ca commit 23ff7b2
Show file tree
Hide file tree
Showing 9 changed files with 231 additions and 53 deletions.
12 changes: 9 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -47,16 +47,22 @@ func main() {

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

// TODO
`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`
- `DESCRIBE TABLE`

- [Index](SQL_INDEX.md):
- `DESCRIBE LSI`
Expand All @@ -65,7 +71,7 @@ func main() {
- `ALTER GSI`
- `DROP GSI`

- [Document](SQL_DOCUMENT.md)
- [Document](SQL_DOCUMENT.md):
- `INSERT`
- `SELECT`
- `UPDATE`
Expand Down
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`.
45 changes: 38 additions & 7 deletions SQL_DOCUMENT.md
Original file line number Diff line number Diff line change
Expand Up @@ -29,9 +29,9 @@ Syntax: [PartiQL select statements for DynamoDB](https://docs.aws.amazon.com/ama

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

Expand All @@ -44,22 +44,45 @@ Sample result:
|------|---|----|
|true|"frontend"|"user1"|

## DELETE
## UPDATE

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

Example:
```go
result, err := db.Exec(`DELETE FROM "tbltest" WHERE "app"=? AND "user"=?`, "app0", "user1")
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()
...
}
```

`Query` can also be used to have the content of the old item returned.
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()
...
}
```
Expand All @@ -68,7 +91,15 @@ 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"|"user2"|
|"app0"|"AU"|"Windows"|"user1"|
46 changes: 23 additions & 23 deletions SQL_TABLE.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,9 @@

- `CREATE TABLE`
- `LIST TABLES`
- `DESCRIBE TABLE`
- `ALTER TABLE`
- `DROP TABLE`
- `DESCRIBE TABLE`

## CREATE TABLE

Expand Down Expand Up @@ -74,6 +74,28 @@ Sample result:
|tbltest2|
|tbltest3|

## DESCRIBE TABLE

Syntax:
```sql
DESCRIBE TABLE <table-name>
```

Example:
```go
result, err := db.Query(`DESCRIBE TABLE demo`)
if err == nil {
...
}
```

Description: return info of a DynamoDB table specified by `table-name`.

Sample result:
|ArchivalSummary|AttributeDefinitions|BillingModeSummary|CreationDateTime|DeletionProtectionEnabled|GlobalSecondaryIndexes|GlobalTableVersion|ItemCount|KeySchema|LatestStreamArn|LatestStreamLabel|LocalSecondaryIndexes|ProvisionedThroughput|Replicas|RestoreSummary|SSEDescription|StreamSpecification|TableArn|TableClassSummary|TableId|TableName|TableSizeBytes|TableStatus|
|---------------|--------------------|------------------|----------------|-------------------------|----------------------|------------------|---------|---------|---------------|-----------------|---------------------|---------------------|--------|--------------|--------------|-------------------|--------|-----------------|-------|---------|--------------|-----------|
|null|[{"AttributeName":"app","AttributeType":"S"},{"AttributeName":"user","AttributeType":"S"},{"AttributeName":"timestamp","AttributeType":"N"},{"AttributeName":"browser","AttributeType":"S"},{"AttributeName":"os","AttributeType":"S"}]|{"BillingMode":"PAY_PER_REQUEST","LastUpdateToPayPerRequestDateTime":"2023-05-23T01:58:27.352Z"}|"2023-05-23T01:58:27.352Z"|null|null|null|0|[{"AttributeName":"app","KeyType":"HASH"},{"AttributeName":"user","KeyType":"RANGE"}]|null|null|[{"IndexArn":"arn:aws:dynamodb:ddblocal:000000000000:table/tbltemp/index/idxos","IndexName":"idxos","IndexSizeBytes":0,"ItemCount":0,"KeySchema":[{"AttributeName":"app","KeyType":"HASH"},{"AttributeName":"os","KeyType":"RANGE"}],"Projection":{"NonKeyAttributes":["os_name","os_version"],"ProjectionType":"INCLUDE"}},{"IndexArn":"arn:aws:dynamodb:ddblocal:000000000000:table/tbltemp/index/idxbrowser","IndexName":"idxbrowser","IndexSizeBytes":0,"ItemCount":0,"KeySchema":[{"AttributeName":"app","KeyType":"HASH"},{"AttributeName":"browser","KeyType":"RANGE"}],"Projection":{"NonKeyAttributes":null,"ProjectionType":"ALL"}},{"IndexArn":"arn:aws:dynamodb:ddblocal:000000000000:table/tbltemp/index/idxtime","IndexName":"idxtime","IndexSizeBytes":0,"ItemCount":0,"KeySchema":[{"AttributeName":"app","KeyType":"HASH"},{"AttributeName":"timestamp","KeyType":"RANGE"}],"Projection":{"NonKeyAttributes":null,"ProjectionType":"KEYS_ONLY"}}]|{"LastDecreaseDateTime":"1970-01-01T00:00:00Z","LastIncreaseDateTime":"1970-01-01T00:00:00Z","NumberOfDecreasesToday":0,"ReadCapacityUnits":0,"WriteCapacityUnits":0}|null|null|null|null|"arn:aws:dynamodb:ddblocal:000000000000:table/tbltemp"|null|null|"tbltemp"|0|"ACTIVE"|

## ALTER TABLE

Syntax:
Expand Down Expand Up @@ -125,25 +147,3 @@ Description: delete an existing DynamoDB table specified by `table-name`.
- 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`

## DESCRIBE TABLE

Syntax:
```sql
DESCRIBE TABLE <table-name>
```

Example:
```go
result, err := db.Query(`DESCRIBE TABLE demo`)
if err == nil {
...
}
```

Description: return info of a DynamoDB table specified by `table-name`.

Sample result:
|ArchivalSummary|AttributeDefinitions|BillingModeSummary|CreationDateTime|DeletionProtectionEnabled|GlobalSecondaryIndexes|GlobalTableVersion|ItemCount|KeySchema|LatestStreamArn|LatestStreamLabel|LocalSecondaryIndexes|ProvisionedThroughput|Replicas|RestoreSummary|SSEDescription|StreamSpecification|TableArn|TableClassSummary|TableId|TableName|TableSizeBytes|TableStatus|
|---------------|--------------------|------------------|----------------|-------------------------|----------------------|------------------|---------|---------|---------------|-----------------|---------------------|---------------------|--------|--------------|--------------|-------------------|--------|-----------------|-------|---------|--------------|-----------|
|null|[{"AttributeName":"app","AttributeType":"S"},{"AttributeName":"user","AttributeType":"S"},{"AttributeName":"timestamp","AttributeType":"N"},{"AttributeName":"browser","AttributeType":"S"},{"AttributeName":"os","AttributeType":"S"}]|{"BillingMode":"PAY_PER_REQUEST","LastUpdateToPayPerRequestDateTime":"2023-05-23T01:58:27.352Z"}|"2023-05-23T01:58:27.352Z"|null|null|null|0|[{"AttributeName":"app","KeyType":"HASH"},{"AttributeName":"user","KeyType":"RANGE"}]|null|null|[{"IndexArn":"arn:aws:dynamodb:ddblocal:000000000000:table/tbltemp/index/idxos","IndexName":"idxos","IndexSizeBytes":0,"ItemCount":0,"KeySchema":[{"AttributeName":"app","KeyType":"HASH"},{"AttributeName":"os","KeyType":"RANGE"}],"Projection":{"NonKeyAttributes":["os_name","os_version"],"ProjectionType":"INCLUDE"}},{"IndexArn":"arn:aws:dynamodb:ddblocal:000000000000:table/tbltemp/index/idxbrowser","IndexName":"idxbrowser","IndexSizeBytes":0,"ItemCount":0,"KeySchema":[{"AttributeName":"app","KeyType":"HASH"},{"AttributeName":"browser","KeyType":"RANGE"}],"Projection":{"NonKeyAttributes":null,"ProjectionType":"ALL"}},{"IndexArn":"arn:aws:dynamodb:ddblocal:000000000000:table/tbltemp/index/idxtime","IndexName":"idxtime","IndexSizeBytes":0,"ItemCount":0,"KeySchema":[{"AttributeName":"app","KeyType":"HASH"},{"AttributeName":"timestamp","KeyType":"RANGE"}],"Projection":{"NonKeyAttributes":null,"ProjectionType":"KEYS_ONLY"}}]|{"LastDecreaseDateTime":"1970-01-01T00:00:00Z","LastIncreaseDateTime":"1970-01-01T00:00:00Z","NumberOfDecreasesToday":0,"ReadCapacityUnits":0,"WriteCapacityUnits":0}|null|null|null|null|"arn:aws:dynamodb:ddblocal:000000000000:table/tbltemp"|null|null|"tbltemp"|0|"ACTIVE"|
13 changes: 8 additions & 5 deletions driver.go
Original file line number Diff line number Diff line change
Expand Up @@ -50,17 +50,20 @@ func (d *Driver) Open(connStr string) (driver.Conn, error) {
if err != nil || timeoutMs < 0 {
timeoutMs = 10000
}
region := params["REGION"]
if region == "" {
region = os.Getenv("AWS_REGION")
}
akid := params["AKID"]
if akid == "" {
akid = os.Getenv("AWS_ACCESS_KEY_ID")
}
secretKey := params["SECRET_KEY"]
if secretKey == "" {
secretKey = os.Getenv("AWS_SECRET_ACCESS_KEY")
}
region := params["REGION"]
if region == "" {
region = os.Getenv("AWS_REGION")
secretKey = params["SECRETKEY"]
if secretKey == "" {
secretKey = os.Getenv("AWS_SECRET_ACCESS_KEY")
}
}
opts := dynamodb.Options{
Credentials: credentials.NewStaticCredentialsProvider(akid, secretKey, ""),
Expand Down
2 changes: 1 addition & 1 deletion godynamo.go
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ var (
}
)

// IsAwsError returns true if err is an AWS-specific error and it matches awsErrCode.
// IsAwsError returns true if err is an AWS-specific error, and it matches awsErrCode.
func IsAwsError(err error, awsErrCode string) bool {
if aerr, ok := err.(*smithy.OperationError); ok {
if herr, ok := aerr.Err.(*http.ResponseError); ok {
Expand Down
16 changes: 16 additions & 0 deletions release.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
#!/bin/sh

## Utility script to release project with a tag
## Usage:
## ./release.sh <tag-name>

if [ "$1" == "" ]; then
echo "Usage: $0 tag-name"
exit -1
fi

echo "$1"
git commit -m "$1"
git tag -f -a "$1" -m "$1"
git push origin "$1" -f
git push
38 changes: 24 additions & 14 deletions stmt_document.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,10 @@ import (
"github.com/aws/aws-sdk-go-v2/service/dynamodb/types"
)

var rePlaceholder = regexp.MustCompile(`(?m)\?\s*[\,\]\}\s]`)
var (
rePlaceholder = regexp.MustCompile(`(?m)\?\s*[\,\]\}\s]`)
reReturning = regexp.MustCompile(`(?im)\s+RETURNING\s+((ALL\s+OLD)|(MODIFIED\s+OLD)|(ALL\s+NEW)|(MODIFIED\s+NEW))\s+\*\s*$`)
)

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

Expand Down Expand Up @@ -195,30 +198,40 @@ func (s *StmtSelect) Exec(_ []driver.Value) (driver.Result, error) {
// StmtUpdate implements "UPDATE" statement.
//
// Syntax: follow "PartiQL update statements for DynamoDB" https://docs.aws.amazon.com/amazondynamodb/latest/developerguide/ql-reference.update.html
//
// Note: StmtUpdate returns the updated item by appending "RETURNING ALL OLD *" to the statement.
type StmtUpdate struct {
*StmtExecutable
}

func (s *StmtUpdate) parse() error {
if !reReturning.MatchString(s.query) {
s.query += " RETURNING ALL OLD *"
}
return s.StmtExecutable.parse()
}

// Query implements driver.Stmt.Query.
func (s *StmtUpdate) Query(values []driver.Value) (driver.Rows, error) {
output, err := s.Execute(values)
result := &ResultResultSet{dbResult: output, columnTypes: make(map[string]reflect.Type)}
if err == nil {
if err == nil || IsAwsError(err, "ConditionalCheckFailedException") {
result.init()
err = nil
}
return result, err
}

// Exec implements driver.Stmt.Exec.
func (s *StmtUpdate) Exec(values []driver.Value) (driver.Result, error) {
_, err := s.Execute(values)
result := &ResultNoResultSet{Successful: err == nil}
output, err := s.Execute(values)
if IsAwsError(err, "ConditionalCheckFailedException") {
return &ResultNoResultSet{Successful: true, AffectedRows: 0}, nil
}
if err != nil {
result.AffectedRows = 0
} else {
result.AffectedRows = 1
return &ResultNoResultSet{Successful: false, AffectedRows: 0}, err
}
return result, err
return &ResultNoResultSet{Successful: true, AffectedRows: int64(len(output.Items))}, nil
}

/*----------------------------------------------------------------------*/
Expand All @@ -227,15 +240,11 @@ func (s *StmtUpdate) Exec(values []driver.Value) (driver.Result, error) {
//
// Syntax: follow "PartiQL delete statements for DynamoDB" https://docs.aws.amazon.com/amazondynamodb/latest/developerguide/ql-reference.delete.html
//
// Note: StmtDelete returns the deleted item by appending "RETURNING RETURNING ALL OLD *" to the statement.
// Note: StmtDelete returns the deleted item by appending "RETURNING ALL OLD *" to the statement.
type StmtDelete struct {
*StmtExecutable
}

var (
reReturning = regexp.MustCompile(`(?im)\s+RETURNING\s+((ALL\s+OLD)|(MODIFIED\s+OLD)|(ALL\s+NEW)|(MODIFIED\s+NEW))\s+\*\s*$`)
)

func (s *StmtDelete) parse() error {
if !reReturning.MatchString(s.query) {
s.query += " RETURNING ALL OLD *"
Expand All @@ -247,8 +256,9 @@ func (s *StmtDelete) parse() error {
func (s *StmtDelete) Query(values []driver.Value) (driver.Rows, error) {
output, err := s.Execute(values)
result := &ResultResultSet{dbResult: output, columnTypes: make(map[string]reflect.Type)}
if err == nil {
if err == nil || IsAwsError(err, "ConditionalCheckFailedException") {
result.init()
err = nil
}
return result, err
}
Expand Down
Loading

0 comments on commit 23ff7b2

Please sign in to comment.