749 lines
26 KiB
Python
749 lines
26 KiB
Python
"""replace collection name with observer_observed
|
|
|
|
Revision ID: 08894082221a
|
|
Revises: 564ba40505c5
|
|
Create Date: 2025-10-03 16:48:13.270834
|
|
|
|
"""
|
|
|
|
from collections.abc import Sequence
|
|
|
|
import sqlalchemy as sa
|
|
from alembic import op
|
|
from nanoid import generate as generate_nanoid
|
|
from sqlalchemy import text
|
|
|
|
from migrations.utils import column_exists, constraint_exists, fk_exists, index_exists
|
|
from src.config import settings
|
|
|
|
# revision identifiers, used by Alembic.
|
|
revision: str = "08894082221a"
|
|
down_revision: str | None = "564ba40505c5"
|
|
branch_labels: str | Sequence[str] | None = None
|
|
depends_on: str | Sequence[str] | None = None
|
|
|
|
# Batch size for bulk operations
|
|
BATCH_SIZE = 10000
|
|
|
|
|
|
def upgrade() -> None:
|
|
"""Replace collections.name and documents.collection_name with observer and observed fields."""
|
|
schema = settings.DB.SCHEMA
|
|
connection = op.get_bind()
|
|
|
|
# SESSION_NAME MIGRATION
|
|
# Replace NULL session_name values with __global_observations__ and make column non-nullable
|
|
# This only applies to documents table
|
|
|
|
inspector = sa.inspect(connection)
|
|
|
|
# query documents table to get ALL workspace_names that have documents without a session_name
|
|
workspace_names = connection.execute(
|
|
text(
|
|
f"""
|
|
SELECT DISTINCT workspace_name FROM {schema}.documents WHERE session_name IS NULL
|
|
"""
|
|
)
|
|
).fetchall()
|
|
|
|
if workspace_names and column_exists("sessions", "name", inspector):
|
|
# Create __global_observations__ session for EACH workspace that needs it
|
|
for (workspace_name,) in workspace_names:
|
|
session_id = generate_nanoid()
|
|
connection.execute(
|
|
text(
|
|
f"""
|
|
INSERT INTO {schema}.sessions (id, name, workspace_name, is_active, metadata, internal_metadata, configuration, created_at) VALUES (:session_id, '__global_observations__', :workspace_name, true, '{{}}', '{{}}', '{{}}', NOW()) ON CONFLICT DO NOTHING
|
|
"""
|
|
),
|
|
{"session_id": session_id, "workspace_name": workspace_name},
|
|
)
|
|
# Update all documents with NULL session_name in batches
|
|
while True:
|
|
result = connection.execute(
|
|
text(
|
|
f"""
|
|
WITH batch AS (
|
|
SELECT id
|
|
FROM {schema}.documents
|
|
WHERE session_name IS NULL
|
|
LIMIT :batch_size
|
|
)
|
|
UPDATE {schema}.documents d
|
|
SET session_name = '__global_observations__'
|
|
FROM batch
|
|
WHERE d.id = batch.id
|
|
"""
|
|
),
|
|
{"batch_size": BATCH_SIZE},
|
|
)
|
|
if result.rowcount == 0:
|
|
break
|
|
|
|
op.alter_column("documents", "session_name", nullable=False, schema=schema)
|
|
|
|
# COLLECTIONS TABLE
|
|
# Step 1: Add new observer and observed columns to collections
|
|
if not column_exists("collections", "observer", inspector):
|
|
op.add_column(
|
|
"collections",
|
|
sa.Column("observer", sa.TEXT(), nullable=True),
|
|
schema=schema,
|
|
)
|
|
|
|
if not column_exists("collections", "observed", inspector):
|
|
op.add_column(
|
|
"collections",
|
|
sa.Column("observed", sa.TEXT(), nullable=True),
|
|
schema=schema,
|
|
)
|
|
|
|
# Step 2a: Identify collections that should be deleted (in memory)
|
|
# These are user-created collections that are not used in the system:
|
|
# - name = 'global_representation'
|
|
# - name starts with peer_name + "_" (pattern: observer_observed)
|
|
# - name ends with "_" + peer_name (pattern: observed_observer)
|
|
collections_to_delete = connection.execute(
|
|
text(
|
|
f"""
|
|
SELECT id, name, peer_name, workspace_name
|
|
FROM {schema}.collections
|
|
WHERE name != 'global_representation'
|
|
AND name NOT LIKE peer_name || '_%'
|
|
AND name NOT LIKE '%_' || peer_name
|
|
"""
|
|
)
|
|
).fetchall()
|
|
|
|
# Step 2b: Delete documents that reference collections marked for deletion (in batches)
|
|
if collections_to_delete:
|
|
collection_ids = [row.id for row in collections_to_delete]
|
|
|
|
# Delete in smaller chunks of actual documents to avoid locking issues
|
|
while True:
|
|
result = connection.execute(
|
|
text(
|
|
f"""
|
|
WITH to_delete AS (
|
|
SELECT d.id
|
|
FROM {schema}.documents d
|
|
JOIN {schema}.collections c
|
|
ON d.collection_name = c.name
|
|
AND d.peer_name = c.peer_name
|
|
AND d.workspace_name = c.workspace_name
|
|
WHERE c.id = ANY(:collection_ids)
|
|
LIMIT :batch_size
|
|
)
|
|
DELETE FROM {schema}.documents
|
|
WHERE id IN (SELECT id FROM to_delete)
|
|
"""
|
|
),
|
|
{"collection_ids": collection_ids, "batch_size": BATCH_SIZE},
|
|
)
|
|
|
|
if result.rowcount == 0:
|
|
break # No more rows to delete
|
|
|
|
# Step 2c: Delete the collections identified in step 2a (in batches)
|
|
if collections_to_delete:
|
|
for i in range(0, len(collections_to_delete), BATCH_SIZE):
|
|
batch = collections_to_delete[i : i + BATCH_SIZE]
|
|
collection_ids = [row.id for row in batch]
|
|
|
|
connection.execute(
|
|
text(
|
|
f"""
|
|
DELETE FROM {schema}.collections
|
|
WHERE id = ANY(:collection_ids)
|
|
"""
|
|
),
|
|
{"collection_ids": collection_ids},
|
|
)
|
|
|
|
# Step 2d: Populate collections observer and observed from existing name field in batches
|
|
# The logic is:
|
|
# - observer = peer_name (the exact peer ID)
|
|
# - If name is "global_representation", observed = peer_name (self-observation)
|
|
# - If name starts with peer_name + "_", extract the observed part (pattern: observer_observed)
|
|
# - If name ends with "_" + peer_name, extract the first part (pattern: observed_observer)
|
|
# - Any legacy edge cases will have been deleted in step 2a.
|
|
|
|
# Create temporary index to speed up batching
|
|
if not index_exists("collections", "idx_temp_collections_null_observer"):
|
|
op.create_index(
|
|
"idx_temp_collections_null_observer",
|
|
"collections",
|
|
["id"],
|
|
postgresql_where=text("observer IS NULL OR observed IS NULL"),
|
|
schema=schema,
|
|
)
|
|
|
|
while True:
|
|
result = connection.execute(
|
|
text(
|
|
f"""
|
|
WITH batch AS (
|
|
SELECT id
|
|
FROM {schema}.collections
|
|
WHERE observer IS NULL OR observed IS NULL
|
|
LIMIT :batch_size
|
|
)
|
|
UPDATE {schema}.collections c
|
|
SET
|
|
observer = c.peer_name,
|
|
observed = CASE
|
|
WHEN c.name = 'global_representation' THEN c.peer_name
|
|
WHEN c.name LIKE c.peer_name || '_%' THEN substring(c.name from length(c.peer_name) + 2)
|
|
WHEN c.name LIKE '%_' || c.peer_name THEN substring(c.name from 1 for length(c.name) - length(c.peer_name) - 1)
|
|
ELSE c.peer_name
|
|
END
|
|
FROM batch
|
|
WHERE c.id = batch.id
|
|
"""
|
|
),
|
|
{"batch_size": BATCH_SIZE},
|
|
)
|
|
if result.rowcount == 0:
|
|
break
|
|
|
|
# Drop temporary index after batching is complete
|
|
if index_exists("collections", "idx_temp_collections_null_observer", inspector):
|
|
op.drop_index(
|
|
"idx_temp_collections_null_observer", "collections", schema=schema
|
|
)
|
|
|
|
# Step 3: Make collections observer and observed NOT NULL
|
|
op.alter_column("collections", "observer", nullable=False, schema=schema)
|
|
op.alter_column("collections", "observed", nullable=False, schema=schema)
|
|
|
|
# DOCUMENTS TABLE
|
|
# Step 4: Add new observer and observed columns to documents
|
|
if not column_exists("documents", "observer", inspector):
|
|
op.add_column(
|
|
"documents",
|
|
sa.Column("observer", sa.TEXT(), nullable=True),
|
|
schema=schema,
|
|
)
|
|
|
|
if not column_exists("documents", "observed", inspector):
|
|
op.add_column(
|
|
"documents",
|
|
sa.Column("observed", sa.TEXT(), nullable=True),
|
|
schema=schema,
|
|
)
|
|
|
|
# Step 5: Populate documents observer and observed from collections table
|
|
# Join to the already-populated collections table to get authoritative values
|
|
# Process in batches to reduce query size
|
|
|
|
# Create temporary index to speed up batching
|
|
if not index_exists("documents", "idx_temp_docs_null_observer"):
|
|
op.create_index(
|
|
"idx_temp_docs_null_observer",
|
|
"documents",
|
|
["id"],
|
|
postgresql_where=text("observer IS NULL OR observed IS NULL"),
|
|
schema=schema,
|
|
)
|
|
|
|
while True:
|
|
result = connection.execute(
|
|
text(
|
|
f"""
|
|
WITH batch AS (
|
|
SELECT d.id
|
|
FROM {schema}.documents d
|
|
WHERE d.observer IS NULL OR d.observed IS NULL
|
|
LIMIT :batch_size
|
|
)
|
|
UPDATE {schema}.documents d
|
|
SET
|
|
observer = c.observer,
|
|
observed = c.observed
|
|
FROM {schema}.collections c, batch
|
|
WHERE d.id = batch.id
|
|
AND d.collection_name = c.name
|
|
AND d.peer_name = c.peer_name
|
|
AND d.workspace_name = c.workspace_name
|
|
"""
|
|
),
|
|
{"batch_size": BATCH_SIZE},
|
|
)
|
|
if result.rowcount == 0:
|
|
break
|
|
|
|
# Drop temporary index after batching is complete
|
|
if index_exists("documents", "idx_temp_docs_null_observer", inspector):
|
|
op.drop_index("idx_temp_docs_null_observer", "documents", schema=schema)
|
|
|
|
# Step 6: Make documents observer and observed NOT NULL
|
|
op.alter_column("documents", "observer", nullable=False, schema=schema)
|
|
op.alter_column("documents", "observed", nullable=False, schema=schema)
|
|
|
|
# CONSTRAINTS AND INDEXES
|
|
# Step 7: Drop ALL foreign key constraints from documents that might reference collections.peer_name
|
|
# Get all foreign keys on documents table
|
|
documents_fks = inspector.get_foreign_keys("documents", schema=schema)
|
|
for fk in documents_fks:
|
|
fk_name = fk.get("name")
|
|
# Drop any FK that references collections or peers and includes peer_name or collection_name
|
|
if fk_name and any(
|
|
pattern in fk_name
|
|
for pattern in [
|
|
"collection_name",
|
|
"peer_name",
|
|
]
|
|
):
|
|
op.drop_constraint(
|
|
fk_name,
|
|
"documents",
|
|
type_="foreignkey",
|
|
schema=schema,
|
|
)
|
|
|
|
# Step 7a: Drop ALL foreign key constraints from collections that reference peer_name
|
|
collections_fks = inspector.get_foreign_keys("collections", schema=schema)
|
|
for fk in collections_fks:
|
|
fk_name = fk.get("name")
|
|
if fk_name and "peer_name" in fk_name:
|
|
op.drop_constraint(
|
|
fk_name,
|
|
"collections",
|
|
type_="foreignkey",
|
|
schema=schema,
|
|
)
|
|
|
|
# Step 7c: Drop the peer_name column from collections (observer replaces it)
|
|
if column_exists("collections", "peer_name", inspector):
|
|
op.drop_column("collections", "peer_name", schema=schema)
|
|
|
|
# Step 7d: Drop the peer_name column from documents (observed replaces it)
|
|
if column_exists("documents", "peer_name", inspector):
|
|
op.drop_column("documents", "peer_name", schema=schema)
|
|
|
|
# Step 8: Drop the old unique constraint on collections that includes name
|
|
if constraint_exists(
|
|
"collections", "unique_name_collection_peer", "unique", inspector
|
|
):
|
|
op.drop_constraint(
|
|
"unique_name_collection_peer", "collections", type_="unique", schema=schema
|
|
)
|
|
|
|
# Step 9: Create new unique constraint on collections (without peer_name)
|
|
if not constraint_exists(
|
|
"collections", "unique_observer_observed_collection", "unique", inspector
|
|
):
|
|
op.create_unique_constraint(
|
|
"unique_observer_observed_collection",
|
|
"collections",
|
|
["observer", "observed", "workspace_name"],
|
|
schema=schema,
|
|
)
|
|
|
|
# Step 10: Add composite foreign key constraint for observer peer on collections
|
|
if not fk_exists(
|
|
"collections", "fk_collections_observer_workspace_name_peers", inspector
|
|
):
|
|
op.create_foreign_key(
|
|
"fk_collections_observer_workspace_name_peers",
|
|
"collections",
|
|
"peers",
|
|
["observer", "workspace_name"],
|
|
["name", "workspace_name"],
|
|
source_schema=schema,
|
|
referent_schema=schema,
|
|
)
|
|
|
|
# Step 11: Add composite foreign key constraint for observed peer on collections
|
|
if not fk_exists(
|
|
"collections", "fk_collections_observed_workspace_name_peers", inspector
|
|
):
|
|
op.create_foreign_key(
|
|
"fk_collections_observed_workspace_name_peers",
|
|
"collections",
|
|
"peers",
|
|
["observed", "workspace_name"],
|
|
["name", "workspace_name"],
|
|
source_schema=schema,
|
|
referent_schema=schema,
|
|
)
|
|
|
|
# Step 12: Add composite foreign key constraint from documents to collections using observer/observed
|
|
if not fk_exists(
|
|
"documents",
|
|
"fk_documents_observer_observed_workspace_name_collections",
|
|
inspector,
|
|
):
|
|
op.create_foreign_key(
|
|
"fk_documents_observer_observed_workspace_name_collections",
|
|
"documents",
|
|
"collections",
|
|
["observer", "observed", "workspace_name"],
|
|
["observer", "observed", "workspace_name"],
|
|
source_schema=schema,
|
|
referent_schema=schema,
|
|
)
|
|
|
|
# Step 13: Add composite foreign key constraint for observer peer on documents
|
|
if not fk_exists(
|
|
"documents", "fk_documents_observer_workspace_name_peers", inspector
|
|
):
|
|
op.create_foreign_key(
|
|
"fk_documents_observer_workspace_name_peers",
|
|
"documents",
|
|
"peers",
|
|
["observer", "workspace_name"],
|
|
["name", "workspace_name"],
|
|
source_schema=schema,
|
|
referent_schema=schema,
|
|
)
|
|
|
|
# Step 14: Add composite foreign key constraint for observed peer on documents
|
|
if not fk_exists(
|
|
"documents", "fk_documents_observed_workspace_name_peers", inspector
|
|
):
|
|
op.create_foreign_key(
|
|
"fk_documents_observed_workspace_name_peers",
|
|
"documents",
|
|
"peers",
|
|
["observed", "workspace_name"],
|
|
["name", "workspace_name"],
|
|
source_schema=schema,
|
|
referent_schema=schema,
|
|
)
|
|
|
|
# Step 15: Create indexes for observer and observed on collections
|
|
if not index_exists("collections", "idx_collections_observer", inspector):
|
|
op.create_index(
|
|
"idx_collections_observer",
|
|
"collections",
|
|
["observer"],
|
|
schema=schema,
|
|
)
|
|
|
|
if not index_exists("collections", "idx_collections_observed", inspector):
|
|
op.create_index(
|
|
"idx_collections_observed",
|
|
"collections",
|
|
["observed"],
|
|
schema=schema,
|
|
)
|
|
|
|
# Step 16: Create indexes for observer and observed on documents
|
|
if not index_exists("documents", "idx_documents_observer", inspector):
|
|
op.create_index(
|
|
"idx_documents_observer",
|
|
"documents",
|
|
["observer"],
|
|
schema=schema,
|
|
)
|
|
|
|
if not index_exists("documents", "idx_documents_observed", inspector):
|
|
op.create_index(
|
|
"idx_documents_observed",
|
|
"documents",
|
|
["observed"],
|
|
schema=schema,
|
|
)
|
|
|
|
# Step 17: Drop the name_length check constraint before dropping the name column from collections
|
|
if constraint_exists("collections", "name_length", "check", inspector):
|
|
op.drop_constraint("name_length", "collections", schema=schema)
|
|
|
|
if column_exists("collections", "name", inspector):
|
|
op.drop_column("collections", "name", schema=schema)
|
|
|
|
# Step 18: Drop the collection_name column from documents
|
|
if column_exists("documents", "collection_name", inspector):
|
|
op.drop_column("documents", "collection_name", schema=schema)
|
|
|
|
|
|
def downgrade() -> None:
|
|
"""Restore collections.name and documents.collection_name from observer and observed fields."""
|
|
schema = settings.DB.SCHEMA
|
|
inspector = sa.inspect(op.get_bind())
|
|
connection = op.get_bind()
|
|
|
|
# SESSION_NAME MIGRATION ROLLBACK
|
|
# Make session_name nullable again for documents table
|
|
|
|
# Revert documents table
|
|
if column_exists("documents", "session_name", inspector):
|
|
op.alter_column("documents", "session_name", nullable=True, schema=schema)
|
|
|
|
# COLLECTIONS TABLE
|
|
# Step 1: Add back the name column to collections
|
|
if not column_exists("collections", "name", inspector):
|
|
op.add_column(
|
|
"collections",
|
|
sa.Column("name", sa.TEXT(), nullable=True),
|
|
schema=schema,
|
|
)
|
|
|
|
# Step 2: Populate collections name from observer and observed
|
|
connection.execute(
|
|
text(
|
|
f"""
|
|
UPDATE {schema}.collections
|
|
SET name = CASE
|
|
WHEN observer = observed THEN 'global_representation'
|
|
ELSE observer || '_' || observed
|
|
END
|
|
WHERE name IS NULL
|
|
"""
|
|
)
|
|
)
|
|
|
|
# Step 3: Make collections name NOT NULL
|
|
op.alter_column("collections", "name", nullable=False, schema=schema)
|
|
|
|
# Step 3a: Restore peer_name column to collections (set to observer value)
|
|
if not column_exists("collections", "peer_name", inspector):
|
|
op.add_column(
|
|
"collections",
|
|
sa.Column("peer_name", sa.TEXT(), nullable=True),
|
|
schema=schema,
|
|
)
|
|
|
|
# Populate peer_name with observer value
|
|
connection.execute(
|
|
text(
|
|
f"""
|
|
UPDATE {schema}.collections
|
|
SET peer_name = observer
|
|
WHERE peer_name IS NULL
|
|
"""
|
|
)
|
|
)
|
|
|
|
# Make peer_name NOT NULL
|
|
op.alter_column("collections", "peer_name", nullable=False, schema=schema)
|
|
|
|
# Recreate the legacy foreign key constraint for peer_name
|
|
if not fk_exists("collections", "fk_collections_peer_name_peers", inspector):
|
|
op.create_foreign_key(
|
|
"fk_collections_peer_name_peers",
|
|
"collections",
|
|
"peers",
|
|
["peer_name", "workspace_name"],
|
|
["name", "workspace_name"],
|
|
source_schema=schema,
|
|
referent_schema=schema,
|
|
)
|
|
|
|
# DOCUMENTS TABLE
|
|
# Step 4: Add back the collection_name column to documents
|
|
if not column_exists("documents", "collection_name", inspector):
|
|
op.add_column(
|
|
"documents",
|
|
sa.Column("collection_name", sa.TEXT(), nullable=True),
|
|
schema=schema,
|
|
)
|
|
|
|
# Step 5: Populate documents collection_name from observer and observed in batches
|
|
while True:
|
|
result = connection.execute(
|
|
text(
|
|
f"""
|
|
WITH batch AS (
|
|
SELECT id
|
|
FROM {schema}.documents
|
|
WHERE collection_name IS NULL
|
|
LIMIT :batch_size
|
|
)
|
|
UPDATE {schema}.documents d
|
|
SET collection_name = CASE
|
|
WHEN d.observer = d.observed THEN 'global_representation'
|
|
ELSE d.observer || '_' || d.observed
|
|
END
|
|
FROM batch
|
|
WHERE d.id = batch.id
|
|
AND d.collection_name IS NULL
|
|
"""
|
|
),
|
|
{"batch_size": BATCH_SIZE},
|
|
)
|
|
if result.rowcount == 0:
|
|
break
|
|
|
|
# Step 6: Make documents collection_name NOT NULL
|
|
op.alter_column("documents", "collection_name", nullable=False, schema=schema)
|
|
|
|
# Step 6a: Restore peer_name column to documents (set to observer value)
|
|
if not column_exists("documents", "peer_name", inspector):
|
|
op.add_column(
|
|
"documents",
|
|
sa.Column("peer_name", sa.TEXT(), nullable=True),
|
|
schema=schema,
|
|
)
|
|
|
|
# Populate peer_name with observed value in batches
|
|
while True:
|
|
result = connection.execute(
|
|
text(
|
|
f"""
|
|
WITH batch AS (
|
|
SELECT id
|
|
FROM {schema}.documents
|
|
WHERE peer_name IS NULL
|
|
LIMIT :batch_size
|
|
)
|
|
UPDATE {schema}.documents d
|
|
SET peer_name = d.observed
|
|
FROM batch
|
|
WHERE d.id = batch.id
|
|
AND d.peer_name IS NULL
|
|
"""
|
|
),
|
|
{"batch_size": BATCH_SIZE},
|
|
)
|
|
if result.rowcount == 0:
|
|
break
|
|
|
|
# Make peer_name NOT NULL
|
|
op.alter_column("documents", "peer_name", nullable=False, schema=schema)
|
|
|
|
# Recreate the foreign key constraint for peer_name on documents
|
|
if not fk_exists(
|
|
"documents", "fk_documents_peer_name_workspace_name_peers", inspector
|
|
):
|
|
op.create_foreign_key(
|
|
"fk_documents_peer_name_workspace_name_peers",
|
|
"documents",
|
|
"peers",
|
|
["peer_name", "workspace_name"],
|
|
["name", "workspace_name"],
|
|
source_schema=schema,
|
|
referent_schema=schema,
|
|
)
|
|
|
|
# Step 7: Add check constraint for name length on collections
|
|
if not constraint_exists("collections", "name_length", "check", inspector):
|
|
op.create_check_constraint(
|
|
"name_length",
|
|
"collections",
|
|
"length(name) <= 1025",
|
|
schema=schema,
|
|
)
|
|
|
|
# CONSTRAINTS AND INDEXES
|
|
# Step 8: Drop new foreign key constraints from documents
|
|
if fk_exists(
|
|
"documents",
|
|
"fk_documents_observer_observed_workspace_name_collections",
|
|
inspector,
|
|
):
|
|
op.drop_constraint(
|
|
"fk_documents_observer_observed_workspace_name_collections",
|
|
"documents",
|
|
type_="foreignkey",
|
|
schema=schema,
|
|
)
|
|
|
|
if fk_exists("documents", "fk_documents_observer_workspace_name_peers", inspector):
|
|
op.drop_constraint(
|
|
"fk_documents_observer_workspace_name_peers",
|
|
"documents",
|
|
type_="foreignkey",
|
|
schema=schema,
|
|
)
|
|
|
|
if fk_exists("documents", "fk_documents_observed_workspace_name_peers", inspector):
|
|
op.drop_constraint(
|
|
"fk_documents_observed_workspace_name_peers",
|
|
"documents",
|
|
type_="foreignkey",
|
|
schema=schema,
|
|
)
|
|
|
|
# Step 9: Drop the new unique constraint on collections
|
|
if constraint_exists(
|
|
"collections", "unique_observer_observed_collection", "unique", inspector
|
|
):
|
|
op.drop_constraint(
|
|
"unique_observer_observed_collection",
|
|
"collections",
|
|
type_="unique",
|
|
schema=schema,
|
|
)
|
|
|
|
# Step 10: Recreate the old unique constraint on collections
|
|
if not constraint_exists(
|
|
"collections", "unique_name_collection_peer", "unique", inspector
|
|
):
|
|
op.create_unique_constraint(
|
|
"unique_name_collection_peer",
|
|
"collections",
|
|
["name", "peer_name", "workspace_name"],
|
|
schema=schema,
|
|
)
|
|
|
|
# Step 11: Recreate the legacy foreign key constraint from documents to collections
|
|
if not fk_exists(
|
|
"documents",
|
|
"fk_documents_collection_name_collections",
|
|
inspector,
|
|
):
|
|
op.create_foreign_key(
|
|
"fk_documents_collection_name_collections",
|
|
"documents",
|
|
"collections",
|
|
["collection_name", "peer_name", "workspace_name"],
|
|
["name", "peer_name", "workspace_name"],
|
|
source_schema=schema,
|
|
referent_schema=schema,
|
|
)
|
|
|
|
# Step 12: Drop foreign key constraints from collections
|
|
if fk_exists(
|
|
"collections", "fk_collections_observer_workspace_name_peers", inspector
|
|
):
|
|
op.drop_constraint(
|
|
"fk_collections_observer_workspace_name_peers",
|
|
"collections",
|
|
type_="foreignkey",
|
|
schema=schema,
|
|
)
|
|
|
|
if fk_exists(
|
|
"collections", "fk_collections_observed_workspace_name_peers", inspector
|
|
):
|
|
op.drop_constraint(
|
|
"fk_collections_observed_workspace_name_peers",
|
|
"collections",
|
|
type_="foreignkey",
|
|
schema=schema,
|
|
)
|
|
|
|
# Step 13: Drop indexes from collections
|
|
if index_exists("collections", "idx_collections_observer", inspector):
|
|
op.drop_index(
|
|
"idx_collections_observer", table_name="collections", schema=schema
|
|
)
|
|
|
|
if index_exists("collections", "idx_collections_observed", inspector):
|
|
op.drop_index(
|
|
"idx_collections_observed", table_name="collections", schema=schema
|
|
)
|
|
|
|
# Step 14: Drop indexes from documents
|
|
if index_exists("documents", "idx_documents_observer", inspector):
|
|
op.drop_index("idx_documents_observer", table_name="documents", schema=schema)
|
|
|
|
if index_exists("documents", "idx_documents_observed", inspector):
|
|
op.drop_index("idx_documents_observed", table_name="documents", schema=schema)
|
|
|
|
# Step 15: Drop observer and observed columns from collections
|
|
if column_exists("collections", "observer", inspector):
|
|
op.drop_column("collections", "observer", schema=schema)
|
|
|
|
if column_exists("collections", "observed", inspector):
|
|
op.drop_column("collections", "observed", schema=schema)
|
|
|
|
# Step 16: Drop observer and observed columns from documents
|
|
if column_exists("documents", "observer", inspector):
|
|
op.drop_column("documents", "observer", schema=schema)
|
|
|
|
if column_exists("documents", "observed", inspector):
|
|
op.drop_column("documents", "observed", schema=schema)
|