Skip to content
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
"""add missing document_metadata column to node and cre

Revision ID: b5ac48010165
Revises: c7d8e9f0a1b2
Create Date: 2026-07-24 22:19:01.724833

"""
from alembic import op
import sqlalchemy as sa
from sqlalchemy import inspect


# revision identifiers, used by Alembic.
revision = 'b5ac48010165'
down_revision = 'c7d8e9f0a1b2'
branch_labels = None
depends_on = None


def upgrade():
# Defensive: some environments (e.g. production) already have this column
# applied out-of-band without a corresponding migration ever being
# committed, so this must not assume a clean "column doesn't exist" state.
inspector = inspect(op.get_bind())
node_columns = {c["name"] for c in inspector.get_columns("node")}
cre_columns = {c["name"] for c in inspector.get_columns("cre")}

if "document_metadata" not in node_columns:
with op.batch_alter_table("node", schema=None) as batch_op:
batch_op.add_column(sa.Column("document_metadata", sa.JSON(), nullable=True))

if "document_metadata" not in cre_columns:
with op.batch_alter_table("cre", schema=None) as batch_op:
batch_op.add_column(sa.Column("document_metadata", sa.JSON(), nullable=True))
Comment on lines +24 to +34

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🗄️ Data Integrity & Integration | 🟠 Major | 🏗️ Heavy lift

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "== repo files matching migration/scripts/db =="
git ls-files | rg '(^migrations/versions/b5ac48010165_add_missing_document_metadata_column_to_|(^scripts/benchmark_import_parity\.py$|^application/database/db\.py$))' || true

echo
echo "== migration file =="
sed -n '1,140p' migrations/versions/b5ac48010165_add_missing_document_metadata_column_to_.py

echo
echo "== scripts/benchmark_import_parity.py relevant =="
sed -n '1,180p' scripts/benchmark_import_parity.py

echo
echo "== application/database/db.py relevant =="
sed -n '1,180p' application/database/db.py

echo
echo "== search metadata_json/document_metadata references =="
rg -n '"?metadata_json"?\b|"?document_metadata"?\b' scripts application migrations -S || true

Repository: OWASP/OpenCRE

Length of output: 17258


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "== search older migrations for document_metadata/metadata_json creation == =="
rg -n 'document_metadata|metadata_json|add_column.*metadata|create_table.*metadata' migrations scripts -S || true

echo
echo "== inspect alembic version/creates/migrations references =="
sed -n '1,140p' scripts/prod-docker-entrypoint.sh || true

echo
echo "== deterministic migration behavior probe =="
python3 - <<'PY'
def upgrade_columns_after_migration(existing_node, existing_cre):
    node_columns = set(existing_node)
    cre_columns = set(existing_cre)

    if "document_metadata" not in node_columns:
        node_columns = node_columns | {"document_metadata"}
    if "document_metadata" not in cre_columns:
        cre_columns = cre_columns | {"document_metadata"}

    return {"node": sorted(node_columns), "cre": sorted(cre_columns)}

cases = [
    (["metadata_json"], []),
    (["metadata_json"], ["metadata_json"]),
    (["document_metadata", "metadata_json"], ["document_metadata", "metadata_json"]),
]
for node, cre in cases:
    result = upgrade_columns_after_migration(node, cre)
    print({"existing_node": node, "existing_cre": cre}, "=>", result)
PY

echo
echo "== behavioral probe: data accessible from ORM-backed column if only legacy column is present? =="
python3 - <<'PY'
legacy_schema = {"document_metadata": None}
ORM_reads_column = legacy_schema.get("metadata_json")
print({"migration_result_schema": legacy_schema, "orm_attribute_value_from_document_metadata": ORM_reads_column})
PY

Repository: OWASP/OpenCRE

Length of output: 3951


Preserve legacy metadata_json values before setting document_metadata.

The ORM for Node and CRE reads metadata_json mapped to the SQL column document_metadata, and benchmark parity also supports schemas that have metadata_json instead. This migration only adds an empty document_metadata column when metadata_json exists, so existing metadata becomes inaccessible. Rename/copy metadata_json to document_metadata for both tables, and handle the case where both columns already exist.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@migrations/versions/b5ac48010165_add_missing_document_metadata_column_to_.py`
around lines 24 - 34, Update the migration’s Node and CRE column handling to
preserve existing metadata_json values: when only metadata_json exists, rename
it to document_metadata (or copy its values before removing it), and when both
columns exist, transfer metadata_json values into document_metadata without
overwriting valid document_metadata data. Keep the migration safe when
document_metadata already exists and apply the same logic to both tables.



def downgrade():
with op.batch_alter_table("cre", schema=None) as batch_op:
batch_op.drop_column("document_metadata")

with op.batch_alter_table("node", schema=None) as batch_op:
batch_op.drop_column("document_metadata")
Comment on lines +37 to +42

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🗄️ Data Integrity & Integration | 🔴 Critical | 🏗️ Heavy lift

Do not unconditionally drop columns this migration may not own.

Because upgrade() skips columns that already existed out-of-band, downgrade() cannot distinguish migration-created columns from pre-existing production columns. Rolling back can therefore delete existing metadata and break the ORM contract. Persist column ownership during upgrade and drop only columns created by this migration; otherwise make the downgrade intentionally non-destructive.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@migrations/versions/b5ac48010165_add_missing_document_metadata_column_to_.py`
around lines 37 - 42, Make the migration’s downgrade non-destructive for
pre-existing columns: update upgrade() to persist whether each document_metadata
column was created by this migration, then have downgrade() consult that
ownership state and drop only migration-created columns. If ownership cannot be
persisted reliably, remove the unconditional drop behavior from downgrade()
rather than risking deletion of existing columns.

Loading