fix: Add Alembic migration 0104 for missing embedding + audit_log columns
Two critical bugs found by API integration tests: 1. contacts.embedding (vector(768)) — ORM model updated in Phase 5.3 but plugin migration 0002_embeddings.sql was never run as Alembic migration. Also adds embedding columns to mails, companies, files, calendar_entries, tags. 2. audit_log.created_at, updated_at, deleted_at — AuditLog inherits TenantMixin which expects these columns, but they were never added to the DB table. Also adds to deletion_log. Migration uses IF NOT EXISTS checks for all columns/indexes. Alembic head: 0103 → 0104
This commit is contained in:
@@ -0,0 +1,99 @@
|
|||||||
|
"""Add embedding column to contacts + timestamp columns to audit_log.
|
||||||
|
|
||||||
|
Fixes two issues found by API integration tests:
|
||||||
|
1. contacts.embedding (vector(768)) — ORM model was updated in Phase 5.3 but
|
||||||
|
the plugin migration 0002_embeddings.sql was never run as an Alembic migration.
|
||||||
|
2. audit_log.created_at, updated_at, deleted_at — AuditLog model inherits TenantMixin
|
||||||
|
which expects these columns, but they were never added to the DB table.
|
||||||
|
|
||||||
|
Revision ID: 0104
|
||||||
|
"""
|
||||||
|
|
||||||
|
from alembic import op
|
||||||
|
import sqlalchemy as sa
|
||||||
|
|
||||||
|
revision = "0104"
|
||||||
|
down_revision = "0103"
|
||||||
|
|
||||||
|
|
||||||
|
def upgrade() -> None:
|
||||||
|
conn = op.get_bind()
|
||||||
|
|
||||||
|
# 1. Add embedding column to contacts (if not exists)
|
||||||
|
result = conn.execute(sa.text(
|
||||||
|
"SELECT column_name FROM information_schema.columns "
|
||||||
|
"WHERE table_name = 'contacts' AND column_name = 'embedding'"
|
||||||
|
))
|
||||||
|
if result.fetchone() is None:
|
||||||
|
op.execute("ALTER TABLE contacts ADD COLUMN embedding vector(768)")
|
||||||
|
op.execute(
|
||||||
|
"CREATE INDEX IF NOT EXISTS ix_contacts_embedding "
|
||||||
|
"ON contacts USING hnsw(embedding vector_cosine_ops)"
|
||||||
|
)
|
||||||
|
|
||||||
|
# 2. Add embedding columns to other tables (from plugin migration 0002)
|
||||||
|
for table in ["mails", "companies", "files", "calendar_entries"]:
|
||||||
|
result = conn.execute(sa.text(
|
||||||
|
f"SELECT column_name FROM information_schema.columns "
|
||||||
|
f"WHERE table_name = '{table}' AND column_name = 'embedding'"
|
||||||
|
))
|
||||||
|
if result.fetchone() is None:
|
||||||
|
op.execute(f"ALTER TABLE {table} ADD COLUMN embedding vector(768)")
|
||||||
|
op.execute(
|
||||||
|
f"CREATE INDEX IF NOT EXISTS ix_{table}_embedding "
|
||||||
|
f"ON {table} USING hnsw(embedding vector_cosine_ops)"
|
||||||
|
)
|
||||||
|
|
||||||
|
# Tags use 384-dim embeddings
|
||||||
|
result = conn.execute(sa.text(
|
||||||
|
"SELECT column_name FROM information_schema.columns "
|
||||||
|
"WHERE table_name = 'tags' AND column_name = 'embedding'"
|
||||||
|
))
|
||||||
|
if result.fetchone() is None:
|
||||||
|
op.execute("ALTER TABLE tags ADD COLUMN embedding vector(384)")
|
||||||
|
op.execute(
|
||||||
|
"CREATE INDEX IF NOT EXISTS ix_tags_embedding "
|
||||||
|
"ON tags USING hnsw(embedding vector_cosine_ops)"
|
||||||
|
)
|
||||||
|
|
||||||
|
# 3. Add timestamp columns to audit_log (if not exists)
|
||||||
|
for col in ["created_at", "updated_at", "deleted_at"]:
|
||||||
|
result = conn.execute(sa.text(
|
||||||
|
f"SELECT column_name FROM information_schema.columns "
|
||||||
|
f"WHERE table_name = 'audit_log' AND column_name = '{col}'"
|
||||||
|
))
|
||||||
|
if result.fetchone() is None:
|
||||||
|
op.execute(
|
||||||
|
f"ALTER TABLE audit_log ADD COLUMN {col} "
|
||||||
|
f"TIMESTAMPTZ DEFAULT NOW()"
|
||||||
|
)
|
||||||
|
|
||||||
|
# 4. Add timestamp columns to deletion_log (if not exists)
|
||||||
|
for col in ["created_at", "updated_at", "deleted_at"]:
|
||||||
|
result = conn.execute(sa.text(
|
||||||
|
f"SELECT column_name FROM information_schema.columns "
|
||||||
|
f"WHERE table_name = 'deletion_log' AND column_name = '{col}'"
|
||||||
|
))
|
||||||
|
if result.fetchone() is None:
|
||||||
|
op.execute(
|
||||||
|
f"ALTER TABLE deletion_log ADD COLUMN {col} "
|
||||||
|
f"TIMESTAMPTZ DEFAULT NOW()"
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def downgrade() -> None:
|
||||||
|
# Drop embedding columns
|
||||||
|
for table in ["contacts", "mails", "companies", "files", "calendar_entries"]:
|
||||||
|
op.execute(f"DROP INDEX IF EXISTS ix_{table}_embedding")
|
||||||
|
op.execute(f"ALTER TABLE {table} DROP COLUMN IF EXISTS embedding")
|
||||||
|
|
||||||
|
op.execute("DROP INDEX IF EXISTS ix_tags_embedding")
|
||||||
|
op.execute("ALTER TABLE tags DROP COLUMN IF EXISTS embedding")
|
||||||
|
|
||||||
|
# Drop timestamp columns from audit_log
|
||||||
|
for col in ["created_at", "updated_at", "deleted_at"]:
|
||||||
|
op.execute(f"ALTER TABLE audit_log DROP COLUMN IF EXISTS {col}")
|
||||||
|
|
||||||
|
# Drop timestamp columns from deletion_log
|
||||||
|
for col in ["created_at", "updated_at", "deleted_at"]:
|
||||||
|
op.execute(f"ALTER TABLE deletion_log DROP COLUMN IF EXISTS {col}")
|
||||||
Reference in New Issue
Block a user