-
-
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
Conversation
Why didn't you just reopen the previous pull request? |
@pashagolub I have no right to re-open closed PRs in this repository. I cannot see the button near "Comment". |
The example could be completed by serving all the results within an HTML page. What Do you think? |
Sorry for delay with answer. I want examples to be as real as possible showing the real life use cases. That why I don't buy this one: example.batch.Queue("SELECT * FROM ledger")
example.batch.Queue("SELECT * FROM ledger WHERE amount = 1") I will never in my life execute such a batch. Why? The first result set has it all. |
Tests failed and coverage is below threshold. === RUN TestExpectBatch
--- PASS: TestExpectBatch (0.00s)
=== RUN TestExpectBegin
.\pgxmock\examples\batch\batch_test.go:50: there were unfulfilled expectations: there is a remaining expectation which was not matched: ExpectedBegin => expecting call to Begin() or to BeginTx()
--- FAIL: TestExpectBegin (0.00s)
FAIL
coverage: 6.7% of statements
FAIL github.com/pashagolub/pgxmock/v4/examples/batch 0.029s |
examples/batch/batch.go
Outdated
} | ||
sql := `INSERT INTO metadata (title, authors, subject, description) | ||
VALUES | ||
(` + strings.Join(fields[:4], ", ") + ");" |
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.
We don't do parameters concatenation, this is the possible place for SQL injection. We should use parameterized queries.
examples/batch/batch.go
Outdated
defer example.Close() | ||
|
||
// Add an example to database table | ||
example.insertRow( |
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.
Why we insert here and in databaseSetup()
?
examples/batch/batch.go
Outdated
|
||
} | ||
|
||
func (ex *ExampleBatch) requestBatch() (err error) { |
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.
I don't see any need in this function. Let's keep it simple
examples/batch/batch.go
Outdated
output strings.Builder | ||
} | ||
|
||
func (ex *ExampleBatch) insertRow(fields ...string) (err error) { |
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.
I don't see any value in insertRow
function. And it's not covered by tests. It's better to remove it and keep things simple
examples/batch/batch.go
Outdated
Begin(context.Context) (pgx.Tx, error) | ||
Exec(context.Context, string, ...interface{}) (pgconn.CommandTag, error) |
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
neither Exec
used anywhere in code. Although this is not an error, we want to show our user that interfaces must be minimal
@pashagolub Can you explain in what order a single row is retrieved with
Thanks for your comments. 💡 |
Yes, this is the idea behind the batch. You send all queries at once and then receive all results at once. And you are free to work with every result set as you wish. If the example below the magic of sending and execution happened during func (pg *postgres) BulkInsertUsers(ctx context.Context, users []model.User) error {
query := `INSERT INTO users (name, email) VALUES (@userName, @userEmail)`
batch := &pgx.Batch{}
for _, user := range users {
args := pgx.NamedArgs{
"userName": user.Name,
"userEmail": user.Email,
}
batch.Queue(query, args)
}
results := pg.db.SendBatch(ctx, batch)
defer results.Close()
for _, user := range users {
_, err := results.Exec()
if err != nil {
var pgErr *pgconn.PgError
if errors.As(err, &pgErr) && pgErr.Code == pgerrcode.UniqueViolation {
log.Printf("user %s already exists", user.Name)
continue
}
return fmt.Errorf("unable to insert row: %w", err)
}
}
return results.Close()
} |
example.SendCustomBatch([]string{ | ||
"SELECT title FROM metadata", | ||
"SELECT authors FROM metadata", | ||
"SELECT subject, description FROM metadata", | ||
}) |
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.
I don't buy it. It should be one query
SELECT title, authors, subject, description FROM metadata
This is bad example.
for _, query := range example.batch.QueuedQueries { | ||
fmt.Println(query.SQL) | ||
rows, _ := example.results.Query() | ||
for rows.Next() { | ||
values, _ := rows.Values() | ||
fmt.Println(values) | ||
} | ||
} | ||
} |
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.
Useless piece of code. No purpose. No meaning.
type exampleBatch struct { | ||
batch *pgx.Batch // batch pointer | ||
results pgx.BatchResults // query results | ||
db PgxPoolInterface // database connection | ||
} |
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.
I don't understand the meaning of this.
Unfortunately, I can't accept this work. This is not a proper example of using |
This PR would like to improve my work on #201