fix(db): make migrations and glossary index SQLite-compatible
All checks were successful
Deploy to Production / Build and Deploy (push) Successful in 2m57s
All checks were successful
Deploy to Production / Build and Deploy (push) Successful in 2m57s
This commit is contained in:
@@ -21,11 +21,14 @@ depends_on: Union[str, Sequence[str], None] = None
|
|||||||
def upgrade() -> None:
|
def upgrade() -> None:
|
||||||
op.add_column("users", sa.Column("reset_token", sa.String(length=255), nullable=True))
|
op.add_column("users", sa.Column("reset_token", sa.String(length=255), nullable=True))
|
||||||
op.add_column("users", sa.Column("reset_token_expires", sa.DateTime(), nullable=True))
|
op.add_column("users", sa.Column("reset_token_expires", sa.DateTime(), nullable=True))
|
||||||
# Make legacy password_hash column nullable (ORM uses hashed_password now)
|
# Make legacy password_hash column nullable (ORM uses hashed_password now).
|
||||||
op.alter_column("users", "password_hash", nullable=True)
|
# Use batch_alter_table for SQLite compatibility.
|
||||||
|
with op.batch_alter_table("users") as batch_op:
|
||||||
|
batch_op.alter_column("password_hash", nullable=True)
|
||||||
|
|
||||||
|
|
||||||
def downgrade() -> None:
|
def downgrade() -> None:
|
||||||
op.drop_column("users", "reset_token_expires")
|
with op.batch_alter_table("users") as batch_op:
|
||||||
op.drop_column("users", "reset_token")
|
batch_op.drop_column("reset_token_expires")
|
||||||
op.alter_column("users", "password_hash", nullable=False)
|
batch_op.drop_column("reset_token")
|
||||||
|
batch_op.alter_column("password_hash", nullable=False)
|
||||||
|
|||||||
@@ -12,20 +12,38 @@ depends_on = None
|
|||||||
|
|
||||||
|
|
||||||
def upgrade() -> None:
|
def upgrade() -> None:
|
||||||
op.execute(
|
conn = op.get_bind()
|
||||||
"ALTER TABLE users DROP CONSTRAINT IF EXISTS ck_users_tier"
|
dialect = conn.dialect.name
|
||||||
)
|
|
||||||
op.execute(
|
if dialect == "sqlite":
|
||||||
"ALTER TABLE users ADD CONSTRAINT ck_users_tier "
|
# Migration 002 skipped the CHECK constraint on SQLite; just create it now.
|
||||||
"CHECK (tier IN ('free', 'starter', 'pro', 'business', 'enterprise'))"
|
with op.batch_alter_table("users") as batch_op:
|
||||||
)
|
batch_op.create_check_constraint(
|
||||||
|
"ck_users_tier",
|
||||||
|
"tier IN ('free', 'starter', 'pro', 'business', 'enterprise')",
|
||||||
|
)
|
||||||
|
else:
|
||||||
|
op.execute("ALTER TABLE users DROP CONSTRAINT IF EXISTS ck_users_tier")
|
||||||
|
op.execute(
|
||||||
|
"ALTER TABLE users ADD CONSTRAINT ck_users_tier "
|
||||||
|
"CHECK (tier IN ('free', 'starter', 'pro', 'business', 'enterprise'))"
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
def downgrade() -> None:
|
def downgrade() -> None:
|
||||||
op.execute(
|
conn = op.get_bind()
|
||||||
"ALTER TABLE users DROP CONSTRAINT IF EXISTS ck_users_tier"
|
dialect = conn.dialect.name
|
||||||
)
|
|
||||||
op.execute(
|
if dialect == "sqlite":
|
||||||
"ALTER TABLE users ADD CONSTRAINT ck_users_tier "
|
with op.batch_alter_table("users") as batch_op:
|
||||||
"CHECK (tier IN ('free', 'pro'))"
|
batch_op.drop_constraint("ck_users_tier", type_="check")
|
||||||
)
|
batch_op.create_check_constraint(
|
||||||
|
"ck_users_tier",
|
||||||
|
"tier IN ('free', 'pro')",
|
||||||
|
)
|
||||||
|
else:
|
||||||
|
op.execute("ALTER TABLE users DROP CONSTRAINT IF EXISTS ck_users_tier")
|
||||||
|
op.execute(
|
||||||
|
"ALTER TABLE users ADD CONSTRAINT ck_users_tier "
|
||||||
|
"CHECK (tier IN ('free', 'pro'))"
|
||||||
|
)
|
||||||
|
|||||||
@@ -26,23 +26,44 @@ depends_on: Union[str, Sequence[str], None] = None
|
|||||||
|
|
||||||
|
|
||||||
def upgrade() -> None:
|
def upgrade() -> None:
|
||||||
|
conn = op.get_bind()
|
||||||
|
dialect = conn.dialect.name
|
||||||
|
|
||||||
# Glossaries with terms containing 5+ translation keys are multilingual templates
|
# Glossaries with terms containing 5+ translation keys are multilingual templates
|
||||||
# (enriched glossaries have 11 translations: de, es, it, pt, nl, ru, ja, ko, zh, ar, fa)
|
# (enriched glossaries have 11 translations: de, es, it, pt, nl, ru, ja, ko, zh, ar, fa)
|
||||||
op.execute("""
|
if dialect == "sqlite":
|
||||||
UPDATE glossaries
|
# SQLite does not have jsonb_object_keys. Use json_each for compatibility.
|
||||||
SET target_language = 'multi'
|
op.execute("""
|
||||||
WHERE id IN (
|
UPDATE glossaries
|
||||||
SELECT DISTINCT g.id
|
SET target_language = 'multi'
|
||||||
FROM glossaries g
|
WHERE id IN (
|
||||||
JOIN glossary_terms gt ON gt.glossary_id = g.id
|
SELECT DISTINCT g.id
|
||||||
WHERE gt.translations IS NOT NULL
|
FROM glossaries g
|
||||||
AND jsonb_typeof(gt.translations) = 'object'
|
JOIN glossary_terms gt ON gt.glossary_id = g.id
|
||||||
AND (
|
WHERE gt.translations IS NOT NULL
|
||||||
SELECT count(*)
|
AND json_type(gt.translations, '$') = 'object'
|
||||||
FROM jsonb_object_keys(gt.translations)
|
AND (
|
||||||
) >= 5
|
SELECT count(*)
|
||||||
)
|
FROM json_each(gt.translations)
|
||||||
""")
|
) >= 5
|
||||||
|
)
|
||||||
|
""")
|
||||||
|
else:
|
||||||
|
op.execute("""
|
||||||
|
UPDATE glossaries
|
||||||
|
SET target_language = 'multi'
|
||||||
|
WHERE id IN (
|
||||||
|
SELECT DISTINCT g.id
|
||||||
|
FROM glossaries g
|
||||||
|
JOIN glossary_terms gt ON gt.glossary_id = g.id
|
||||||
|
WHERE gt.translations IS NOT NULL
|
||||||
|
AND jsonb_typeof(gt.translations) = 'object'
|
||||||
|
AND (
|
||||||
|
SELECT count(*)
|
||||||
|
FROM jsonb_object_keys(gt.translations)
|
||||||
|
) >= 5
|
||||||
|
)
|
||||||
|
""")
|
||||||
|
|
||||||
# Also rename glossaries: replace "Anglais" with "Multilingue" in the name
|
# Also rename glossaries: replace "Anglais" with "Multilingue" in the name
|
||||||
op.execute("""
|
op.execute("""
|
||||||
|
|||||||
@@ -22,6 +22,14 @@ depends_on: Union[str, Sequence[str], None] = None
|
|||||||
|
|
||||||
|
|
||||||
def upgrade() -> None:
|
def upgrade() -> None:
|
||||||
|
conn = op.get_bind()
|
||||||
|
dialect = conn.dialect.name
|
||||||
|
|
||||||
|
if dialect == "sqlite":
|
||||||
|
# SQLite test databases are created fresh and have no legacy glossaries to clean up.
|
||||||
|
# The PostgreSQL-specific jsonb_object_keys logic is not needed here.
|
||||||
|
return
|
||||||
|
|
||||||
# 1. Delete old FR→EN glossaries that don't have multilingual translations
|
# 1. Delete old FR→EN glossaries that don't have multilingual translations
|
||||||
# (imported before enrichment, they are stale duplicates)
|
# (imported before enrichment, they are stale duplicates)
|
||||||
op.execute("""
|
op.execute("""
|
||||||
|
|||||||
@@ -21,10 +21,18 @@ depends_on: Union[str, Sequence[str], None] = None
|
|||||||
|
|
||||||
|
|
||||||
def upgrade() -> None:
|
def upgrade() -> None:
|
||||||
|
conn = op.get_bind()
|
||||||
|
dialect = conn.dialect.name
|
||||||
|
|
||||||
# Step 1: Delete ALL glossaries with target_language='en' (old stale imports)
|
# Step 1: Delete ALL glossaries with target_language='en' (old stale imports)
|
||||||
op.execute("DELETE FROM glossary_terms WHERE glossary_id IN (SELECT id FROM glossaries WHERE target_language = 'en')")
|
op.execute("DELETE FROM glossary_terms WHERE glossary_id IN (SELECT id FROM glossaries WHERE target_language = 'en')")
|
||||||
op.execute("DELETE FROM glossaries WHERE target_language = 'en'")
|
op.execute("DELETE FROM glossaries WHERE target_language = 'en'")
|
||||||
|
|
||||||
|
if dialect == "sqlite":
|
||||||
|
# SQLite test databases are created fresh and have no duplicate multilingual glossaries.
|
||||||
|
# The PostgreSQL-specific DISTINCT ON logic is not needed here.
|
||||||
|
return
|
||||||
|
|
||||||
# Step 2: Deduplicate multilingual glossaries — keep only the newest per name
|
# Step 2: Deduplicate multilingual glossaries — keep only the newest per name
|
||||||
# Delete terms for duplicates first, then the duplicates themselves
|
# Delete terms for duplicates first, then the duplicates themselves
|
||||||
op.execute("""
|
op.execute("""
|
||||||
|
|||||||
@@ -334,7 +334,7 @@ class Glossary(Base):
|
|||||||
name = Column(String(255), nullable=False)
|
name = Column(String(255), nullable=False)
|
||||||
source_language = Column(String(10), nullable=False, default="fr")
|
source_language = Column(String(10), nullable=False, default="fr")
|
||||||
target_language = Column(String(10), nullable=True, default="en")
|
target_language = Column(String(10), nullable=True, default="en")
|
||||||
template_id = Column(String(50), nullable=True, index=True)
|
template_id = Column(String(50), nullable=True)
|
||||||
created_at = Column(DateTime, default=_utcnow)
|
created_at = Column(DateTime, default=_utcnow)
|
||||||
updated_at = Column(DateTime, default=_utcnow, onupdate=_utcnow)
|
updated_at = Column(DateTime, default=_utcnow, onupdate=_utcnow)
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user