* api: detect an absent copy body by io.EOF from the decoder rather than
Content-Length. A chunked request reports -1, not 0, so the length test
read an empty chunked body as malformed and 400'd instead of taking the
default-name path. Covered by a new unknown-length test.
* store: hoist the garden_objects/plantings INSERTs into shared
objectInsert/plantingInsert statements with matching *InsertArgs
helpers. CopyGarden had its own copies of both column lists, so a
column added to the table could be wired into Create* and silently
dropped from a copy.
* store: move the queryer interface to sqlite.go, next to the other
shared query plumbing, instead of users.go.
* web: derive a copy's prefilled name via defaultCopyName, mirroring the
server's copyName including its 200-BYTE cap. The inline
`${name} (copy)` both duplicated the suffix and, for a garden whose
name was already at the cap, prefilled an over-long name that the
server rejected with a 400. Unit-tested, including multi-byte
truncation on a code-point boundary.
* test: drop a throwaway `_ = kept`.
Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]>
This commit is contained in:
+18
-13
@@ -104,16 +104,25 @@ func (d *DB) GetPlanting(ctx context.Context, id int64) (*domain.Planting, error
|
||||
return p, nil
|
||||
}
|
||||
|
||||
// plantingInsert inserts one plantings row and returns it. Shared by
|
||||
// CreatePlanting, the CreatePlantings batch and CopyGarden's in-transaction copy
|
||||
// — with plantingInsertArgs supplying its parameters — so all three stay in step
|
||||
// with the table. removed_at is never set here: a new plop is active, and "clear
|
||||
// bed" sets removed_at later via UpdatePlanting.
|
||||
const plantingInsert = `INSERT INTO plantings (object_id, plant_id, x_cm, y_cm, radius_cm, count, label, planted_at)
|
||||
VALUES (?, ?, ?, ?, ?, ?, ?, ?)
|
||||
RETURNING ` + plantingColumns
|
||||
|
||||
// plantingInsertArgs builds plantingInsert's parameters, parenting p to objectID
|
||||
// (p's own object normally, and the copied object when copying a garden).
|
||||
func plantingInsertArgs(objectID int64, p *domain.Planting) []any {
|
||||
return []any{objectID, p.PlantID, p.XCM, p.YCM, p.RadiusCM, p.Count, p.Label, p.PlantedAt}
|
||||
}
|
||||
|
||||
// CreatePlanting inserts a plop (fields already validated by the service) and
|
||||
// returns the stored row. removed_at is always NULL on create — a new plop is
|
||||
// active; "clear bed" sets removed_at later via UpdatePlanting.
|
||||
// returns the stored row.
|
||||
func (d *DB) CreatePlanting(ctx context.Context, p *domain.Planting) (*domain.Planting, error) {
|
||||
created, err := scanPlanting(d.sql.QueryRowContext(ctx,
|
||||
`INSERT INTO plantings (object_id, plant_id, x_cm, y_cm, radius_cm, count, label, planted_at)
|
||||
VALUES (?, ?, ?, ?, ?, ?, ?, ?)
|
||||
RETURNING `+plantingColumns,
|
||||
p.ObjectID, p.PlantID, p.XCM, p.YCM, p.RadiusCM, p.Count, p.Label, p.PlantedAt,
|
||||
))
|
||||
created, err := scanPlanting(d.sql.QueryRowContext(ctx, plantingInsert, plantingInsertArgs(p.ObjectID, p)...))
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("store: insert planting: %w", err)
|
||||
}
|
||||
@@ -132,13 +141,9 @@ func (d *DB) CreatePlantings(ctx context.Context, plantings []*domain.Planting)
|
||||
}
|
||||
defer tx.Rollback() //nolint:errcheck // no-op after a successful commit
|
||||
|
||||
const stmt = `INSERT INTO plantings (object_id, plant_id, x_cm, y_cm, radius_cm, count, label, planted_at)
|
||||
VALUES (?, ?, ?, ?, ?, ?, ?, ?)
|
||||
RETURNING ` + plantingColumns
|
||||
out := make([]domain.Planting, 0, len(plantings))
|
||||
for _, p := range plantings {
|
||||
created, err := scanPlanting(tx.QueryRowContext(ctx, stmt,
|
||||
p.ObjectID, p.PlantID, p.XCM, p.YCM, p.RadiusCM, p.Count, p.Label, p.PlantedAt))
|
||||
created, err := scanPlanting(tx.QueryRowContext(ctx, plantingInsert, plantingInsertArgs(p.ObjectID, p)...))
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("store: insert planting (batch): %w", err)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user