Skip to content

Commit 39fbc79

Browse files
committed
review-stack 1/4: code (36 files, +2980/-3977)
Review-and-land stack for synap5e/feat/asset-record-content-split, generated by review-stack.py. Once approved, merges DOWN into the layer below (a fast-forward); only the bottom layer squash-merges into the real base. See ~/adocs/review-stack.md. Rule: path not under tests-unit/ or tests/ Question: Is the logic change right? Source tip: f65c9e5 Merge-base: 783545f
1 parent 783545f commit 39fbc79

36 files changed

Lines changed: 2980 additions & 3977 deletions

alembic_db/versions/0001_assets.py

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -150,7 +150,6 @@ def downgrade() -> None:
150150

151151
op.drop_index("ix_asset_cache_state_asset_id", table_name="asset_cache_state")
152152
op.drop_index("ix_asset_cache_state_file_path", table_name="asset_cache_state")
153-
op.drop_constraint("uq_asset_cache_state_file_path", table_name="asset_cache_state")
154153
op.drop_table("asset_cache_state")
155154

156155
op.drop_index("ix_asset_info_tags_asset_info_id", table_name="asset_info_tags")
@@ -160,7 +159,6 @@ def downgrade() -> None:
160159
op.drop_index("ix_tags_tag_type", table_name="tags")
161160
op.drop_table("tags")
162161

163-
op.drop_constraint("uq_assets_info_asset_owner_name", table_name="assets_info")
164162
op.drop_index("ix_assets_info_owner_name", table_name="assets_info")
165163
op.drop_index("ix_assets_info_last_access_time", table_name="assets_info")
166164
op.drop_index("ix_assets_info_created_at", table_name="assets_info")

alembic_db/versions/0002_merge_to_asset_references.py

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -177,7 +177,10 @@ def downgrade() -> None:
177177
178178
NOTE: Data is not recoverable. The upgrade discards all rows from the old
179179
tables and truncates assets. After downgrade the old schema will be empty.
180-
A filesystem rescan will repopulate data once the older code is running.
180+
A filesystem rescan can repopulate only data derived from paths. It will
181+
not restore user_metadata, manually-applied tags, preview_id nominations,
182+
name renames, or job_id, because build_asset_specs derives names and tags
183+
from the path alone.
181184
"""
182185
# Drop new tables (order matters due to FK constraints)
183186
op.drop_index("ix_asset_reference_meta_key_val_bool", table_name="asset_reference_meta")
Lines changed: 125 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,125 @@
1+
"""
2+
Record/content split.
3+
4+
This migration intentionally discards the existing asset database. The
5+
asset_reference_meta, asset_reference_tags, asset_references, and assets
6+
tables are dropped, and DELETE FROM tags removes all existing tag rows. No
7+
data migration is performed.
8+
9+
A filesystem rescan after this migration will not restore user_metadata,
10+
manually-applied tags, preview_id nominations, name renames, or job_id.
11+
build_asset_specs derives names and tags from the path alone.
12+
13+
Revision ID: 0007_record_content_split
14+
Revises: 0006_add_loader_path
15+
Create Date: 2026-08-26
16+
"""
17+
18+
from alembic import op
19+
import sqlalchemy as sa
20+
21+
22+
revision = "0007_record_content_split"
23+
down_revision = "0006_add_loader_path"
24+
branch_labels = None
25+
depends_on = None
26+
27+
28+
def upgrade() -> None:
29+
op.drop_table("asset_reference_meta")
30+
op.drop_table("asset_reference_tags")
31+
op.drop_table("asset_references")
32+
op.drop_table("assets")
33+
op.execute("DELETE FROM tags")
34+
op.create_table(
35+
"asset_contents",
36+
sa.Column("id", sa.String(36), primary_key=True),
37+
sa.Column("hash", sa.String(256)),
38+
sa.Column("size_bytes", sa.BigInteger(), nullable=False),
39+
sa.Column("path", sa.Text(), nullable=False),
40+
sa.Column("mtime_ns", sa.BigInteger()),
41+
sa.Column("is_missing", sa.Boolean(), nullable=False, server_default="0"),
42+
sa.Column("created_at", sa.DateTime(), nullable=False),
43+
sa.CheckConstraint("size_bytes >= 0", name="ck_asset_contents_size_nonneg"),
44+
sa.CheckConstraint("mtime_ns >= 0", name="ck_asset_contents_mtime_nonneg"),
45+
)
46+
op.create_index("ix_asset_contents_hash", "asset_contents", ["hash"])
47+
op.create_index(
48+
"uq_asset_contents_path_live", "asset_contents", ["path"], unique=True,
49+
sqlite_where=sa.text("is_missing = 0"),
50+
)
51+
op.create_table(
52+
"assets",
53+
sa.Column("id", sa.String(36), primary_key=True),
54+
sa.Column("content_id", sa.String(36), sa.ForeignKey("asset_contents.id", ondelete="RESTRICT"), nullable=False),
55+
sa.Column("name", sa.String(512), nullable=False),
56+
sa.Column("mime_type", sa.String(255)),
57+
sa.Column("system_metadata", sa.JSON()),
58+
sa.Column("job_id", sa.String(36)),
59+
sa.Column("user_metadata", sa.JSON()),
60+
sa.Column("loader_path", sa.Text()),
61+
sa.Column("preview_id", sa.String(36), sa.ForeignKey("assets.id", ondelete="SET NULL")),
62+
sa.Column("created_at", sa.DateTime(), nullable=False),
63+
sa.Column("updated_at", sa.DateTime(), nullable=False),
64+
sa.Column("last_access_time", sa.DateTime()),
65+
)
66+
op.create_index("ix_assets_content_id", "assets", ["content_id"])
67+
op.create_index("ix_assets_name", "assets", ["name"])
68+
op.create_index("ix_assets_created_at", "assets", ["created_at"])
69+
op.create_index("ix_assets_preview_id", "assets", ["preview_id"])
70+
op.create_table(
71+
"asset_meta",
72+
sa.Column("asset_id", sa.String(36), sa.ForeignKey("assets.id", ondelete="CASCADE"), primary_key=True),
73+
sa.Column("key", sa.String(256), primary_key=True),
74+
sa.Column("ordinal", sa.Integer(), primary_key=True),
75+
sa.Column("val_str", sa.String(2048)), sa.Column("val_num", sa.Numeric(38, 10)),
76+
sa.Column("val_bool", sa.Boolean()), sa.Column("val_json", sa.JSON()),
77+
sa.CheckConstraint("val_str IS NOT NULL OR val_num IS NOT NULL OR val_bool IS NOT NULL OR val_json IS NOT NULL", name="ck_asset_meta_has_value"),
78+
)
79+
op.create_index("ix_asset_meta_key", "asset_meta", ["key"])
80+
op.create_index("ix_asset_meta_key_val_str", "asset_meta", ["key", "val_str"])
81+
op.create_index("ix_asset_meta_key_val_num", "asset_meta", ["key", "val_num"])
82+
op.create_index("ix_asset_meta_key_val_bool", "asset_meta", ["key", "val_bool"])
83+
op.create_table("asset_tags", sa.Column("asset_id", sa.String(36), sa.ForeignKey("assets.id", ondelete="CASCADE"), primary_key=True), sa.Column("tag_name", sa.String(512), sa.ForeignKey("tags.name", ondelete="RESTRICT"), primary_key=True), sa.Column("origin", sa.String(32), nullable=False), sa.Column("added_at", sa.DateTime(), nullable=False))
84+
op.create_index("ix_asset_tags_tag_name", "asset_tags", ["tag_name"])
85+
op.create_index("ix_asset_tags_asset_id", "asset_tags", ["asset_id"])
86+
op.create_table("asset_system_state", sa.Column("key", sa.String(256), primary_key=True), sa.Column("value", sa.Text(), nullable=False))
87+
88+
89+
def downgrade() -> None:
90+
op.drop_table("asset_system_state")
91+
op.drop_table("asset_tags")
92+
op.drop_table("asset_meta")
93+
op.drop_table("assets")
94+
op.drop_table("asset_contents")
95+
op.create_table(
96+
"assets",
97+
sa.Column("id", sa.String(length=36), primary_key=True),
98+
sa.Column("hash", sa.String(length=256), nullable=True),
99+
sa.Column("size_bytes", sa.BigInteger(), nullable=False, server_default="0"),
100+
sa.Column("mime_type", sa.String(length=255), nullable=True),
101+
sa.Column("created_at", sa.DateTime(timezone=False), nullable=False),
102+
sa.CheckConstraint("size_bytes >= 0", name="ck_assets_size_nonneg"),
103+
)
104+
op.create_index("uq_assets_hash", "assets", ["hash"], unique=True)
105+
op.create_index("ix_assets_mime_type", "assets", ["mime_type"])
106+
op.create_table("asset_references", sa.Column("id", sa.String(36), primary_key=True), sa.Column("asset_id", sa.String(36), sa.ForeignKey("assets.id", ondelete="CASCADE"), nullable=False), sa.Column("file_path", sa.Text()), sa.Column("loader_path", sa.Text()), sa.Column("mtime_ns", sa.BigInteger()), sa.Column("needs_verify", sa.Boolean(), nullable=False), sa.Column("is_missing", sa.Boolean(), nullable=False), sa.Column("enrichment_level", sa.Integer(), nullable=False), sa.Column("owner_id", sa.String(128), nullable=False), sa.Column("name", sa.String(512), nullable=False), sa.Column("preview_id", sa.String(36), sa.ForeignKey("asset_references.id", ondelete="SET NULL")), sa.Column("user_metadata", sa.JSON()), sa.Column("system_metadata", sa.JSON()), sa.Column("job_id", sa.String(36)), sa.Column("created_at", sa.DateTime(), nullable=False), sa.Column("updated_at", sa.DateTime(), nullable=False), sa.Column("last_access_time", sa.DateTime(), nullable=False), sa.Column("deleted_at", sa.DateTime()))
107+
op.create_index("uq_asset_references_file_path", "asset_references", ["file_path"], unique=True)
108+
op.create_index("ix_asset_references_asset_id", "asset_references", ["asset_id"])
109+
op.create_index("ix_asset_references_owner_id", "asset_references", ["owner_id"])
110+
op.create_index("ix_asset_references_name", "asset_references", ["name"])
111+
op.create_index("ix_asset_references_is_missing", "asset_references", ["is_missing"])
112+
op.create_index("ix_asset_references_enrichment_level", "asset_references", ["enrichment_level"])
113+
op.create_index("ix_asset_references_created_at", "asset_references", ["created_at"])
114+
op.create_index("ix_asset_references_last_access_time", "asset_references", ["last_access_time"])
115+
op.create_index("ix_asset_references_owner_name", "asset_references", ["owner_id", "name"])
116+
op.create_index("ix_asset_references_deleted_at", "asset_references", ["deleted_at"])
117+
op.create_index("ix_asset_references_preview_id", "asset_references", ["preview_id"])
118+
op.create_table("asset_reference_meta", sa.Column("asset_reference_id", sa.String(36), sa.ForeignKey("asset_references.id", ondelete="CASCADE"), primary_key=True), sa.Column("key", sa.String(256), primary_key=True), sa.Column("ordinal", sa.Integer(), primary_key=True), sa.Column("val_str", sa.String(2048)), sa.Column("val_num", sa.Numeric(38, 10)), sa.Column("val_bool", sa.Boolean()), sa.Column("val_json", sa.JSON()), sa.CheckConstraint("val_str IS NOT NULL OR val_num IS NOT NULL OR val_bool IS NOT NULL OR val_json IS NOT NULL", name="ck_asset_reference_meta_has_value"))
119+
op.create_index("ix_asset_reference_meta_key", "asset_reference_meta", ["key"])
120+
op.create_index("ix_asset_reference_meta_key_val_str", "asset_reference_meta", ["key", "val_str"])
121+
op.create_index("ix_asset_reference_meta_key_val_num", "asset_reference_meta", ["key", "val_num"])
122+
op.create_index("ix_asset_reference_meta_key_val_bool", "asset_reference_meta", ["key", "val_bool"])
123+
op.create_table("asset_reference_tags", sa.Column("asset_reference_id", sa.String(36), sa.ForeignKey("asset_references.id", ondelete="CASCADE"), primary_key=True), sa.Column("tag_name", sa.String(512), sa.ForeignKey("tags.name", ondelete="RESTRICT"), primary_key=True), sa.Column("origin", sa.String(32), nullable=False), sa.Column("added_at", sa.DateTime(), nullable=False))
124+
op.create_index("ix_asset_reference_tags_tag_name", "asset_reference_tags", ["tag_name"])
125+
op.create_index("ix_asset_reference_tags_asset_reference_id", "asset_reference_tags", ["asset_reference_id"])

0 commit comments

Comments
 (0)