v0.3.4-pre.002

This commit is contained in:
2026-08-30 21:11:23 +02:00
parent 03ad0ba063
commit 6bd593f467
18 changed files with 710 additions and 46 deletions

View File

@@ -0,0 +1,12 @@
DO $ksp$
BEGIN
IF NOT EXISTS (
SELECT 1
FROM pg_constraint
WHERE conname = 'pk_ksp_raw_account_states'
AND conrelid = to_regclass('ksp_raw_account_states')
) THEN
ALTER TABLE ksp_raw_account_states ADD CONSTRAINT pk_ksp_raw_account_states PRIMARY KEY (pubkey, slot, state_hash);
END IF;
END
$ksp$;

View File

@@ -0,0 +1,12 @@
DO $ksp$
BEGIN
IF NOT EXISTS (
SELECT 1
FROM pg_constraint
WHERE conname = 'pk_ksp_raw_account_observations'
AND conrelid = to_regclass('ksp_raw_account_observations')
) THEN
ALTER TABLE ksp_raw_account_observations ADD CONSTRAINT pk_ksp_raw_account_observations PRIMARY KEY (observation_key);
END IF;
END
$ksp$;

View File

@@ -0,0 +1,14 @@
DO $ksp$
BEGIN
IF NOT EXISTS (
SELECT 1
FROM pg_constraint
WHERE conname = 'fk_ksp_raw_account_observations_state'
AND conrelid = to_regclass('ksp_raw_account_observations')
) THEN
ALTER TABLE ksp_raw_account_observations
ADD CONSTRAINT fk_ksp_raw_account_observations_state FOREIGN KEY (account_pubkey, account_slot, account_state_hash)
REFERENCES ksp_raw_account_states(pubkey, slot, state_hash) ON DELETE RESTRICT;
END IF;
END
$ksp$;

View File

@@ -0,0 +1,19 @@
CREATE TABLE IF NOT EXISTS ksp_raw_account_states (
pubkey BYTEA NOT NULL,
slot NUMERIC(20, 0) NOT NULL,
state_hash BYTEA NOT NULL,
lamports NUMERIC(20, 0) NOT NULL,
owner BYTEA NOT NULL,
executable BOOLEAN NOT NULL,
rent_epoch NUMERIC(20, 0) NOT NULL,
data BYTEA NOT NULL
);
ALTER TABLE ksp_raw_account_states ADD COLUMN IF NOT EXISTS pubkey BYTEA NOT NULL;
ALTER TABLE ksp_raw_account_states ADD COLUMN IF NOT EXISTS slot NUMERIC(20, 0) NOT NULL;
ALTER TABLE ksp_raw_account_states ADD COLUMN IF NOT EXISTS state_hash BYTEA NOT NULL;
ALTER TABLE ksp_raw_account_states ADD COLUMN IF NOT EXISTS lamports NUMERIC(20, 0) NOT NULL;
ALTER TABLE ksp_raw_account_states ADD COLUMN IF NOT EXISTS owner BYTEA NOT NULL;
ALTER TABLE ksp_raw_account_states ADD COLUMN IF NOT EXISTS executable BOOLEAN NOT NULL;
ALTER TABLE ksp_raw_account_states ADD COLUMN IF NOT EXISTS rent_epoch NUMERIC(20, 0) NOT NULL;
ALTER TABLE ksp_raw_account_states ADD COLUMN IF NOT EXISTS data BYTEA NOT NULL;

View File

@@ -0,0 +1,41 @@
CREATE TABLE IF NOT EXISTS ksp_raw_account_observations (
observation_key BYTEA NOT NULL,
account_pubkey BYTEA NOT NULL,
account_slot NUMERIC(20, 0) NOT NULL,
account_state_hash BYTEA NOT NULL,
provider TEXT NOT NULL,
protocol TEXT NOT NULL,
acquisition_method TEXT NOT NULL,
origin TEXT NOT NULL,
received_at_unix_millis BIGINT NOT NULL,
capture_session_id TEXT NULL,
commitment TEXT NULL,
endpoint_id TEXT NULL,
filter_id TEXT NULL,
observed_at_unix_millis BIGINT NULL,
source_payload_hash BYTEA NULL,
source_payload_size_bytes BIGINT NULL,
is_startup BOOLEAN NULL,
transaction_signature BYTEA NULL,
write_version NUMERIC(20, 0) NULL
);
ALTER TABLE ksp_raw_account_observations ADD COLUMN IF NOT EXISTS observation_key BYTEA NOT NULL;
ALTER TABLE ksp_raw_account_observations ADD COLUMN IF NOT EXISTS account_pubkey BYTEA NOT NULL;
ALTER TABLE ksp_raw_account_observations ADD COLUMN IF NOT EXISTS account_slot NUMERIC(20, 0) NOT NULL;
ALTER TABLE ksp_raw_account_observations ADD COLUMN IF NOT EXISTS account_state_hash BYTEA NOT NULL;
ALTER TABLE ksp_raw_account_observations ADD COLUMN IF NOT EXISTS provider TEXT NOT NULL;
ALTER TABLE ksp_raw_account_observations ADD COLUMN IF NOT EXISTS protocol TEXT NOT NULL;
ALTER TABLE ksp_raw_account_observations ADD COLUMN IF NOT EXISTS acquisition_method TEXT NOT NULL;
ALTER TABLE ksp_raw_account_observations ADD COLUMN IF NOT EXISTS origin TEXT NOT NULL;
ALTER TABLE ksp_raw_account_observations ADD COLUMN IF NOT EXISTS received_at_unix_millis BIGINT NOT NULL;
ALTER TABLE ksp_raw_account_observations ADD COLUMN IF NOT EXISTS capture_session_id TEXT NULL;
ALTER TABLE ksp_raw_account_observations ADD COLUMN IF NOT EXISTS commitment TEXT NULL;
ALTER TABLE ksp_raw_account_observations ADD COLUMN IF NOT EXISTS endpoint_id TEXT NULL;
ALTER TABLE ksp_raw_account_observations ADD COLUMN IF NOT EXISTS filter_id TEXT NULL;
ALTER TABLE ksp_raw_account_observations ADD COLUMN IF NOT EXISTS observed_at_unix_millis BIGINT NULL;
ALTER TABLE ksp_raw_account_observations ADD COLUMN IF NOT EXISTS source_payload_hash BYTEA NULL;
ALTER TABLE ksp_raw_account_observations ADD COLUMN IF NOT EXISTS source_payload_size_bytes BIGINT NULL;
ALTER TABLE ksp_raw_account_observations ADD COLUMN IF NOT EXISTS is_startup BOOLEAN NULL;
ALTER TABLE ksp_raw_account_observations ADD COLUMN IF NOT EXISTS transaction_signature BYTEA NULL;
ALTER TABLE ksp_raw_account_observations ADD COLUMN IF NOT EXISTS write_version NUMERIC(20, 0) NULL;

View File

@@ -1,5 +1,5 @@
// file: crates/ksp-store-postgres-lib/src/lib.rs
// version: 14
// version: 15
#![warn(missing_docs)]
#![deny(unreachable_pub)]
@@ -21,6 +21,8 @@
//! with compare-and-transition outcomes and explicit rejection of `Compacted`.
//! `0.3.3-pre.008` implements all six `RawTransaction*` capabilities directly on
//! `PostgresBackend` while preserving the existing narrow backend bridge.
//! `0.3.4-pre.002` registers additive V002 and its two minimal RAW account
//! tables with canonical state/observation PKs and the observation-state FK.
//!
//! This crate depends on `ksp-store-api` and never on `ksp-store-lib`. The
//! common facade consumes only this crate's narrow backend bridge and never
@@ -93,6 +95,8 @@ pub(crate) use self::schema::SchemaResourceState;
pub(crate) use self::schema::V000_RESOURCES;
/// Private V001 schema resource inventory consumed by the migration engine.
pub(crate) use self::schema::V001_RESOURCES;
/// Private V002 schema resource inventory consumed by the migration engine.
pub(crate) use self::schema::V002_RESOURCES;
/// Private physical schema resource inspector consumed by the migration engine.
pub(crate) use self::schema::inspect_resource;
/// Private V001 adoption probe consumed by the migration engine.

View File

@@ -1,5 +1,5 @@
// file: crates/ksp-store-postgres-lib/src/migration.rs
// version: 7
// version: 8
use sha2::Digest; // rust-rules: trait-import
@@ -19,6 +19,13 @@ const EMBEDDED_MIGRATIONS: &[EmbeddedMigration] = &[
resources: crate::V001_RESOURCES,
version: 1,
},
EmbeddedMigration {
checksum: MigrationChecksum::Resources,
hook: MigrationHook::None,
name: "raw_account_state",
resources: crate::V002_RESOURCES,
version: 2,
},
];
const HEX_LOWER: &[u8; 16] = b"0123456789abcdef";
const HISTORY_INSERT_SQL: &str = "INSERT INTO ksp_store_schema_migrations (version, name, checksum, applied_at) VALUES ($1, $2, $3, CURRENT_TIMESTAMP)";

View File

@@ -1,5 +1,5 @@
// file: crates/ksp-store-postgres-lib/src/schema.rs
// version: 7
// version: 8
/// Immutable V000 physical schema resource inventory.
pub(crate) const V000_RESOURCES: &[SchemaResource] = &[SchemaResource {
@@ -378,6 +378,55 @@ pub(crate) const V001_RESOURCES: &[SchemaResource] = &[
sql: include_str!("../migrations/v001_raw_transaction/indexes/001_ix_ksp_raw_transactions_slot_signature.sql"),
},
];
/// V002 physical schema resource inventory for the minimal RAW account state foundation.
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"),
},
];
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
@@ -710,6 +759,199 @@ const RAW_TRANSACTION_OBSERVATIONS_COLUMNS: &[ColumnContract] = &[
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",

View File

@@ -1,5 +1,5 @@
// file: crates/ksp-store-postgres-lib/tests/dependency_boundary.rs
// version: 14
// version: 15
#![warn(missing_docs)]
#![deny(unreachable_pub)]
@@ -23,6 +23,7 @@ fn pre_005_backend_owns_exact_physical_runtime_dependencies_without_reverse_faca
let bootstrap_sql = include_str!("../migrations/v000_bootstrap/tables/001_ksp_store_schema_migrations.sql");
assert!(migration.contains("crate::V000_RESOURCES"));
assert!(migration.contains("crate::V001_RESOURCES"));
assert!(migration.contains("crate::V002_RESOURCES"));
assert!(schema.contains("../migrations/v000_bootstrap/tables/001_ksp_store_schema_migrations.sql"));
assert!(schema.contains("../migrations/v001_raw_transaction/tables/001_ksp_store_identity.sql"));
assert!(bootstrap_sql.contains("ksp_store_schema_migrations"));
@@ -113,7 +114,7 @@ fn pre_003_fix_001_migration_engine_uses_split_schema_contract_and_binds_network
assert!(observation_table.contains("CREATE TABLE IF NOT EXISTS ksp_raw_transaction_observations"));
assert!(archive_table.contains("CREATE TABLE IF NOT EXISTS ksp_raw_transaction_archive_payloads"));
assert!(index.contains("CREATE INDEX IF NOT EXISTS ix_ksp_raw_transactions_slot_signature"));
for forbidden in ["impl ksp_store_api::RawTransaction", "repository", "sqlx", "RawAccountState"] {
for forbidden in ["impl ksp_store_api::RawTransaction", "impl ksp_store_api::RawAccount", "repository", "sqlx"] {
assert!(!migration.contains(forbidden), "repository/cross-scope implementation leaked into migration engine: {forbidden}");
assert!(!schema.contains(forbidden), "repository/cross-scope implementation leaked into schema contract: {forbidden}");
}
@@ -124,6 +125,25 @@ fn pre_003_fix_001_migration_engine_uses_split_schema_contract_and_binds_network
return;
}
#[test]
fn pre_002_v002_is_migration_only_and_does_not_open_account_repository_or_runtime_dispatch() {
let crate_root = include_str!("../src/lib.rs");
let migration = include_str!("../src/migration.rs");
let schema = include_str!("../src/schema.rs");
let runtime = include_str!("../src/runtime.rs");
let states = include_str!("../migrations/v002_raw_account_state/tables/001_ksp_raw_account_states.sql");
let observations = include_str!("../migrations/v002_raw_account_state/tables/002_ksp_raw_account_observations.sql");
assert!(migration.contains("name: \"raw_account_state\""));
assert!(migration.contains("resources: crate::V002_RESOURCES"));
assert!(schema.contains("pub(crate) const V002_RESOURCES"));
assert!(states.contains("ksp_raw_account_states"));
assert!(observations.contains("ksp_raw_account_observations"));
assert!(!crate_root.contains("mod raw_account;"));
assert!(!runtime.contains("impl ksp_store_api::RawAccount"));
assert!(!std::path::Path::new(env!("CARGO_MANIFEST_DIR")).join("src/raw_account.rs").exists());
return;
}
#[test]
fn pre_004_raw_read_sql_and_mapping_remain_backend_private() {
let crate_root = include_str!("../src/lib.rs");

View File

@@ -1,5 +1,5 @@
// file: crates/ksp-store-postgres-lib/tests/hardening_completeness.rs
// version: 11
// version: 12
#![warn(missing_docs)]
#![deny(unreachable_pub)]
@@ -279,7 +279,9 @@ fn pre_010_raw_transaction_capability_implementation_inventory_is_exact_and_raw_
assert!(!runtime.contains(forbidden), "RawAccountState scope opened during RawTransaction hardening: {forbidden}");
}
let migration = include_str!("../src/migration.rs");
assert!(!migration.contains("ksp_raw_account"));
assert!(migration.contains("raw_account_state"));
assert!(migration.contains("crate::V002_RESOURCES"));
assert!(!runtime.contains("mod raw_account"));
return;
}

View File

@@ -1,5 +1,5 @@
// file: crates/ksp-store-postgres-lib/tests/postgres_foundation_live.rs
// version: 3
// version: 4
#![warn(missing_docs)]
#![deny(unreachable_pub)]
@@ -153,7 +153,7 @@ async fn run_foundation_scenario(admin: &mut tokio_postgres::Client, uri: &str,
}
*owns_schema = true;
let initial_health = initial.health().await;
if !initial_health.is_ready() || initial_health.migration_version() != std::option::Option::Some(1) || initial_health.pending_migration_count() != 0 {
if !initial_health.is_ready() || initial_health.migration_version() != std::option::Option::Some(2) || initial_health.pending_migration_count() != 0 {
return std::result::Result::Err(LiveFailure::new("initial_health"));
}
let initial_close = close_backend(initial).await;
@@ -167,7 +167,7 @@ async fn run_foundation_scenario(admin: &mut tokio_postgres::Client, uri: &str,
};
let idempotent_health = idempotent.health().await;
if !idempotent_health.is_ready()
|| idempotent_health.migration_version() != std::option::Option::Some(1)
|| idempotent_health.migration_version() != std::option::Option::Some(2)
|| idempotent_health.pending_migration_count() != 0
{
return std::result::Result::Err(LiveFailure::new("idempotent_health"));
@@ -250,7 +250,7 @@ async fn run_foundation_scenario(admin: &mut tokio_postgres::Client, uri: &str,
std::result::Result::Err(error) => return std::result::Result::Err(error),
};
let final_health = final_backend.health().await;
if !final_health.is_ready() || final_health.migration_version() != std::option::Option::Some(1) || final_health.pending_migration_count() != 0 {
if !final_health.is_ready() || final_health.migration_version() != std::option::Option::Some(2) || final_health.pending_migration_count() != 0 {
return std::result::Result::Err(LiveFailure::new("final_health"));
}
return close_backend(final_backend).await;

View File

@@ -1,5 +1,5 @@
// file: crates/ksp-store-postgres-lib/tests/postgres_raw_transaction_live.rs
// version: 3
// version: 4
#![warn(missing_docs)]
#![deny(unreachable_pub)]
@@ -150,7 +150,7 @@ async fn run_raw_transaction_scenario(admin: &mut tokio_postgres::Client, uri: &
};
*owns_schema = true;
let initial_health = initial.health().await;
if !initial_health.is_ready() || initial_health.migration_version() != std::option::Option::Some(1) || initial_health.pending_migration_count() != 0 {
if !initial_health.is_ready() || initial_health.migration_version() != std::option::Option::Some(2) || initial_health.pending_migration_count() != 0 {
return std::result::Result::Err(LiveFailure::new("initial_health"));
}
let wrong_network_result = open_backend_result(uri, "testnet", true, true).await;
@@ -211,7 +211,7 @@ async fn run_raw_transaction_scenario(admin: &mut tokio_postgres::Client, uri: &
std::result::Result::Err(error) => return std::result::Result::Err(error),
};
let reopened_health = reopened.health().await;
if !reopened_health.is_ready() || reopened_health.migration_version() != std::option::Option::Some(1) {
if !reopened_health.is_ready() || reopened_health.migration_version() != std::option::Option::Some(2) {
return std::result::Result::Err(LiveFailure::new("reopen_health"));
}
let reference_result = raw_reference(10);

View File

@@ -1,5 +1,5 @@
// file: crates/ksp-store-postgres-lib/unit_tests/migration.rs
// version: 5
// version: 6
fn applied(version: i64, name: &str, checksum: &str) -> super::AppliedMigration {
return super::AppliedMigration { checksum: checksum.to_owned(), name: name.to_owned(), version };
@@ -17,7 +17,7 @@ fn embedded(version: i64, name: &'static str, resources: &'static [crate::Schema
#[test]
fn pre_003_fix_001_embedded_registry_keeps_v000_checksum_and_uses_resource_owned_v001() {
assert_eq!(super::EMBEDDED_MIGRATIONS.len(), 2);
assert!(super::EMBEDDED_MIGRATIONS.len() >= 2);
let v000 = &super::EMBEDDED_MIGRATIONS[0];
assert_eq!(v000.version, 0);
assert_eq!(v000.name, "bootstrap");
@@ -32,7 +32,29 @@ fn pre_003_fix_001_embedded_registry_keeps_v000_checksum_and_uses_resource_owned
assert_eq!(v001.resources.len(), 40);
assert_eq!(super::migration_checksum(v001), "31488cda2f08f3f46c4cdbdbb6c18c243662fada02eac4487040c8735d72cc51");
assert!(super::validate_embedded_registry(super::EMBEDDED_MIGRATIONS).is_ok());
assert_eq!(crate::current_migration_version(), 1);
return;
}
#[test]
fn pre_002_v002_registry_is_additive_minimal_and_keeps_v000_v001_checksums_stable() {
assert_eq!(super::EMBEDDED_MIGRATIONS.len(), 3);
let v000 = &super::EMBEDDED_MIGRATIONS[0];
let v001 = &super::EMBEDDED_MIGRATIONS[1];
let v002 = &super::EMBEDDED_MIGRATIONS[2];
assert_eq!(super::migration_checksum(v000), "d29068b8c13b9dc0cc9ef6aaadd0fa12d41e0fe4c56541a1118c4bfc846a1450");
assert_eq!(super::migration_checksum(v001), "31488cda2f08f3f46c4cdbdbb6c18c243662fada02eac4487040c8735d72cc51");
assert_eq!(v002.version, 2);
assert_eq!(v002.name, "raw_account_state");
assert_eq!(v002.hook, super::MigrationHook::None);
assert_eq!(v002.resources.len(), 5);
assert!(super::validate_embedded_registry(super::EMBEDDED_MIGRATIONS).is_ok());
assert_eq!(crate::current_migration_version(), 2);
let full = [
applied(0, v000.name, super::migration_checksum(v000).as_str()),
applied(1, v001.name, super::migration_checksum(v001).as_str()),
applied(2, v002.name, super::migration_checksum(v002).as_str()),
];
assert_eq!(super::validate_history(&full, super::EMBEDDED_MIGRATIONS).ok(), std::option::Option::Some(3));
return;
}

View File

@@ -1,5 +1,5 @@
// file: crates/ksp-store-postgres-lib/unit_tests/schema.rs
// version: 4
// version: 5
fn actual_column(name: &str, udt_name: &str, nullable: bool) -> super::ActualColumn {
return super::ActualColumn {
@@ -48,6 +48,37 @@ fn pre_003_fix_001_v001_resources_are_split_and_idempotent_by_object_family() {
return;
}
#[test]
fn pre_002_v002_resources_are_exactly_two_tables_plus_base_pk_fk_without_indexes_or_domain_checks() {
assert_eq!(crate::V002_RESOURCES.len(), 5);
let ids = crate::V002_RESOURCES.iter().map(|resource| return resource.id).collect::<std::vec::Vec<_>>();
assert_eq!(ids[..2], ["tables/001_ksp_raw_account_states.sql", "tables/002_ksp_raw_account_observations.sql"]);
assert_eq!(
ids[2..],
[
"constraints/001_pk_ksp_raw_account_states.sql",
"constraints/002_pk_ksp_raw_account_observations.sql",
"constraints/003_fk_ksp_raw_account_observations_state.sql",
]
);
let sql = crate::V002_RESOURCES.iter().map(|resource| return resource.sql).collect::<std::vec::Vec<_>>().concat();
for required in [
"CREATE TABLE IF NOT EXISTS ksp_raw_account_states",
"CREATE TABLE IF NOT EXISTS ksp_raw_account_observations",
"slot NUMERIC(20, 0) NOT NULL",
"write_version NUMERIC(20, 0) NULL",
"PRIMARY KEY (pubkey, slot, state_hash)",
"PRIMARY KEY (observation_key)",
"FOREIGN KEY (account_pubkey, account_slot, account_state_hash) REFERENCES ksp_raw_account_states(pubkey, slot, state_hash) ON DELETE RESTRICT",
] {
assert!(sql.contains(required), "V002 pre.002 physical foundation is missing: {required}");
}
for forbidden in ["CREATE INDEX", " CHECK ", "ON DELETE CASCADE", "BIGSERIAL", "slot BIGINT", "data TEXT"] {
assert!(!sql.contains(forbidden), "pre.002 advanced V002 beyond the minimal table/PK/FK scope: {forbidden}");
}
return;
}
#[test]
fn pre_003_fix_001_external_extra_columns_are_accepted_only_when_they_cannot_block_ksp_inserts() {
let nullable = actual_column("external_nullable", "text", true);