67 lines
3.0 KiB
Python
67 lines
3.0 KiB
Python
"""backups plugin baseline (real create).
|
|
|
|
Built after the ADR-008 ownership cutover, so unlike the ten cutover plugins
|
|
whose 0001 is a stamp-only anchor, this baseline genuinely CREATES the table.
|
|
The core chain never knew about backuprevisions, so this per-plugin chain is its
|
|
sole authoritative creator.
|
|
|
|
Emits explicit Alembic ops rather than using the create_plugin_tables helper:
|
|
the helper builds a per-plugin MetaData filtered to the plugin's own tables, so
|
|
the foreign key to the core assets table cannot resolve at CreateTable-compile
|
|
time (NoReferencedTableError). Same shape autogenerate produces. Tables inherit
|
|
the connection's default charset, matching how the core chain creates its own.
|
|
"""
|
|
from alembic import op
|
|
|
|
from shopdb.plugins.alembic_template import (
|
|
create_index_if_missing,
|
|
create_table_if_missing,
|
|
)
|
|
import sqlalchemy as sa
|
|
|
|
|
|
# revision identifiers, used by Alembic.
|
|
revision = 'backups0001baseline'
|
|
down_revision = None
|
|
branch_labels = None
|
|
depends_on = None
|
|
|
|
|
|
def upgrade():
|
|
create_table_if_missing(
|
|
'backuprevisions',
|
|
sa.Column('backuprevisionid', sa.Integer(), nullable=False),
|
|
sa.Column('assetid', sa.Integer(), nullable=False),
|
|
sa.Column('backupkind', sa.String(length=50), nullable=False),
|
|
sa.Column('storagebackend', sa.String(length=20), nullable=False,
|
|
server_default='shopdb'),
|
|
sa.Column('contenthash', sa.String(length=64), nullable=False),
|
|
sa.Column('payloadjson', sa.Text(length=16777215), nullable=True),
|
|
sa.Column('sharepath', sa.String(length=500), nullable=True),
|
|
sa.Column('sourcefilename', sa.String(length=255), nullable=True),
|
|
sa.Column('bytesize', sa.Integer(), nullable=True),
|
|
sa.Column('sourcehostname', sa.String(length=255), nullable=True),
|
|
sa.Column('collectedat', sa.DateTime(), nullable=True),
|
|
sa.Column('createdat', sa.DateTime(), nullable=False),
|
|
sa.ForeignKeyConstraint(['assetid'], ['assets.assetid'],
|
|
ondelete='CASCADE'),
|
|
sa.PrimaryKeyConstraint('backuprevisionid'),
|
|
)
|
|
create_index_if_missing('ixbackuprevisionsassetid', 'backuprevisions', ['assetid'])
|
|
create_index_if_missing('ixbackuprevisionsbackupkind', 'backuprevisions',
|
|
['backupkind'])
|
|
create_index_if_missing('ixbackuprevisionscontenthash', 'backuprevisions',
|
|
['contenthash'])
|
|
# The dedup read path is "latest revision for this asset+kind", so the
|
|
# composite index is the one that actually gets used on every collector post.
|
|
create_index_if_missing('ixbackuprevisionsassetkind', 'backuprevisions',
|
|
['assetid', 'backupkind'])
|
|
|
|
|
|
def downgrade():
|
|
op.drop_index('ixbackuprevisionsassetkind', table_name='backuprevisions')
|
|
op.drop_index('ixbackuprevisionscontenthash', table_name='backuprevisions')
|
|
op.drop_index('ixbackuprevisionsbackupkind', table_name='backuprevisions')
|
|
op.drop_index('ixbackuprevisionsassetid', table_name='backuprevisions')
|
|
op.drop_table('backuprevisions')
|