// file: crates/ksp-store-postgres-lib/src/schema.rs // version: 9 /// Immutable V000 physical schema resource inventory. pub(crate) const V000_RESOURCES: &[SchemaResource] = &[SchemaResource { id: "tables/001_ksp_store_schema_migrations.sql", object: SchemaObjectContract::Table(TableContract { columns: STORE_SCHEMA_MIGRATIONS_COLUMNS, name: "ksp_store_schema_migrations", primary_key_columns: std::option::Option::Some("version"), }), repair_existing: false, sql: include_str!("../migrations/v000_bootstrap/tables/001_ksp_store_schema_migrations.sql"), }]; /// Immutable V001 physical schema resource inventory. pub(crate) const V001_RESOURCES: &[SchemaResource] = &[ SchemaResource { id: "tables/001_ksp_store_identity.sql", object: SchemaObjectContract::Table(TableContract { columns: STORE_IDENTITY_COLUMNS, name: "ksp_store_identity", primary_key_columns: std::option::Option::None, }), repair_existing: false, sql: include_str!("../migrations/v001_raw_transaction/tables/001_ksp_store_identity.sql"), }, SchemaResource { id: "tables/002_ksp_raw_transactions.sql", object: SchemaObjectContract::Table(TableContract { columns: RAW_TRANSACTIONS_COLUMNS, name: "ksp_raw_transactions", primary_key_columns: std::option::Option::None, }), repair_existing: true, sql: include_str!("../migrations/v001_raw_transaction/tables/002_ksp_raw_transactions.sql"), }, SchemaResource { id: "tables/003_ksp_raw_transaction_observations.sql", object: SchemaObjectContract::Table(TableContract { columns: RAW_TRANSACTION_OBSERVATIONS_COLUMNS, name: "ksp_raw_transaction_observations", primary_key_columns: std::option::Option::None, }), repair_existing: true, sql: include_str!("../migrations/v001_raw_transaction/tables/003_ksp_raw_transaction_observations.sql"), }, SchemaResource { id: "tables/004_ksp_raw_transaction_archive_payloads.sql", object: SchemaObjectContract::Table(TableContract { columns: RAW_TRANSACTION_ARCHIVE_PAYLOADS_COLUMNS, name: "ksp_raw_transaction_archive_payloads", primary_key_columns: std::option::Option::None, }), repair_existing: true, sql: include_str!("../migrations/v001_raw_transaction/tables/004_ksp_raw_transaction_archive_payloads.sql"), }, SchemaResource { id: "constraints/001_pk_ksp_store_identity.sql", object: SchemaObjectContract::Constraint(ConstraintContract { kind: "p", name: "pk_ksp_store_identity", table: "ksp_store_identity" }), repair_existing: true, sql: include_str!("../migrations/v001_raw_transaction/constraints/001_pk_ksp_store_identity.sql"), }, SchemaResource { id: "constraints/002_ck_ksp_store_identity_singleton.sql", object: SchemaObjectContract::Constraint(ConstraintContract { kind: "c", name: "ck_ksp_store_identity_singleton", table: "ksp_store_identity" }), repair_existing: true, sql: include_str!("../migrations/v001_raw_transaction/constraints/002_ck_ksp_store_identity_singleton.sql"), }, SchemaResource { id: "constraints/003_ck_ksp_store_identity_network.sql", object: SchemaObjectContract::Constraint(ConstraintContract { kind: "c", name: "ck_ksp_store_identity_network", table: "ksp_store_identity" }), repair_existing: true, sql: include_str!("../migrations/v001_raw_transaction/constraints/003_ck_ksp_store_identity_network.sql"), }, SchemaResource { id: "constraints/004_pk_ksp_raw_transactions.sql", object: SchemaObjectContract::Constraint(ConstraintContract { kind: "p", name: "pk_ksp_raw_transactions", table: "ksp_raw_transactions" }), repair_existing: true, sql: include_str!("../migrations/v001_raw_transaction/constraints/004_pk_ksp_raw_transactions.sql"), }, SchemaResource { id: "constraints/005_ck_ksp_raw_transactions_signature.sql", object: SchemaObjectContract::Constraint(ConstraintContract { kind: "c", name: "ck_ksp_raw_transactions_signature", table: "ksp_raw_transactions" }), repair_existing: true, sql: include_str!("../migrations/v001_raw_transaction/constraints/005_ck_ksp_raw_transactions_signature.sql"), }, SchemaResource { id: "constraints/006_ck_ksp_raw_transactions_slot.sql", object: SchemaObjectContract::Constraint(ConstraintContract { kind: "c", name: "ck_ksp_raw_transactions_slot", table: "ksp_raw_transactions" }), repair_existing: true, sql: include_str!("../migrations/v001_raw_transaction/constraints/006_ck_ksp_raw_transactions_slot.sql"), }, SchemaResource { id: "constraints/007_ck_ksp_raw_transactions_block_time.sql", object: SchemaObjectContract::Constraint(ConstraintContract { kind: "c", name: "ck_ksp_raw_transactions_block_time", table: "ksp_raw_transactions" }), repair_existing: true, sql: include_str!("../migrations/v001_raw_transaction/constraints/007_ck_ksp_raw_transactions_block_time.sql"), }, SchemaResource { id: "constraints/008_ck_ksp_raw_transactions_format_id.sql", object: SchemaObjectContract::Constraint(ConstraintContract { kind: "c", name: "ck_ksp_raw_transactions_format_id", table: "ksp_raw_transactions" }), repair_existing: true, sql: include_str!("../migrations/v001_raw_transaction/constraints/008_ck_ksp_raw_transactions_format_id.sql"), }, SchemaResource { id: "constraints/009_ck_ksp_raw_transactions_format_version.sql", object: SchemaObjectContract::Constraint(ConstraintContract { kind: "c", name: "ck_ksp_raw_transactions_format_version", table: "ksp_raw_transactions", }), repair_existing: true, sql: include_str!("../migrations/v001_raw_transaction/constraints/009_ck_ksp_raw_transactions_format_version.sql"), }, SchemaResource { id: "constraints/010_ck_ksp_raw_transactions_content_hash.sql", object: SchemaObjectContract::Constraint(ConstraintContract { kind: "c", name: "ck_ksp_raw_transactions_content_hash", table: "ksp_raw_transactions" }), repair_existing: true, sql: include_str!("../migrations/v001_raw_transaction/constraints/010_ck_ksp_raw_transactions_content_hash.sql"), }, SchemaResource { id: "constraints/011_ck_ksp_raw_transactions_payload.sql", object: SchemaObjectContract::Constraint(ConstraintContract { kind: "c", name: "ck_ksp_raw_transactions_payload", table: "ksp_raw_transactions" }), repair_existing: true, sql: include_str!("../migrations/v001_raw_transaction/constraints/011_ck_ksp_raw_transactions_payload.sql"), }, SchemaResource { id: "constraints/012_ck_ksp_raw_transactions_retention_state.sql", object: SchemaObjectContract::Constraint(ConstraintContract { kind: "c", name: "ck_ksp_raw_transactions_retention_state", table: "ksp_raw_transactions", }), repair_existing: true, sql: include_str!("../migrations/v001_raw_transaction/constraints/012_ck_ksp_raw_transactions_retention_state.sql"), }, SchemaResource { id: "constraints/013_ck_ksp_raw_transactions_payload_state.sql", object: SchemaObjectContract::Constraint(ConstraintContract { kind: "c", name: "ck_ksp_raw_transactions_payload_state", table: "ksp_raw_transactions", }), repair_existing: true, sql: include_str!("../migrations/v001_raw_transaction/constraints/013_ck_ksp_raw_transactions_payload_state.sql"), }, SchemaResource { id: "constraints/014_ck_ksp_raw_transactions_purged_block_time.sql", object: SchemaObjectContract::Constraint(ConstraintContract { kind: "c", name: "ck_ksp_raw_transactions_purged_block_time", table: "ksp_raw_transactions", }), repair_existing: true, sql: include_str!("../migrations/v001_raw_transaction/constraints/014_ck_ksp_raw_transactions_purged_block_time.sql"), }, SchemaResource { id: "constraints/015_pk_ksp_raw_transaction_observations.sql", object: SchemaObjectContract::Constraint(ConstraintContract { kind: "p", name: "pk_ksp_raw_transaction_observations", table: "ksp_raw_transaction_observations", }), repair_existing: true, sql: include_str!("../migrations/v001_raw_transaction/constraints/015_pk_ksp_raw_transaction_observations.sql"), }, SchemaResource { id: "constraints/016_fk_ksp_raw_transaction_observations_transaction.sql", object: SchemaObjectContract::Constraint(ConstraintContract { kind: "f", name: "fk_ksp_raw_transaction_observations_transaction", table: "ksp_raw_transaction_observations", }), repair_existing: true, sql: include_str!("../migrations/v001_raw_transaction/constraints/016_fk_ksp_raw_transaction_observations_transaction.sql"), }, SchemaResource { id: "constraints/017_ck_ksp_raw_transaction_observations_key.sql", object: SchemaObjectContract::Constraint(ConstraintContract { kind: "c", name: "ck_ksp_raw_transaction_observations_key", table: "ksp_raw_transaction_observations", }), repair_existing: true, sql: include_str!("../migrations/v001_raw_transaction/constraints/017_ck_ksp_raw_transaction_observations_key.sql"), }, SchemaResource { id: "constraints/018_ck_ksp_raw_transaction_observations_signature.sql", object: SchemaObjectContract::Constraint(ConstraintContract { kind: "c", name: "ck_ksp_raw_transaction_observations_signature", table: "ksp_raw_transaction_observations", }), repair_existing: true, sql: include_str!("../migrations/v001_raw_transaction/constraints/018_ck_ksp_raw_transaction_observations_signature.sql"), }, SchemaResource { id: "constraints/019_ck_ksp_raw_transaction_observations_provider.sql", object: SchemaObjectContract::Constraint(ConstraintContract { kind: "c", name: "ck_ksp_raw_transaction_observations_provider", table: "ksp_raw_transaction_observations", }), repair_existing: true, sql: include_str!("../migrations/v001_raw_transaction/constraints/019_ck_ksp_raw_transaction_observations_provider.sql"), }, SchemaResource { id: "constraints/020_ck_ksp_raw_transaction_observations_protocol.sql", object: SchemaObjectContract::Constraint(ConstraintContract { kind: "c", name: "ck_ksp_raw_transaction_observations_protocol", table: "ksp_raw_transaction_observations", }), repair_existing: true, sql: include_str!("../migrations/v001_raw_transaction/constraints/020_ck_ksp_raw_transaction_observations_protocol.sql"), }, SchemaResource { id: "constraints/021_ck_ksp_raw_transaction_observations_method.sql", object: SchemaObjectContract::Constraint(ConstraintContract { kind: "c", name: "ck_ksp_raw_transaction_observations_method", table: "ksp_raw_transaction_observations", }), repair_existing: true, sql: include_str!("../migrations/v001_raw_transaction/constraints/021_ck_ksp_raw_transaction_observations_method.sql"), }, SchemaResource { id: "constraints/022_ck_ksp_raw_transaction_observations_origin.sql", object: SchemaObjectContract::Constraint(ConstraintContract { kind: "c", name: "ck_ksp_raw_transaction_observations_origin", table: "ksp_raw_transaction_observations", }), repair_existing: true, sql: include_str!("../migrations/v001_raw_transaction/constraints/022_ck_ksp_raw_transaction_observations_origin.sql"), }, SchemaResource { id: "constraints/023_ck_ksp_raw_transaction_observations_received_at.sql", object: SchemaObjectContract::Constraint(ConstraintContract { kind: "c", name: "ck_ksp_raw_transaction_observations_received_at", table: "ksp_raw_transaction_observations", }), repair_existing: true, sql: include_str!("../migrations/v001_raw_transaction/constraints/023_ck_ksp_raw_transaction_observations_received_at.sql"), }, SchemaResource { id: "constraints/024_ck_ksp_raw_transaction_observations_capture_session.sql", object: SchemaObjectContract::Constraint(ConstraintContract { kind: "c", name: "ck_ksp_raw_transaction_observations_capture_session", table: "ksp_raw_transaction_observations", }), repair_existing: true, sql: include_str!("../migrations/v001_raw_transaction/constraints/024_ck_ksp_raw_transaction_observations_capture_session.sql"), }, SchemaResource { id: "constraints/025_ck_ksp_raw_transaction_observations_commitment.sql", object: SchemaObjectContract::Constraint(ConstraintContract { kind: "c", name: "ck_ksp_raw_transaction_observations_commitment", table: "ksp_raw_transaction_observations", }), repair_existing: true, sql: include_str!("../migrations/v001_raw_transaction/constraints/025_ck_ksp_raw_transaction_observations_commitment.sql"), }, SchemaResource { id: "constraints/026_ck_ksp_raw_transaction_observations_endpoint.sql", object: SchemaObjectContract::Constraint(ConstraintContract { kind: "c", name: "ck_ksp_raw_transaction_observations_endpoint", table: "ksp_raw_transaction_observations", }), repair_existing: true, sql: include_str!("../migrations/v001_raw_transaction/constraints/026_ck_ksp_raw_transaction_observations_endpoint.sql"), }, SchemaResource { id: "constraints/027_ck_ksp_raw_transaction_observations_filter.sql", object: SchemaObjectContract::Constraint(ConstraintContract { kind: "c", name: "ck_ksp_raw_transaction_observations_filter", table: "ksp_raw_transaction_observations", }), repair_existing: true, sql: include_str!("../migrations/v001_raw_transaction/constraints/027_ck_ksp_raw_transaction_observations_filter.sql"), }, SchemaResource { id: "constraints/028_ck_ksp_raw_transaction_observations_observed_at.sql", object: SchemaObjectContract::Constraint(ConstraintContract { kind: "c", name: "ck_ksp_raw_transaction_observations_observed_at", table: "ksp_raw_transaction_observations", }), repair_existing: true, sql: include_str!("../migrations/v001_raw_transaction/constraints/028_ck_ksp_raw_transaction_observations_observed_at.sql"), }, SchemaResource { id: "constraints/029_ck_ksp_raw_transaction_observations_time_order.sql", object: SchemaObjectContract::Constraint(ConstraintContract { kind: "c", name: "ck_ksp_raw_transaction_observations_time_order", table: "ksp_raw_transaction_observations", }), repair_existing: true, sql: include_str!("../migrations/v001_raw_transaction/constraints/029_ck_ksp_raw_transaction_observations_time_order.sql"), }, SchemaResource { id: "constraints/030_ck_ksp_raw_transaction_observations_source_hash.sql", object: SchemaObjectContract::Constraint(ConstraintContract { kind: "c", name: "ck_ksp_raw_transaction_observations_source_hash", table: "ksp_raw_transaction_observations", }), repair_existing: true, sql: include_str!("../migrations/v001_raw_transaction/constraints/030_ck_ksp_raw_transaction_observations_source_hash.sql"), }, SchemaResource { id: "constraints/031_ck_ksp_raw_transaction_observations_source_size.sql", object: SchemaObjectContract::Constraint(ConstraintContract { kind: "c", name: "ck_ksp_raw_transaction_observations_source_size", table: "ksp_raw_transaction_observations", }), repair_existing: true, sql: include_str!("../migrations/v001_raw_transaction/constraints/031_ck_ksp_raw_transaction_observations_source_size.sql"), }, SchemaResource { id: "constraints/032_pk_ksp_raw_transaction_archive_payloads.sql", object: SchemaObjectContract::Constraint(ConstraintContract { kind: "p", name: "pk_ksp_raw_transaction_archive_payloads", table: "ksp_raw_transaction_archive_payloads", }), repair_existing: true, sql: include_str!("../migrations/v001_raw_transaction/constraints/032_pk_ksp_raw_transaction_archive_payloads.sql"), }, SchemaResource { id: "constraints/033_fk_ksp_raw_transaction_archive_payloads_transaction.sql", object: SchemaObjectContract::Constraint(ConstraintContract { kind: "f", name: "fk_ksp_raw_transaction_archive_payloads_transaction", table: "ksp_raw_transaction_archive_payloads", }), repair_existing: true, sql: include_str!("../migrations/v001_raw_transaction/constraints/033_fk_ksp_raw_transaction_archive_payloads_transaction.sql"), }, SchemaResource { id: "constraints/034_ck_ksp_raw_transaction_archive_payloads_signature.sql", object: SchemaObjectContract::Constraint(ConstraintContract { kind: "c", name: "ck_ksp_raw_transaction_archive_payloads_signature", table: "ksp_raw_transaction_archive_payloads", }), repair_existing: true, sql: include_str!("../migrations/v001_raw_transaction/constraints/034_ck_ksp_raw_transaction_archive_payloads_signature.sql"), }, SchemaResource { id: "constraints/035_ck_ksp_raw_transaction_archive_payloads_payload.sql", object: SchemaObjectContract::Constraint(ConstraintContract { kind: "c", name: "ck_ksp_raw_transaction_archive_payloads_payload", table: "ksp_raw_transaction_archive_payloads", }), repair_existing: true, sql: include_str!("../migrations/v001_raw_transaction/constraints/035_ck_ksp_raw_transaction_archive_payloads_payload.sql"), }, SchemaResource { id: "indexes/001_ix_ksp_raw_transactions_slot_signature.sql", object: SchemaObjectContract::Index(IndexContract { access_method: "btree", key_fragment: "slot,signature", name: "ix_ksp_raw_transactions_slot_signature", predicate_fragment: std::option::Option::Some("retention_state<>'purged'"), table: "ksp_raw_transactions", unique: false, }), repair_existing: true, sql: include_str!("../migrations/v001_raw_transaction/indexes/001_ix_ksp_raw_transactions_slot_signature.sql"), }, ]; /// Final V002 physical schema resource inventory for RAW account state persistence. pub(crate) const V002_RESOURCES: &[SchemaResource] = &[ SchemaResource { id: "tables/001_ksp_raw_account_states.sql", object: SchemaObjectContract::Table(TableContract { columns: RAW_ACCOUNT_STATES_COLUMNS, name: "ksp_raw_account_states", primary_key_columns: std::option::Option::None, }), repair_existing: true, sql: include_str!("../migrations/v002_raw_account_state/tables/001_ksp_raw_account_states.sql"), }, SchemaResource { id: "tables/002_ksp_raw_account_observations.sql", object: SchemaObjectContract::Table(TableContract { columns: RAW_ACCOUNT_OBSERVATIONS_COLUMNS, name: "ksp_raw_account_observations", primary_key_columns: std::option::Option::None, }), repair_existing: true, sql: include_str!("../migrations/v002_raw_account_state/tables/002_ksp_raw_account_observations.sql"), }, SchemaResource { id: "constraints/001_pk_ksp_raw_account_states.sql", object: SchemaObjectContract::Constraint(ConstraintContract { kind: "p", name: "pk_ksp_raw_account_states", table: "ksp_raw_account_states" }), repair_existing: true, sql: include_str!("../migrations/v002_raw_account_state/constraints/001_pk_ksp_raw_account_states.sql"), }, SchemaResource { id: "constraints/002_pk_ksp_raw_account_observations.sql", object: SchemaObjectContract::Constraint(ConstraintContract { kind: "p", name: "pk_ksp_raw_account_observations", table: "ksp_raw_account_observations", }), repair_existing: true, sql: include_str!("../migrations/v002_raw_account_state/constraints/002_pk_ksp_raw_account_observations.sql"), }, SchemaResource { id: "constraints/003_fk_ksp_raw_account_observations_state.sql", object: SchemaObjectContract::Constraint(ConstraintContract { kind: "f", name: "fk_ksp_raw_account_observations_state", table: "ksp_raw_account_observations", }), repair_existing: true, sql: include_str!("../migrations/v002_raw_account_state/constraints/003_fk_ksp_raw_account_observations_state.sql"), }, SchemaResource { id: "constraints/004_ck_ksp_raw_account_states_pubkey.sql", object: SchemaObjectContract::Constraint(ConstraintContract { kind: "c", name: "ck_ksp_raw_account_states_pubkey", table: "ksp_raw_account_states" }), repair_existing: true, sql: include_str!("../migrations/v002_raw_account_state/constraints/004_ck_ksp_raw_account_states_pubkey.sql"), }, SchemaResource { id: "constraints/005_ck_ksp_raw_account_states_slot.sql", object: SchemaObjectContract::Constraint(ConstraintContract { kind: "c", name: "ck_ksp_raw_account_states_slot", table: "ksp_raw_account_states" }), repair_existing: true, sql: include_str!("../migrations/v002_raw_account_state/constraints/005_ck_ksp_raw_account_states_slot.sql"), }, SchemaResource { id: "constraints/006_ck_ksp_raw_account_states_state_hash.sql", object: SchemaObjectContract::Constraint(ConstraintContract { kind: "c", name: "ck_ksp_raw_account_states_state_hash", table: "ksp_raw_account_states", }), repair_existing: true, sql: include_str!("../migrations/v002_raw_account_state/constraints/006_ck_ksp_raw_account_states_state_hash.sql"), }, SchemaResource { id: "constraints/007_ck_ksp_raw_account_states_lamports.sql", object: SchemaObjectContract::Constraint(ConstraintContract { kind: "c", name: "ck_ksp_raw_account_states_lamports", table: "ksp_raw_account_states" }), repair_existing: true, sql: include_str!("../migrations/v002_raw_account_state/constraints/007_ck_ksp_raw_account_states_lamports.sql"), }, SchemaResource { id: "constraints/008_ck_ksp_raw_account_states_owner.sql", object: SchemaObjectContract::Constraint(ConstraintContract { kind: "c", name: "ck_ksp_raw_account_states_owner", table: "ksp_raw_account_states" }), repair_existing: true, sql: include_str!("../migrations/v002_raw_account_state/constraints/008_ck_ksp_raw_account_states_owner.sql"), }, SchemaResource { id: "constraints/009_ck_ksp_raw_account_states_rent_epoch.sql", object: SchemaObjectContract::Constraint(ConstraintContract { kind: "c", name: "ck_ksp_raw_account_states_rent_epoch", table: "ksp_raw_account_states", }), repair_existing: true, sql: include_str!("../migrations/v002_raw_account_state/constraints/009_ck_ksp_raw_account_states_rent_epoch.sql"), }, SchemaResource { id: "constraints/010_ck_ksp_raw_account_states_data.sql", object: SchemaObjectContract::Constraint(ConstraintContract { kind: "c", name: "ck_ksp_raw_account_states_data", table: "ksp_raw_account_states" }), repair_existing: true, sql: include_str!("../migrations/v002_raw_account_state/constraints/010_ck_ksp_raw_account_states_data.sql"), }, SchemaResource { id: "constraints/011_ck_ksp_raw_account_observations_key.sql", object: SchemaObjectContract::Constraint(ConstraintContract { kind: "c", name: "ck_ksp_raw_account_observations_key", table: "ksp_raw_account_observations", }), repair_existing: true, sql: include_str!("../migrations/v002_raw_account_state/constraints/011_ck_ksp_raw_account_observations_key.sql"), }, SchemaResource { id: "constraints/012_ck_ksp_raw_account_observations_account_pubkey.sql", object: SchemaObjectContract::Constraint(ConstraintContract { kind: "c", name: "ck_ksp_raw_account_observations_account_pubkey", table: "ksp_raw_account_observations", }), repair_existing: true, sql: include_str!("../migrations/v002_raw_account_state/constraints/012_ck_ksp_raw_account_observations_account_pubkey.sql"), }, SchemaResource { id: "constraints/013_ck_ksp_raw_account_observations_account_slot.sql", object: SchemaObjectContract::Constraint(ConstraintContract { kind: "c", name: "ck_ksp_raw_account_observations_account_slot", table: "ksp_raw_account_observations", }), repair_existing: true, sql: include_str!("../migrations/v002_raw_account_state/constraints/013_ck_ksp_raw_account_observations_account_slot.sql"), }, SchemaResource { id: "constraints/014_ck_ksp_raw_account_observations_account_state_hash.sql", object: SchemaObjectContract::Constraint(ConstraintContract { kind: "c", name: "ck_ksp_raw_account_observations_account_state_hash", table: "ksp_raw_account_observations", }), repair_existing: true, sql: include_str!("../migrations/v002_raw_account_state/constraints/014_ck_ksp_raw_account_observations_account_state_hash.sql"), }, SchemaResource { id: "constraints/015_ck_ksp_raw_account_observations_provider.sql", object: SchemaObjectContract::Constraint(ConstraintContract { kind: "c", name: "ck_ksp_raw_account_observations_provider", table: "ksp_raw_account_observations", }), repair_existing: true, sql: include_str!("../migrations/v002_raw_account_state/constraints/015_ck_ksp_raw_account_observations_provider.sql"), }, SchemaResource { id: "constraints/016_ck_ksp_raw_account_observations_protocol.sql", object: SchemaObjectContract::Constraint(ConstraintContract { kind: "c", name: "ck_ksp_raw_account_observations_protocol", table: "ksp_raw_account_observations", }), repair_existing: true, sql: include_str!("../migrations/v002_raw_account_state/constraints/016_ck_ksp_raw_account_observations_protocol.sql"), }, SchemaResource { id: "constraints/017_ck_ksp_raw_account_observations_method.sql", object: SchemaObjectContract::Constraint(ConstraintContract { kind: "c", name: "ck_ksp_raw_account_observations_method", table: "ksp_raw_account_observations", }), repair_existing: true, sql: include_str!("../migrations/v002_raw_account_state/constraints/017_ck_ksp_raw_account_observations_method.sql"), }, SchemaResource { id: "constraints/018_ck_ksp_raw_account_observations_origin.sql", object: SchemaObjectContract::Constraint(ConstraintContract { kind: "c", name: "ck_ksp_raw_account_observations_origin", table: "ksp_raw_account_observations", }), repair_existing: true, sql: include_str!("../migrations/v002_raw_account_state/constraints/018_ck_ksp_raw_account_observations_origin.sql"), }, SchemaResource { id: "constraints/019_ck_ksp_raw_account_observations_received_at.sql", object: SchemaObjectContract::Constraint(ConstraintContract { kind: "c", name: "ck_ksp_raw_account_observations_received_at", table: "ksp_raw_account_observations", }), repair_existing: true, sql: include_str!("../migrations/v002_raw_account_state/constraints/019_ck_ksp_raw_account_observations_received_at.sql"), }, SchemaResource { id: "constraints/020_ck_ksp_raw_account_observations_capture_session.sql", object: SchemaObjectContract::Constraint(ConstraintContract { kind: "c", name: "ck_ksp_raw_account_observations_capture_session", table: "ksp_raw_account_observations", }), repair_existing: true, sql: include_str!("../migrations/v002_raw_account_state/constraints/020_ck_ksp_raw_account_observations_capture_session.sql"), }, SchemaResource { id: "constraints/021_ck_ksp_raw_account_observations_commitment.sql", object: SchemaObjectContract::Constraint(ConstraintContract { kind: "c", name: "ck_ksp_raw_account_observations_commitment", table: "ksp_raw_account_observations", }), repair_existing: true, sql: include_str!("../migrations/v002_raw_account_state/constraints/021_ck_ksp_raw_account_observations_commitment.sql"), }, SchemaResource { id: "constraints/022_ck_ksp_raw_account_observations_endpoint.sql", object: SchemaObjectContract::Constraint(ConstraintContract { kind: "c", name: "ck_ksp_raw_account_observations_endpoint", table: "ksp_raw_account_observations", }), repair_existing: true, sql: include_str!("../migrations/v002_raw_account_state/constraints/022_ck_ksp_raw_account_observations_endpoint.sql"), }, SchemaResource { id: "constraints/023_ck_ksp_raw_account_observations_filter.sql", object: SchemaObjectContract::Constraint(ConstraintContract { kind: "c", name: "ck_ksp_raw_account_observations_filter", table: "ksp_raw_account_observations", }), repair_existing: true, sql: include_str!("../migrations/v002_raw_account_state/constraints/023_ck_ksp_raw_account_observations_filter.sql"), }, SchemaResource { id: "constraints/024_ck_ksp_raw_account_observations_observed_at.sql", object: SchemaObjectContract::Constraint(ConstraintContract { kind: "c", name: "ck_ksp_raw_account_observations_observed_at", table: "ksp_raw_account_observations", }), repair_existing: true, sql: include_str!("../migrations/v002_raw_account_state/constraints/024_ck_ksp_raw_account_observations_observed_at.sql"), }, SchemaResource { id: "constraints/025_ck_ksp_raw_account_observations_time_order.sql", object: SchemaObjectContract::Constraint(ConstraintContract { kind: "c", name: "ck_ksp_raw_account_observations_time_order", table: "ksp_raw_account_observations", }), repair_existing: true, sql: include_str!("../migrations/v002_raw_account_state/constraints/025_ck_ksp_raw_account_observations_time_order.sql"), }, SchemaResource { id: "constraints/026_ck_ksp_raw_account_observations_source_hash.sql", object: SchemaObjectContract::Constraint(ConstraintContract { kind: "c", name: "ck_ksp_raw_account_observations_source_hash", table: "ksp_raw_account_observations", }), repair_existing: true, sql: include_str!("../migrations/v002_raw_account_state/constraints/026_ck_ksp_raw_account_observations_source_hash.sql"), }, SchemaResource { id: "constraints/027_ck_ksp_raw_account_observations_source_size.sql", object: SchemaObjectContract::Constraint(ConstraintContract { kind: "c", name: "ck_ksp_raw_account_observations_source_size", table: "ksp_raw_account_observations", }), repair_existing: true, sql: include_str!("../migrations/v002_raw_account_state/constraints/027_ck_ksp_raw_account_observations_source_size.sql"), }, SchemaResource { id: "constraints/028_ck_ksp_raw_account_observations_transaction_signature.sql", object: SchemaObjectContract::Constraint(ConstraintContract { kind: "c", name: "ck_ksp_raw_account_observations_transaction_signature", table: "ksp_raw_account_observations", }), repair_existing: true, sql: include_str!("../migrations/v002_raw_account_state/constraints/028_ck_ksp_raw_account_observations_transaction_signature.sql"), }, SchemaResource { id: "constraints/029_ck_ksp_raw_account_observations_write_version.sql", object: SchemaObjectContract::Constraint(ConstraintContract { kind: "c", name: "ck_ksp_raw_account_observations_write_version", table: "ksp_raw_account_observations", }), repair_existing: true, sql: include_str!("../migrations/v002_raw_account_state/constraints/029_ck_ksp_raw_account_observations_write_version.sql"), }, SchemaResource { id: "indexes/001_ix_ksp_raw_account_states_slot_pubkey_state_hash.sql", object: SchemaObjectContract::Index(IndexContract { access_method: "btree", key_fragment: "slot,pubkey,state_hash", name: "ix_ksp_raw_account_states_slot_pubkey_state_hash", predicate_fragment: std::option::Option::None, table: "ksp_raw_account_states", unique: false, }), repair_existing: true, sql: include_str!("../migrations/v002_raw_account_state/indexes/001_ix_ksp_raw_account_states_slot_pubkey_state_hash.sql"), }, ]; const COLUMN_LOAD_SQL: &str = r#"SELECT column_name::TEXT, udt_name::TEXT, (is_nullable = 'YES') AS nullable, numeric_precision::INTEGER, numeric_scale::INTEGER, column_default::TEXT, is_identity::TEXT, is_generated::TEXT FROM information_schema.columns WHERE table_schema = current_schema() AND table_name = $1 ORDER BY ordinal_position"#; const CONSTRAINT_LOAD_SQL: &str = r#"SELECT con.contype::TEXT, con.convalidated, con.condeferrable, con.condeferred, pg_get_constraintdef(con.oid, true)::TEXT FROM pg_constraint con JOIN pg_class rel ON rel.oid = con.conrelid JOIN pg_namespace ns ON ns.oid = rel.relnamespace WHERE ns.nspname = current_schema() AND rel.relname = $1 AND con.conname = $2"#; const CONSTRAINT_FALLBACK_LOAD_SQL: &str = r#"SELECT con.contype::TEXT, con.convalidated, con.condeferrable, con.condeferred, pg_get_constraintdef(con.oid, true)::TEXT FROM pg_constraint con JOIN pg_class rel ON rel.oid = con.conrelid JOIN pg_namespace ns ON ns.oid = rel.relnamespace WHERE ns.nspname = current_schema() AND rel.relname = $1 AND con.contype::TEXT = $2 ORDER BY con.conname"#; const INDEX_LOAD_SQL: &str = r#"SELECT idx.indisunique, am.amname::TEXT, pg_get_indexdef(index_rel.oid)::TEXT, pg_get_expr(idx.indpred, idx.indrelid, true)::TEXT FROM pg_index idx JOIN pg_class index_rel ON index_rel.oid = idx.indexrelid JOIN pg_class table_rel ON table_rel.oid = idx.indrelid JOIN pg_namespace ns ON ns.oid = table_rel.relnamespace JOIN pg_am am ON am.oid = index_rel.relam WHERE ns.nspname = current_schema() AND table_rel.relname = $1 AND index_rel.relname = $2"#; const MANAGED_OBJECT_EXISTS_SQL: &str = r#"SELECT EXISTS ( SELECT 1 FROM information_schema.tables WHERE table_schema = current_schema() AND table_name IN ('ksp_store_identity', 'ksp_raw_transactions', 'ksp_raw_transaction_observations', 'ksp_raw_transaction_archive_payloads', 'ksp_raw_account_states', 'ksp_raw_account_observations') AND table_type = 'BASE TABLE' )"#; const PRIMARY_KEY_LOAD_SQL: &str = r#"SELECT string_agg(att.attname::TEXT, ',' ORDER BY key_part.ord)::TEXT FROM pg_constraint con JOIN pg_class rel ON rel.oid = con.conrelid JOIN pg_namespace ns ON ns.oid = rel.relnamespace JOIN LATERAL unnest(con.conkey) WITH ORDINALITY AS key_part(attnum, ord) ON true JOIN pg_attribute att ON att.attrelid = con.conrelid AND att.attnum = key_part.attnum WHERE ns.nspname = current_schema() AND rel.relname = $1 AND con.contype = 'p' GROUP BY con.oid"#; const RULE_COUNT_SQL: &str = r#"SELECT COUNT(*)::BIGINT FROM pg_rewrite rw JOIN pg_class rel ON rel.oid = rw.ev_class JOIN pg_namespace ns ON ns.oid = rel.relnamespace WHERE ns.nspname = current_schema() AND rel.relname = $1 AND rw.rulename <> '_RETURN'"#; const TABLE_LOAD_SQL: &str = r#"SELECT rel.relkind::TEXT, rel.relrowsecurity, rel.relforcerowsecurity FROM pg_class rel JOIN pg_namespace ns ON ns.oid = rel.relnamespace WHERE ns.nspname = current_schema() AND rel.relname = $1"#; const TRIGGER_COUNT_SQL: &str = r#"SELECT COUNT(*)::BIGINT FROM pg_trigger trg JOIN pg_class rel ON rel.oid = trg.tgrelid JOIN pg_namespace ns ON ns.oid = rel.relnamespace WHERE ns.nspname = current_schema() AND rel.relname = $1 AND NOT trg.tgisinternal AND trg.tgenabled <> 'D'"#; const UNEXPECTED_CONSTRAINTS_SQL: &str = r#"SELECT con.conname::TEXT, con.contype::TEXT, con.convalidated, con.condeferrable, con.condeferred, pg_get_constraintdef(con.oid, true)::TEXT FROM pg_constraint con JOIN pg_class rel ON rel.oid = con.conrelid JOIN pg_namespace ns ON ns.oid = rel.relnamespace WHERE ns.nspname = current_schema() AND rel.relname = $1 ORDER BY con.conname"#; const UNIQUE_INDEXES_SQL: &str = r#"SELECT index_rel.relname::TEXT, EXISTS (SELECT 1 FROM pg_constraint con WHERE con.conindid = idx.indexrelid) AS constraint_backed FROM pg_index idx JOIN pg_class index_rel ON index_rel.oid = idx.indexrelid JOIN pg_class table_rel ON table_rel.oid = idx.indrelid JOIN pg_namespace ns ON ns.oid = table_rel.relnamespace WHERE ns.nspname = current_schema() AND table_rel.relname = $1 AND idx.indisunique ORDER BY index_rel.relname"#; #[derive(Clone, Copy)] struct ColumnContract { name: &'static str, udt_name: &'static str, nullable: bool, numeric_precision: std::option::Option, numeric_scale: std::option::Option, } #[derive(Clone, Copy)] struct ConstraintContract { kind: &'static str, name: &'static str, table: &'static str, } #[derive(Clone, Copy)] struct IndexContract { access_method: &'static str, key_fragment: &'static str, name: &'static str, predicate_fragment: std::option::Option<&'static str>, table: &'static str, unique: bool, } #[derive(Clone, Copy)] struct TableContract { columns: &'static [ColumnContract], name: &'static str, primary_key_columns: std::option::Option<&'static str>, } #[derive(Clone, Copy)] enum SchemaObjectContract { Constraint(ConstraintContract), Index(IndexContract), Table(TableContract), } #[derive(Clone, Copy)] /// Embedded physical schema resource owned by one logical migration. pub(crate) struct SchemaResource { /// Stable resource identifier relative to the logical migration directory. pub(crate) id: &'static str, object: SchemaObjectContract, /// Whether an applied migration may safely recreate this resource when missing. pub(crate) repair_existing: bool, /// Embedded idempotent SQL used to create or additively repair the resource. pub(crate) sql: &'static str, } #[derive(Clone, Copy, Debug, Eq, PartialEq)] /// Compatibility state of one managed physical schema resource. pub(crate) enum SchemaResourceState { /// The effective PostgreSQL object satisfies the KSP contract. Compatible, /// An object exists but its effective structure can block or alter KSP semantics. Incompatible, /// The required managed object or one of its required additive parts is absent. Missing, } const STORE_SCHEMA_MIGRATIONS_COLUMNS: &[ColumnContract] = &[ ColumnContract { name: "version", nullable: false, numeric_precision: std::option::Option::None, numeric_scale: std::option::Option::None, udt_name: "int8", }, ColumnContract { name: "name", nullable: false, numeric_precision: std::option::Option::None, numeric_scale: std::option::Option::None, udt_name: "text", }, ColumnContract { name: "checksum", nullable: false, numeric_precision: std::option::Option::None, numeric_scale: std::option::Option::None, udt_name: "text", }, ColumnContract { name: "applied_at", nullable: false, numeric_precision: std::option::Option::None, numeric_scale: std::option::Option::None, udt_name: "timestamptz", }, ]; const STORE_IDENTITY_COLUMNS: &[ColumnContract] = &[ ColumnContract { name: "singleton", nullable: false, numeric_precision: std::option::Option::None, numeric_scale: std::option::Option::None, udt_name: "int2", }, ColumnContract { name: "network", nullable: false, numeric_precision: std::option::Option::None, numeric_scale: std::option::Option::None, udt_name: "text", }, ]; const RAW_TRANSACTIONS_COLUMNS: &[ColumnContract] = &[ ColumnContract { name: "signature", nullable: false, numeric_precision: std::option::Option::None, numeric_scale: std::option::Option::None, udt_name: "bytea", }, ColumnContract { name: "slot", nullable: false, numeric_precision: std::option::Option::Some(20), numeric_scale: std::option::Option::Some(0), udt_name: "numeric", }, ColumnContract { name: "block_time_unix_millis", nullable: true, numeric_precision: std::option::Option::None, numeric_scale: std::option::Option::None, udt_name: "int8", }, ColumnContract { name: "format_id", nullable: false, numeric_precision: std::option::Option::None, numeric_scale: std::option::Option::None, udt_name: "text", }, ColumnContract { name: "format_version", nullable: false, numeric_precision: std::option::Option::None, numeric_scale: std::option::Option::None, udt_name: "int8", }, ColumnContract { name: "content_hash", nullable: false, numeric_precision: std::option::Option::None, numeric_scale: std::option::Option::None, udt_name: "bytea", }, ColumnContract { name: "payload", nullable: true, numeric_precision: std::option::Option::None, numeric_scale: std::option::Option::None, udt_name: "bytea", }, ColumnContract { name: "retention_state", nullable: false, numeric_precision: std::option::Option::None, numeric_scale: std::option::Option::None, udt_name: "text", }, ]; const RAW_TRANSACTION_OBSERVATIONS_COLUMNS: &[ColumnContract] = &[ ColumnContract { name: "observation_key", nullable: false, numeric_precision: std::option::Option::None, numeric_scale: std::option::Option::None, udt_name: "bytea", }, ColumnContract { name: "transaction_signature", nullable: false, numeric_precision: std::option::Option::None, numeric_scale: std::option::Option::None, udt_name: "bytea", }, ColumnContract { name: "provider", nullable: false, numeric_precision: std::option::Option::None, numeric_scale: std::option::Option::None, udt_name: "text", }, ColumnContract { name: "protocol", nullable: false, numeric_precision: std::option::Option::None, numeric_scale: std::option::Option::None, udt_name: "text", }, ColumnContract { name: "acquisition_method", nullable: false, numeric_precision: std::option::Option::None, numeric_scale: std::option::Option::None, udt_name: "text", }, ColumnContract { name: "origin", nullable: false, numeric_precision: std::option::Option::None, numeric_scale: std::option::Option::None, udt_name: "text", }, ColumnContract { name: "received_at_unix_millis", nullable: false, numeric_precision: std::option::Option::None, numeric_scale: std::option::Option::None, udt_name: "int8", }, ColumnContract { name: "capture_session_id", nullable: true, numeric_precision: std::option::Option::None, numeric_scale: std::option::Option::None, udt_name: "text", }, ColumnContract { name: "commitment", nullable: true, numeric_precision: std::option::Option::None, numeric_scale: std::option::Option::None, udt_name: "text", }, ColumnContract { name: "endpoint_id", nullable: true, numeric_precision: std::option::Option::None, numeric_scale: std::option::Option::None, udt_name: "text", }, ColumnContract { name: "filter_id", nullable: true, numeric_precision: std::option::Option::None, numeric_scale: std::option::Option::None, udt_name: "text", }, ColumnContract { name: "observed_at_unix_millis", nullable: true, numeric_precision: std::option::Option::None, numeric_scale: std::option::Option::None, udt_name: "int8", }, ColumnContract { name: "source_payload_hash", nullable: true, numeric_precision: std::option::Option::None, numeric_scale: std::option::Option::None, udt_name: "bytea", }, ColumnContract { name: "source_payload_size_bytes", nullable: true, numeric_precision: std::option::Option::None, numeric_scale: std::option::Option::None, udt_name: "int8", }, ]; const RAW_ACCOUNT_STATES_COLUMNS: &[ColumnContract] = &[ ColumnContract { name: "pubkey", nullable: false, numeric_precision: std::option::Option::None, numeric_scale: std::option::Option::None, udt_name: "bytea", }, ColumnContract { name: "slot", nullable: false, numeric_precision: std::option::Option::Some(20), numeric_scale: std::option::Option::Some(0), udt_name: "numeric", }, ColumnContract { name: "state_hash", nullable: false, numeric_precision: std::option::Option::None, numeric_scale: std::option::Option::None, udt_name: "bytea", }, ColumnContract { name: "lamports", nullable: false, numeric_precision: std::option::Option::Some(20), numeric_scale: std::option::Option::Some(0), udt_name: "numeric", }, ColumnContract { name: "owner", nullable: false, numeric_precision: std::option::Option::None, numeric_scale: std::option::Option::None, udt_name: "bytea", }, ColumnContract { name: "executable", nullable: false, numeric_precision: std::option::Option::None, numeric_scale: std::option::Option::None, udt_name: "bool", }, ColumnContract { name: "rent_epoch", nullable: false, numeric_precision: std::option::Option::Some(20), numeric_scale: std::option::Option::Some(0), udt_name: "numeric", }, ColumnContract { name: "data", nullable: false, numeric_precision: std::option::Option::None, numeric_scale: std::option::Option::None, udt_name: "bytea", }, ]; const RAW_ACCOUNT_OBSERVATIONS_COLUMNS: &[ColumnContract] = &[ ColumnContract { name: "observation_key", nullable: false, numeric_precision: std::option::Option::None, numeric_scale: std::option::Option::None, udt_name: "bytea", }, ColumnContract { name: "account_pubkey", nullable: false, numeric_precision: std::option::Option::None, numeric_scale: std::option::Option::None, udt_name: "bytea", }, ColumnContract { name: "account_slot", nullable: false, numeric_precision: std::option::Option::Some(20), numeric_scale: std::option::Option::Some(0), udt_name: "numeric", }, ColumnContract { name: "account_state_hash", nullable: false, numeric_precision: std::option::Option::None, numeric_scale: std::option::Option::None, udt_name: "bytea", }, ColumnContract { name: "provider", nullable: false, numeric_precision: std::option::Option::None, numeric_scale: std::option::Option::None, udt_name: "text", }, ColumnContract { name: "protocol", nullable: false, numeric_precision: std::option::Option::None, numeric_scale: std::option::Option::None, udt_name: "text", }, ColumnContract { name: "acquisition_method", nullable: false, numeric_precision: std::option::Option::None, numeric_scale: std::option::Option::None, udt_name: "text", }, ColumnContract { name: "origin", nullable: false, numeric_precision: std::option::Option::None, numeric_scale: std::option::Option::None, udt_name: "text", }, ColumnContract { name: "received_at_unix_millis", nullable: false, numeric_precision: std::option::Option::None, numeric_scale: std::option::Option::None, udt_name: "int8", }, ColumnContract { name: "capture_session_id", nullable: true, numeric_precision: std::option::Option::None, numeric_scale: std::option::Option::None, udt_name: "text", }, ColumnContract { name: "commitment", nullable: true, numeric_precision: std::option::Option::None, numeric_scale: std::option::Option::None, udt_name: "text", }, ColumnContract { name: "endpoint_id", nullable: true, numeric_precision: std::option::Option::None, numeric_scale: std::option::Option::None, udt_name: "text", }, ColumnContract { name: "filter_id", nullable: true, numeric_precision: std::option::Option::None, numeric_scale: std::option::Option::None, udt_name: "text", }, ColumnContract { name: "observed_at_unix_millis", nullable: true, numeric_precision: std::option::Option::None, numeric_scale: std::option::Option::None, udt_name: "int8", }, ColumnContract { name: "source_payload_hash", nullable: true, numeric_precision: std::option::Option::None, numeric_scale: std::option::Option::None, udt_name: "bytea", }, ColumnContract { name: "source_payload_size_bytes", nullable: true, numeric_precision: std::option::Option::None, numeric_scale: std::option::Option::None, udt_name: "int8", }, ColumnContract { name: "is_startup", nullable: true, numeric_precision: std::option::Option::None, numeric_scale: std::option::Option::None, udt_name: "bool", }, ColumnContract { name: "transaction_signature", nullable: true, numeric_precision: std::option::Option::None, numeric_scale: std::option::Option::None, udt_name: "bytea", }, ColumnContract { name: "write_version", nullable: true, numeric_precision: std::option::Option::Some(20), numeric_scale: std::option::Option::Some(0), udt_name: "numeric", }, ]; const RAW_TRANSACTION_ARCHIVE_PAYLOADS_COLUMNS: &[ColumnContract] = &[ ColumnContract { name: "signature", nullable: false, numeric_precision: std::option::Option::None, numeric_scale: std::option::Option::None, udt_name: "bytea", }, ColumnContract { name: "payload", nullable: false, numeric_precision: std::option::Option::None, numeric_scale: std::option::Option::None, udt_name: "bytea", }, ]; const V001_TABLE_NAMES: &[&str] = &["ksp_store_identity", "ksp_raw_transactions", "ksp_raw_transaction_observations", "ksp_raw_transaction_archive_payloads"]; const V002_TABLE_NAMES: &[&str] = &["ksp_raw_account_states", "ksp_raw_account_observations"]; /// Inspects one embedded schema resource against the effective PostgreSQL catalog. pub(crate) async fn inspect_resource( transaction: &deadpool_postgres::Transaction<'_>, resource: &SchemaResource, ) -> std::result::Result { return match resource.object { SchemaObjectContract::Constraint(contract) => inspect_constraint(transaction, contract, resource.sql).await, SchemaObjectContract::Index(contract) => inspect_index(transaction, contract).await, SchemaObjectContract::Table(contract) => inspect_table(transaction, contract).await, }; } /// Returns whether any KSP-managed Store base table already exists in the active schema. pub(crate) async fn managed_schema_objects_exist(transaction: &deadpool_postgres::Transaction<'_>) -> std::result::Result { let result = transaction.query_one(MANAGED_OBJECT_EXISTS_SQL, &[]).await; let row = match result { std::result::Result::Ok(value) => value, std::result::Result::Err(_) => return schema_query_error("schema_adoption_probe"), }; return match row.try_get::(0) { std::result::Result::Ok(value) => std::result::Result::Ok(value), std::result::Result::Err(_) => schema_query_error("schema_adoption_probe_decode"), }; } /// Rejects external V001 schema extensions that can constrain or mutate KSP writes. pub(crate) async fn verify_v001_external_compatibility( transaction: &deadpool_postgres::Transaction<'_>, ) -> std::result::Result<(), crate::PostgresBackendError> { return verify_external_compatibility(transaction, V001_TABLE_NAMES, V001_RESOURCES).await; } /// Rejects external V002 schema extensions that can constrain or mutate KSP writes. pub(crate) async fn verify_v002_external_compatibility( transaction: &deadpool_postgres::Transaction<'_>, ) -> std::result::Result<(), crate::PostgresBackendError> { return verify_external_compatibility(transaction, V002_TABLE_NAMES, V002_RESOURCES).await; } async fn verify_external_compatibility( transaction: &deadpool_postgres::Transaction<'_>, tables: &[&str], resources: &[SchemaResource], ) -> std::result::Result<(), crate::PostgresBackendError> { for table in tables { let table = *table; let constraint_rows = transaction.query(UNEXPECTED_CONSTRAINTS_SQL, &[&table]).await; let constraint_rows = match constraint_rows { std::result::Result::Ok(value) => value, std::result::Result::Err(_) => return schema_query_error("schema_constraint_inventory"), }; for row in constraint_rows { let name = row.try_get::(0); let kind = row.try_get::(1); let validated = row.try_get::(2); let deferrable = row.try_get::(3); let deferred = row.try_get::(4); let definition = row.try_get::(5); let (name, kind, validated, deferrable, deferred, definition) = match (name, kind, validated, deferrable, deferred, definition) { ( std::result::Result::Ok(name), std::result::Result::Ok(kind), std::result::Result::Ok(validated), std::result::Result::Ok(deferrable), std::result::Result::Ok(deferred), std::result::Result::Ok(definition), ) => (name, kind, validated, deferrable, deferred, definition), _ => return schema_query_error("schema_constraint_inventory_decode"), }; if is_expected_constraint(resources, table, name.as_str()) { continue; } let definition = normalize_catalog_sql(definition.as_str()); if !validated || deferrable || deferred || !matches_expected_constraint_definition(resources, table, kind.as_str(), definition.as_str()) { return schema_incompatible("schema_external_constraint"); } } let unique_rows = transaction.query(UNIQUE_INDEXES_SQL, &[&table]).await; let unique_rows = match unique_rows { std::result::Result::Ok(value) => value, std::result::Result::Err(_) => return schema_query_error("schema_unique_index_inventory"), }; for row in unique_rows { let name = row.try_get::(0); let constraint_backed = row.try_get::(1); let (_name, constraint_backed) = match (name, constraint_backed) { (std::result::Result::Ok(name), std::result::Result::Ok(constraint_backed)) => (name, constraint_backed), _ => return schema_query_error("schema_unique_index_inventory_decode"), }; if !constraint_backed { return schema_incompatible("schema_external_unique_index"); } } let trigger_count = load_count(transaction, TRIGGER_COUNT_SQL, table, "schema_trigger_inventory").await; let trigger_count = match trigger_count { std::result::Result::Ok(value) => value, std::result::Result::Err(error) => return std::result::Result::Err(error), }; if trigger_count != 0 { return schema_incompatible("schema_external_trigger"); } let rule_count = load_count(transaction, RULE_COUNT_SQL, table, "schema_rule_inventory").await; let rule_count = match rule_count { std::result::Result::Ok(value) => value, std::result::Result::Err(error) => return std::result::Result::Err(error), }; if rule_count != 0 { return schema_incompatible("schema_external_rule"); } } return std::result::Result::Ok(()); } async fn inspect_constraint( transaction: &deadpool_postgres::Transaction<'_>, contract: ConstraintContract, resource_sql: &str, ) -> std::result::Result { let expected_definition = match expected_constraint_definition(resource_sql, contract.name) { std::option::Option::Some(value) => value, std::option::Option::None => return schema_query_error("schema_constraint_contract"), }; let result = transaction.query_opt(CONSTRAINT_LOAD_SQL, &[&contract.table, &contract.name]).await; let row = match result { std::result::Result::Ok(std::option::Option::Some(value)) => value, std::result::Result::Ok(std::option::Option::None) => { return inspect_equivalent_constraint(transaction, contract, expected_definition.as_str()).await; }, std::result::Result::Err(_) => return schema_query_error("schema_constraint_probe"), }; let kind = row.try_get::(0); let validated = row.try_get::(1); let deferrable = row.try_get::(2); let deferred = row.try_get::(3); let definition = row.try_get::(4); let (kind, validated, deferrable, deferred, definition) = match (kind, validated, deferrable, deferred, definition) { ( std::result::Result::Ok(kind), std::result::Result::Ok(validated), std::result::Result::Ok(deferrable), std::result::Result::Ok(deferred), std::result::Result::Ok(definition), ) => (kind, validated, deferrable, deferred, definition), _ => return schema_query_error("schema_constraint_probe_decode"), }; let definition = normalize_catalog_sql(definition.as_str()); if kind != contract.kind || !validated || deferrable || deferred || definition != expected_definition { return std::result::Result::Ok(SchemaResourceState::Incompatible); } return std::result::Result::Ok(SchemaResourceState::Compatible); } async fn inspect_index( transaction: &deadpool_postgres::Transaction<'_>, contract: IndexContract, ) -> std::result::Result { let result = transaction.query_opt(INDEX_LOAD_SQL, &[&contract.table, &contract.name]).await; let row = match result { std::result::Result::Ok(std::option::Option::Some(value)) => value, std::result::Result::Ok(std::option::Option::None) => return std::result::Result::Ok(SchemaResourceState::Missing), std::result::Result::Err(_) => return schema_query_error("schema_index_probe"), }; let unique = row.try_get::(0); let access_method = row.try_get::(1); let definition = row.try_get::(2); let predicate = row.try_get::>(3); let (unique, access_method, definition, predicate) = match (unique, access_method, definition, predicate) { (std::result::Result::Ok(unique), std::result::Result::Ok(access_method), std::result::Result::Ok(definition), std::result::Result::Ok(predicate)) => { (unique, access_method, definition, predicate) }, _ => return schema_query_error("schema_index_probe_decode"), }; let definition = normalize_catalog_sql(definition.as_str()); let predicate = predicate.map(|value| return normalize_catalog_sql(value.as_str())); let predicate_matches = match (contract.predicate_fragment, predicate.as_deref()) { (std::option::Option::Some(expected), std::option::Option::Some(value)) => value.contains(expected), (std::option::Option::None, std::option::Option::None) => true, (std::option::Option::Some(_), std::option::Option::None) | (std::option::Option::None, std::option::Option::Some(_)) => false, }; if unique != contract.unique || access_method != contract.access_method || !definition.contains(contract.key_fragment) || !predicate_matches { return std::result::Result::Ok(SchemaResourceState::Incompatible); } return std::result::Result::Ok(SchemaResourceState::Compatible); } async fn inspect_table( transaction: &deadpool_postgres::Transaction<'_>, contract: TableContract, ) -> std::result::Result { let table_result = transaction.query_opt(TABLE_LOAD_SQL, &[&contract.name]).await; let table_row = match table_result { std::result::Result::Ok(std::option::Option::Some(value)) => value, std::result::Result::Ok(std::option::Option::None) => return std::result::Result::Ok(SchemaResourceState::Missing), std::result::Result::Err(_) => return schema_query_error("schema_table_probe"), }; let relkind = table_row.try_get::(0); let row_security = table_row.try_get::(1); let force_row_security = table_row.try_get::(2); let (relkind, row_security, force_row_security) = match (relkind, row_security, force_row_security) { (std::result::Result::Ok(relkind), std::result::Result::Ok(row_security), std::result::Result::Ok(force_row_security)) => { (relkind, row_security, force_row_security) }, _ => return schema_query_error("schema_table_probe_decode"), }; if relkind != "r" || row_security || force_row_security { return std::result::Result::Ok(SchemaResourceState::Incompatible); } let rows_result = transaction.query(COLUMN_LOAD_SQL, &[&contract.name]).await; let rows = match rows_result { std::result::Result::Ok(value) => value, std::result::Result::Err(_) => return schema_query_error("schema_column_probe"), }; let mut actual = std::vec::Vec::::with_capacity(rows.len()); for row in rows { let decoded = ActualColumn::from_row(&row); let decoded = match decoded { std::result::Result::Ok(value) => value, std::result::Result::Err(error) => return std::result::Result::Err(error), }; actual.push(decoded); } let mut missing = false; for expected in contract.columns { let found = actual.iter().find(|candidate| return candidate.name == expected.name); let found = match found { std::option::Option::Some(value) => value, std::option::Option::None => { missing = true; continue; }, }; if !found.matches(expected) { return std::result::Result::Ok(SchemaResourceState::Incompatible); } } for column in &actual { if contract.columns.iter().any(|expected| return expected.name == column.name) { continue; } if !column.is_non_blocking_extra() { return std::result::Result::Ok(SchemaResourceState::Incompatible); } } if let std::option::Option::Some(expected_key) = contract.primary_key_columns { let key_result = transaction.query_opt(PRIMARY_KEY_LOAD_SQL, &[&contract.name]).await; let key_row = match key_result { std::result::Result::Ok(std::option::Option::Some(value)) => value, std::result::Result::Ok(std::option::Option::None) => return std::result::Result::Ok(SchemaResourceState::Incompatible), std::result::Result::Err(_) => return schema_query_error("schema_primary_key_probe"), }; let key_columns = key_row.try_get::(0); let key_columns = match key_columns { std::result::Result::Ok(value) => value, std::result::Result::Err(_) => return schema_query_error("schema_primary_key_probe_decode"), }; if key_columns != expected_key { return std::result::Result::Ok(SchemaResourceState::Incompatible); } } if missing { return std::result::Result::Ok(SchemaResourceState::Missing); } return std::result::Result::Ok(SchemaResourceState::Compatible); } struct ActualColumn { default: std::option::Option, generated: std::string::String, identity: std::string::String, name: std::string::String, nullable: bool, numeric_precision: std::option::Option, numeric_scale: std::option::Option, udt_name: std::string::String, } impl ActualColumn { fn from_row(row: &tokio_postgres::Row) -> std::result::Result { let name = row.try_get::(0); let udt_name = row.try_get::(1); let nullable = row.try_get::(2); let numeric_precision = row.try_get::>(3); let numeric_scale = row.try_get::>(4); let default = row.try_get::>(5); let identity = row.try_get::(6); let generated = row.try_get::(7); return match (name, udt_name, nullable, numeric_precision, numeric_scale, default, identity, generated) { ( std::result::Result::Ok(name), std::result::Result::Ok(udt_name), std::result::Result::Ok(nullable), std::result::Result::Ok(numeric_precision), std::result::Result::Ok(numeric_scale), std::result::Result::Ok(default), std::result::Result::Ok(identity), std::result::Result::Ok(generated), ) => std::result::Result::Ok(Self { default, generated, identity, name, nullable, numeric_precision, numeric_scale, udt_name }), _ => schema_query_error("schema_column_probe_decode"), }; } fn is_non_blocking_extra(&self) -> bool { return self.nullable && self.default.is_none() && self.identity == "NO" && self.generated == "NEVER"; } fn matches(&self, expected: &ColumnContract) -> bool { let numeric_matches = if expected.udt_name == "numeric" { self.numeric_precision == expected.numeric_precision && self.numeric_scale == expected.numeric_scale } else { true }; return self.udt_name == expected.udt_name && self.nullable == expected.nullable && numeric_matches && self.identity == "NO" && self.generated == "NEVER"; } } async fn load_count( transaction: &deadpool_postgres::Transaction<'_>, sql: &str, table: &str, phase: &'static str, ) -> std::result::Result { let result = transaction.query_one(sql, &[&table]).await; let row = match result { std::result::Result::Ok(value) => value, std::result::Result::Err(_) => return schema_query_error(phase), }; return match row.try_get::(0) { std::result::Result::Ok(value) => std::result::Result::Ok(value), std::result::Result::Err(_) => schema_query_error("schema_inventory_decode"), }; } async fn inspect_equivalent_constraint( transaction: &deadpool_postgres::Transaction<'_>, contract: ConstraintContract, expected_definition: &str, ) -> std::result::Result { let rows = transaction.query(CONSTRAINT_FALLBACK_LOAD_SQL, &[&contract.table, &contract.kind]).await; let rows = match rows { std::result::Result::Ok(value) => value, std::result::Result::Err(_) => return schema_query_error("schema_constraint_fallback_probe"), }; for row in rows { let validated = row.try_get::(1); let deferrable = row.try_get::(2); let deferred = row.try_get::(3); let definition = row.try_get::(4); let (validated, deferrable, deferred, definition) = match (validated, deferrable, deferred, definition) { ( std::result::Result::Ok(validated), std::result::Result::Ok(deferrable), std::result::Result::Ok(deferred), std::result::Result::Ok(definition), ) => (validated, deferrable, deferred, definition), _ => return schema_query_error("schema_constraint_fallback_probe_decode"), }; let definition = normalize_catalog_sql(definition.as_str()); if validated && !deferrable && !deferred && definition == expected_definition { return std::result::Result::Ok(SchemaResourceState::Compatible); } } return std::result::Result::Ok(SchemaResourceState::Missing); } fn expected_constraint_definition(resource_sql: &str, name: &str) -> std::option::Option { let marker = std::format!("ADD CONSTRAINT {name} "); let marker_start = match resource_sql.find(marker.as_str()) { std::option::Option::Some(value) => value, std::option::Option::None => return std::option::Option::None, }; let start = match marker_start.checked_add(marker.len()) { std::option::Option::Some(value) => value, std::option::Option::None => return std::option::Option::None, }; let tail = match resource_sql.get(start..) { std::option::Option::Some(value) => value, std::option::Option::None => return std::option::Option::None, }; let end = match tail.find(';') { std::option::Option::Some(value) => value, std::option::Option::None => return std::option::Option::None, }; let definition = match tail.get(..end) { std::option::Option::Some(value) => value, std::option::Option::None => return std::option::Option::None, }; return std::option::Option::Some(normalize_catalog_sql(definition)); } fn is_expected_constraint(resources: &[SchemaResource], table: &str, name: &str) -> bool { for resource in resources { let contract = match resource.object { SchemaObjectContract::Constraint(value) => value, SchemaObjectContract::Index(_) | SchemaObjectContract::Table(_) => continue, }; if contract.table == table && contract.name == name { return true; } } return false; } fn matches_expected_constraint_definition(resources: &[SchemaResource], table: &str, kind: &str, definition: &str) -> bool { for resource in resources { let contract = match resource.object { SchemaObjectContract::Constraint(value) => value, SchemaObjectContract::Index(_) | SchemaObjectContract::Table(_) => continue, }; if contract.table != table || contract.kind != kind { continue; } let expected = expected_constraint_definition(resource.sql, contract.name); if let std::option::Option::Some(expected) = expected && expected == definition { return true; } } return false; } fn normalize_catalog_sql(value: &str) -> std::string::String { let value = normalize_quoted_integral_cast_literals(value); return value .chars() .filter(|character| return !character.is_whitespace() && *character != '"' && *character != '(' && *character != ')') .collect::() .replace("::text", "") .replace("::numeric", "") .replace("::bigint", "") .replace("::smallint", "") .replace("::integer", "") .replace("::int8", "") .replace("::int4", "") .replace("::int2", "") .to_ascii_lowercase(); } fn normalize_quoted_integral_cast_literals(value: &str) -> std::string::String { const INTEGRAL_CASTS: &[&str] = &["::bigint", "::int2", "::int4", "::int8", "::integer", "::numeric", "::smallint"]; let mut normalized = std::string::String::with_capacity(value.len()); let mut remaining = value; loop { let (before_quote, after_quote) = match remaining.split_once('\'') { std::option::Option::Some(value) => value, std::option::Option::None => { normalized.push_str(remaining); break; }, }; normalized.push_str(before_quote); let (literal, after_literal) = match after_quote.split_once('\'') { std::option::Option::Some(value) => value, std::option::Option::None => { normalized.push('\''); normalized.push_str(after_quote); break; }, }; let has_digit = literal.chars().any(|character| return character.is_ascii_digit()); let integral_literal = !literal.is_empty() && has_digit && literal .chars() .enumerate() .all(|(offset, character)| return character.is_ascii_digit() || (offset == 0 && (character == '+' || character == '-'))); let mut after_integral_cast = std::option::Option::None; if integral_literal { for cast in INTEGRAL_CASTS { if let std::option::Option::Some(value) = after_literal.strip_prefix(*cast) { after_integral_cast = std::option::Option::Some(value); break; } } } if let std::option::Option::Some(value) = after_integral_cast { normalized.push_str(literal); remaining = value; continue; } normalized.push('\''); normalized.push_str(literal); normalized.push('\''); remaining = after_literal; } return normalized; } fn schema_incompatible(phase: &'static str) -> std::result::Result { return std::result::Result::Err(crate::PostgresBackendError::new(crate::PostgresBackendErrorKind::MigrationMismatch, phase)); } fn schema_query_error(phase: &'static str) -> std::result::Result { return std::result::Result::Err(crate::PostgresBackendError::new(crate::PostgresBackendErrorKind::MigrationFailed, phase)); } #[cfg(test)] #[path = "../unit_tests/schema.rs"] mod tests;