feat(podcasts): widen reader_settings key ceiling — item 2.1 foundation
reader_settings.setting_key was VARCHAR(50), and the composite podcast_order:<uuid> key already sits at exactly 50 chars — zero headroom. The deferred queue-editor idea (plan §0 item 2b) and any future per-feed/per-collection preference key would have been forced to payload-stuff inside the order row by a column width. Composite prefix:<uuid> keys are the norm now (§10 R5), so the ceiling widens to VARCHAR(120): idempotent in-place ALTER (varchar widening is metadata-only, no table rewrite), re-runs harmlessly on every boot per the schema convention. No new episode-membership tables: collection_items already carries ordered playlist membership (position, A2c) against media_items, which podcast episodes ARE — episodes join collections through the existing table. Pinned by TestReaderSettingsCompositeKeyHeadroom: a 57-char composite key (the podcast_custom_order:<uuid> shape) upserts and reads back. Under the old width this fails; sqlc codegen is byte-identical.
This commit is contained in:
1 parent
f7ba4d24d6
commit
7bf0b1257d
2 files changed
+36
-1
No files matched your search
@@ -471,6 +471,34 @@ func TestDeleteMediaItemPodcastGuard(t *testing.T) {
|
||||
})
|
||||
}
|
||||
|
||||
// ---- reader_settings key width (§10 R5, item 2.1): composite
|
||||
// prefix:<uuid> keys have headroom past the old 50-char ceiling ----
|
||||
|
||||
func TestReaderSettingsCompositeKeyHeadroom(t *testing.T) {
|
||||
setup := setupTestServer(t)
|
||||
userID := getRegularUserID(t, setup.DB)
|
||||
|
||||
// 57 chars — the deferred podcast_custom_order:<uuid> shape. Under
|
||||
// the old VARCHAR(50) this upsert fails; the widening (item 2.1)
|
||||
// makes it ordinary preference state.
|
||||
key := "podcast_custom_order:01921a2b-3c4d-5e6f-7a8b-9c0d1e2f3a4b"
|
||||
require.Len(t, key, 57, "sentinel: the pinned key must exceed the old VARCHAR(50)")
|
||||
|
||||
err := setup.DB.UpsertUserSettingByKey(seedCtx(), database.UpsertUserSettingByKeyParams{
|
||||
UserID: pgtype.UUID{Bytes: userID, Valid: true},
|
||||
SettingKey: key,
|
||||
SettingValue: []byte(`{"order":"custom"}`),
|
||||
})
|
||||
require.NoError(t, err, "composite keys longer than 50 chars must store (VARCHAR(120) headroom)")
|
||||
|
||||
val, err := setup.DB.GetUserSettingByKey(seedCtx(), database.GetUserSettingByKeyParams{
|
||||
UserID: pgtype.UUID{Bytes: userID, Valid: true},
|
||||
SettingKey: key,
|
||||
})
|
||||
require.NoError(t, err)
|
||||
require.JSONEq(t, `{"order":"custom"}`, string(val))
|
||||
}
|
||||
|
||||
// ---- download worker: state machine, idempotency, file layout ----
|
||||
|
||||
func TestPodcastDownloadWorker(t *testing.T) {
|
||||
|
||||
@@ -1628,12 +1628,19 @@ CREATE INDEX IF NOT EXISTS idx_dictionary_word ON dictionary_cache(word);
|
||||
CREATE TABLE IF NOT EXISTS reader_settings (
|
||||
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||
user_id UUID NOT NULL REFERENCES users(id) ON DELETE CASCADE,
|
||||
setting_key VARCHAR(50) NOT NULL,
|
||||
setting_key VARCHAR(120) NOT NULL,
|
||||
setting_value JSONB NOT NULL,
|
||||
updated_at TIMESTAMPTZ DEFAULT NOW(),
|
||||
UNIQUE(user_id, setting_key)
|
||||
);
|
||||
|
||||
-- Key-width headroom (podcast plan §10 R5, item 2.1): the composite
|
||||
-- `podcast_order:<uuid>` key sits at exactly 50 chars, and future
|
||||
-- per-feed/per-collection preference keys must not be forced to
|
||||
-- payload-stuff by a column width. Widening a varchar is metadata-only
|
||||
-- (no table rewrite); idempotent on every boot.
|
||||
ALTER TABLE reader_settings ALTER COLUMN setting_key TYPE VARCHAR(120);
|
||||
|
||||
CREATE INDEX IF NOT EXISTS idx_reader_settings_user ON reader_settings(user_id);
|
||||
|
||||
-- PDF bookmarks (custom user bookmarks)
|
||||
|
||||
Reference in new issue
Block a user