-
-
Notifications
You must be signed in to change notification settings - Fork 700
Add solution for Challenge 13 by Johrespi #721
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
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| @@ -0,0 +1,219 @@ | ||||||||||||||||||||||||||||||||||||||||||||||||
| package main | ||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||
| import ( | ||||||||||||||||||||||||||||||||||||||||||||||||
| "database/sql" | ||||||||||||||||||||||||||||||||||||||||||||||||
| "errors" | ||||||||||||||||||||||||||||||||||||||||||||||||
| "fmt" | ||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||
| _ "github.com/mattn/go-sqlite3" | ||||||||||||||||||||||||||||||||||||||||||||||||
| ) | ||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||
| // Product represents a product in the inventory system | ||||||||||||||||||||||||||||||||||||||||||||||||
| type Product struct { | ||||||||||||||||||||||||||||||||||||||||||||||||
| ID int64 | ||||||||||||||||||||||||||||||||||||||||||||||||
| Name string | ||||||||||||||||||||||||||||||||||||||||||||||||
| Price float64 | ||||||||||||||||||||||||||||||||||||||||||||||||
| Quantity int | ||||||||||||||||||||||||||||||||||||||||||||||||
| Category string | ||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||
| // ProductStore manages product operations | ||||||||||||||||||||||||||||||||||||||||||||||||
| type ProductStore struct { | ||||||||||||||||||||||||||||||||||||||||||||||||
| db *sql.DB | ||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||
| // NewProductStore creates a new ProductStore with the given database connection | ||||||||||||||||||||||||||||||||||||||||||||||||
| func NewProductStore(db *sql.DB) *ProductStore { | ||||||||||||||||||||||||||||||||||||||||||||||||
| return &ProductStore{db: db} | ||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||
| // InitDB sets up a new SQLite database and creates the products table | ||||||||||||||||||||||||||||||||||||||||||||||||
| func InitDB(dbPath string) (*sql.DB, error) { | ||||||||||||||||||||||||||||||||||||||||||||||||
| // TODO: Open a SQLite database connection | ||||||||||||||||||||||||||||||||||||||||||||||||
| // TODO: Create the products table if it doesn't exist | ||||||||||||||||||||||||||||||||||||||||||||||||
| // The table should have columns: id, name, price, quantity, category | ||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||
| db, err := sql.Open("sqlite3", dbPath) | ||||||||||||||||||||||||||||||||||||||||||||||||
| if err != nil { | ||||||||||||||||||||||||||||||||||||||||||||||||
| return nil, err | ||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||
| createTableQuery := ` CREATE TABLE IF NOT EXISTS products ( | ||||||||||||||||||||||||||||||||||||||||||||||||
| id INTEGER PRIMARY KEY AUTOINCREMENT, | ||||||||||||||||||||||||||||||||||||||||||||||||
| name TEXT NOT NULL, | ||||||||||||||||||||||||||||||||||||||||||||||||
| price REAL NOT NULL, | ||||||||||||||||||||||||||||||||||||||||||||||||
| quantity INTEGER NOT NULL, | ||||||||||||||||||||||||||||||||||||||||||||||||
| category TEXT NOT NULL | ||||||||||||||||||||||||||||||||||||||||||||||||
| );` | ||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||
| _, err = db.Exec(createTableQuery) | ||||||||||||||||||||||||||||||||||||||||||||||||
| if err != nil { | ||||||||||||||||||||||||||||||||||||||||||||||||
| return nil, err | ||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||
| return db, nil | ||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||
| // CreateProduct adds a new product to the database | ||||||||||||||||||||||||||||||||||||||||||||||||
| func (ps *ProductStore) CreateProduct(product *Product) error { | ||||||||||||||||||||||||||||||||||||||||||||||||
| // TODO: Insert the product into the database | ||||||||||||||||||||||||||||||||||||||||||||||||
| // TODO: Update the product.ID with the database-generated ID | ||||||||||||||||||||||||||||||||||||||||||||||||
| stmt := `INSERT INTO products (name, price, quantity, category) VALUES(?, ?, ?, ?)` | ||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||
| dbResult, err := ps.db.Exec(stmt, product.Name, product.Price, product.Quantity, product.Category) | ||||||||||||||||||||||||||||||||||||||||||||||||
| if err != nil { | ||||||||||||||||||||||||||||||||||||||||||||||||
| return err | ||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||
| id, err := dbResult.LastInsertId() | ||||||||||||||||||||||||||||||||||||||||||||||||
| if err != nil { | ||||||||||||||||||||||||||||||||||||||||||||||||
| return err | ||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||
| product.ID = id | ||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||
| return nil | ||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||
| // GetProduct retrieves a product by ID | ||||||||||||||||||||||||||||||||||||||||||||||||
| func (ps *ProductStore) GetProduct(id int64) (*Product, error) { | ||||||||||||||||||||||||||||||||||||||||||||||||
| // TODO: Query the database for a product with the given ID | ||||||||||||||||||||||||||||||||||||||||||||||||
| // TODO: Return a Product struct populated with the data or an error if not found | ||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||
| stmt := `SELECT id, name, price, quantity, category FROM products WHERE id = ?` | ||||||||||||||||||||||||||||||||||||||||||||||||
| row := ps.db.QueryRow(stmt, id) | ||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||
| var product Product | ||||||||||||||||||||||||||||||||||||||||||||||||
| err := row.Scan(&product.ID, &product.Name, &product.Price, &product.Quantity, &product.Category) | ||||||||||||||||||||||||||||||||||||||||||||||||
| if err != nil { | ||||||||||||||||||||||||||||||||||||||||||||||||
| if err == sql.ErrNoRows { | ||||||||||||||||||||||||||||||||||||||||||||||||
| return nil, errors.New("product not found") | ||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||
| return nil, err | ||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||
| return &product, nil | ||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||
| // UpdateProduct updates an existing product | ||||||||||||||||||||||||||||||||||||||||||||||||
| func (ps *ProductStore) UpdateProduct(product *Product) error { | ||||||||||||||||||||||||||||||||||||||||||||||||
| // TODO: Update the product in the database | ||||||||||||||||||||||||||||||||||||||||||||||||
| // TODO: Return an error if the product doesn't exist | ||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||
| p, err := ps.GetProduct(product.ID) | ||||||||||||||||||||||||||||||||||||||||||||||||
| if err != nil { | ||||||||||||||||||||||||||||||||||||||||||||||||
| return err | ||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||
| p.ID = product.ID | ||||||||||||||||||||||||||||||||||||||||||||||||
| p.Name = product.Name | ||||||||||||||||||||||||||||||||||||||||||||||||
| p.Price = product.Price | ||||||||||||||||||||||||||||||||||||||||||||||||
| p.Quantity = product.Quantity | ||||||||||||||||||||||||||||||||||||||||||||||||
| p.Category = product.Category | ||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||
| stmt := `UPDATE products SET name = ?, price = ?, quantity = ?, category = ? WHERE id = ?` | ||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||
| _, err = ps.db.Exec(stmt, p.Name, p.Price, p.Quantity, p.Category, p.ID) | ||||||||||||||||||||||||||||||||||||||||||||||||
| if err != nil { | ||||||||||||||||||||||||||||||||||||||||||||||||
| return err | ||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||
| return nil | ||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||
| // DeleteProduct removes a product by ID | ||||||||||||||||||||||||||||||||||||||||||||||||
| func (ps *ProductStore) DeleteProduct(id int64) error { | ||||||||||||||||||||||||||||||||||||||||||||||||
| // TODO: Delete the product from the database | ||||||||||||||||||||||||||||||||||||||||||||||||
| // TODO: Return an error if the product doesn't exist | ||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||
| stmt := `DELETE FROM products WHERE id = ?` | ||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||
| result, err := ps.db.Exec(stmt, id) | ||||||||||||||||||||||||||||||||||||||||||||||||
| if err != nil { | ||||||||||||||||||||||||||||||||||||||||||||||||
| return err | ||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||
| rowsAffected, err := result.RowsAffected() | ||||||||||||||||||||||||||||||||||||||||||||||||
| if err != nil { | ||||||||||||||||||||||||||||||||||||||||||||||||
| return err | ||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||
| if rowsAffected == 0 { | ||||||||||||||||||||||||||||||||||||||||||||||||
| return errors.New("product not found") | ||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||
| return nil | ||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||
| // ListProducts returns all products with optional filtering by category | ||||||||||||||||||||||||||||||||||||||||||||||||
| func (ps *ProductStore) ListProducts(category string) ([]*Product, error) { | ||||||||||||||||||||||||||||||||||||||||||||||||
| // TODO: Query the database for products | ||||||||||||||||||||||||||||||||||||||||||||||||
| // TODO: If category is not empty, filter by category | ||||||||||||||||||||||||||||||||||||||||||||||||
| // TODO: Return a slice of Product pointers | ||||||||||||||||||||||||||||||||||||||||||||||||
| stmt := `SELECT id, name, price, quantity, category FROM products | ||||||||||||||||||||||||||||||||||||||||||||||||
| WHERE (? = '' OR category = ?)` | ||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||
| var products []*Product | ||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||
| rows, err := ps.db.Query(stmt, category, category) | ||||||||||||||||||||||||||||||||||||||||||||||||
| if err != nil { | ||||||||||||||||||||||||||||||||||||||||||||||||
| return nil, err | ||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||
| defer rows.Close() | ||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||
| for rows.Next() { | ||||||||||||||||||||||||||||||||||||||||||||||||
| var product Product | ||||||||||||||||||||||||||||||||||||||||||||||||
| err := rows.Scan(&product.ID, &product.Name, &product.Price, &product.Quantity, &product.Category) | ||||||||||||||||||||||||||||||||||||||||||||||||
| if err != nil { | ||||||||||||||||||||||||||||||||||||||||||||||||
| return nil, err | ||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||
| products = append(products, &product) | ||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||
| return products, nil | ||||||||||||||||||||||||||||||||||||||||||||||||
|
Comment on lines
+166
to
+175
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Surface iteration errors from rows.Err() If the driver reports an error after Apply this diff: for rows.Next() {
product := &Product{}
if err := rows.Scan(&product.ID, &product.Name, &product.Price, &product.Quantity, &product.Category); err != nil {
return nil, err
}
products = append(products, product)
}
+
+ if err := rows.Err(); err != nil {
+ return nil, err
+ }
return products, nil📝 Committable suggestion
Suggested change
🤖 Prompt for AI Agents |
||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||
| // BatchUpdateInventory updates the quantity of multiple products in a single transaction | ||||||||||||||||||||||||||||||||||||||||||||||||
| func (ps *ProductStore) BatchUpdateInventory(updates map[int64]int) error { | ||||||||||||||||||||||||||||||||||||||||||||||||
| // TODO: Start a transaction | ||||||||||||||||||||||||||||||||||||||||||||||||
| // TODO: For each product ID in the updates map, update its quantity | ||||||||||||||||||||||||||||||||||||||||||||||||
| // TODO: If any update fails, roll back the transaction | ||||||||||||||||||||||||||||||||||||||||||||||||
| // TODO: Otherwise, commit the transaction | ||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||
| tx, err := ps.db.Begin() | ||||||||||||||||||||||||||||||||||||||||||||||||
| if err != nil { | ||||||||||||||||||||||||||||||||||||||||||||||||
| return err | ||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||
| defer func() { | ||||||||||||||||||||||||||||||||||||||||||||||||
| if err != nil { | ||||||||||||||||||||||||||||||||||||||||||||||||
| tx.Rollback() | ||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||
| }() | ||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||
| stmt := `UPDATE products SET quantity = ? WHERE id = ?` | ||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||
| for productID, newQuantity := range updates { | ||||||||||||||||||||||||||||||||||||||||||||||||
| result, err := tx.Exec(stmt, newQuantity, productID) | ||||||||||||||||||||||||||||||||||||||||||||||||
| if err != nil { | ||||||||||||||||||||||||||||||||||||||||||||||||
| return err | ||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||
| rowsAffected, err := result.RowsAffected() | ||||||||||||||||||||||||||||||||||||||||||||||||
| if err != nil { | ||||||||||||||||||||||||||||||||||||||||||||||||
| return err | ||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||
| if rowsAffected == 0 { | ||||||||||||||||||||||||||||||||||||||||||||||||
| return fmt.Errorf("product with ID %d not found", productID) | ||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||
| return tx.Commit() | ||||||||||||||||||||||||||||||||||||||||||||||||
|
Comment on lines
+186
to
+214
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Guarantee rollback when Commit fails Because the function returns Apply this diff: -func (ps *ProductStore) BatchUpdateInventory(updates map[int64]int) error {
+func (ps *ProductStore) BatchUpdateInventory(updates map[int64]int) (err error) {
tx, err := ps.db.Begin()
if err != nil {
return err
}
defer func() {
if err != nil {
- tx.Rollback()
+ _ = tx.Rollback()
}
}()
stmt := `UPDATE products SET quantity = ? WHERE id = ?`
for productID, newQuantity := range updates {
- result, err := tx.Exec(stmt, newQuantity, productID)
- if err != nil {
- return err
- }
+ result, execErr := tx.Exec(stmt, newQuantity, productID)
+ if execErr != nil {
+ err = execErr
+ return err
+ }
- rowsAffected, err := result.RowsAffected()
- if err != nil {
- return err
- }
+ rowsAffected, rowsErr := result.RowsAffected()
+ if rowsErr != nil {
+ err = rowsErr
+ return err
+ }
if rowsAffected == 0 {
- return fmt.Errorf("product with ID %d not found", productID)
+ err = fmt.Errorf("product with ID %d not found", productID)
+ return err
}
}
- return tx.Commit()
+ if commitErr := tx.Commit(); commitErr != nil {
+ _ = tx.Rollback()
+ return commitErr
+ }
+ return nil
}
|
||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||
| func main() { | ||||||||||||||||||||||||||||||||||||||||||||||||
| // Optional: you can write code here to test your implementation | ||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||
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.
Fix pointer reuse in ListProducts
productis a loop-scoped value, so every appended pointer ends up referencing the same memory and the slice returns multiple aliases of the last row. Please allocate a new struct per iteration before scanning to guarantee each entry points to its own product instance.Apply this diff:
(freecodecamp.org)
📝 Committable suggestion
🤖 Prompt for AI Agents