-
-
Notifications
You must be signed in to change notification settings - Fork 50
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Pgxmock batch example in Go #212
Closed
Closed
Changes from 15 commits
Commits
Show all changes
22 commits
Select commit
Hold shift + click to select a range
3a09c76
Batch example draft.
giuliocn 8e2db26
Working example with pgx.Batch
giuliocn d36e83e
Create README.md
giuliocn 1762d12
Added batch example with comments.
giuliocn 1a89fce
Resolved comments from author.
giuliocn b3539db
Resolved comments from author.
giuliocn a36b504
Update README.md
giuliocn 3255207
Update batch_test.go
giuliocn 886f321
Reads results and print rows to stdout.
giuliocn 49f014d
Fix typos and update README.
giuliocn 4841976
Create OUTPUT.md and formatting.
giuliocn b0d1e2a
bump to v4
pashagolub e8cf2a8
one-statement queries are automatically executed in the transaction
pashagolub 0768aaf
Merge branch 'pashagolub:master' into pull-request-batch
giuliocn bd3d7cd
Delete .md files from example tree.
giuliocn 8a03813
Example Batch new struct added and sample tests.
giuliocn 84a0ec5
Merge branch 'pashagolub:master' into pr-batch-example
giuliocn ad1dad8
Batch results at localhost in raw text format.
giuliocn 2f03bb6
Merge branch 'master' into pr-batch-example
pashagolub 117f4c9
Merge branch 'pashagolub:master' into pr-batch-example
giuliocn efee7a3
Complete example with revised structure.
giuliocn 4a4e1d9
Failing tests due to expected queries.
giuliocn File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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,167 @@ | ||
package main | ||
|
||
import ( | ||
"context" | ||
"errors" | ||
"fmt" | ||
"os" | ||
|
||
pgx "github.com/jackc/pgx/v5" | ||
pgconn "github.com/jackc/pgx/v5/pgconn" | ||
pgxpool "github.com/jackc/pgx/v5/pgxpool" | ||
) | ||
|
||
type PgxIface interface { | ||
Begin(context.Context) (pgx.Tx, error) | ||
Exec(context.Context, string, ...interface{}) (pgconn.CommandTag, error) | ||
Close() | ||
} | ||
|
||
func databaseSetup(db PgxIface) (err error) { | ||
// Create a new table 'ledger' | ||
sql := `CREATE TABLE IF NOT EXISTS ledger ( | ||
id BIGINT PRIMARY KEY GENERATED BY DEFAULT AS IDENTITY, | ||
description TEXT NOT NULL, | ||
amount BIGINT NOT NULL);` | ||
_, err = db.Exec(context.Background(), sql) | ||
return err | ||
} | ||
|
||
func iterateResults(br pgx.BatchResults, QueuedQueries []*pgx.QueuedQuery) error { | ||
|
||
file, err := os.Create("OUTPUT.md") | ||
if err != nil { | ||
return fmt.Errorf("iterateResults: %s", err) | ||
} | ||
defer file.Close() | ||
|
||
fmt.Fprintf(file, "## PostgreSQL Batch Example output\n") | ||
|
||
// Iterate over a batch of queued queries | ||
for _, query := range QueuedQueries { | ||
|
||
// Print SQL field of the current query | ||
fmt.Fprintf(file, "### %v \n", query.SQL) | ||
|
||
// reads results from the current query | ||
rows, err := br.Query() | ||
if err != nil { | ||
return fmt.Errorf("iterateResults: %s", err) | ||
} | ||
|
||
// Print column headers | ||
fmt.Fprintf(file, "- *DESCRIPTION* , *AMOUNT* \n") | ||
// Iterate over the resulted rows | ||
// | ||
var id, amount, descr = int64(0), int64(0), string("") | ||
_, err = pgx.ForEachRow(rows, []any{&id, &descr, &amount}, func() error { | ||
fmt.Fprintf(file, "- \"%v\" , %d \n", descr, amount) | ||
return nil | ||
}) | ||
if err != nil { | ||
return fmt.Errorf("iterateResults: %s", err) | ||
} | ||
} | ||
|
||
return err | ||
} | ||
|
||
func requestBatch(db PgxIface) (err error) { | ||
|
||
// Initialize a database object | ||
tx, err := db.Begin(context.Background()) | ||
if err != nil { | ||
return fmt.Errorf("requestBatch: %s", err) | ||
} | ||
pashagolub marked this conversation as resolved.
Show resolved
Hide resolved
|
||
// Finally, commit changes or rollback | ||
defer func() { | ||
switch err { | ||
case nil: | ||
err = tx.Commit(context.Background()) | ||
default: | ||
_ = tx.Rollback(context.Background()) | ||
} | ||
}() | ||
|
||
// Create a Batch object | ||
batch := &pgx.Batch{} | ||
|
||
// Add SQL commands to queue | ||
batch.Queue( | ||
`INSERT INTO ledger(description, amount) VALUES ($1, $2), ($3, $4)`, | ||
"first item", 1, "second item", 2) | ||
|
||
batch.Queue("SELECT * FROM ledger") | ||
batch.Queue("SELECT * FROM ledger WHERE amount = 1") | ||
|
||
// Efficiently transmits queued queries as a single transaction. | ||
// After the queries are run, a BatchResults object is returned. | ||
// | ||
br := tx.SendBatch(context.Background(), batch) | ||
if br == nil { | ||
return errors.New("SendBatch returns a NIL object") | ||
} | ||
defer br.Close() | ||
|
||
// Read the first query | ||
_, err = br.Exec() | ||
if err != nil { | ||
return err | ||
} | ||
// Iterate over batch results and queries. | ||
// Note: the first query is left out of the queue. | ||
// | ||
return iterateResults(br, batch.QueuedQueries[1:]) | ||
|
||
} | ||
|
||
func databaseCleanup(db PgxIface) (err error) { | ||
|
||
// Initialize a database object | ||
tx, err := db.Begin(context.Background()) | ||
if err != nil { | ||
return fmt.Errorf("databaseCleanup: %s", err) | ||
} | ||
// Finally, commit changes or rollback | ||
defer func() { | ||
switch err { | ||
case nil: | ||
err = tx.Commit(context.Background()) | ||
default: | ||
_ = tx.Rollback(context.Background()) | ||
} | ||
}() | ||
|
||
// Delete all rows in table ledger | ||
sql := `DELETE FROM ledger ;` | ||
|
||
// Execute SQL commands | ||
_, err = tx.Exec(context.Background(), sql) | ||
|
||
return err | ||
} | ||
|
||
func main() { | ||
|
||
// @NOTE: the real connection is not required for tests | ||
db, err := pgxpool.New(context.Background(), "postgres://<user>:<password>@<hostname>/<database>") | ||
if err != nil { | ||
panic(err) | ||
} | ||
defer db.Close() | ||
|
||
// Create a database table | ||
if err = databaseSetup(db); err != nil { | ||
panic(err) | ||
} | ||
|
||
// Create and send a batch request | ||
if err = requestBatch(db); err != nil { | ||
panic(err) | ||
} | ||
|
||
// Delete all rows in table ledger | ||
if err = databaseCleanup(db); err != nil { | ||
panic(err) | ||
} | ||
} |
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,41 @@ | ||
package main | ||
|
||
import ( | ||
"testing" | ||
|
||
"github.com/pashagolub/pgxmock/v4" | ||
) | ||
|
||
// a successful test case | ||
func TestShouldSelectRows(t *testing.T) { | ||
mock, err := pgxmock.NewPool() | ||
if err != nil { | ||
t.Fatal(err) | ||
} | ||
defer mock.Close() | ||
|
||
// TODO | ||
} | ||
|
||
// a failing test case | ||
func TestShouldRollbackOnFailure(t *testing.T) { | ||
mock, err := pgxmock.NewPool() | ||
if err != nil { | ||
t.Fatal(err) | ||
} | ||
defer mock.Close() | ||
|
||
mock.ExpectBegin() | ||
// TODO: mock.ExpectBatch() | ||
mock.ExpectRollback() | ||
pashagolub marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
||
// now we execute our method | ||
if err = requestBatch(mock); err == nil { | ||
t.Errorf("was expecting an error, but there was none") | ||
} | ||
|
||
// we make sure that all expectations were met | ||
if err := mock.ExpectationsWereMet(); err != nil { | ||
t.Errorf("there were unfulfilled expectations: %s", err) | ||
} | ||
} |
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nor
Begin
neitherExec
used anywhere in code. Although this is not an error, we want to show our user that interfaces must be minimal