pqxd

package module
v0.5.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Nov 2, 2024 License: MIT Imports: 20 Imported by: 0

README

pqxd - database/sql driver for PartiQL in DynamoDB

Go Reference CI GitHub go.mod Go version (subdirectory of monorepo) GitHub release (latest by date) Go Report Card GitHub License

Quick Start

Install
go get github.com/miyamo2/pqxd
Usage
package main

import (
	"context"
	"database/sql"
	"fmt"
	"github.com/aws/aws-sdk-go-v2/config"
	"github.com/miyamo2/pqxd"
	"log"
	"time"
)

func main() {
	ctx := context.Background()
	cfg, err := config.LoadDefaultConfig(ctx, config.WithRegion("ap-northeast-1"))
	if err != nil {
		log.Fatalf("unable to load SDK config, %v", err)
	}
	db := sql.OpenDB(pqxd.NewConnector(cfg))
	if db == nil {
		log.Fatal(err)
	}
	if err := db.Ping(); err != nil {
		log.Fatal(err)
	}

	ctx, cancel := context.WithTimeout(context.Background(), time.Second)
	defer cancel()

	rows, err := db.QueryContext(ctx, `SELECT id, name FROM "users"`)
	if err != nil {
		fmt.Printf("something happend. err: %s\n", err.Error())
		return
	}

	for rows.NextResultSet() { // page feed with next token
		for rows.Next() {
			var (
				id   string
				name string
			)
			if err := rows.Scan(&id, &name); err != nil {
				fmt.Printf("something happend. err: %s\n", err.Error())
				continue
			}
			fmt.Printf("id: %s, name: %s\n", id, name)
		}
	}
}
SELECT

[!TIP] If * is specified in the select column list,
the results of the rows are automatically sorted by column name(asc).

However, if specified with *, the number of attributes may differ from row to row.

Therefore, it is recommended that the selection column list specify the attribute names.

Scan
rows, err := db.QueryContext(context.Background(), `SELECT id, name FROM "users"`)
for rows.NextResultSet() { // page feed with next token
    for rows.Next() {
        var (
            id string
            name string
        )
        if err := rows.Scan(&id, &name); err != nil {
            fmt.Printf("something happend. err: %s\n", err.Error())
            continue
        }
        fmt.Printf("id: %s, name: %s\n", id, name)
    }
}
GetItem
row := db.QueryRowContext(context.Background(), `SELECT id, name FROM "users" WHERE id = ?`, "1")
var (
    id string
    name string
)
if err := row.Scan(&id, &name); err != nil {
    fmt.Printf("something happend. err: %s\n", err.Error())
    return
}
fmt.Printf("id: %s, name: %s\n", id, name)
GetItem with Global Secondary Index
row := db.QueryRowContext(context.Background(), `SELECT id, name FROM "users"."gsi_pk-gsi-sk_index" WHERE gsi_pk = ? AND gsi_sk = ?`, "foo", "bar")

var (
    id string
    name string
)
if err := row.Scan(&id, &name); err != nil {
    fmt.Printf("something happend. err: %s\n", err.Error())
    return
}
fmt.Printf("id: %s, name: %s\n", id, name)
With Prepared Statement
ctx := context.Background()

stmt, err := db.PrepareContext(ctx, `SELECT id, name FROM "users" WHERE id = ?`)
if err != nil {
    fmt.Printf("something happend. err: %s\n", err.Error())
    return
}
defer stmt.Close()

rows, err := stmt.QueryRowContext(ctx, "1")
if err != nil {
    fmt.Printf("something happend. err: %s\n", err.Error())
    return
}

var (
    id string
    name string
)
if err := row.Scan(&id, &name); err != nil {
    fmt.Printf("something happend. err: %s\n", err.Error())
    return
}
fmt.Printf("id: %s, name: %s\n", id, name)
With Transaction
tx, err := db.Begin()
if err != nil {
    return err
}

ctx := context.Background()

rows, err := tx.QueryContext(ctx, `SELECT id, name FROM "users" WHERE id = ?`, "1")
if err != nil {
    tx.Rollback()
    return err
}

row := tx.QueryRowContext(ctx, `SELECT id, name FROM "users" WHERE id = ?`, "2")

// WARNING: Do not use `tx.Commit()` when using `SELECT` statement.
//
// Each `sql.Rows` or `sql.Row` is resolved 
// the first time `rows.NextResultSet()`, `rows.Next()` or `row.Scan()` 
// is performed within that transaction.
// So, after the `rows.NextResultSet()`, `rows.Next()` or `row.Scan()` is performed,
// the transaction is automatically committed.
for rows.Next() {
    var (
        id string
        name string
    )
    if err := rows.Scan(&id, &name); err != nil {
        fmt.Printf("something happend. err: %s\n", err.Error())
        continue
    }
    fmt.Printf("id: %s, name: %s\n", id, name)
}

var (
    id string
    name string
)
if err := row.Scan(&id, &name); err != nil {
    fmt.Printf("something happend. err: %s\n", err.Error())
    return
}
fmt.Printf("id: %s, name: %s\n", id, name)
RETURNING

pqxd supports the RETURNING clause.

row := db.QueryRowContext(context.Background(), `UPDATE "users" SET name = ? SET nickname = ? WHERE id = ? RETURNING MODIFIED OLD *`, "David", "Dave", "3")

var name, nickname sql.NullString
if err := row.Scan(&name, &nickname); err != nil {
    fmt.Printf("something happend. err: %s\n", err.Error())
    return
}
if name.Valid {
    fmt.Printf("name: %s\n", name.String)
}
if nickname.Valid {
    fmt.Printf("nickname: %s\n", nickname.String)
}

And provides proprietary syntax for specifying a column list instead of *.

row := db.QueryRowContext(context.Background(), `UPDATE "users" SET name = ? SET nickname = ? WHERE id = ? RETURNING ALL OLD id`, "Robert", "Bob", "2")

var id string
if err := row.Scan(&id); err != nil {
    fmt.Printf("something happend. err: %s\n", err.Error())
    return
}
fmt.Printf("id: %s\n", id)
Describe Table

pqxd supports the DescribeTable API with !pqxd_describe_table, the meta-table.

row := db.QueryRowContext(context.Background(), `SELECT TableStatus FROM "!pqxd_describe_table" WHERE table_name = ?`, "users")

var tableStatus pqxd.TableStatus
if err := row.Scan(&tableStatus); err != nil {
    fmt.Println(err.Error())
    return
}
fmt.Printf("TableStatus: %v\n", tableStatus)
List Tables

pqxd supports the ListTables API with !pqxd_list_tables, the meta-table.

rows, err := db.QueryContext(context.Background(), `SELECT * FROM "!pqxd_list_tables"`)

for rows.NextResultSet() { // page feed with last-evaluated-key
    for rows.Next() {
        var tableName string
        if err := rows.Scan(&tableName); err != nil {
            fmt.Println(err.Error())
            continue
        }
        fmt.Printf("tableName: %s\n", tableName)
    }
}
INSERT/UPDATE/DELETE
insertResult, err := db.Exec(`INSERT INTO "users" VALUE { 'id': ?, 'name': ? }`, "3", "Alice")
if err != nil {
    return err
}
affected, err := insertResult.RowsAffected()
if err != nil {
    return err
}
if affected != 1 {
    return fmt.Errorf("expected 1 row affected, got %d", affected)
}

updateResult, err := db.Exec(`UPDATE "users" SET name = ? WHERE id = ?`, "Bob", "2")
if err != nil {
    return err
}
affected, err = updateResult.RowsAffected()
if err != nil {
    return err
}
if affected != 1 {
    return fmt.Errorf("expected 1 row affected, got %d", affected)
}

deleteResult, err := db.Exec(`DELETE FROM "users" WHERE id = ?`, "1")
if err != nil {
    return err
}
affected, err = deleteResult.RowsAffected()
if err != nil {
    return err
}
if affected != 1 {
    return fmt.Errorf("expected 1 row affected, got %d", affected)
}
With Prepared Statement
stmt, err := db.Prepare(`INSERT INTO "users" VALUE { 'id': ?, 'name': ? }`)
if err != nil {
    return err
}
defer stmt.Close()

insertResult, err := stmt.Exec("3", "Alice")
if err != nil {
    return err
}
affected, err := insertResult.RowsAffected()
if err != nil {
    return err
}
if affected != 1 {
    return fmt.Errorf("expected 1 row affected, got %d", affected)
}
With Transaction
tx, err := db.Begin()
if err != nil {
    return err
}

insertResult, err := tx.Exec(`INSERT INTO "users" VALUE { 'id': ?, 'name': ? }`, "3", "Alice")
if err != nil {
    tx.Rollback()
    return err
}

updateResult, err := tx.Exec(`UPDATE "users" SET name = ? WHERE id = ?`, "Bob", "2")
if err != nil {
    tx.Rollback()
    return err
}

deleteResult, err := tx.Exec(`DELETE FROM "users" WHERE id = ?`, "1")
if err != nil {
    tx.Rollback()
    return err
}

// RowsAffected is available after commit
tx.Commit()

// RowsAffected might return 0 or 1. If 0, it means statement is not successful.
if affected, err := insertResult.RowsAffected(); err != nil || affected != 1 {
    return err
}

if affected, err := updateResult.RowsAffected(); err != nil || affected != 1 {
    return err
}

if affected, err := deleteResult.RowsAffected(); err != nil || affected != 1 {
    return err
}
Connection String

We recommend using sql.OpenDB with pqxd.NewConnector instead of sql.Open. But if you want to use sql.Open, you can use the following connection string.

AWS_REGION=<aws region>
;AWS_ACCESS_KEY_ID=<access key ID>
;AWS_SECRET_ACCESS_KEY=<secret access key>
[;ENDPOINT=<amazon dynamodb endpoint>]
Key description
AWS_REGION AWS Region. If not supplied, it is resolved from one of the following environment variables; AWS_REGION or AWS_DEFAULT_REGION.
AWS_ACCESS_KEY_ID AWS Access Key ID. If not supplied, it is resolved from one of the following environment variables; AWS_ACCESS_KEY or AWS_ACCESS_KEY_ID.
AWS_SECRET_ACCESS_KEY AWS Secret Access Key. If not supplied, it is resolved from one of the following environment variables; AWS_SECRET_KEY or AWS_SECRET_ACCESS_KEY.
ENDPOINT Endpoint of DynamoDB. Used to connect locally to an emulator or to a DynamoDB compatible interface.
db, err := sql.Open(pqxd.DriverName, "AWS_REGION=ap-northeast-1;AWS_ACCESS_KEY_ID=AKIA...;AWS_SECRET_ACCESS_KEY=...;")
O11y
New Relic
package main

import (
	"context"
	"database/sql"
	"github.com/aws/aws-sdk-go-v2/config"
	"github.com/miyamo2/pqxd"
	nraws "github.com/newrelic/go-agent/v3/integrations/nrawssdk-v2"
	"log"
)

func main() {
	ctx := context.Background()
	cfg, err := config.LoadDefaultConfig(ctx, config.WithRegion("ap-northeast-1"))
	if err != nil {
		log.Fatalf("unable to load SDK config, %v", err)
	}
	
	// Instrumenting New Relic
	nraws.AppendMiddlewares(&cfg.APIOptions, nil)
	
	db := sql.OpenDB(pqxd.NewConnector(cfg))
	if db == nil {
		log.Fatal(err)
	}
	db.Ping()
}
Datadog
package main

import (
	"context"
	"database/sql"
	"github.com/aws/aws-sdk-go-v2/config"
	"github.com/miyamo2/pqxd"
	awstrace "gopkg.in/DataDog/dd-trace-go.v1/contrib/aws/aws-sdk-go-v2/aws"
	"log"
)

func main() {
	ctx := context.Background()
	cfg, err := config.LoadDefaultConfig(ctx, config.WithRegion("ap-northeast-1"))
	if err != nil {
		log.Fatalf("unable to load SDK config, %v", err)
	}
	
	// Instrumenting Datadog
	awstrace.AppendMiddleware(&cfg)

	db := sql.OpenDB(pqxd.NewConnector(cfg))
	if db == nil {
		log.Fatal(err)
	}
	db.Ping()
}
AWS X-Ray
package main

import (
	"context"
	"database/sql"
	"github.com/miyamo2/pqxd"
	"log"

	"github.com/aws/aws-sdk-go-v2/aws"
	"github.com/aws/aws-sdk-go-v2/config"
	"github.com/aws/aws-sdk-go-v2/service/dynamodb"
	"github.com/aws/aws-xray-sdk-go/instrumentation/awsv2"
	"github.com/aws/aws-xray-sdk-go/xray"
)

func main() {
	ctx := context.Background()
	cfg, err := config.LoadDefaultConfig(ctx, config.WithRegion("ap-northeast-1"))
	if err != nil {
		log.Fatalf("unable to load SDK config, %v", err)
	}
	
	// Instrumenting X-Ray
	awsv2.AWSV2Instrumentor(&cfg.APIOptions)

	db := sql.OpenDB(pqxd.NewConnector(cfg))
	if db == nil {
		log.Fatal(err)
	}
	db.Ping()
}

Contributing

Feel free to open a PR or an Issue.

However, you must promise to follow our Code of Conduct.

Tasks

We recommend that this section be run with xc

test:unit
go test -v -coverpkg='github.com/miyamo2/pqxd' -coverprofile=coverage.out
test:integration
cd tests/integration
xc test

License

pqxd released under the MIT License

Special Thanks

pqxd is inspired by the following projects.
With the utmost respect, we would like to thank the authors and contributors of these projects.

Documentation

Overview

Example
package main

import (
	"context"
	"database/sql"
	"fmt"
	"github.com/aws/aws-sdk-go-v2/config"
	"github.com/miyamo2/pqxd"
)

func main() {
	awsConfig, err := config.LoadDefaultConfig(context.Background())
	if err != nil {
		fmt.Println(err.Error())
		return
	}
	db := sql.OpenDB(pqxd.NewConnector(awsConfig))
	if err != nil {
		fmt.Println(err.Error())
		return
	}
	if err := db.Ping(); err != nil {
		fmt.Println(err.Error())
	}
}
Output:

Example (DescribeTable)
package main

import (
	"context"
	"database/sql"
	"fmt"
	"github.com/aws/aws-sdk-go-v2/config"
	"github.com/miyamo2/pqxd"
)

func MustDB() *sql.DB {
	awsConfig, err := config.LoadDefaultConfig(context.Background())
	if err != nil {
		panic(err)
	}
	return sql.OpenDB(pqxd.NewConnector(awsConfig))
}

func main() {
	db := MustDB()

	row := db.QueryRowContext(context.Background(), `SELECT * FROM "!pqxd_describe_table" WHERE table_name = ?`, "test_tables")
	var (
		archivalSummary           pqxd.ArchivalSummary
		attributeDefinitions      pqxd.AttributeDefinitions
		billingModeSummary        pqxd.BillingModeSummary
		creationDateTime          pqxd.CreationDateTime
		deletionProtectionEnabled pqxd.DeletionProtectionEnabled
		keySchema                 pqxd.KeySchema
		globalSecondaryIndexes    pqxd.GlobalSecondaryIndexes
		globalTableVersion        pqxd.GlobalTableVersion
		itemCount                 pqxd.ItemCount
		localSecondaryIndexes     pqxd.LocalSecondaryIndexes
		onDemandThroughput        pqxd.OnDemandThroughput
		provisionedThroughput     pqxd.ProvisionedThroughput
		replicas                  pqxd.Replicas
		restoreSummary            pqxd.RestoreSummary
		sseDescription            pqxd.SSEDescription
		streamSpecification       pqxd.StreamSpecification
		tableClassSummary         pqxd.TableClassSummary
		tableStatus               pqxd.TableStatus
	)
	err := row.Scan(&archivalSummary,
		&attributeDefinitions,
		&billingModeSummary,
		&creationDateTime,
		&deletionProtectionEnabled,
		&keySchema,
		&globalSecondaryIndexes,
		&globalTableVersion,
		&itemCount,
		&localSecondaryIndexes,
		&onDemandThroughput,
		&provisionedThroughput,
		&replicas,
		&restoreSummary,
		&sseDescription,
		&streamSpecification,
		&tableClassSummary,
		&tableStatus,
	)
	if err != nil {
		fmt.Println(err.Error())
		return
	}
	fmt.Printf("archivalSummary: %v\n", archivalSummary)
}
Output:

Example (DescribeTableWithSpecifyColumn)
package main

import (
	"context"
	"database/sql"
	"fmt"
	"github.com/aws/aws-sdk-go-v2/config"
	"github.com/miyamo2/pqxd"
)

func MustDB() *sql.DB {
	awsConfig, err := config.LoadDefaultConfig(context.Background())
	if err != nil {
		panic(err)
	}
	return sql.OpenDB(pqxd.NewConnector(awsConfig))
}

func main() {
	db := MustDB()

	row := db.QueryRowContext(context.Background(), `SELECT TableStatus FROM "!pqxd_describe_table" WHERE table_name = ?`, "test_tables")

	var tableStatus pqxd.TableStatus
	if err := row.Scan(&tableStatus); err != nil {
		fmt.Println(err.Error())
		return
	}
	fmt.Printf("TableStatus: %v\n", tableStatus)
}
Output:

Example (ExecContext)
package main

import (
	"context"
	"database/sql"
	"fmt"
	"github.com/aws/aws-sdk-go-v2/config"
	"github.com/miyamo2/pqxd"
)

func MustDB() *sql.DB {
	awsConfig, err := config.LoadDefaultConfig(context.Background())
	if err != nil {
		panic(err)
	}
	return sql.OpenDB(pqxd.NewConnector(awsConfig))
}

func main() {
	db := MustDB()

	insertResult, err := db.Exec(`INSERT INTO "users" VALUE { 'id': ?, 'name': ? }`, "3", "Alice")
	if err != nil {
		fmt.Println(err.Error())
		return
	}
	affected, err := insertResult.RowsAffected()
	if err != nil {
		fmt.Println(err.Error())
	}
	if affected != 1 {
		fmt.Println(fmt.Errorf("expected 1 row affected, got %d", affected))
		return
	}

	updateResult, err := db.Exec(`UPDATE "users" SET "name" = ? WHERE id = ?`, "Bob", "2")
	if err != nil {
		fmt.Println(err.Error())
		return
	}
	affected, err = updateResult.RowsAffected()
	if err != nil {
		fmt.Println(err.Error())
		return
	}
	if affected != 1 {
		fmt.Println(fmt.Errorf("expected 1 row affected, got %d", affected))
		return
	}

	deleteResult, err := db.Exec(`DELETE FROM "users" WHERE id = ?`, "1")
	if err != nil {
		fmt.Println(err.Error())
		return
	}
	affected, err = deleteResult.RowsAffected()
	if err != nil {
		fmt.Println(err.Error())
		return
	}
	if affected != 1 {
		fmt.Println(fmt.Errorf("expected 1 row affected, got %d", affected))
		return
	}
}
Output:

Example (ExecInTransaction)
package main

import (
	"context"
	"database/sql"
	"fmt"
	"github.com/aws/aws-sdk-go-v2/config"
	"github.com/miyamo2/pqxd"
)

func MustDB() *sql.DB {
	awsConfig, err := config.LoadDefaultConfig(context.Background())
	if err != nil {
		panic(err)
	}
	return sql.OpenDB(pqxd.NewConnector(awsConfig))
}

func main() {
	db := MustDB()

	tx, err := db.Begin()
	if err != nil {
		fmt.Println(err.Error())
		return
	}

	insertResult, err := tx.Exec(`INSERT INTO "users" VALUE { 'id': ?, 'name': ? }`, "3", "Alice")
	if err != nil {
		fmt.Println(err.Error())
		tx.Rollback()
		return
	}

	updateResult, err := tx.Exec(`UPDATE "users" SET "name" = ? WHERE id = ?`, "Bob", "2")
	if err != nil {
		fmt.Println(err.Error())
		tx.Rollback()
		return
	}

	deleteResult, err := tx.Exec(`DELETE FROM "users" WHERE id = ?`, "1")
	if err != nil {
		fmt.Println(err.Error())
		tx.Rollback()
		return
	}

	// RowsAffected is available after commit
	tx.Commit()

	// RowsAffected might return 0 or 1. If 0, it means statement is not successful.
	if affected, err := insertResult.RowsAffected(); err != nil || affected != 1 {
		fmt.Println(err.Error())
		return
	}

	if affected, err := updateResult.RowsAffected(); err != nil || affected != 1 {
		fmt.Println(err.Error())
		return
	}

	if affected, err := deleteResult.RowsAffected(); err != nil || affected != 1 {
		fmt.Println(err.Error())
		return
	}
}
Output:

Example (ListTables)
package main

import (
	"context"
	"database/sql"
	"fmt"
	"github.com/aws/aws-sdk-go-v2/config"
	"github.com/miyamo2/pqxd"
)

func MustDB() *sql.DB {
	awsConfig, err := config.LoadDefaultConfig(context.Background())
	if err != nil {
		panic(err)
	}
	return sql.OpenDB(pqxd.NewConnector(awsConfig))
}

func main() {
	db := MustDB()

	rows, err := db.QueryContext(context.Background(), `SELECT * FROM "!pqxd_list_tables"`)
	if err != nil {
		fmt.Println(err.Error())
		return
	}

	for rows.NextResultSet() {
		for rows.Next() {
			var tableName string
			if err := rows.Scan(&tableName); err != nil {
				fmt.Println(err.Error())
				return
			}
			fmt.Printf("tableName: %s\n", tableName)
		}
	}
}
Output:

Example (PrepareContext)
package main

import (
	"context"
	"database/sql"
	"fmt"
	"github.com/aws/aws-sdk-go-v2/config"
	"github.com/miyamo2/pqxd"
)

func MustDB() *sql.DB {
	awsConfig, err := config.LoadDefaultConfig(context.Background())
	if err != nil {
		panic(err)
	}
	return sql.OpenDB(pqxd.NewConnector(awsConfig))
}

func main() {
	db := MustDB()

	stmt, err := db.PrepareContext(context.Background(), `SELECT id, name FROM "users" WHERE id = ?`)
	if err != nil {
		fmt.Println(err.Error())
		return
	}
	defer stmt.Close()

	row := stmt.QueryRowContext(context.Background(), 1)
	var id, name string
	if err := row.Scan(&id, &name); err != nil {
		fmt.Println(err.Error())
		return
	}
	fmt.Printf("id: %s, name: %s\n", id, name)

	stmt, err = db.PrepareContext(context.Background(), `INSERT INTO "users" VALUE { 'id': ?, 'name': ? }`)
	if err != nil {
		fmt.Println(err.Error())
		return
	}
	defer stmt.Close()

	insertResult, err := stmt.Exec("3", "Alice")
	if err != nil {
		fmt.Println(err.Error())
		return
	}
	affected, err := insertResult.RowsAffected()
	if err != nil {
		fmt.Println(err.Error())
		return
	}
	if affected != 1 {
		fmt.Println(fmt.Errorf("expected 1 row affected, got %d", affected))
		return
	}
}
Output:

Example (QueryContext)
package main

import (
	"context"
	"database/sql"
	"fmt"
	"github.com/aws/aws-sdk-go-v2/config"
	"github.com/miyamo2/pqxd"
)

func MustDB() *sql.DB {
	awsConfig, err := config.LoadDefaultConfig(context.Background())
	if err != nil {
		panic(err)
	}
	return sql.OpenDB(pqxd.NewConnector(awsConfig))
}

func main() {
	db := MustDB()
	rows, err := db.QueryContext(context.Background(), `SELECT id, name FROM "users"`)
	if err != nil {
		fmt.Println(err.Error())
		return
	}

	for rows.NextResultSet() {
		for rows.Next() {
			var id, name string
			err := rows.Scan(&id, &name)
			if err != nil {
				fmt.Println(err.Error())
				return
			}
			fmt.Printf("id: %s, name: %s\n", id, name)
		}
	}
	rows, err = db.QueryContext(context.Background(), `SELECT name FROM "users"`)
	if err != nil {
		fmt.Println(err.Error())
	}
	for rows.NextResultSet() {
		for rows.Next() {
			var name string
			if err := rows.Scan(&name); err != nil {
				fmt.Println(err.Error())
				return
			}
			fmt.Printf("name: %s\n", name)
		}
	}
}
Output:

Example (QueryInTransaction)
package main

import (
	"context"
	"database/sql"
	"fmt"
	"github.com/aws/aws-sdk-go-v2/config"
	"github.com/miyamo2/pqxd"
)

func MustDB() *sql.DB {
	awsConfig, err := config.LoadDefaultConfig(context.Background())
	if err != nil {
		panic(err)
	}
	return sql.OpenDB(pqxd.NewConnector(awsConfig))
}

func main() {
	db := MustDB()

	tx, err := db.BeginTx(context.Background(), nil)
	if err != nil {
		fmt.Println(err.Error())
		return
	}
	row := tx.QueryRowContext(context.Background(), `SELECT id, name FROM "users" WHERE id = ?`, 1)
	rows, err := tx.QueryContext(context.Background(), `SELECT id, name FROM "users" WHERE id = ?`, 2)
	if err != nil {
		fmt.Println(err.Error())
		tx.Rollback()
		return
	}

	// WARNING: Do not use `tx.Commit()` when using `SELECT` statement.
	//
	// Each `sql.Rows` or `sql.Row` is resolved
	// the first time `rows.NextResultSet()`, `rows.Next()` or `row.Scan()`
	// is performed within that transaction.
	// So, after the `rows.NextResultSet()`, `rows.Next()` or `row.Scan()` is performed,
	// the transaction is automatically committed.
	for rows.Next() {
		var id, name string
		if err := rows.Scan(&id, &name); err != nil {
			fmt.Println(err.Error())
			tx.Rollback()
			return
		}
		fmt.Printf("id: %s, name: %s\n", id, name)
	}

	var id, name string
	if err := row.Scan(id, name); err != nil {
		return
	}
	fmt.Printf("id: %s, name: %s\n", id, name)
}
Output:

Example (QueryRowContext)
package main

import (
	"context"
	"database/sql"
	"fmt"
	"github.com/aws/aws-sdk-go-v2/config"
	"github.com/miyamo2/pqxd"
)

func MustDB() *sql.DB {
	awsConfig, err := config.LoadDefaultConfig(context.Background())
	if err != nil {
		panic(err)
	}
	return sql.OpenDB(pqxd.NewConnector(awsConfig))
}

func main() {
	db := MustDB()

	var id, name string
	err := db.QueryRowContext(context.Background(), `SELECT id, name FROM "users" WHERE id = ?`, 1).Scan(&id, &name)
	if err != nil {
		fmt.Println(err.Error())
		return
	}
	fmt.Printf("id: %s, name: %s\n", id, name)
}
Output:

Example (Returning)
package main

import (
	"context"
	"database/sql"
	"fmt"
	"github.com/aws/aws-sdk-go-v2/config"
	"github.com/miyamo2/pqxd"
)

func MustDB() *sql.DB {
	awsConfig, err := config.LoadDefaultConfig(context.Background())
	if err != nil {
		panic(err)
	}
	return sql.OpenDB(pqxd.NewConnector(awsConfig))
}

func main() {
	db := MustDB()

	row := db.QueryRowContext(context.Background(), `UPDATE "users" SET name = ? SET nickname = ? WHERE id = ? RETURNING MODIFIED OLD *`, "David", "Dave", "3")

	var name, nickname sql.NullString
	if err := row.Scan(&name, &nickname); err != nil {
		fmt.Printf("something happend. err: %s\n", err.Error())
		return
	}
	if name.Valid {
		fmt.Printf("name: %s\n", name.String)
	}
	if nickname.Valid {
		fmt.Printf("nickname: %s\n", nickname.String)
	}

	row = db.QueryRowContext(context.Background(), `UPDATE "users" SET name = ? SET nickname = ? WHERE id = ? RETURNING ALL OLD id`, "Bob", "2")
	var id string
	if err := row.Scan(&id); err != nil {
		fmt.Println(err.Error())
		return
	}
	fmt.Printf("id: %s\n", id)
}
Output:

Example (WithOpen)
package main

import (
	"database/sql"
	"fmt"
	"github.com/miyamo2/pqxd"
	"os"
)

func main() {
	region := os.Getenv("AWS_REGION")
	ak := os.Getenv("AWS_ACCESS_KEY_ID")
	sk := os.Getenv("AWS_SECRET_ACCESS_KEY")

	db, err := sql.Open(pqxd.DriverName, fmt.Sprintf("AWS_REGION=%s;AWS_ACCESS_KEY_ID=%s;AWS_SECRET_ACCESS_KEY=%s", region, ak, sk))
	if err != nil {
		fmt.Println(err.Error())
		return
	}
	if err := db.Ping(); err != nil {
		fmt.Println(err.Error())
	}
}
Output:

Index

Examples

Constants

View Source
const DriverName = "pqxd"

DriverName is the name that should be used in sql.Open to use this driver

Example:

db, err := sql.Open(pqxd.DriverName, "REGION=ap-northeast-1;ACCESSKEY=dummy;SECRETKEY=dummy")

Variables

View Source
var (
	// ErrNotSupported occurs when performed operation that is not supported in pqxd
	ErrNotSupported = errors.New("pqxd: not supported this operation")

	// ErrInvalidPreparedStatement occurs when the prepared statement is invalid
	ErrInvalidPreparedStatement = errors.New("pqxd: invalid prepared statement")

	// ErrStatementClosed occurs when the statement is closed
	ErrStatementClosed = errors.New("pqxd: statement is closed")

	// ErrTxDualBoot occurs when running more than one transaction at a time in a single connection
	ErrTxDualBoot = errors.New("pqxd: cannot run more than transaction at a time in a single connection")

	// ErrInvalidSyntaxOfQuery occurs when the query syntax is invalid
	ErrInvalidSyntaxOfQuery = errors.New("pqxd: invalid syntax of query")

	// ErrNotSupportedWithinTx occurs when performed operation that is not supported within transaction
	ErrNotSupportedWithinTx = errors.New("pqxd: not supported within transaction")
)

Functions

func NewConnector

func NewConnector(awsConfig aws.Config, options ...ConnectorOption) driver.Connector

NewConnector creates a new connector with the given aws.Config and ConnectorOption.

Example
package main

import (
	"context"
	"database/sql"
	"fmt"
	"github.com/aws/aws-sdk-go-v2/config"
	"github.com/miyamo2/pqxd"
)

func main() {
	awsConfig, err := config.LoadDefaultConfig(context.Background())
	if err != nil {
		fmt.Println(err.Error())
		return
	}
	db := sql.OpenDB(pqxd.NewConnector(awsConfig))
	if err != nil {
		fmt.Println(err.Error())
		return
	}
	if err := db.Ping(); err != nil {
		fmt.Println(err.Error())
	}
}
Output:

Types

type ArchivalSummary added in v0.4.0

type ArchivalSummary sql.Null[types.ArchivalSummary]

ArchivalSummary See: https://pkg.go.dev/github.com/aws/aws-sdk-go-v2/service/dynamodb/types#ArchivalSummary

func (*ArchivalSummary) Scan added in v0.4.0

func (a *ArchivalSummary) Scan(src any) error

Scan implements the sql.Scanner interface.

type AttributeDefinitions added in v0.4.0

type AttributeDefinitions []types.AttributeDefinition

AttributeDefinitions See: https://pkg.go.dev/github.com/aws/aws-sdk-go-v2/service/dynamodb/types#AttributeDefinitions

func (*AttributeDefinitions) Scan added in v0.4.0

func (a *AttributeDefinitions) Scan(src any) error

Scan implements the sql.Scanner interface.

type BillingModeSummary added in v0.4.0

type BillingModeSummary sql.Null[types.BillingModeSummary]

BillingModeSummary See: https://pkg.go.dev/github.com/aws/aws-sdk-go-v2/service/dynamodb/types#BillingModeSummary

func (*BillingModeSummary) Scan added in v0.4.0

func (b *BillingModeSummary) Scan(src any) error

Scan implements the sql.Scanner interface.

type ConnectorOption

type ConnectorOption func(*ConnectorSetting)

ConnectorOption is the option for the connector.

type ConnectorSetting

type ConnectorSetting struct{}

ConnectorSetting is the setting for the connector.

type CreationDateTime added in v0.4.0

type CreationDateTime struct {
	sql.NullTime
}

CreationDateTime See: https://pkg.go.dev/github.com/aws/aws-sdk-go-v2/service/dynamodb/types#TableDescription

func (*CreationDateTime) Scan added in v0.4.0

func (c *CreationDateTime) Scan(src any) error

Scan implements the sql.Scanner interface.

type DeletionProtectionEnabled added in v0.4.0

type DeletionProtectionEnabled struct {
	sql.NullBool
}

DeletionProtectionEnabled See: https://pkg.go.dev/github.com/aws/aws-sdk-go-v2/service/dynamodb/types#TableDescription

func (*DeletionProtectionEnabled) Scan added in v0.4.0

func (d *DeletionProtectionEnabled) Scan(src any) error

Scan implements the sql.Scanner interface.

type GlobalSecondaryIndexes added in v0.4.0

type GlobalSecondaryIndexes []types.GlobalSecondaryIndexDescription

GlobalSecondaryIndexes See: https://pkg.go.dev/github.com/aws/aws-sdk-go-v2/service/dynamodb/types#GlobalSecondaryIndex

func (*GlobalSecondaryIndexes) Scan added in v0.4.0

func (g *GlobalSecondaryIndexes) Scan(src any) error

type GlobalTableVersion added in v0.4.0

type GlobalTableVersion struct {
	sql.NullString
}

GlobalTableVersion See: https://pkg.go.dev/github.com/aws/aws-sdk-go-v2/service/dynamodb/types#TableDescription

func (*GlobalTableVersion) Scan added in v0.4.0

func (g *GlobalTableVersion) Scan(src any) error

Scan implements the sql.Scanner interface.

type ItemCount added in v0.4.0

type ItemCount struct {
	sql.NullInt64
}

ItemCount See: https://pkg.go.dev/github.com/aws/aws-sdk-go-v2/service/dynamodb/types#TableDescription

func (*ItemCount) Scan added in v0.4.0

func (i *ItemCount) Scan(src any) error

Scan implements the sql.Scanner interface.

type KeySchema added in v0.4.0

type KeySchema []types.KeySchemaElement

KeySchema See: https://pkg.go.dev/github.com/aws/aws-sdk-go-v2/service/dynamodb/types#KeySchemaElement

func (*KeySchema) Scan added in v0.4.0

func (k *KeySchema) Scan(src any) error

Scan implements the sql.Scanner interface.

type LocalSecondaryIndexes added in v0.4.0

type LocalSecondaryIndexes []types.LocalSecondaryIndex

LocalSecondaryIndexes See: https://pkg.go.dev/github.com/aws/aws-sdk-go-v2/service/dynamodb/types#LocalSecondaryIndex

func (*LocalSecondaryIndexes) Scan added in v0.4.0

func (l *LocalSecondaryIndexes) Scan(src any) error

Scan implements the sql.Scanner interface.

type OnDemandThroughput added in v0.4.0

type OnDemandThroughput sql.Null[types.OnDemandThroughput]

OnDemandThroughput See: https://pkg.go.dev/github.com/aws/aws-sdk-go-v2/service/dynamodb/types#OnDemandThroughput

func (*OnDemandThroughput) Scan added in v0.4.0

func (o *OnDemandThroughput) Scan(src any) error

Scan implements the sql.Scanner interface.

type ProvisionedThroughput added in v0.4.0

type ProvisionedThroughput sql.Null[types.ProvisionedThroughputDescription]

ProvisionedThroughput See: https://pkg.go.dev/github.com/aws/aws-sdk-go-v2/service/dynamodb/types#ProvisionedThroughputDescription

func (*ProvisionedThroughput) Scan added in v0.4.0

func (p *ProvisionedThroughput) Scan(src any) error

Scan implements the sql.Scanner interface.

type Replicas added in v0.4.0

type Replicas sql.Null[types.ReplicaDescription]

Replicas See: https://pkg.go.dev/github.com/aws/aws-sdk-go-v2/service/dynamodb/types#ReplicaDescription

func (*Replicas) Scan added in v0.4.0

func (r *Replicas) Scan(src any) error

Scan implements the sql.Scanner interface.

type RestoreSummary added in v0.4.0

type RestoreSummary sql.Null[types.RestoreSummary]

RestoreSummary See: https://pkg.go.dev/github.com/aws/aws-sdk-go-v2/service/dynamodb/types#RestoreSummary

func (*RestoreSummary) Scan added in v0.4.0

func (r *RestoreSummary) Scan(src any) error

Scan implements the sql.Scanner interface.

type SSEDescription added in v0.4.0

type SSEDescription sql.Null[types.SSEDescription]

SSEDescription See: https://pkg.go.dev/github.com/aws/aws-sdk-go-v2/service/dynamodb/types#SSEDescription

func (*SSEDescription) Scan added in v0.4.0

func (s *SSEDescription) Scan(src any) error

Scan implements the sql.Scanner interface.

type StreamSpecification added in v0.4.0

type StreamSpecification sql.Null[types.StreamSpecification]

StreamSpecification See: https://pkg.go.dev/github.com/aws/aws-sdk-go-v2/service/dynamodb/types#StreamSpecification

func (*StreamSpecification) Scan added in v0.4.0

func (s *StreamSpecification) Scan(src any) error

Scan implements the sql.Scanner interface.

type TableClassSummary added in v0.4.0

type TableClassSummary sql.Null[types.TableClassSummary]

TableClassSummary See: https://pkg.go.dev/github.com/aws/aws-sdk-go-v2/service/dynamodb/types#TableClassSummary

func (*TableClassSummary) Scan added in v0.4.0

func (t *TableClassSummary) Scan(src any) error

Scan implements the sql.Scanner interface.

type TableStatus added in v0.4.0

type TableStatus struct {
	types.TableStatus
}

TableStatus See: https://pkg.go.dev/github.com/aws/aws-sdk-go-v2/service/dynamodb/types#TableDescription

func (*TableStatus) Scan added in v0.4.0

func (t *TableStatus) Scan(src any) error

Scan implements the sql.Scanner interface.

func (*TableStatus) String added in v0.4.0

func (t *TableStatus) String() string

String returns the string representation of the TableStatus.

Directories

Path Synopsis
Package internal is a generated GoMock package.
Package internal is a generated GoMock package.
tests
integration Module

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL