mirror of
https://github.com/mauriceboe/TREK.git
synced 2026-10-24 07:53:09 +00:00
fix(server): plan 4 — trek_photo_cache_meta.cache_key NOT NULL
The column was a bare TEXT PRIMARY KEY, which SQLite does not implicitly make NOT NULL (unlike an INTEGER PRIMARY KEY rowid alias). Every writer (TrekPhotoCacheMetaRepository.upsertMeta, the table's only INSERT path) has always bound a real string — 3e's R5 confirmed this. Appends a new migration rebuilding the table with cache_key NOT NULL (SQLite has no ALTER COLUMN, so this follows the same rebuild recipe Migration20200101020000 uses in the opposite direction) and updates the entity/generated metadata to match. Schema tightening, not a behaviour change.
This commit is contained in:
@@ -4,7 +4,7 @@ import { TrekPhotoCacheMetaRepository } from '../repositories/TrekPhotoCacheMeta
|
||||
export class TrekPhotoCacheMeta {
|
||||
[EntityRepositoryType]?: TrekPhotoCacheMetaRepository;
|
||||
[PrimaryKeyProp]?: 'cache_key';
|
||||
cache_key?: string | null;
|
||||
cache_key!: string;
|
||||
content_type: string & Opt = 'image/jpeg';
|
||||
fetched_at!: number;
|
||||
}
|
||||
@@ -13,7 +13,7 @@ export const TrekPhotoCacheMetaSchema = defineEntity({
|
||||
class: TrekPhotoCacheMeta,
|
||||
repository: () => TrekPhotoCacheMetaRepository,
|
||||
properties: {
|
||||
cache_key: p.text().primary().nullable(),
|
||||
cache_key: p.text().primary(),
|
||||
content_type: p.text().default('image/jpeg'),
|
||||
fetched_at: p.integer().index('idx_trek_photo_cache_meta_fetched_at'),
|
||||
},
|
||||
|
||||
+41
@@ -0,0 +1,41 @@
|
||||
import { Migration } from '@mikro-orm/migrations';
|
||||
|
||||
/**
|
||||
* Plan 4 Task 8a program carry — `trek_photo_cache_meta.cache_key` was
|
||||
* declared a bare `TEXT PRIMARY KEY` (Migration20200101014900), which SQLite
|
||||
* does NOT implicitly make `NOT NULL` the way it does for an `INTEGER
|
||||
* PRIMARY KEY` rowid alias — a genuine, if surprising, SQLite quirk (3e's R5
|
||||
* Task 0 finding). Every statement that writes this column
|
||||
* (`TrekPhotoCacheMetaRepository.upsertMeta`'s `INSERT OR REPLACE`, its only
|
||||
* writer) has always bound a real `string`, never `null` — this migration
|
||||
* tightens the schema to match the code's actual invariant, not a behaviour
|
||||
* change. SQLite has no `ALTER COLUMN ... SET NOT NULL`, so the column is
|
||||
* added via the standard rebuild-table recipe (the same shape
|
||||
* Migration20200101020000 uses in the opposite direction).
|
||||
*/
|
||||
export class Migration20200101040200_trek_photo_cache_meta_cache_key_not_null extends Migration {
|
||||
override name = 'Migration20200101040200_trek_photo_cache_meta_cache_key_not_null';
|
||||
|
||||
override up(): void {
|
||||
this.addSql(`
|
||||
CREATE TABLE trek_photo_cache_meta_new (
|
||||
cache_key TEXT PRIMARY KEY NOT NULL,
|
||||
content_type TEXT NOT NULL DEFAULT 'image/jpeg',
|
||||
fetched_at INTEGER NOT NULL
|
||||
)
|
||||
`);
|
||||
|
||||
this.addSql(`
|
||||
INSERT INTO trek_photo_cache_meta_new
|
||||
SELECT cache_key, content_type, fetched_at
|
||||
FROM trek_photo_cache_meta
|
||||
WHERE cache_key IS NOT NULL
|
||||
`);
|
||||
|
||||
this.addSql(`DROP TABLE trek_photo_cache_meta`);
|
||||
|
||||
this.addSql(`ALTER TABLE trek_photo_cache_meta_new RENAME TO trek_photo_cache_meta`);
|
||||
|
||||
this.addSql(`CREATE INDEX IF NOT EXISTS idx_trek_photo_cache_meta_fetched_at ON trek_photo_cache_meta (fetched_at)`);
|
||||
}
|
||||
}
|
||||
@@ -26,9 +26,11 @@ export class TrekPhotoCacheMetaRepository extends TrekRepository<TrekPhotoCacheM
|
||||
/**
|
||||
* TC4 (`put`) — `INSERT OR REPLACE INTO trek_photo_cache_meta (cache_key,
|
||||
* content_type, fetched_at) VALUES (?, ?, ?)`. `cache_key` is the table's
|
||||
* only unique/primary column (a genuine nullable TEXT PK per R5's Task 0
|
||||
* finding — not a bug, and not touched here), so it is the sole conflict
|
||||
* target, same upsert shape as `AppSettingsRepository.setValue`.
|
||||
* only unique/primary column (`NOT NULL` since Plan 4 Task 8a's
|
||||
* cache_key-tightening migration — R5's Task 0 finding was that every
|
||||
* writer already bound a non-null value, so this was a schema tightening,
|
||||
* not a behaviour change), so it is the sole conflict target, same upsert
|
||||
* shape as `AppSettingsRepository.setValue`.
|
||||
*/
|
||||
async upsertMeta(cache_key: string, content_type: string, fetched_at: number): Promise<void> {
|
||||
await this.upsert({ cache_key, content_type, fetched_at }, { onConflictFields: ['cache_key'], onConflictAction: 'merge' });
|
||||
|
||||
Reference in New Issue
Block a user