Skip to content

Conversation

@Johrespi
Copy link
Contributor

@Johrespi Johrespi commented Nov 8, 2025

Challenge 13 Solution

Submitted by: @Johrespi
Challenge: Challenge 13

Description

This PR contains my solution for Challenge 13.

Changes

  • Added solution file to challenge-13/submissions/Johrespi/solution-template.go

Testing

  • Solution passes all test cases
  • Code follows Go best practices

Thank you for reviewing my submission! 🚀

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Nov 8, 2025

Walkthrough

Introduces a ProductStore system in Go with SQLite backing, featuring a Product struct and ProductStore that encapsulates database operations. Implements database initialization, CRUD operations (CreateProduct, GetProduct, UpdateProduct, DeleteProduct), product listing with optional category filtering, and transactional batch inventory updates using prepared statements.

Changes

Cohort / File(s) Summary
ProductStore Implementation
challenge-13/submissions/Johrespi/solution-template.go
Added Product struct (ID, Name, Price, Quantity, Category) and ProductStore struct wrapping *sql.DB; added InitDB for SQLite schema setup; added NewProductStore constructor; implemented CRUD methods (CreateProduct, GetProduct, UpdateProduct, DeleteProduct); added ListProducts with category filtering and BatchUpdateInventory with transaction support

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

  • SQL correctness: Verify parameterized queries prevent injection and table schema initialization is sound
  • Transaction handling: Ensure BatchUpdateInventory properly manages transaction rollback and isolation
  • Error handling: Validate error cases (not found, constraint violations) are handled consistently across methods
  • Edge cases: Check boundary conditions in batch updates and category filtering logic

Possibly related PRs

Pre-merge checks

❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 66.67% which is insufficient. The required threshold is 80.00%. You can run @coderabbitai generate docstrings to improve docstring coverage.
✅ Passed checks (2 passed)
Check name Status Explanation
Title check ✅ Passed The title 'Add solution for Challenge 13 by Johrespi' is related to the changeset and accurately describes that a solution for Challenge 13 is being added, though it is generic and does not convey the specific implementation details.
Description check ✅ Passed The description is directly related to the changeset, clearly stating that it contains a solution for Challenge 13 with a file added to the appropriate directory, and includes testing validation checkpoints.

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 3

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 2d4b9cf and 69841c7.

📒 Files selected for processing (1)
  • challenge-13/submissions/Johrespi/solution-template.go (1 hunks)

Comment on lines +166 to +175
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
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Surface iteration errors from rows.Err()

If the driver reports an error after rows.Next() stops, it’s currently ignored and you return partial data. Add a rows.Err() check before returning to propagate those failures.

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

(freecodecamp.org)

📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
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
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
🤖 Prompt for AI Agents
In challenge-13/submissions/Johrespi/solution-template.go around lines 166 to
175, the code iterates rows and returns products without checking for any error
reported by the driver after iteration; add a check for rows.Err() after the for
loop and before returning to surface any iteration errors and return that error
(or wrap it) instead of silently returning partial results.

Comment on lines +166 to +173
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)
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical

Fix pointer reuse in ListProducts

product is 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:

-	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)
-	}
+	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)
+	}

(freecodecamp.org)

📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
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)
}
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)
}
🤖 Prompt for AI Agents
challenge-13/submissions/Johrespi/solution-template.go around lines 166 to 173:
the loop reuses a single loop-scoped variable "product" and appends its pointer
each iteration, causing all slice entries to point to the same struct; fix by
allocating a new Product instance inside the loop for each row (e.g., create a
new variable or take address of a fresh struct) and scan into that fresh
instance before appending so each slice element references its own product.

Comment on lines +186 to +214
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()
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Guarantee rollback when Commit fails

Because the function returns tx.Commit() directly, err stays nil and the deferred rollback never runs on commit failure. Capture the commit error first, run an explicit rollback on failure, and only then return. Converting to a named return makes the defer pattern work as intended.

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
 }

(tip.golang.org)

Committable suggestion skipped: line range outside the PR's diff.

@RezaSi RezaSi merged commit f0ca42a into RezaSi:main Nov 9, 2025
5 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants