Skip to content

Commit 638966b

Browse files
Add solution for Challenge 13 by Kosench (#835)
* Add solution for Challenge 13 * fix --------- Co-authored-by: go-interview-practice-bot[bot] <230190823+go-interview-practice-bot[bot]@users.noreply.github.com>
1 parent 74326a7 commit 638966b

File tree

1 file changed

+241
-0
lines changed

1 file changed

+241
-0
lines changed
Lines changed: 241 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,241 @@
1+
package main
2+
3+
import (
4+
"database/sql"
5+
"errors"
6+
"fmt"
7+
8+
_ "github.com/mattn/go-sqlite3"
9+
)
10+
11+
// Product represents a product in the inventory system
12+
type Product struct {
13+
ID int64
14+
Name string
15+
Price float64
16+
Quantity int
17+
Category string
18+
}
19+
20+
// ProductStore manages product operations
21+
type ProductStore struct {
22+
db *sql.DB
23+
}
24+
25+
// NewProductStore creates a new ProductStore with the given database connection
26+
func NewProductStore(db *sql.DB) *ProductStore {
27+
return &ProductStore{db: db}
28+
}
29+
30+
// InitDB sets up a new SQLite database and creates the products table
31+
func InitDB(dbPath string) (*sql.DB, error) {
32+
db, err := sql.Open("sqlite3", dbPath)
33+
if err != nil {
34+
return nil, fmt.Errorf("failed to open database: %w", err)
35+
}
36+
37+
if err := db.Ping(); err != nil {
38+
db.Close()
39+
return nil, fmt.Errorf("failed to ping database: %w", err)
40+
}
41+
42+
query := `
43+
CREATE TABLE IF NOT EXISTS products (
44+
id INTEGER PRIMARY KEY,
45+
name TEXT,
46+
price REAL NOT NULL CHECK(price >= 0),
47+
quantity INTEGER NOT NULL DEFAULT 0 CHECK(quantity >= 0),
48+
category TEXT
49+
)`
50+
51+
_, err = db.Exec(query)
52+
if err != nil {
53+
db.Close()
54+
return nil, fmt.Errorf("failed to create table: %w", err)
55+
}
56+
57+
// The table should have columns: id, name, price, quantity, category
58+
return db, nil
59+
}
60+
61+
// CreateProduct adds a new product to the database
62+
func (ps *ProductStore) CreateProduct(product *Product) error {
63+
query := `
64+
INSERT INTO products (name, price, quantity, category)
65+
VALUES (?, ?, ?, ?)
66+
`
67+
68+
result, err := ps.db.Exec(query, product.Name, product.Price, product.Quantity, product.Category)
69+
if err != nil {
70+
return fmt.Errorf("failed to create product: %w", err)
71+
}
72+
73+
id, err := result.LastInsertId()
74+
if err != nil {
75+
return fmt.Errorf("failed to get last insert id: %w", err)
76+
}
77+
78+
product.ID = id
79+
80+
return nil
81+
}
82+
83+
// GetProduct retrieves a product by ID
84+
func (ps *ProductStore) GetProduct(id int64) (*Product, error) {
85+
query := `
86+
SELECT id, name, price, quantity, category
87+
FROM products
88+
WHERE id = ?
89+
`
90+
91+
row := ps.db.QueryRow(query, id)
92+
93+
p := &Product{}
94+
err := row.Scan(&p.ID, &p.Name, &p.Price, &p.Quantity, &p.Category)
95+
if err != nil {
96+
if errors.Is(err, sql.ErrNoRows) {
97+
return nil, fmt.Errorf("product with ID %d not found", id)
98+
}
99+
return nil, err
100+
}
101+
102+
return p, nil
103+
}
104+
105+
// UpdateProduct updates an existing product
106+
func (ps *ProductStore) UpdateProduct(product *Product) error {
107+
query := `
108+
UPDATE products
109+
SET name = ?, price = ?, quantity = ?, category = ?
110+
WHERE id = ?
111+
`
112+
113+
result, err := ps.db.Exec(query,
114+
product.Name,
115+
product.Price,
116+
product.Quantity,
117+
product.Category,
118+
product.ID,
119+
)
120+
if err != nil {
121+
return fmt.Errorf("failed to update product: %w", err)
122+
}
123+
124+
rowsAffected, err := result.RowsAffected()
125+
if err != nil {
126+
return fmt.Errorf("failed to get rows affected: %w", err)
127+
}
128+
129+
if rowsAffected == 0 {
130+
return fmt.Errorf("product with ID %d not found", product.ID)
131+
}
132+
133+
return nil
134+
}
135+
136+
// DeleteProduct removes a product by ID
137+
func (ps *ProductStore) DeleteProduct(id int64) error {
138+
query := `DELETE FROM products WHERE id = ?`
139+
140+
result, err := ps.db.Exec(query, id)
141+
if err != nil {
142+
return fmt.Errorf("failed to delete product: %w", err)
143+
}
144+
145+
rowsAffected, err := result.RowsAffected()
146+
if err != nil {
147+
return fmt.Errorf("failed to get rows affected: %w", err)
148+
}
149+
150+
if rowsAffected == 0 {
151+
return fmt.Errorf("product with ID %d not found", id)
152+
}
153+
154+
return nil
155+
}
156+
157+
// ListProducts returns all products with optional filtering by category
158+
func (ps *ProductStore) ListProducts(category string) ([]*Product, error) {
159+
var query string
160+
var rows *sql.Rows
161+
var err error
162+
163+
if category == "" {
164+
query = `
165+
SELECT id, name, price, quantity, category
166+
FROM products
167+
`
168+
rows, err = ps.db.Query(query)
169+
} else {
170+
query = `
171+
SELECT id, name, price, quantity, category
172+
FROM products
173+
WHERE category = ?
174+
`
175+
rows, err = ps.db.Query(query, category)
176+
}
177+
178+
if err != nil {
179+
return nil, fmt.Errorf("failed to query products: %w", err)
180+
}
181+
defer rows.Close()
182+
183+
var products []*Product
184+
for rows.Next() {
185+
p := &Product{}
186+
err := rows.Scan(&p.ID, &p.Name, &p.Price, &p.Quantity, &p.Category)
187+
if err != nil {
188+
return nil, fmt.Errorf("failed to scan product: %w", err)
189+
}
190+
products = append(products, p)
191+
}
192+
193+
if err = rows.Err(); err != nil {
194+
return nil, fmt.Errorf("error iterating products: %w", err)
195+
}
196+
return products, nil
197+
}
198+
199+
// BatchUpdateInventory updates the quantity of multiple products in a single transaction
200+
func (ps *ProductStore) BatchUpdateInventory(updates map[int64]int) error {
201+
tx, err := ps.db.Begin()
202+
if err != nil {
203+
return fmt.Errorf("failed to begin transaction: %w", err)
204+
}
205+
206+
stmt, err := tx.Prepare("UPDATE products SET quantity = ? WHERE id = ?")
207+
if err != nil {
208+
tx.Rollback()
209+
return fmt.Errorf("failed to prepare statement: %w", err)
210+
}
211+
defer stmt.Close()
212+
213+
for id, quantity := range updates {
214+
result, err := stmt.Exec(quantity, id)
215+
if err != nil {
216+
tx.Rollback()
217+
return fmt.Errorf("failed to update product %d: %w", id, err)
218+
}
219+
220+
rowsAffected, err := result.RowsAffected()
221+
if err != nil {
222+
tx.Rollback()
223+
return fmt.Errorf("failed to get rows affected: %w", err)
224+
}
225+
226+
if rowsAffected == 0 {
227+
tx.Rollback()
228+
return fmt.Errorf("product with ID %d not found", id)
229+
}
230+
}
231+
232+
if err := tx.Commit(); err != nil {
233+
return fmt.Errorf("failed to commit transaction: %w", err)
234+
}
235+
236+
return nil
237+
}
238+
239+
func main() {
240+
// Optional: you can write code here to test your implementation
241+
}

0 commit comments

Comments
 (0)